| /**************************************************************************** |
| * 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; |
| } |
| |