Blame view

ipc/sem.c 56.3 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
  /*
   * linux/ipc/sem.c
   * Copyright (C) 1992 Krishna Balasubramanian
   * Copyright (C) 1995 Eric Schenk, Bruno Haible
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
7
8
   * /proc/sysvipc/sem support (c) 1999 Dragos Acostachioaie <dragos@iname.com>
   *
   * SMP-threaded, sysctl's added
624dffcbc   Christian Kujau   correct email add...
9
   * (c) 1999 Manfred Spraul <manfred@colorfullife.com>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
   * Enforced range limit on SEM_UNDO
046c68842   Alan Cox   mm: update my add...
11
   * (c) 2001 Red Hat Inc
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
   * Lockless wakeup
   * (c) 2003 Manfred Spraul <manfred@colorfullife.com>
c5cf6359a   Manfred Spraul   ipc/sem.c: update...
14
15
   * Further wakeup optimizations, documentation
   * (c) 2010 Manfred Spraul <manfred@colorfullife.com>
073115d6b   Steve Grubb   [PATCH] Rework of...
16
17
18
   *
   * support for audit of ipc object properties and permission changes
   * Dustin Kirkland <dustin.kirkland@us.ibm.com>
e38935341   Kirill Korotaev   [PATCH] IPC names...
19
20
21
22
   *
   * namespaces support
   * OpenVZ, SWsoft Inc.
   * Pavel Emelianov <xemul@openvz.org>
c5cf6359a   Manfred Spraul   ipc/sem.c: update...
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
   *
   * Implementation notes: (May 2010)
   * This file implements System V semaphores.
   *
   * User space visible behavior:
   * - FIFO ordering for semop() operations (just FIFO, not starvation
   *   protection)
   * - multiple semaphore operations that alter the same semaphore in
   *   one semop() are handled.
   * - sem_ctime (time of last semctl()) is updated in the IPC_SET, SETVAL and
   *   SETALL calls.
   * - two Linux specific semctl() commands: SEM_STAT, SEM_INFO.
   * - undo adjustments at process exit are limited to 0..SEMVMX.
   * - namespace are supported.
   * - SEMMSL, SEMMNS, SEMOPM and SEMMNI can be configured at runtine by writing
   *   to /proc/sys/kernel/sem.
   * - statistics about the usage are reported in /proc/sysvipc/sem.
   *
   * Internals:
   * - scalability:
   *   - all global variables are read-mostly.
   *   - semop() calls and semctl(RMID) are synchronized by RCU.
   *   - most operations do write operations (actually: spin_lock calls) to
   *     the per-semaphore array structure.
   *   Thus: Perfect SMP scaling between independent semaphore arrays.
   *         If multiple semaphores in one array are used, then cache line
   *         trashing on the semaphore array spinlock will limit the scaling.
2f2ed41dc   Manfred Spraul   ipc/sem.c: remove...
50
   * - semncnt and semzcnt are calculated on demand in count_semcnt()
c5cf6359a   Manfred Spraul   ipc/sem.c: update...
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
   * - the task that performs a successful semop() scans the list of all
   *   sleeping tasks and completes any pending operations that can be fulfilled.
   *   Semaphores are actively given to waiting tasks (necessary for FIFO).
   *   (see update_queue())
   * - To improve the scalability, the actual wake-up calls are performed after
   *   dropping all locks. (see wake_up_sem_queue_prepare(),
   *   wake_up_sem_queue_do())
   * - All work is done by the waker, the woken up task does not have to do
   *   anything - not even acquiring a lock or dropping a refcount.
   * - A woken up task may not even touch the semaphore array anymore, it may
   *   have been destroyed already by a semctl(RMID).
   * - The synchronizations between wake-ups due to a timeout/signal and a
   *   wake-up due to a completed semaphore operation is achieved by using an
   *   intermediate state (IN_WAKEUP).
   * - UNDO values are stored in an array (one per process and per
   *   semaphore array, lazily allocated). For backwards compatibility, multiple
   *   modes for the UNDO variables are supported (per process, per thread)
   *   (see copy_semundo, CLONE_SYSVSEM)
   * - There are two lists of the pending operations: a per-array list
   *   and per-semaphore list (stored in the array). This allows to achieve FIFO
   *   ordering without always scanning all pending operations.
   *   The worst-case behavior is nevertheless O(N^2) for N wakeups.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
76
77
78
  #include <linux/slab.h>
  #include <linux/spinlock.h>
  #include <linux/init.h>
  #include <linux/proc_fs.h>
  #include <linux/time.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
80
81
  #include <linux/security.h>
  #include <linux/syscalls.h>
  #include <linux/audit.h>
c59ede7b7   Randy.Dunlap   [PATCH] move capa...
82
  #include <linux/capability.h>
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
83
  #include <linux/seq_file.h>
3e148c799   Nadia Derbey   fix idr_find() lo...
84
  #include <linux/rwsem.h>
e38935341   Kirill Korotaev   [PATCH] IPC names...
85
  #include <linux/nsproxy.h>
ae5e1b22f   Pavel Emelyanov   namespaces: move ...
86
  #include <linux/ipc_namespace.h>
5f921ae96   Ingo Molnar   [PATCH] sem2mutex...
87

7153e4027   Paul McQuade   ipc, kernel: use ...
88
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
  #include "util.h"
e57940d71   Manfred Spraul   ipc/sem.c: remove...
90
91
92
  /* One semaphore structure for each semaphore in the system. */
  struct sem {
  	int	semval;		/* current value */
a5f4db877   Davidlohr Bueso   ipc/sem: make sem...
93
94
95
96
97
98
99
100
  	/*
  	 * PID of the process that last modified the semaphore. For
  	 * Linux, specifically these are:
  	 *  - semop
  	 *  - semctl, via SETVAL and SETALL.
  	 *  - at task exit when performing undo adjustments (see exit_sem).
  	 */
  	int	sempid;
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
101
  	spinlock_t	lock;	/* spinlock for fine-grained semtimedop */
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
102
103
104
105
  	struct list_head pending_alter; /* pending single-sop operations */
  					/* that alter the semaphore */
  	struct list_head pending_const; /* pending single-sop operations */
  					/* that do not alter the semaphore*/
d12e1e50e   Manfred Spraul   ipc/sem.c: replac...
106
  	time_t	sem_otime;	/* candidate for sem_otime */
f5c936c0f   Manfred Spraul   ipc/sem.c: cachel...
107
  } ____cacheline_aligned_in_smp;
e57940d71   Manfred Spraul   ipc/sem.c: remove...
108
109
110
  
  /* One queue for each sleeping process in the system. */
  struct sem_queue {
e57940d71   Manfred Spraul   ipc/sem.c: remove...
111
112
113
114
115
116
  	struct list_head	list;	 /* queue of pending operations */
  	struct task_struct	*sleeper; /* this process */
  	struct sem_undo		*undo;	 /* undo structure */
  	int			pid;	 /* process id of requesting process */
  	int			status;	 /* completion status of operation */
  	struct sembuf		*sops;	 /* array of pending operations */
ed247b7ca   Manfred Spraul   ipc/sem.c: store ...
117
  	struct sembuf		*blocking; /* the operation that blocked */
e57940d71   Manfred Spraul   ipc/sem.c: remove...
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
  	int			nsops;	 /* number of operations */
  	int			alter;	 /* does *sops alter the array? */
  };
  
  /* Each task has a list of undo requests. They are executed automatically
   * when the process exits.
   */
  struct sem_undo {
  	struct list_head	list_proc;	/* per-process list: *
  						 * all undos from one process
  						 * rcu protected */
  	struct rcu_head		rcu;		/* rcu struct for sem_undo */
  	struct sem_undo_list	*ulp;		/* back ptr to sem_undo_list */
  	struct list_head	list_id;	/* per semaphore array list:
  						 * all undos for one array */
  	int			semid;		/* semaphore set identifier */
  	short			*semadj;	/* array of adjustments */
  						/* one per semaphore */
  };
  
  /* sem_undo_list controls shared access to the list of sem_undo structures
   * that may be shared among all a CLONE_SYSVSEM task group.
   */
  struct sem_undo_list {
  	atomic_t		refcnt;
  	spinlock_t		lock;
  	struct list_head	list_proc;
  };
ed2ddbf88   Pierre Peiffer   IPC: make struct ...
146
  #define sem_ids(ns)	((ns)->ids[IPC_SEM_IDS])
e38935341   Kirill Korotaev   [PATCH] IPC names...
147

1b531f213   Nadia Derbey   ipc: remove unnee...
148
  #define sem_checkid(sma, semid)	ipc_checkid(&sma->sem_perm, semid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149

7748dbfaa   Nadia Derbey   ipc: unify the sy...
150
  static int newary(struct ipc_namespace *, struct ipc_params *);
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
151
  static void freeary(struct ipc_namespace *, struct kern_ipc_perm *);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
  #ifdef CONFIG_PROC_FS
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
153
  static int sysvipc_sem_proc_show(struct seq_file *s, void *it);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
155
156
157
158
159
  #endif
  
  #define SEMMSL_FAST	256 /* 512 bytes on stack */
  #define SEMOPM_FAST	64  /* ~ 372 bytes on stack */
  
  /*
758a6ba39   Manfred Spraul   ipc/sem.c: rename...
160
   * Locking:
5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
161
   * a) global sem_lock() for read/write
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
   *	sem_undo.id_next,
758a6ba39   Manfred Spraul   ipc/sem.c: rename...
163
   *	sem_array.complex_count,
5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
164
165
166
   *	sem_array.complex_mode
   *	sem_array.pending{_alter,_const},
   *	sem_array.sem_undo
46c0a8ca3   Paul McQuade   ipc, kernel: clea...
167
   *
5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
168
   * b) global or semaphore sem_lock() for read/write:
758a6ba39   Manfred Spraul   ipc/sem.c: rename...
169
   *	sem_array.sem_base[i].pending_{const,alter}:
5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
170
171
172
173
174
175
   *	sem_array.complex_mode (for read)
   *
   * c) special:
   *	sem_undo_list.list_proc:
   *	* undo_list->lock for write
   *	* rcu for read
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
   */
e38935341   Kirill Korotaev   [PATCH] IPC names...
177
178
179
180
  #define sc_semmsl	sem_ctls[0]
  #define sc_semmns	sem_ctls[1]
  #define sc_semopm	sem_ctls[2]
  #define sc_semmni	sem_ctls[3]
ed2ddbf88   Pierre Peiffer   IPC: make struct ...
181
  void sem_init_ns(struct ipc_namespace *ns)
e38935341   Kirill Korotaev   [PATCH] IPC names...
182
  {
e38935341   Kirill Korotaev   [PATCH] IPC names...
183
184
185
186
187
  	ns->sc_semmsl = SEMMSL;
  	ns->sc_semmns = SEMMNS;
  	ns->sc_semopm = SEMOPM;
  	ns->sc_semmni = SEMMNI;
  	ns->used_sems = 0;
ed2ddbf88   Pierre Peiffer   IPC: make struct ...
188
  	ipc_init_ids(&ns->ids[IPC_SEM_IDS]);
e38935341   Kirill Korotaev   [PATCH] IPC names...
189
  }
ae5e1b22f   Pavel Emelyanov   namespaces: move ...
190
  #ifdef CONFIG_IPC_NS
e38935341   Kirill Korotaev   [PATCH] IPC names...
191
192
  void sem_exit_ns(struct ipc_namespace *ns)
  {
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
193
  	free_ipcs(ns, &sem_ids(ns), freeary);
7d6feeb28   Serge E. Hallyn   ipc ns: fix memor...
194
  	idr_destroy(&ns->ids[IPC_SEM_IDS].ipcs_idr);
e38935341   Kirill Korotaev   [PATCH] IPC names...
195
  }
ae5e1b22f   Pavel Emelyanov   namespaces: move ...
196
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197

239521f31   Manfred Spraul   ipc: whitespace c...
198
  void __init sem_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
  {
ed2ddbf88   Pierre Peiffer   IPC: make struct ...
200
  	sem_init_ns(&init_ipc_ns);
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
201
202
203
  	ipc_init_proc_interface("sysvipc/sem",
  				"       key      semid perms      nsems   uid   gid  cuid  cgid      otime      ctime
  ",
e38935341   Kirill Korotaev   [PATCH] IPC names...
204
  				IPC_SEM_IDS, sysvipc_sem_proc_show);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
  }
f269f40ad   Manfred Spraul   ipc/sem.c: always...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
  /**
   * unmerge_queues - unmerge queues, if possible.
   * @sma: semaphore array
   *
   * The function unmerges the wait queues if complex_count is 0.
   * It must be called prior to dropping the global semaphore array lock.
   */
  static void unmerge_queues(struct sem_array *sma)
  {
  	struct sem_queue *q, *tq;
  
  	/* complex operations still around? */
  	if (sma->complex_count)
  		return;
  	/*
  	 * We will switch back to simple mode.
  	 * Move all pending operation back into the per-semaphore
  	 * queues.
  	 */
  	list_for_each_entry_safe(q, tq, &sma->pending_alter, list) {
  		struct sem *curr;
  		curr = &sma->sem_base[q->sops[0].sem_num];
  
  		list_add_tail(&q->list, &curr->pending_alter);
  	}
  	INIT_LIST_HEAD(&sma->pending_alter);
  }
  
  /**
8001c8581   Davidlohr Bueso   ipc: standardize ...
235
   * merge_queues - merge single semop queues into global queue
f269f40ad   Manfred Spraul   ipc/sem.c: always...
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
   * @sma: semaphore array
   *
   * This function merges all per-semaphore queues into the global queue.
   * It is necessary to achieve FIFO ordering for the pending single-sop
   * operations when a multi-semop operation must sleep.
   * Only the alter operations must be moved, the const operations can stay.
   */
  static void merge_queues(struct sem_array *sma)
  {
  	int i;
  	for (i = 0; i < sma->sem_nsems; i++) {
  		struct sem *sem = sma->sem_base + i;
  
  		list_splice_init(&sem->pending_alter, &sma->pending_alter);
  	}
  }
53dad6d3a   Davidlohr Bueso   ipc: fix race wit...
252
253
254
255
256
257
258
259
  static void sem_rcu_free(struct rcu_head *head)
  {
  	struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu);
  	struct sem_array *sma = ipc_rcu_to_struct(p);
  
  	security_sem_free(sma);
  	ipc_rcu_free(head);
  }
