484 lines
14 KiB
C
484 lines
14 KiB
C
/****************************************************************************
|
|
* mm/shm/shmget.c
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <sys/shm.h>
|
|
#include <sys/ipc.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <debug.h>
|
|
#include <errno.h>
|
|
|
|
#include <nuttx/pgalloc.h>
|
|
#include <nuttx/sched.h>
|
|
|
|
#include "shm/shm.h"
|
|
|
|
/****************************************************************************
|
|
* Public Data
|
|
****************************************************************************/
|
|
|
|
/* State of the all shared memory */
|
|
|
|
struct shm_info_s g_shminfo =
|
|
{
|
|
NXMUTEX_INITIALIZER
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: shm_find
|
|
*
|
|
* Description:
|
|
* Find the shared memory region with matching key
|
|
*
|
|
* Input Parameters:
|
|
* key - The value that uniquely identifies a shared memory region.
|
|
*
|
|
* Returned Value:
|
|
* On success, an index in the range of 0 to CONFIG_ARCH_SHM_MAXREGIONS-1
|
|
* is returned to identify the matching region; -ENOENT is returned on
|
|
* failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int shm_find(key_t key)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < CONFIG_ARCH_SHM_MAXREGIONS; i++)
|
|
{
|
|
if (g_shminfo.si_region[i].sr_key == key)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -ENOENT;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: shm_reserve
|
|
*
|
|
* Description:
|
|
* Allocate an unused shared memory region. That is one with a key of -1
|
|
*
|
|
* Input Parameters:
|
|
* None
|
|
*
|
|
* Returned Value:
|
|
* On success, an index in the range of 0 to CONFIG_ARCH_SHM_MAXREGIONS-1
|
|
* is returned to identify the matching region; -ENOSPC is returned on
|
|
* failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int shm_reserve(key_t key, int shmflg)
|
|
{
|
|
FAR struct shm_region_s *region;
|
|
int i;
|
|
|
|
for (i = 0; i < CONFIG_ARCH_SHM_MAXREGIONS; i++)
|
|
{
|
|
/* Is this region in use? */
|
|
|
|
region = &g_shminfo.si_region[i];
|
|
if (region->sr_flags == SRFLAG_AVAILABLE)
|
|
{
|
|
/* No... reserve it for the caller now */
|
|
|
|
memset(region, 0, sizeof(struct shm_region_s));
|
|
region->sr_key = key;
|
|
region->sr_flags = SRFLAG_INUSE;
|
|
|
|
nxmutex_init(®ion->sr_lock);
|
|
|
|
/* Set the low-order nine bits of shm_perm.mode to the low-order
|
|
* nine bits of shmflg.
|
|
*/
|
|
|
|
region->sr_ds.shm_perm.mode = shmflg & IPC_MODE;
|
|
|
|
/* The value of shm_segsz is left equal to zero for now because no
|
|
* memory has yet been allocated.
|
|
*
|
|
* The values of shm_lpid, shm_nattch, shm_atime, and shm_dtime are
|
|
* set equal to 0.
|
|
*/
|
|
|
|
/* The value of shm_ctime is set equal to the current time. */
|
|
|
|
region->sr_ds.shm_ctime = time(NULL);
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -ENOSPC;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: shm_extend
|
|
*
|
|
* Description:
|
|
* Extend the size of a memory regions by allocating physical pages as
|
|
* necessary
|
|
*
|
|
* Input Parameters:
|
|
* shmid - The index of the region of interest in the shared memory region
|
|
* table.
|
|
* size - The new size of the region.
|
|
*
|
|
* Returned Value:
|
|
* Zero is returned on success; -ENOMEM is returned on failure.
|
|
* (Should a different error be returned if the region is just too big?)
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int shm_extend(int shmid, size_t size)
|
|
{
|
|
FAR struct shm_region_s *region = &g_shminfo.si_region[shmid];
|
|
unsigned int pgalloc;
|
|
unsigned int pgneeded;
|
|
|
|
/* This is the number of pages that are needed to satisfy the allocation */
|
|
|
|
pgneeded = MM_NPAGES(size);
|
|
|
|
/* This is the number of pages that have already been allocated */
|
|
|
|
pgalloc = MM_NPAGES(region->sr_ds.shm_segsz);
|
|
|
|
/* Loop until all pages have been allocated (or something bad happens) */
|
|
|
|
while (pgalloc < pgneeded && pgalloc < CONFIG_ARCH_SHM_NPAGES)
|
|
{
|
|
/* Allocate one more physical page */
|
|
|
|
region->sr_pages[pgalloc] = mm_pgalloc(1);
|
|
if (region->sr_pages[pgalloc] == 0)
|
|
{
|
|
shmerr("ERROR: mm_pgalloc(1) failed\n");
|
|
break;
|
|
}
|
|
|
|
/* Zero the allocated page. */
|
|
|
|
memset((FAR void *)region->sr_pages[pgalloc], 0, MM_PGSIZE);
|
|
|
|
/* Increment the number of pages successfully allocated */
|
|
|
|
pgalloc++;
|
|
}
|
|
|
|
/* We get here (1) because all of the pages were successfully, (2) because
|
|
* mm_pgalloc() failed, or (3) because any additional pages allocated
|
|
* would exceed CONFIG_ARCH_SHM_NPAGES.
|
|
*/
|
|
|
|
if (pgalloc < pgneeded)
|
|
{
|
|
/* Set the amount memory available which will be less than the
|
|
* requested size.
|
|
*/
|
|
|
|
region->sr_ds.shm_segsz = pgalloc << MM_PGSHIFT;
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Set the new region size and return success */
|
|
|
|
region->sr_ds.shm_segsz = size;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: shm_create
|
|
*
|
|
* Description:
|
|
* Create the shared memory region.
|
|
*
|
|
* Input Parameters:
|
|
* key - The key that is used to access the unique shared memory
|
|
* identifier.
|
|
* size - The shared memory region that is created will be at least
|
|
* this size in bytes.
|
|
* shmflgs - See IPC_* definitions in sys/ipc.h. Only the values
|
|
* IPC_PRIVATE or IPC_CREAT are supported.
|
|
*
|
|
* Returned Value:
|
|
* Zero is returned on success; A negated errno value is returned on
|
|
* failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int shm_create(key_t key, size_t size, int shmflg)
|
|
{
|
|
FAR struct shm_region_s *region;
|
|
int shmid;
|
|
int ret;
|
|
|
|
/* Reserve the shared memory region */
|
|
|
|
ret = shm_reserve(key, shmflg);
|
|
if (ret < 0)
|
|
{
|
|
shmerr("ERROR: shm_reserve failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Save the shared memory ID */
|
|
|
|
shmid = ret;
|
|
|
|
/* Then allocate the physical memory (by extending it from the initial
|
|
* size of zero).
|
|
*/
|
|
|
|
ret = shm_extend(shmid, size);
|
|
if (ret < 0)
|
|
{
|
|
/* Free any partial allocations and unreserve the region */
|
|
|
|
shm_destroy(shmid);
|
|
return ret;
|
|
}
|
|
|
|
/* Save the process ID of the creator */
|
|
|
|
region = &g_shminfo.si_region[shmid];
|
|
region->sr_ds.shm_cpid = _SCHED_GETPID();
|
|
|
|
/* Return the shared memory ID */
|
|
|
|
return shmid;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: shmget
|
|
*
|
|
* Description:
|
|
* The shmget() function will return the shared memory identifier
|
|
* associated with key.
|
|
*
|
|
* A shared memory identifier, associated data structure, and shared
|
|
* memory segment of at least size bytes is created for key if one of the
|
|
* following is true:
|
|
*
|
|
* - The argument key is equal to IPC_PRIVATE.
|
|
* - The argument key does not already have a shared memory identifier
|
|
* associated with it and (shmflg & IPC_CREAT) is non-zero.
|
|
*
|
|
* Upon creation, the data structure associated with the new shared memory
|
|
* identifier will be initialized as follows:
|
|
*
|
|
* - The low-order nine bits of shm_perm.mode are set equal to the low-
|
|
* order nine bits of shmflg.
|
|
* - The value of shm_segsz is set equal to the value of size.
|
|
* - The values of shm_lpid, shm_nattch, shm_atime, and shm_dtime are
|
|
* set equal to 0.
|
|
* - The value of shm_ctime is set equal to the current time.
|
|
*
|
|
* When the shared memory segment is created, it will be initialized with
|
|
* all zero values.
|
|
*
|
|
* Input Parameters:
|
|
* key - The key that is used to access the unique shared memory
|
|
* identifier.
|
|
* size - The shared memory region that is created will be at least
|
|
* this size in bytes.
|
|
* shmflgs - See IPC_* definitions in sys/ipc.h. Only the values
|
|
* IPC_PRIVATE or IPC_CREAT are supported.
|
|
*
|
|
* Returned Value:
|
|
* Upon successful completion, shmget() will return a non-negative
|
|
* integer, namely a shared memory identifier; otherwise, it will return
|
|
* -1 and set errno to indicate the error.
|
|
*
|
|
* - EACCES
|
|
* A shared memory identifier exists for key but operation permission
|
|
* as specified by the low-order nine bits of shmflg would not be
|
|
* granted.
|
|
* - EEXIST
|
|
* A shared memory identifier exists for the argument key but
|
|
* (shmflg & IPC_CREAT) && (shmflg & IPC_EXCL) are non-zero.
|
|
* - EINVAL
|
|
* A shared memory segment is to be created and the value of size is
|
|
* less than the system-imposed minimum or greater than the system-
|
|
* imposed maximum.
|
|
* - EINVAL
|
|
* No shared memory segment is to be created and a shared memory
|
|
* segment exists for key but the size of the segment associated with
|
|
* it is less than size and size is not 0.
|
|
* - ENOENT
|
|
* A shared memory identifier does not exist for the argument key and
|
|
* (shmflg & IPC_CREAT) is 0.
|
|
* - ENOMEM
|
|
* A shared memory identifier and associated shared memory segment
|
|
* will be created, but the amount of available physical memory is
|
|
* not sufficient to fill the request.
|
|
* - ENOSPC
|
|
* A shared memory identifier is to be created, but the system-imposed
|
|
* limit on the maximum number of allowed shared memory identifiers
|
|
* system-wide would be exceeded.
|
|
*
|
|
* POSIX Deviations:
|
|
* - The values of shm_perm.cuid, shm_perm.uid, shm_perm.cgid, and
|
|
* shm_perm.gid should be set equal to the effective user ID and
|
|
* effective group ID, respectively, of the calling process.
|
|
* The NuttX ipc_perm structure, however, does not support these
|
|
* fields because user and group IDs are not yet supported by NuttX.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int shmget(key_t key, size_t size, int shmflg)
|
|
{
|
|
FAR struct shm_region_s *region;
|
|
int shmid = -1;
|
|
int ret;
|
|
|
|
/* Check for the special case where the caller doesn't really want shared
|
|
* memory (they why do they bother to call us?)
|
|
*/
|
|
|
|
if (key == IPC_PRIVATE)
|
|
{
|
|
/* Not yet implemented */
|
|
|
|
ret = -ENOSYS;
|
|
goto errout;
|
|
}
|
|
|
|
/* Get exclusive access to the global list of shared memory regions */
|
|
|
|
ret = nxmutex_lock(&g_shminfo.si_lock);
|
|
if (ret < 0)
|
|
{
|
|
goto errout;
|
|
}
|
|
|
|
/* Find the requested memory region */
|
|
|
|
ret = shm_find(key);
|
|
if (ret < 0)
|
|
{
|
|
/* The memory region does not exist.. create it if IPC_CREAT is
|
|
* included in the shmflags.
|
|
*/
|
|
|
|
if ((shmflg & IPC_CREAT) != 0)
|
|
{
|
|
/* Create the memory region */
|
|
|
|
ret = shm_create(key, size, shmflg);
|
|
if (ret < 0)
|
|
{
|
|
shmerr("ERROR: shm_create failed: %d\n", ret);
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
/* Return the shared memory ID */
|
|
|
|
shmid = ret;
|
|
}
|
|
else
|
|
{
|
|
/* Fail with ENOENT */
|
|
|
|
goto errout_with_lock;
|
|
}
|
|
}
|
|
|
|
/* The region exists */
|
|
|
|
else
|
|
{
|
|
/* Remember the shared memory ID */
|
|
|
|
shmid = ret;
|
|
|
|
/* Is the region big enough for the request? */
|
|
|
|
region = &g_shminfo.si_region[shmid];
|
|
if (region->sr_ds.shm_segsz < size)
|
|
{
|
|
/* We we asked to create the region? If so we can just
|
|
* extend it.
|
|
*
|
|
* REVISIT: We should check the mode bits of the regions
|
|
* first
|
|
*/
|
|
|
|
if ((shmflg & IPC_CREAT) != 0)
|
|
{
|
|
/* Extend the region */
|
|
|
|
ret = shm_extend(shmid, size);
|
|
if (ret < 0)
|
|
{
|
|
shmerr("ERROR: shm_create failed: %d\n", ret);
|
|
goto errout_with_lock;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Fail with EINVAL */
|
|
|
|
ret = -EINVAL;
|
|
goto errout_with_lock;
|
|
}
|
|
}
|
|
|
|
/* The region is already big enough or else we successfully
|
|
* extended the size of the region. If the region was previously
|
|
* deleted, but waiting for processes to detach from the region,
|
|
* then it is no longer deleted.
|
|
*/
|
|
|
|
region->sr_flags = SRFLAG_INUSE;
|
|
}
|
|
|
|
/* Release our lock on the shared memory region list */
|
|
|
|
nxmutex_unlock(&g_shminfo.si_lock);
|
|
return shmid;
|
|
|
|
errout_with_lock:
|
|
nxmutex_unlock(&g_shminfo.si_lock);
|
|
|
|
errout:
|
|
set_errno(-ret);
|
|
return ERROR;
|
|
}
|
|
|