162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/ipc/sem.c 462306a36Sopenharmony_ci * Copyright (C) 1992 Krishna Balasubramanian 562306a36Sopenharmony_ci * Copyright (C) 1995 Eric Schenk, Bruno Haible 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * /proc/sysvipc/sem support (c) 1999 Dragos Acostachioaie <dragos@iname.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * SMP-threaded, sysctl's added 1062306a36Sopenharmony_ci * (c) 1999 Manfred Spraul <manfred@colorfullife.com> 1162306a36Sopenharmony_ci * Enforced range limit on SEM_UNDO 1262306a36Sopenharmony_ci * (c) 2001 Red Hat Inc 1362306a36Sopenharmony_ci * Lockless wakeup 1462306a36Sopenharmony_ci * (c) 2003 Manfred Spraul <manfred@colorfullife.com> 1562306a36Sopenharmony_ci * (c) 2016 Davidlohr Bueso <dave@stgolabs.net> 1662306a36Sopenharmony_ci * Further wakeup optimizations, documentation 1762306a36Sopenharmony_ci * (c) 2010 Manfred Spraul <manfred@colorfullife.com> 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * support for audit of ipc object properties and permission changes 2062306a36Sopenharmony_ci * Dustin Kirkland <dustin.kirkland@us.ibm.com> 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * namespaces support 2362306a36Sopenharmony_ci * OpenVZ, SWsoft Inc. 2462306a36Sopenharmony_ci * Pavel Emelianov <xemul@openvz.org> 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * Implementation notes: (May 2010) 2762306a36Sopenharmony_ci * This file implements System V semaphores. 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * User space visible behavior: 3062306a36Sopenharmony_ci * - FIFO ordering for semop() operations (just FIFO, not starvation 3162306a36Sopenharmony_ci * protection) 3262306a36Sopenharmony_ci * - multiple semaphore operations that alter the same semaphore in 3362306a36Sopenharmony_ci * one semop() are handled. 3462306a36Sopenharmony_ci * - sem_ctime (time of last semctl()) is updated in the IPC_SET, SETVAL and 3562306a36Sopenharmony_ci * SETALL calls. 3662306a36Sopenharmony_ci * - two Linux specific semctl() commands: SEM_STAT, SEM_INFO. 3762306a36Sopenharmony_ci * - undo adjustments at process exit are limited to 0..SEMVMX. 3862306a36Sopenharmony_ci * - namespace are supported. 3962306a36Sopenharmony_ci * - SEMMSL, SEMMNS, SEMOPM and SEMMNI can be configured at runtime by writing 4062306a36Sopenharmony_ci * to /proc/sys/kernel/sem. 4162306a36Sopenharmony_ci * - statistics about the usage are reported in /proc/sysvipc/sem. 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * Internals: 4462306a36Sopenharmony_ci * - scalability: 4562306a36Sopenharmony_ci * - all global variables are read-mostly. 4662306a36Sopenharmony_ci * - semop() calls and semctl(RMID) are synchronized by RCU. 4762306a36Sopenharmony_ci * - most operations do write operations (actually: spin_lock calls) to 4862306a36Sopenharmony_ci * the per-semaphore array structure. 4962306a36Sopenharmony_ci * Thus: Perfect SMP scaling between independent semaphore arrays. 5062306a36Sopenharmony_ci * If multiple semaphores in one array are used, then cache line 5162306a36Sopenharmony_ci * trashing on the semaphore array spinlock will limit the scaling. 5262306a36Sopenharmony_ci * - semncnt and semzcnt are calculated on demand in count_semcnt() 5362306a36Sopenharmony_ci * - the task that performs a successful semop() scans the list of all 5462306a36Sopenharmony_ci * sleeping tasks and completes any pending operations that can be fulfilled. 5562306a36Sopenharmony_ci * Semaphores are actively given to waiting tasks (necessary for FIFO). 5662306a36Sopenharmony_ci * (see update_queue()) 5762306a36Sopenharmony_ci * - To improve the scalability, the actual wake-up calls are performed after 5862306a36Sopenharmony_ci * dropping all locks. (see wake_up_sem_queue_prepare()) 5962306a36Sopenharmony_ci * - All work is done by the waker, the woken up task does not have to do 6062306a36Sopenharmony_ci * anything - not even acquiring a lock or dropping a refcount. 6162306a36Sopenharmony_ci * - A woken up task may not even touch the semaphore array anymore, it may 6262306a36Sopenharmony_ci * have been destroyed already by a semctl(RMID). 6362306a36Sopenharmony_ci * - UNDO values are stored in an array (one per process and per 6462306a36Sopenharmony_ci * semaphore array, lazily allocated). For backwards compatibility, multiple 6562306a36Sopenharmony_ci * modes for the UNDO variables are supported (per process, per thread) 6662306a36Sopenharmony_ci * (see copy_semundo, CLONE_SYSVSEM) 6762306a36Sopenharmony_ci * - There are two lists of the pending operations: a per-array list 6862306a36Sopenharmony_ci * and per-semaphore list (stored in the array). This allows to achieve FIFO 6962306a36Sopenharmony_ci * ordering without always scanning all pending operations. 7062306a36Sopenharmony_ci * The worst-case behavior is nevertheless O(N^2) for N wakeups. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#include <linux/compat.h> 7462306a36Sopenharmony_ci#include <linux/slab.h> 7562306a36Sopenharmony_ci#include <linux/spinlock.h> 7662306a36Sopenharmony_ci#include <linux/init.h> 7762306a36Sopenharmony_ci#include <linux/proc_fs.h> 7862306a36Sopenharmony_ci#include <linux/time.h> 7962306a36Sopenharmony_ci#include <linux/security.h> 8062306a36Sopenharmony_ci#include <linux/syscalls.h> 8162306a36Sopenharmony_ci#include <linux/audit.h> 8262306a36Sopenharmony_ci#include <linux/capability.h> 8362306a36Sopenharmony_ci#include <linux/seq_file.h> 8462306a36Sopenharmony_ci#include <linux/rwsem.h> 8562306a36Sopenharmony_ci#include <linux/nsproxy.h> 8662306a36Sopenharmony_ci#include <linux/ipc_namespace.h> 8762306a36Sopenharmony_ci#include <linux/sched/wake_q.h> 8862306a36Sopenharmony_ci#include <linux/nospec.h> 8962306a36Sopenharmony_ci#include <linux/rhashtable.h> 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#include <linux/uaccess.h> 9262306a36Sopenharmony_ci#include "util.h" 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* One semaphore structure for each semaphore in the system. */ 9562306a36Sopenharmony_cistruct sem { 9662306a36Sopenharmony_ci int semval; /* current value */ 9762306a36Sopenharmony_ci /* 9862306a36Sopenharmony_ci * PID of the process that last modified the semaphore. For 9962306a36Sopenharmony_ci * Linux, specifically these are: 10062306a36Sopenharmony_ci * - semop 10162306a36Sopenharmony_ci * - semctl, via SETVAL and SETALL. 10262306a36Sopenharmony_ci * - at task exit when performing undo adjustments (see exit_sem). 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ci struct pid *sempid; 10562306a36Sopenharmony_ci spinlock_t lock; /* spinlock for fine-grained semtimedop */ 10662306a36Sopenharmony_ci struct list_head pending_alter; /* pending single-sop operations */ 10762306a36Sopenharmony_ci /* that alter the semaphore */ 10862306a36Sopenharmony_ci struct list_head pending_const; /* pending single-sop operations */ 10962306a36Sopenharmony_ci /* that do not alter the semaphore*/ 11062306a36Sopenharmony_ci time64_t sem_otime; /* candidate for sem_otime */ 11162306a36Sopenharmony_ci} ____cacheline_aligned_in_smp; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* One sem_array data structure for each set of semaphores in the system. */ 11462306a36Sopenharmony_cistruct sem_array { 11562306a36Sopenharmony_ci struct kern_ipc_perm sem_perm; /* permissions .. see ipc.h */ 11662306a36Sopenharmony_ci time64_t sem_ctime; /* create/last semctl() time */ 11762306a36Sopenharmony_ci struct list_head pending_alter; /* pending operations */ 11862306a36Sopenharmony_ci /* that alter the array */ 11962306a36Sopenharmony_ci struct list_head pending_const; /* pending complex operations */ 12062306a36Sopenharmony_ci /* that do not alter semvals */ 12162306a36Sopenharmony_ci struct list_head list_id; /* undo requests on this array */ 12262306a36Sopenharmony_ci int sem_nsems; /* no. of semaphores in array */ 12362306a36Sopenharmony_ci int complex_count; /* pending complex operations */ 12462306a36Sopenharmony_ci unsigned int use_global_lock;/* >0: global lock required */ 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci struct sem sems[]; 12762306a36Sopenharmony_ci} __randomize_layout; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* One queue for each sleeping process in the system. */ 13062306a36Sopenharmony_cistruct sem_queue { 13162306a36Sopenharmony_ci struct list_head list; /* queue of pending operations */ 13262306a36Sopenharmony_ci struct task_struct *sleeper; /* this process */ 13362306a36Sopenharmony_ci struct sem_undo *undo; /* undo structure */ 13462306a36Sopenharmony_ci struct pid *pid; /* process id of requesting process */ 13562306a36Sopenharmony_ci int status; /* completion status of operation */ 13662306a36Sopenharmony_ci struct sembuf *sops; /* array of pending operations */ 13762306a36Sopenharmony_ci struct sembuf *blocking; /* the operation that blocked */ 13862306a36Sopenharmony_ci int nsops; /* number of operations */ 13962306a36Sopenharmony_ci bool alter; /* does *sops alter the array? */ 14062306a36Sopenharmony_ci bool dupsop; /* sops on more than one sem_num */ 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* Each task has a list of undo requests. They are executed automatically 14462306a36Sopenharmony_ci * when the process exits. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_cistruct sem_undo { 14762306a36Sopenharmony_ci struct list_head list_proc; /* per-process list: * 14862306a36Sopenharmony_ci * all undos from one process 14962306a36Sopenharmony_ci * rcu protected */ 15062306a36Sopenharmony_ci struct rcu_head rcu; /* rcu struct for sem_undo */ 15162306a36Sopenharmony_ci struct sem_undo_list *ulp; /* back ptr to sem_undo_list */ 15262306a36Sopenharmony_ci struct list_head list_id; /* per semaphore array list: 15362306a36Sopenharmony_ci * all undos for one array */ 15462306a36Sopenharmony_ci int semid; /* semaphore set identifier */ 15562306a36Sopenharmony_ci short semadj[]; /* array of adjustments */ 15662306a36Sopenharmony_ci /* one per semaphore */ 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* sem_undo_list controls shared access to the list of sem_undo structures 16062306a36Sopenharmony_ci * that may be shared among all a CLONE_SYSVSEM task group. 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_cistruct sem_undo_list { 16362306a36Sopenharmony_ci refcount_t refcnt; 16462306a36Sopenharmony_ci spinlock_t lock; 16562306a36Sopenharmony_ci struct list_head list_proc; 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci#define sem_ids(ns) ((ns)->ids[IPC_SEM_IDS]) 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int newary(struct ipc_namespace *, struct ipc_params *); 17262306a36Sopenharmony_cistatic void freeary(struct ipc_namespace *, struct kern_ipc_perm *); 17362306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 17462306a36Sopenharmony_cistatic int sysvipc_sem_proc_show(struct seq_file *s, void *it); 17562306a36Sopenharmony_ci#endif 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci#define SEMMSL_FAST 256 /* 512 bytes on stack */ 17862306a36Sopenharmony_ci#define SEMOPM_FAST 64 /* ~ 372 bytes on stack */ 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/* 18162306a36Sopenharmony_ci * Switching from the mode suitable for simple ops 18262306a36Sopenharmony_ci * to the mode for complex ops is costly. Therefore: 18362306a36Sopenharmony_ci * use some hysteresis 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci#define USE_GLOBAL_LOCK_HYSTERESIS 10 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/* 18862306a36Sopenharmony_ci * Locking: 18962306a36Sopenharmony_ci * a) global sem_lock() for read/write 19062306a36Sopenharmony_ci * sem_undo.id_next, 19162306a36Sopenharmony_ci * sem_array.complex_count, 19262306a36Sopenharmony_ci * sem_array.pending{_alter,_const}, 19362306a36Sopenharmony_ci * sem_array.sem_undo 19462306a36Sopenharmony_ci * 19562306a36Sopenharmony_ci * b) global or semaphore sem_lock() for read/write: 19662306a36Sopenharmony_ci * sem_array.sems[i].pending_{const,alter}: 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * c) special: 19962306a36Sopenharmony_ci * sem_undo_list.list_proc: 20062306a36Sopenharmony_ci * * undo_list->lock for write 20162306a36Sopenharmony_ci * * rcu for read 20262306a36Sopenharmony_ci * use_global_lock: 20362306a36Sopenharmony_ci * * global sem_lock() for write 20462306a36Sopenharmony_ci * * either local or global sem_lock() for read. 20562306a36Sopenharmony_ci * 20662306a36Sopenharmony_ci * Memory ordering: 20762306a36Sopenharmony_ci * Most ordering is enforced by using spin_lock() and spin_unlock(). 20862306a36Sopenharmony_ci * 20962306a36Sopenharmony_ci * Exceptions: 21062306a36Sopenharmony_ci * 1) use_global_lock: (SEM_BARRIER_1) 21162306a36Sopenharmony_ci * Setting it from non-zero to 0 is a RELEASE, this is ensured by 21262306a36Sopenharmony_ci * using smp_store_release(): Immediately after setting it to 0, 21362306a36Sopenharmony_ci * a simple op can start. 21462306a36Sopenharmony_ci * Testing if it is non-zero is an ACQUIRE, this is ensured by using 21562306a36Sopenharmony_ci * smp_load_acquire(). 21662306a36Sopenharmony_ci * Setting it from 0 to non-zero must be ordered with regards to 21762306a36Sopenharmony_ci * this smp_load_acquire(), this is guaranteed because the smp_load_acquire() 21862306a36Sopenharmony_ci * is inside a spin_lock() and after a write from 0 to non-zero a 21962306a36Sopenharmony_ci * spin_lock()+spin_unlock() is done. 22062306a36Sopenharmony_ci * To prevent the compiler/cpu temporarily writing 0 to use_global_lock, 22162306a36Sopenharmony_ci * READ_ONCE()/WRITE_ONCE() is used. 22262306a36Sopenharmony_ci * 22362306a36Sopenharmony_ci * 2) queue.status: (SEM_BARRIER_2) 22462306a36Sopenharmony_ci * Initialization is done while holding sem_lock(), so no further barrier is 22562306a36Sopenharmony_ci * required. 22662306a36Sopenharmony_ci * Setting it to a result code is a RELEASE, this is ensured by both a 22762306a36Sopenharmony_ci * smp_store_release() (for case a) and while holding sem_lock() 22862306a36Sopenharmony_ci * (for case b). 22962306a36Sopenharmony_ci * The ACQUIRE when reading the result code without holding sem_lock() is 23062306a36Sopenharmony_ci * achieved by using READ_ONCE() + smp_acquire__after_ctrl_dep(). 23162306a36Sopenharmony_ci * (case a above). 23262306a36Sopenharmony_ci * Reading the result code while holding sem_lock() needs no further barriers, 23362306a36Sopenharmony_ci * the locks inside sem_lock() enforce ordering (case b above) 23462306a36Sopenharmony_ci * 23562306a36Sopenharmony_ci * 3) current->state: 23662306a36Sopenharmony_ci * current->state is set to TASK_INTERRUPTIBLE while holding sem_lock(). 23762306a36Sopenharmony_ci * The wakeup is handled using the wake_q infrastructure. wake_q wakeups may 23862306a36Sopenharmony_ci * happen immediately after calling wake_q_add. As wake_q_add_safe() is called 23962306a36Sopenharmony_ci * when holding sem_lock(), no further barriers are required. 24062306a36Sopenharmony_ci * 24162306a36Sopenharmony_ci * See also ipc/mqueue.c for more details on the covered races. 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci#define sc_semmsl sem_ctls[0] 24562306a36Sopenharmony_ci#define sc_semmns sem_ctls[1] 24662306a36Sopenharmony_ci#define sc_semopm sem_ctls[2] 24762306a36Sopenharmony_ci#define sc_semmni sem_ctls[3] 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_civoid sem_init_ns(struct ipc_namespace *ns) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci ns->sc_semmsl = SEMMSL; 25262306a36Sopenharmony_ci ns->sc_semmns = SEMMNS; 25362306a36Sopenharmony_ci ns->sc_semopm = SEMOPM; 25462306a36Sopenharmony_ci ns->sc_semmni = SEMMNI; 25562306a36Sopenharmony_ci ns->used_sems = 0; 25662306a36Sopenharmony_ci ipc_init_ids(&ns->ids[IPC_SEM_IDS]); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci#ifdef CONFIG_IPC_NS 26062306a36Sopenharmony_civoid sem_exit_ns(struct ipc_namespace *ns) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci free_ipcs(ns, &sem_ids(ns), freeary); 26362306a36Sopenharmony_ci idr_destroy(&ns->ids[IPC_SEM_IDS].ipcs_idr); 26462306a36Sopenharmony_ci rhashtable_destroy(&ns->ids[IPC_SEM_IDS].key_ht); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci#endif 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_civoid __init sem_init(void) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci sem_init_ns(&init_ipc_ns); 27162306a36Sopenharmony_ci ipc_init_proc_interface("sysvipc/sem", 27262306a36Sopenharmony_ci " key semid perms nsems uid gid cuid cgid otime ctime\n", 27362306a36Sopenharmony_ci IPC_SEM_IDS, sysvipc_sem_proc_show); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci/** 27762306a36Sopenharmony_ci * unmerge_queues - unmerge queues, if possible. 27862306a36Sopenharmony_ci * @sma: semaphore array 27962306a36Sopenharmony_ci * 28062306a36Sopenharmony_ci * The function unmerges the wait queues if complex_count is 0. 28162306a36Sopenharmony_ci * It must be called prior to dropping the global semaphore array lock. 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_cistatic void unmerge_queues(struct sem_array *sma) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct sem_queue *q, *tq; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* complex operations still around? */ 28862306a36Sopenharmony_ci if (sma->complex_count) 28962306a36Sopenharmony_ci return; 29062306a36Sopenharmony_ci /* 29162306a36Sopenharmony_ci * We will switch back to simple mode. 29262306a36Sopenharmony_ci * Move all pending operation back into the per-semaphore 29362306a36Sopenharmony_ci * queues. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci list_for_each_entry_safe(q, tq, &sma->pending_alter, list) { 29662306a36Sopenharmony_ci struct sem *curr; 29762306a36Sopenharmony_ci curr = &sma->sems[q->sops[0].sem_num]; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci list_add_tail(&q->list, &curr->pending_alter); 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci INIT_LIST_HEAD(&sma->pending_alter); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci/** 30562306a36Sopenharmony_ci * merge_queues - merge single semop queues into global queue 30662306a36Sopenharmony_ci * @sma: semaphore array 30762306a36Sopenharmony_ci * 30862306a36Sopenharmony_ci * This function merges all per-semaphore queues into the global queue. 30962306a36Sopenharmony_ci * It is necessary to achieve FIFO ordering for the pending single-sop 31062306a36Sopenharmony_ci * operations when a multi-semop operation must sleep. 31162306a36Sopenharmony_ci * Only the alter operations must be moved, the const operations can stay. 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_cistatic void merge_queues(struct sem_array *sma) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci int i; 31662306a36Sopenharmony_ci for (i = 0; i < sma->sem_nsems; i++) { 31762306a36Sopenharmony_ci struct sem *sem = &sma->sems[i]; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci list_splice_init(&sem->pending_alter, &sma->pending_alter); 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic void sem_rcu_free(struct rcu_head *head) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct kern_ipc_perm *p = container_of(head, struct kern_ipc_perm, rcu); 32662306a36Sopenharmony_ci struct sem_array *sma = container_of(p, struct sem_array, sem_perm); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci security_sem_free(&sma->sem_perm); 32962306a36Sopenharmony_ci kvfree(sma); 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci/* 33362306a36Sopenharmony_ci * Enter the mode suitable for non-simple operations: 33462306a36Sopenharmony_ci * Caller must own sem_perm.lock. 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_cistatic void complexmode_enter(struct sem_array *sma) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci int i; 33962306a36Sopenharmony_ci struct sem *sem; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (sma->use_global_lock > 0) { 34262306a36Sopenharmony_ci /* 34362306a36Sopenharmony_ci * We are already in global lock mode. 34462306a36Sopenharmony_ci * Nothing to do, just reset the 34562306a36Sopenharmony_ci * counter until we return to simple mode. 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_ci WRITE_ONCE(sma->use_global_lock, USE_GLOBAL_LOCK_HYSTERESIS); 34862306a36Sopenharmony_ci return; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci WRITE_ONCE(sma->use_global_lock, USE_GLOBAL_LOCK_HYSTERESIS); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci for (i = 0; i < sma->sem_nsems; i++) { 35362306a36Sopenharmony_ci sem = &sma->sems[i]; 35462306a36Sopenharmony_ci spin_lock(&sem->lock); 35562306a36Sopenharmony_ci spin_unlock(&sem->lock); 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci/* 36062306a36Sopenharmony_ci * Try to leave the mode that disallows simple operations: 36162306a36Sopenharmony_ci * Caller must own sem_perm.lock. 36262306a36Sopenharmony_ci */ 36362306a36Sopenharmony_cistatic void complexmode_tryleave(struct sem_array *sma) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci if (sma->complex_count) { 36662306a36Sopenharmony_ci /* Complex ops are sleeping. 36762306a36Sopenharmony_ci * We must stay in complex mode 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_ci return; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci if (sma->use_global_lock == 1) { 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* See SEM_BARRIER_1 for purpose/pairing */ 37462306a36Sopenharmony_ci smp_store_release(&sma->use_global_lock, 0); 37562306a36Sopenharmony_ci } else { 37662306a36Sopenharmony_ci WRITE_ONCE(sma->use_global_lock, 37762306a36Sopenharmony_ci sma->use_global_lock-1); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci#define SEM_GLOBAL_LOCK (-1) 38262306a36Sopenharmony_ci/* 38362306a36Sopenharmony_ci * If the request contains only one semaphore operation, and there are 38462306a36Sopenharmony_ci * no complex transactions pending, lock only the semaphore involved. 38562306a36Sopenharmony_ci * Otherwise, lock the entire semaphore array, since we either have 38662306a36Sopenharmony_ci * multiple semaphores in our own semops, or we need to look at 38762306a36Sopenharmony_ci * semaphores from other pending complex operations. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_cistatic inline int sem_lock(struct sem_array *sma, struct sembuf *sops, 39062306a36Sopenharmony_ci int nsops) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct sem *sem; 39362306a36Sopenharmony_ci int idx; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (nsops != 1) { 39662306a36Sopenharmony_ci /* Complex operation - acquire a full lock */ 39762306a36Sopenharmony_ci ipc_lock_object(&sma->sem_perm); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* Prevent parallel simple ops */ 40062306a36Sopenharmony_ci complexmode_enter(sma); 40162306a36Sopenharmony_ci return SEM_GLOBAL_LOCK; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* 40562306a36Sopenharmony_ci * Only one semaphore affected - try to optimize locking. 40662306a36Sopenharmony_ci * Optimized locking is possible if no complex operation 40762306a36Sopenharmony_ci * is either enqueued or processed right now. 40862306a36Sopenharmony_ci * 40962306a36Sopenharmony_ci * Both facts are tracked by use_global_mode. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_ci idx = array_index_nospec(sops->sem_num, sma->sem_nsems); 41262306a36Sopenharmony_ci sem = &sma->sems[idx]; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* 41562306a36Sopenharmony_ci * Initial check for use_global_lock. Just an optimization, 41662306a36Sopenharmony_ci * no locking, no memory barrier. 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_ci if (!READ_ONCE(sma->use_global_lock)) { 41962306a36Sopenharmony_ci /* 42062306a36Sopenharmony_ci * It appears that no complex operation is around. 42162306a36Sopenharmony_ci * Acquire the per-semaphore lock. 42262306a36Sopenharmony_ci */ 42362306a36Sopenharmony_ci spin_lock(&sem->lock); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* see SEM_BARRIER_1 for purpose/pairing */ 42662306a36Sopenharmony_ci if (!smp_load_acquire(&sma->use_global_lock)) { 42762306a36Sopenharmony_ci /* fast path successful! */ 42862306a36Sopenharmony_ci return sops->sem_num; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci spin_unlock(&sem->lock); 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* slow path: acquire the full lock */ 43462306a36Sopenharmony_ci ipc_lock_object(&sma->sem_perm); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (sma->use_global_lock == 0) { 43762306a36Sopenharmony_ci /* 43862306a36Sopenharmony_ci * The use_global_lock mode ended while we waited for 43962306a36Sopenharmony_ci * sma->sem_perm.lock. Thus we must switch to locking 44062306a36Sopenharmony_ci * with sem->lock. 44162306a36Sopenharmony_ci * Unlike in the fast path, there is no need to recheck 44262306a36Sopenharmony_ci * sma->use_global_lock after we have acquired sem->lock: 44362306a36Sopenharmony_ci * We own sma->sem_perm.lock, thus use_global_lock cannot 44462306a36Sopenharmony_ci * change. 44562306a36Sopenharmony_ci */ 44662306a36Sopenharmony_ci spin_lock(&sem->lock); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci ipc_unlock_object(&sma->sem_perm); 44962306a36Sopenharmony_ci return sops->sem_num; 45062306a36Sopenharmony_ci } else { 45162306a36Sopenharmony_ci /* 45262306a36Sopenharmony_ci * Not a false alarm, thus continue to use the global lock 45362306a36Sopenharmony_ci * mode. No need for complexmode_enter(), this was done by 45462306a36Sopenharmony_ci * the caller that has set use_global_mode to non-zero. 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_ci return SEM_GLOBAL_LOCK; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic inline void sem_unlock(struct sem_array *sma, int locknum) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci if (locknum == SEM_GLOBAL_LOCK) { 46362306a36Sopenharmony_ci unmerge_queues(sma); 46462306a36Sopenharmony_ci complexmode_tryleave(sma); 46562306a36Sopenharmony_ci ipc_unlock_object(&sma->sem_perm); 46662306a36Sopenharmony_ci } else { 46762306a36Sopenharmony_ci struct sem *sem = &sma->sems[locknum]; 46862306a36Sopenharmony_ci spin_unlock(&sem->lock); 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci/* 47362306a36Sopenharmony_ci * sem_lock_(check_) routines are called in the paths where the rwsem 47462306a36Sopenharmony_ci * is not held. 47562306a36Sopenharmony_ci * 47662306a36Sopenharmony_ci * The caller holds the RCU read lock. 47762306a36Sopenharmony_ci */ 47862306a36Sopenharmony_cistatic inline struct sem_array *sem_obtain_object(struct ipc_namespace *ns, int id) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct kern_ipc_perm *ipcp = ipc_obtain_object_idr(&sem_ids(ns), id); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (IS_ERR(ipcp)) 48362306a36Sopenharmony_ci return ERR_CAST(ipcp); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci return container_of(ipcp, struct sem_array, sem_perm); 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic inline struct sem_array *sem_obtain_object_check(struct ipc_namespace *ns, 48962306a36Sopenharmony_ci int id) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct kern_ipc_perm *ipcp = ipc_obtain_object_check(&sem_ids(ns), id); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (IS_ERR(ipcp)) 49462306a36Sopenharmony_ci return ERR_CAST(ipcp); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci return container_of(ipcp, struct sem_array, sem_perm); 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic inline void sem_lock_and_putref(struct sem_array *sma) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci sem_lock(sma, NULL, -1); 50262306a36Sopenharmony_ci ipc_rcu_putref(&sma->sem_perm, sem_rcu_free); 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci ipc_rmid(&sem_ids(ns), &s->sem_perm); 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic struct sem_array *sem_alloc(size_t nsems) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct sem_array *sma; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (nsems > (INT_MAX - sizeof(*sma)) / sizeof(sma->sems[0])) 51562306a36Sopenharmony_ci return NULL; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci sma = kvzalloc(struct_size(sma, sems, nsems), GFP_KERNEL_ACCOUNT); 51862306a36Sopenharmony_ci if (unlikely(!sma)) 51962306a36Sopenharmony_ci return NULL; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return sma; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci/** 52562306a36Sopenharmony_ci * newary - Create a new semaphore set 52662306a36Sopenharmony_ci * @ns: namespace 52762306a36Sopenharmony_ci * @params: ptr to the structure that contains key, semflg and nsems 52862306a36Sopenharmony_ci * 52962306a36Sopenharmony_ci * Called with sem_ids.rwsem held (as a writer) 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_cistatic int newary(struct ipc_namespace *ns, struct ipc_params *params) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci int retval; 53462306a36Sopenharmony_ci struct sem_array *sma; 53562306a36Sopenharmony_ci key_t key = params->key; 53662306a36Sopenharmony_ci int nsems = params->u.nsems; 53762306a36Sopenharmony_ci int semflg = params->flg; 53862306a36Sopenharmony_ci int i; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (!nsems) 54162306a36Sopenharmony_ci return -EINVAL; 54262306a36Sopenharmony_ci if (ns->used_sems + nsems > ns->sc_semmns) 54362306a36Sopenharmony_ci return -ENOSPC; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci sma = sem_alloc(nsems); 54662306a36Sopenharmony_ci if (!sma) 54762306a36Sopenharmony_ci return -ENOMEM; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci sma->sem_perm.mode = (semflg & S_IRWXUGO); 55062306a36Sopenharmony_ci sma->sem_perm.key = key; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci sma->sem_perm.security = NULL; 55362306a36Sopenharmony_ci retval = security_sem_alloc(&sma->sem_perm); 55462306a36Sopenharmony_ci if (retval) { 55562306a36Sopenharmony_ci kvfree(sma); 55662306a36Sopenharmony_ci return retval; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci for (i = 0; i < nsems; i++) { 56062306a36Sopenharmony_ci INIT_LIST_HEAD(&sma->sems[i].pending_alter); 56162306a36Sopenharmony_ci INIT_LIST_HEAD(&sma->sems[i].pending_const); 56262306a36Sopenharmony_ci spin_lock_init(&sma->sems[i].lock); 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci sma->complex_count = 0; 56662306a36Sopenharmony_ci sma->use_global_lock = USE_GLOBAL_LOCK_HYSTERESIS; 56762306a36Sopenharmony_ci INIT_LIST_HEAD(&sma->pending_alter); 56862306a36Sopenharmony_ci INIT_LIST_HEAD(&sma->pending_const); 56962306a36Sopenharmony_ci INIT_LIST_HEAD(&sma->list_id); 57062306a36Sopenharmony_ci sma->sem_nsems = nsems; 57162306a36Sopenharmony_ci sma->sem_ctime = ktime_get_real_seconds(); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* ipc_addid() locks sma upon success. */ 57462306a36Sopenharmony_ci retval = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni); 57562306a36Sopenharmony_ci if (retval < 0) { 57662306a36Sopenharmony_ci ipc_rcu_putref(&sma->sem_perm, sem_rcu_free); 57762306a36Sopenharmony_ci return retval; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci ns->used_sems += nsems; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci sem_unlock(sma, -1); 58262306a36Sopenharmony_ci rcu_read_unlock(); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci return sma->sem_perm.id; 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci/* 58962306a36Sopenharmony_ci * Called with sem_ids.rwsem and ipcp locked. 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_cistatic int sem_more_checks(struct kern_ipc_perm *ipcp, struct ipc_params *params) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct sem_array *sma; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci sma = container_of(ipcp, struct sem_array, sem_perm); 59662306a36Sopenharmony_ci if (params->u.nsems > sma->sem_nsems) 59762306a36Sopenharmony_ci return -EINVAL; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cilong ksys_semget(key_t key, int nsems, int semflg) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci struct ipc_namespace *ns; 60562306a36Sopenharmony_ci static const struct ipc_ops sem_ops = { 60662306a36Sopenharmony_ci .getnew = newary, 60762306a36Sopenharmony_ci .associate = security_sem_associate, 60862306a36Sopenharmony_ci .more_checks = sem_more_checks, 60962306a36Sopenharmony_ci }; 61062306a36Sopenharmony_ci struct ipc_params sem_params; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci ns = current->nsproxy->ipc_ns; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (nsems < 0 || nsems > ns->sc_semmsl) 61562306a36Sopenharmony_ci return -EINVAL; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci sem_params.key = key; 61862306a36Sopenharmony_ci sem_params.flg = semflg; 61962306a36Sopenharmony_ci sem_params.u.nsems = nsems; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params); 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ciSYSCALL_DEFINE3(semget, key_t, key, int, nsems, int, semflg) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci return ksys_semget(key, nsems, semflg); 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci/** 63062306a36Sopenharmony_ci * perform_atomic_semop[_slow] - Attempt to perform semaphore 63162306a36Sopenharmony_ci * operations on a given array. 63262306a36Sopenharmony_ci * @sma: semaphore array 63362306a36Sopenharmony_ci * @q: struct sem_queue that describes the operation 63462306a36Sopenharmony_ci * 63562306a36Sopenharmony_ci * Caller blocking are as follows, based the value 63662306a36Sopenharmony_ci * indicated by the semaphore operation (sem_op): 63762306a36Sopenharmony_ci * 63862306a36Sopenharmony_ci * (1) >0 never blocks. 63962306a36Sopenharmony_ci * (2) 0 (wait-for-zero operation): semval is non-zero. 64062306a36Sopenharmony_ci * (3) <0 attempting to decrement semval to a value smaller than zero. 64162306a36Sopenharmony_ci * 64262306a36Sopenharmony_ci * Returns 0 if the operation was possible. 64362306a36Sopenharmony_ci * Returns 1 if the operation is impossible, the caller must sleep. 64462306a36Sopenharmony_ci * Returns <0 for error codes. 64562306a36Sopenharmony_ci */ 64662306a36Sopenharmony_cistatic int perform_atomic_semop_slow(struct sem_array *sma, struct sem_queue *q) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci int result, sem_op, nsops; 64962306a36Sopenharmony_ci struct pid *pid; 65062306a36Sopenharmony_ci struct sembuf *sop; 65162306a36Sopenharmony_ci struct sem *curr; 65262306a36Sopenharmony_ci struct sembuf *sops; 65362306a36Sopenharmony_ci struct sem_undo *un; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci sops = q->sops; 65662306a36Sopenharmony_ci nsops = q->nsops; 65762306a36Sopenharmony_ci un = q->undo; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci for (sop = sops; sop < sops + nsops; sop++) { 66062306a36Sopenharmony_ci int idx = array_index_nospec(sop->sem_num, sma->sem_nsems); 66162306a36Sopenharmony_ci curr = &sma->sems[idx]; 66262306a36Sopenharmony_ci sem_op = sop->sem_op; 66362306a36Sopenharmony_ci result = curr->semval; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (!sem_op && result) 66662306a36Sopenharmony_ci goto would_block; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci result += sem_op; 66962306a36Sopenharmony_ci if (result < 0) 67062306a36Sopenharmony_ci goto would_block; 67162306a36Sopenharmony_ci if (result > SEMVMX) 67262306a36Sopenharmony_ci goto out_of_range; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci if (sop->sem_flg & SEM_UNDO) { 67562306a36Sopenharmony_ci int undo = un->semadj[sop->sem_num] - sem_op; 67662306a36Sopenharmony_ci /* Exceeding the undo range is an error. */ 67762306a36Sopenharmony_ci if (undo < (-SEMAEM - 1) || undo > SEMAEM) 67862306a36Sopenharmony_ci goto out_of_range; 67962306a36Sopenharmony_ci un->semadj[sop->sem_num] = undo; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci curr->semval = result; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci sop--; 68662306a36Sopenharmony_ci pid = q->pid; 68762306a36Sopenharmony_ci while (sop >= sops) { 68862306a36Sopenharmony_ci ipc_update_pid(&sma->sems[sop->sem_num].sempid, pid); 68962306a36Sopenharmony_ci sop--; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci return 0; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ciout_of_range: 69562306a36Sopenharmony_ci result = -ERANGE; 69662306a36Sopenharmony_ci goto undo; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ciwould_block: 69962306a36Sopenharmony_ci q->blocking = sop; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (sop->sem_flg & IPC_NOWAIT) 70262306a36Sopenharmony_ci result = -EAGAIN; 70362306a36Sopenharmony_ci else 70462306a36Sopenharmony_ci result = 1; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ciundo: 70762306a36Sopenharmony_ci sop--; 70862306a36Sopenharmony_ci while (sop >= sops) { 70962306a36Sopenharmony_ci sem_op = sop->sem_op; 71062306a36Sopenharmony_ci sma->sems[sop->sem_num].semval -= sem_op; 71162306a36Sopenharmony_ci if (sop->sem_flg & SEM_UNDO) 71262306a36Sopenharmony_ci un->semadj[sop->sem_num] += sem_op; 71362306a36Sopenharmony_ci sop--; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci return result; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic int perform_atomic_semop(struct sem_array *sma, struct sem_queue *q) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci int result, sem_op, nsops; 72262306a36Sopenharmony_ci struct sembuf *sop; 72362306a36Sopenharmony_ci struct sem *curr; 72462306a36Sopenharmony_ci struct sembuf *sops; 72562306a36Sopenharmony_ci struct sem_undo *un; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci sops = q->sops; 72862306a36Sopenharmony_ci nsops = q->nsops; 72962306a36Sopenharmony_ci un = q->undo; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (unlikely(q->dupsop)) 73262306a36Sopenharmony_ci return perform_atomic_semop_slow(sma, q); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci /* 73562306a36Sopenharmony_ci * We scan the semaphore set twice, first to ensure that the entire 73662306a36Sopenharmony_ci * operation can succeed, therefore avoiding any pointless writes 73762306a36Sopenharmony_ci * to shared memory and having to undo such changes in order to block 73862306a36Sopenharmony_ci * until the operations can go through. 73962306a36Sopenharmony_ci */ 74062306a36Sopenharmony_ci for (sop = sops; sop < sops + nsops; sop++) { 74162306a36Sopenharmony_ci int idx = array_index_nospec(sop->sem_num, sma->sem_nsems); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci curr = &sma->sems[idx]; 74462306a36Sopenharmony_ci sem_op = sop->sem_op; 74562306a36Sopenharmony_ci result = curr->semval; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (!sem_op && result) 74862306a36Sopenharmony_ci goto would_block; /* wait-for-zero */ 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci result += sem_op; 75162306a36Sopenharmony_ci if (result < 0) 75262306a36Sopenharmony_ci goto would_block; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (result > SEMVMX) 75562306a36Sopenharmony_ci return -ERANGE; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci if (sop->sem_flg & SEM_UNDO) { 75862306a36Sopenharmony_ci int undo = un->semadj[sop->sem_num] - sem_op; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci /* Exceeding the undo range is an error. */ 76162306a36Sopenharmony_ci if (undo < (-SEMAEM - 1) || undo > SEMAEM) 76262306a36Sopenharmony_ci return -ERANGE; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci for (sop = sops; sop < sops + nsops; sop++) { 76762306a36Sopenharmony_ci curr = &sma->sems[sop->sem_num]; 76862306a36Sopenharmony_ci sem_op = sop->sem_op; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci if (sop->sem_flg & SEM_UNDO) { 77162306a36Sopenharmony_ci int undo = un->semadj[sop->sem_num] - sem_op; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci un->semadj[sop->sem_num] = undo; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci curr->semval += sem_op; 77662306a36Sopenharmony_ci ipc_update_pid(&curr->sempid, q->pid); 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci return 0; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ciwould_block: 78262306a36Sopenharmony_ci q->blocking = sop; 78362306a36Sopenharmony_ci return sop->sem_flg & IPC_NOWAIT ? -EAGAIN : 1; 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic inline void wake_up_sem_queue_prepare(struct sem_queue *q, int error, 78762306a36Sopenharmony_ci struct wake_q_head *wake_q) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct task_struct *sleeper; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci sleeper = get_task_struct(q->sleeper); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* see SEM_BARRIER_2 for purpose/pairing */ 79462306a36Sopenharmony_ci smp_store_release(&q->status, error); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci wake_q_add_safe(wake_q, sleeper); 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic void unlink_queue(struct sem_array *sma, struct sem_queue *q) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci list_del(&q->list); 80262306a36Sopenharmony_ci if (q->nsops > 1) 80362306a36Sopenharmony_ci sma->complex_count--; 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci/** check_restart(sma, q) 80762306a36Sopenharmony_ci * @sma: semaphore array 80862306a36Sopenharmony_ci * @q: the operation that just completed 80962306a36Sopenharmony_ci * 81062306a36Sopenharmony_ci * update_queue is O(N^2) when it restarts scanning the whole queue of 81162306a36Sopenharmony_ci * waiting operations. Therefore this function checks if the restart is 81262306a36Sopenharmony_ci * really necessary. It is called after a previously waiting operation 81362306a36Sopenharmony_ci * modified the array. 81462306a36Sopenharmony_ci * Note that wait-for-zero operations are handled without restart. 81562306a36Sopenharmony_ci */ 81662306a36Sopenharmony_cistatic inline int check_restart(struct sem_array *sma, struct sem_queue *q) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci /* pending complex alter operations are too difficult to analyse */ 81962306a36Sopenharmony_ci if (!list_empty(&sma->pending_alter)) 82062306a36Sopenharmony_ci return 1; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci /* we were a sleeping complex operation. Too difficult */ 82362306a36Sopenharmony_ci if (q->nsops > 1) 82462306a36Sopenharmony_ci return 1; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci /* It is impossible that someone waits for the new value: 82762306a36Sopenharmony_ci * - complex operations always restart. 82862306a36Sopenharmony_ci * - wait-for-zero are handled separately. 82962306a36Sopenharmony_ci * - q is a previously sleeping simple operation that 83062306a36Sopenharmony_ci * altered the array. It must be a decrement, because 83162306a36Sopenharmony_ci * simple increments never sleep. 83262306a36Sopenharmony_ci * - If there are older (higher priority) decrements 83362306a36Sopenharmony_ci * in the queue, then they have observed the original 83462306a36Sopenharmony_ci * semval value and couldn't proceed. The operation 83562306a36Sopenharmony_ci * decremented to value - thus they won't proceed either. 83662306a36Sopenharmony_ci */ 83762306a36Sopenharmony_ci return 0; 83862306a36Sopenharmony_ci} 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci/** 84162306a36Sopenharmony_ci * wake_const_ops - wake up non-alter tasks 84262306a36Sopenharmony_ci * @sma: semaphore array. 84362306a36Sopenharmony_ci * @semnum: semaphore that was modified. 84462306a36Sopenharmony_ci * @wake_q: lockless wake-queue head. 84562306a36Sopenharmony_ci * 84662306a36Sopenharmony_ci * wake_const_ops must be called after a semaphore in a semaphore array 84762306a36Sopenharmony_ci * was set to 0. If complex const operations are pending, wake_const_ops must 84862306a36Sopenharmony_ci * be called with semnum = -1, as well as with the number of each modified 84962306a36Sopenharmony_ci * semaphore. 85062306a36Sopenharmony_ci * The tasks that must be woken up are added to @wake_q. The return code 85162306a36Sopenharmony_ci * is stored in q->pid. 85262306a36Sopenharmony_ci * The function returns 1 if at least one operation was completed successfully. 85362306a36Sopenharmony_ci */ 85462306a36Sopenharmony_cistatic int wake_const_ops(struct sem_array *sma, int semnum, 85562306a36Sopenharmony_ci struct wake_q_head *wake_q) 85662306a36Sopenharmony_ci{ 85762306a36Sopenharmony_ci struct sem_queue *q, *tmp; 85862306a36Sopenharmony_ci struct list_head *pending_list; 85962306a36Sopenharmony_ci int semop_completed = 0; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (semnum == -1) 86262306a36Sopenharmony_ci pending_list = &sma->pending_const; 86362306a36Sopenharmony_ci else 86462306a36Sopenharmony_ci pending_list = &sma->sems[semnum].pending_const; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci list_for_each_entry_safe(q, tmp, pending_list, list) { 86762306a36Sopenharmony_ci int error = perform_atomic_semop(sma, q); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci if (error > 0) 87062306a36Sopenharmony_ci continue; 87162306a36Sopenharmony_ci /* operation completed, remove from queue & wakeup */ 87262306a36Sopenharmony_ci unlink_queue(sma, q); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci wake_up_sem_queue_prepare(q, error, wake_q); 87562306a36Sopenharmony_ci if (error == 0) 87662306a36Sopenharmony_ci semop_completed = 1; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci return semop_completed; 88062306a36Sopenharmony_ci} 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci/** 88362306a36Sopenharmony_ci * do_smart_wakeup_zero - wakeup all wait for zero tasks 88462306a36Sopenharmony_ci * @sma: semaphore array 88562306a36Sopenharmony_ci * @sops: operations that were performed 88662306a36Sopenharmony_ci * @nsops: number of operations 88762306a36Sopenharmony_ci * @wake_q: lockless wake-queue head 88862306a36Sopenharmony_ci * 88962306a36Sopenharmony_ci * Checks all required queue for wait-for-zero operations, based 89062306a36Sopenharmony_ci * on the actual changes that were performed on the semaphore array. 89162306a36Sopenharmony_ci * The function returns 1 if at least one operation was completed successfully. 89262306a36Sopenharmony_ci */ 89362306a36Sopenharmony_cistatic int do_smart_wakeup_zero(struct sem_array *sma, struct sembuf *sops, 89462306a36Sopenharmony_ci int nsops, struct wake_q_head *wake_q) 89562306a36Sopenharmony_ci{ 89662306a36Sopenharmony_ci int i; 89762306a36Sopenharmony_ci int semop_completed = 0; 89862306a36Sopenharmony_ci int got_zero = 0; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci /* first: the per-semaphore queues, if known */ 90162306a36Sopenharmony_ci if (sops) { 90262306a36Sopenharmony_ci for (i = 0; i < nsops; i++) { 90362306a36Sopenharmony_ci int num = sops[i].sem_num; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci if (sma->sems[num].semval == 0) { 90662306a36Sopenharmony_ci got_zero = 1; 90762306a36Sopenharmony_ci semop_completed |= wake_const_ops(sma, num, wake_q); 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci } else { 91162306a36Sopenharmony_ci /* 91262306a36Sopenharmony_ci * No sops means modified semaphores not known. 91362306a36Sopenharmony_ci * Assume all were changed. 91462306a36Sopenharmony_ci */ 91562306a36Sopenharmony_ci for (i = 0; i < sma->sem_nsems; i++) { 91662306a36Sopenharmony_ci if (sma->sems[i].semval == 0) { 91762306a36Sopenharmony_ci got_zero = 1; 91862306a36Sopenharmony_ci semop_completed |= wake_const_ops(sma, i, wake_q); 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci /* 92362306a36Sopenharmony_ci * If one of the modified semaphores got 0, 92462306a36Sopenharmony_ci * then check the global queue, too. 92562306a36Sopenharmony_ci */ 92662306a36Sopenharmony_ci if (got_zero) 92762306a36Sopenharmony_ci semop_completed |= wake_const_ops(sma, -1, wake_q); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci return semop_completed; 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci/** 93462306a36Sopenharmony_ci * update_queue - look for tasks that can be completed. 93562306a36Sopenharmony_ci * @sma: semaphore array. 93662306a36Sopenharmony_ci * @semnum: semaphore that was modified. 93762306a36Sopenharmony_ci * @wake_q: lockless wake-queue head. 93862306a36Sopenharmony_ci * 93962306a36Sopenharmony_ci * update_queue must be called after a semaphore in a semaphore array 94062306a36Sopenharmony_ci * was modified. If multiple semaphores were modified, update_queue must 94162306a36Sopenharmony_ci * be called with semnum = -1, as well as with the number of each modified 94262306a36Sopenharmony_ci * semaphore. 94362306a36Sopenharmony_ci * The tasks that must be woken up are added to @wake_q. The return code 94462306a36Sopenharmony_ci * is stored in q->pid. 94562306a36Sopenharmony_ci * The function internally checks if const operations can now succeed. 94662306a36Sopenharmony_ci * 94762306a36Sopenharmony_ci * The function return 1 if at least one semop was completed successfully. 94862306a36Sopenharmony_ci */ 94962306a36Sopenharmony_cistatic int update_queue(struct sem_array *sma, int semnum, struct wake_q_head *wake_q) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci struct sem_queue *q, *tmp; 95262306a36Sopenharmony_ci struct list_head *pending_list; 95362306a36Sopenharmony_ci int semop_completed = 0; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci if (semnum == -1) 95662306a36Sopenharmony_ci pending_list = &sma->pending_alter; 95762306a36Sopenharmony_ci else 95862306a36Sopenharmony_ci pending_list = &sma->sems[semnum].pending_alter; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ciagain: 96162306a36Sopenharmony_ci list_for_each_entry_safe(q, tmp, pending_list, list) { 96262306a36Sopenharmony_ci int error, restart; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci /* If we are scanning the single sop, per-semaphore list of 96562306a36Sopenharmony_ci * one semaphore and that semaphore is 0, then it is not 96662306a36Sopenharmony_ci * necessary to scan further: simple increments 96762306a36Sopenharmony_ci * that affect only one entry succeed immediately and cannot 96862306a36Sopenharmony_ci * be in the per semaphore pending queue, and decrements 96962306a36Sopenharmony_ci * cannot be successful if the value is already 0. 97062306a36Sopenharmony_ci */ 97162306a36Sopenharmony_ci if (semnum != -1 && sma->sems[semnum].semval == 0) 97262306a36Sopenharmony_ci break; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci error = perform_atomic_semop(sma, q); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* Does q->sleeper still need to sleep? */ 97762306a36Sopenharmony_ci if (error > 0) 97862306a36Sopenharmony_ci continue; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci unlink_queue(sma, q); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci if (error) { 98362306a36Sopenharmony_ci restart = 0; 98462306a36Sopenharmony_ci } else { 98562306a36Sopenharmony_ci semop_completed = 1; 98662306a36Sopenharmony_ci do_smart_wakeup_zero(sma, q->sops, q->nsops, wake_q); 98762306a36Sopenharmony_ci restart = check_restart(sma, q); 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci wake_up_sem_queue_prepare(q, error, wake_q); 99162306a36Sopenharmony_ci if (restart) 99262306a36Sopenharmony_ci goto again; 99362306a36Sopenharmony_ci } 99462306a36Sopenharmony_ci return semop_completed; 99562306a36Sopenharmony_ci} 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci/** 99862306a36Sopenharmony_ci * set_semotime - set sem_otime 99962306a36Sopenharmony_ci * @sma: semaphore array 100062306a36Sopenharmony_ci * @sops: operations that modified the array, may be NULL 100162306a36Sopenharmony_ci * 100262306a36Sopenharmony_ci * sem_otime is replicated to avoid cache line trashing. 100362306a36Sopenharmony_ci * This function sets one instance to the current time. 100462306a36Sopenharmony_ci */ 100562306a36Sopenharmony_cistatic void set_semotime(struct sem_array *sma, struct sembuf *sops) 100662306a36Sopenharmony_ci{ 100762306a36Sopenharmony_ci if (sops == NULL) { 100862306a36Sopenharmony_ci sma->sems[0].sem_otime = ktime_get_real_seconds(); 100962306a36Sopenharmony_ci } else { 101062306a36Sopenharmony_ci sma->sems[sops[0].sem_num].sem_otime = 101162306a36Sopenharmony_ci ktime_get_real_seconds(); 101262306a36Sopenharmony_ci } 101362306a36Sopenharmony_ci} 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci/** 101662306a36Sopenharmony_ci * do_smart_update - optimized update_queue 101762306a36Sopenharmony_ci * @sma: semaphore array 101862306a36Sopenharmony_ci * @sops: operations that were performed 101962306a36Sopenharmony_ci * @nsops: number of operations 102062306a36Sopenharmony_ci * @otime: force setting otime 102162306a36Sopenharmony_ci * @wake_q: lockless wake-queue head 102262306a36Sopenharmony_ci * 102362306a36Sopenharmony_ci * do_smart_update() does the required calls to update_queue and wakeup_zero, 102462306a36Sopenharmony_ci * based on the actual changes that were performed on the semaphore array. 102562306a36Sopenharmony_ci * Note that the function does not do the actual wake-up: the caller is 102662306a36Sopenharmony_ci * responsible for calling wake_up_q(). 102762306a36Sopenharmony_ci * It is safe to perform this call after dropping all locks. 102862306a36Sopenharmony_ci */ 102962306a36Sopenharmony_cistatic void do_smart_update(struct sem_array *sma, struct sembuf *sops, int nsops, 103062306a36Sopenharmony_ci int otime, struct wake_q_head *wake_q) 103162306a36Sopenharmony_ci{ 103262306a36Sopenharmony_ci int i; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci otime |= do_smart_wakeup_zero(sma, sops, nsops, wake_q); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci if (!list_empty(&sma->pending_alter)) { 103762306a36Sopenharmony_ci /* semaphore array uses the global queue - just process it. */ 103862306a36Sopenharmony_ci otime |= update_queue(sma, -1, wake_q); 103962306a36Sopenharmony_ci } else { 104062306a36Sopenharmony_ci if (!sops) { 104162306a36Sopenharmony_ci /* 104262306a36Sopenharmony_ci * No sops, thus the modified semaphores are not 104362306a36Sopenharmony_ci * known. Check all. 104462306a36Sopenharmony_ci */ 104562306a36Sopenharmony_ci for (i = 0; i < sma->sem_nsems; i++) 104662306a36Sopenharmony_ci otime |= update_queue(sma, i, wake_q); 104762306a36Sopenharmony_ci } else { 104862306a36Sopenharmony_ci /* 104962306a36Sopenharmony_ci * Check the semaphores that were increased: 105062306a36Sopenharmony_ci * - No complex ops, thus all sleeping ops are 105162306a36Sopenharmony_ci * decrease. 105262306a36Sopenharmony_ci * - if we decreased the value, then any sleeping 105362306a36Sopenharmony_ci * semaphore ops won't be able to run: If the 105462306a36Sopenharmony_ci * previous value was too small, then the new 105562306a36Sopenharmony_ci * value will be too small, too. 105662306a36Sopenharmony_ci */ 105762306a36Sopenharmony_ci for (i = 0; i < nsops; i++) { 105862306a36Sopenharmony_ci if (sops[i].sem_op > 0) { 105962306a36Sopenharmony_ci otime |= update_queue(sma, 106062306a36Sopenharmony_ci sops[i].sem_num, wake_q); 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci if (otime) 106662306a36Sopenharmony_ci set_semotime(sma, sops); 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci/* 107062306a36Sopenharmony_ci * check_qop: Test if a queued operation sleeps on the semaphore semnum 107162306a36Sopenharmony_ci */ 107262306a36Sopenharmony_cistatic int check_qop(struct sem_array *sma, int semnum, struct sem_queue *q, 107362306a36Sopenharmony_ci bool count_zero) 107462306a36Sopenharmony_ci{ 107562306a36Sopenharmony_ci struct sembuf *sop = q->blocking; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci /* 107862306a36Sopenharmony_ci * Linux always (since 0.99.10) reported a task as sleeping on all 107962306a36Sopenharmony_ci * semaphores. This violates SUS, therefore it was changed to the 108062306a36Sopenharmony_ci * standard compliant behavior. 108162306a36Sopenharmony_ci * Give the administrators a chance to notice that an application 108262306a36Sopenharmony_ci * might misbehave because it relies on the Linux behavior. 108362306a36Sopenharmony_ci */ 108462306a36Sopenharmony_ci pr_info_once("semctl(GETNCNT/GETZCNT) is since 3.16 Single Unix Specification compliant.\n" 108562306a36Sopenharmony_ci "The task %s (%d) triggered the difference, watch for misbehavior.\n", 108662306a36Sopenharmony_ci current->comm, task_pid_nr(current)); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci if (sop->sem_num != semnum) 108962306a36Sopenharmony_ci return 0; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci if (count_zero && sop->sem_op == 0) 109262306a36Sopenharmony_ci return 1; 109362306a36Sopenharmony_ci if (!count_zero && sop->sem_op < 0) 109462306a36Sopenharmony_ci return 1; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci return 0; 109762306a36Sopenharmony_ci} 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci/* The following counts are associated to each semaphore: 110062306a36Sopenharmony_ci * semncnt number of tasks waiting on semval being nonzero 110162306a36Sopenharmony_ci * semzcnt number of tasks waiting on semval being zero 110262306a36Sopenharmony_ci * 110362306a36Sopenharmony_ci * Per definition, a task waits only on the semaphore of the first semop 110462306a36Sopenharmony_ci * that cannot proceed, even if additional operation would block, too. 110562306a36Sopenharmony_ci */ 110662306a36Sopenharmony_cistatic int count_semcnt(struct sem_array *sma, ushort semnum, 110762306a36Sopenharmony_ci bool count_zero) 110862306a36Sopenharmony_ci{ 110962306a36Sopenharmony_ci struct list_head *l; 111062306a36Sopenharmony_ci struct sem_queue *q; 111162306a36Sopenharmony_ci int semcnt; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci semcnt = 0; 111462306a36Sopenharmony_ci /* First: check the simple operations. They are easy to evaluate */ 111562306a36Sopenharmony_ci if (count_zero) 111662306a36Sopenharmony_ci l = &sma->sems[semnum].pending_const; 111762306a36Sopenharmony_ci else 111862306a36Sopenharmony_ci l = &sma->sems[semnum].pending_alter; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci list_for_each_entry(q, l, list) { 112162306a36Sopenharmony_ci /* all task on a per-semaphore list sleep on exactly 112262306a36Sopenharmony_ci * that semaphore 112362306a36Sopenharmony_ci */ 112462306a36Sopenharmony_ci semcnt++; 112562306a36Sopenharmony_ci } 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci /* Then: check the complex operations. */ 112862306a36Sopenharmony_ci list_for_each_entry(q, &sma->pending_alter, list) { 112962306a36Sopenharmony_ci semcnt += check_qop(sma, semnum, q, count_zero); 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci if (count_zero) { 113262306a36Sopenharmony_ci list_for_each_entry(q, &sma->pending_const, list) { 113362306a36Sopenharmony_ci semcnt += check_qop(sma, semnum, q, count_zero); 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci return semcnt; 113762306a36Sopenharmony_ci} 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci/* Free a semaphore set. freeary() is called with sem_ids.rwsem locked 114062306a36Sopenharmony_ci * as a writer and the spinlock for this semaphore set hold. sem_ids.rwsem 114162306a36Sopenharmony_ci * remains locked on exit. 114262306a36Sopenharmony_ci */ 114362306a36Sopenharmony_cistatic void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) 114462306a36Sopenharmony_ci{ 114562306a36Sopenharmony_ci struct sem_undo *un, *tu; 114662306a36Sopenharmony_ci struct sem_queue *q, *tq; 114762306a36Sopenharmony_ci struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm); 114862306a36Sopenharmony_ci int i; 114962306a36Sopenharmony_ci DEFINE_WAKE_Q(wake_q); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci /* Free the existing undo structures for this semaphore set. */ 115262306a36Sopenharmony_ci ipc_assert_locked_object(&sma->sem_perm); 115362306a36Sopenharmony_ci list_for_each_entry_safe(un, tu, &sma->list_id, list_id) { 115462306a36Sopenharmony_ci list_del(&un->list_id); 115562306a36Sopenharmony_ci spin_lock(&un->ulp->lock); 115662306a36Sopenharmony_ci un->semid = -1; 115762306a36Sopenharmony_ci list_del_rcu(&un->list_proc); 115862306a36Sopenharmony_ci spin_unlock(&un->ulp->lock); 115962306a36Sopenharmony_ci kvfree_rcu(un, rcu); 116062306a36Sopenharmony_ci } 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci /* Wake up all pending processes and let them fail with EIDRM. */ 116362306a36Sopenharmony_ci list_for_each_entry_safe(q, tq, &sma->pending_const, list) { 116462306a36Sopenharmony_ci unlink_queue(sma, q); 116562306a36Sopenharmony_ci wake_up_sem_queue_prepare(q, -EIDRM, &wake_q); 116662306a36Sopenharmony_ci } 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci list_for_each_entry_safe(q, tq, &sma->pending_alter, list) { 116962306a36Sopenharmony_ci unlink_queue(sma, q); 117062306a36Sopenharmony_ci wake_up_sem_queue_prepare(q, -EIDRM, &wake_q); 117162306a36Sopenharmony_ci } 117262306a36Sopenharmony_ci for (i = 0; i < sma->sem_nsems; i++) { 117362306a36Sopenharmony_ci struct sem *sem = &sma->sems[i]; 117462306a36Sopenharmony_ci list_for_each_entry_safe(q, tq, &sem->pending_const, list) { 117562306a36Sopenharmony_ci unlink_queue(sma, q); 117662306a36Sopenharmony_ci wake_up_sem_queue_prepare(q, -EIDRM, &wake_q); 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci list_for_each_entry_safe(q, tq, &sem->pending_alter, list) { 117962306a36Sopenharmony_ci unlink_queue(sma, q); 118062306a36Sopenharmony_ci wake_up_sem_queue_prepare(q, -EIDRM, &wake_q); 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ci ipc_update_pid(&sem->sempid, NULL); 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci /* Remove the semaphore set from the IDR */ 118662306a36Sopenharmony_ci sem_rmid(ns, sma); 118762306a36Sopenharmony_ci sem_unlock(sma, -1); 118862306a36Sopenharmony_ci rcu_read_unlock(); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci wake_up_q(&wake_q); 119162306a36Sopenharmony_ci ns->used_sems -= sma->sem_nsems; 119262306a36Sopenharmony_ci ipc_rcu_putref(&sma->sem_perm, sem_rcu_free); 119362306a36Sopenharmony_ci} 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_cistatic unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in, int version) 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci switch (version) { 119862306a36Sopenharmony_ci case IPC_64: 119962306a36Sopenharmony_ci return copy_to_user(buf, in, sizeof(*in)); 120062306a36Sopenharmony_ci case IPC_OLD: 120162306a36Sopenharmony_ci { 120262306a36Sopenharmony_ci struct semid_ds out; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci memset(&out, 0, sizeof(out)); 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci ipc64_perm_to_ipc_perm(&in->sem_perm, &out.sem_perm); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci out.sem_otime = in->sem_otime; 120962306a36Sopenharmony_ci out.sem_ctime = in->sem_ctime; 121062306a36Sopenharmony_ci out.sem_nsems = in->sem_nsems; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci return copy_to_user(buf, &out, sizeof(out)); 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci default: 121562306a36Sopenharmony_ci return -EINVAL; 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci} 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_cistatic time64_t get_semotime(struct sem_array *sma) 122062306a36Sopenharmony_ci{ 122162306a36Sopenharmony_ci int i; 122262306a36Sopenharmony_ci time64_t res; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci res = sma->sems[0].sem_otime; 122562306a36Sopenharmony_ci for (i = 1; i < sma->sem_nsems; i++) { 122662306a36Sopenharmony_ci time64_t to = sma->sems[i].sem_otime; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci if (to > res) 122962306a36Sopenharmony_ci res = to; 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci return res; 123262306a36Sopenharmony_ci} 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_cistatic int semctl_stat(struct ipc_namespace *ns, int semid, 123562306a36Sopenharmony_ci int cmd, struct semid64_ds *semid64) 123662306a36Sopenharmony_ci{ 123762306a36Sopenharmony_ci struct sem_array *sma; 123862306a36Sopenharmony_ci time64_t semotime; 123962306a36Sopenharmony_ci int err; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci memset(semid64, 0, sizeof(*semid64)); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci rcu_read_lock(); 124462306a36Sopenharmony_ci if (cmd == SEM_STAT || cmd == SEM_STAT_ANY) { 124562306a36Sopenharmony_ci sma = sem_obtain_object(ns, semid); 124662306a36Sopenharmony_ci if (IS_ERR(sma)) { 124762306a36Sopenharmony_ci err = PTR_ERR(sma); 124862306a36Sopenharmony_ci goto out_unlock; 124962306a36Sopenharmony_ci } 125062306a36Sopenharmony_ci } else { /* IPC_STAT */ 125162306a36Sopenharmony_ci sma = sem_obtain_object_check(ns, semid); 125262306a36Sopenharmony_ci if (IS_ERR(sma)) { 125362306a36Sopenharmony_ci err = PTR_ERR(sma); 125462306a36Sopenharmony_ci goto out_unlock; 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci /* see comment for SHM_STAT_ANY */ 125962306a36Sopenharmony_ci if (cmd == SEM_STAT_ANY) 126062306a36Sopenharmony_ci audit_ipc_obj(&sma->sem_perm); 126162306a36Sopenharmony_ci else { 126262306a36Sopenharmony_ci err = -EACCES; 126362306a36Sopenharmony_ci if (ipcperms(ns, &sma->sem_perm, S_IRUGO)) 126462306a36Sopenharmony_ci goto out_unlock; 126562306a36Sopenharmony_ci } 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci err = security_sem_semctl(&sma->sem_perm, cmd); 126862306a36Sopenharmony_ci if (err) 126962306a36Sopenharmony_ci goto out_unlock; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci ipc_lock_object(&sma->sem_perm); 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) { 127462306a36Sopenharmony_ci ipc_unlock_object(&sma->sem_perm); 127562306a36Sopenharmony_ci err = -EIDRM; 127662306a36Sopenharmony_ci goto out_unlock; 127762306a36Sopenharmony_ci } 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci kernel_to_ipc64_perm(&sma->sem_perm, &semid64->sem_perm); 128062306a36Sopenharmony_ci semotime = get_semotime(sma); 128162306a36Sopenharmony_ci semid64->sem_otime = semotime; 128262306a36Sopenharmony_ci semid64->sem_ctime = sma->sem_ctime; 128362306a36Sopenharmony_ci#ifndef CONFIG_64BIT 128462306a36Sopenharmony_ci semid64->sem_otime_high = semotime >> 32; 128562306a36Sopenharmony_ci semid64->sem_ctime_high = sma->sem_ctime >> 32; 128662306a36Sopenharmony_ci#endif 128762306a36Sopenharmony_ci semid64->sem_nsems = sma->sem_nsems; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci if (cmd == IPC_STAT) { 129062306a36Sopenharmony_ci /* 129162306a36Sopenharmony_ci * As defined in SUS: 129262306a36Sopenharmony_ci * Return 0 on success 129362306a36Sopenharmony_ci */ 129462306a36Sopenharmony_ci err = 0; 129562306a36Sopenharmony_ci } else { 129662306a36Sopenharmony_ci /* 129762306a36Sopenharmony_ci * SEM_STAT and SEM_STAT_ANY (both Linux specific) 129862306a36Sopenharmony_ci * Return the full id, including the sequence number 129962306a36Sopenharmony_ci */ 130062306a36Sopenharmony_ci err = sma->sem_perm.id; 130162306a36Sopenharmony_ci } 130262306a36Sopenharmony_ci ipc_unlock_object(&sma->sem_perm); 130362306a36Sopenharmony_ciout_unlock: 130462306a36Sopenharmony_ci rcu_read_unlock(); 130562306a36Sopenharmony_ci return err; 130662306a36Sopenharmony_ci} 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_cistatic int semctl_info(struct ipc_namespace *ns, int semid, 130962306a36Sopenharmony_ci int cmd, void __user *p) 131062306a36Sopenharmony_ci{ 131162306a36Sopenharmony_ci struct seminfo seminfo; 131262306a36Sopenharmony_ci int max_idx; 131362306a36Sopenharmony_ci int err; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci err = security_sem_semctl(NULL, cmd); 131662306a36Sopenharmony_ci if (err) 131762306a36Sopenharmony_ci return err; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci memset(&seminfo, 0, sizeof(seminfo)); 132062306a36Sopenharmony_ci seminfo.semmni = ns->sc_semmni; 132162306a36Sopenharmony_ci seminfo.semmns = ns->sc_semmns; 132262306a36Sopenharmony_ci seminfo.semmsl = ns->sc_semmsl; 132362306a36Sopenharmony_ci seminfo.semopm = ns->sc_semopm; 132462306a36Sopenharmony_ci seminfo.semvmx = SEMVMX; 132562306a36Sopenharmony_ci seminfo.semmnu = SEMMNU; 132662306a36Sopenharmony_ci seminfo.semmap = SEMMAP; 132762306a36Sopenharmony_ci seminfo.semume = SEMUME; 132862306a36Sopenharmony_ci down_read(&sem_ids(ns).rwsem); 132962306a36Sopenharmony_ci if (cmd == SEM_INFO) { 133062306a36Sopenharmony_ci seminfo.semusz = sem_ids(ns).in_use; 133162306a36Sopenharmony_ci seminfo.semaem = ns->used_sems; 133262306a36Sopenharmony_ci } else { 133362306a36Sopenharmony_ci seminfo.semusz = SEMUSZ; 133462306a36Sopenharmony_ci seminfo.semaem = SEMAEM; 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci max_idx = ipc_get_maxidx(&sem_ids(ns)); 133762306a36Sopenharmony_ci up_read(&sem_ids(ns).rwsem); 133862306a36Sopenharmony_ci if (copy_to_user(p, &seminfo, sizeof(struct seminfo))) 133962306a36Sopenharmony_ci return -EFAULT; 134062306a36Sopenharmony_ci return (max_idx < 0) ? 0 : max_idx; 134162306a36Sopenharmony_ci} 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_cistatic int semctl_setval(struct ipc_namespace *ns, int semid, int semnum, 134462306a36Sopenharmony_ci int val) 134562306a36Sopenharmony_ci{ 134662306a36Sopenharmony_ci struct sem_undo *un; 134762306a36Sopenharmony_ci struct sem_array *sma; 134862306a36Sopenharmony_ci struct sem *curr; 134962306a36Sopenharmony_ci int err; 135062306a36Sopenharmony_ci DEFINE_WAKE_Q(wake_q); 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci if (val > SEMVMX || val < 0) 135362306a36Sopenharmony_ci return -ERANGE; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci rcu_read_lock(); 135662306a36Sopenharmony_ci sma = sem_obtain_object_check(ns, semid); 135762306a36Sopenharmony_ci if (IS_ERR(sma)) { 135862306a36Sopenharmony_ci rcu_read_unlock(); 135962306a36Sopenharmony_ci return PTR_ERR(sma); 136062306a36Sopenharmony_ci } 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci if (semnum < 0 || semnum >= sma->sem_nsems) { 136362306a36Sopenharmony_ci rcu_read_unlock(); 136462306a36Sopenharmony_ci return -EINVAL; 136562306a36Sopenharmony_ci } 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci if (ipcperms(ns, &sma->sem_perm, S_IWUGO)) { 136962306a36Sopenharmony_ci rcu_read_unlock(); 137062306a36Sopenharmony_ci return -EACCES; 137162306a36Sopenharmony_ci } 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci err = security_sem_semctl(&sma->sem_perm, SETVAL); 137462306a36Sopenharmony_ci if (err) { 137562306a36Sopenharmony_ci rcu_read_unlock(); 137662306a36Sopenharmony_ci return -EACCES; 137762306a36Sopenharmony_ci } 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci sem_lock(sma, NULL, -1); 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) { 138262306a36Sopenharmony_ci sem_unlock(sma, -1); 138362306a36Sopenharmony_ci rcu_read_unlock(); 138462306a36Sopenharmony_ci return -EIDRM; 138562306a36Sopenharmony_ci } 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci semnum = array_index_nospec(semnum, sma->sem_nsems); 138862306a36Sopenharmony_ci curr = &sma->sems[semnum]; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci ipc_assert_locked_object(&sma->sem_perm); 139162306a36Sopenharmony_ci list_for_each_entry(un, &sma->list_id, list_id) 139262306a36Sopenharmony_ci un->semadj[semnum] = 0; 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci curr->semval = val; 139562306a36Sopenharmony_ci ipc_update_pid(&curr->sempid, task_tgid(current)); 139662306a36Sopenharmony_ci sma->sem_ctime = ktime_get_real_seconds(); 139762306a36Sopenharmony_ci /* maybe some queued-up processes were waiting for this */ 139862306a36Sopenharmony_ci do_smart_update(sma, NULL, 0, 0, &wake_q); 139962306a36Sopenharmony_ci sem_unlock(sma, -1); 140062306a36Sopenharmony_ci rcu_read_unlock(); 140162306a36Sopenharmony_ci wake_up_q(&wake_q); 140262306a36Sopenharmony_ci return 0; 140362306a36Sopenharmony_ci} 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_cistatic int semctl_main(struct ipc_namespace *ns, int semid, int semnum, 140662306a36Sopenharmony_ci int cmd, void __user *p) 140762306a36Sopenharmony_ci{ 140862306a36Sopenharmony_ci struct sem_array *sma; 140962306a36Sopenharmony_ci struct sem *curr; 141062306a36Sopenharmony_ci int err, nsems; 141162306a36Sopenharmony_ci ushort fast_sem_io[SEMMSL_FAST]; 141262306a36Sopenharmony_ci ushort *sem_io = fast_sem_io; 141362306a36Sopenharmony_ci DEFINE_WAKE_Q(wake_q); 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci rcu_read_lock(); 141662306a36Sopenharmony_ci sma = sem_obtain_object_check(ns, semid); 141762306a36Sopenharmony_ci if (IS_ERR(sma)) { 141862306a36Sopenharmony_ci rcu_read_unlock(); 141962306a36Sopenharmony_ci return PTR_ERR(sma); 142062306a36Sopenharmony_ci } 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci nsems = sma->sem_nsems; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci err = -EACCES; 142562306a36Sopenharmony_ci if (ipcperms(ns, &sma->sem_perm, cmd == SETALL ? S_IWUGO : S_IRUGO)) 142662306a36Sopenharmony_ci goto out_rcu_wakeup; 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci err = security_sem_semctl(&sma->sem_perm, cmd); 142962306a36Sopenharmony_ci if (err) 143062306a36Sopenharmony_ci goto out_rcu_wakeup; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci switch (cmd) { 143362306a36Sopenharmony_ci case GETALL: 143462306a36Sopenharmony_ci { 143562306a36Sopenharmony_ci ushort __user *array = p; 143662306a36Sopenharmony_ci int i; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci sem_lock(sma, NULL, -1); 143962306a36Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) { 144062306a36Sopenharmony_ci err = -EIDRM; 144162306a36Sopenharmony_ci goto out_unlock; 144262306a36Sopenharmony_ci } 144362306a36Sopenharmony_ci if (nsems > SEMMSL_FAST) { 144462306a36Sopenharmony_ci if (!ipc_rcu_getref(&sma->sem_perm)) { 144562306a36Sopenharmony_ci err = -EIDRM; 144662306a36Sopenharmony_ci goto out_unlock; 144762306a36Sopenharmony_ci } 144862306a36Sopenharmony_ci sem_unlock(sma, -1); 144962306a36Sopenharmony_ci rcu_read_unlock(); 145062306a36Sopenharmony_ci sem_io = kvmalloc_array(nsems, sizeof(ushort), 145162306a36Sopenharmony_ci GFP_KERNEL); 145262306a36Sopenharmony_ci if (sem_io == NULL) { 145362306a36Sopenharmony_ci ipc_rcu_putref(&sma->sem_perm, sem_rcu_free); 145462306a36Sopenharmony_ci return -ENOMEM; 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci rcu_read_lock(); 145862306a36Sopenharmony_ci sem_lock_and_putref(sma); 145962306a36Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) { 146062306a36Sopenharmony_ci err = -EIDRM; 146162306a36Sopenharmony_ci goto out_unlock; 146262306a36Sopenharmony_ci } 146362306a36Sopenharmony_ci } 146462306a36Sopenharmony_ci for (i = 0; i < sma->sem_nsems; i++) 146562306a36Sopenharmony_ci sem_io[i] = sma->sems[i].semval; 146662306a36Sopenharmony_ci sem_unlock(sma, -1); 146762306a36Sopenharmony_ci rcu_read_unlock(); 146862306a36Sopenharmony_ci err = 0; 146962306a36Sopenharmony_ci if (copy_to_user(array, sem_io, nsems*sizeof(ushort))) 147062306a36Sopenharmony_ci err = -EFAULT; 147162306a36Sopenharmony_ci goto out_free; 147262306a36Sopenharmony_ci } 147362306a36Sopenharmony_ci case SETALL: 147462306a36Sopenharmony_ci { 147562306a36Sopenharmony_ci int i; 147662306a36Sopenharmony_ci struct sem_undo *un; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci if (!ipc_rcu_getref(&sma->sem_perm)) { 147962306a36Sopenharmony_ci err = -EIDRM; 148062306a36Sopenharmony_ci goto out_rcu_wakeup; 148162306a36Sopenharmony_ci } 148262306a36Sopenharmony_ci rcu_read_unlock(); 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci if (nsems > SEMMSL_FAST) { 148562306a36Sopenharmony_ci sem_io = kvmalloc_array(nsems, sizeof(ushort), 148662306a36Sopenharmony_ci GFP_KERNEL); 148762306a36Sopenharmony_ci if (sem_io == NULL) { 148862306a36Sopenharmony_ci ipc_rcu_putref(&sma->sem_perm, sem_rcu_free); 148962306a36Sopenharmony_ci return -ENOMEM; 149062306a36Sopenharmony_ci } 149162306a36Sopenharmony_ci } 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci if (copy_from_user(sem_io, p, nsems*sizeof(ushort))) { 149462306a36Sopenharmony_ci ipc_rcu_putref(&sma->sem_perm, sem_rcu_free); 149562306a36Sopenharmony_ci err = -EFAULT; 149662306a36Sopenharmony_ci goto out_free; 149762306a36Sopenharmony_ci } 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci for (i = 0; i < nsems; i++) { 150062306a36Sopenharmony_ci if (sem_io[i] > SEMVMX) { 150162306a36Sopenharmony_ci ipc_rcu_putref(&sma->sem_perm, sem_rcu_free); 150262306a36Sopenharmony_ci err = -ERANGE; 150362306a36Sopenharmony_ci goto out_free; 150462306a36Sopenharmony_ci } 150562306a36Sopenharmony_ci } 150662306a36Sopenharmony_ci rcu_read_lock(); 150762306a36Sopenharmony_ci sem_lock_and_putref(sma); 150862306a36Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) { 150962306a36Sopenharmony_ci err = -EIDRM; 151062306a36Sopenharmony_ci goto out_unlock; 151162306a36Sopenharmony_ci } 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci for (i = 0; i < nsems; i++) { 151462306a36Sopenharmony_ci sma->sems[i].semval = sem_io[i]; 151562306a36Sopenharmony_ci ipc_update_pid(&sma->sems[i].sempid, task_tgid(current)); 151662306a36Sopenharmony_ci } 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci ipc_assert_locked_object(&sma->sem_perm); 151962306a36Sopenharmony_ci list_for_each_entry(un, &sma->list_id, list_id) { 152062306a36Sopenharmony_ci for (i = 0; i < nsems; i++) 152162306a36Sopenharmony_ci un->semadj[i] = 0; 152262306a36Sopenharmony_ci } 152362306a36Sopenharmony_ci sma->sem_ctime = ktime_get_real_seconds(); 152462306a36Sopenharmony_ci /* maybe some queued-up processes were waiting for this */ 152562306a36Sopenharmony_ci do_smart_update(sma, NULL, 0, 0, &wake_q); 152662306a36Sopenharmony_ci err = 0; 152762306a36Sopenharmony_ci goto out_unlock; 152862306a36Sopenharmony_ci } 152962306a36Sopenharmony_ci /* GETVAL, GETPID, GETNCTN, GETZCNT: fall-through */ 153062306a36Sopenharmony_ci } 153162306a36Sopenharmony_ci err = -EINVAL; 153262306a36Sopenharmony_ci if (semnum < 0 || semnum >= nsems) 153362306a36Sopenharmony_ci goto out_rcu_wakeup; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci sem_lock(sma, NULL, -1); 153662306a36Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) { 153762306a36Sopenharmony_ci err = -EIDRM; 153862306a36Sopenharmony_ci goto out_unlock; 153962306a36Sopenharmony_ci } 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci semnum = array_index_nospec(semnum, nsems); 154262306a36Sopenharmony_ci curr = &sma->sems[semnum]; 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci switch (cmd) { 154562306a36Sopenharmony_ci case GETVAL: 154662306a36Sopenharmony_ci err = curr->semval; 154762306a36Sopenharmony_ci goto out_unlock; 154862306a36Sopenharmony_ci case GETPID: 154962306a36Sopenharmony_ci err = pid_vnr(curr->sempid); 155062306a36Sopenharmony_ci goto out_unlock; 155162306a36Sopenharmony_ci case GETNCNT: 155262306a36Sopenharmony_ci err = count_semcnt(sma, semnum, 0); 155362306a36Sopenharmony_ci goto out_unlock; 155462306a36Sopenharmony_ci case GETZCNT: 155562306a36Sopenharmony_ci err = count_semcnt(sma, semnum, 1); 155662306a36Sopenharmony_ci goto out_unlock; 155762306a36Sopenharmony_ci } 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ciout_unlock: 156062306a36Sopenharmony_ci sem_unlock(sma, -1); 156162306a36Sopenharmony_ciout_rcu_wakeup: 156262306a36Sopenharmony_ci rcu_read_unlock(); 156362306a36Sopenharmony_ci wake_up_q(&wake_q); 156462306a36Sopenharmony_ciout_free: 156562306a36Sopenharmony_ci if (sem_io != fast_sem_io) 156662306a36Sopenharmony_ci kvfree(sem_io); 156762306a36Sopenharmony_ci return err; 156862306a36Sopenharmony_ci} 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_cistatic inline unsigned long 157162306a36Sopenharmony_cicopy_semid_from_user(struct semid64_ds *out, void __user *buf, int version) 157262306a36Sopenharmony_ci{ 157362306a36Sopenharmony_ci switch (version) { 157462306a36Sopenharmony_ci case IPC_64: 157562306a36Sopenharmony_ci if (copy_from_user(out, buf, sizeof(*out))) 157662306a36Sopenharmony_ci return -EFAULT; 157762306a36Sopenharmony_ci return 0; 157862306a36Sopenharmony_ci case IPC_OLD: 157962306a36Sopenharmony_ci { 158062306a36Sopenharmony_ci struct semid_ds tbuf_old; 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old))) 158362306a36Sopenharmony_ci return -EFAULT; 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci out->sem_perm.uid = tbuf_old.sem_perm.uid; 158662306a36Sopenharmony_ci out->sem_perm.gid = tbuf_old.sem_perm.gid; 158762306a36Sopenharmony_ci out->sem_perm.mode = tbuf_old.sem_perm.mode; 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci return 0; 159062306a36Sopenharmony_ci } 159162306a36Sopenharmony_ci default: 159262306a36Sopenharmony_ci return -EINVAL; 159362306a36Sopenharmony_ci } 159462306a36Sopenharmony_ci} 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci/* 159762306a36Sopenharmony_ci * This function handles some semctl commands which require the rwsem 159862306a36Sopenharmony_ci * to be held in write mode. 159962306a36Sopenharmony_ci * NOTE: no locks must be held, the rwsem is taken inside this function. 160062306a36Sopenharmony_ci */ 160162306a36Sopenharmony_cistatic int semctl_down(struct ipc_namespace *ns, int semid, 160262306a36Sopenharmony_ci int cmd, struct semid64_ds *semid64) 160362306a36Sopenharmony_ci{ 160462306a36Sopenharmony_ci struct sem_array *sma; 160562306a36Sopenharmony_ci int err; 160662306a36Sopenharmony_ci struct kern_ipc_perm *ipcp; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci down_write(&sem_ids(ns).rwsem); 160962306a36Sopenharmony_ci rcu_read_lock(); 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci ipcp = ipcctl_obtain_check(ns, &sem_ids(ns), semid, cmd, 161262306a36Sopenharmony_ci &semid64->sem_perm, 0); 161362306a36Sopenharmony_ci if (IS_ERR(ipcp)) { 161462306a36Sopenharmony_ci err = PTR_ERR(ipcp); 161562306a36Sopenharmony_ci goto out_unlock1; 161662306a36Sopenharmony_ci } 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci sma = container_of(ipcp, struct sem_array, sem_perm); 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci err = security_sem_semctl(&sma->sem_perm, cmd); 162162306a36Sopenharmony_ci if (err) 162262306a36Sopenharmony_ci goto out_unlock1; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci switch (cmd) { 162562306a36Sopenharmony_ci case IPC_RMID: 162662306a36Sopenharmony_ci sem_lock(sma, NULL, -1); 162762306a36Sopenharmony_ci /* freeary unlocks the ipc object and rcu */ 162862306a36Sopenharmony_ci freeary(ns, ipcp); 162962306a36Sopenharmony_ci goto out_up; 163062306a36Sopenharmony_ci case IPC_SET: 163162306a36Sopenharmony_ci sem_lock(sma, NULL, -1); 163262306a36Sopenharmony_ci err = ipc_update_perm(&semid64->sem_perm, ipcp); 163362306a36Sopenharmony_ci if (err) 163462306a36Sopenharmony_ci goto out_unlock0; 163562306a36Sopenharmony_ci sma->sem_ctime = ktime_get_real_seconds(); 163662306a36Sopenharmony_ci break; 163762306a36Sopenharmony_ci default: 163862306a36Sopenharmony_ci err = -EINVAL; 163962306a36Sopenharmony_ci goto out_unlock1; 164062306a36Sopenharmony_ci } 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ciout_unlock0: 164362306a36Sopenharmony_ci sem_unlock(sma, -1); 164462306a36Sopenharmony_ciout_unlock1: 164562306a36Sopenharmony_ci rcu_read_unlock(); 164662306a36Sopenharmony_ciout_up: 164762306a36Sopenharmony_ci up_write(&sem_ids(ns).rwsem); 164862306a36Sopenharmony_ci return err; 164962306a36Sopenharmony_ci} 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_cistatic long ksys_semctl(int semid, int semnum, int cmd, unsigned long arg, int version) 165262306a36Sopenharmony_ci{ 165362306a36Sopenharmony_ci struct ipc_namespace *ns; 165462306a36Sopenharmony_ci void __user *p = (void __user *)arg; 165562306a36Sopenharmony_ci struct semid64_ds semid64; 165662306a36Sopenharmony_ci int err; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci if (semid < 0) 165962306a36Sopenharmony_ci return -EINVAL; 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci ns = current->nsproxy->ipc_ns; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci switch (cmd) { 166462306a36Sopenharmony_ci case IPC_INFO: 166562306a36Sopenharmony_ci case SEM_INFO: 166662306a36Sopenharmony_ci return semctl_info(ns, semid, cmd, p); 166762306a36Sopenharmony_ci case IPC_STAT: 166862306a36Sopenharmony_ci case SEM_STAT: 166962306a36Sopenharmony_ci case SEM_STAT_ANY: 167062306a36Sopenharmony_ci err = semctl_stat(ns, semid, cmd, &semid64); 167162306a36Sopenharmony_ci if (err < 0) 167262306a36Sopenharmony_ci return err; 167362306a36Sopenharmony_ci if (copy_semid_to_user(p, &semid64, version)) 167462306a36Sopenharmony_ci err = -EFAULT; 167562306a36Sopenharmony_ci return err; 167662306a36Sopenharmony_ci case GETALL: 167762306a36Sopenharmony_ci case GETVAL: 167862306a36Sopenharmony_ci case GETPID: 167962306a36Sopenharmony_ci case GETNCNT: 168062306a36Sopenharmony_ci case GETZCNT: 168162306a36Sopenharmony_ci case SETALL: 168262306a36Sopenharmony_ci return semctl_main(ns, semid, semnum, cmd, p); 168362306a36Sopenharmony_ci case SETVAL: { 168462306a36Sopenharmony_ci int val; 168562306a36Sopenharmony_ci#if defined(CONFIG_64BIT) && defined(__BIG_ENDIAN) 168662306a36Sopenharmony_ci /* big-endian 64bit */ 168762306a36Sopenharmony_ci val = arg >> 32; 168862306a36Sopenharmony_ci#else 168962306a36Sopenharmony_ci /* 32bit or little-endian 64bit */ 169062306a36Sopenharmony_ci val = arg; 169162306a36Sopenharmony_ci#endif 169262306a36Sopenharmony_ci return semctl_setval(ns, semid, semnum, val); 169362306a36Sopenharmony_ci } 169462306a36Sopenharmony_ci case IPC_SET: 169562306a36Sopenharmony_ci if (copy_semid_from_user(&semid64, p, version)) 169662306a36Sopenharmony_ci return -EFAULT; 169762306a36Sopenharmony_ci fallthrough; 169862306a36Sopenharmony_ci case IPC_RMID: 169962306a36Sopenharmony_ci return semctl_down(ns, semid, cmd, &semid64); 170062306a36Sopenharmony_ci default: 170162306a36Sopenharmony_ci return -EINVAL; 170262306a36Sopenharmony_ci } 170362306a36Sopenharmony_ci} 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ciSYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg) 170662306a36Sopenharmony_ci{ 170762306a36Sopenharmony_ci return ksys_semctl(semid, semnum, cmd, arg, IPC_64); 170862306a36Sopenharmony_ci} 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci#ifdef CONFIG_ARCH_WANT_IPC_PARSE_VERSION 171162306a36Sopenharmony_cilong ksys_old_semctl(int semid, int semnum, int cmd, unsigned long arg) 171262306a36Sopenharmony_ci{ 171362306a36Sopenharmony_ci int version = ipc_parse_version(&cmd); 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci return ksys_semctl(semid, semnum, cmd, arg, version); 171662306a36Sopenharmony_ci} 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ciSYSCALL_DEFINE4(old_semctl, int, semid, int, semnum, int, cmd, unsigned long, arg) 171962306a36Sopenharmony_ci{ 172062306a36Sopenharmony_ci return ksys_old_semctl(semid, semnum, cmd, arg); 172162306a36Sopenharmony_ci} 172262306a36Sopenharmony_ci#endif 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_cistruct compat_semid_ds { 172762306a36Sopenharmony_ci struct compat_ipc_perm sem_perm; 172862306a36Sopenharmony_ci old_time32_t sem_otime; 172962306a36Sopenharmony_ci old_time32_t sem_ctime; 173062306a36Sopenharmony_ci compat_uptr_t sem_base; 173162306a36Sopenharmony_ci compat_uptr_t sem_pending; 173262306a36Sopenharmony_ci compat_uptr_t sem_pending_last; 173362306a36Sopenharmony_ci compat_uptr_t undo; 173462306a36Sopenharmony_ci unsigned short sem_nsems; 173562306a36Sopenharmony_ci}; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_cistatic int copy_compat_semid_from_user(struct semid64_ds *out, void __user *buf, 173862306a36Sopenharmony_ci int version) 173962306a36Sopenharmony_ci{ 174062306a36Sopenharmony_ci memset(out, 0, sizeof(*out)); 174162306a36Sopenharmony_ci if (version == IPC_64) { 174262306a36Sopenharmony_ci struct compat_semid64_ds __user *p = buf; 174362306a36Sopenharmony_ci return get_compat_ipc64_perm(&out->sem_perm, &p->sem_perm); 174462306a36Sopenharmony_ci } else { 174562306a36Sopenharmony_ci struct compat_semid_ds __user *p = buf; 174662306a36Sopenharmony_ci return get_compat_ipc_perm(&out->sem_perm, &p->sem_perm); 174762306a36Sopenharmony_ci } 174862306a36Sopenharmony_ci} 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_cistatic int copy_compat_semid_to_user(void __user *buf, struct semid64_ds *in, 175162306a36Sopenharmony_ci int version) 175262306a36Sopenharmony_ci{ 175362306a36Sopenharmony_ci if (version == IPC_64) { 175462306a36Sopenharmony_ci struct compat_semid64_ds v; 175562306a36Sopenharmony_ci memset(&v, 0, sizeof(v)); 175662306a36Sopenharmony_ci to_compat_ipc64_perm(&v.sem_perm, &in->sem_perm); 175762306a36Sopenharmony_ci v.sem_otime = lower_32_bits(in->sem_otime); 175862306a36Sopenharmony_ci v.sem_otime_high = upper_32_bits(in->sem_otime); 175962306a36Sopenharmony_ci v.sem_ctime = lower_32_bits(in->sem_ctime); 176062306a36Sopenharmony_ci v.sem_ctime_high = upper_32_bits(in->sem_ctime); 176162306a36Sopenharmony_ci v.sem_nsems = in->sem_nsems; 176262306a36Sopenharmony_ci return copy_to_user(buf, &v, sizeof(v)); 176362306a36Sopenharmony_ci } else { 176462306a36Sopenharmony_ci struct compat_semid_ds v; 176562306a36Sopenharmony_ci memset(&v, 0, sizeof(v)); 176662306a36Sopenharmony_ci to_compat_ipc_perm(&v.sem_perm, &in->sem_perm); 176762306a36Sopenharmony_ci v.sem_otime = in->sem_otime; 176862306a36Sopenharmony_ci v.sem_ctime = in->sem_ctime; 176962306a36Sopenharmony_ci v.sem_nsems = in->sem_nsems; 177062306a36Sopenharmony_ci return copy_to_user(buf, &v, sizeof(v)); 177162306a36Sopenharmony_ci } 177262306a36Sopenharmony_ci} 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_cistatic long compat_ksys_semctl(int semid, int semnum, int cmd, int arg, int version) 177562306a36Sopenharmony_ci{ 177662306a36Sopenharmony_ci void __user *p = compat_ptr(arg); 177762306a36Sopenharmony_ci struct ipc_namespace *ns; 177862306a36Sopenharmony_ci struct semid64_ds semid64; 177962306a36Sopenharmony_ci int err; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci ns = current->nsproxy->ipc_ns; 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci if (semid < 0) 178462306a36Sopenharmony_ci return -EINVAL; 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci switch (cmd & (~IPC_64)) { 178762306a36Sopenharmony_ci case IPC_INFO: 178862306a36Sopenharmony_ci case SEM_INFO: 178962306a36Sopenharmony_ci return semctl_info(ns, semid, cmd, p); 179062306a36Sopenharmony_ci case IPC_STAT: 179162306a36Sopenharmony_ci case SEM_STAT: 179262306a36Sopenharmony_ci case SEM_STAT_ANY: 179362306a36Sopenharmony_ci err = semctl_stat(ns, semid, cmd, &semid64); 179462306a36Sopenharmony_ci if (err < 0) 179562306a36Sopenharmony_ci return err; 179662306a36Sopenharmony_ci if (copy_compat_semid_to_user(p, &semid64, version)) 179762306a36Sopenharmony_ci err = -EFAULT; 179862306a36Sopenharmony_ci return err; 179962306a36Sopenharmony_ci case GETVAL: 180062306a36Sopenharmony_ci case GETPID: 180162306a36Sopenharmony_ci case GETNCNT: 180262306a36Sopenharmony_ci case GETZCNT: 180362306a36Sopenharmony_ci case GETALL: 180462306a36Sopenharmony_ci case SETALL: 180562306a36Sopenharmony_ci return semctl_main(ns, semid, semnum, cmd, p); 180662306a36Sopenharmony_ci case SETVAL: 180762306a36Sopenharmony_ci return semctl_setval(ns, semid, semnum, arg); 180862306a36Sopenharmony_ci case IPC_SET: 180962306a36Sopenharmony_ci if (copy_compat_semid_from_user(&semid64, p, version)) 181062306a36Sopenharmony_ci return -EFAULT; 181162306a36Sopenharmony_ci fallthrough; 181262306a36Sopenharmony_ci case IPC_RMID: 181362306a36Sopenharmony_ci return semctl_down(ns, semid, cmd, &semid64); 181462306a36Sopenharmony_ci default: 181562306a36Sopenharmony_ci return -EINVAL; 181662306a36Sopenharmony_ci } 181762306a36Sopenharmony_ci} 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ciCOMPAT_SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, int, arg) 182062306a36Sopenharmony_ci{ 182162306a36Sopenharmony_ci return compat_ksys_semctl(semid, semnum, cmd, arg, IPC_64); 182262306a36Sopenharmony_ci} 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci#ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION 182562306a36Sopenharmony_cilong compat_ksys_old_semctl(int semid, int semnum, int cmd, int arg) 182662306a36Sopenharmony_ci{ 182762306a36Sopenharmony_ci int version = compat_ipc_parse_version(&cmd); 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci return compat_ksys_semctl(semid, semnum, cmd, arg, version); 183062306a36Sopenharmony_ci} 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ciCOMPAT_SYSCALL_DEFINE4(old_semctl, int, semid, int, semnum, int, cmd, int, arg) 183362306a36Sopenharmony_ci{ 183462306a36Sopenharmony_ci return compat_ksys_old_semctl(semid, semnum, cmd, arg); 183562306a36Sopenharmony_ci} 183662306a36Sopenharmony_ci#endif 183762306a36Sopenharmony_ci#endif 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci/* If the task doesn't already have a undo_list, then allocate one 184062306a36Sopenharmony_ci * here. We guarantee there is only one thread using this undo list, 184162306a36Sopenharmony_ci * and current is THE ONE 184262306a36Sopenharmony_ci * 184362306a36Sopenharmony_ci * If this allocation and assignment succeeds, but later 184462306a36Sopenharmony_ci * portions of this code fail, there is no need to free the sem_undo_list. 184562306a36Sopenharmony_ci * Just let it stay associated with the task, and it'll be freed later 184662306a36Sopenharmony_ci * at exit time. 184762306a36Sopenharmony_ci * 184862306a36Sopenharmony_ci * This can block, so callers must hold no locks. 184962306a36Sopenharmony_ci */ 185062306a36Sopenharmony_cistatic inline int get_undo_list(struct sem_undo_list **undo_listp) 185162306a36Sopenharmony_ci{ 185262306a36Sopenharmony_ci struct sem_undo_list *undo_list; 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci undo_list = current->sysvsem.undo_list; 185562306a36Sopenharmony_ci if (!undo_list) { 185662306a36Sopenharmony_ci undo_list = kzalloc(sizeof(*undo_list), GFP_KERNEL_ACCOUNT); 185762306a36Sopenharmony_ci if (undo_list == NULL) 185862306a36Sopenharmony_ci return -ENOMEM; 185962306a36Sopenharmony_ci spin_lock_init(&undo_list->lock); 186062306a36Sopenharmony_ci refcount_set(&undo_list->refcnt, 1); 186162306a36Sopenharmony_ci INIT_LIST_HEAD(&undo_list->list_proc); 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci current->sysvsem.undo_list = undo_list; 186462306a36Sopenharmony_ci } 186562306a36Sopenharmony_ci *undo_listp = undo_list; 186662306a36Sopenharmony_ci return 0; 186762306a36Sopenharmony_ci} 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_cistatic struct sem_undo *__lookup_undo(struct sem_undo_list *ulp, int semid) 187062306a36Sopenharmony_ci{ 187162306a36Sopenharmony_ci struct sem_undo *un; 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci list_for_each_entry_rcu(un, &ulp->list_proc, list_proc, 187462306a36Sopenharmony_ci spin_is_locked(&ulp->lock)) { 187562306a36Sopenharmony_ci if (un->semid == semid) 187662306a36Sopenharmony_ci return un; 187762306a36Sopenharmony_ci } 187862306a36Sopenharmony_ci return NULL; 187962306a36Sopenharmony_ci} 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_cistatic struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid) 188262306a36Sopenharmony_ci{ 188362306a36Sopenharmony_ci struct sem_undo *un; 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci assert_spin_locked(&ulp->lock); 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci un = __lookup_undo(ulp, semid); 188862306a36Sopenharmony_ci if (un) { 188962306a36Sopenharmony_ci list_del_rcu(&un->list_proc); 189062306a36Sopenharmony_ci list_add_rcu(&un->list_proc, &ulp->list_proc); 189162306a36Sopenharmony_ci } 189262306a36Sopenharmony_ci return un; 189362306a36Sopenharmony_ci} 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci/** 189662306a36Sopenharmony_ci * find_alloc_undo - lookup (and if not present create) undo array 189762306a36Sopenharmony_ci * @ns: namespace 189862306a36Sopenharmony_ci * @semid: semaphore array id 189962306a36Sopenharmony_ci * 190062306a36Sopenharmony_ci * The function looks up (and if not present creates) the undo structure. 190162306a36Sopenharmony_ci * The size of the undo structure depends on the size of the semaphore 190262306a36Sopenharmony_ci * array, thus the alloc path is not that straightforward. 190362306a36Sopenharmony_ci * Lifetime-rules: sem_undo is rcu-protected, on success, the function 190462306a36Sopenharmony_ci * performs a rcu_read_lock(). 190562306a36Sopenharmony_ci */ 190662306a36Sopenharmony_cistatic struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid) 190762306a36Sopenharmony_ci{ 190862306a36Sopenharmony_ci struct sem_array *sma; 190962306a36Sopenharmony_ci struct sem_undo_list *ulp; 191062306a36Sopenharmony_ci struct sem_undo *un, *new; 191162306a36Sopenharmony_ci int nsems, error; 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci error = get_undo_list(&ulp); 191462306a36Sopenharmony_ci if (error) 191562306a36Sopenharmony_ci return ERR_PTR(error); 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci rcu_read_lock(); 191862306a36Sopenharmony_ci spin_lock(&ulp->lock); 191962306a36Sopenharmony_ci un = lookup_undo(ulp, semid); 192062306a36Sopenharmony_ci spin_unlock(&ulp->lock); 192162306a36Sopenharmony_ci if (likely(un != NULL)) 192262306a36Sopenharmony_ci goto out; 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci /* no undo structure around - allocate one. */ 192562306a36Sopenharmony_ci /* step 1: figure out the size of the semaphore array */ 192662306a36Sopenharmony_ci sma = sem_obtain_object_check(ns, semid); 192762306a36Sopenharmony_ci if (IS_ERR(sma)) { 192862306a36Sopenharmony_ci rcu_read_unlock(); 192962306a36Sopenharmony_ci return ERR_CAST(sma); 193062306a36Sopenharmony_ci } 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci nsems = sma->sem_nsems; 193362306a36Sopenharmony_ci if (!ipc_rcu_getref(&sma->sem_perm)) { 193462306a36Sopenharmony_ci rcu_read_unlock(); 193562306a36Sopenharmony_ci un = ERR_PTR(-EIDRM); 193662306a36Sopenharmony_ci goto out; 193762306a36Sopenharmony_ci } 193862306a36Sopenharmony_ci rcu_read_unlock(); 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci /* step 2: allocate new undo structure */ 194162306a36Sopenharmony_ci new = kvzalloc(struct_size(new, semadj, nsems), GFP_KERNEL_ACCOUNT); 194262306a36Sopenharmony_ci if (!new) { 194362306a36Sopenharmony_ci ipc_rcu_putref(&sma->sem_perm, sem_rcu_free); 194462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 194562306a36Sopenharmony_ci } 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci /* step 3: Acquire the lock on semaphore array */ 194862306a36Sopenharmony_ci rcu_read_lock(); 194962306a36Sopenharmony_ci sem_lock_and_putref(sma); 195062306a36Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) { 195162306a36Sopenharmony_ci sem_unlock(sma, -1); 195262306a36Sopenharmony_ci rcu_read_unlock(); 195362306a36Sopenharmony_ci kvfree(new); 195462306a36Sopenharmony_ci un = ERR_PTR(-EIDRM); 195562306a36Sopenharmony_ci goto out; 195662306a36Sopenharmony_ci } 195762306a36Sopenharmony_ci spin_lock(&ulp->lock); 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci /* 196062306a36Sopenharmony_ci * step 4: check for races: did someone else allocate the undo struct? 196162306a36Sopenharmony_ci */ 196262306a36Sopenharmony_ci un = lookup_undo(ulp, semid); 196362306a36Sopenharmony_ci if (un) { 196462306a36Sopenharmony_ci spin_unlock(&ulp->lock); 196562306a36Sopenharmony_ci kvfree(new); 196662306a36Sopenharmony_ci goto success; 196762306a36Sopenharmony_ci } 196862306a36Sopenharmony_ci /* step 5: initialize & link new undo structure */ 196962306a36Sopenharmony_ci new->ulp = ulp; 197062306a36Sopenharmony_ci new->semid = semid; 197162306a36Sopenharmony_ci assert_spin_locked(&ulp->lock); 197262306a36Sopenharmony_ci list_add_rcu(&new->list_proc, &ulp->list_proc); 197362306a36Sopenharmony_ci ipc_assert_locked_object(&sma->sem_perm); 197462306a36Sopenharmony_ci list_add(&new->list_id, &sma->list_id); 197562306a36Sopenharmony_ci un = new; 197662306a36Sopenharmony_ci spin_unlock(&ulp->lock); 197762306a36Sopenharmony_cisuccess: 197862306a36Sopenharmony_ci sem_unlock(sma, -1); 197962306a36Sopenharmony_ciout: 198062306a36Sopenharmony_ci return un; 198162306a36Sopenharmony_ci} 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_cilong __do_semtimedop(int semid, struct sembuf *sops, 198462306a36Sopenharmony_ci unsigned nsops, const struct timespec64 *timeout, 198562306a36Sopenharmony_ci struct ipc_namespace *ns) 198662306a36Sopenharmony_ci{ 198762306a36Sopenharmony_ci int error = -EINVAL; 198862306a36Sopenharmony_ci struct sem_array *sma; 198962306a36Sopenharmony_ci struct sembuf *sop; 199062306a36Sopenharmony_ci struct sem_undo *un; 199162306a36Sopenharmony_ci int max, locknum; 199262306a36Sopenharmony_ci bool undos = false, alter = false, dupsop = false; 199362306a36Sopenharmony_ci struct sem_queue queue; 199462306a36Sopenharmony_ci unsigned long dup = 0; 199562306a36Sopenharmony_ci ktime_t expires, *exp = NULL; 199662306a36Sopenharmony_ci bool timed_out = false; 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci if (nsops < 1 || semid < 0) 199962306a36Sopenharmony_ci return -EINVAL; 200062306a36Sopenharmony_ci if (nsops > ns->sc_semopm) 200162306a36Sopenharmony_ci return -E2BIG; 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_ci if (timeout) { 200462306a36Sopenharmony_ci if (!timespec64_valid(timeout)) 200562306a36Sopenharmony_ci return -EINVAL; 200662306a36Sopenharmony_ci expires = ktime_add_safe(ktime_get(), 200762306a36Sopenharmony_ci timespec64_to_ktime(*timeout)); 200862306a36Sopenharmony_ci exp = &expires; 200962306a36Sopenharmony_ci } 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci max = 0; 201362306a36Sopenharmony_ci for (sop = sops; sop < sops + nsops; sop++) { 201462306a36Sopenharmony_ci unsigned long mask = 1ULL << ((sop->sem_num) % BITS_PER_LONG); 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci if (sop->sem_num >= max) 201762306a36Sopenharmony_ci max = sop->sem_num; 201862306a36Sopenharmony_ci if (sop->sem_flg & SEM_UNDO) 201962306a36Sopenharmony_ci undos = true; 202062306a36Sopenharmony_ci if (dup & mask) { 202162306a36Sopenharmony_ci /* 202262306a36Sopenharmony_ci * There was a previous alter access that appears 202362306a36Sopenharmony_ci * to have accessed the same semaphore, thus use 202462306a36Sopenharmony_ci * the dupsop logic. "appears", because the detection 202562306a36Sopenharmony_ci * can only check % BITS_PER_LONG. 202662306a36Sopenharmony_ci */ 202762306a36Sopenharmony_ci dupsop = true; 202862306a36Sopenharmony_ci } 202962306a36Sopenharmony_ci if (sop->sem_op != 0) { 203062306a36Sopenharmony_ci alter = true; 203162306a36Sopenharmony_ci dup |= mask; 203262306a36Sopenharmony_ci } 203362306a36Sopenharmony_ci } 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci if (undos) { 203662306a36Sopenharmony_ci /* On success, find_alloc_undo takes the rcu_read_lock */ 203762306a36Sopenharmony_ci un = find_alloc_undo(ns, semid); 203862306a36Sopenharmony_ci if (IS_ERR(un)) { 203962306a36Sopenharmony_ci error = PTR_ERR(un); 204062306a36Sopenharmony_ci goto out; 204162306a36Sopenharmony_ci } 204262306a36Sopenharmony_ci } else { 204362306a36Sopenharmony_ci un = NULL; 204462306a36Sopenharmony_ci rcu_read_lock(); 204562306a36Sopenharmony_ci } 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci sma = sem_obtain_object_check(ns, semid); 204862306a36Sopenharmony_ci if (IS_ERR(sma)) { 204962306a36Sopenharmony_ci rcu_read_unlock(); 205062306a36Sopenharmony_ci error = PTR_ERR(sma); 205162306a36Sopenharmony_ci goto out; 205262306a36Sopenharmony_ci } 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci error = -EFBIG; 205562306a36Sopenharmony_ci if (max >= sma->sem_nsems) { 205662306a36Sopenharmony_ci rcu_read_unlock(); 205762306a36Sopenharmony_ci goto out; 205862306a36Sopenharmony_ci } 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci error = -EACCES; 206162306a36Sopenharmony_ci if (ipcperms(ns, &sma->sem_perm, alter ? S_IWUGO : S_IRUGO)) { 206262306a36Sopenharmony_ci rcu_read_unlock(); 206362306a36Sopenharmony_ci goto out; 206462306a36Sopenharmony_ci } 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci error = security_sem_semop(&sma->sem_perm, sops, nsops, alter); 206762306a36Sopenharmony_ci if (error) { 206862306a36Sopenharmony_ci rcu_read_unlock(); 206962306a36Sopenharmony_ci goto out; 207062306a36Sopenharmony_ci } 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci error = -EIDRM; 207362306a36Sopenharmony_ci locknum = sem_lock(sma, sops, nsops); 207462306a36Sopenharmony_ci /* 207562306a36Sopenharmony_ci * We eventually might perform the following check in a lockless 207662306a36Sopenharmony_ci * fashion, considering ipc_valid_object() locking constraints. 207762306a36Sopenharmony_ci * If nsops == 1 and there is no contention for sem_perm.lock, then 207862306a36Sopenharmony_ci * only a per-semaphore lock is held and it's OK to proceed with the 207962306a36Sopenharmony_ci * check below. More details on the fine grained locking scheme 208062306a36Sopenharmony_ci * entangled here and why it's RMID race safe on comments at sem_lock() 208162306a36Sopenharmony_ci */ 208262306a36Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) 208362306a36Sopenharmony_ci goto out_unlock; 208462306a36Sopenharmony_ci /* 208562306a36Sopenharmony_ci * semid identifiers are not unique - find_alloc_undo may have 208662306a36Sopenharmony_ci * allocated an undo structure, it was invalidated by an RMID 208762306a36Sopenharmony_ci * and now a new array with received the same id. Check and fail. 208862306a36Sopenharmony_ci * This case can be detected checking un->semid. The existence of 208962306a36Sopenharmony_ci * "un" itself is guaranteed by rcu. 209062306a36Sopenharmony_ci */ 209162306a36Sopenharmony_ci if (un && un->semid == -1) 209262306a36Sopenharmony_ci goto out_unlock; 209362306a36Sopenharmony_ci 209462306a36Sopenharmony_ci queue.sops = sops; 209562306a36Sopenharmony_ci queue.nsops = nsops; 209662306a36Sopenharmony_ci queue.undo = un; 209762306a36Sopenharmony_ci queue.pid = task_tgid(current); 209862306a36Sopenharmony_ci queue.alter = alter; 209962306a36Sopenharmony_ci queue.dupsop = dupsop; 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci error = perform_atomic_semop(sma, &queue); 210262306a36Sopenharmony_ci if (error == 0) { /* non-blocking successful path */ 210362306a36Sopenharmony_ci DEFINE_WAKE_Q(wake_q); 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci /* 210662306a36Sopenharmony_ci * If the operation was successful, then do 210762306a36Sopenharmony_ci * the required updates. 210862306a36Sopenharmony_ci */ 210962306a36Sopenharmony_ci if (alter) 211062306a36Sopenharmony_ci do_smart_update(sma, sops, nsops, 1, &wake_q); 211162306a36Sopenharmony_ci else 211262306a36Sopenharmony_ci set_semotime(sma, sops); 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci sem_unlock(sma, locknum); 211562306a36Sopenharmony_ci rcu_read_unlock(); 211662306a36Sopenharmony_ci wake_up_q(&wake_q); 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci goto out; 211962306a36Sopenharmony_ci } 212062306a36Sopenharmony_ci if (error < 0) /* non-blocking error path */ 212162306a36Sopenharmony_ci goto out_unlock; 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci /* 212462306a36Sopenharmony_ci * We need to sleep on this operation, so we put the current 212562306a36Sopenharmony_ci * task into the pending queue and go to sleep. 212662306a36Sopenharmony_ci */ 212762306a36Sopenharmony_ci if (nsops == 1) { 212862306a36Sopenharmony_ci struct sem *curr; 212962306a36Sopenharmony_ci int idx = array_index_nospec(sops->sem_num, sma->sem_nsems); 213062306a36Sopenharmony_ci curr = &sma->sems[idx]; 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ci if (alter) { 213362306a36Sopenharmony_ci if (sma->complex_count) { 213462306a36Sopenharmony_ci list_add_tail(&queue.list, 213562306a36Sopenharmony_ci &sma->pending_alter); 213662306a36Sopenharmony_ci } else { 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci list_add_tail(&queue.list, 213962306a36Sopenharmony_ci &curr->pending_alter); 214062306a36Sopenharmony_ci } 214162306a36Sopenharmony_ci } else { 214262306a36Sopenharmony_ci list_add_tail(&queue.list, &curr->pending_const); 214362306a36Sopenharmony_ci } 214462306a36Sopenharmony_ci } else { 214562306a36Sopenharmony_ci if (!sma->complex_count) 214662306a36Sopenharmony_ci merge_queues(sma); 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci if (alter) 214962306a36Sopenharmony_ci list_add_tail(&queue.list, &sma->pending_alter); 215062306a36Sopenharmony_ci else 215162306a36Sopenharmony_ci list_add_tail(&queue.list, &sma->pending_const); 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci sma->complex_count++; 215462306a36Sopenharmony_ci } 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci do { 215762306a36Sopenharmony_ci /* memory ordering ensured by the lock in sem_lock() */ 215862306a36Sopenharmony_ci WRITE_ONCE(queue.status, -EINTR); 215962306a36Sopenharmony_ci queue.sleeper = current; 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci /* memory ordering is ensured by the lock in sem_lock() */ 216262306a36Sopenharmony_ci __set_current_state(TASK_INTERRUPTIBLE); 216362306a36Sopenharmony_ci sem_unlock(sma, locknum); 216462306a36Sopenharmony_ci rcu_read_unlock(); 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci timed_out = !schedule_hrtimeout_range(exp, 216762306a36Sopenharmony_ci current->timer_slack_ns, HRTIMER_MODE_ABS); 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci /* 217062306a36Sopenharmony_ci * fastpath: the semop has completed, either successfully or 217162306a36Sopenharmony_ci * not, from the syscall pov, is quite irrelevant to us at this 217262306a36Sopenharmony_ci * point; we're done. 217362306a36Sopenharmony_ci * 217462306a36Sopenharmony_ci * We _do_ care, nonetheless, about being awoken by a signal or 217562306a36Sopenharmony_ci * spuriously. The queue.status is checked again in the 217662306a36Sopenharmony_ci * slowpath (aka after taking sem_lock), such that we can detect 217762306a36Sopenharmony_ci * scenarios where we were awakened externally, during the 217862306a36Sopenharmony_ci * window between wake_q_add() and wake_up_q(). 217962306a36Sopenharmony_ci */ 218062306a36Sopenharmony_ci rcu_read_lock(); 218162306a36Sopenharmony_ci error = READ_ONCE(queue.status); 218262306a36Sopenharmony_ci if (error != -EINTR) { 218362306a36Sopenharmony_ci /* see SEM_BARRIER_2 for purpose/pairing */ 218462306a36Sopenharmony_ci smp_acquire__after_ctrl_dep(); 218562306a36Sopenharmony_ci rcu_read_unlock(); 218662306a36Sopenharmony_ci goto out; 218762306a36Sopenharmony_ci } 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci locknum = sem_lock(sma, sops, nsops); 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) 219262306a36Sopenharmony_ci goto out_unlock; 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci /* 219562306a36Sopenharmony_ci * No necessity for any barrier: We are protect by sem_lock() 219662306a36Sopenharmony_ci */ 219762306a36Sopenharmony_ci error = READ_ONCE(queue.status); 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci /* 220062306a36Sopenharmony_ci * If queue.status != -EINTR we are woken up by another process. 220162306a36Sopenharmony_ci * Leave without unlink_queue(), but with sem_unlock(). 220262306a36Sopenharmony_ci */ 220362306a36Sopenharmony_ci if (error != -EINTR) 220462306a36Sopenharmony_ci goto out_unlock; 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci /* 220762306a36Sopenharmony_ci * If an interrupt occurred we have to clean up the queue. 220862306a36Sopenharmony_ci */ 220962306a36Sopenharmony_ci if (timed_out) 221062306a36Sopenharmony_ci error = -EAGAIN; 221162306a36Sopenharmony_ci } while (error == -EINTR && !signal_pending(current)); /* spurious */ 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_ci unlink_queue(sma, &queue); 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ciout_unlock: 221662306a36Sopenharmony_ci sem_unlock(sma, locknum); 221762306a36Sopenharmony_ci rcu_read_unlock(); 221862306a36Sopenharmony_ciout: 221962306a36Sopenharmony_ci return error; 222062306a36Sopenharmony_ci} 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_cistatic long do_semtimedop(int semid, struct sembuf __user *tsops, 222362306a36Sopenharmony_ci unsigned nsops, const struct timespec64 *timeout) 222462306a36Sopenharmony_ci{ 222562306a36Sopenharmony_ci struct sembuf fast_sops[SEMOPM_FAST]; 222662306a36Sopenharmony_ci struct sembuf *sops = fast_sops; 222762306a36Sopenharmony_ci struct ipc_namespace *ns; 222862306a36Sopenharmony_ci int ret; 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci ns = current->nsproxy->ipc_ns; 223162306a36Sopenharmony_ci if (nsops > ns->sc_semopm) 223262306a36Sopenharmony_ci return -E2BIG; 223362306a36Sopenharmony_ci if (nsops < 1) 223462306a36Sopenharmony_ci return -EINVAL; 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci if (nsops > SEMOPM_FAST) { 223762306a36Sopenharmony_ci sops = kvmalloc_array(nsops, sizeof(*sops), GFP_KERNEL); 223862306a36Sopenharmony_ci if (sops == NULL) 223962306a36Sopenharmony_ci return -ENOMEM; 224062306a36Sopenharmony_ci } 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_ci if (copy_from_user(sops, tsops, nsops * sizeof(*tsops))) { 224362306a36Sopenharmony_ci ret = -EFAULT; 224462306a36Sopenharmony_ci goto out_free; 224562306a36Sopenharmony_ci } 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci ret = __do_semtimedop(semid, sops, nsops, timeout, ns); 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ciout_free: 225062306a36Sopenharmony_ci if (sops != fast_sops) 225162306a36Sopenharmony_ci kvfree(sops); 225262306a36Sopenharmony_ci 225362306a36Sopenharmony_ci return ret; 225462306a36Sopenharmony_ci} 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_cilong ksys_semtimedop(int semid, struct sembuf __user *tsops, 225762306a36Sopenharmony_ci unsigned int nsops, const struct __kernel_timespec __user *timeout) 225862306a36Sopenharmony_ci{ 225962306a36Sopenharmony_ci if (timeout) { 226062306a36Sopenharmony_ci struct timespec64 ts; 226162306a36Sopenharmony_ci if (get_timespec64(&ts, timeout)) 226262306a36Sopenharmony_ci return -EFAULT; 226362306a36Sopenharmony_ci return do_semtimedop(semid, tsops, nsops, &ts); 226462306a36Sopenharmony_ci } 226562306a36Sopenharmony_ci return do_semtimedop(semid, tsops, nsops, NULL); 226662306a36Sopenharmony_ci} 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_ciSYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, 226962306a36Sopenharmony_ci unsigned int, nsops, const struct __kernel_timespec __user *, timeout) 227062306a36Sopenharmony_ci{ 227162306a36Sopenharmony_ci return ksys_semtimedop(semid, tsops, nsops, timeout); 227262306a36Sopenharmony_ci} 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_ci#ifdef CONFIG_COMPAT_32BIT_TIME 227562306a36Sopenharmony_cilong compat_ksys_semtimedop(int semid, struct sembuf __user *tsems, 227662306a36Sopenharmony_ci unsigned int nsops, 227762306a36Sopenharmony_ci const struct old_timespec32 __user *timeout) 227862306a36Sopenharmony_ci{ 227962306a36Sopenharmony_ci if (timeout) { 228062306a36Sopenharmony_ci struct timespec64 ts; 228162306a36Sopenharmony_ci if (get_old_timespec32(&ts, timeout)) 228262306a36Sopenharmony_ci return -EFAULT; 228362306a36Sopenharmony_ci return do_semtimedop(semid, tsems, nsops, &ts); 228462306a36Sopenharmony_ci } 228562306a36Sopenharmony_ci return do_semtimedop(semid, tsems, nsops, NULL); 228662306a36Sopenharmony_ci} 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_ciSYSCALL_DEFINE4(semtimedop_time32, int, semid, struct sembuf __user *, tsems, 228962306a36Sopenharmony_ci unsigned int, nsops, 229062306a36Sopenharmony_ci const struct old_timespec32 __user *, timeout) 229162306a36Sopenharmony_ci{ 229262306a36Sopenharmony_ci return compat_ksys_semtimedop(semid, tsems, nsops, timeout); 229362306a36Sopenharmony_ci} 229462306a36Sopenharmony_ci#endif 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ciSYSCALL_DEFINE3(semop, int, semid, struct sembuf __user *, tsops, 229762306a36Sopenharmony_ci unsigned, nsops) 229862306a36Sopenharmony_ci{ 229962306a36Sopenharmony_ci return do_semtimedop(semid, tsops, nsops, NULL); 230062306a36Sopenharmony_ci} 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_ci/* If CLONE_SYSVSEM is set, establish sharing of SEM_UNDO state between 230362306a36Sopenharmony_ci * parent and child tasks. 230462306a36Sopenharmony_ci */ 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ciint copy_semundo(unsigned long clone_flags, struct task_struct *tsk) 230762306a36Sopenharmony_ci{ 230862306a36Sopenharmony_ci struct sem_undo_list *undo_list; 230962306a36Sopenharmony_ci int error; 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci if (clone_flags & CLONE_SYSVSEM) { 231262306a36Sopenharmony_ci error = get_undo_list(&undo_list); 231362306a36Sopenharmony_ci if (error) 231462306a36Sopenharmony_ci return error; 231562306a36Sopenharmony_ci refcount_inc(&undo_list->refcnt); 231662306a36Sopenharmony_ci tsk->sysvsem.undo_list = undo_list; 231762306a36Sopenharmony_ci } else 231862306a36Sopenharmony_ci tsk->sysvsem.undo_list = NULL; 231962306a36Sopenharmony_ci 232062306a36Sopenharmony_ci return 0; 232162306a36Sopenharmony_ci} 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci/* 232462306a36Sopenharmony_ci * add semadj values to semaphores, free undo structures. 232562306a36Sopenharmony_ci * undo structures are not freed when semaphore arrays are destroyed 232662306a36Sopenharmony_ci * so some of them may be out of date. 232762306a36Sopenharmony_ci * IMPLEMENTATION NOTE: There is some confusion over whether the 232862306a36Sopenharmony_ci * set of adjustments that needs to be done should be done in an atomic 232962306a36Sopenharmony_ci * manner or not. That is, if we are attempting to decrement the semval 233062306a36Sopenharmony_ci * should we queue up and wait until we can do so legally? 233162306a36Sopenharmony_ci * The original implementation attempted to do this (queue and wait). 233262306a36Sopenharmony_ci * The current implementation does not do so. The POSIX standard 233362306a36Sopenharmony_ci * and SVID should be consulted to determine what behavior is mandated. 233462306a36Sopenharmony_ci */ 233562306a36Sopenharmony_civoid exit_sem(struct task_struct *tsk) 233662306a36Sopenharmony_ci{ 233762306a36Sopenharmony_ci struct sem_undo_list *ulp; 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_ci ulp = tsk->sysvsem.undo_list; 234062306a36Sopenharmony_ci if (!ulp) 234162306a36Sopenharmony_ci return; 234262306a36Sopenharmony_ci tsk->sysvsem.undo_list = NULL; 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci if (!refcount_dec_and_test(&ulp->refcnt)) 234562306a36Sopenharmony_ci return; 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_ci for (;;) { 234862306a36Sopenharmony_ci struct sem_array *sma; 234962306a36Sopenharmony_ci struct sem_undo *un; 235062306a36Sopenharmony_ci int semid, i; 235162306a36Sopenharmony_ci DEFINE_WAKE_Q(wake_q); 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci cond_resched(); 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_ci rcu_read_lock(); 235662306a36Sopenharmony_ci un = list_entry_rcu(ulp->list_proc.next, 235762306a36Sopenharmony_ci struct sem_undo, list_proc); 235862306a36Sopenharmony_ci if (&un->list_proc == &ulp->list_proc) { 235962306a36Sopenharmony_ci /* 236062306a36Sopenharmony_ci * We must wait for freeary() before freeing this ulp, 236162306a36Sopenharmony_ci * in case we raced with last sem_undo. There is a small 236262306a36Sopenharmony_ci * possibility where we exit while freeary() didn't 236362306a36Sopenharmony_ci * finish unlocking sem_undo_list. 236462306a36Sopenharmony_ci */ 236562306a36Sopenharmony_ci spin_lock(&ulp->lock); 236662306a36Sopenharmony_ci spin_unlock(&ulp->lock); 236762306a36Sopenharmony_ci rcu_read_unlock(); 236862306a36Sopenharmony_ci break; 236962306a36Sopenharmony_ci } 237062306a36Sopenharmony_ci spin_lock(&ulp->lock); 237162306a36Sopenharmony_ci semid = un->semid; 237262306a36Sopenharmony_ci spin_unlock(&ulp->lock); 237362306a36Sopenharmony_ci 237462306a36Sopenharmony_ci /* exit_sem raced with IPC_RMID, nothing to do */ 237562306a36Sopenharmony_ci if (semid == -1) { 237662306a36Sopenharmony_ci rcu_read_unlock(); 237762306a36Sopenharmony_ci continue; 237862306a36Sopenharmony_ci } 237962306a36Sopenharmony_ci 238062306a36Sopenharmony_ci sma = sem_obtain_object_check(tsk->nsproxy->ipc_ns, semid); 238162306a36Sopenharmony_ci /* exit_sem raced with IPC_RMID, nothing to do */ 238262306a36Sopenharmony_ci if (IS_ERR(sma)) { 238362306a36Sopenharmony_ci rcu_read_unlock(); 238462306a36Sopenharmony_ci continue; 238562306a36Sopenharmony_ci } 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_ci sem_lock(sma, NULL, -1); 238862306a36Sopenharmony_ci /* exit_sem raced with IPC_RMID, nothing to do */ 238962306a36Sopenharmony_ci if (!ipc_valid_object(&sma->sem_perm)) { 239062306a36Sopenharmony_ci sem_unlock(sma, -1); 239162306a36Sopenharmony_ci rcu_read_unlock(); 239262306a36Sopenharmony_ci continue; 239362306a36Sopenharmony_ci } 239462306a36Sopenharmony_ci un = __lookup_undo(ulp, semid); 239562306a36Sopenharmony_ci if (un == NULL) { 239662306a36Sopenharmony_ci /* exit_sem raced with IPC_RMID+semget() that created 239762306a36Sopenharmony_ci * exactly the same semid. Nothing to do. 239862306a36Sopenharmony_ci */ 239962306a36Sopenharmony_ci sem_unlock(sma, -1); 240062306a36Sopenharmony_ci rcu_read_unlock(); 240162306a36Sopenharmony_ci continue; 240262306a36Sopenharmony_ci } 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_ci /* remove un from the linked lists */ 240562306a36Sopenharmony_ci ipc_assert_locked_object(&sma->sem_perm); 240662306a36Sopenharmony_ci list_del(&un->list_id); 240762306a36Sopenharmony_ci 240862306a36Sopenharmony_ci spin_lock(&ulp->lock); 240962306a36Sopenharmony_ci list_del_rcu(&un->list_proc); 241062306a36Sopenharmony_ci spin_unlock(&ulp->lock); 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci /* perform adjustments registered in un */ 241362306a36Sopenharmony_ci for (i = 0; i < sma->sem_nsems; i++) { 241462306a36Sopenharmony_ci struct sem *semaphore = &sma->sems[i]; 241562306a36Sopenharmony_ci if (un->semadj[i]) { 241662306a36Sopenharmony_ci semaphore->semval += un->semadj[i]; 241762306a36Sopenharmony_ci /* 241862306a36Sopenharmony_ci * Range checks of the new semaphore value, 241962306a36Sopenharmony_ci * not defined by sus: 242062306a36Sopenharmony_ci * - Some unices ignore the undo entirely 242162306a36Sopenharmony_ci * (e.g. HP UX 11i 11.22, Tru64 V5.1) 242262306a36Sopenharmony_ci * - some cap the value (e.g. FreeBSD caps 242362306a36Sopenharmony_ci * at 0, but doesn't enforce SEMVMX) 242462306a36Sopenharmony_ci * 242562306a36Sopenharmony_ci * Linux caps the semaphore value, both at 0 242662306a36Sopenharmony_ci * and at SEMVMX. 242762306a36Sopenharmony_ci * 242862306a36Sopenharmony_ci * Manfred <manfred@colorfullife.com> 242962306a36Sopenharmony_ci */ 243062306a36Sopenharmony_ci if (semaphore->semval < 0) 243162306a36Sopenharmony_ci semaphore->semval = 0; 243262306a36Sopenharmony_ci if (semaphore->semval > SEMVMX) 243362306a36Sopenharmony_ci semaphore->semval = SEMVMX; 243462306a36Sopenharmony_ci ipc_update_pid(&semaphore->sempid, task_tgid(current)); 243562306a36Sopenharmony_ci } 243662306a36Sopenharmony_ci } 243762306a36Sopenharmony_ci /* maybe some queued-up processes were waiting for this */ 243862306a36Sopenharmony_ci do_smart_update(sma, NULL, 0, 1, &wake_q); 243962306a36Sopenharmony_ci sem_unlock(sma, -1); 244062306a36Sopenharmony_ci rcu_read_unlock(); 244162306a36Sopenharmony_ci wake_up_q(&wake_q); 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_ci kvfree_rcu(un, rcu); 244462306a36Sopenharmony_ci } 244562306a36Sopenharmony_ci kfree(ulp); 244662306a36Sopenharmony_ci} 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 244962306a36Sopenharmony_cistatic int sysvipc_sem_proc_show(struct seq_file *s, void *it) 245062306a36Sopenharmony_ci{ 245162306a36Sopenharmony_ci struct user_namespace *user_ns = seq_user_ns(s); 245262306a36Sopenharmony_ci struct kern_ipc_perm *ipcp = it; 245362306a36Sopenharmony_ci struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm); 245462306a36Sopenharmony_ci time64_t sem_otime; 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci /* 245762306a36Sopenharmony_ci * The proc interface isn't aware of sem_lock(), it calls 245862306a36Sopenharmony_ci * ipc_lock_object(), i.e. spin_lock(&sma->sem_perm.lock). 245962306a36Sopenharmony_ci * (in sysvipc_find_ipc) 246062306a36Sopenharmony_ci * In order to stay compatible with sem_lock(), we must 246162306a36Sopenharmony_ci * enter / leave complex_mode. 246262306a36Sopenharmony_ci */ 246362306a36Sopenharmony_ci complexmode_enter(sma); 246462306a36Sopenharmony_ci 246562306a36Sopenharmony_ci sem_otime = get_semotime(sma); 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci seq_printf(s, 246862306a36Sopenharmony_ci "%10d %10d %4o %10u %5u %5u %5u %5u %10llu %10llu\n", 246962306a36Sopenharmony_ci sma->sem_perm.key, 247062306a36Sopenharmony_ci sma->sem_perm.id, 247162306a36Sopenharmony_ci sma->sem_perm.mode, 247262306a36Sopenharmony_ci sma->sem_nsems, 247362306a36Sopenharmony_ci from_kuid_munged(user_ns, sma->sem_perm.uid), 247462306a36Sopenharmony_ci from_kgid_munged(user_ns, sma->sem_perm.gid), 247562306a36Sopenharmony_ci from_kuid_munged(user_ns, sma->sem_perm.cuid), 247662306a36Sopenharmony_ci from_kgid_munged(user_ns, sma->sem_perm.cgid), 247762306a36Sopenharmony_ci sem_otime, 247862306a36Sopenharmony_ci sma->sem_ctime); 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_ci complexmode_tryleave(sma); 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_ci return 0; 248362306a36Sopenharmony_ci} 248462306a36Sopenharmony_ci#endif 2485