18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/ipc/sem.c 48c2ecf20Sopenharmony_ci * Copyright (C) 1992 Krishna Balasubramanian 58c2ecf20Sopenharmony_ci * Copyright (C) 1995 Eric Schenk, Bruno Haible 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * /proc/sysvipc/sem support (c) 1999 Dragos Acostachioaie <dragos@iname.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * SMP-threaded, sysctl's added 108c2ecf20Sopenharmony_ci * (c) 1999 Manfred Spraul <manfred@colorfullife.com> 118c2ecf20Sopenharmony_ci * Enforced range limit on SEM_UNDO 128c2ecf20Sopenharmony_ci * (c) 2001 Red Hat Inc 138c2ecf20Sopenharmony_ci * Lockless wakeup 148c2ecf20Sopenharmony_ci * (c) 2003 Manfred Spraul <manfred@colorfullife.com> 158c2ecf20Sopenharmony_ci * (c) 2016 Davidlohr Bueso <dave@stgolabs.net> 168c2ecf20Sopenharmony_ci * Further wakeup optimizations, documentation 178c2ecf20Sopenharmony_ci * (c) 2010 Manfred Spraul <manfred@colorfullife.com> 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * support for audit of ipc object properties and permission changes 208c2ecf20Sopenharmony_ci * Dustin Kirkland <dustin.kirkland@us.ibm.com> 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * namespaces support 238c2ecf20Sopenharmony_ci * OpenVZ, SWsoft Inc. 248c2ecf20Sopenharmony_ci * Pavel Emelianov <xemul@openvz.org> 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * Implementation notes: (May 2010) 278c2ecf20Sopenharmony_ci * This file implements System V semaphores. 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * User space visible behavior: 308c2ecf20Sopenharmony_ci * - FIFO ordering for semop() operations (just FIFO, not starvation 318c2ecf20Sopenharmony_ci * protection) 328c2ecf20Sopenharmony_ci * - multiple semaphore operations that alter the same semaphore in 338c2ecf20Sopenharmony_ci * one semop() are handled. 348c2ecf20Sopenharmony_ci * - sem_ctime (time of last semctl()) is updated in the IPC_SET, SETVAL and 358c2ecf20Sopenharmony_ci * SETALL calls. 368c2ecf20Sopenharmony_ci * - two Linux specific semctl() commands: SEM_STAT, SEM_INFO. 378c2ecf20Sopenharmony_ci * - undo adjustments at process exit are limited to 0..SEMVMX. 388c2ecf20Sopenharmony_ci * - namespace are supported. 398c2ecf20Sopenharmony_ci * - SEMMSL, SEMMNS, SEMOPM and SEMMNI can be configured at runtine by writing 408c2ecf20Sopenharmony_ci * to /proc/sys/kernel/sem. 418c2ecf20Sopenharmony_ci * - statistics about the usage are reported in /proc/sysvipc/sem. 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * Internals: 448c2ecf20Sopenharmony_ci * - scalability: 458c2ecf20Sopenharmony_ci * - all global variables are read-mostly. 468c2ecf20Sopenharmony_ci * - semop() calls and semctl(RMID) are synchronized by RCU. 478c2ecf20Sopenharmony_ci * - most operations do write operations (actually: spin_lock calls) to 488c2ecf20Sopenharmony_ci * the per-semaphore array structure. 498c2ecf20Sopenharmony_ci * Thus: Perfect SMP scaling between independent semaphore arrays. 508c2ecf20Sopenharmony_ci * If multiple semaphores in one array are used, then cache line 518c2ecf20Sopenharmony_ci * trashing on the semaphore array spinlock will limit the scaling. 528c2ecf20Sopenharmony_ci * - semncnt and semzcnt are calculated on demand in count_semcnt() 538c2ecf20Sopenharmony_ci * - the task that performs a successful semop() scans the list of all 548c2ecf20Sopenharmony_ci * sleeping tasks and completes any pending operations that can be fulfilled. 558c2ecf20Sopenharmony_ci * Semaphores are actively given to waiting tasks (necessary for FIFO). 568c2ecf20Sopenharmony_ci * (see update_queue()) 578c2ecf20Sopenharmony_ci * - To improve the scalability, the actual wake-up calls are performed after 588c2ecf20Sopenharmony_ci * dropping all locks. (see wake_up_sem_queue_prepare()) 598c2ecf20Sopenharmony_ci * - All work is done by the waker, the woken up task does not have to do 608c2ecf20Sopenharmony_ci * anything - not even acquiring a lock or dropping a refcount. 618c2ecf20Sopenharmony_ci * - A woken up task may not even touch the semaphore array anymore, it may 628c2ecf20Sopenharmony_ci * have been destroyed already by a semctl(RMID). 638c2ecf20Sopenharmony_ci * - UNDO values are stored in an array (one per process and per 648c2ecf20Sopenharmony_ci * semaphore array, lazily allocated). For backwards compatibility, multiple 658c2ecf20Sopenharmony_ci * modes for the UNDO variables are supported (per process, per thread) 668c2ecf20Sopenharmony_ci * (see copy_semundo, CLONE_SYSVSEM) 678c2ecf20Sopenharmony_ci * - There are two lists of the pending operations: a per-array list 688c2ecf20Sopenharmony_ci * and per-semaphore list (stored in the array). This allows to achieve FIFO 698c2ecf20Sopenharmony_ci * ordering without always scanning all pending operations. 708c2ecf20Sopenharmony_ci * The worst-case behavior is nevertheless O(N^2) for N wakeups. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#include <linux/compat.h> 748c2ecf20Sopenharmony_ci#include <linux/slab.h> 758c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 768c2ecf20Sopenharmony_ci#include <linux/init.h> 778c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 788c2ecf20Sopenharmony_ci#include <linux/time.h> 798c2ecf20Sopenharmony_ci#include <linux/security.h> 808c2ecf20Sopenharmony_ci#include <linux/syscalls.h> 818c2ecf20Sopenharmony_ci#include <linux/audit.h> 828c2ecf20Sopenharmony_ci#include <linux/capability.h> 838c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 848c2ecf20Sopenharmony_ci#include <linux/rwsem.h> 858c2ecf20Sopenharmony_ci#include <linux/nsproxy.h> 868c2ecf20Sopenharmony_ci#include <linux/ipc_namespace.h> 878c2ecf20Sopenharmony_ci#include <linux/sched/wake_q.h> 888c2ecf20Sopenharmony_ci#include <linux/nospec.h> 898c2ecf20Sopenharmony_ci#include <linux/rhashtable.h> 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 928c2ecf20Sopenharmony_ci#include "util.h" 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* One semaphore structure for each semaphore in the system. */ 958c2ecf20Sopenharmony_cistruct sem { 968c2ecf20Sopenharmony_ci int semval; /* current value */ 978c2ecf20Sopenharmony_ci /* 988c2ecf20Sopenharmony_ci * PID of the process that last modified the semaphore. For 998c2ecf20Sopenharmony_ci * Linux, specifically these are: 1008c2ecf20Sopenharmony_ci * - semop 1018c2ecf20Sopenharmony_ci * - semctl, via SETVAL and SETALL. 1028c2ecf20Sopenharmony_ci * - at task exit when performing undo adjustments (see exit_sem). 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_ci struct pid *sempid; 1058c2ecf20Sopenharmony_ci spinlock_t lock; /* spinlock for fine-grained semtimedop */ 1068c2ecf20Sopenharmony_ci struct list_head pending_alter; /* pending single-sop operations */ 1078c2ecf20Sopenharmony_ci /* that alter the semaphore */ 1088c2ecf20Sopenharmony_ci struct list_head pending_const; /* pending single-sop operations */ 1098c2ecf20Sopenharmony_ci /* that do not alter the semaphore*/ 1108c2ecf20Sopenharmony_ci time64_t sem_otime; /* candidate for sem_otime */ 1118c2ecf20Sopenharmony_ci} ____cacheline_aligned_in_smp; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* One sem_array data structure for each set of semaphores in the system. */ 1148c2ecf20Sopenharmony_cistruct sem_array { 1158c2ecf20Sopenharmony_ci struct kern_ipc_perm sem_perm; /* permissions .. see ipc.h */ 1168c2ecf20Sopenharmony_ci time64_t sem_ctime; /* create/last semctl() time */ 1178c2ecf20Sopenharmony_ci struct list_head pending_alter; /* pending operations */ 1188c2ecf20Sopenharmony_ci /* that alter the array */ 1198c2ecf20Sopenharmony_ci struct list_head pending_const; /* pending complex operations */ 1208c2ecf20Sopenharmony_ci /* that do not alter semvals */ 1218c2ecf20Sopenharmony_ci struct list_head list_id; /* undo requests on this array */ 1228c2ecf20Sopenharmony_ci int sem_nsems; /* no. of semaphores in array */ 1238c2ecf20Sopenharmony_ci int complex_count; /* pending complex operations */ 1248c2ecf20Sopenharmony_ci unsigned int use_global_lock;/* >0: global lock required */ 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci struct sem sems[]; 1278c2ecf20Sopenharmony_ci} __randomize_layout; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* One queue for each sleeping process in the system. */ 1308c2ecf20Sopenharmony_cistruct sem_queue { 1318c2ecf20Sopenharmony_ci struct list_head list; /* queue of pending operations */ 1328c2ecf20Sopenharmony_ci struct task_struct *sleeper; /* this process */ 1338c2ecf20Sopenharmony_ci struct sem_undo *undo; /* undo structure */ 1348c2ecf20Sopenharmony_ci struct pid *pid; /* process id of requesting process */ 1358c2ecf20Sopenharmony_ci int status; /* completion status of operation */ 1368c2ecf20Sopenharmony_ci struct sembuf *sops; /* array of pending operations */ 1378c2ecf20Sopenharmony_ci struct sembuf *blocking; /* the operation that blocked */ 1388c2ecf20Sopenharmony_ci int nsops; /* number of operations */ 1398c2ecf20Sopenharmony_ci bool alter; /* does *sops alter the array? */ 1408c2ecf20Sopenharmony_ci bool dupsop; /* sops on more than one sem_num */ 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* Each task has a list of undo requests. They are executed automatically 1448c2ecf20Sopenharmony_ci * when the process exits. 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_cistruct sem_undo { 1478c2ecf20Sopenharmony_ci struct list_head list_proc; /* per-process list: * 1488c2ecf20Sopenharmony_ci * all undos from one process 1498c2ecf20Sopenharmony_ci * rcu protected */ 1508c2ecf20Sopenharmony_ci struct rcu_head rcu; /* rcu struct for sem_undo */ 1518c2ecf20Sopenharmony_ci struct sem_undo_list *ulp; /* back ptr to sem_undo_list */ 1528c2ecf20Sopenharmony_ci struct list_head list_id; /* per semaphore array list: 1538c2ecf20Sopenharmony_ci * all undos for one array */ 1548c2ecf20Sopenharmony_ci int semid; /* semaphore set identifier */ 1558c2ecf20Sopenharmony_ci short *semadj; /* array of adjustments */ 1568c2ecf20Sopenharmony_ci /* one per semaphore */ 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/* sem_undo_list controls shared access to the list of sem_undo structures 1608c2ecf20Sopenharmony_ci * that may be shared among all a CLONE_SYSVSEM task group. 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_cistruct sem_undo_list { 1638c2ecf20Sopenharmony_ci refcount_t refcnt; 1648c2ecf20Sopenharmony_ci spinlock_t lock; 1658c2ecf20Sopenharmony_ci struct list_head list_proc; 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci#define sem_ids(ns) ((ns)->ids[IPC_SEM_IDS]) 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic int newary(struct ipc_namespace *, struct ipc_params *); 1728c2ecf20Sopenharmony_cistatic void freeary(struct ipc_namespace *, struct kern_ipc_perm *); 1738c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 1748c2ecf20Sopenharmony_cistatic int sysvipc_sem_proc_show(struct seq_file *s, void *it); 1758c2ecf20Sopenharmony_ci#endif 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci#define SEMMSL_FAST 256 /* 512 bytes on stack */ 1788c2ecf20Sopenharmony_ci#define SEMOPM_FAST 64 /* ~ 372 bytes on stack */ 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/* 1818c2ecf20Sopenharmony_ci * Switching from the mode suitable for simple ops 1828c2ecf20Sopenharmony_ci * to the mode for complex ops is costly. Therefore: 1838c2ecf20Sopenharmony_ci * use some hysteresis 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ci#define USE_GLOBAL_LOCK_HYSTERESIS 10 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci/* 1888c2ecf20Sopenharmony_ci * Locking: 1898c2ecf20Sopenharmony_ci * a) global sem_lock() for read/write 1908c2ecf20Sopenharmony_ci * sem_undo.id_next, 1918c2ecf20Sopenharmony_ci * sem_array.complex_count, 1928c2ecf20Sopenharmony_ci * sem_array.pending{_alter,_const}, 1938c2ecf20Sopenharmony_ci * sem_array.sem_undo 1948c2ecf20Sopenharmony_ci * 1958c2ecf20Sopenharmony_ci * b) global or semaphore sem_lock() for read/write: 1968c2ecf20Sopenharmony_ci * sem_array.sems[i].pending_{const,alter}: 1978c2ecf20Sopenharmony_ci * 1988c2ecf20Sopenharmony_ci * c) special: 1998c2ecf20Sopenharmony_ci * sem_undo_list.list_proc: 2008c2ecf20Sopenharmony_ci * * undo_list->lock for write 2018c2ecf20Sopenharmony_ci * * rcu for read 2028c2ecf20Sopenharmony_ci * use_global_lock: 2038c2ecf20Sopenharmony_ci * * global sem_lock() for write 2048c2ecf20Sopenharmony_ci * * either local or global sem_lock() for read. 2058c2ecf20Sopenharmony_ci * 2068c2ecf20Sopenharmony_ci * Memory ordering: 2078c2ecf20Sopenharmony_ci * Most ordering is enforced by using spin_lock() and spin_unlock(). 2088c2ecf20Sopenharmony_ci * 2098c2ecf20Sopenharmony_ci * Exceptions: 2108c2ecf20Sopenharmony_ci * 1) use_global_lock: (SEM_BARRIER_1) 2118c2ecf20Sopenharmony_ci * Setting it from non-zero to 0 is a RELEASE, this is ensured by 2128c2ecf20Sopenharmony_ci * using smp_store_release(): Immediately after setting it to 0, 2138c2ecf20Sopenharmony_ci * a simple op can start. 2148c2ecf20Sopenharmony_ci * Testing if it is non-zero is an ACQUIRE, this is ensured by using 2158c2ecf20Sopenharmony_ci * smp_load_acquire(). 2168c2ecf20Sopenharmony_ci * Setting it from 0 to non-zero must be ordered with regards to 2178c2ecf20Sopenharmony_ci * this smp_load_acquire(), this is guaranteed because the smp_load_acquire() 2188c2ecf20Sopenharmony_ci * is inside a spin_lock() and after a write from 0 to non-zero a 2198c2ecf20Sopenharmony_ci * spin_lock()+spin_unlock() is done. 2208c2ecf20Sopenharmony_ci * 2218c2ecf20Sopenharmony_ci * 2) queue.status: (SEM_BARRIER_2) 2228c2ecf20Sopenharmony_ci * Initialization is done while holding sem_lock(), so no further barrier is 2238c2ecf20Sopenharmony_ci * required. 2248c2ecf20Sopenharmony_ci * Setting it to a result code is a RELEASE, this is ensured by both a 2258c2ecf20Sopenharmony_ci * smp_store_release() (for case a) and while holding sem_lock() 2268c2ecf20Sopenharmony_ci * (for case b). 2278c2ecf20Sopenharmony_ci * The AQUIRE when reading the result code without holding sem_lock() is 2288c2ecf20Sopenharmony_ci * achieved by using READ_ONCE() + smp_acquire__after_ctrl_dep(). 2298c2ecf20Sopenharmony_ci * (case a above). 2308c2ecf20Sopenharmony_ci * Reading the result code while holding sem_lock() needs no further barriers, 2318c2ecf20Sopenharmony_ci * the locks inside sem_lock() enforce ordering (case b above) 2328c2ecf20Sopenharmony_ci * 2338c2ecf20Sopenharmony_ci * 3) current->state: 2348c2ecf20Sopenharmony_ci * current->state is set to TASK_INTERRUPTIBLE while holding sem_lock(). 2358c2ecf20Sopenharmony_ci * The wakeup is handled using the wake_q infrastructure. wake_q wakeups may 2368c2ecf20Sopenharmony_ci * happen immediately after calling wake_q_add. As wake_q_add_safe() is called 2378c2ecf20Sopenharmony_ci * when holding sem_lock(), no further barriers are required. 2388c2ecf20Sopenharmony_ci * 2398c2ecf20Sopenharmony_ci * See also ipc/mqueue.c for more details on the covered races. 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci#define sc_semmsl sem_ctls[0] 2438c2ecf20Sopenharmony_ci#define sc_semmns sem_ctls[1] 2448c2ecf20Sopenharmony_ci#define sc_semopm sem_ctls[2] 2458c2ecf20Sopenharmony_ci#define sc_semmni sem_ctls[3] 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_civoid sem_init_ns(struct ipc_namespace *ns) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci ns->sc_semmsl = SEMMSL; 2508c2ecf20Sopenharmony_ci ns->sc_semmns = SEMMNS; 2518c2ecf20Sopenharmony_ci ns->sc_semopm = SEMOPM; 2528c2ecf20Sopenharmony_ci ns->sc_semmni = SEMMNI; 2538c2ecf20Sopenharmony_ci ns->used_sems = 0; 2548c2ecf20Sopenharmony_ci ipc_init_ids(&ns->ids[IPC_SEM_IDS]); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci#ifdef CONFIG_IPC_NS 2588c2ecf20Sopenharmony_civoid sem_exit_ns(struct ipc_namespace *ns) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci free_ipcs(ns, &sem_ids(ns), freeary); 2618c2ecf20Sopenharmony_ci idr_destroy(&ns->ids[IPC_SEM_IDS].ipcs_idr); 2628c2ecf20Sopenharmony_ci rhashtable_destroy(&ns->ids[IPC_SEM_IDS].key_ht); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci#endif 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_civoid __init sem_init(void) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci sem_init_ns(&init_ipc_ns); 2698c2ecf20Sopenharmony_ci ipc_init_proc_interface("sysvipc/sem", 2708c2ecf20Sopenharmony_ci " key semid perms nsems uid gid cuid cgid otime ctime\n", 2718c2ecf20Sopenharmony_ci IPC_SEM_IDS, sysvipc_sem_proc_show); 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci/** 2758c2ecf20Sopenharmony_ci * unmerge_queues - unmerge queues, if possible. 2768c2ecf20Sopenharmony_ci * @sma: semaphore array 2778c2ecf20Sopenharmony_ci * 2788c2ecf20Sopenharmony_ci * The function unmerges the wait queues if complex_count is 0. 2798c2ecf20Sopenharmony_ci * It must be called prior to dropping the global semaphore array lock. 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_cistatic void unmerge_queues(struct sem_array *sma) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct sem_queue *q, *tq; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* complex operations still around? */ 2868c2ecf20Sopenharmony_ci if (sma->complex_count) 2878c2ecf20Sopenharmony_ci return; 2888c2ecf20Sopenharmony_ci /* 2898c2ecf20Sopenharmony_ci * We will switch back to simple mode. 2908c2ecf20Sopenharmony_ci * Move all pending operation back into the per-semaphore 2918c2ecf20Sopenharmony_ci * queues. 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_ci list_for_each_entry_safe(q, tq, &sma->pending_alter, list) { 2948c2ecf20Sopenharmony_ci struct sem *curr; 2958c2ecf20Sopenharmony_ci curr = &sma->sems[q->sops[0].sem_num]; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci list_add_tail(&q->list, &curr->pending_alter); 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sma->pending_alter); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/** 3038c2ecf20Sopenharmony_ci * merge_queues - merge single semop queues into global queue 3048c2ecf20Sopenharmony_ci * @sma: semaphore array 3058c2ecf20Sopenharmony_ci * 3068c2ecf20Sopenharmony_ci * This function merges all per-semaphore queues into the global queue. 3078c2ecf20Sopenharmony_ci * It is necessary to achieve FIFO ordering for the pending single-sop 3088c2ecf20Sopenharmony_ci * operations when a multi-semop operation must sleep. 3098c2ecf20Sopenharmony_ci * Only the alter operations must be moved, the const operations can stay. 3108c2ecf20Sopenharmony_ci */ 3118c2ecf20Sopenharmony_cistatic void merge_queues(struct sem_array *sma) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci int i; 3148c2ecf20Sopenharmony_ci for (i = 0; i < sma->sem_nsems; i++) { 3158c2ecf20Sopenharmony_ci struct sem *sem = &sma->sems[i]; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci list_splice_init(&sem->pending_alter, &sma->pending_alter); 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic void sem_rcu_free(struct rcu_head *head) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct kern_ipc_perm *p = container_of(head, struct kern_ipc_perm, rcu); 3248c2ecf20Sopenharmony_ci struct sem_array *sma = container_of(p, struct sem_array, sem_perm); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci security_sem_free(&sma->sem_perm); 3278c2ecf20Sopenharmony_ci kvfree(sma); 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci/* 3318c2ecf20Sopenharmony_ci * Enter the mode suitable for non-simple operations: 3328c2ecf20Sopenharmony_ci * Caller must own sem_perm.lock. 3338c2ecf20Sopenharmony_ci */ 3348c2ecf20Sopenharmony_cistatic void complexmode_enter(struct sem_array *sma) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci int i; 3378c2ecf20Sopenharmony_ci struct sem *sem; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (sma->use_global_lock > 0) { 3408c2ecf20Sopenharmony_ci /* 3418c2ecf20Sopenharmony_ci * We are already in global lock mode. 3428c2ecf20Sopenharmony_ci * Nothing to do, just reset the 3438c2ecf20Sopenharmony_ci * counter until we return to simple mode. 3448c2ecf20Sopenharmony_ci */ 3458c2ecf20Sopenharmony_ci sma->use_global_lock = USE_GLOBAL_LOCK_HYSTERESIS; 3468c2ecf20Sopenharmony_ci return; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci sma->use_global_lock = USE_GLOBAL_LOCK_HYSTERESIS; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci for (i = 0; i < sma->sem_nsems; i++) { 3518c2ecf20Sopenharmony_ci sem = &sma->sems[i]; 3528c2ecf20Sopenharmony_ci spin_lock(&sem->lock); 3538c2ecf20Sopenharmony_ci spin_unlock(&sem->lock); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci/* 3588c2ecf20Sopenharmony_ci * Try to leave the mode that disallows simple operations: 3598c2ecf20Sopenharmony_ci * Caller must own sem_perm.lock. 3608c2ecf20Sopenharmony_ci */ 3618c2ecf20Sopenharmony_cistatic void complexmode_tryleave(struct sem_array *sma) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci if (sma->complex_count) { 3648c2ecf20Sopenharmony_ci /* Complex ops are sleeping. 3658c2ecf20Sopenharmony_ci * We must stay in complex mode 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ci return; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci if (sma->use_global_lock == 1) { 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* See SEM_BARRIER_1 for purpose/pairing */ 3728c2ecf20Sopenharmony_ci smp_store_release(&sma->use_global_lock, 0); 3738c2ecf20Sopenharmony_ci } else { 3748c2ecf20Sopenharmony_ci sma->use_global_lock--; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci#define SEM_GLOBAL_LOCK (-1) 3798c2ecf20Sopenharmony_ci/* 3808c2ecf20Sopenharmony_ci * If the request contains only one semaphore operation, and there are 3818c2ecf20Sopenharmony_ci * no complex transactions pending, lock only the semaphore involved. 3828c2ecf20Sopenharmony_ci * Otherwise, lock the entire semaphore array, since we either have 3838c2ecf20Sopenharmony_ci * multiple semaphores in our own semops, or we need to look at 3848c2ecf20Sopenharmony_ci * semaphores from other pending complex operations. 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_cistatic inline int sem_lock(struct sem_array *sma, struct sembuf *sops, 3878c2ecf20Sopenharmony_ci int nsops) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct sem *sem; 3908c2ecf20Sopenharmony_ci int idx; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (nsops != 1) { 3938c2ecf20Sopenharmony_ci /* Complex operation - acquire a full lock */ 3948c2ecf20Sopenharmony_ci ipc_lock_object(&sma->sem_perm); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* Prevent parallel simple ops */ 3978c2ecf20Sopenharmony_ci complexmode_enter(sma); 3988c2ecf20Sopenharmony_ci return SEM_GLOBAL_LOCK; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* 4028c2ecf20Sopenharmony_ci * Only one semaphore affected - try to optimize locking. 4038c2ecf20Sopenharmony_ci * Optimized locking is possible if no complex operation 4048c2ecf20Sopenharmony_ci * is either enqueued or processed right now. 4058c2ecf20Sopenharmony_ci * 4068c2ecf20Sopenharmony_ci * Both facts are tracked by use_global_mode. 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_ci idx = array_index_nospec(sops->sem_num, sma->sem_nsems); 4098c2ecf20Sopenharmony_ci sem = &sma->sems[idx]; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* 4128c2ecf20Sopenharmony_ci * Initial check for use_global_lock. Just an optimization, 4138c2ecf20Sopenharmony_ci * no locking, no memory barrier. 4148c2ecf20Sopenharmony_ci */ 4158c2ecf20Sopenharmony_ci if (!sma->use_global_lock) { 4168c2ecf20Sopenharmony_ci /* 4178c2ecf20Sopenharmony_ci * It appears that no complex operation is around. 4188c2ecf20Sopenharmony_ci * Acquire the per-semaphore lock. 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_ci spin_lock(&sem->lock); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* see SEM_BARRIER_1 for purpose/pairing */ 4238c2ecf20Sopenharmony_ci if (!smp_load_acquire(&sma->use_global_lock)) { 4248c2ecf20Sopenharmony_ci /* fast path successful! */ 4258c2ecf20Sopenharmony_ci return sops->sem_num; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci spin_unlock(&sem->lock); 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* slow path: acquire the full lock */ 4318c2ecf20Sopenharmony_ci ipc_lock_object(&sma->sem_perm); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (sma->use_global_lock == 0) { 4348c2ecf20Sopenharmony_ci /* 4358c2ecf20Sopenharmony_ci * The use_global_lock mode ended while we waited for 4368c2ecf20Sopenharmony_ci * sma->sem_perm.lock. Thus we must switch to locking 4378c2ecf20Sopenharmony_ci * with sem->lock. 4388c2ecf20Sopenharmony_ci * Unlike in the fast path, there is no need to recheck 4398c2ecf20Sopenharmony_ci * sma->use_global_lock after we have acquired sem->lock: 4408c2ecf20Sopenharmony_ci * We own sma->sem_perm.lock, thus use_global_lock cannot 4418c2ecf20Sopenharmony_ci * change. 4428c2ecf20Sopenharmony_ci */ 4438c2ecf20Sopenharmony_ci spin_lock(&sem->lock); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci ipc_unlock_object(&sma->sem_perm); 4468c2ecf20Sopenharmony_ci return sops->sem_num; 4478c2ecf20Sopenharmony_ci } else { 4488c2ecf20Sopenharmony_ci /* 4498c2ecf20Sopenharmony_ci * Not a false alarm, thus continue to use the global lock 4508c2ecf20Sopenharmony_ci * mode. No need for complexmode_enter(), this was done by 4518c2ecf20Sopenharmony_ci * the caller that has set use_global_mode to non-zero. 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_ci return SEM_GLOBAL_LOCK; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic inline void sem_unlock(struct sem_array *sma, int locknum) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci if (locknum == SEM_GLOBAL_LOCK) { 4608c2ecf20Sopenharmony_ci unmerge_queues(sma); 4618c2ecf20Sopenharmony_ci complexmode_tryleave(sma); 4628c2ecf20Sopenharmony_ci ipc_unlock_object(&sma->sem_perm); 4638c2ecf20Sopenharmony_ci } else { 4648c2ecf20Sopenharmony_ci struct sem *sem = &sma->sems[locknum]; 4658c2ecf20Sopenharmony_ci spin_unlock(&sem->lock); 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci/* 4708c2ecf20Sopenharmony_ci * sem_lock_(check_) routines are called in the paths where the rwsem 4718c2ecf20Sopenharmony_ci * is not held. 4728c2ecf20Sopenharmony_ci * 4738c2ecf20Sopenharmony_ci * The caller holds the RCU read lock. 4748c2ecf20Sopenharmony_ci */ 4758c2ecf20Sopenharmony_cistatic inline struct sem_array *sem_obtain_object(struct ipc_namespace *ns, int id) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct kern_ipc_perm *ipcp = ipc_obtain_object_idr(&sem_ids(ns), id); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (IS_ERR(ipcp)) 4808c2ecf20Sopenharmony_ci return ERR_CAST(ipcp); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci return container_of(ipcp, struct sem_array, sem_perm); 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic inline struct sem_array *sem_obtain_object_check(struct ipc_namespace *ns, 4868c2ecf20Sopenharmony_ci int id) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci struct kern_ipc_perm *ipcp = ipc_obtain_object_check(&sem_ids(ns), id); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (IS_ERR(ipcp)) 4918c2ecf20Sopenharmony_ci return ERR_CAST(ipcp); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci return container_of(ipcp, struct sem_array, sem_perm); 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic inline void sem_lock_and_putref(struct sem_array *sma) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci sem_lock(sma, NULL, -1); 4998c2ecf20Sopenharmony_ci ipc_rcu_putref(&sma->sem_perm, sem_rcu_free); 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci ipc_rmid(&sem_ids(ns), &s->sem_perm); 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic struct sem_array *sem_alloc(size_t nsems) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct sem_array *sma; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (nsems > (INT_MAX - sizeof(*sma)) / sizeof(sma->sems[0])) 5128c2ecf20Sopenharmony_ci return NULL; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci sma = kvzalloc(struct_size(sma, sems, nsems), GFP_KERNEL_ACCOUNT); 5158c2ecf20Sopenharmony_ci if (unlikely(!sma)) 5168c2ecf20Sopenharmony_ci return NULL; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return sma; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci/** 5228c2ecf20Sopenharmony_ci * newary - Create a new semaphore set 5238c2ecf20Sopenharmony_ci * @ns: namespace 5248c2ecf20Sopenharmony_ci * @params: ptr to the structure that contains key, semflg and nsems 5258c2ecf20Sopenharmony_ci * 5268c2ecf20Sopenharmony_ci * Called with sem_ids.rwsem held (as a writer) 5278c2ecf20Sopenharmony_ci */ 5288c2ecf20Sopenharmony_cistatic int newary(struct ipc_namespace *ns, struct ipc_params *params) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci int retval; 5318c2ecf20Sopenharmony_ci struct sem_array *sma; 5328c2ecf20Sopenharmony_ci key_t key = params->key; 5338c2ecf20Sopenharmony_ci int nsems = params->u.nsems; 5348c2ecf20Sopenharmony_ci int semflg = params->flg; 5358c2ecf20Sopenharmony_ci int i; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (!nsems) 5388c2ecf20Sopenharmony_ci return -EINVAL; 5398c2ecf20Sopenharmony_ci if (ns->used_sems + nsems > ns->sc_semmns) 5408c2ecf20Sopenharmony_ci return -ENOSPC; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci sma = sem_alloc(nsems); 5438c2ecf20Sopenharmony_ci if (!sma) 5448c2ecf20Sopenharmony_ci return -ENOMEM; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci sma->sem_perm.mode = (semflg & S_IRWXUGO); 5478c2ecf20Sopenharmony_ci sma->sem_perm.key = key; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci sma->sem_perm.security = NULL; 5508c2ecf20Sopenharmony_ci retval = security_sem_alloc(&sma->sem_perm); 5518c2ecf20Sopenharmony_ci if (retval) { 5528c2ecf20Sopenharmony_ci kvfree(sma); 5538c2ecf20Sopenharmony_ci return retval; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci for (i = 0; i < nsems; i++) { 5578c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sma->sems[i].pending_alter); 5588c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sma->sems[i].pending_const); 5598c2ecf20Sopenharmony_ci spin_lock_init(&sma->sems[i].lock); 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci sma->complex_count = 0; 5638c2ecf20Sopenharmony_ci sma->use_global_lock = USE_GLOBAL_LOCK_HYSTERESIS; 5648c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sma->pending_alter); 5658c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sma->pending_const); 5668c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sma->list_id); 5678c2ecf20Sopenharmony_ci sma->sem_nsems = nsems; 5688c2ecf20Sopenharmony_ci sma->sem_ctime = ktime_get_real_seconds(); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci /* ipc_addid() locks sma upon success. */ 5718c2ecf20Sopenharmony_ci retval = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni); 5728c2ecf20Sopenharmony_ci if (retval < 0) { 5738c2ecf20Sopenharmony_ci ipc_rcu_putref(&sma->sem_perm, sem_rcu_free); 5748c2ecf20Sopenharmony_ci return retval; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci ns->used_sems += nsems; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci sem_unlock(sma, -1); 5798c2ecf20Sopenharmony_ci rcu_read_unlock(); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci return sma->sem_perm.id; 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci/* 5868c2ecf20Sopenharmony_ci * Called with sem_ids.rwsem and ipcp locked. 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_cistatic int sem_more_checks(struct kern_ipc_perm *ipcp, struct ipc_params *params) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci struct sem_array *sma; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci sma = container_of(ipcp, struct sem_array, sem_perm); 5938c2ecf20Sopenharmony_ci if (params->u.nsems > sma->sem_nsems) 5948c2ecf20Sopenharmony_ci return -EINVAL; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci return 0; 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cilong ksys_semget(key_t key, int nsems, int semflg) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci struct ipc_namespace *ns; 6028c2ecf20Sopenharmony_ci static const struct ipc_ops sem_ops = { 6038c2ecf20Sopenharmony_ci .getnew = newary, 6048c2ecf20Sopenharmony_ci .associate = security_sem_associate, 6058c2ecf20Sopenharmony_ci .more_checks = sem_more_checks, 6068c2ecf20Sopenharmony_ci }; 6078c2ecf20Sopenharmony_ci struct ipc_params sem_params; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci ns = current->nsproxy->ipc_ns; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (nsems < 0 || nsems > ns->sc_semmsl) 6128c2ecf20Sopenharmony_ci return -EINVAL; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci sem_params.key = key; 6158c2ecf20Sopenharmony_ci sem_params.flg = semflg; 6168c2ecf20Sopenharmony_ci sem_params.u.nsems = nsems; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params); 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ciSYSCALL_DEFINE3(semget, key_t, key, int, nsems, int, semflg) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci return ksys_semget(key, nsems, semflg); 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci/** 6278c2ecf20Sopenharmony_ci * perform_atomic_semop[_slow] - Attempt to perform semaphore 6288c2ecf20Sopenharmony_ci * operations on a given array. 6298c2ecf20Sopenharmony_ci * @sma: semaphore array 6308c2ecf20Sopenharmony_ci * @q: struct sem_queue that describes the operation 6318c2ecf20Sopenharmony_ci * 6328c2ecf20Sopenharmony_ci * Caller blocking are as follows, based the value 6338c2ecf20Sopenharmony_ci * indicated by the semaphore operation (sem_op): 6348c2ecf20Sopenharmony_ci * 6358c2ecf20Sopenharmony_ci * (1) >0 never blocks. 6368c2ecf20Sopenharmony_ci * (2) 0 (wait-for-zero operation): semval is non-zero. 6378c2ecf20Sopenharmony_ci * (3) <0 attempting to decrement semval to a value smaller than zero. 6388c2ecf20Sopenharmony_ci * 6398c2ecf20Sopenharmony_ci * Returns 0 if the operation was possible. 6408c2ecf20Sopenharmony_ci * Returns 1 if the operation is impossible, the caller must sleep. 6418c2ecf20Sopenharmony_ci * Returns <0 for error codes. 6428c2ecf20Sopenharmony_ci */ 6438c2ecf20Sopenharmony_cistatic int perform_atomic_semop_slow(struct sem_array *sma, struct sem_queue *q) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci int result, sem_op, nsops; 6468c2ecf20Sopenharmony_ci struct pid *pid; 6478c2ecf20Sopenharmony_ci struct sembuf *sop; 6488c2ecf20Sopenharmony_ci struct sem *curr; 6498c2ecf20Sopenharmony_ci struct sembuf *sops; 6508c2ecf20Sopenharmony_ci struct sem_undo *un; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci sops = q->sops; 6538c2ecf20Sopenharmony_ci nsops = q->nsops; 6548c2ecf20Sopenharmony_ci un = q->undo; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci for (sop = sops; sop < sops + nsops; sop++) { 6578c2ecf20Sopenharmony_ci int idx = array_index_nospec(sop->sem_num, sma->sem_nsems); 6588c2ecf20Sopenharmony_ci curr = &sma->sems[idx]; 6598c2ecf20Sopenharmony_ci sem_op = sop->sem_op; 6608c2ecf20Sopenharmony_ci result = curr->semval; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (!sem_op && result) 6638c2ecf20Sopenharmony_ci goto would_block; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci result += sem_op; 6668c2ecf20Sopenharmony_ci if (result < 0) 6678c2ecf20Sopenharmony_ci goto would_block; 6688c2ecf20Sopenharmony_ci if (result > SEMVMX) 6698c2ecf20Sopenharmony_ci goto out_of_range; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci if (sop->sem_flg & SEM_UNDO) { 6728c2ecf20Sopenharmony_ci int undo = un->semadj[sop->sem_num] - sem_op; 6738c2ecf20Sopenharmony_ci /* Exceeding the undo range is an error. */ 6748c2ecf20Sopenharmony_ci if (undo < (-SEMAEM - 1) || undo > SEMAEM) 6758c2ecf20Sopenharmony_ci goto out_of_range; 6768c2ecf20Sopenharmony_ci un->semadj[sop->sem_num] = undo; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci curr->semval = result; 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci sop--; 6838c2ecf20Sopenharmony_ci pid = q->pid; 6848c2ecf20Sopenharmony_ci while (sop >= sops) { 6858c2ecf20Sopenharmony_ci ipc_update_pid(&sma->sems[sop->sem_num].sempid, pid); 6868c2ecf20Sopenharmony_ci sop--; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci return 0; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ciout_of_range: 6928c2ecf20Sopenharmony_ci result = -ERANGE; 6938c2ecf20Sopenharmony_ci goto undo; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ciwould_block: 6968c2ecf20Sopenharmony_ci q->blocking = sop; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci if (sop->sem_flg & IPC_NOWAIT) 6998c2ecf20Sopenharmony_ci result = -EAGAIN; 7008c2ecf20Sopenharmony_ci else 7018c2ecf20Sopenharmony_ci result = 1; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ciundo: 7048c2ecf20Sopenharmony_ci sop--; 7058c2ecf20Sopenharmony_ci while (sop >= sops) { 7068c2ecf20Sopenharmony_ci sem_op = sop->sem_op; 7078c2ecf20Sopenharmony_ci sma->sems[sop->sem_num].semval -= sem_op; 7088c2ecf20Sopenharmony_ci if (sop->sem_flg & SEM_UNDO) 7098c2ecf20Sopenharmony_ci un->semadj[sop->sem_num] += sem_op; 7108c2ecf20Sopenharmony_ci sop--; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci return result; 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic int perform_atomic_semop(struct sem_array *sma, struct sem_queue *q) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci int result, sem_op, nsops; 7198c2ecf20Sopenharmony_ci struct sembuf *sop; 7208c2ecf20Sopenharmony_ci struct sem *curr; 7218c2ecf20Sopenharmony_ci struct sembuf *sops; 7228c2ecf20Sopenharmony_ci struct sem_undo *un; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci sops = q->sops; 7258c2ecf20Sopenharmony_ci nsops = q->nsops; 7268c2ecf20Sopenharmony_ci un = q->undo; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci if (unlikely(q->dupsop)) 7298c2ecf20Sopenharmony_ci return perform_atomic_semop_slow(sma, q); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci /* 7328c2ecf20Sopenharmony_ci * We scan the semaphore set twice, first to ensure that the entire 7338c2ecf20Sopenharmony_ci * operation can succeed, therefore avoiding any pointless writes 7348c2ecf20Sopenharmony_ci * to shared memory and having to undo such changes in order to block 7358c2ecf20Sopenharmony_ci * until the operations can go through. 7368c2ecf20Sopenharmony_ci */ 7378c2ecf20Sopenharmony_ci for (sop = sops; sop < sops + nsops; sop++) { 7388c2ecf20Sopenharmony_ci int idx = array_index_nospec(sop->sem_num, sma->sem_nsems); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci curr = &sma->sems[idx]; 7418c2ecf20Sopenharmony_ci sem_op = sop->sem_op; 7428c2ecf20Sopenharmony_ci result = curr->semval; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (!sem_op && result) 7458c2ecf20Sopenharmony_ci goto would_block; /* wait-for-zero */ 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci result += sem_op; 7488c2ecf20Sopenharmony_ci if (result < 0) 7498c2ecf20Sopenharmony_ci goto would_block; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci if (result > SEMVMX) 7528c2ecf20Sopenharmony_ci return -ERANGE; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci if (sop->sem_flg & SEM_UNDO) { 7558c2ecf20Sopenharmony_ci int undo = un->semadj[sop->sem_num] - sem_op; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci /* Exceeding the undo range is an error. */ 7588c2ecf20Sopenharmony_ci if (undo < (-SEMAEM - 1) || undo > SEMAEM) 7598c2ecf20Sopenharmony_ci return -ERANGE; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci for (sop = sops; sop < sops + nsops; sop++) { 7648c2ecf20Sopenharmony_ci curr = &sma->sems[sop->sem_num]; 7658c2ecf20Sopenharmony_ci sem_op = sop->sem_op; 7668c2ecf20Sopenharmony_ci result = curr->semval; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci if (sop->sem_flg & SEM_UNDO) { 7698c2ecf20Sopenharmony_ci int undo = un->semadj[sop->sem_num] - sem_op; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci un->semadj[sop->sem_num] = undo; 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci curr->semval += sem_op; 7748c2ecf20Sopenharmony_ci ipc_update_pid(&curr->sempid, q->pid); 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci return 0; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ciwould_block: 7808c2ecf20Sopenharmony_ci q->blocking = sop; 7818c2ecf20Sopenharmony_ci return sop->sem_flg & IPC_NOWAIT ? -EAGAIN : 1; 7828c2ecf20Sopenharmony_ci} 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_cistatic inline void wake_up_sem_queue_prepare(struct sem_queue *q, int error, 7858c2ecf20Sopenharmony_ci struct wake_q_head *wake_q) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci struct task_struct *sleeper; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci sleeper = get_task_struct(q->sleeper); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci /* see SEM_BARRIER_2 for purpuse/pairing */ 7928c2ecf20Sopenharmony_ci smp_store_release(&q->status, error); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci wake_q_add_safe(wake_q, sleeper); 7958c2ecf20Sopenharmony_ci} 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_cistatic void unlink_queue(struct sem_array *sma, struct sem_queue *q) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci list_del(&q->list); 8008c2ecf20Sopenharmony_ci if (q->nsops > 1) 8018c2ecf20Sopenharmony_ci sma->complex_count--; 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci/** check_restart(sma, q) 8058c2ecf20Sopenharmony_ci * @sma: semaphore array 8068c2ecf20Sopenharmony_ci * @q: the operation that just completed 8078c2ecf20Sopenharmony_ci * 8088c2ecf20Sopenharmony_ci * update_queue is O(N^2) when it restarts scanning the whole queue of 8098c2ecf20Sopenharmony_ci * waiting operations. Therefore this function checks if the restart is 8108c2ecf20Sopenharmony_ci * really necessary. It is called after a previously waiting operation 8118c2ecf20Sopenharmony_ci * modified the array. 8128c2ecf20Sopenharmony_ci * Note that wait-for-zero operations are handled without restart. 8138c2ecf20Sopenharmony_ci */ 8148c2ecf20Sopenharmony_cistatic inline int check_restart(struct sem_array *sma, struct sem_queue *q) 8158c2ecf20Sopenharmony_ci{ 8168c2ecf20Sopenharmony_ci /* pending complex alter operations are too difficult to analyse */ 8178c2ecf20Sopenharmony_ci if (!list_empty(&sma->pending_alter)) 8188c2ecf20Sopenharmony_ci return 1; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci /* we were a sleeping complex operation. Too difficult */ 8218c2ecf20Sopenharmony_ci if (q->nsops > 1) 8228c2ecf20Sopenharmony_ci return 1; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci /* It is impossible that someone waits for the new value: 8258c2ecf20Sopenharmony_ci * - complex operations always restart. 8268c2ecf20Sopenharmony_ci * - wait-for-zero are handled seperately. 8278c2ecf20Sopenharmony_ci * - q is a previously sleeping simple operation that 8288c2ecf20Sopenharmony_ci * altered the array. It must be a decrement, because 8298c2ecf20Sopenharmony_ci * simple increments never sleep. 8308c2ecf20Sopenharmony_ci * - If there are older (higher priority) decrements 8318c2ecf20Sopenharmony_ci * in the queue, then they have observed the original 8328c2ecf20Sopenharmony_ci * semval value and couldn't proceed. The operation 8338c2ecf20Sopenharmony_ci * decremented to value - thus they won't proceed either. 8348c2ecf20Sopenharmony_ci */ 8358c2ecf20Sopenharmony_ci return 0; 8368c2ecf20Sopenharmony_ci} 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci/** 8398c2ecf20Sopenharmony_ci * wake_const_ops - wake up non-alter tasks 8408c2ecf20Sopenharmony_ci * @sma: semaphore array. 8418c2ecf20Sopenharmony_ci * @semnum: semaphore that was modified. 8428c2ecf20Sopenharmony_ci * @wake_q: lockless wake-queue head. 8438c2ecf20Sopenharmony_ci * 8448c2ecf20Sopenharmony_ci * wake_const_ops must be called after a semaphore in a semaphore array 8458c2ecf20Sopenharmony_ci * was set to 0. If complex const operations are pending, wake_const_ops must 8468c2ecf20Sopenharmony_ci * be called with semnum = -1, as well as with the number of each modified 8478c2ecf20Sopenharmony_ci * semaphore. 8488c2ecf20Sopenharmony_ci * The tasks that must be woken up are added to @wake_q. The return code 8498c2ecf20Sopenharmony_ci * is stored in q->pid. 8508c2ecf20Sopenharmony_ci * The function returns 1 if at least one operation was completed successfully. 8518c2ecf20Sopenharmony_ci */ 8528c2ecf20Sopenharmony_cistatic int wake_const_ops(struct sem_array *sma, int semnum, 8538c2ecf20Sopenharmony_ci struct wake_q_head *wake_q) 8548c2ecf20Sopenharmony_ci{ 8558c2ecf20Sopenharmony_ci struct sem_queue *q, *tmp; 8568c2ecf20Sopenharmony_ci struct list_head *pending_list; 8578c2ecf20Sopenharmony_ci int semop_completed = 0; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if (semnum == -1) 8608c2ecf20Sopenharmony_ci pending_list = &sma->pending_const; 8618c2ecf20Sopenharmony_ci else 8628c2ecf20Sopenharmony_ci pending_list = &sma->sems[semnum].pending_const; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci list_for_each_entry_safe(q, tmp, pending_list, list) { 8658c2ecf20Sopenharmony_ci int error = perform_atomic_semop(sma, q); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci if (error > 0) 8688c2ecf20Sopenharmony_ci continue; 8698c2ecf20Sopenharmony_ci /* operation completed, remove from queue & wakeup */ 8708c2ecf20Sopenharmony_ci unlink_queue(sma, q); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci wake_up_sem_queue_prepare(q, error, wake_q); 8738c2ecf20Sopenharmony_ci if (error == 0) 8748c2ecf20Sopenharmony_ci semop_completed = 1; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci return semop_completed; 8788c2ecf20Sopenharmony_ci} 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci/** 8818c2ecf20Sopenharmony_ci * do_smart_wakeup_zero - wakeup all wait for zero tasks 8828c2ecf20Sopenharmony_ci * @sma: semaphore array 8838c2ecf20Sopenharmony_ci * @sops: operations that were performed 8848c2ecf20Sopenharmony_ci * @nsops: number of operations 8858c2ecf20Sopenharmony_ci * @wake_q: lockless wake-queue head 8868c2ecf20Sopenharmony_ci * 8878c2ecf20Sopenharmony_ci * Checks all required queue for wait-for-zero operations, based 8888c2ecf20Sopenharmony_ci * on the actual changes that were performed on the semaphore array. 8898c2ecf20Sopenharmony_ci * The function returns 1 if at least one operation was completed successfully. 8908c2ecf20Sopenharmony_ci */ 8918c2ecf20Sopenharmony_cistatic int do_smart_wakeup_zero(struct sem_array *sma, struct sembuf *sops, 8928c2ecf20Sopenharmony_ci int nsops, struct wake_q_head *wake_q) 8938c2ecf20Sopenharmony_ci{ 8948c2ecf20Sopenharmony_ci int i; 8958c2ecf20Sopenharmony_ci int semop_completed = 0; 8968c2ecf20Sopenharmony_ci int got_zero = 0; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci /* first: the per-semaphore queues, if known */ 8998c2ecf20Sopenharmony_ci if (sops) { 9008c2ecf20Sopenharmony_ci for (i = 0; i < nsops; i++) { 9018c2ecf20Sopenharmony_ci int num = sops[i].sem_num; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci if (sma->sems[num].semval == 0) { 9048c2ecf20Sopenharmony_ci got_zero = 1; 9058c2ecf20Sopenharmony_ci semop_completed |= wake_const_ops(sma, num, wake_q); 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci } else { 9098c2ecf20Sopenharmony_ci /* 9108c2ecf20Sopenharmony_ci * No sops means modified semaphores not known. 9118c2ecf20Sopenharmony_ci * Assume all were changed. 9128c2ecf20Sopenharmony_ci */ 9138c2ecf20Sopenharmony_ci for (i = 0; i < sma->sem_nsems; i++) { 9148c2ecf20Sopenharmony_ci if (sma->sems[i].semval == 0) { 9158c2ecf20Sopenharmony_ci got_zero = 1; 9168c2ecf20Sopenharmony_ci semop_completed |= wake_const_ops(sma, i, wake_q); 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci /* 9218c2ecf20Sopenharmony_ci * If one of the modified semaphores got 0, 9228c2ecf20Sopenharmony_ci * then check the global queue, too. 9238c2ecf20Sopenharmony_ci */ 9248c2ecf20Sopenharmony_ci if (got_zero) 9258c2ecf20Sopenharmony_ci semop_completed |= wake_const_ops(sma, -1, wake_q); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci return semop_completed; 9288c2ecf20Sopenharmony_ci} 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci/** 9328c2ecf20Sopenharmony_ci * update_queue - look for tasks that can be completed. 9338c2ecf20Sopenharmony_ci * @sma: semaphore array. 9348c2ecf20Sopenharmony_ci * @semnum: semaphore that was modified. 9358c2ecf20Sopenharmony_ci * @wake_q: lockless wake-queue head. 9368c2ecf20Sopenharmony_ci * 9378c2ecf20Sopenharmony_ci * update_queue must be called after a semaphore in a semaphore array 9388c2ecf20Sopenharmony_ci * was modified. If multiple semaphores were modified, update_queue must 9398c2ecf20Sopenharmony_ci * be called with semnum = -1, as well as with the number of each modified 9408c2ecf20Sopenharmony_ci * semaphore. 9418c2ecf20Sopenharmony_ci * The tasks that must be woken up are added to @wake_q. The return code 9428c2ecf20Sopenharmony_ci * is stored in q->pid. 9438c2ecf20Sopenharmony_ci * The function internally checks if const operations can now succeed. 9448c2ecf20Sopenharmony_ci * 9458c2ecf20Sopenharmony_ci * The function return 1 if at least one semop was completed successfully. 9468c2ecf20Sopenharmony_ci */ 9478c2ecf20Sopenharmony_cistatic int update_queue(struct sem_array *sma, int semnum, struct wake_q_head *wake_q) 9488c2ecf20Sopenharmony_ci{ 9498c2ecf20Sopenharmony_ci struct sem_queue *q, *tmp; 9508c2ecf20Sopenharmony_ci struct list_head *pending_list; 9518c2ecf20Sopenharmony_ci int semop_completed = 0; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci if (semnum == -1) 9548c2ecf20Sopenharmony_ci pending_list = &sma->pending_alter; 9558c2ecf20Sopenharmony_ci else 9568c2ecf20Sopenharmony_ci pending_list = &sma->sems[semnum].pending_alter; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ciagain: 9598c2ecf20Sopenharmony_ci list_for_each_entry_safe(q, tmp, pending_list, list) { 9608c2ecf20Sopenharmony_ci int error, restart; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci /* If we are scanning the single sop, per-semaphore list of 9638c2ecf20Sopenharmony_ci * one semaphore and that semaphore is 0, then it is not 9648c2ecf20Sopenharmony_ci * necessary to scan further: simple increments 9658c2ecf20Sopenharmony_ci * that affect only one entry succeed immediately and cannot 9668c2ecf20Sopenharmony_ci * be in the per semaphore pending queue, and decrements 9678c2ecf20Sopenharmony_ci * cannot be successful if the value is already 0. 9688c2ecf20Sopenharmony_ci */ 9698c2ecf20Sopenharmony_ci if (semnum != -1 && sma->sems[semnum].semval == 0) 9708c2ecf20Sopenharmony_ci break; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci error = perform_atomic_semop(sma, q); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci /* Does q->sleeper still need to sleep? */ 9758c2ecf20Sopenharmony_ci if (error > 0) 9768c2ecf20Sopenharmony_ci continue; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci unlink_queue(sma, q); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci if (error) { 9818c2ecf20Sopenharmony_ci restart = 0; 9828c2ecf20Sopenharmony_ci } else { 9838c2ecf20Sopenharmony_ci semop_completed = 1; 9848c2ecf20Sopenharmony_ci do_smart_wakeup_zero(sma, q->sops, q->nsops, wake_q); 9858c2ecf20Sopenharmony_ci restart = check_restart(sma, q); 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci wake_up_sem_queue_prepare(q, error, wake_q); 9898c2ecf20Sopenharmony_ci if (restart) 9908c2ecf20Sopenharmony_ci goto again; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci return semop_completed; 9938c2ecf20Sopenharmony_ci} 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci/** 9968c2ecf20Sopenharmony_ci * set_semotime - set sem_otime 9978c2ecf20Sopenharmony_ci * @sma: semaphore array 9988c2ecf20Sopenharmony_ci * @sops: operations that modified the array, may be NULL 9998c2ecf20Sopenharmony_ci * 10008c2ecf20Sopenharmony_ci * sem_otime is replicated to avoid cache line trashing. 10018c2ecf20Sopenharmony_ci * This function sets one instance to the current time. 10028c2ecf20Sopenharmony_ci */ 10038c2ecf20Sopenharmony_cistatic void set_semotime(struct sem_array *sma, struct sembuf *sops) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci if (sops == NULL) { 10068c2ecf20Sopenharmony_ci sma->sems[0].sem_otime = ktime_get_real_seconds(); 10078c2ecf20Sopenharmony_ci } else { 10088c2ecf20Sopenharmony_ci sma->sems[sops[0].sem_num].sem_otime = 10098c2ecf20Sopenharmony_ci ktime_get_real_seconds(); 10108c2ecf20Sopenharmony_ci } 10118c2ecf20Sopenharmony_ci} 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci/** 10148c2ecf20Sopenharmony_ci * do_smart_update - optimized update_queue 10158c2ecf20Sopenharmony_ci * @sma: semaphore array 10168c2ecf20Sopenharmony_ci * @sops: operations that were performed 10178c2ecf20Sopenharmony_ci * @nsops: number of operations 10188c2ecf20Sopenharmony_ci * @otime: force setting otime 10198c2ecf20Sopenharmony_ci * @wake_q: lockless wake-queue head 10208c2ecf20Sopenharmony_ci * 10218c2ecf20Sopenharmony_ci * do_smart_update() does the required calls to update_queue and wakeup_zero, 10228c2ecf20Sopenharmony_ci * based on the actual changes that were performed on the semaphore array. 10238c2ecf20Sopenharmony_ci * Note that the function does not do the actual wake-up: the caller is 10248c2ecf20Sopenharmony_ci * responsible for calling wake_up_q(). 10258c2ecf20Sopenharmony_ci * It is safe to perform this call after dropping all locks. 10268c2ecf20Sopenharmony_ci */ 10278c2ecf20Sopenharmony_cistatic void do_smart_update(struct sem_array *sma, struct sembuf *sops, int nsops, 10288c2ecf20Sopenharmony_ci int otime, struct wake_q_head *wake_q) 10298c2ecf20Sopenharmony_ci{ 10308c2ecf20Sopenharmony_ci int i; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci otime |= do_smart_wakeup_zero(sma, sops, nsops, wake_q); 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci if (!list_empty(&sma->pending_alter)) { 10358c2ecf20Sopenharmony_ci /* semaphore array uses the global queue - just process it. */ 10368c2ecf20Sopenharmony_ci otime |= update_queue(sma, -1, wake_q); 10378c2ecf20Sopenharmony_ci } else { 10388c2ecf20Sopenharmony_ci if (!sops) { 10398c2ecf20Sopenharmony_ci /* 10408c2ecf20Sopenharmony_ci * No sops, thus the modified semaphores are not 10418c2ecf20Sopenharmony_ci * known. Check all. 10428c2ecf20Sopenharmony_ci */ 10438c2ecf20Sopenharmony_ci for (i = 0; i < sma->sem_nsems; i++) 10448c2ecf20Sopenharmony_ci otime |= update_queue(sma, i, wake_q); 10458c2ecf20Sopenharmony_ci } else { 10468c2ecf20Sopenharmony_ci /* 10478c2ecf20Sopenharmony_ci * Check the semaphores that were increased: 10488c2ecf20Sopenharmony_ci * - No complex ops, thus all sleeping ops are 10498c2ecf20Sopenharmony_ci * decrease. 10508c2ecf20Sopenharmony_ci * - if we decreased the value, then any sleeping 10518c2ecf20Sopenharmony_ci * semaphore ops wont be able to run: If the 10528c2ecf20Sopenharmony_ci * previous value was too small, then the new 10538c2ecf20Sopenharmony_ci * value will be too small, too. 10548c2ecf20Sopenharmony_ci */ 10558c2ecf20Sopenharmony_ci for (i = 0; i < nsops; i++) { 10568c2ecf20Sopenharmony_ci if (sops[i].sem_op > 0) { 10578c2ecf20Sopenharmony_ci otime |= update_queue(sma, 10588c2ecf20Sopenharmony_ci sops[i].sem_num, wake_q); 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci } 10618c2ecf20Sopenharmony_ci } 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci if (otime) 10648c2ecf20Sopenharmony_ci set_semotime(sma, sops); 10658c2ecf20Sopenharmony_ci} 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci/* 10688c2ecf20Sopenharmony_ci * check_qop: Test if a queued operation sleeps on the semaphore semnum 10698c2ecf20Sopenharmony_ci */ 10708c2ecf20Sopenharmony_cistatic int check_qop(struct sem_array *sma, int semnum, struct sem_queue *q, 10718c2ecf20Sopenharmony_ci bool count_zero) 10728c2ecf20Sopenharmony_ci{ 10738c2ecf20Sopenharmony_ci struct sembuf *sop = q->blocking; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci /* 10768c2ecf20Sopenharmony_ci * Linux always (since 0.99.10) reported a task as sleeping on all 10778c2ecf20Sopenharmony_ci * semaphores. This violates SUS, therefore it was changed to the 10788c2ecf20Sopenharmony_ci * standard compliant behavior. 10798c2ecf20Sopenharmony_ci * Give the administrators a chance to notice that an application 10808c2ecf20Sopenharmony_ci * might misbehave because it relies on the Linux behavior. 10818c2ecf20Sopenharmony_ci */ 10828c2ecf20Sopenharmony_ci pr_info_once("semctl(GETNCNT/GETZCNT) is since 3.16 Single Unix Specification compliant.\n" 10838c2ecf20Sopenharmony_ci "The task %s (%d) triggered the difference, watch for misbehavior.\n", 10848c2ecf20Sopenharmony_ci current->comm, task_pid_nr(current)); 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci if (sop->sem_num != semnum) 10878c2ecf20Sopenharmony_ci return 0; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci if (count_zero && sop->sem_op == 0) 10908c2ecf20Sopenharmony_ci return 1; 10918c2ecf20Sopenharmony_ci if (!count_zero && sop->sem_op < 0) 10928c2ecf20Sopenharmony_ci return 1; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci return 0; 10958c2ecf20Sopenharmony_ci} 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci/* The following counts are associated to each semaphore: 10988c2ecf20Sopenharmony_ci * semncnt number of tasks waiting on semval being nonzero 10998c2ecf20Sopenharmony_ci * semzcnt number of tasks waiting on semval being zero 11008c2ecf20Sopenharmony_ci * 11018c2ecf20Sopenharmony_ci * Per definition, a task waits only on the semaphore of the first semop 11028c2ecf20Sopenharmony_ci * that cannot proceed, even if additional operation would block, too. 11038c2ecf20Sopenharmony_ci */ 11048c2ecf20Sopenharmony_cistatic int count_semcnt(struct sem_array *sma, ushort semnum, 11058c2ecf20Sopenharmony_ci bool count_zero) 11068c2ecf20Sopenharmony_ci{ 11078c2ecf20Sopenharmony_ci struct list_head *l; 11088c2ecf20Sopenharmony_ci struct sem_queue *q; 11098c2ecf20Sopenharmony_ci int semcnt; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci semcnt = 0; 11128c2ecf20Sopenharmony_ci /* First: check the simple operations. They are easy to evaluate */ 11138c2ecf20Sopenharmony_ci if (count_zero) 11148c2ecf20Sopenharmony_ci l = &sma->sems[semnum].pending_const; 11158c2ecf20Sopenharmony_ci else 11168c2ecf20Sopenharmony_ci l = &sma->sems[semnum].pending_alter; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci list_for_each_entry(q, l, list) { 11198c2ecf20Sopenharmony_ci /* all task on a per-semaphore list sleep on exactly 11208c2ecf20Sopenharmony_ci * that semaphore 11218c2ecf20Sopenharmony_ci */ 11228c2ecf20Sopenharmony_ci semcnt++; 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci /* Then: check the complex operations. */ 11268c2ecf20Sopenharmony_ci list_for_each_entry(q, &sma->pending_alter, list) { 11278c2ecf20Sopenharmony_ci semcnt += check_qop(sma, semnum, q, count_zero); 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci if (count_zero) { 11308c2ecf20Sopenharmony_ci list_for_each_entry(q, &sma->pending_const, list) { 11318c2ecf20Sopenharmony_ci semcnt += check_qop(sma, semnum, q, count_zero); 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci return semcnt; 11358c2ecf20Sopenharmony_ci} 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci/* Free a semaphore set. freeary() is called with sem_ids.rwsem locked 11388c2ecf20Sopenharmony_ci * as a writer and the spinlock for this semaphore set hold. sem_ids.rwsem 11398c2ecf20Sopenharmony_ci * remains locked on exit. 11408c2ecf20Sopenharmony_ci */ 11418c2ecf20Sopenharmony_cistatic void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) 11428c2ecf20Sopenharmony_ci{ 11438c2ecf20Sopenharmony_ci struct sem_undo *un, *tu; 11448c2ecf20Sopenharmony_ci struct sem_queue *q, *tq; 11458c2ecf20Sopenharmony_ci struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm); 11468c2ecf20Sopenharmony_ci int i; 11478c2ecf20Sopenharmony_ci DEFINE_WAKE_Q(wake_q); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci /* Free the existing undo structures for this semaphore set. */ 11508c2ecf20Sopenharmony_ci ipc_assert_locked_object(&sma->sem_perm); 11518c2ecf20Sopenharmony_ci list_for_each_entry_safe(un, tu, &sma->list_id, list_id) { 11528c2ecf20Sopenharmony_ci list_del(&un->list_id); 11538c2ecf20Sopenharmony_ci spin_lock(&un->ulp->lock); 11548c2ecf20Sopenharmony_ci un->semid = -1; 11558c2ecf20Sopenharmony_ci list_del_rcu(&un->list_proc); 11568c2ecf20Sopenharmony_ci spin_unlock(&un->ulp->lock); 11578c2ecf20Sopenharmony_ci kfree_rcu(un, rcu); 11588c2ecf20Sopenharmony_ci } 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci /* Wake up all pending processes and let them fail with EIDRM. */ 11618c2ecf20Sopenharmony_ci list_for_each_entry_safe(q, tq, &sma->pending_const, list) { 11628c2ecf20Sopenharmony_ci unlink_queue(sma, q); 11638c2ecf20Sopenharmony_ci wake_up_sem_queue_prepare(q, -EIDRM, &wake_q); 11648c2ecf20Sopenharmony_ci } 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci list_for_each_entry_safe(q, tq, &sma->pending_alter, list) { 11678c2ecf20Sopenharmony_ci unlink_queue(sma, q); 11688c2ecf20Sopenharmony_ci wake_up_sem_queue_prepare(q, -EIDRM, &wake_q); 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci for (i = 0; i < sma->sem_nsems; i++) { 11718c2ecf20Sopenharmony_ci struct sem *sem = &sma->sems[i]; 11728c2ecf20Sopenharmony_ci list_for_each_entry_safe(q, tq, &sem->pending_const, list) { 11738c2ecf20Sopenharmony_ci unlink_queue(sma, q); 11748c2ecf20Sopenharmony_ci wake_up_sem_queue_prepare(q, -EIDRM, &wake_q); 11758c2ecf20Sopenharmony_ci } 11768c2ecf20Sopenharmony_ci list_for_each_entry_safe(q, tq, &sem->pending_alter, list) { 11778c2ecf20Sopenharmony_ci unlink_queue(sma, q); 11788c2ecf20Sopenharmony_ci wake_up_sem_queue_prepare(q, -EIDRM, &wake_q); 11798c2ecf20Sopenharmony_ci } 11808c2ecf20Sopenharmony_ci ipc_update_pid(&sem->sempid, NULL); 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci /* Remove the semaphore set from the IDR */ 11848c2ecf20Sopenharmony_ci sem_rmid(ns, sma); 11858c2ecf20Sopenharmony_ci sem_unlock(sma, -1); 11868c2ecf20Sopenharmony_ci rcu_read_unlock(); 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci wake_up_q(&wake_q); 11898c2ecf20Sopenharmony_ci ns->used_sems -= sma->sem_nsems; 11908c2ecf20Sopenharmony_ci ipc_rcu_putref(&sma->sem_perm, sem_rcu_free); 11918c2ecf20Sopenharmony_ci} 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_cistatic unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in, int version) 11948c2ecf20Sopenharmony_ci{ 11958c2ecf20Sopenharmony_ci switch (version) { 11968c2ecf20Sopenharmony_ci case IPC_64: 11978c2ecf20Sopenharmony_ci return copy_to_user(buf, in, sizeof(*in)); 11988c2ecf20Sopenharmony_ci case IPC_OLD: 11998c2ecf20Sopenharmony_ci { 12008c2ecf20Sopenharmony_ci struct semid_ds out; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci memset(&out, 0, sizeof(out)); 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci ipc64_perm_to_ipc_perm(&in->sem_perm, &out.sem_perm); 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci out.sem_otime = in->sem_otime; 12078c2ecf20Sopenharmony_ci out.sem_ctime = in->sem_ctime; 12088c2ecf20Sopenharmony_ci out.sem_nsems = in->sem_nsems; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci return copy_to_user(buf, &out, sizeof(out)); 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci default: 12138c2ecf20Sopenharmony_ci return -EINVAL; 12148c2ecf20Sopenharmony_ci } 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_cistatic time64_t get_semotime(struct sem_array *sma) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci int i; 12208c2ecf20Sopenharmony_ci time64_t res; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci res = sma->sems[0].sem_otime; 12238c2ecf20Sopenharmony_ci for (i = 1; i < sma->sem_nsems; i++) { 12248c2ecf20Sopenharmony_ci time64_t to = sma->sems[i].sem_otime; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci if (to > res) 12278c2ecf20Sopenharmony_ci res = to; 12288c2ecf20Sopenharmony_ci } 12298c2ecf20Sopenharmony_ci return res; 12308c2ecf20Sopenharmony_ci} 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_cistatic int semctl_stat(struct ipc_namespace *ns, int semid, 12338c2ecf20Sopenharmony_ci int cmd, struct semid64_ds *semid64) 12348c2ecf20Sopenharmony_ci{ 12358c2ecf20Sopenharmony_ci struct sem_array *sma; 12368c2ecf20Sopenharmony_ci time64_t semotime; 12378c2ecf20Sopenharmony_ci int err; 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci memset(semid64, 0, sizeof(*semid64)); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci rcu_read_lock(); 12428c2ecf20Sopenharmony_ci if (cmd == SEM_STAT || cmd == SEM_STAT_ANY) { 12438c2ecf20Sopenharmony_ci sma = sem_obtain_object(ns, semid); 12448c2ecf20Sopenharmony_ci if (IS_ERR(sma)) { 12458c2ecf20Sopenharmony_ci err = PTR_ERR(sma); 12468c2ecf20Sopenharmony_ci goto out_unlock; 12478c2ecf20Sopenharmony_ci } 12488c2ecf20Sopenharmony_ci } else { /* IPC_STAT */ 12498c2ecf20Sopenharmony_ci sma = sem_obtain_object_check(ns, semid); 12508c2ecf20Sopenharmony_ci if (IS_ERR(sma)) { 12518c2ecf20Sopenharmony_ci err = PTR_ERR(sma); 12528c2ecf20Sopenharmony_ci goto out_unlock; 12538c2ecf20Sopenharmony_ci } 12548c2ecf20Sopenharmony_ci } 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci /* see comment for SHM_STAT_ANY */ 12578c2ecf20Sopenharmony_ci if (cmd == SEM_STAT_ANY) 12588c2ecf20Sopenharmony_ci audit_ipc_obj(&sma->sem_perm); 12598c2ecf20Sopenharmony_ci else { 12608c2ecf20Sopenharmony_ci err = -EACCES; 12618c2ecf20Sopenharmony_ci if (ipcperms(ns, &sma->sem_perm, S_IRUGO)) 12628c2ecf20Sopenharmony_ci goto out_unlock; 12638c2ecf20Sopenharmony_ci } 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci err = security_sem_semctl(&sma->sem_perm, cmd); 12668c2ecf20Sopenharmony_ci if (err) 12678c2ecf20Sopenharmony_ci goto out_unlock; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci ipc_lock_object(&sma->sem_perm); 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) { 12728c2ecf20Sopenharmony_ci ipc_unlock_object(&sma->sem_perm); 12738c2ecf20Sopenharmony_ci err = -EIDRM; 12748c2ecf20Sopenharmony_ci goto out_unlock; 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci kernel_to_ipc64_perm(&sma->sem_perm, &semid64->sem_perm); 12788c2ecf20Sopenharmony_ci semotime = get_semotime(sma); 12798c2ecf20Sopenharmony_ci semid64->sem_otime = semotime; 12808c2ecf20Sopenharmony_ci semid64->sem_ctime = sma->sem_ctime; 12818c2ecf20Sopenharmony_ci#ifndef CONFIG_64BIT 12828c2ecf20Sopenharmony_ci semid64->sem_otime_high = semotime >> 32; 12838c2ecf20Sopenharmony_ci semid64->sem_ctime_high = sma->sem_ctime >> 32; 12848c2ecf20Sopenharmony_ci#endif 12858c2ecf20Sopenharmony_ci semid64->sem_nsems = sma->sem_nsems; 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci if (cmd == IPC_STAT) { 12888c2ecf20Sopenharmony_ci /* 12898c2ecf20Sopenharmony_ci * As defined in SUS: 12908c2ecf20Sopenharmony_ci * Return 0 on success 12918c2ecf20Sopenharmony_ci */ 12928c2ecf20Sopenharmony_ci err = 0; 12938c2ecf20Sopenharmony_ci } else { 12948c2ecf20Sopenharmony_ci /* 12958c2ecf20Sopenharmony_ci * SEM_STAT and SEM_STAT_ANY (both Linux specific) 12968c2ecf20Sopenharmony_ci * Return the full id, including the sequence number 12978c2ecf20Sopenharmony_ci */ 12988c2ecf20Sopenharmony_ci err = sma->sem_perm.id; 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci ipc_unlock_object(&sma->sem_perm); 13018c2ecf20Sopenharmony_ciout_unlock: 13028c2ecf20Sopenharmony_ci rcu_read_unlock(); 13038c2ecf20Sopenharmony_ci return err; 13048c2ecf20Sopenharmony_ci} 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_cistatic int semctl_info(struct ipc_namespace *ns, int semid, 13078c2ecf20Sopenharmony_ci int cmd, void __user *p) 13088c2ecf20Sopenharmony_ci{ 13098c2ecf20Sopenharmony_ci struct seminfo seminfo; 13108c2ecf20Sopenharmony_ci int max_idx; 13118c2ecf20Sopenharmony_ci int err; 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci err = security_sem_semctl(NULL, cmd); 13148c2ecf20Sopenharmony_ci if (err) 13158c2ecf20Sopenharmony_ci return err; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci memset(&seminfo, 0, sizeof(seminfo)); 13188c2ecf20Sopenharmony_ci seminfo.semmni = ns->sc_semmni; 13198c2ecf20Sopenharmony_ci seminfo.semmns = ns->sc_semmns; 13208c2ecf20Sopenharmony_ci seminfo.semmsl = ns->sc_semmsl; 13218c2ecf20Sopenharmony_ci seminfo.semopm = ns->sc_semopm; 13228c2ecf20Sopenharmony_ci seminfo.semvmx = SEMVMX; 13238c2ecf20Sopenharmony_ci seminfo.semmnu = SEMMNU; 13248c2ecf20Sopenharmony_ci seminfo.semmap = SEMMAP; 13258c2ecf20Sopenharmony_ci seminfo.semume = SEMUME; 13268c2ecf20Sopenharmony_ci down_read(&sem_ids(ns).rwsem); 13278c2ecf20Sopenharmony_ci if (cmd == SEM_INFO) { 13288c2ecf20Sopenharmony_ci seminfo.semusz = sem_ids(ns).in_use; 13298c2ecf20Sopenharmony_ci seminfo.semaem = ns->used_sems; 13308c2ecf20Sopenharmony_ci } else { 13318c2ecf20Sopenharmony_ci seminfo.semusz = SEMUSZ; 13328c2ecf20Sopenharmony_ci seminfo.semaem = SEMAEM; 13338c2ecf20Sopenharmony_ci } 13348c2ecf20Sopenharmony_ci max_idx = ipc_get_maxidx(&sem_ids(ns)); 13358c2ecf20Sopenharmony_ci up_read(&sem_ids(ns).rwsem); 13368c2ecf20Sopenharmony_ci if (copy_to_user(p, &seminfo, sizeof(struct seminfo))) 13378c2ecf20Sopenharmony_ci return -EFAULT; 13388c2ecf20Sopenharmony_ci return (max_idx < 0) ? 0 : max_idx; 13398c2ecf20Sopenharmony_ci} 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_cistatic int semctl_setval(struct ipc_namespace *ns, int semid, int semnum, 13428c2ecf20Sopenharmony_ci int val) 13438c2ecf20Sopenharmony_ci{ 13448c2ecf20Sopenharmony_ci struct sem_undo *un; 13458c2ecf20Sopenharmony_ci struct sem_array *sma; 13468c2ecf20Sopenharmony_ci struct sem *curr; 13478c2ecf20Sopenharmony_ci int err; 13488c2ecf20Sopenharmony_ci DEFINE_WAKE_Q(wake_q); 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci if (val > SEMVMX || val < 0) 13518c2ecf20Sopenharmony_ci return -ERANGE; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci rcu_read_lock(); 13548c2ecf20Sopenharmony_ci sma = sem_obtain_object_check(ns, semid); 13558c2ecf20Sopenharmony_ci if (IS_ERR(sma)) { 13568c2ecf20Sopenharmony_ci rcu_read_unlock(); 13578c2ecf20Sopenharmony_ci return PTR_ERR(sma); 13588c2ecf20Sopenharmony_ci } 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci if (semnum < 0 || semnum >= sma->sem_nsems) { 13618c2ecf20Sopenharmony_ci rcu_read_unlock(); 13628c2ecf20Sopenharmony_ci return -EINVAL; 13638c2ecf20Sopenharmony_ci } 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci if (ipcperms(ns, &sma->sem_perm, S_IWUGO)) { 13678c2ecf20Sopenharmony_ci rcu_read_unlock(); 13688c2ecf20Sopenharmony_ci return -EACCES; 13698c2ecf20Sopenharmony_ci } 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci err = security_sem_semctl(&sma->sem_perm, SETVAL); 13728c2ecf20Sopenharmony_ci if (err) { 13738c2ecf20Sopenharmony_ci rcu_read_unlock(); 13748c2ecf20Sopenharmony_ci return -EACCES; 13758c2ecf20Sopenharmony_ci } 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci sem_lock(sma, NULL, -1); 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) { 13808c2ecf20Sopenharmony_ci sem_unlock(sma, -1); 13818c2ecf20Sopenharmony_ci rcu_read_unlock(); 13828c2ecf20Sopenharmony_ci return -EIDRM; 13838c2ecf20Sopenharmony_ci } 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci semnum = array_index_nospec(semnum, sma->sem_nsems); 13868c2ecf20Sopenharmony_ci curr = &sma->sems[semnum]; 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci ipc_assert_locked_object(&sma->sem_perm); 13898c2ecf20Sopenharmony_ci list_for_each_entry(un, &sma->list_id, list_id) 13908c2ecf20Sopenharmony_ci un->semadj[semnum] = 0; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci curr->semval = val; 13938c2ecf20Sopenharmony_ci ipc_update_pid(&curr->sempid, task_tgid(current)); 13948c2ecf20Sopenharmony_ci sma->sem_ctime = ktime_get_real_seconds(); 13958c2ecf20Sopenharmony_ci /* maybe some queued-up processes were waiting for this */ 13968c2ecf20Sopenharmony_ci do_smart_update(sma, NULL, 0, 0, &wake_q); 13978c2ecf20Sopenharmony_ci sem_unlock(sma, -1); 13988c2ecf20Sopenharmony_ci rcu_read_unlock(); 13998c2ecf20Sopenharmony_ci wake_up_q(&wake_q); 14008c2ecf20Sopenharmony_ci return 0; 14018c2ecf20Sopenharmony_ci} 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_cistatic int semctl_main(struct ipc_namespace *ns, int semid, int semnum, 14048c2ecf20Sopenharmony_ci int cmd, void __user *p) 14058c2ecf20Sopenharmony_ci{ 14068c2ecf20Sopenharmony_ci struct sem_array *sma; 14078c2ecf20Sopenharmony_ci struct sem *curr; 14088c2ecf20Sopenharmony_ci int err, nsems; 14098c2ecf20Sopenharmony_ci ushort fast_sem_io[SEMMSL_FAST]; 14108c2ecf20Sopenharmony_ci ushort *sem_io = fast_sem_io; 14118c2ecf20Sopenharmony_ci DEFINE_WAKE_Q(wake_q); 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci rcu_read_lock(); 14148c2ecf20Sopenharmony_ci sma = sem_obtain_object_check(ns, semid); 14158c2ecf20Sopenharmony_ci if (IS_ERR(sma)) { 14168c2ecf20Sopenharmony_ci rcu_read_unlock(); 14178c2ecf20Sopenharmony_ci return PTR_ERR(sma); 14188c2ecf20Sopenharmony_ci } 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci nsems = sma->sem_nsems; 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci err = -EACCES; 14238c2ecf20Sopenharmony_ci if (ipcperms(ns, &sma->sem_perm, cmd == SETALL ? S_IWUGO : S_IRUGO)) 14248c2ecf20Sopenharmony_ci goto out_rcu_wakeup; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci err = security_sem_semctl(&sma->sem_perm, cmd); 14278c2ecf20Sopenharmony_ci if (err) 14288c2ecf20Sopenharmony_ci goto out_rcu_wakeup; 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci err = -EACCES; 14318c2ecf20Sopenharmony_ci switch (cmd) { 14328c2ecf20Sopenharmony_ci case GETALL: 14338c2ecf20Sopenharmony_ci { 14348c2ecf20Sopenharmony_ci ushort __user *array = p; 14358c2ecf20Sopenharmony_ci int i; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci sem_lock(sma, NULL, -1); 14388c2ecf20Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) { 14398c2ecf20Sopenharmony_ci err = -EIDRM; 14408c2ecf20Sopenharmony_ci goto out_unlock; 14418c2ecf20Sopenharmony_ci } 14428c2ecf20Sopenharmony_ci if (nsems > SEMMSL_FAST) { 14438c2ecf20Sopenharmony_ci if (!ipc_rcu_getref(&sma->sem_perm)) { 14448c2ecf20Sopenharmony_ci err = -EIDRM; 14458c2ecf20Sopenharmony_ci goto out_unlock; 14468c2ecf20Sopenharmony_ci } 14478c2ecf20Sopenharmony_ci sem_unlock(sma, -1); 14488c2ecf20Sopenharmony_ci rcu_read_unlock(); 14498c2ecf20Sopenharmony_ci sem_io = kvmalloc_array(nsems, sizeof(ushort), 14508c2ecf20Sopenharmony_ci GFP_KERNEL); 14518c2ecf20Sopenharmony_ci if (sem_io == NULL) { 14528c2ecf20Sopenharmony_ci ipc_rcu_putref(&sma->sem_perm, sem_rcu_free); 14538c2ecf20Sopenharmony_ci return -ENOMEM; 14548c2ecf20Sopenharmony_ci } 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci rcu_read_lock(); 14578c2ecf20Sopenharmony_ci sem_lock_and_putref(sma); 14588c2ecf20Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) { 14598c2ecf20Sopenharmony_ci err = -EIDRM; 14608c2ecf20Sopenharmony_ci goto out_unlock; 14618c2ecf20Sopenharmony_ci } 14628c2ecf20Sopenharmony_ci } 14638c2ecf20Sopenharmony_ci for (i = 0; i < sma->sem_nsems; i++) 14648c2ecf20Sopenharmony_ci sem_io[i] = sma->sems[i].semval; 14658c2ecf20Sopenharmony_ci sem_unlock(sma, -1); 14668c2ecf20Sopenharmony_ci rcu_read_unlock(); 14678c2ecf20Sopenharmony_ci err = 0; 14688c2ecf20Sopenharmony_ci if (copy_to_user(array, sem_io, nsems*sizeof(ushort))) 14698c2ecf20Sopenharmony_ci err = -EFAULT; 14708c2ecf20Sopenharmony_ci goto out_free; 14718c2ecf20Sopenharmony_ci } 14728c2ecf20Sopenharmony_ci case SETALL: 14738c2ecf20Sopenharmony_ci { 14748c2ecf20Sopenharmony_ci int i; 14758c2ecf20Sopenharmony_ci struct sem_undo *un; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci if (!ipc_rcu_getref(&sma->sem_perm)) { 14788c2ecf20Sopenharmony_ci err = -EIDRM; 14798c2ecf20Sopenharmony_ci goto out_rcu_wakeup; 14808c2ecf20Sopenharmony_ci } 14818c2ecf20Sopenharmony_ci rcu_read_unlock(); 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci if (nsems > SEMMSL_FAST) { 14848c2ecf20Sopenharmony_ci sem_io = kvmalloc_array(nsems, sizeof(ushort), 14858c2ecf20Sopenharmony_ci GFP_KERNEL); 14868c2ecf20Sopenharmony_ci if (sem_io == NULL) { 14878c2ecf20Sopenharmony_ci ipc_rcu_putref(&sma->sem_perm, sem_rcu_free); 14888c2ecf20Sopenharmony_ci return -ENOMEM; 14898c2ecf20Sopenharmony_ci } 14908c2ecf20Sopenharmony_ci } 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci if (copy_from_user(sem_io, p, nsems*sizeof(ushort))) { 14938c2ecf20Sopenharmony_ci ipc_rcu_putref(&sma->sem_perm, sem_rcu_free); 14948c2ecf20Sopenharmony_ci err = -EFAULT; 14958c2ecf20Sopenharmony_ci goto out_free; 14968c2ecf20Sopenharmony_ci } 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci for (i = 0; i < nsems; i++) { 14998c2ecf20Sopenharmony_ci if (sem_io[i] > SEMVMX) { 15008c2ecf20Sopenharmony_ci ipc_rcu_putref(&sma->sem_perm, sem_rcu_free); 15018c2ecf20Sopenharmony_ci err = -ERANGE; 15028c2ecf20Sopenharmony_ci goto out_free; 15038c2ecf20Sopenharmony_ci } 15048c2ecf20Sopenharmony_ci } 15058c2ecf20Sopenharmony_ci rcu_read_lock(); 15068c2ecf20Sopenharmony_ci sem_lock_and_putref(sma); 15078c2ecf20Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) { 15088c2ecf20Sopenharmony_ci err = -EIDRM; 15098c2ecf20Sopenharmony_ci goto out_unlock; 15108c2ecf20Sopenharmony_ci } 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci for (i = 0; i < nsems; i++) { 15138c2ecf20Sopenharmony_ci sma->sems[i].semval = sem_io[i]; 15148c2ecf20Sopenharmony_ci ipc_update_pid(&sma->sems[i].sempid, task_tgid(current)); 15158c2ecf20Sopenharmony_ci } 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci ipc_assert_locked_object(&sma->sem_perm); 15188c2ecf20Sopenharmony_ci list_for_each_entry(un, &sma->list_id, list_id) { 15198c2ecf20Sopenharmony_ci for (i = 0; i < nsems; i++) 15208c2ecf20Sopenharmony_ci un->semadj[i] = 0; 15218c2ecf20Sopenharmony_ci } 15228c2ecf20Sopenharmony_ci sma->sem_ctime = ktime_get_real_seconds(); 15238c2ecf20Sopenharmony_ci /* maybe some queued-up processes were waiting for this */ 15248c2ecf20Sopenharmony_ci do_smart_update(sma, NULL, 0, 0, &wake_q); 15258c2ecf20Sopenharmony_ci err = 0; 15268c2ecf20Sopenharmony_ci goto out_unlock; 15278c2ecf20Sopenharmony_ci } 15288c2ecf20Sopenharmony_ci /* GETVAL, GETPID, GETNCTN, GETZCNT: fall-through */ 15298c2ecf20Sopenharmony_ci } 15308c2ecf20Sopenharmony_ci err = -EINVAL; 15318c2ecf20Sopenharmony_ci if (semnum < 0 || semnum >= nsems) 15328c2ecf20Sopenharmony_ci goto out_rcu_wakeup; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci sem_lock(sma, NULL, -1); 15358c2ecf20Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) { 15368c2ecf20Sopenharmony_ci err = -EIDRM; 15378c2ecf20Sopenharmony_ci goto out_unlock; 15388c2ecf20Sopenharmony_ci } 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci semnum = array_index_nospec(semnum, nsems); 15418c2ecf20Sopenharmony_ci curr = &sma->sems[semnum]; 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci switch (cmd) { 15448c2ecf20Sopenharmony_ci case GETVAL: 15458c2ecf20Sopenharmony_ci err = curr->semval; 15468c2ecf20Sopenharmony_ci goto out_unlock; 15478c2ecf20Sopenharmony_ci case GETPID: 15488c2ecf20Sopenharmony_ci err = pid_vnr(curr->sempid); 15498c2ecf20Sopenharmony_ci goto out_unlock; 15508c2ecf20Sopenharmony_ci case GETNCNT: 15518c2ecf20Sopenharmony_ci err = count_semcnt(sma, semnum, 0); 15528c2ecf20Sopenharmony_ci goto out_unlock; 15538c2ecf20Sopenharmony_ci case GETZCNT: 15548c2ecf20Sopenharmony_ci err = count_semcnt(sma, semnum, 1); 15558c2ecf20Sopenharmony_ci goto out_unlock; 15568c2ecf20Sopenharmony_ci } 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ciout_unlock: 15598c2ecf20Sopenharmony_ci sem_unlock(sma, -1); 15608c2ecf20Sopenharmony_ciout_rcu_wakeup: 15618c2ecf20Sopenharmony_ci rcu_read_unlock(); 15628c2ecf20Sopenharmony_ci wake_up_q(&wake_q); 15638c2ecf20Sopenharmony_ciout_free: 15648c2ecf20Sopenharmony_ci if (sem_io != fast_sem_io) 15658c2ecf20Sopenharmony_ci kvfree(sem_io); 15668c2ecf20Sopenharmony_ci return err; 15678c2ecf20Sopenharmony_ci} 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_cistatic inline unsigned long 15708c2ecf20Sopenharmony_cicopy_semid_from_user(struct semid64_ds *out, void __user *buf, int version) 15718c2ecf20Sopenharmony_ci{ 15728c2ecf20Sopenharmony_ci switch (version) { 15738c2ecf20Sopenharmony_ci case IPC_64: 15748c2ecf20Sopenharmony_ci if (copy_from_user(out, buf, sizeof(*out))) 15758c2ecf20Sopenharmony_ci return -EFAULT; 15768c2ecf20Sopenharmony_ci return 0; 15778c2ecf20Sopenharmony_ci case IPC_OLD: 15788c2ecf20Sopenharmony_ci { 15798c2ecf20Sopenharmony_ci struct semid_ds tbuf_old; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old))) 15828c2ecf20Sopenharmony_ci return -EFAULT; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci out->sem_perm.uid = tbuf_old.sem_perm.uid; 15858c2ecf20Sopenharmony_ci out->sem_perm.gid = tbuf_old.sem_perm.gid; 15868c2ecf20Sopenharmony_ci out->sem_perm.mode = tbuf_old.sem_perm.mode; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci return 0; 15898c2ecf20Sopenharmony_ci } 15908c2ecf20Sopenharmony_ci default: 15918c2ecf20Sopenharmony_ci return -EINVAL; 15928c2ecf20Sopenharmony_ci } 15938c2ecf20Sopenharmony_ci} 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci/* 15968c2ecf20Sopenharmony_ci * This function handles some semctl commands which require the rwsem 15978c2ecf20Sopenharmony_ci * to be held in write mode. 15988c2ecf20Sopenharmony_ci * NOTE: no locks must be held, the rwsem is taken inside this function. 15998c2ecf20Sopenharmony_ci */ 16008c2ecf20Sopenharmony_cistatic int semctl_down(struct ipc_namespace *ns, int semid, 16018c2ecf20Sopenharmony_ci int cmd, struct semid64_ds *semid64) 16028c2ecf20Sopenharmony_ci{ 16038c2ecf20Sopenharmony_ci struct sem_array *sma; 16048c2ecf20Sopenharmony_ci int err; 16058c2ecf20Sopenharmony_ci struct kern_ipc_perm *ipcp; 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci down_write(&sem_ids(ns).rwsem); 16088c2ecf20Sopenharmony_ci rcu_read_lock(); 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci ipcp = ipcctl_obtain_check(ns, &sem_ids(ns), semid, cmd, 16118c2ecf20Sopenharmony_ci &semid64->sem_perm, 0); 16128c2ecf20Sopenharmony_ci if (IS_ERR(ipcp)) { 16138c2ecf20Sopenharmony_ci err = PTR_ERR(ipcp); 16148c2ecf20Sopenharmony_ci goto out_unlock1; 16158c2ecf20Sopenharmony_ci } 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci sma = container_of(ipcp, struct sem_array, sem_perm); 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci err = security_sem_semctl(&sma->sem_perm, cmd); 16208c2ecf20Sopenharmony_ci if (err) 16218c2ecf20Sopenharmony_ci goto out_unlock1; 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci switch (cmd) { 16248c2ecf20Sopenharmony_ci case IPC_RMID: 16258c2ecf20Sopenharmony_ci sem_lock(sma, NULL, -1); 16268c2ecf20Sopenharmony_ci /* freeary unlocks the ipc object and rcu */ 16278c2ecf20Sopenharmony_ci freeary(ns, ipcp); 16288c2ecf20Sopenharmony_ci goto out_up; 16298c2ecf20Sopenharmony_ci case IPC_SET: 16308c2ecf20Sopenharmony_ci sem_lock(sma, NULL, -1); 16318c2ecf20Sopenharmony_ci err = ipc_update_perm(&semid64->sem_perm, ipcp); 16328c2ecf20Sopenharmony_ci if (err) 16338c2ecf20Sopenharmony_ci goto out_unlock0; 16348c2ecf20Sopenharmony_ci sma->sem_ctime = ktime_get_real_seconds(); 16358c2ecf20Sopenharmony_ci break; 16368c2ecf20Sopenharmony_ci default: 16378c2ecf20Sopenharmony_ci err = -EINVAL; 16388c2ecf20Sopenharmony_ci goto out_unlock1; 16398c2ecf20Sopenharmony_ci } 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ciout_unlock0: 16428c2ecf20Sopenharmony_ci sem_unlock(sma, -1); 16438c2ecf20Sopenharmony_ciout_unlock1: 16448c2ecf20Sopenharmony_ci rcu_read_unlock(); 16458c2ecf20Sopenharmony_ciout_up: 16468c2ecf20Sopenharmony_ci up_write(&sem_ids(ns).rwsem); 16478c2ecf20Sopenharmony_ci return err; 16488c2ecf20Sopenharmony_ci} 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_cistatic long ksys_semctl(int semid, int semnum, int cmd, unsigned long arg, int version) 16518c2ecf20Sopenharmony_ci{ 16528c2ecf20Sopenharmony_ci struct ipc_namespace *ns; 16538c2ecf20Sopenharmony_ci void __user *p = (void __user *)arg; 16548c2ecf20Sopenharmony_ci struct semid64_ds semid64; 16558c2ecf20Sopenharmony_ci int err; 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci if (semid < 0) 16588c2ecf20Sopenharmony_ci return -EINVAL; 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci ns = current->nsproxy->ipc_ns; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci switch (cmd) { 16638c2ecf20Sopenharmony_ci case IPC_INFO: 16648c2ecf20Sopenharmony_ci case SEM_INFO: 16658c2ecf20Sopenharmony_ci return semctl_info(ns, semid, cmd, p); 16668c2ecf20Sopenharmony_ci case IPC_STAT: 16678c2ecf20Sopenharmony_ci case SEM_STAT: 16688c2ecf20Sopenharmony_ci case SEM_STAT_ANY: 16698c2ecf20Sopenharmony_ci err = semctl_stat(ns, semid, cmd, &semid64); 16708c2ecf20Sopenharmony_ci if (err < 0) 16718c2ecf20Sopenharmony_ci return err; 16728c2ecf20Sopenharmony_ci if (copy_semid_to_user(p, &semid64, version)) 16738c2ecf20Sopenharmony_ci err = -EFAULT; 16748c2ecf20Sopenharmony_ci return err; 16758c2ecf20Sopenharmony_ci case GETALL: 16768c2ecf20Sopenharmony_ci case GETVAL: 16778c2ecf20Sopenharmony_ci case GETPID: 16788c2ecf20Sopenharmony_ci case GETNCNT: 16798c2ecf20Sopenharmony_ci case GETZCNT: 16808c2ecf20Sopenharmony_ci case SETALL: 16818c2ecf20Sopenharmony_ci return semctl_main(ns, semid, semnum, cmd, p); 16828c2ecf20Sopenharmony_ci case SETVAL: { 16838c2ecf20Sopenharmony_ci int val; 16848c2ecf20Sopenharmony_ci#if defined(CONFIG_64BIT) && defined(__BIG_ENDIAN) 16858c2ecf20Sopenharmony_ci /* big-endian 64bit */ 16868c2ecf20Sopenharmony_ci val = arg >> 32; 16878c2ecf20Sopenharmony_ci#else 16888c2ecf20Sopenharmony_ci /* 32bit or little-endian 64bit */ 16898c2ecf20Sopenharmony_ci val = arg; 16908c2ecf20Sopenharmony_ci#endif 16918c2ecf20Sopenharmony_ci return semctl_setval(ns, semid, semnum, val); 16928c2ecf20Sopenharmony_ci } 16938c2ecf20Sopenharmony_ci case IPC_SET: 16948c2ecf20Sopenharmony_ci if (copy_semid_from_user(&semid64, p, version)) 16958c2ecf20Sopenharmony_ci return -EFAULT; 16968c2ecf20Sopenharmony_ci fallthrough; 16978c2ecf20Sopenharmony_ci case IPC_RMID: 16988c2ecf20Sopenharmony_ci return semctl_down(ns, semid, cmd, &semid64); 16998c2ecf20Sopenharmony_ci default: 17008c2ecf20Sopenharmony_ci return -EINVAL; 17018c2ecf20Sopenharmony_ci } 17028c2ecf20Sopenharmony_ci} 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ciSYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg) 17058c2ecf20Sopenharmony_ci{ 17068c2ecf20Sopenharmony_ci return ksys_semctl(semid, semnum, cmd, arg, IPC_64); 17078c2ecf20Sopenharmony_ci} 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_WANT_IPC_PARSE_VERSION 17108c2ecf20Sopenharmony_cilong ksys_old_semctl(int semid, int semnum, int cmd, unsigned long arg) 17118c2ecf20Sopenharmony_ci{ 17128c2ecf20Sopenharmony_ci int version = ipc_parse_version(&cmd); 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci return ksys_semctl(semid, semnum, cmd, arg, version); 17158c2ecf20Sopenharmony_ci} 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ciSYSCALL_DEFINE4(old_semctl, int, semid, int, semnum, int, cmd, unsigned long, arg) 17188c2ecf20Sopenharmony_ci{ 17198c2ecf20Sopenharmony_ci return ksys_old_semctl(semid, semnum, cmd, arg); 17208c2ecf20Sopenharmony_ci} 17218c2ecf20Sopenharmony_ci#endif 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_cistruct compat_semid_ds { 17268c2ecf20Sopenharmony_ci struct compat_ipc_perm sem_perm; 17278c2ecf20Sopenharmony_ci old_time32_t sem_otime; 17288c2ecf20Sopenharmony_ci old_time32_t sem_ctime; 17298c2ecf20Sopenharmony_ci compat_uptr_t sem_base; 17308c2ecf20Sopenharmony_ci compat_uptr_t sem_pending; 17318c2ecf20Sopenharmony_ci compat_uptr_t sem_pending_last; 17328c2ecf20Sopenharmony_ci compat_uptr_t undo; 17338c2ecf20Sopenharmony_ci unsigned short sem_nsems; 17348c2ecf20Sopenharmony_ci}; 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_cistatic int copy_compat_semid_from_user(struct semid64_ds *out, void __user *buf, 17378c2ecf20Sopenharmony_ci int version) 17388c2ecf20Sopenharmony_ci{ 17398c2ecf20Sopenharmony_ci memset(out, 0, sizeof(*out)); 17408c2ecf20Sopenharmony_ci if (version == IPC_64) { 17418c2ecf20Sopenharmony_ci struct compat_semid64_ds __user *p = buf; 17428c2ecf20Sopenharmony_ci return get_compat_ipc64_perm(&out->sem_perm, &p->sem_perm); 17438c2ecf20Sopenharmony_ci } else { 17448c2ecf20Sopenharmony_ci struct compat_semid_ds __user *p = buf; 17458c2ecf20Sopenharmony_ci return get_compat_ipc_perm(&out->sem_perm, &p->sem_perm); 17468c2ecf20Sopenharmony_ci } 17478c2ecf20Sopenharmony_ci} 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_cistatic int copy_compat_semid_to_user(void __user *buf, struct semid64_ds *in, 17508c2ecf20Sopenharmony_ci int version) 17518c2ecf20Sopenharmony_ci{ 17528c2ecf20Sopenharmony_ci if (version == IPC_64) { 17538c2ecf20Sopenharmony_ci struct compat_semid64_ds v; 17548c2ecf20Sopenharmony_ci memset(&v, 0, sizeof(v)); 17558c2ecf20Sopenharmony_ci to_compat_ipc64_perm(&v.sem_perm, &in->sem_perm); 17568c2ecf20Sopenharmony_ci v.sem_otime = lower_32_bits(in->sem_otime); 17578c2ecf20Sopenharmony_ci v.sem_otime_high = upper_32_bits(in->sem_otime); 17588c2ecf20Sopenharmony_ci v.sem_ctime = lower_32_bits(in->sem_ctime); 17598c2ecf20Sopenharmony_ci v.sem_ctime_high = upper_32_bits(in->sem_ctime); 17608c2ecf20Sopenharmony_ci v.sem_nsems = in->sem_nsems; 17618c2ecf20Sopenharmony_ci return copy_to_user(buf, &v, sizeof(v)); 17628c2ecf20Sopenharmony_ci } else { 17638c2ecf20Sopenharmony_ci struct compat_semid_ds v; 17648c2ecf20Sopenharmony_ci memset(&v, 0, sizeof(v)); 17658c2ecf20Sopenharmony_ci to_compat_ipc_perm(&v.sem_perm, &in->sem_perm); 17668c2ecf20Sopenharmony_ci v.sem_otime = in->sem_otime; 17678c2ecf20Sopenharmony_ci v.sem_ctime = in->sem_ctime; 17688c2ecf20Sopenharmony_ci v.sem_nsems = in->sem_nsems; 17698c2ecf20Sopenharmony_ci return copy_to_user(buf, &v, sizeof(v)); 17708c2ecf20Sopenharmony_ci } 17718c2ecf20Sopenharmony_ci} 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_cistatic long compat_ksys_semctl(int semid, int semnum, int cmd, int arg, int version) 17748c2ecf20Sopenharmony_ci{ 17758c2ecf20Sopenharmony_ci void __user *p = compat_ptr(arg); 17768c2ecf20Sopenharmony_ci struct ipc_namespace *ns; 17778c2ecf20Sopenharmony_ci struct semid64_ds semid64; 17788c2ecf20Sopenharmony_ci int err; 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci ns = current->nsproxy->ipc_ns; 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci if (semid < 0) 17838c2ecf20Sopenharmony_ci return -EINVAL; 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci switch (cmd & (~IPC_64)) { 17868c2ecf20Sopenharmony_ci case IPC_INFO: 17878c2ecf20Sopenharmony_ci case SEM_INFO: 17888c2ecf20Sopenharmony_ci return semctl_info(ns, semid, cmd, p); 17898c2ecf20Sopenharmony_ci case IPC_STAT: 17908c2ecf20Sopenharmony_ci case SEM_STAT: 17918c2ecf20Sopenharmony_ci case SEM_STAT_ANY: 17928c2ecf20Sopenharmony_ci err = semctl_stat(ns, semid, cmd, &semid64); 17938c2ecf20Sopenharmony_ci if (err < 0) 17948c2ecf20Sopenharmony_ci return err; 17958c2ecf20Sopenharmony_ci if (copy_compat_semid_to_user(p, &semid64, version)) 17968c2ecf20Sopenharmony_ci err = -EFAULT; 17978c2ecf20Sopenharmony_ci return err; 17988c2ecf20Sopenharmony_ci case GETVAL: 17998c2ecf20Sopenharmony_ci case GETPID: 18008c2ecf20Sopenharmony_ci case GETNCNT: 18018c2ecf20Sopenharmony_ci case GETZCNT: 18028c2ecf20Sopenharmony_ci case GETALL: 18038c2ecf20Sopenharmony_ci case SETALL: 18048c2ecf20Sopenharmony_ci return semctl_main(ns, semid, semnum, cmd, p); 18058c2ecf20Sopenharmony_ci case SETVAL: 18068c2ecf20Sopenharmony_ci return semctl_setval(ns, semid, semnum, arg); 18078c2ecf20Sopenharmony_ci case IPC_SET: 18088c2ecf20Sopenharmony_ci if (copy_compat_semid_from_user(&semid64, p, version)) 18098c2ecf20Sopenharmony_ci return -EFAULT; 18108c2ecf20Sopenharmony_ci fallthrough; 18118c2ecf20Sopenharmony_ci case IPC_RMID: 18128c2ecf20Sopenharmony_ci return semctl_down(ns, semid, cmd, &semid64); 18138c2ecf20Sopenharmony_ci default: 18148c2ecf20Sopenharmony_ci return -EINVAL; 18158c2ecf20Sopenharmony_ci } 18168c2ecf20Sopenharmony_ci} 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ciCOMPAT_SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, int, arg) 18198c2ecf20Sopenharmony_ci{ 18208c2ecf20Sopenharmony_ci return compat_ksys_semctl(semid, semnum, cmd, arg, IPC_64); 18218c2ecf20Sopenharmony_ci} 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION 18248c2ecf20Sopenharmony_cilong compat_ksys_old_semctl(int semid, int semnum, int cmd, int arg) 18258c2ecf20Sopenharmony_ci{ 18268c2ecf20Sopenharmony_ci int version = compat_ipc_parse_version(&cmd); 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci return compat_ksys_semctl(semid, semnum, cmd, arg, version); 18298c2ecf20Sopenharmony_ci} 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ciCOMPAT_SYSCALL_DEFINE4(old_semctl, int, semid, int, semnum, int, cmd, int, arg) 18328c2ecf20Sopenharmony_ci{ 18338c2ecf20Sopenharmony_ci return compat_ksys_old_semctl(semid, semnum, cmd, arg); 18348c2ecf20Sopenharmony_ci} 18358c2ecf20Sopenharmony_ci#endif 18368c2ecf20Sopenharmony_ci#endif 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_ci/* If the task doesn't already have a undo_list, then allocate one 18398c2ecf20Sopenharmony_ci * here. We guarantee there is only one thread using this undo list, 18408c2ecf20Sopenharmony_ci * and current is THE ONE 18418c2ecf20Sopenharmony_ci * 18428c2ecf20Sopenharmony_ci * If this allocation and assignment succeeds, but later 18438c2ecf20Sopenharmony_ci * portions of this code fail, there is no need to free the sem_undo_list. 18448c2ecf20Sopenharmony_ci * Just let it stay associated with the task, and it'll be freed later 18458c2ecf20Sopenharmony_ci * at exit time. 18468c2ecf20Sopenharmony_ci * 18478c2ecf20Sopenharmony_ci * This can block, so callers must hold no locks. 18488c2ecf20Sopenharmony_ci */ 18498c2ecf20Sopenharmony_cistatic inline int get_undo_list(struct sem_undo_list **undo_listp) 18508c2ecf20Sopenharmony_ci{ 18518c2ecf20Sopenharmony_ci struct sem_undo_list *undo_list; 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci undo_list = current->sysvsem.undo_list; 18548c2ecf20Sopenharmony_ci if (!undo_list) { 18558c2ecf20Sopenharmony_ci undo_list = kzalloc(sizeof(*undo_list), GFP_KERNEL_ACCOUNT); 18568c2ecf20Sopenharmony_ci if (undo_list == NULL) 18578c2ecf20Sopenharmony_ci return -ENOMEM; 18588c2ecf20Sopenharmony_ci spin_lock_init(&undo_list->lock); 18598c2ecf20Sopenharmony_ci refcount_set(&undo_list->refcnt, 1); 18608c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&undo_list->list_proc); 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci current->sysvsem.undo_list = undo_list; 18638c2ecf20Sopenharmony_ci } 18648c2ecf20Sopenharmony_ci *undo_listp = undo_list; 18658c2ecf20Sopenharmony_ci return 0; 18668c2ecf20Sopenharmony_ci} 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_cistatic struct sem_undo *__lookup_undo(struct sem_undo_list *ulp, int semid) 18698c2ecf20Sopenharmony_ci{ 18708c2ecf20Sopenharmony_ci struct sem_undo *un; 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci list_for_each_entry_rcu(un, &ulp->list_proc, list_proc, 18738c2ecf20Sopenharmony_ci spin_is_locked(&ulp->lock)) { 18748c2ecf20Sopenharmony_ci if (un->semid == semid) 18758c2ecf20Sopenharmony_ci return un; 18768c2ecf20Sopenharmony_ci } 18778c2ecf20Sopenharmony_ci return NULL; 18788c2ecf20Sopenharmony_ci} 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_cistatic struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid) 18818c2ecf20Sopenharmony_ci{ 18828c2ecf20Sopenharmony_ci struct sem_undo *un; 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci assert_spin_locked(&ulp->lock); 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci un = __lookup_undo(ulp, semid); 18878c2ecf20Sopenharmony_ci if (un) { 18888c2ecf20Sopenharmony_ci list_del_rcu(&un->list_proc); 18898c2ecf20Sopenharmony_ci list_add_rcu(&un->list_proc, &ulp->list_proc); 18908c2ecf20Sopenharmony_ci } 18918c2ecf20Sopenharmony_ci return un; 18928c2ecf20Sopenharmony_ci} 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci/** 18958c2ecf20Sopenharmony_ci * find_alloc_undo - lookup (and if not present create) undo array 18968c2ecf20Sopenharmony_ci * @ns: namespace 18978c2ecf20Sopenharmony_ci * @semid: semaphore array id 18988c2ecf20Sopenharmony_ci * 18998c2ecf20Sopenharmony_ci * The function looks up (and if not present creates) the undo structure. 19008c2ecf20Sopenharmony_ci * The size of the undo structure depends on the size of the semaphore 19018c2ecf20Sopenharmony_ci * array, thus the alloc path is not that straightforward. 19028c2ecf20Sopenharmony_ci * Lifetime-rules: sem_undo is rcu-protected, on success, the function 19038c2ecf20Sopenharmony_ci * performs a rcu_read_lock(). 19048c2ecf20Sopenharmony_ci */ 19058c2ecf20Sopenharmony_cistatic struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid) 19068c2ecf20Sopenharmony_ci{ 19078c2ecf20Sopenharmony_ci struct sem_array *sma; 19088c2ecf20Sopenharmony_ci struct sem_undo_list *ulp; 19098c2ecf20Sopenharmony_ci struct sem_undo *un, *new; 19108c2ecf20Sopenharmony_ci int nsems, error; 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci error = get_undo_list(&ulp); 19138c2ecf20Sopenharmony_ci if (error) 19148c2ecf20Sopenharmony_ci return ERR_PTR(error); 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci rcu_read_lock(); 19178c2ecf20Sopenharmony_ci spin_lock(&ulp->lock); 19188c2ecf20Sopenharmony_ci un = lookup_undo(ulp, semid); 19198c2ecf20Sopenharmony_ci spin_unlock(&ulp->lock); 19208c2ecf20Sopenharmony_ci if (likely(un != NULL)) 19218c2ecf20Sopenharmony_ci goto out; 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci /* no undo structure around - allocate one. */ 19248c2ecf20Sopenharmony_ci /* step 1: figure out the size of the semaphore array */ 19258c2ecf20Sopenharmony_ci sma = sem_obtain_object_check(ns, semid); 19268c2ecf20Sopenharmony_ci if (IS_ERR(sma)) { 19278c2ecf20Sopenharmony_ci rcu_read_unlock(); 19288c2ecf20Sopenharmony_ci return ERR_CAST(sma); 19298c2ecf20Sopenharmony_ci } 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci nsems = sma->sem_nsems; 19328c2ecf20Sopenharmony_ci if (!ipc_rcu_getref(&sma->sem_perm)) { 19338c2ecf20Sopenharmony_ci rcu_read_unlock(); 19348c2ecf20Sopenharmony_ci un = ERR_PTR(-EIDRM); 19358c2ecf20Sopenharmony_ci goto out; 19368c2ecf20Sopenharmony_ci } 19378c2ecf20Sopenharmony_ci rcu_read_unlock(); 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci /* step 2: allocate new undo structure */ 19408c2ecf20Sopenharmony_ci new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL_ACCOUNT); 19418c2ecf20Sopenharmony_ci if (!new) { 19428c2ecf20Sopenharmony_ci ipc_rcu_putref(&sma->sem_perm, sem_rcu_free); 19438c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 19448c2ecf20Sopenharmony_ci } 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci /* step 3: Acquire the lock on semaphore array */ 19478c2ecf20Sopenharmony_ci rcu_read_lock(); 19488c2ecf20Sopenharmony_ci sem_lock_and_putref(sma); 19498c2ecf20Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) { 19508c2ecf20Sopenharmony_ci sem_unlock(sma, -1); 19518c2ecf20Sopenharmony_ci rcu_read_unlock(); 19528c2ecf20Sopenharmony_ci kfree(new); 19538c2ecf20Sopenharmony_ci un = ERR_PTR(-EIDRM); 19548c2ecf20Sopenharmony_ci goto out; 19558c2ecf20Sopenharmony_ci } 19568c2ecf20Sopenharmony_ci spin_lock(&ulp->lock); 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci /* 19598c2ecf20Sopenharmony_ci * step 4: check for races: did someone else allocate the undo struct? 19608c2ecf20Sopenharmony_ci */ 19618c2ecf20Sopenharmony_ci un = lookup_undo(ulp, semid); 19628c2ecf20Sopenharmony_ci if (un) { 19638c2ecf20Sopenharmony_ci kfree(new); 19648c2ecf20Sopenharmony_ci goto success; 19658c2ecf20Sopenharmony_ci } 19668c2ecf20Sopenharmony_ci /* step 5: initialize & link new undo structure */ 19678c2ecf20Sopenharmony_ci new->semadj = (short *) &new[1]; 19688c2ecf20Sopenharmony_ci new->ulp = ulp; 19698c2ecf20Sopenharmony_ci new->semid = semid; 19708c2ecf20Sopenharmony_ci assert_spin_locked(&ulp->lock); 19718c2ecf20Sopenharmony_ci list_add_rcu(&new->list_proc, &ulp->list_proc); 19728c2ecf20Sopenharmony_ci ipc_assert_locked_object(&sma->sem_perm); 19738c2ecf20Sopenharmony_ci list_add(&new->list_id, &sma->list_id); 19748c2ecf20Sopenharmony_ci un = new; 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_cisuccess: 19778c2ecf20Sopenharmony_ci spin_unlock(&ulp->lock); 19788c2ecf20Sopenharmony_ci sem_unlock(sma, -1); 19798c2ecf20Sopenharmony_ciout: 19808c2ecf20Sopenharmony_ci return un; 19818c2ecf20Sopenharmony_ci} 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_cistatic long do_semtimedop(int semid, struct sembuf __user *tsops, 19848c2ecf20Sopenharmony_ci unsigned nsops, const struct timespec64 *timeout) 19858c2ecf20Sopenharmony_ci{ 19868c2ecf20Sopenharmony_ci int error = -EINVAL; 19878c2ecf20Sopenharmony_ci struct sem_array *sma; 19888c2ecf20Sopenharmony_ci struct sembuf fast_sops[SEMOPM_FAST]; 19898c2ecf20Sopenharmony_ci struct sembuf *sops = fast_sops, *sop; 19908c2ecf20Sopenharmony_ci struct sem_undo *un; 19918c2ecf20Sopenharmony_ci int max, locknum; 19928c2ecf20Sopenharmony_ci bool undos = false, alter = false, dupsop = false; 19938c2ecf20Sopenharmony_ci struct sem_queue queue; 19948c2ecf20Sopenharmony_ci unsigned long dup = 0, jiffies_left = 0; 19958c2ecf20Sopenharmony_ci struct ipc_namespace *ns; 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci ns = current->nsproxy->ipc_ns; 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci if (nsops < 1 || semid < 0) 20008c2ecf20Sopenharmony_ci return -EINVAL; 20018c2ecf20Sopenharmony_ci if (nsops > ns->sc_semopm) 20028c2ecf20Sopenharmony_ci return -E2BIG; 20038c2ecf20Sopenharmony_ci if (nsops > SEMOPM_FAST) { 20048c2ecf20Sopenharmony_ci sops = kvmalloc_array(nsops, sizeof(*sops), GFP_KERNEL); 20058c2ecf20Sopenharmony_ci if (sops == NULL) 20068c2ecf20Sopenharmony_ci return -ENOMEM; 20078c2ecf20Sopenharmony_ci } 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci if (copy_from_user(sops, tsops, nsops * sizeof(*tsops))) { 20108c2ecf20Sopenharmony_ci error = -EFAULT; 20118c2ecf20Sopenharmony_ci goto out_free; 20128c2ecf20Sopenharmony_ci } 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci if (timeout) { 20158c2ecf20Sopenharmony_ci if (timeout->tv_sec < 0 || timeout->tv_nsec < 0 || 20168c2ecf20Sopenharmony_ci timeout->tv_nsec >= 1000000000L) { 20178c2ecf20Sopenharmony_ci error = -EINVAL; 20188c2ecf20Sopenharmony_ci goto out_free; 20198c2ecf20Sopenharmony_ci } 20208c2ecf20Sopenharmony_ci jiffies_left = timespec64_to_jiffies(timeout); 20218c2ecf20Sopenharmony_ci } 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci max = 0; 20248c2ecf20Sopenharmony_ci for (sop = sops; sop < sops + nsops; sop++) { 20258c2ecf20Sopenharmony_ci unsigned long mask = 1ULL << ((sop->sem_num) % BITS_PER_LONG); 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci if (sop->sem_num >= max) 20288c2ecf20Sopenharmony_ci max = sop->sem_num; 20298c2ecf20Sopenharmony_ci if (sop->sem_flg & SEM_UNDO) 20308c2ecf20Sopenharmony_ci undos = true; 20318c2ecf20Sopenharmony_ci if (dup & mask) { 20328c2ecf20Sopenharmony_ci /* 20338c2ecf20Sopenharmony_ci * There was a previous alter access that appears 20348c2ecf20Sopenharmony_ci * to have accessed the same semaphore, thus use 20358c2ecf20Sopenharmony_ci * the dupsop logic. "appears", because the detection 20368c2ecf20Sopenharmony_ci * can only check % BITS_PER_LONG. 20378c2ecf20Sopenharmony_ci */ 20388c2ecf20Sopenharmony_ci dupsop = true; 20398c2ecf20Sopenharmony_ci } 20408c2ecf20Sopenharmony_ci if (sop->sem_op != 0) { 20418c2ecf20Sopenharmony_ci alter = true; 20428c2ecf20Sopenharmony_ci dup |= mask; 20438c2ecf20Sopenharmony_ci } 20448c2ecf20Sopenharmony_ci } 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci if (undos) { 20478c2ecf20Sopenharmony_ci /* On success, find_alloc_undo takes the rcu_read_lock */ 20488c2ecf20Sopenharmony_ci un = find_alloc_undo(ns, semid); 20498c2ecf20Sopenharmony_ci if (IS_ERR(un)) { 20508c2ecf20Sopenharmony_ci error = PTR_ERR(un); 20518c2ecf20Sopenharmony_ci goto out_free; 20528c2ecf20Sopenharmony_ci } 20538c2ecf20Sopenharmony_ci } else { 20548c2ecf20Sopenharmony_ci un = NULL; 20558c2ecf20Sopenharmony_ci rcu_read_lock(); 20568c2ecf20Sopenharmony_ci } 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci sma = sem_obtain_object_check(ns, semid); 20598c2ecf20Sopenharmony_ci if (IS_ERR(sma)) { 20608c2ecf20Sopenharmony_ci rcu_read_unlock(); 20618c2ecf20Sopenharmony_ci error = PTR_ERR(sma); 20628c2ecf20Sopenharmony_ci goto out_free; 20638c2ecf20Sopenharmony_ci } 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci error = -EFBIG; 20668c2ecf20Sopenharmony_ci if (max >= sma->sem_nsems) { 20678c2ecf20Sopenharmony_ci rcu_read_unlock(); 20688c2ecf20Sopenharmony_ci goto out_free; 20698c2ecf20Sopenharmony_ci } 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci error = -EACCES; 20728c2ecf20Sopenharmony_ci if (ipcperms(ns, &sma->sem_perm, alter ? S_IWUGO : S_IRUGO)) { 20738c2ecf20Sopenharmony_ci rcu_read_unlock(); 20748c2ecf20Sopenharmony_ci goto out_free; 20758c2ecf20Sopenharmony_ci } 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci error = security_sem_semop(&sma->sem_perm, sops, nsops, alter); 20788c2ecf20Sopenharmony_ci if (error) { 20798c2ecf20Sopenharmony_ci rcu_read_unlock(); 20808c2ecf20Sopenharmony_ci goto out_free; 20818c2ecf20Sopenharmony_ci } 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci error = -EIDRM; 20848c2ecf20Sopenharmony_ci locknum = sem_lock(sma, sops, nsops); 20858c2ecf20Sopenharmony_ci /* 20868c2ecf20Sopenharmony_ci * We eventually might perform the following check in a lockless 20878c2ecf20Sopenharmony_ci * fashion, considering ipc_valid_object() locking constraints. 20888c2ecf20Sopenharmony_ci * If nsops == 1 and there is no contention for sem_perm.lock, then 20898c2ecf20Sopenharmony_ci * only a per-semaphore lock is held and it's OK to proceed with the 20908c2ecf20Sopenharmony_ci * check below. More details on the fine grained locking scheme 20918c2ecf20Sopenharmony_ci * entangled here and why it's RMID race safe on comments at sem_lock() 20928c2ecf20Sopenharmony_ci */ 20938c2ecf20Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) 20948c2ecf20Sopenharmony_ci goto out_unlock_free; 20958c2ecf20Sopenharmony_ci /* 20968c2ecf20Sopenharmony_ci * semid identifiers are not unique - find_alloc_undo may have 20978c2ecf20Sopenharmony_ci * allocated an undo structure, it was invalidated by an RMID 20988c2ecf20Sopenharmony_ci * and now a new array with received the same id. Check and fail. 20998c2ecf20Sopenharmony_ci * This case can be detected checking un->semid. The existence of 21008c2ecf20Sopenharmony_ci * "un" itself is guaranteed by rcu. 21018c2ecf20Sopenharmony_ci */ 21028c2ecf20Sopenharmony_ci if (un && un->semid == -1) 21038c2ecf20Sopenharmony_ci goto out_unlock_free; 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci queue.sops = sops; 21068c2ecf20Sopenharmony_ci queue.nsops = nsops; 21078c2ecf20Sopenharmony_ci queue.undo = un; 21088c2ecf20Sopenharmony_ci queue.pid = task_tgid(current); 21098c2ecf20Sopenharmony_ci queue.alter = alter; 21108c2ecf20Sopenharmony_ci queue.dupsop = dupsop; 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci error = perform_atomic_semop(sma, &queue); 21138c2ecf20Sopenharmony_ci if (error == 0) { /* non-blocking succesfull path */ 21148c2ecf20Sopenharmony_ci DEFINE_WAKE_Q(wake_q); 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci /* 21178c2ecf20Sopenharmony_ci * If the operation was successful, then do 21188c2ecf20Sopenharmony_ci * the required updates. 21198c2ecf20Sopenharmony_ci */ 21208c2ecf20Sopenharmony_ci if (alter) 21218c2ecf20Sopenharmony_ci do_smart_update(sma, sops, nsops, 1, &wake_q); 21228c2ecf20Sopenharmony_ci else 21238c2ecf20Sopenharmony_ci set_semotime(sma, sops); 21248c2ecf20Sopenharmony_ci 21258c2ecf20Sopenharmony_ci sem_unlock(sma, locknum); 21268c2ecf20Sopenharmony_ci rcu_read_unlock(); 21278c2ecf20Sopenharmony_ci wake_up_q(&wake_q); 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ci goto out_free; 21308c2ecf20Sopenharmony_ci } 21318c2ecf20Sopenharmony_ci if (error < 0) /* non-blocking error path */ 21328c2ecf20Sopenharmony_ci goto out_unlock_free; 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci /* 21358c2ecf20Sopenharmony_ci * We need to sleep on this operation, so we put the current 21368c2ecf20Sopenharmony_ci * task into the pending queue and go to sleep. 21378c2ecf20Sopenharmony_ci */ 21388c2ecf20Sopenharmony_ci if (nsops == 1) { 21398c2ecf20Sopenharmony_ci struct sem *curr; 21408c2ecf20Sopenharmony_ci int idx = array_index_nospec(sops->sem_num, sma->sem_nsems); 21418c2ecf20Sopenharmony_ci curr = &sma->sems[idx]; 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci if (alter) { 21448c2ecf20Sopenharmony_ci if (sma->complex_count) { 21458c2ecf20Sopenharmony_ci list_add_tail(&queue.list, 21468c2ecf20Sopenharmony_ci &sma->pending_alter); 21478c2ecf20Sopenharmony_ci } else { 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci list_add_tail(&queue.list, 21508c2ecf20Sopenharmony_ci &curr->pending_alter); 21518c2ecf20Sopenharmony_ci } 21528c2ecf20Sopenharmony_ci } else { 21538c2ecf20Sopenharmony_ci list_add_tail(&queue.list, &curr->pending_const); 21548c2ecf20Sopenharmony_ci } 21558c2ecf20Sopenharmony_ci } else { 21568c2ecf20Sopenharmony_ci if (!sma->complex_count) 21578c2ecf20Sopenharmony_ci merge_queues(sma); 21588c2ecf20Sopenharmony_ci 21598c2ecf20Sopenharmony_ci if (alter) 21608c2ecf20Sopenharmony_ci list_add_tail(&queue.list, &sma->pending_alter); 21618c2ecf20Sopenharmony_ci else 21628c2ecf20Sopenharmony_ci list_add_tail(&queue.list, &sma->pending_const); 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci sma->complex_count++; 21658c2ecf20Sopenharmony_ci } 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci do { 21688c2ecf20Sopenharmony_ci /* memory ordering ensured by the lock in sem_lock() */ 21698c2ecf20Sopenharmony_ci WRITE_ONCE(queue.status, -EINTR); 21708c2ecf20Sopenharmony_ci queue.sleeper = current; 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci /* memory ordering is ensured by the lock in sem_lock() */ 21738c2ecf20Sopenharmony_ci __set_current_state(TASK_INTERRUPTIBLE); 21748c2ecf20Sopenharmony_ci sem_unlock(sma, locknum); 21758c2ecf20Sopenharmony_ci rcu_read_unlock(); 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci if (timeout) 21788c2ecf20Sopenharmony_ci jiffies_left = schedule_timeout(jiffies_left); 21798c2ecf20Sopenharmony_ci else 21808c2ecf20Sopenharmony_ci schedule(); 21818c2ecf20Sopenharmony_ci 21828c2ecf20Sopenharmony_ci /* 21838c2ecf20Sopenharmony_ci * fastpath: the semop has completed, either successfully or 21848c2ecf20Sopenharmony_ci * not, from the syscall pov, is quite irrelevant to us at this 21858c2ecf20Sopenharmony_ci * point; we're done. 21868c2ecf20Sopenharmony_ci * 21878c2ecf20Sopenharmony_ci * We _do_ care, nonetheless, about being awoken by a signal or 21888c2ecf20Sopenharmony_ci * spuriously. The queue.status is checked again in the 21898c2ecf20Sopenharmony_ci * slowpath (aka after taking sem_lock), such that we can detect 21908c2ecf20Sopenharmony_ci * scenarios where we were awakened externally, during the 21918c2ecf20Sopenharmony_ci * window between wake_q_add() and wake_up_q(). 21928c2ecf20Sopenharmony_ci */ 21938c2ecf20Sopenharmony_ci rcu_read_lock(); 21948c2ecf20Sopenharmony_ci error = READ_ONCE(queue.status); 21958c2ecf20Sopenharmony_ci if (error != -EINTR) { 21968c2ecf20Sopenharmony_ci /* see SEM_BARRIER_2 for purpose/pairing */ 21978c2ecf20Sopenharmony_ci smp_acquire__after_ctrl_dep(); 21988c2ecf20Sopenharmony_ci rcu_read_unlock(); 21998c2ecf20Sopenharmony_ci goto out_free; 22008c2ecf20Sopenharmony_ci } 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci locknum = sem_lock(sma, sops, nsops); 22038c2ecf20Sopenharmony_ci 22048c2ecf20Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) 22058c2ecf20Sopenharmony_ci goto out_unlock_free; 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_ci /* 22088c2ecf20Sopenharmony_ci * No necessity for any barrier: We are protect by sem_lock() 22098c2ecf20Sopenharmony_ci */ 22108c2ecf20Sopenharmony_ci error = READ_ONCE(queue.status); 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_ci /* 22138c2ecf20Sopenharmony_ci * If queue.status != -EINTR we are woken up by another process. 22148c2ecf20Sopenharmony_ci * Leave without unlink_queue(), but with sem_unlock(). 22158c2ecf20Sopenharmony_ci */ 22168c2ecf20Sopenharmony_ci if (error != -EINTR) 22178c2ecf20Sopenharmony_ci goto out_unlock_free; 22188c2ecf20Sopenharmony_ci 22198c2ecf20Sopenharmony_ci /* 22208c2ecf20Sopenharmony_ci * If an interrupt occurred we have to clean up the queue. 22218c2ecf20Sopenharmony_ci */ 22228c2ecf20Sopenharmony_ci if (timeout && jiffies_left == 0) 22238c2ecf20Sopenharmony_ci error = -EAGAIN; 22248c2ecf20Sopenharmony_ci } while (error == -EINTR && !signal_pending(current)); /* spurious */ 22258c2ecf20Sopenharmony_ci 22268c2ecf20Sopenharmony_ci unlink_queue(sma, &queue); 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_ciout_unlock_free: 22298c2ecf20Sopenharmony_ci sem_unlock(sma, locknum); 22308c2ecf20Sopenharmony_ci rcu_read_unlock(); 22318c2ecf20Sopenharmony_ciout_free: 22328c2ecf20Sopenharmony_ci if (sops != fast_sops) 22338c2ecf20Sopenharmony_ci kvfree(sops); 22348c2ecf20Sopenharmony_ci return error; 22358c2ecf20Sopenharmony_ci} 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_cilong ksys_semtimedop(int semid, struct sembuf __user *tsops, 22388c2ecf20Sopenharmony_ci unsigned int nsops, const struct __kernel_timespec __user *timeout) 22398c2ecf20Sopenharmony_ci{ 22408c2ecf20Sopenharmony_ci if (timeout) { 22418c2ecf20Sopenharmony_ci struct timespec64 ts; 22428c2ecf20Sopenharmony_ci if (get_timespec64(&ts, timeout)) 22438c2ecf20Sopenharmony_ci return -EFAULT; 22448c2ecf20Sopenharmony_ci return do_semtimedop(semid, tsops, nsops, &ts); 22458c2ecf20Sopenharmony_ci } 22468c2ecf20Sopenharmony_ci return do_semtimedop(semid, tsops, nsops, NULL); 22478c2ecf20Sopenharmony_ci} 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_ciSYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, 22508c2ecf20Sopenharmony_ci unsigned int, nsops, const struct __kernel_timespec __user *, timeout) 22518c2ecf20Sopenharmony_ci{ 22528c2ecf20Sopenharmony_ci return ksys_semtimedop(semid, tsops, nsops, timeout); 22538c2ecf20Sopenharmony_ci} 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT_32BIT_TIME 22568c2ecf20Sopenharmony_cilong compat_ksys_semtimedop(int semid, struct sembuf __user *tsems, 22578c2ecf20Sopenharmony_ci unsigned int nsops, 22588c2ecf20Sopenharmony_ci const struct old_timespec32 __user *timeout) 22598c2ecf20Sopenharmony_ci{ 22608c2ecf20Sopenharmony_ci if (timeout) { 22618c2ecf20Sopenharmony_ci struct timespec64 ts; 22628c2ecf20Sopenharmony_ci if (get_old_timespec32(&ts, timeout)) 22638c2ecf20Sopenharmony_ci return -EFAULT; 22648c2ecf20Sopenharmony_ci return do_semtimedop(semid, tsems, nsops, &ts); 22658c2ecf20Sopenharmony_ci } 22668c2ecf20Sopenharmony_ci return do_semtimedop(semid, tsems, nsops, NULL); 22678c2ecf20Sopenharmony_ci} 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_ciSYSCALL_DEFINE4(semtimedop_time32, int, semid, struct sembuf __user *, tsems, 22708c2ecf20Sopenharmony_ci unsigned int, nsops, 22718c2ecf20Sopenharmony_ci const struct old_timespec32 __user *, timeout) 22728c2ecf20Sopenharmony_ci{ 22738c2ecf20Sopenharmony_ci return compat_ksys_semtimedop(semid, tsems, nsops, timeout); 22748c2ecf20Sopenharmony_ci} 22758c2ecf20Sopenharmony_ci#endif 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_ciSYSCALL_DEFINE3(semop, int, semid, struct sembuf __user *, tsops, 22788c2ecf20Sopenharmony_ci unsigned, nsops) 22798c2ecf20Sopenharmony_ci{ 22808c2ecf20Sopenharmony_ci return do_semtimedop(semid, tsops, nsops, NULL); 22818c2ecf20Sopenharmony_ci} 22828c2ecf20Sopenharmony_ci 22838c2ecf20Sopenharmony_ci/* If CLONE_SYSVSEM is set, establish sharing of SEM_UNDO state between 22848c2ecf20Sopenharmony_ci * parent and child tasks. 22858c2ecf20Sopenharmony_ci */ 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_ciint copy_semundo(unsigned long clone_flags, struct task_struct *tsk) 22888c2ecf20Sopenharmony_ci{ 22898c2ecf20Sopenharmony_ci struct sem_undo_list *undo_list; 22908c2ecf20Sopenharmony_ci int error; 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci if (clone_flags & CLONE_SYSVSEM) { 22938c2ecf20Sopenharmony_ci error = get_undo_list(&undo_list); 22948c2ecf20Sopenharmony_ci if (error) 22958c2ecf20Sopenharmony_ci return error; 22968c2ecf20Sopenharmony_ci refcount_inc(&undo_list->refcnt); 22978c2ecf20Sopenharmony_ci tsk->sysvsem.undo_list = undo_list; 22988c2ecf20Sopenharmony_ci } else 22998c2ecf20Sopenharmony_ci tsk->sysvsem.undo_list = NULL; 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci return 0; 23028c2ecf20Sopenharmony_ci} 23038c2ecf20Sopenharmony_ci 23048c2ecf20Sopenharmony_ci/* 23058c2ecf20Sopenharmony_ci * add semadj values to semaphores, free undo structures. 23068c2ecf20Sopenharmony_ci * undo structures are not freed when semaphore arrays are destroyed 23078c2ecf20Sopenharmony_ci * so some of them may be out of date. 23088c2ecf20Sopenharmony_ci * IMPLEMENTATION NOTE: There is some confusion over whether the 23098c2ecf20Sopenharmony_ci * set of adjustments that needs to be done should be done in an atomic 23108c2ecf20Sopenharmony_ci * manner or not. That is, if we are attempting to decrement the semval 23118c2ecf20Sopenharmony_ci * should we queue up and wait until we can do so legally? 23128c2ecf20Sopenharmony_ci * The original implementation attempted to do this (queue and wait). 23138c2ecf20Sopenharmony_ci * The current implementation does not do so. The POSIX standard 23148c2ecf20Sopenharmony_ci * and SVID should be consulted to determine what behavior is mandated. 23158c2ecf20Sopenharmony_ci */ 23168c2ecf20Sopenharmony_civoid exit_sem(struct task_struct *tsk) 23178c2ecf20Sopenharmony_ci{ 23188c2ecf20Sopenharmony_ci struct sem_undo_list *ulp; 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ci ulp = tsk->sysvsem.undo_list; 23218c2ecf20Sopenharmony_ci if (!ulp) 23228c2ecf20Sopenharmony_ci return; 23238c2ecf20Sopenharmony_ci tsk->sysvsem.undo_list = NULL; 23248c2ecf20Sopenharmony_ci 23258c2ecf20Sopenharmony_ci if (!refcount_dec_and_test(&ulp->refcnt)) 23268c2ecf20Sopenharmony_ci return; 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci for (;;) { 23298c2ecf20Sopenharmony_ci struct sem_array *sma; 23308c2ecf20Sopenharmony_ci struct sem_undo *un; 23318c2ecf20Sopenharmony_ci int semid, i; 23328c2ecf20Sopenharmony_ci DEFINE_WAKE_Q(wake_q); 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci cond_resched(); 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ci rcu_read_lock(); 23378c2ecf20Sopenharmony_ci un = list_entry_rcu(ulp->list_proc.next, 23388c2ecf20Sopenharmony_ci struct sem_undo, list_proc); 23398c2ecf20Sopenharmony_ci if (&un->list_proc == &ulp->list_proc) { 23408c2ecf20Sopenharmony_ci /* 23418c2ecf20Sopenharmony_ci * We must wait for freeary() before freeing this ulp, 23428c2ecf20Sopenharmony_ci * in case we raced with last sem_undo. There is a small 23438c2ecf20Sopenharmony_ci * possibility where we exit while freeary() didn't 23448c2ecf20Sopenharmony_ci * finish unlocking sem_undo_list. 23458c2ecf20Sopenharmony_ci */ 23468c2ecf20Sopenharmony_ci spin_lock(&ulp->lock); 23478c2ecf20Sopenharmony_ci spin_unlock(&ulp->lock); 23488c2ecf20Sopenharmony_ci rcu_read_unlock(); 23498c2ecf20Sopenharmony_ci break; 23508c2ecf20Sopenharmony_ci } 23518c2ecf20Sopenharmony_ci spin_lock(&ulp->lock); 23528c2ecf20Sopenharmony_ci semid = un->semid; 23538c2ecf20Sopenharmony_ci spin_unlock(&ulp->lock); 23548c2ecf20Sopenharmony_ci 23558c2ecf20Sopenharmony_ci /* exit_sem raced with IPC_RMID, nothing to do */ 23568c2ecf20Sopenharmony_ci if (semid == -1) { 23578c2ecf20Sopenharmony_ci rcu_read_unlock(); 23588c2ecf20Sopenharmony_ci continue; 23598c2ecf20Sopenharmony_ci } 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_ci sma = sem_obtain_object_check(tsk->nsproxy->ipc_ns, semid); 23628c2ecf20Sopenharmony_ci /* exit_sem raced with IPC_RMID, nothing to do */ 23638c2ecf20Sopenharmony_ci if (IS_ERR(sma)) { 23648c2ecf20Sopenharmony_ci rcu_read_unlock(); 23658c2ecf20Sopenharmony_ci continue; 23668c2ecf20Sopenharmony_ci } 23678c2ecf20Sopenharmony_ci 23688c2ecf20Sopenharmony_ci sem_lock(sma, NULL, -1); 23698c2ecf20Sopenharmony_ci /* exit_sem raced with IPC_RMID, nothing to do */ 23708c2ecf20Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) { 23718c2ecf20Sopenharmony_ci sem_unlock(sma, -1); 23728c2ecf20Sopenharmony_ci rcu_read_unlock(); 23738c2ecf20Sopenharmony_ci continue; 23748c2ecf20Sopenharmony_ci } 23758c2ecf20Sopenharmony_ci un = __lookup_undo(ulp, semid); 23768c2ecf20Sopenharmony_ci if (un == NULL) { 23778c2ecf20Sopenharmony_ci /* exit_sem raced with IPC_RMID+semget() that created 23788c2ecf20Sopenharmony_ci * exactly the same semid. Nothing to do. 23798c2ecf20Sopenharmony_ci */ 23808c2ecf20Sopenharmony_ci sem_unlock(sma, -1); 23818c2ecf20Sopenharmony_ci rcu_read_unlock(); 23828c2ecf20Sopenharmony_ci continue; 23838c2ecf20Sopenharmony_ci } 23848c2ecf20Sopenharmony_ci 23858c2ecf20Sopenharmony_ci /* remove un from the linked lists */ 23868c2ecf20Sopenharmony_ci ipc_assert_locked_object(&sma->sem_perm); 23878c2ecf20Sopenharmony_ci list_del(&un->list_id); 23888c2ecf20Sopenharmony_ci 23898c2ecf20Sopenharmony_ci spin_lock(&ulp->lock); 23908c2ecf20Sopenharmony_ci list_del_rcu(&un->list_proc); 23918c2ecf20Sopenharmony_ci spin_unlock(&ulp->lock); 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_ci /* perform adjustments registered in un */ 23948c2ecf20Sopenharmony_ci for (i = 0; i < sma->sem_nsems; i++) { 23958c2ecf20Sopenharmony_ci struct sem *semaphore = &sma->sems[i]; 23968c2ecf20Sopenharmony_ci if (un->semadj[i]) { 23978c2ecf20Sopenharmony_ci semaphore->semval += un->semadj[i]; 23988c2ecf20Sopenharmony_ci /* 23998c2ecf20Sopenharmony_ci * Range checks of the new semaphore value, 24008c2ecf20Sopenharmony_ci * not defined by sus: 24018c2ecf20Sopenharmony_ci * - Some unices ignore the undo entirely 24028c2ecf20Sopenharmony_ci * (e.g. HP UX 11i 11.22, Tru64 V5.1) 24038c2ecf20Sopenharmony_ci * - some cap the value (e.g. FreeBSD caps 24048c2ecf20Sopenharmony_ci * at 0, but doesn't enforce SEMVMX) 24058c2ecf20Sopenharmony_ci * 24068c2ecf20Sopenharmony_ci * Linux caps the semaphore value, both at 0 24078c2ecf20Sopenharmony_ci * and at SEMVMX. 24088c2ecf20Sopenharmony_ci * 24098c2ecf20Sopenharmony_ci * Manfred <manfred@colorfullife.com> 24108c2ecf20Sopenharmony_ci */ 24118c2ecf20Sopenharmony_ci if (semaphore->semval < 0) 24128c2ecf20Sopenharmony_ci semaphore->semval = 0; 24138c2ecf20Sopenharmony_ci if (semaphore->semval > SEMVMX) 24148c2ecf20Sopenharmony_ci semaphore->semval = SEMVMX; 24158c2ecf20Sopenharmony_ci ipc_update_pid(&semaphore->sempid, task_tgid(current)); 24168c2ecf20Sopenharmony_ci } 24178c2ecf20Sopenharmony_ci } 24188c2ecf20Sopenharmony_ci /* maybe some queued-up processes were waiting for this */ 24198c2ecf20Sopenharmony_ci do_smart_update(sma, NULL, 0, 1, &wake_q); 24208c2ecf20Sopenharmony_ci sem_unlock(sma, -1); 24218c2ecf20Sopenharmony_ci rcu_read_unlock(); 24228c2ecf20Sopenharmony_ci wake_up_q(&wake_q); 24238c2ecf20Sopenharmony_ci 24248c2ecf20Sopenharmony_ci kfree_rcu(un, rcu); 24258c2ecf20Sopenharmony_ci } 24268c2ecf20Sopenharmony_ci kfree(ulp); 24278c2ecf20Sopenharmony_ci} 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 24308c2ecf20Sopenharmony_cistatic int sysvipc_sem_proc_show(struct seq_file *s, void *it) 24318c2ecf20Sopenharmony_ci{ 24328c2ecf20Sopenharmony_ci struct user_namespace *user_ns = seq_user_ns(s); 24338c2ecf20Sopenharmony_ci struct kern_ipc_perm *ipcp = it; 24348c2ecf20Sopenharmony_ci struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm); 24358c2ecf20Sopenharmony_ci time64_t sem_otime; 24368c2ecf20Sopenharmony_ci 24378c2ecf20Sopenharmony_ci /* 24388c2ecf20Sopenharmony_ci * The proc interface isn't aware of sem_lock(), it calls 24398c2ecf20Sopenharmony_ci * ipc_lock_object() directly (in sysvipc_find_ipc). 24408c2ecf20Sopenharmony_ci * In order to stay compatible with sem_lock(), we must 24418c2ecf20Sopenharmony_ci * enter / leave complex_mode. 24428c2ecf20Sopenharmony_ci */ 24438c2ecf20Sopenharmony_ci complexmode_enter(sma); 24448c2ecf20Sopenharmony_ci 24458c2ecf20Sopenharmony_ci sem_otime = get_semotime(sma); 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci seq_printf(s, 24488c2ecf20Sopenharmony_ci "%10d %10d %4o %10u %5u %5u %5u %5u %10llu %10llu\n", 24498c2ecf20Sopenharmony_ci sma->sem_perm.key, 24508c2ecf20Sopenharmony_ci sma->sem_perm.id, 24518c2ecf20Sopenharmony_ci sma->sem_perm.mode, 24528c2ecf20Sopenharmony_ci sma->sem_nsems, 24538c2ecf20Sopenharmony_ci from_kuid_munged(user_ns, sma->sem_perm.uid), 24548c2ecf20Sopenharmony_ci from_kgid_munged(user_ns, sma->sem_perm.gid), 24558c2ecf20Sopenharmony_ci from_kuid_munged(user_ns, sma->sem_perm.cuid), 24568c2ecf20Sopenharmony_ci from_kgid_munged(user_ns, sma->sem_perm.cgid), 24578c2ecf20Sopenharmony_ci sem_otime, 24588c2ecf20Sopenharmony_ci sma->sem_ctime); 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_ci complexmode_tryleave(sma); 24618c2ecf20Sopenharmony_ci 24628c2ecf20Sopenharmony_ci return 0; 24638c2ecf20Sopenharmony_ci} 24648c2ecf20Sopenharmony_ci#endif 2465