3e148c799   Nadia Derbey   fix idr_find() lo...
260
  /*
5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
261
   * Enter the mode suitable for non-simple operations:
5e9d52759   Manfred Spraul   ipc/sem.c: fix ra...
262
   * Caller must own sem_perm.lock.
5e9d52759   Manfred Spraul   ipc/sem.c: fix ra...
263
   */
5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
264
  static void complexmode_enter(struct sem_array *sma)
5e9d52759   Manfred Spraul   ipc/sem.c: fix ra...
265
266
267
  {
  	int i;
  	struct sem *sem;
5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
268
269
  	if (sma->complex_mode)  {
  		/* We are already in complex_mode. Nothing to do */
6d07b68ce   Manfred Spraul   ipc/sem.c: optimi...
270
271
  		return;
  	}
5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
272
273
274
275
276
  	/* We need a full barrier after seting complex_mode:
  	 * The write to complex_mode must be visible
  	 * before we read the first sem->lock spinlock state.
  	 */
  	smp_store_mb(sma->complex_mode, true);
5e9d52759   Manfred Spraul   ipc/sem.c: fix ra...
277
278
279
280
  	for (i = 0; i < sma->sem_nsems; i++) {
  		sem = sma->sem_base + i;
  		spin_unlock_wait(&sem->lock);
  	}
5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
  	/*
  	 * spin_unlock_wait() is not a memory barriers, it is only a
  	 * control barrier. The code must pair with spin_unlock(&sem->lock),
  	 * thus just the control barrier is insufficient.
  	 *
  	 * smp_rmb() is sufficient, as writes cannot pass the control barrier.
  	 */
  	smp_rmb();
  }
  
  /*
   * Try to leave the mode that disallows simple operations:
   * Caller must own sem_perm.lock.
   */
  static void complexmode_tryleave(struct sem_array *sma)
  {
  	if (sma->complex_count)  {
  		/* Complex ops are sleeping.
  		 * We must stay in complex mode
  		 */
  		return;
  	}
  	/*
  	 * Immediately after setting complex_mode to false,
  	 * a simple op can start. Thus: all memory writes
  	 * performed by the current operation must be visible
  	 * before we set complex_mode to false.
  	 */
  	smp_store_release(&sma->complex_mode, false);
5e9d52759   Manfred Spraul   ipc/sem.c: fix ra...
310
  }
5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
311
  #define SEM_GLOBAL_LOCK	(-1)
5e9d52759   Manfred Spraul   ipc/sem.c: fix ra...
312
  /*
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
313
314
315
316
317
   * If the request contains only one semaphore operation, and there are
   * no complex transactions pending, lock only the semaphore involved.
   * Otherwise, lock the entire semaphore array, since we either have
   * multiple semaphores in our own semops, or we need to look at
   * semaphores from other pending complex operations.
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
318
319
320
321
   */
  static inline int sem_lock(struct sem_array *sma, struct sembuf *sops,
  			      int nsops)
  {
5e9d52759   Manfred Spraul   ipc/sem.c: fix ra...
322
  	struct sem *sem;
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
323

5e9d52759   Manfred Spraul   ipc/sem.c: fix ra...
324
325
326
  	if (nsops != 1) {
  		/* Complex operation - acquire a full lock */
  		ipc_lock_object(&sma->sem_perm);
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
327

5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
328
329
330
  		/* Prevent parallel simple ops */
  		complexmode_enter(sma);
  		return SEM_GLOBAL_LOCK;
5e9d52759   Manfred Spraul   ipc/sem.c: fix ra...
331
332
333
334
  	}
  
  	/*
  	 * Only one semaphore affected - try to optimize locking.
5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
335
336
337
338
  	 * Optimized locking is possible if no complex operation
  	 * is either enqueued or processed right now.
  	 *
  	 * Both facts are tracked by complex_mode.
5e9d52759   Manfred Spraul   ipc/sem.c: fix ra...
339
340
  	 */
  	sem = sma->sem_base + sops->sem_num;
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
341

5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
342
343
344
345
346
  	/*
  	 * Initial check for complex_mode. Just an optimization,
  	 * no locking, no memory barrier.
  	 */
  	if (!sma->complex_mode) {
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
347
  		/*
5e9d52759   Manfred Spraul   ipc/sem.c: fix ra...
348
349
  		 * It appears that no complex operation is around.
  		 * Acquire the per-semaphore lock.
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
350
  		 */
5e9d52759   Manfred Spraul   ipc/sem.c: fix ra...
351
  		spin_lock(&sem->lock);
5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
352
353
354
355
356
357
358
  		/*
  		 * See 51d7d5205d33
  		 * ("powerpc: Add smp_mb() to arch_spin_is_locked()"):
  		 * A full barrier is required: the write of sem->lock
  		 * must be visible before the read is executed
  		 */
  		smp_mb();
5e9d52759   Manfred Spraul   ipc/sem.c: fix ra...
359

5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
360
361
362
  		if (!smp_load_acquire(&sma->complex_mode)) {
  			/* fast path successful! */
  			return sops->sem_num;
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
363
  		}
5e9d52759   Manfred Spraul   ipc/sem.c: fix ra...
364
365
366
367
368
  		spin_unlock(&sem->lock);
  	}
  
  	/* slow path: acquire the full lock */
  	ipc_lock_object(&sma->sem_perm);
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
369

5e9d52759   Manfred Spraul   ipc/sem.c: fix ra...
370
371
372
373
374
375
376
377
  	if (sma->complex_count == 0) {
  		/* False alarm:
  		 * There is no complex operation, thus we can switch
  		 * back to the fast path.
  		 */
  		spin_lock(&sem->lock);
  		ipc_unlock_object(&sma->sem_perm);
  		return sops->sem_num;
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
378
  	} else {
5e9d52759   Manfred Spraul   ipc/sem.c: fix ra...
379
380
  		/* Not a false alarm, thus complete the sequence for a
  		 * full lock.
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
381
  		 */
5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
382
383
  		complexmode_enter(sma);
  		return SEM_GLOBAL_LOCK;
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
384
  	}
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
385
386
387
388
  }
  
  static inline void sem_unlock(struct sem_array *sma, int locknum)
  {
5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
389
  	if (locknum == SEM_GLOBAL_LOCK) {
f269f40ad   Manfred Spraul   ipc/sem.c: always...
390
  		unmerge_queues(sma);
5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
391
  		complexmode_tryleave(sma);
cf9d5d78d   Davidlohr Bueso   ipc: close open c...
392
  		ipc_unlock_object(&sma->sem_perm);
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
393
394
395
396
  	} else {
  		struct sem *sem = sma->sem_base + locknum;
  		spin_unlock(&sem->lock);
  	}
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
397
398
399
  }
  
  /*
d9a605e40   Davidlohr Bueso   ipc: rename ids->...
400
   * sem_lock_(check_) routines are called in the paths where the rwsem
3e148c799   Nadia Derbey   fix idr_find() lo...
401
   * is not held.
321310ced   Linus Torvalds   ipc: move sem_obt...
402
403
   *
   * The caller holds the RCU read lock.
3e148c799   Nadia Derbey   fix idr_find() lo...
404
   */
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
405
406
  static inline struct sem_array *sem_obtain_lock(struct ipc_namespace *ns,
  			int id, struct sembuf *sops, int nsops, int *locknum)
023a53557   Nadia Derbey   ipc: integrate ip...
407
  {
c460b662d   Rik van Riel   ipc,sem: open cod...
408
409
  	struct kern_ipc_perm *ipcp;
  	struct sem_array *sma;
03f02c765   Nadia Derbey   Storing ipcs into...
410

55b7ae501   Davidlohr Bueso   ipc: rename ipc_o...
411
  	ipcp = ipc_obtain_object_idr(&sem_ids(ns), id);
321310ced   Linus Torvalds   ipc: move sem_obt...
412
413
  	if (IS_ERR(ipcp))
  		return ERR_CAST(ipcp);
b1ed88b47   Pierre Peiffer   IPC: fix error ch...
414

6062a8dc0   Rik van Riel   ipc,sem: fine gra...
415
416
  	sma = container_of(ipcp, struct sem_array, sem_perm);
  	*locknum = sem_lock(sma, sops, nsops);
c460b662d   Rik van Riel   ipc,sem: open cod...
417
418
419
420
  
  	/* ipc_rmid() may have already freed the ID while sem_lock
  	 * was spinning: verify that the structure is still valid
  	 */
72a8ff2f9   Rafael Aquini   ipc: change kern_...
421
  	if (ipc_valid_object(ipcp))
c460b662d   Rik van Riel   ipc,sem: open cod...
422
  		return container_of(ipcp, struct sem_array, sem_perm);
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
423
  	sem_unlock(sma, *locknum);
321310ced   Linus Torvalds   ipc: move sem_obt...
424
  	return ERR_PTR(-EINVAL);
023a53557   Nadia Derbey   ipc: integrate ip...
425
  }
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
426
427
  static inline struct sem_array *sem_obtain_object(struct ipc_namespace *ns, int id)
  {
55b7ae501   Davidlohr Bueso   ipc: rename ipc_o...
428
  	struct kern_ipc_perm *ipcp = ipc_obtain_object_idr(&sem_ids(ns), id);
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
429
430
431
432
433
434
  
  	if (IS_ERR(ipcp))
  		return ERR_CAST(ipcp);
  
  	return container_of(ipcp, struct sem_array, sem_perm);
  }
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
435
436
437
438
439
440
441
  static inline struct sem_array *sem_obtain_object_check(struct ipc_namespace *ns,
  							int id)
  {
  	struct kern_ipc_perm *ipcp = ipc_obtain_object_check(&sem_ids(ns), id);
  
  	if (IS_ERR(ipcp))
  		return ERR_CAST(ipcp);
b1ed88b47   Pierre Peiffer   IPC: fix error ch...
442

03f02c765   Nadia Derbey   Storing ipcs into...
443
  	return container_of(ipcp, struct sem_array, sem_perm);
023a53557   Nadia Derbey   ipc: integrate ip...
444
  }
6ff379721   Pierre Peiffer   IPC/semaphores: c...
445
446
  static inline void sem_lock_and_putref(struct sem_array *sma)
  {
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
447
  	sem_lock(sma, NULL, -1);
9b24fef9f   Fabian Frederick   sysv, ipc: fix se...
448
  	ipc_rcu_putref(sma, sem_rcu_free);
6ff379721   Pierre Peiffer   IPC/semaphores: c...
449
  }
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
450
451
452
453
  static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s)
  {
  	ipc_rmid(&sem_ids(ns), &s->sem_perm);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
455
456
457
458
  /*
   * Lockless wakeup algorithm:
   * Without the check/retry algorithm a lockless wakeup is possible:
   * - queue.status is initialized to -EINTR before blocking.
   * - wakeup is performed by
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
459
   *	* unlinking the queue entry from the pending list
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460
461
462
463
464
465
   *	* setting queue.status to IN_WAKEUP
   *	  This is the notification for the blocked thread that a
   *	  result value is imminent.
   *	* call wake_up_process
   *	* set queue.status to the final value.
   * - the previously blocked thread checks queue.status:
239521f31   Manfred Spraul   ipc: whitespace c...
466
467
468
469
470
   *	* if it's IN_WAKEUP, then it must wait until the value changes
   *	* if it's not -EINTR, then the operation was completed by
   *	  update_queue. semtimedop can return queue.status without
   *	  performing any operation on the sem array.
   *	* otherwise it must acquire the spinlock and check what's up.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
   *
   * The two-stage algorithm is necessary to protect against the following
   * races:
   * - if queue.status is set after wake_up_process, then the woken up idle
   *   thread could race forward and try (and fail) to acquire sma->lock
   *   before update_queue had a chance to set queue.status
   * - if queue.status is written before wake_up_process and if the
   *   blocked process is woken up by a signal between writing
   *   queue.status and the wake_up_process, then the woken up
   *   process could return from semtimedop and die by calling
   *   sys_exit before wake_up_process is called. Then wake_up_process
   *   will oops, because the task structure is already invalid.
   *   (yes, this happened on s390 with sysv msg).
   *
   */
  #define IN_WAKEUP	1
f4566f048   Nadia Derbey   ipc: fix wrong co...
487
488
489
490
491
  /**
   * newary - Create a new semaphore set
   * @ns: namespace
   * @params: ptr to the structure that contains key, semflg and nsems
   *
d9a605e40   Davidlohr Bueso   ipc: rename ids->...
492
   * Called with sem_ids.rwsem held (as a writer)
f4566f048   Nadia Derbey   ipc: fix wrong co...
493
   */
7748dbfaa   Nadia Derbey   ipc: unify the sy...
494
  static int newary(struct ipc_namespace *ns, struct ipc_params *params)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
496
497
498
499
  {
  	int id;
  	int retval;
  	struct sem_array *sma;
  	int size;
7748dbfaa   Nadia Derbey   ipc: unify the sy...
500
501
502
  	key_t key = params->key;
  	int nsems = params->u.nsems;
  	int semflg = params->flg;
b97e820ff   Manfred Spraul   ipc/sem.c: add a ...
503
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
504
505
506
  
  	if (!nsems)
  		return -EINVAL;
e38935341   Kirill Korotaev   [PATCH] IPC names...
507
  	if (ns->used_sems + nsems > ns->sc_semmns)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508
  		return -ENOSPC;
239521f31   Manfred Spraul   ipc: whitespace c...
509
  	size = sizeof(*sma) + nsems * sizeof(struct sem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
510
  	sma = ipc_rcu_alloc(size);
3ab08fe20   Davidlohr Bueso   ipc: remove brace...
511
  	if (!sma)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512
  		return -ENOMEM;
3ab08fe20   Davidlohr Bueso   ipc: remove brace...
513

239521f31   Manfred Spraul   ipc: whitespace c...
514
  	memset(sma, 0, size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515
516
517
518
519
520
521
  
  	sma->sem_perm.mode = (semflg & S_IRWXUGO);
  	sma->sem_perm.key = key;
  
  	sma->sem_perm.security = NULL;
  	retval = security_sem_alloc(sma);
  	if (retval) {
53dad6d3a   Davidlohr Bueso   ipc: fix race wit...
522
  		ipc_rcu_putref(sma, ipc_rcu_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
524
  		return retval;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
525
  	sma->sem_base = (struct sem *) &sma[1];
b97e820ff   Manfred Spraul   ipc/sem.c: add a ...
526

6062a8dc0   Rik van Riel   ipc,sem: fine gra...
527
  	for (i = 0; i < nsems; i++) {
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
528
529
  		INIT_LIST_HEAD(&sma->sem_base[i].pending_alter);
  		INIT_LIST_HEAD(&sma->sem_base[i].pending_const);
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
530
531
  		spin_lock_init(&sma->sem_base[i].lock);
  	}
b97e820ff   Manfred Spraul   ipc/sem.c: add a ...
532
533
  
  	sma->complex_count = 0;
5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
534
  	sma->complex_mode = true; /* dropped by sem_unlock below */
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
535
536
  	INIT_LIST_HEAD(&sma->pending_alter);
  	INIT_LIST_HEAD(&sma->pending_const);
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
537
  	INIT_LIST_HEAD(&sma->list_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538
539
  	sma->sem_nsems = nsems;
  	sma->sem_ctime = get_seconds();
e8577d1f0   Manfred Spraul   ipc/sem.c: fully ...
540
541
542
543
544
545
546
  
  	id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni);
  	if (id < 0) {
  		ipc_rcu_putref(sma, sem_rcu_free);
  		return id;
  	}
  	ns->used_sems += nsems;
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
547
  	sem_unlock(sma, -1);
6d49dab8a   Linus Torvalds   ipc: move rcu_rea...
548
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
549

7ca7e564e   Nadia Derbey   ipc: store ipcs i...
550
  	return sma->sem_perm.id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
551
  }
7748dbfaa   Nadia Derbey   ipc: unify the sy...
552

f4566f048   Nadia Derbey   ipc: fix wrong co...
553
  /*
d9a605e40   Davidlohr Bueso   ipc: rename ids->...
554
   * Called with sem_ids.rwsem and ipcp locked.
f4566f048   Nadia Derbey   ipc: fix wrong co...
555
   */
03f02c765   Nadia Derbey   Storing ipcs into...
556
  static inline int sem_security(struct kern_ipc_perm *ipcp, int semflg)
7748dbfaa   Nadia Derbey   ipc: unify the sy...
557
  {
03f02c765   Nadia Derbey   Storing ipcs into...
558
559
560
561
  	struct sem_array *sma;
  
  	sma = container_of(ipcp, struct sem_array, sem_perm);
  	return security_sem_associate(sma, semflg);
7748dbfaa   Nadia Derbey   ipc: unify the sy...
562
  }
f4566f048   Nadia Derbey   ipc: fix wrong co...
563
  /*
d9a605e40   Davidlohr Bueso   ipc: rename ids->...
564
   * Called with sem_ids.rwsem and ipcp locked.
f4566f048   Nadia Derbey   ipc: fix wrong co...
565
   */
03f02c765   Nadia Derbey   Storing ipcs into...
566
567
  static inline int sem_more_checks(struct kern_ipc_perm *ipcp,
  				struct ipc_params *params)
7748dbfaa   Nadia Derbey   ipc: unify the sy...
568
  {
03f02c765   Nadia Derbey   Storing ipcs into...
569
570
571
572
  	struct sem_array *sma;
  
  	sma = container_of(ipcp, struct sem_array, sem_perm);
  	if (params->u.nsems > sma->sem_nsems)
7748dbfaa   Nadia Derbey   ipc: unify the sy...
573
574
575
576
  		return -EINVAL;
  
  	return 0;
  }
d5460c997   Heiko Carstens   [CVE-2009-0029] S...
577
  SYSCALL_DEFINE3(semget, key_t, key, int, nsems, int, semflg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
578
  {
e38935341   Kirill Korotaev   [PATCH] IPC names...
579
  	struct ipc_namespace *ns;
eb66ec44f   Mathias Krause   ipc: constify ipc...
580
581
582
583
584
  	static const struct ipc_ops sem_ops = {
  		.getnew = newary,
  		.associate = sem_security,
  		.more_checks = sem_more_checks,
  	};
7748dbfaa   Nadia Derbey   ipc: unify the sy...
585
  	struct ipc_params sem_params;
e38935341   Kirill Korotaev   [PATCH] IPC names...
586
587
  
  	ns = current->nsproxy->ipc_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588

e38935341   Kirill Korotaev   [PATCH] IPC names...
589
  	if (nsems < 0 || nsems > ns->sc_semmsl)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
  		return -EINVAL;
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
591

7748dbfaa   Nadia Derbey   ipc: unify the sy...
592
593
594
  	sem_params.key = key;
  	sem_params.flg = semflg;
  	sem_params.u.nsems = nsems;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595

7748dbfaa   Nadia Derbey   ipc: unify the sy...
596
  	return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
597
  }
78f5009cc   Petr Mladek   ipc/sem.c: avoid ...
598
599
  /**
   * perform_atomic_semop - Perform (if possible) a semaphore operation
758a6ba39   Manfred Spraul   ipc/sem.c: rename...
600
   * @sma: semaphore array
d198cd6d6   Manfred Spraul   ipc/sem.c: change...
601
   * @q: struct sem_queue that describes the operation
758a6ba39   Manfred Spraul   ipc/sem.c: rename...
602
603
604
605
   *
   * Returns 0 if the operation was possible.
   * Returns 1 if the operation is impossible, the caller must sleep.
   * Negative values are error codes.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
606
   */
d198cd6d6   Manfred Spraul   ipc/sem.c: change...
607
  static int perform_atomic_semop(struct sem_array *sma, struct sem_queue *q)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
608
  {
d198cd6d6   Manfred Spraul   ipc/sem.c: change...
609
  	int result, sem_op, nsops, pid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
610
  	struct sembuf *sop;
239521f31   Manfred Spraul   ipc: whitespace c...
611
  	struct sem *curr;
d198cd6d6   Manfred Spraul   ipc/sem.c: change...
612
613
614
615
616
617
  	struct sembuf *sops;
  	struct sem_undo *un;
  
  	sops = q->sops;
  	nsops = q->nsops;
  	un = q->undo;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
618
619
620
621
622
  
  	for (sop = sops; sop < sops + nsops; sop++) {
  		curr = sma->sem_base + sop->sem_num;
  		sem_op = sop->sem_op;
  		result = curr->semval;
78f5009cc   Petr Mladek   ipc/sem.c: avoid ...
623

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
624
625
626
627
628
629
630
631
  		if (!sem_op && result)
  			goto would_block;
  
  		result += sem_op;
  		if (result < 0)
  			goto would_block;
  		if (result > SEMVMX)
  			goto out_of_range;
78f5009cc   Petr Mladek   ipc/sem.c: avoid ...
632

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
633
634
  		if (sop->sem_flg & SEM_UNDO) {
  			int undo = un->semadj[sop->sem_num] - sem_op;
78f5009cc   Petr Mladek   ipc/sem.c: avoid ...
635
  			/* Exceeding the undo range is an error. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
637
  			if (undo < (-SEMAEM - 1) || undo > SEMAEM)
  				goto out_of_range;
78f5009cc   Petr Mladek   ipc/sem.c: avoid ...
638
  			un->semadj[sop->sem_num] = undo;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
639
  		}
78f5009cc   Petr Mladek   ipc/sem.c: avoid ...
640

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641
642
643
644
  		curr->semval = result;
  	}
  
  	sop--;
d198cd6d6   Manfred Spraul   ipc/sem.c: change...
645
  	pid = q->pid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
646
647
  	while (sop >= sops) {
  		sma->sem_base[sop->sem_num].sempid = pid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
648
649
  		sop--;
  	}
78f5009cc   Petr Mladek   ipc/sem.c: avoid ...
650

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
651
652
653
654
655
656
657
  	return 0;
  
  out_of_range:
  	result = -ERANGE;
  	goto undo;
  
  would_block:
ed247b7ca   Manfred Spraul   ipc/sem.c: store ...
658
  	q->blocking = sop;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
659
660
661
662
663
664
665
666
  	if (sop->sem_flg & IPC_NOWAIT)
  		result = -EAGAIN;
  	else
  		result = 1;
  
  undo:
  	sop--;
  	while (sop >= sops) {
78f5009cc   Petr Mladek   ipc/sem.c: avoid ...
667
668
669
670
  		sem_op = sop->sem_op;
  		sma->sem_base[sop->sem_num].semval -= sem_op;
  		if (sop->sem_flg & SEM_UNDO)
  			un->semadj[sop->sem_num] += sem_op;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
671
672
673
674
675
  		sop--;
  	}
  
  	return result;
  }
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
676
677
678
679
680
  /** wake_up_sem_queue_prepare(q, error): Prepare wake-up
   * @q: queue entry that must be signaled
   * @error: Error value for the signal
   *
   * Prepare the wake-up of the queue entry q.
d4212093d   Nick Piggin   ipc/sem.c: sem pr...
681
   */
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
682
683
  static void wake_up_sem_queue_prepare(struct list_head *pt,
  				struct sem_queue *q, int error)
d4212093d   Nick Piggin   ipc/sem.c: sem pr...
684
  {
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
685
686
687
688
689
690
691
  	if (list_empty(pt)) {
  		/*
  		 * Hold preempt off so that we don't get preempted and have the
  		 * wakee busy-wait until we're scheduled back on.
  		 */
  		preempt_disable();
  	}
d4212093d   Nick Piggin   ipc/sem.c: sem pr...
692
  	q->status = IN_WAKEUP;
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
693
  	q->pid = error;
9f1bc2c90   Rik van Riel   ipc,sem: have onl...
694
  	list_add_tail(&q->list, pt);
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
695
696
697
  }
  
  /**
8001c8581   Davidlohr Bueso   ipc: standardize ...
698
   * wake_up_sem_queue_do - do the actual wake-up
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
699
700
701
702
703
704
705
706
707
708
709
710
711
   * @pt: list of tasks to be woken up
   *
   * Do the actual wake-up.
   * The function is called without any locks held, thus the semaphore array
   * could be destroyed already and the tasks can disappear as soon as the
   * status is set to the actual return code.
   */
  static void wake_up_sem_queue_do(struct list_head *pt)
  {
  	struct sem_queue *q, *t;
  	int did_something;
  
  	did_something = !list_empty(pt);
9f1bc2c90   Rik van Riel   ipc,sem: have onl...
712
  	list_for_each_entry_safe(q, t, pt, list) {
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
713
714
715
716
717
718
719
  		wake_up_process(q->sleeper);
  		/* q can disappear immediately after writing q->status. */
  		smp_wmb();
  		q->status = q->pid;
  	}
  	if (did_something)
  		preempt_enable();
d4212093d   Nick Piggin   ipc/sem.c: sem pr...
720
  }
b97e820ff   Manfred Spraul   ipc/sem.c: add a ...
721
722
723
  static void unlink_queue(struct sem_array *sma, struct sem_queue *q)
  {
  	list_del(&q->list);
9f1bc2c90   Rik van Riel   ipc,sem: have onl...
724
  	if (q->nsops > 1)
b97e820ff   Manfred Spraul   ipc/sem.c: add a ...
725
726
  		sma->complex_count--;
  }
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
727
728
729
730
731
732
733
  /** check_restart(sma, q)
   * @sma: semaphore array
   * @q: the operation that just completed
   *
   * update_queue is O(N^2) when it restarts scanning the whole queue of
   * waiting operations. Therefore this function checks if the restart is
   * really necessary. It is called after a previously waiting operation
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
734
735
   * modified the array.
   * Note that wait-for-zero operations are handled without restart.
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
736
737
738
   */
  static int check_restart(struct sem_array *sma, struct sem_queue *q)
  {
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
739
740
  	/* pending complex alter operations are too difficult to analyse */
  	if (!list_empty(&sma->pending_alter))
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
741
742
743
744
745
  		return 1;
  
  	/* we were a sleeping complex operation. Too difficult */
  	if (q->nsops > 1)
  		return 1;
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
746
747
748
749
750
751
752
753
754
755
756
757
758
  	/* It is impossible that someone waits for the new value:
  	 * - complex operations always restart.
  	 * - wait-for-zero are handled seperately.
  	 * - q is a previously sleeping simple operation that
  	 *   altered the array. It must be a decrement, because
  	 *   simple increments never sleep.
  	 * - If there are older (higher priority) decrements
  	 *   in the queue, then they have observed the original
  	 *   semval value and couldn't proceed. The operation
  	 *   decremented to value - thus they won't proceed either.
  	 */
  	return 0;
  }
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
759

1a82e9e1d   Manfred Spraul   ipc/sem: separate...
760
  /**
8001c8581   Davidlohr Bueso   ipc: standardize ...
761
   * wake_const_ops - wake up non-alter tasks
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
   * @sma: semaphore array.
   * @semnum: semaphore that was modified.
   * @pt: list head for the tasks that must be woken up.
   *
   * wake_const_ops must be called after a semaphore in a semaphore array
   * was set to 0. If complex const operations are pending, wake_const_ops must
   * be called with semnum = -1, as well as with the number of each modified
   * semaphore.
   * The tasks that must be woken up are added to @pt. The return code
   * is stored in q->pid.
   * The function returns 1 if at least one operation was completed successfully.
   */
  static int wake_const_ops(struct sem_array *sma, int semnum,
  				struct list_head *pt)
  {
  	struct sem_queue *q;
  	struct list_head *walk;
  	struct list_head *pending_list;
  	int semop_completed = 0;
  
  	if (semnum == -1)
  		pending_list = &sma->pending_const;
  	else
  		pending_list = &sma->sem_base[semnum].pending_const;
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
786

1a82e9e1d   Manfred Spraul   ipc/sem: separate...
787
788
789
790
791
792
  	walk = pending_list->next;
  	while (walk != pending_list) {
  		int error;
  
  		q = container_of(walk, struct sem_queue, list);
  		walk = walk->next;
d198cd6d6   Manfred Spraul   ipc/sem.c: change...
793
  		error = perform_atomic_semop(sma, q);
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
  
  		if (error <= 0) {
  			/* operation completed, remove from queue & wakeup */
  
  			unlink_queue(sma, q);
  
  			wake_up_sem_queue_prepare(pt, q, error);
  			if (error == 0)
  				semop_completed = 1;
  		}
  	}
  	return semop_completed;
  }
  
  /**
8001c8581   Davidlohr Bueso   ipc: standardize ...
809
   * do_smart_wakeup_zero - wakeup all wait for zero tasks
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
810
811
812
813
814
   * @sma: semaphore array
   * @sops: operations that were performed
   * @nsops: number of operations
   * @pt: list head of the tasks that must be woken up.
   *
8001c8581   Davidlohr Bueso   ipc: standardize ...
815
816
   * Checks all required queue for wait-for-zero operations, based
   * on the actual changes that were performed on the semaphore array.
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
   * The function returns 1 if at least one operation was completed successfully.
   */
  static int do_smart_wakeup_zero(struct sem_array *sma, struct sembuf *sops,
  					int nsops, struct list_head *pt)
  {
  	int i;
  	int semop_completed = 0;
  	int got_zero = 0;
  
  	/* first: the per-semaphore queues, if known */
  	if (sops) {
  		for (i = 0; i < nsops; i++) {
  			int num = sops[i].sem_num;
  
  			if (sma->sem_base[num].semval == 0) {
  				got_zero = 1;
  				semop_completed |= wake_const_ops(sma, num, pt);
  			}
  		}
  	} else {
  		/*
  		 * No sops means modified semaphores not known.
  		 * Assume all were changed.
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
840
  		 */
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
841
842
843
844
845
846
  		for (i = 0; i < sma->sem_nsems; i++) {
  			if (sma->sem_base[i].semval == 0) {
  				got_zero = 1;
  				semop_completed |= wake_const_ops(sma, i, pt);
  			}
  		}
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
847
848
  	}
  	/*
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
849
850
  	 * If one of the modified semaphores got 0,
  	 * then check the global queue, too.
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
851
  	 */
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
852
853
  	if (got_zero)
  		semop_completed |= wake_const_ops(sma, -1, pt);
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
854

1a82e9e1d   Manfred Spraul   ipc/sem: separate...
855
  	return semop_completed;
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
856
  }
636c6be82   Manfred Spraul   ipc/sem.c: optimi...
857
858
  
  /**
8001c8581   Davidlohr Bueso   ipc: standardize ...
859
   * update_queue - look for tasks that can be completed.
636c6be82   Manfred Spraul   ipc/sem.c: optimi...
860
861
   * @sma: semaphore array.
   * @semnum: semaphore that was modified.
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
862
   * @pt: list head for the tasks that must be woken up.
636c6be82   Manfred Spraul   ipc/sem.c: optimi...
863
864
   *
   * update_queue must be called after a semaphore in a semaphore array
9f1bc2c90   Rik van Riel   ipc,sem: have onl...
865
866
867
   * was modified. If multiple semaphores were modified, update_queue must
   * be called with semnum = -1, as well as with the number of each modified
   * semaphore.
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
868
869
   * The tasks that must be woken up are added to @pt. The return code
   * is stored in q->pid.
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
870
871
   * The function internally checks if const operations can now succeed.
   *
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
872
   * The function return 1 if at least one semop was completed successfully.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
873
   */
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
874
  static int update_queue(struct sem_array *sma, int semnum, struct list_head *pt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
875
  {
636c6be82   Manfred Spraul   ipc/sem.c: optimi...
876
877
878
  	struct sem_queue *q;
  	struct list_head *walk;
  	struct list_head *pending_list;
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
879
  	int semop_completed = 0;
636c6be82   Manfred Spraul   ipc/sem.c: optimi...
880

9f1bc2c90   Rik van Riel   ipc,sem: have onl...
881
  	if (semnum == -1)
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
882
  		pending_list = &sma->pending_alter;
9f1bc2c90   Rik van Riel   ipc,sem: have onl...
883
  	else
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
884
  		pending_list = &sma->sem_base[semnum].pending_alter;
9cad200c7   Nick Piggin   ipc/sem.c: sem us...
885
886
  
  again:
636c6be82   Manfred Spraul   ipc/sem.c: optimi...
887
888
  	walk = pending_list->next;
  	while (walk != pending_list) {
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
889
  		int error, restart;
636c6be82   Manfred Spraul   ipc/sem.c: optimi...
890

9f1bc2c90   Rik van Riel   ipc,sem: have onl...
891
  		q = container_of(walk, struct sem_queue, list);
636c6be82   Manfred Spraul   ipc/sem.c: optimi...
892
  		walk = walk->next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
893

d987f8b21   Manfred Spraul   ipc/sem.c: optimi...
894
895
  		/* If we are scanning the single sop, per-semaphore list of
  		 * one semaphore and that semaphore is 0, then it is not
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
896
  		 * necessary to scan further: simple increments
d987f8b21   Manfred Spraul   ipc/sem.c: optimi...
897
898
899
900
  		 * that affect only one entry succeed immediately and cannot
  		 * be in the  per semaphore pending queue, and decrements
  		 * cannot be successful if the value is already 0.
  		 */
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
901
  		if (semnum != -1 && sma->sem_base[semnum].semval == 0)
d987f8b21   Manfred Spraul   ipc/sem.c: optimi...
902
  			break;
d198cd6d6   Manfred Spraul   ipc/sem.c: change...
903
  		error = perform_atomic_semop(sma, q);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
904
905
  
  		/* Does q->sleeper still need to sleep? */
9cad200c7   Nick Piggin   ipc/sem.c: sem us...
906
907
  		if (error > 0)
  			continue;
b97e820ff   Manfred Spraul   ipc/sem.c: add a ...
908
  		unlink_queue(sma, q);
9cad200c7   Nick Piggin   ipc/sem.c: sem us...
909

0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
910
  		if (error) {
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
911
  			restart = 0;
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
912
913
  		} else {
  			semop_completed = 1;
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
914
  			do_smart_wakeup_zero(sma, q->sops, q->nsops, pt);
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
915
  			restart = check_restart(sma, q);
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
916
  		}
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
917

0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
918
  		wake_up_sem_queue_prepare(pt, q, error);
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
919
  		if (restart)
9cad200c7   Nick Piggin   ipc/sem.c: sem us...
920
  			goto again;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
921
  	}
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
922
  	return semop_completed;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
923
  }
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
924
  /**
8001c8581   Davidlohr Bueso   ipc: standardize ...
925
   * set_semotime - set sem_otime
0e8c66569   Manfred Spraul   ipc/sem.c: update...
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
   * @sma: semaphore array
   * @sops: operations that modified the array, may be NULL
   *
   * sem_otime is replicated to avoid cache line trashing.
   * This function sets one instance to the current time.
   */
  static void set_semotime(struct sem_array *sma, struct sembuf *sops)
  {
  	if (sops == NULL) {
  		sma->sem_base[0].sem_otime = get_seconds();
  	} else {
  		sma->sem_base[sops[0].sem_num].sem_otime =
  							get_seconds();
  	}
  }
  
  /**
8001c8581   Davidlohr Bueso   ipc: standardize ...
943
   * do_smart_update - optimized update_queue
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
944
945
946
   * @sma: semaphore array
   * @sops: operations that were performed
   * @nsops: number of operations
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
947
948
   * @otime: force setting otime
   * @pt: list head of the tasks that must be woken up.
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
949
   *
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
950
951
   * do_smart_update() does the required calls to update_queue and wakeup_zero,
   * based on the actual changes that were performed on the semaphore array.
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
952
953
954
   * Note that the function does not do the actual wake-up: the caller is
   * responsible for calling wake_up_sem_queue_do(@pt).
   * It is safe to perform this call after dropping all locks.
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
955
   */
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
956
957
  static void do_smart_update(struct sem_array *sma, struct sembuf *sops, int nsops,
  			int otime, struct list_head *pt)
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
958
959
  {
  	int i;
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
960
  	otime |= do_smart_wakeup_zero(sma, sops, nsops, pt);
f269f40ad   Manfred Spraul   ipc/sem.c: always...
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
  	if (!list_empty(&sma->pending_alter)) {
  		/* semaphore array uses the global queue - just process it. */
  		otime |= update_queue(sma, -1, pt);
  	} else {
  		if (!sops) {
  			/*
  			 * No sops, thus the modified semaphores are not
  			 * known. Check all.
  			 */
  			for (i = 0; i < sma->sem_nsems; i++)
  				otime |= update_queue(sma, i, pt);
  		} else {
  			/*
  			 * Check the semaphores that were increased:
  			 * - No complex ops, thus all sleeping ops are
  			 *   decrease.
  			 * - if we decreased the value, then any sleeping
  			 *   semaphore ops wont be able to run: If the
  			 *   previous value was too small, then the new
  			 *   value will be too small, too.
  			 */
  			for (i = 0; i < nsops; i++) {
  				if (sops[i].sem_op > 0) {
  					otime |= update_queue(sma,
  							sops[i].sem_num, pt);
  				}
ab465df9d   Manfred Spraul   ipc/sem.c: Fix mi...
987
  			}
9f1bc2c90   Rik van Riel   ipc,sem: have onl...
988
  		}
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
989
  	}
0e8c66569   Manfred Spraul   ipc/sem.c: update...
990
991
  	if (otime)
  		set_semotime(sma, sops);
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
992
  }
2f2ed41dc   Manfred Spraul   ipc/sem.c: remove...
993
  /*
b220c57ae   Manfred Spraul   ipc/sem.c: make s...
994
   * check_qop: Test if a queued operation sleeps on the semaphore semnum
2f2ed41dc   Manfred Spraul   ipc/sem.c: remove...
995
996
997
998
   */
  static int check_qop(struct sem_array *sma, int semnum, struct sem_queue *q,
  			bool count_zero)
  {
b220c57ae   Manfred Spraul   ipc/sem.c: make s...
999
  	struct sembuf *sop = q->blocking;
2f2ed41dc   Manfred Spraul   ipc/sem.c: remove...
1000

9b44ee2ee   Manfred Spraul   ipc/sem.c: add a ...
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
  	/*
  	 * Linux always (since 0.99.10) reported a task as sleeping on all
  	 * semaphores. This violates SUS, therefore it was changed to the
  	 * standard compliant behavior.
  	 * Give the administrators a chance to notice that an application
  	 * might misbehave because it relies on the Linux behavior.
  	 */
  	pr_info_once("semctl(GETNCNT/GETZCNT) is since 3.16 Single Unix Specification compliant.
  "
  			"The task %s (%d) triggered the difference, watch for misbehavior.
  ",
  			current->comm, task_pid_nr(current));
b220c57ae   Manfred Spraul   ipc/sem.c: make s...
1013
1014
  	if (sop->sem_num != semnum)
  		return 0;
2f2ed41dc   Manfred Spraul   ipc/sem.c: remove...
1015

b220c57ae   Manfred Spraul   ipc/sem.c: make s...
1016
1017
1018
1019
1020
1021
  	if (count_zero && sop->sem_op == 0)
  		return 1;
  	if (!count_zero && sop->sem_op < 0)
  		return 1;
  
  	return 0;
2f2ed41dc   Manfred Spraul   ipc/sem.c: remove...
1022
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1023
1024
1025
  /* The following counts are associated to each semaphore:
   *   semncnt        number of tasks waiting on semval being nonzero
   *   semzcnt        number of tasks waiting on semval being zero
b220c57ae   Manfred Spraul   ipc/sem.c: make s...
1026
1027
1028
   *
   * Per definition, a task waits only on the semaphore of the first semop
   * that cannot proceed, even if additional operation would block, too.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1029
   */
2f2ed41dc   Manfred Spraul   ipc/sem.c: remove...
1030
1031
  static int count_semcnt(struct sem_array *sma, ushort semnum,
  			bool count_zero)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1032
  {
2f2ed41dc   Manfred Spraul   ipc/sem.c: remove...
1033
  	struct list_head *l;
239521f31   Manfred Spraul   ipc: whitespace c...
1034
  	struct sem_queue *q;
2f2ed41dc   Manfred Spraul   ipc/sem.c: remove...
1035
  	int semcnt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1036

2f2ed41dc   Manfred Spraul   ipc/sem.c: remove...
1037
1038
1039
1040
1041
1042
  	semcnt = 0;
  	/* First: check the simple operations. They are easy to evaluate */
  	if (count_zero)
  		l = &sma->sem_base[semnum].pending_const;
  	else
  		l = &sma->sem_base[semnum].pending_alter;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1043

2f2ed41dc   Manfred Spraul   ipc/sem.c: remove...
1044
1045
1046
1047
1048
  	list_for_each_entry(q, l, list) {
  		/* all task on a per-semaphore list sleep on exactly
  		 * that semaphore
  		 */
  		semcnt++;
ebc2e5e6a   Rik van Riel   ipc,sem: fix semc...
1049
  	}
2f2ed41dc   Manfred Spraul   ipc/sem.c: remove...
1050
  	/* Then: check the complex operations. */
1994862dc   Manfred Spraul   ipc/sem.c: bugfix...
1051
  	list_for_each_entry(q, &sma->pending_alter, list) {
2f2ed41dc   Manfred Spraul   ipc/sem.c: remove...
1052
1053
1054
1055
1056
1057
  		semcnt += check_qop(sma, semnum, q, count_zero);
  	}
  	if (count_zero) {
  		list_for_each_entry(q, &sma->pending_const, list) {
  			semcnt += check_qop(sma, semnum, q, count_zero);
  		}
1994862dc   Manfred Spraul   ipc/sem.c: bugfix...
1058
  	}
2f2ed41dc   Manfred Spraul   ipc/sem.c: remove...
1059
  	return semcnt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1060
  }
d9a605e40   Davidlohr Bueso   ipc: rename ids->...
1061
1062
  /* Free a semaphore set. freeary() is called with sem_ids.rwsem locked
   * as a writer and the spinlock for this semaphore set hold. sem_ids.rwsem
3e148c799   Nadia Derbey   fix idr_find() lo...
1063
   * remains locked on exit.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1064
   */
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
1065
  static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1066
  {
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1067
1068
  	struct sem_undo *un, *tu;
  	struct sem_queue *q, *tq;
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
1069
  	struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm);
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
1070
  	struct list_head tasks;
9f1bc2c90   Rik van Riel   ipc,sem: have onl...
1071
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1072

380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1073
  	/* Free the existing undo structures for this semaphore set.  */
cf9d5d78d   Davidlohr Bueso   ipc: close open c...
1074
  	ipc_assert_locked_object(&sma->sem_perm);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1075
1076
1077
  	list_for_each_entry_safe(un, tu, &sma->list_id, list_id) {
  		list_del(&un->list_id);
  		spin_lock(&un->ulp->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1078
  		un->semid = -1;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1079
1080
  		list_del_rcu(&un->list_proc);
  		spin_unlock(&un->ulp->lock);
693a8b6ee   Lai Jiangshan   ipc,rcu: Convert ...
1081
  		kfree_rcu(un, rcu);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1082
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1083
1084
  
  	/* Wake up all pending processes and let them fail with EIDRM. */
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
1085
  	INIT_LIST_HEAD(&tasks);
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
1086
1087
1088
1089
1090
1091
  	list_for_each_entry_safe(q, tq, &sma->pending_const, list) {
  		unlink_queue(sma, q);
  		wake_up_sem_queue_prepare(&tasks, q, -EIDRM);
  	}
  
  	list_for_each_entry_safe(q, tq, &sma->pending_alter, list) {
b97e820ff   Manfred Spraul   ipc/sem.c: add a ...
1092
  		unlink_queue(sma, q);
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
1093
  		wake_up_sem_queue_prepare(&tasks, q, -EIDRM);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1094
  	}
9f1bc2c90   Rik van Riel   ipc,sem: have onl...
1095
1096
  	for (i = 0; i < sma->sem_nsems; i++) {
  		struct sem *sem = sma->sem_base + i;
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
1097
1098
1099
1100
1101
  		list_for_each_entry_safe(q, tq, &sem->pending_const, list) {
  			unlink_queue(sma, q);
  			wake_up_sem_queue_prepare(&tasks, q, -EIDRM);
  		}
  		list_for_each_entry_safe(q, tq, &sem->pending_alter, list) {
9f1bc2c90   Rik van Riel   ipc,sem: have onl...
1102
1103
1104
1105
  			unlink_queue(sma, q);
  			wake_up_sem_queue_prepare(&tasks, q, -EIDRM);
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1106

7ca7e564e   Nadia Derbey   ipc: store ipcs i...
1107
1108
  	/* Remove the semaphore set from the IDR */
  	sem_rmid(ns, sma);
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1109
  	sem_unlock(sma, -1);
6d49dab8a   Linus Torvalds   ipc: move rcu_rea...
1110
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1111

0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
1112
  	wake_up_sem_queue_do(&tasks);
e38935341   Kirill Korotaev   [PATCH] IPC names...
1113
  	ns->used_sems -= sma->sem_nsems;
53dad6d3a   Davidlohr Bueso   ipc: fix race wit...
1114
  	ipc_rcu_putref(sma, sem_rcu_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1115
1116
1117
1118
  }
  
  static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in, int version)
  {
239521f31   Manfred Spraul   ipc: whitespace c...
1119
  	switch (version) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1120
1121
1122
1123
1124
  	case IPC_64:
  		return copy_to_user(buf, in, sizeof(*in));
  	case IPC_OLD:
  	    {
  		struct semid_ds out;
982f7c2b2   Dan Rosenberg   sys_semctl: fix k...
1125
  		memset(&out, 0, sizeof(out));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
  		ipc64_perm_to_ipc_perm(&in->sem_perm, &out.sem_perm);
  
  		out.sem_otime	= in->sem_otime;
  		out.sem_ctime	= in->sem_ctime;
  		out.sem_nsems	= in->sem_nsems;
  
  		return copy_to_user(buf, &out, sizeof(out));
  	    }
  	default:
  		return -EINVAL;
  	}
  }
d12e1e50e   Manfred Spraul   ipc/sem.c: replac...
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
  static time_t get_semotime(struct sem_array *sma)
  {
  	int i;
  	time_t res;
  
  	res = sma->sem_base[0].sem_otime;
  	for (i = 1; i < sma->sem_nsems; i++) {
  		time_t to = sma->sem_base[i].sem_otime;
  
  		if (to > res)
  			res = to;
  	}
  	return res;
  }
4b9fcb0ec   Pierre Peiffer   IPC/semaphores: c...
1152
  static int semctl_nolock(struct ipc_namespace *ns, int semid,
e1fd1f490   Al Viro   get rid of union ...
1153
  			 int cmd, int version, void __user *p)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1154
  {
e5cc9c7b1   Amerigo Wang   ipc: remove unrea...
1155
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1156
  	struct sem_array *sma;
239521f31   Manfred Spraul   ipc: whitespace c...
1157
  	switch (cmd) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1158
1159
1160
1161
1162
1163
1164
1165
1166
  	case IPC_INFO:
  	case SEM_INFO:
  	{
  		struct seminfo seminfo;
  		int max_id;
  
  		err = security_sem_semctl(NULL, cmd);
  		if (err)
  			return err;
46c0a8ca3   Paul McQuade   ipc, kernel: clea...
1167

239521f31   Manfred Spraul   ipc: whitespace c...
1168
  		memset(&seminfo, 0, sizeof(seminfo));
e38935341   Kirill Korotaev   [PATCH] IPC names...
1169
1170
1171
1172
  		seminfo.semmni = ns->sc_semmni;
  		seminfo.semmns = ns->sc_semmns;
  		seminfo.semmsl = ns->sc_semmsl;
  		seminfo.semopm = ns->sc_semopm;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1173
1174
1175
1176
  		seminfo.semvmx = SEMVMX;
  		seminfo.semmnu = SEMMNU;
  		seminfo.semmap = SEMMAP;
  		seminfo.semume = SEMUME;
d9a605e40   Davidlohr Bueso   ipc: rename ids->...
1177
  		down_read(&sem_ids(ns).rwsem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1178
  		if (cmd == SEM_INFO) {
e38935341   Kirill Korotaev   [PATCH] IPC names...
1179
1180
  			seminfo.semusz = sem_ids(ns).in_use;
  			seminfo.semaem = ns->used_sems;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1181
1182
1183
1184
  		} else {
  			seminfo.semusz = SEMUSZ;
  			seminfo.semaem = SEMAEM;
  		}
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
1185
  		max_id = ipc_get_maxid(&sem_ids(ns));
d9a605e40   Davidlohr Bueso   ipc: rename ids->...
1186
  		up_read(&sem_ids(ns).rwsem);
46c0a8ca3   Paul McQuade   ipc, kernel: clea...
1187
  		if (copy_to_user(p, &seminfo, sizeof(struct seminfo)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1188
  			return -EFAULT;
239521f31   Manfred Spraul   ipc: whitespace c...
1189
  		return (max_id < 0) ? 0 : max_id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1190
  	}
4b9fcb0ec   Pierre Peiffer   IPC/semaphores: c...
1191
  	case IPC_STAT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1192
1193
1194
  	case SEM_STAT:
  	{
  		struct semid64_ds tbuf;
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
1195
1196
1197
  		int id = 0;
  
  		memset(&tbuf, 0, sizeof(tbuf));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1198

941b0304a   Linus Torvalds   ipc: simplify rcu...
1199
  		rcu_read_lock();
4b9fcb0ec   Pierre Peiffer   IPC/semaphores: c...
1200
  		if (cmd == SEM_STAT) {
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
1201
1202
1203
1204
1205
  			sma = sem_obtain_object(ns, semid);
  			if (IS_ERR(sma)) {
  				err = PTR_ERR(sma);
  				goto out_unlock;
  			}
4b9fcb0ec   Pierre Peiffer   IPC/semaphores: c...
1206
1207
  			id = sma->sem_perm.id;
  		} else {
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
1208
1209
1210
1211
1212
  			sma = sem_obtain_object_check(ns, semid);
  			if (IS_ERR(sma)) {
  				err = PTR_ERR(sma);
  				goto out_unlock;
  			}
4b9fcb0ec   Pierre Peiffer   IPC/semaphores: c...
1213
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1214
1215
  
  		err = -EACCES;
b0e77598f   Serge E. Hallyn   userns: user name...
1216
  		if (ipcperms(ns, &sma->sem_perm, S_IRUGO))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1217
1218
1219
1220
1221
  			goto out_unlock;
  
  		err = security_sem_semctl(sma, cmd);
  		if (err)
  			goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1222
  		kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
d12e1e50e   Manfred Spraul   ipc/sem.c: replac...
1223
1224
1225
  		tbuf.sem_otime = get_semotime(sma);
  		tbuf.sem_ctime = sma->sem_ctime;
  		tbuf.sem_nsems = sma->sem_nsems;
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
1226
  		rcu_read_unlock();
e1fd1f490   Al Viro   get rid of union ...
1227
  		if (copy_semid_to_user(p, &tbuf, version))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1228
1229
1230
1231
1232
1233
  			return -EFAULT;
  		return id;
  	}
  	default:
  		return -EINVAL;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1234
  out_unlock:
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
1235
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1236
1237
  	return err;
  }
e1fd1f490   Al Viro   get rid of union ...
1238
1239
1240
1241
1242
  static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum,
  		unsigned long arg)
  {
  	struct sem_undo *un;
  	struct sem_array *sma;
239521f31   Manfred Spraul   ipc: whitespace c...
1243
  	struct sem *curr;
e1fd1f490   Al Viro   get rid of union ...
1244
  	int err;
e1fd1f490   Al Viro   get rid of union ...
1245
1246
1247
1248
1249
1250
1251
1252
1253
  	struct list_head tasks;
  	int val;
  #if defined(CONFIG_64BIT) && defined(__BIG_ENDIAN)
  	/* big-endian 64bit */
  	val = arg >> 32;
  #else
  	/* 32bit or little-endian 64bit */
  	val = arg;
  #endif
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1254
1255
  	if (val > SEMVMX || val < 0)
  		return -ERANGE;
e1fd1f490   Al Viro   get rid of union ...
1256
1257
  
  	INIT_LIST_HEAD(&tasks);
e1fd1f490   Al Viro   get rid of union ...
1258

6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
  	rcu_read_lock();
  	sma = sem_obtain_object_check(ns, semid);
  	if (IS_ERR(sma)) {
  		rcu_read_unlock();
  		return PTR_ERR(sma);
  	}
  
  	if (semnum < 0 || semnum >= sma->sem_nsems) {
  		rcu_read_unlock();
  		return -EINVAL;
  	}
  
  
  	if (ipcperms(ns, &sma->sem_perm, S_IWUGO)) {
  		rcu_read_unlock();
  		return -EACCES;
  	}
e1fd1f490   Al Viro   get rid of union ...
1276
1277
  
  	err = security_sem_semctl(sma, SETVAL);
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1278
1279
1280
1281
  	if (err) {
  		rcu_read_unlock();
  		return -EACCES;
  	}
e1fd1f490   Al Viro   get rid of union ...
1282

6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1283
  	sem_lock(sma, NULL, -1);
e1fd1f490   Al Viro   get rid of union ...
1284

0f3d2b013   Rafael Aquini   ipc: introduce ip...
1285
  	if (!ipc_valid_object(&sma->sem_perm)) {
6e224f945   Manfred Spraul   ipc/sem.c: synchr...
1286
1287
1288
1289
  		sem_unlock(sma, -1);
  		rcu_read_unlock();
  		return -EIDRM;
  	}
e1fd1f490   Al Viro   get rid of union ...
1290
  	curr = &sma->sem_base[semnum];
cf9d5d78d   Davidlohr Bueso   ipc: close open c...
1291
  	ipc_assert_locked_object(&sma->sem_perm);
e1fd1f490   Al Viro   get rid of union ...
1292
1293
1294
1295
1296
1297
1298
1299
  	list_for_each_entry(un, &sma->list_id, list_id)
  		un->semadj[semnum] = 0;
  
  	curr->semval = val;
  	curr->sempid = task_tgid_vnr(current);
  	sma->sem_ctime = get_seconds();
  	/* maybe some queued-up processes were waiting for this */
  	do_smart_update(sma, NULL, 0, 0, &tasks);
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1300
  	sem_unlock(sma, -1);
6d49dab8a   Linus Torvalds   ipc: move rcu_rea...
1301
  	rcu_read_unlock();
e1fd1f490   Al Viro   get rid of union ...
1302
  	wake_up_sem_queue_do(&tasks);
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1303
  	return 0;
e1fd1f490   Al Viro   get rid of union ...
1304
  }
e38935341   Kirill Korotaev   [PATCH] IPC names...
1305
  static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
e1fd1f490   Al Viro   get rid of union ...
1306
  		int cmd, void __user *p)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1307
1308
  {
  	struct sem_array *sma;
239521f31   Manfred Spraul   ipc: whitespace c...
1309
  	struct sem *curr;
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
1310
  	int err, nsems;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1311
  	ushort fast_sem_io[SEMMSL_FAST];
239521f31   Manfred Spraul   ipc: whitespace c...
1312
  	ushort *sem_io = fast_sem_io;
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
1313
  	struct list_head tasks;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1314

16df3674e   Davidlohr Bueso   ipc,sem: do not h...
1315
1316
1317
1318
1319
1320
  	INIT_LIST_HEAD(&tasks);
  
  	rcu_read_lock();
  	sma = sem_obtain_object_check(ns, semid);
  	if (IS_ERR(sma)) {
  		rcu_read_unlock();
023a53557   Nadia Derbey   ipc: integrate ip...
1321
  		return PTR_ERR(sma);
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
1322
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1323
1324
  
  	nsems = sma->sem_nsems;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1325
  	err = -EACCES;
c728b9c87   Linus Torvalds   ipc: simplify sem...
1326
1327
  	if (ipcperms(ns, &sma->sem_perm, cmd == SETALL ? S_IWUGO : S_IRUGO))
  		goto out_rcu_wakeup;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1328
1329
  
  	err = security_sem_semctl(sma, cmd);
c728b9c87   Linus Torvalds   ipc: simplify sem...
1330
1331
  	if (err)
  		goto out_rcu_wakeup;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1332
1333
1334
1335
1336
  
  	err = -EACCES;
  	switch (cmd) {
  	case GETALL:
  	{
e1fd1f490   Al Viro   get rid of union ...
1337
  		ushort __user *array = p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1338
  		int i;
ce857229e   Al Viro   ipc: fix GETALL/I...
1339
  		sem_lock(sma, NULL, -1);
0f3d2b013   Rafael Aquini   ipc: introduce ip...
1340
  		if (!ipc_valid_object(&sma->sem_perm)) {
6e224f945   Manfred Spraul   ipc/sem.c: synchr...
1341
1342
1343
  			err = -EIDRM;
  			goto out_unlock;
  		}
239521f31   Manfred Spraul   ipc: whitespace c...
1344
  		if (nsems > SEMMSL_FAST) {
ce857229e   Al Viro   ipc: fix GETALL/I...
1345
  			if (!ipc_rcu_getref(sma)) {
ce857229e   Al Viro   ipc: fix GETALL/I...
1346
  				err = -EIDRM;
6e224f945   Manfred Spraul   ipc/sem.c: synchr...
1347
  				goto out_unlock;
ce857229e   Al Viro   ipc: fix GETALL/I...
1348
1349
  			}
  			sem_unlock(sma, -1);
6d49dab8a   Linus Torvalds   ipc: move rcu_rea...
1350
  			rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1351
  			sem_io = ipc_alloc(sizeof(ushort)*nsems);
239521f31   Manfred Spraul   ipc: whitespace c...
1352
  			if (sem_io == NULL) {
9b24fef9f   Fabian Frederick   sysv, ipc: fix se...
1353
  				ipc_rcu_putref(sma, sem_rcu_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1354
1355
  				return -ENOMEM;
  			}
4091fd942   Linus Torvalds   ipc: move the rcu...
1356
  			rcu_read_lock();
6ff379721   Pierre Peiffer   IPC/semaphores: c...
1357
  			sem_lock_and_putref(sma);
0f3d2b013   Rafael Aquini   ipc: introduce ip...
1358
  			if (!ipc_valid_object(&sma->sem_perm)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1359
  				err = -EIDRM;
6e224f945   Manfred Spraul   ipc/sem.c: synchr...
1360
  				goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1361
  			}
ce857229e   Al Viro   ipc: fix GETALL/I...
1362
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1363
1364
  		for (i = 0; i < sma->sem_nsems; i++)
  			sem_io[i] = sma->sem_base[i].semval;
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1365
  		sem_unlock(sma, -1);
6d49dab8a   Linus Torvalds   ipc: move rcu_rea...
1366
  		rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1367
  		err = 0;
239521f31   Manfred Spraul   ipc: whitespace c...
1368
  		if (copy_to_user(array, sem_io, nsems*sizeof(ushort)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1369
1370
1371
1372
1373
1374
1375
  			err = -EFAULT;
  		goto out_free;
  	}
  	case SETALL:
  	{
  		int i;
  		struct sem_undo *un;
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1376
  		if (!ipc_rcu_getref(sma)) {
6e224f945   Manfred Spraul   ipc/sem.c: synchr...
1377
1378
  			err = -EIDRM;
  			goto out_rcu_wakeup;
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1379
  		}
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
1380
  		rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1381

239521f31   Manfred Spraul   ipc: whitespace c...
1382
  		if (nsems > SEMMSL_FAST) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1383
  			sem_io = ipc_alloc(sizeof(ushort)*nsems);
239521f31   Manfred Spraul   ipc: whitespace c...
1384
  			if (sem_io == NULL) {
9b24fef9f   Fabian Frederick   sysv, ipc: fix se...
1385
  				ipc_rcu_putref(sma, sem_rcu_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1386
1387
1388
  				return -ENOMEM;
  			}
  		}
239521f31   Manfred Spraul   ipc: whitespace c...
1389
  		if (copy_from_user(sem_io, p, nsems*sizeof(ushort))) {
9b24fef9f   Fabian Frederick   sysv, ipc: fix se...
1390
  			ipc_rcu_putref(sma, sem_rcu_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1391
1392
1393
1394
1395
1396
  			err = -EFAULT;
  			goto out_free;
  		}
  
  		for (i = 0; i < nsems; i++) {
  			if (sem_io[i] > SEMVMX) {
9b24fef9f   Fabian Frederick   sysv, ipc: fix se...
1397
  				ipc_rcu_putref(sma, sem_rcu_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1398
1399
1400
1401
  				err = -ERANGE;
  				goto out_free;
  			}
  		}
4091fd942   Linus Torvalds   ipc: move the rcu...
1402
  		rcu_read_lock();
6ff379721   Pierre Peiffer   IPC/semaphores: c...
1403
  		sem_lock_and_putref(sma);
0f3d2b013   Rafael Aquini   ipc: introduce ip...
1404
  		if (!ipc_valid_object(&sma->sem_perm)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1405
  			err = -EIDRM;
6e224f945   Manfred Spraul   ipc/sem.c: synchr...
1406
  			goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1407
  		}
a5f4db877   Davidlohr Bueso   ipc/sem: make sem...
1408
  		for (i = 0; i < nsems; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1409
  			sma->sem_base[i].semval = sem_io[i];
a5f4db877   Davidlohr Bueso   ipc/sem: make sem...
1410
1411
  			sma->sem_base[i].sempid = task_tgid_vnr(current);
  		}
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1412

cf9d5d78d   Davidlohr Bueso   ipc: close open c...
1413
  		ipc_assert_locked_object(&sma->sem_perm);
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1414
  		list_for_each_entry(un, &sma->list_id, list_id) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1415
1416
  			for (i = 0; i < nsems; i++)
  				un->semadj[i] = 0;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1417
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1418
1419
  		sma->sem_ctime = get_seconds();
  		/* maybe some queued-up processes were waiting for this */
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
1420
  		do_smart_update(sma, NULL, 0, 0, &tasks);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1421
1422
1423
  		err = 0;
  		goto out_unlock;
  	}
e1fd1f490   Al Viro   get rid of union ...
1424
  	/* GETVAL, GETPID, GETNCTN, GETZCNT: fall-through */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1425
1426
  	}
  	err = -EINVAL;
c728b9c87   Linus Torvalds   ipc: simplify sem...
1427
1428
  	if (semnum < 0 || semnum >= nsems)
  		goto out_rcu_wakeup;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1429

6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1430
  	sem_lock(sma, NULL, -1);
0f3d2b013   Rafael Aquini   ipc: introduce ip...
1431
  	if (!ipc_valid_object(&sma->sem_perm)) {
6e224f945   Manfred Spraul   ipc/sem.c: synchr...
1432
1433
1434
  		err = -EIDRM;
  		goto out_unlock;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
  	curr = &sma->sem_base[semnum];
  
  	switch (cmd) {
  	case GETVAL:
  		err = curr->semval;
  		goto out_unlock;
  	case GETPID:
  		err = curr->sempid;
  		goto out_unlock;
  	case GETNCNT:
2f2ed41dc   Manfred Spraul   ipc/sem.c: remove...
1445
  		err = count_semcnt(sma, semnum, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1446
1447
  		goto out_unlock;
  	case GETZCNT:
2f2ed41dc   Manfred Spraul   ipc/sem.c: remove...
1448
  		err = count_semcnt(sma, semnum, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1449
  		goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1450
  	}
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
1451

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1452
  out_unlock:
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1453
  	sem_unlock(sma, -1);
c728b9c87   Linus Torvalds   ipc: simplify sem...
1454
  out_rcu_wakeup:
6d49dab8a   Linus Torvalds   ipc: move rcu_rea...
1455
  	rcu_read_unlock();
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
1456
  	wake_up_sem_queue_do(&tasks);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1457
  out_free:
239521f31   Manfred Spraul   ipc: whitespace c...
1458
  	if (sem_io != fast_sem_io)
1d5cfdb07   Tetsuo Handa   tree wide: use kv...
1459
  		ipc_free(sem_io);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1460
1461
  	return err;
  }
016d7132f   Pierre Peiffer   IPC: get rid of t...
1462
1463
  static inline unsigned long
  copy_semid_from_user(struct semid64_ds *out, void __user *buf, int version)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1464
  {
239521f31   Manfred Spraul   ipc: whitespace c...
1465
  	switch (version) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1466
  	case IPC_64:
016d7132f   Pierre Peiffer   IPC: get rid of t...
1467
  		if (copy_from_user(out, buf, sizeof(*out)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1468
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1469
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1470
1471
1472
  	case IPC_OLD:
  	    {
  		struct semid_ds tbuf_old;
239521f31   Manfred Spraul   ipc: whitespace c...
1473
  		if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1474
  			return -EFAULT;
016d7132f   Pierre Peiffer   IPC: get rid of t...
1475
1476
1477
  		out->sem_perm.uid	= tbuf_old.sem_perm.uid;
  		out->sem_perm.gid	= tbuf_old.sem_perm.gid;
  		out->sem_perm.mode	= tbuf_old.sem_perm.mode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1478
1479
1480
1481
1482
1483
1484
  
  		return 0;
  	    }
  	default:
  		return -EINVAL;
  	}
  }
522bb2a2b   Pierre Peiffer   IPC/semaphores: m...
1485
  /*
d9a605e40   Davidlohr Bueso   ipc: rename ids->...
1486
   * This function handles some semctl commands which require the rwsem
522bb2a2b   Pierre Peiffer   IPC/semaphores: m...
1487
   * to be held in write mode.
d9a605e40   Davidlohr Bueso   ipc: rename ids->...
1488
   * NOTE: no locks must be held, the rwsem is taken inside this function.
522bb2a2b   Pierre Peiffer   IPC/semaphores: m...
1489
   */
21a4826a7   Pierre Peiffer   IPC/semaphores: r...
1490
  static int semctl_down(struct ipc_namespace *ns, int semid,
e1fd1f490   Al Viro   get rid of union ...
1491
  		       int cmd, int version, void __user *p)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1492
1493
1494
  {
  	struct sem_array *sma;
  	int err;
016d7132f   Pierre Peiffer   IPC: get rid of t...
1495
  	struct semid64_ds semid64;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1496
  	struct kern_ipc_perm *ipcp;
239521f31   Manfred Spraul   ipc: whitespace c...
1497
  	if (cmd == IPC_SET) {
e1fd1f490   Al Viro   get rid of union ...
1498
  		if (copy_semid_from_user(&semid64, p, version))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1499
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1500
  	}
073115d6b   Steve Grubb   [PATCH] Rework of...
1501

d9a605e40   Davidlohr Bueso   ipc: rename ids->...
1502
  	down_write(&sem_ids(ns).rwsem);
7b4cc5d84   Davidlohr Bueso   ipc: move locking...
1503
  	rcu_read_lock();
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
1504
1505
  	ipcp = ipcctl_pre_down_nolock(ns, &sem_ids(ns), semid, cmd,
  				      &semid64.sem_perm, 0);
7b4cc5d84   Davidlohr Bueso   ipc: move locking...
1506
1507
  	if (IS_ERR(ipcp)) {
  		err = PTR_ERR(ipcp);
7b4cc5d84   Davidlohr Bueso   ipc: move locking...
1508
1509
  		goto out_unlock1;
  	}
073115d6b   Steve Grubb   [PATCH] Rework of...
1510

a5f75e7f2   Pierre Peiffer   IPC: consolidate ...
1511
  	sma = container_of(ipcp, struct sem_array, sem_perm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1512
1513
  
  	err = security_sem_semctl(sma, cmd);
7b4cc5d84   Davidlohr Bueso   ipc: move locking...
1514
1515
  	if (err)
  		goto out_unlock1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1516

7b4cc5d84   Davidlohr Bueso   ipc: move locking...
1517
  	switch (cmd) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1518
  	case IPC_RMID:
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1519
  		sem_lock(sma, NULL, -1);
7b4cc5d84   Davidlohr Bueso   ipc: move locking...
1520
  		/* freeary unlocks the ipc object and rcu */
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
1521
  		freeary(ns, ipcp);
522bb2a2b   Pierre Peiffer   IPC/semaphores: m...
1522
  		goto out_up;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1523
  	case IPC_SET:
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1524
  		sem_lock(sma, NULL, -1);
1efdb69b0   Eric W. Biederman   userns: Convert i...
1525
1526
  		err = ipc_update_perm(&semid64.sem_perm, ipcp);
  		if (err)
7b4cc5d84   Davidlohr Bueso   ipc: move locking...
1527
  			goto out_unlock0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1528
  		sma->sem_ctime = get_seconds();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1529
1530
  		break;
  	default:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1531
  		err = -EINVAL;
7b4cc5d84   Davidlohr Bueso   ipc: move locking...
1532
  		goto out_unlock1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1533
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1534

7b4cc5d84   Davidlohr Bueso   ipc: move locking...
1535
  out_unlock0:
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1536
  	sem_unlock(sma, -1);
7b4cc5d84   Davidlohr Bueso   ipc: move locking...
1537
  out_unlock1:
6d49dab8a   Linus Torvalds   ipc: move rcu_rea...
1538
  	rcu_read_unlock();
522bb2a2b   Pierre Peiffer   IPC/semaphores: m...
1539
  out_up:
d9a605e40   Davidlohr Bueso   ipc: rename ids->...
1540
  	up_write(&sem_ids(ns).rwsem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1541
1542
  	return err;
  }
e1fd1f490   Al Viro   get rid of union ...
1543
  SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1544
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1545
  	int version;
e38935341   Kirill Korotaev   [PATCH] IPC names...
1546
  	struct ipc_namespace *ns;
e1fd1f490   Al Viro   get rid of union ...
1547
  	void __user *p = (void __user *)arg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1548
1549
1550
1551
1552
  
  	if (semid < 0)
  		return -EINVAL;
  
  	version = ipc_parse_version(&cmd);
e38935341   Kirill Korotaev   [PATCH] IPC names...
1553
  	ns = current->nsproxy->ipc_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1554

239521f31   Manfred Spraul   ipc: whitespace c...
1555
  	switch (cmd) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1556
1557
  	case IPC_INFO:
  	case SEM_INFO:
4b9fcb0ec   Pierre Peiffer   IPC/semaphores: c...
1558
  	case IPC_STAT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1559
  	case SEM_STAT:
e1fd1f490   Al Viro   get rid of union ...
1560
  		return semctl_nolock(ns, semid, cmd, version, p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1561
1562
1563
1564
1565
  	case GETALL:
  	case GETVAL:
  	case GETPID:
  	case GETNCNT:
  	case GETZCNT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1566
  	case SETALL:
e1fd1f490   Al Viro   get rid of union ...
1567
1568
1569
  		return semctl_main(ns, semid, semnum, cmd, p);
  	case SETVAL:
  		return semctl_setval(ns, semid, semnum, arg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1570
1571
  	case IPC_RMID:
  	case IPC_SET:
e1fd1f490   Al Viro   get rid of union ...
1572
  		return semctl_down(ns, semid, cmd, version, p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1573
1574
1575
1576
  	default:
  		return -EINVAL;
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
  /* If the task doesn't already have a undo_list, then allocate one
   * here.  We guarantee there is only one thread using this undo list,
   * and current is THE ONE
   *
   * If this allocation and assignment succeeds, but later
   * portions of this code fail, there is no need to free the sem_undo_list.
   * Just let it stay associated with the task, and it'll be freed later
   * at exit time.
   *
   * This can block, so callers must hold no locks.
   */
  static inline int get_undo_list(struct sem_undo_list **undo_listp)
  {
  	struct sem_undo_list *undo_list;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1591
1592
1593
  
  	undo_list = current->sysvsem.undo_list;
  	if (!undo_list) {
2453a3062   Matt Helsley   [PATCH] ipc: repl...
1594
  		undo_list = kzalloc(sizeof(*undo_list), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1595
1596
  		if (undo_list == NULL)
  			return -ENOMEM;
00a5dfdb9   Ingo Molnar   [PATCH] Fix semun...
1597
  		spin_lock_init(&undo_list->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1598
  		atomic_set(&undo_list->refcnt, 1);
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1599
  		INIT_LIST_HEAD(&undo_list->list_proc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1600
1601
1602
1603
1604
  		current->sysvsem.undo_list = undo_list;
  	}
  	*undo_listp = undo_list;
  	return 0;
  }
bf17bb717   Nick Piggin   ipc/sem.c: sem op...
1605
  static struct sem_undo *__lookup_undo(struct sem_undo_list *ulp, int semid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1606
  {
bf17bb717   Nick Piggin   ipc/sem.c: sem op...
1607
  	struct sem_undo *un;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1608

bf17bb717   Nick Piggin   ipc/sem.c: sem op...
1609
1610
1611
  	list_for_each_entry_rcu(un, &ulp->list_proc, list_proc) {
  		if (un->semid == semid)
  			return un;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1612
  	}
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1613
  	return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1614
  }
bf17bb717   Nick Piggin   ipc/sem.c: sem op...
1615
1616
1617
  static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid)
  {
  	struct sem_undo *un;
239521f31   Manfred Spraul   ipc: whitespace c...
1618
  	assert_spin_locked(&ulp->lock);
bf17bb717   Nick Piggin   ipc/sem.c: sem op...
1619
1620
1621
1622
1623
1624
1625
1626
  
  	un = __lookup_undo(ulp, semid);
  	if (un) {
  		list_del_rcu(&un->list_proc);
  		list_add_rcu(&un->list_proc, &ulp->list_proc);
  	}
  	return un;
  }
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1627
  /**
8001c8581   Davidlohr Bueso   ipc: standardize ...
1628
   * find_alloc_undo - lookup (and if not present create) undo array
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1629
1630
1631
1632
1633
1634
   * @ns: namespace
   * @semid: semaphore array id
   *
   * The function looks up (and if not present creates) the undo structure.
   * The size of the undo structure depends on the size of the semaphore
   * array, thus the alloc path is not that straightforward.
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1635
1636
   * Lifetime-rules: sem_undo is rcu-protected, on success, the function
   * performs a rcu_read_lock().
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1637
1638
   */
  static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1639
1640
1641
1642
  {
  	struct sem_array *sma;
  	struct sem_undo_list *ulp;
  	struct sem_undo *un, *new;
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1643
  	int nsems, error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1644
1645
1646
1647
  
  	error = get_undo_list(&ulp);
  	if (error)
  		return ERR_PTR(error);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1648
  	rcu_read_lock();
c530c6ac7   Pierre Peiffer   IPC: cleanup some...
1649
  	spin_lock(&ulp->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1650
  	un = lookup_undo(ulp, semid);
c530c6ac7   Pierre Peiffer   IPC: cleanup some...
1651
  	spin_unlock(&ulp->lock);
239521f31   Manfred Spraul   ipc: whitespace c...
1652
  	if (likely(un != NULL))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1653
1654
1655
  		goto out;
  
  	/* no undo structure around - allocate one. */
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1656
  	/* step 1: figure out the size of the semaphore array */
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
1657
1658
1659
  	sma = sem_obtain_object_check(ns, semid);
  	if (IS_ERR(sma)) {
  		rcu_read_unlock();
4de85cd6d   Julia Lawall   ipc/sem.c: use ER...
1660
  		return ERR_CAST(sma);
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
1661
  	}
023a53557   Nadia Derbey   ipc: integrate ip...
1662

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1663
  	nsems = sma->sem_nsems;
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1664
1665
1666
1667
1668
  	if (!ipc_rcu_getref(sma)) {
  		rcu_read_unlock();
  		un = ERR_PTR(-EIDRM);
  		goto out;
  	}
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
1669
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1670

4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1671
  	/* step 2: allocate new undo structure */
4668edc33   Burman Yan   [PATCH] kernel co...
1672
  	new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1673
  	if (!new) {
9b24fef9f   Fabian Frederick   sysv, ipc: fix se...
1674
  		ipc_rcu_putref(sma, sem_rcu_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1675
1676
  		return ERR_PTR(-ENOMEM);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1677

380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1678
  	/* step 3: Acquire the lock on semaphore array */
4091fd942   Linus Torvalds   ipc: move the rcu...
1679
  	rcu_read_lock();
6ff379721   Pierre Peiffer   IPC/semaphores: c...
1680
  	sem_lock_and_putref(sma);
0f3d2b013   Rafael Aquini   ipc: introduce ip...
1681
  	if (!ipc_valid_object(&sma->sem_perm)) {
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1682
  		sem_unlock(sma, -1);
6d49dab8a   Linus Torvalds   ipc: move rcu_rea...
1683
  		rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1684
1685
1686
1687
  		kfree(new);
  		un = ERR_PTR(-EIDRM);
  		goto out;
  	}
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
  	spin_lock(&ulp->lock);
  
  	/*
  	 * step 4: check for races: did someone else allocate the undo struct?
  	 */
  	un = lookup_undo(ulp, semid);
  	if (un) {
  		kfree(new);
  		goto success;
  	}
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1698
1699
  	/* step 5: initialize & link new undo structure */
  	new->semadj = (short *) &new[1];
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1700
  	new->ulp = ulp;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1701
1702
  	new->semid = semid;
  	assert_spin_locked(&ulp->lock);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1703
  	list_add_rcu(&new->list_proc, &ulp->list_proc);
cf9d5d78d   Davidlohr Bueso   ipc: close open c...
1704
  	ipc_assert_locked_object(&sma->sem_perm);
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1705
  	list_add(&new->list_id, &sma->list_id);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1706
  	un = new;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1707

380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1708
  success:
c530c6ac7   Pierre Peiffer   IPC: cleanup some...
1709
  	spin_unlock(&ulp->lock);
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1710
  	sem_unlock(sma, -1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1711
1712
1713
  out:
  	return un;
  }
c61284e99   Manfred Spraul   ipc/sem.c: bugfix...
1714
1715
  
  /**
8001c8581   Davidlohr Bueso   ipc: standardize ...
1716
   * get_queue_result - retrieve the result code from sem_queue
c61284e99   Manfred Spraul   ipc/sem.c: bugfix...
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
   * @q: Pointer to queue structure
   *
   * Retrieve the return code from the pending queue. If IN_WAKEUP is found in
   * q->status, then we must loop until the value is replaced with the final
   * value: This may happen if a task is woken up by an unrelated event (e.g.
   * signal) and in parallel the task is woken up by another task because it got
   * the requested semaphores.
   *
   * The function can be called with or without holding the semaphore spinlock.
   */
  static int get_queue_result(struct sem_queue *q)
  {
  	int error;
  
  	error = q->status;
  	while (unlikely(error == IN_WAKEUP)) {
  		cpu_relax();
  		error = q->status;
  	}
  
  	return error;
  }
d5460c997   Heiko Carstens   [CVE-2009-0029] S...
1739
1740
  SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
  		unsigned, nsops, const struct timespec __user *, timeout)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1741
1742
1743
1744
  {
  	int error = -EINVAL;
  	struct sem_array *sma;
  	struct sembuf fast_sops[SEMOPM_FAST];
239521f31   Manfred Spraul   ipc: whitespace c...
1745
  	struct sembuf *sops = fast_sops, *sop;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1746
  	struct sem_undo *un;
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1747
  	int undos = 0, alter = 0, max, locknum;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1748
1749
  	struct sem_queue queue;
  	unsigned long jiffies_left = 0;
e38935341   Kirill Korotaev   [PATCH] IPC names...
1750
  	struct ipc_namespace *ns;
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
1751
  	struct list_head tasks;
e38935341   Kirill Korotaev   [PATCH] IPC names...
1752
1753
  
  	ns = current->nsproxy->ipc_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1754
1755
1756
  
  	if (nsops < 1 || semid < 0)
  		return -EINVAL;
e38935341   Kirill Korotaev   [PATCH] IPC names...
1757
  	if (nsops > ns->sc_semopm)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1758
  		return -E2BIG;
239521f31   Manfred Spraul   ipc: whitespace c...
1759
1760
1761
  	if (nsops > SEMOPM_FAST) {
  		sops = kmalloc(sizeof(*sops)*nsops, GFP_KERNEL);
  		if (sops == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1762
1763
  			return -ENOMEM;
  	}
239521f31   Manfred Spraul   ipc: whitespace c...
1764
1765
  	if (copy_from_user(sops, tsops, nsops * sizeof(*tsops))) {
  		error =  -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
  		goto out_free;
  	}
  	if (timeout) {
  		struct timespec _timeout;
  		if (copy_from_user(&_timeout, timeout, sizeof(*timeout))) {
  			error = -EFAULT;
  			goto out_free;
  		}
  		if (_timeout.tv_sec < 0 || _timeout.tv_nsec < 0 ||
  			_timeout.tv_nsec >= 1000000000L) {
  			error = -EINVAL;
  			goto out_free;
  		}
  		jiffies_left = timespec_to_jiffies(&_timeout);
  	}
  	max = 0;
  	for (sop = sops; sop < sops + nsops; sop++) {
  		if (sop->sem_num >= max)
  			max = sop->sem_num;
  		if (sop->sem_flg & SEM_UNDO)
b78755abc   Manfred Spraul   [PATCH] ipcsem: r...
1786
1787
  			undos = 1;
  		if (sop->sem_op != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1788
1789
  			alter = 1;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1790

6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1791
  	INIT_LIST_HEAD(&tasks);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1792
  	if (undos) {
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1793
  		/* On success, find_alloc_undo takes the rcu_read_lock */
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1794
  		un = find_alloc_undo(ns, semid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1795
1796
1797
1798
  		if (IS_ERR(un)) {
  			error = PTR_ERR(un);
  			goto out_free;
  		}
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1799
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1800
  		un = NULL;
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1801
1802
  		rcu_read_lock();
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1803

16df3674e   Davidlohr Bueso   ipc,sem: do not h...
1804
  	sma = sem_obtain_object_check(ns, semid);
023a53557   Nadia Derbey   ipc: integrate ip...
1805
  	if (IS_ERR(sma)) {
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1806
  		rcu_read_unlock();
023a53557   Nadia Derbey   ipc: integrate ip...
1807
  		error = PTR_ERR(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1808
  		goto out_free;
023a53557   Nadia Derbey   ipc: integrate ip...
1809
  	}
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
1810
  	error = -EFBIG;
c728b9c87   Linus Torvalds   ipc: simplify sem...
1811
1812
  	if (max >= sma->sem_nsems)
  		goto out_rcu_wakeup;
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
1813
1814
  
  	error = -EACCES;
c728b9c87   Linus Torvalds   ipc: simplify sem...
1815
1816
  	if (ipcperms(ns, &sma->sem_perm, alter ? S_IWUGO : S_IRUGO))
  		goto out_rcu_wakeup;
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
1817
1818
  
  	error = security_sem_semop(sma, sops, nsops, alter);
c728b9c87   Linus Torvalds   ipc: simplify sem...
1819
1820
  	if (error)
  		goto out_rcu_wakeup;
16df3674e   Davidlohr Bueso   ipc,sem: do not h...
1821

6e224f945   Manfred Spraul   ipc/sem.c: synchr...
1822
1823
  	error = -EIDRM;
  	locknum = sem_lock(sma, sops, nsops);
0f3d2b013   Rafael Aquini   ipc: introduce ip...
1824
1825
1826
1827
1828
1829
1830
1831
1832
  	/*
  	 * We eventually might perform the following check in a lockless
  	 * fashion, considering ipc_valid_object() locking constraints.
  	 * If nsops == 1 and there is no contention for sem_perm.lock, then
  	 * only a per-semaphore lock is held and it's OK to proceed with the
  	 * check below. More details on the fine grained locking scheme
  	 * entangled here and why it's RMID race safe on comments at sem_lock()
  	 */
  	if (!ipc_valid_object(&sma->sem_perm))
6e224f945   Manfred Spraul   ipc/sem.c: synchr...
1833
  		goto out_unlock_free;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1834
  	/*
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1835
  	 * semid identifiers are not unique - find_alloc_undo may have
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1836
  	 * allocated an undo structure, it was invalidated by an RMID
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1837
  	 * and now a new array with received the same id. Check and fail.
25985edce   Lucas De Marchi   Fix common misspe...
1838
  	 * This case can be detected checking un->semid. The existence of
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1839
  	 * "un" itself is guaranteed by rcu.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1840
  	 */
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1841
1842
  	if (un && un->semid == -1)
  		goto out_unlock_free;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1843

d198cd6d6   Manfred Spraul   ipc/sem.c: change...
1844
1845
1846
1847
1848
1849
1850
  	queue.sops = sops;
  	queue.nsops = nsops;
  	queue.undo = un;
  	queue.pid = task_tgid_vnr(current);
  	queue.alter = alter;
  
  	error = perform_atomic_semop(sma, &queue);
0e8c66569   Manfred Spraul   ipc/sem.c: update...
1851
1852
1853
1854
1855
  	if (error == 0) {
  		/* If the operation was successful, then do
  		 * the required updates.
  		 */
  		if (alter)
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
1856
  			do_smart_update(sma, sops, nsops, 1, &tasks);
0e8c66569   Manfred Spraul   ipc/sem.c: update...
1857
1858
  		else
  			set_semotime(sma, sops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1859
  	}
0e8c66569   Manfred Spraul   ipc/sem.c: update...
1860
1861
  	if (error <= 0)
  		goto out_unlock_free;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1862
1863
1864
1865
  
  	/* We need to sleep on this operation, so we put the current
  	 * task into the pending queue and go to sleep.
  	 */
46c0a8ca3   Paul McQuade   ipc, kernel: clea...
1866

b97e820ff   Manfred Spraul   ipc/sem.c: add a ...
1867
1868
1869
  	if (nsops == 1) {
  		struct sem *curr;
  		curr = &sma->sem_base[sops->sem_num];
f269f40ad   Manfred Spraul   ipc/sem.c: always...
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
  		if (alter) {
  			if (sma->complex_count) {
  				list_add_tail(&queue.list,
  						&sma->pending_alter);
  			} else {
  
  				list_add_tail(&queue.list,
  						&curr->pending_alter);
  			}
  		} else {
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
1880
  			list_add_tail(&queue.list, &curr->pending_const);
f269f40ad   Manfred Spraul   ipc/sem.c: always...
1881
  		}
b97e820ff   Manfred Spraul   ipc/sem.c: add a ...
1882
  	} else {
f269f40ad   Manfred Spraul   ipc/sem.c: always...
1883
1884
  		if (!sma->complex_count)
  			merge_queues(sma);
9f1bc2c90   Rik van Riel   ipc,sem: have onl...
1885
  		if (alter)
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
1886
  			list_add_tail(&queue.list, &sma->pending_alter);
9f1bc2c90   Rik van Riel   ipc,sem: have onl...
1887
  		else
1a82e9e1d   Manfred Spraul   ipc/sem: separate...
1888
  			list_add_tail(&queue.list, &sma->pending_const);
b97e820ff   Manfred Spraul   ipc/sem.c: add a ...
1889
1890
  		sma->complex_count++;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1891
1892
  	queue.status = -EINTR;
  	queue.sleeper = current;
0b0577f60   Manfred Spraul   ipc/sem.c: handle...
1893
1894
  
  sleep_again:
52644c9ab   Davidlohr Bueso   ipc,sem: use curr...
1895
  	__set_current_state(TASK_INTERRUPTIBLE);
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1896
  	sem_unlock(sma, locknum);
6d49dab8a   Linus Torvalds   ipc: move rcu_rea...
1897
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1898
1899
1900
1901
1902
  
  	if (timeout)
  		jiffies_left = schedule_timeout(jiffies_left);
  	else
  		schedule();
c61284e99   Manfred Spraul   ipc/sem.c: bugfix...
1903
  	error = get_queue_result(&queue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1904
1905
1906
  
  	if (error != -EINTR) {
  		/* fast path: update_queue already obtained all requested
c61284e99   Manfred Spraul   ipc/sem.c: bugfix...
1907
1908
1909
1910
1911
1912
1913
  		 * resources.
  		 * Perform a smp_mb(): User space could assume that semop()
  		 * is a memory barrier: Without the mb(), the cpu could
  		 * speculatively read in user space stale data that was
  		 * overwritten by the previous owner of the semaphore.
  		 */
  		smp_mb();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1914
1915
  		goto out_free;
  	}
321310ced   Linus Torvalds   ipc: move sem_obt...
1916
  	rcu_read_lock();
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1917
  	sma = sem_obtain_lock(ns, semid, sops, nsops, &locknum);
d694ad62b   Manfred Spraul   ipc/sem.c: fix ra...
1918
1919
1920
1921
1922
1923
1924
1925
1926
  
  	/*
  	 * Wait until it's guaranteed that no wakeup_sem_queue_do() is ongoing.
  	 */
  	error = get_queue_result(&queue);
  
  	/*
  	 * Array removed? If yes, leave without sem_unlock().
  	 */
023a53557   Nadia Derbey   ipc: integrate ip...
1927
  	if (IS_ERR(sma)) {
321310ced   Linus Torvalds   ipc: move sem_obt...
1928
  		rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1929
1930
  		goto out_free;
  	}
c61284e99   Manfred Spraul   ipc/sem.c: bugfix...
1931

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1932
  	/*
d694ad62b   Manfred Spraul   ipc/sem.c: fix ra...
1933
1934
  	 * If queue.status != -EINTR we are woken up by another process.
  	 * Leave without unlink_queue(), but with sem_unlock().
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1935
  	 */
3ab08fe20   Davidlohr Bueso   ipc: remove brace...
1936
  	if (error != -EINTR)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1937
  		goto out_unlock_free;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1938
1939
1940
1941
1942
1943
  
  	/*
  	 * If an interrupt occurred we have to clean up the queue
  	 */
  	if (timeout && jiffies_left == 0)
  		error = -EAGAIN;
0b0577f60   Manfred Spraul   ipc/sem.c: handle...
1944
1945
1946
1947
1948
1949
  
  	/*
  	 * If the wakeup was spurious, just retry
  	 */
  	if (error == -EINTR && !signal_pending(current))
  		goto sleep_again;
b97e820ff   Manfred Spraul   ipc/sem.c: add a ...
1950
  	unlink_queue(sma, &queue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1951
1952
  
  out_unlock_free:
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
1953
  	sem_unlock(sma, locknum);
c728b9c87   Linus Torvalds   ipc: simplify sem...
1954
  out_rcu_wakeup:
6d49dab8a   Linus Torvalds   ipc: move rcu_rea...
1955
  	rcu_read_unlock();
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
1956
  	wake_up_sem_queue_do(&tasks);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1957
  out_free:
239521f31   Manfred Spraul   ipc: whitespace c...
1958
  	if (sops != fast_sops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1959
1960
1961
  		kfree(sops);
  	return error;
  }
d5460c997   Heiko Carstens   [CVE-2009-0029] S...
1962
1963
  SYSCALL_DEFINE3(semop, int, semid, struct sembuf __user *, tsops,
  		unsigned, nsops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1964
1965
1966
1967
1968
1969
  {
  	return sys_semtimedop(semid, tsops, nsops, NULL);
  }
  
  /* If CLONE_SYSVSEM is set, establish sharing of SEM_UNDO state between
   * parent and child tasks.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
   */
  
  int copy_semundo(unsigned long clone_flags, struct task_struct *tsk)
  {
  	struct sem_undo_list *undo_list;
  	int error;
  
  	if (clone_flags & CLONE_SYSVSEM) {
  		error = get_undo_list(&undo_list);
  		if (error)
  			return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1981
1982
  		atomic_inc(&undo_list->refcnt);
  		tsk->sysvsem.undo_list = undo_list;
46c0a8ca3   Paul McQuade   ipc, kernel: clea...
1983
  	} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
  		tsk->sysvsem.undo_list = NULL;
  
  	return 0;
  }
  
  /*
   * add semadj values to semaphores, free undo structures.
   * undo structures are not freed when semaphore arrays are destroyed
   * so some of them may be out of date.
   * IMPLEMENTATION NOTE: There is some confusion over whether the
   * set of adjustments that needs to be done should be done in an atomic
   * manner or not. That is, if we are attempting to decrement the semval
   * should we queue up and wait until we can do so legally?
   * The original implementation attempted to do this (queue and wait).
   * The current implementation does not do so. The POSIX standard
   * and SVID should be consulted to determine what behavior is mandated.
   */
  void exit_sem(struct task_struct *tsk)
  {
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
2003
  	struct sem_undo_list *ulp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2004

4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
2005
2006
  	ulp = tsk->sysvsem.undo_list;
  	if (!ulp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2007
  		return;
9edff4ab1   Manfred Spraul   ipc: sysvsem: imp...
2008
  	tsk->sysvsem.undo_list = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2009

4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
2010
  	if (!atomic_dec_and_test(&ulp->refcnt))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2011
  		return;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
2012
  	for (;;) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2013
  		struct sem_array *sma;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
2014
  		struct sem_undo *un;
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
2015
  		struct list_head tasks;
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
2016
  		int semid, i;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
2017

2a1613a58   Nikolay Borisov   ipc/sem.c: add co...
2018
  		cond_resched();
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
2019
  		rcu_read_lock();
05725f7eb   Jiri Pirko   rculist: use list...
2020
2021
  		un = list_entry_rcu(ulp->list_proc.next,
  				    struct sem_undo, list_proc);
602b8593d   Herton R. Krzesinski   ipc,sem: fix use ...
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
  		if (&un->list_proc == &ulp->list_proc) {
  			/*
  			 * We must wait for freeary() before freeing this ulp,
  			 * in case we raced with last sem_undo. There is a small
  			 * possibility where we exit while freeary() didn't
  			 * finish unlocking sem_undo_list.
  			 */
  			spin_unlock_wait(&ulp->lock);
  			rcu_read_unlock();
  			break;
  		}
  		spin_lock(&ulp->lock);
  		semid = un->semid;
  		spin_unlock(&ulp->lock);
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
2036

602b8593d   Herton R. Krzesinski   ipc,sem: fix use ...
2037
  		/* exit_sem raced with IPC_RMID, nothing to do */
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
2038
2039
  		if (semid == -1) {
  			rcu_read_unlock();
602b8593d   Herton R. Krzesinski   ipc,sem: fix use ...
2040
  			continue;
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
2041
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2042

602b8593d   Herton R. Krzesinski   ipc,sem: fix use ...
2043
  		sma = sem_obtain_object_check(tsk->nsproxy->ipc_ns, semid);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
2044
  		/* exit_sem raced with IPC_RMID, nothing to do */
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
2045
2046
  		if (IS_ERR(sma)) {
  			rcu_read_unlock();
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
2047
  			continue;
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
2048
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2049

6062a8dc0   Rik van Riel   ipc,sem: fine gra...
2050
  		sem_lock(sma, NULL, -1);
6e224f945   Manfred Spraul   ipc/sem.c: synchr...
2051
  		/* exit_sem raced with IPC_RMID, nothing to do */
0f3d2b013   Rafael Aquini   ipc: introduce ip...
2052
  		if (!ipc_valid_object(&sma->sem_perm)) {
6e224f945   Manfred Spraul   ipc/sem.c: synchr...
2053
2054
2055
2056
  			sem_unlock(sma, -1);
  			rcu_read_unlock();
  			continue;
  		}
bf17bb717   Nick Piggin   ipc/sem.c: sem op...
2057
  		un = __lookup_undo(ulp, semid);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
2058
2059
2060
2061
  		if (un == NULL) {
  			/* exit_sem raced with IPC_RMID+semget() that created
  			 * exactly the same semid. Nothing to do.
  			 */
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
2062
  			sem_unlock(sma, -1);
6d49dab8a   Linus Torvalds   ipc: move rcu_rea...
2063
  			rcu_read_unlock();
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
2064
2065
2066
2067
  			continue;
  		}
  
  		/* remove un from the linked lists */
cf9d5d78d   Davidlohr Bueso   ipc: close open c...
2068
  		ipc_assert_locked_object(&sma->sem_perm);
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
2069
  		list_del(&un->list_id);
a97955844   Herton R. Krzesinski   ipc,sem: remove u...
2070
2071
2072
2073
  		/* we are the last process using this ulp, acquiring ulp->lock
  		 * isn't required. Besides that, we are also protected against
  		 * IPC_RMID as we hold sma->sem_perm lock now
  		 */
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
2074
  		list_del_rcu(&un->list_proc);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
2075

4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
2076
2077
  		/* perform adjustments registered in un */
  		for (i = 0; i < sma->sem_nsems; i++) {
239521f31   Manfred Spraul   ipc: whitespace c...
2078
  			struct sem *semaphore = &sma->sem_base[i];
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
2079
2080
  			if (un->semadj[i]) {
  				semaphore->semval += un->semadj[i];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
  				/*
  				 * Range checks of the new semaphore value,
  				 * not defined by sus:
  				 * - Some unices ignore the undo entirely
  				 *   (e.g. HP UX 11i 11.22, Tru64 V5.1)
  				 * - some cap the value (e.g. FreeBSD caps
  				 *   at 0, but doesn't enforce SEMVMX)
  				 *
  				 * Linux caps the semaphore value, both at 0
  				 * and at SEMVMX.
  				 *
239521f31   Manfred Spraul   ipc: whitespace c...
2092
  				 *	Manfred <manfred@colorfullife.com>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2093
  				 */
5f921ae96   Ingo Molnar   [PATCH] sem2mutex...
2094
2095
2096
2097
  				if (semaphore->semval < 0)
  					semaphore->semval = 0;
  				if (semaphore->semval > SEMVMX)
  					semaphore->semval = SEMVMX;
b488893a3   Pavel Emelyanov   pid namespaces: c...
2098
  				semaphore->sempid = task_tgid_vnr(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2099
2100
  			}
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2101
  		/* maybe some queued-up processes were waiting for this */
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
2102
2103
  		INIT_LIST_HEAD(&tasks);
  		do_smart_update(sma, NULL, 0, 1, &tasks);
6062a8dc0   Rik van Riel   ipc,sem: fine gra...
2104
  		sem_unlock(sma, -1);
6d49dab8a   Linus Torvalds   ipc: move rcu_rea...
2105
  		rcu_read_unlock();
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
2106
  		wake_up_sem_queue_do(&tasks);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
2107

693a8b6ee   Lai Jiangshan   ipc,rcu: Convert ...
2108
  		kfree_rcu(un, rcu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2109
  	}
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
2110
  	kfree(ulp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2111
2112
2113
  }
  
  #ifdef CONFIG_PROC_FS
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
2114
  static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2115
  {
1efdb69b0   Eric W. Biederman   userns: Convert i...
2116
  	struct user_namespace *user_ns = seq_user_ns(s);
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
2117
  	struct sem_array *sma = it;
d12e1e50e   Manfred Spraul   ipc/sem.c: replac...
2118
  	time_t sem_otime;
d8c633766   Manfred Spraul   ipc/sem.c: synchr...
2119
2120
2121
  	/*
  	 * The proc interface isn't aware of sem_lock(), it calls
  	 * ipc_lock_object() directly (in sysvipc_find_ipc).
5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
2122
2123
  	 * In order to stay compatible with sem_lock(), we must
  	 * enter / leave complex_mode.
d8c633766   Manfred Spraul   ipc/sem.c: synchr...
2124
  	 */
5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
2125
  	complexmode_enter(sma);
d8c633766   Manfred Spraul   ipc/sem.c: synchr...
2126

d12e1e50e   Manfred Spraul   ipc/sem.c: replac...
2127
  	sem_otime = get_semotime(sma);
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
2128

7f032d6ef   Joe Perches   ipc: remove use o...
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
  	seq_printf(s,
  		   "%10d %10d  %4o %10u %5u %5u %5u %5u %10lu %10lu
  ",
  		   sma->sem_perm.key,
  		   sma->sem_perm.id,
  		   sma->sem_perm.mode,
  		   sma->sem_nsems,
  		   from_kuid_munged(user_ns, sma->sem_perm.uid),
  		   from_kgid_munged(user_ns, sma->sem_perm.gid),
  		   from_kuid_munged(user_ns, sma->sem_perm.cuid),
  		   from_kgid_munged(user_ns, sma->sem_perm.cgid),
  		   sem_otime,
  		   sma->sem_ctime);
5864a2fd3   Manfred Spraul   ipc/sem.c: fix co...
2142
  	complexmode_tryleave(sma);
7f032d6ef   Joe Perches   ipc: remove use o...
2143
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2144
2145
  }
  #endif