Blame view

ipc/sem.c 33.9 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
50
51
52
53
54
55
56
57
58
  /*
   * linux/ipc/sem.c
   * Copyright (C) 1992 Krishna Balasubramanian
   * Copyright (C) 1995 Eric Schenk, Bruno Haible
   *
   * IMPLEMENTATION NOTES ON CODE REWRITE (Eric Schenk, January 1995):
   * This code underwent a massive rewrite in order to solve some problems
   * with the original code. In particular the original code failed to
   * wake up processes that were waiting for semval to go to 0 if the
   * value went to 0 and was then incremented rapidly enough. In solving
   * this problem I have also modified the implementation so that it
   * processes pending operations in a FIFO manner, thus give a guarantee
   * that processes waiting for a lock on the semaphore won't starve
   * unless another locking process fails to unlock.
   * In addition the following two changes in behavior have been introduced:
   * - The original implementation of semop returned the value
   *   last semaphore element examined on success. This does not
   *   match the manual page specifications, and effectively
   *   allows the user to read the semaphore even if they do not
   *   have read permissions. The implementation now returns 0
   *   on success as stated in the manual page.
   * - There is some confusion over whether the set of undo adjustments
   *   to be performed at exit should be done in an atomic manner.
   *   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.
   *   The current implementation does not do so. This is because I don't
   *   think it is the right thing (TM) to do, and because I couldn't
   *   see a clean way to get the old behavior with the new design.
   *   The POSIX standard and SVID should be consulted to determine
   *   what behavior is mandated.
   *
   * Further notes on refinement (Christoph Rohland, December 1998):
   * - The POSIX standard says, that the undo adjustments simply should
   *   redo. So the current implementation is o.K.
   * - The previous code had two flaws:
   *   1) It actively gave the semaphore to the next waiting process
   *      sleeping on the semaphore. Since this process did not have the
   *      cpu this led to many unnecessary context switches and bad
   *      performance. Now we only check which process should be able to
   *      get the semaphore and if this process wants to reduce some
   *      semaphore value we simply wake it up without doing the
   *      operation. So it has to try to get it later. Thus e.g. the
   *      running process may reacquire the semaphore during the current
   *      time slice. If it only waits for zero or increases the semaphore,
   *      we do the operation in advance and wake it up.
   *   2) It did not wake up all zero waiting processes. We try to do
   *      better but only get the semops right which only wait for zero or
   *      increase. If there are decrement operations in the operations
   *      array we do the same as before.
   *
   * With the incarnation of O(1) scheduler, it becomes unnecessary to perform
   * check/retry algorithm for waking up blocked processes as the new scheduler
   * is better at handling thread switch than the old one.
   *
   * /proc/sysvipc/sem support (c) 1999 Dragos Acostachioaie <dragos@iname.com>
   *
   * SMP-threaded, sysctl's added
624dffcbc   Christian Kujau   correct email add...
59
   * (c) 1999 Manfred Spraul <manfred@colorfullife.com>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
   * Enforced range limit on SEM_UNDO
046c68842   Alan Cox   mm: update my add...
61
   * (c) 2001 Red Hat Inc
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
   * Lockless wakeup
   * (c) 2003 Manfred Spraul <manfred@colorfullife.com>
073115d6b   Steve Grubb   [PATCH] Rework of...
64
65
66
   *
   * support for audit of ipc object properties and permission changes
   * Dustin Kirkland <dustin.kirkland@us.ibm.com>
e38935341   Kirill Korotaev   [PATCH] IPC names...
67
68
69
70
   *
   * namespaces support
   * OpenVZ, SWsoft Inc.
   * Pavel Emelianov <xemul@openvz.org>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
73
74
75
76
  #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
77
78
79
  #include <linux/security.h>
  #include <linux/syscalls.h>
  #include <linux/audit.h>
c59ede7b7   Randy.Dunlap   [PATCH] move capa...
80
  #include <linux/capability.h>
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
81
  #include <linux/seq_file.h>
3e148c799   Nadia Derbey   fix idr_find() lo...
82
  #include <linux/rwsem.h>
e38935341   Kirill Korotaev   [PATCH] IPC names...
83
  #include <linux/nsproxy.h>
ae5e1b22f   Pavel Emelyanov   namespaces: move ...
84
  #include <linux/ipc_namespace.h>
5f921ae96   Ingo Molnar   [PATCH] sem2mutex...
85

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
87
  #include <asm/uaccess.h>
  #include "util.h"
ed2ddbf88   Pierre Peiffer   IPC: make struct ...
88
  #define sem_ids(ns)	((ns)->ids[IPC_SEM_IDS])
e38935341   Kirill Korotaev   [PATCH] IPC names...
89

e38935341   Kirill Korotaev   [PATCH] IPC names...
90
  #define sem_unlock(sma)		ipc_unlock(&(sma)->sem_perm)
1b531f213   Nadia Derbey   ipc: remove unnee...
91
  #define sem_checkid(sma, semid)	ipc_checkid(&sma->sem_perm, semid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92

7748dbfaa   Nadia Derbey   ipc: unify the sy...
93
  static int newary(struct ipc_namespace *, struct ipc_params *);
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
94
  static void freeary(struct ipc_namespace *, struct kern_ipc_perm *);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
  #ifdef CONFIG_PROC_FS
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
96
  static int sysvipc_sem_proc_show(struct seq_file *s, void *it);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
98
99
100
101
102
103
104
105
106
107
108
109
  #endif
  
  #define SEMMSL_FAST	256 /* 512 bytes on stack */
  #define SEMOPM_FAST	64  /* ~ 372 bytes on stack */
  
  /*
   * linked list protection:
   *	sem_undo.id_next,
   *	sem_array.sem_pending{,last},
   *	sem_array.sem_undo: sem_lock() for read/write
   *	sem_undo.proc_next: only "current" is allowed to read/write that field.
   *	
   */
e38935341   Kirill Korotaev   [PATCH] IPC names...
110
111
112
113
  #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 ...
114
  void sem_init_ns(struct ipc_namespace *ns)
e38935341   Kirill Korotaev   [PATCH] IPC names...
115
  {
e38935341   Kirill Korotaev   [PATCH] IPC names...
116
117
118
119
120
  	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 ...
121
  	ipc_init_ids(&ns->ids[IPC_SEM_IDS]);
e38935341   Kirill Korotaev   [PATCH] IPC names...
122
  }
ae5e1b22f   Pavel Emelyanov   namespaces: move ...
123
  #ifdef CONFIG_IPC_NS
e38935341   Kirill Korotaev   [PATCH] IPC names...
124
125
  void sem_exit_ns(struct ipc_namespace *ns)
  {
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
126
  	free_ipcs(ns, &sem_ids(ns), freeary);
e38935341   Kirill Korotaev   [PATCH] IPC names...
127
  }
ae5e1b22f   Pavel Emelyanov   namespaces: move ...
128
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
131
  
  void __init sem_init (void)
  {
ed2ddbf88   Pierre Peiffer   IPC: make struct ...
132
  	sem_init_ns(&init_ipc_ns);
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
133
134
135
  	ipc_init_proc_interface("sysvipc/sem",
  				"       key      semid perms      nsems   uid   gid  cuid  cgid      otime      ctime
  ",
e38935341   Kirill Korotaev   [PATCH] IPC names...
136
  				IPC_SEM_IDS, sysvipc_sem_proc_show);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
  }
3e148c799   Nadia Derbey   fix idr_find() lo...
138
  /*
3e148c799   Nadia Derbey   fix idr_find() lo...
139
140
141
   * sem_lock_(check_) routines are called in the paths where the rw_mutex
   * is not held.
   */
023a53557   Nadia Derbey   ipc: integrate ip...
142
143
  static inline struct sem_array *sem_lock(struct ipc_namespace *ns, int id)
  {
03f02c765   Nadia Derbey   Storing ipcs into...
144
  	struct kern_ipc_perm *ipcp = ipc_lock(&sem_ids(ns), id);
b1ed88b47   Pierre Peiffer   IPC: fix error ch...
145
146
  	if (IS_ERR(ipcp))
  		return (struct sem_array *)ipcp;
03f02c765   Nadia Derbey   Storing ipcs into...
147
  	return container_of(ipcp, struct sem_array, sem_perm);
023a53557   Nadia Derbey   ipc: integrate ip...
148
149
150
151
152
  }
  
  static inline struct sem_array *sem_lock_check(struct ipc_namespace *ns,
  						int id)
  {
03f02c765   Nadia Derbey   Storing ipcs into...
153
  	struct kern_ipc_perm *ipcp = ipc_lock_check(&sem_ids(ns), id);
b1ed88b47   Pierre Peiffer   IPC: fix error ch...
154
155
  	if (IS_ERR(ipcp))
  		return (struct sem_array *)ipcp;
03f02c765   Nadia Derbey   Storing ipcs into...
156
  	return container_of(ipcp, struct sem_array, sem_perm);
023a53557   Nadia Derbey   ipc: integrate ip...
157
  }
6ff379721   Pierre Peiffer   IPC/semaphores: c...
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
  static inline void sem_lock_and_putref(struct sem_array *sma)
  {
  	ipc_lock_by_ptr(&sma->sem_perm);
  	ipc_rcu_putref(sma);
  }
  
  static inline void sem_getref_and_unlock(struct sem_array *sma)
  {
  	ipc_rcu_getref(sma);
  	ipc_unlock(&(sma)->sem_perm);
  }
  
  static inline void sem_putref(struct sem_array *sma)
  {
  	ipc_lock_by_ptr(&sma->sem_perm);
  	ipc_rcu_putref(sma);
  	ipc_unlock(&(sma)->sem_perm);
  }
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
176
177
178
179
  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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
  /*
   * 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
   *	* unlinking the queue entry from sma->sem_pending
   *	* 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:
   *   	* 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
5f921ae96   Ingo Molnar   [PATCH] sem2mutex...
195
   *   	  performing any operation on the sem array.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
   *   	* otherwise it must acquire the spinlock and check what's up.
   *
   * 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...
213
214
215
216
217
  /**
   * newary - Create a new semaphore set
   * @ns: namespace
   * @params: ptr to the structure that contains key, semflg and nsems
   *
3e148c799   Nadia Derbey   fix idr_find() lo...
218
   * Called with sem_ids.rw_mutex held (as a writer)
f4566f048   Nadia Derbey   ipc: fix wrong co...
219
   */
7748dbfaa   Nadia Derbey   ipc: unify the sy...
220
  static int newary(struct ipc_namespace *ns, struct ipc_params *params)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
222
223
224
225
  {
  	int id;
  	int retval;
  	struct sem_array *sma;
  	int size;
7748dbfaa   Nadia Derbey   ipc: unify the sy...
226
227
228
  	key_t key = params->key;
  	int nsems = params->u.nsems;
  	int semflg = params->flg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
231
  
  	if (!nsems)
  		return -EINVAL;
e38935341   Kirill Korotaev   [PATCH] IPC names...
232
  	if (ns->used_sems + nsems > ns->sc_semmns)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
  		return -ENOSPC;
  
  	size = sizeof (*sma) + nsems * sizeof (struct sem);
  	sma = ipc_rcu_alloc(size);
  	if (!sma) {
  		return -ENOMEM;
  	}
  	memset (sma, 0, size);
  
  	sma->sem_perm.mode = (semflg & S_IRWXUGO);
  	sma->sem_perm.key = key;
  
  	sma->sem_perm.security = NULL;
  	retval = security_sem_alloc(sma);
  	if (retval) {
  		ipc_rcu_putref(sma);
  		return retval;
  	}
e38935341   Kirill Korotaev   [PATCH] IPC names...
251
  	id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni);
283bb7fad   Pierre Peiffer   IPC: fix error ca...
252
  	if (id < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
254
  		security_sem_free(sma);
  		ipc_rcu_putref(sma);
283bb7fad   Pierre Peiffer   IPC: fix error ca...
255
  		return id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
  	}
e38935341   Kirill Korotaev   [PATCH] IPC names...
257
  	ns->used_sems += nsems;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
259
  
  	sma->sem_base = (struct sem *) &sma[1];
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
260
  	INIT_LIST_HEAD(&sma->sem_pending);
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
261
  	INIT_LIST_HEAD(&sma->list_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
262
263
264
  	sma->sem_nsems = nsems;
  	sma->sem_ctime = get_seconds();
  	sem_unlock(sma);
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
265
  	return sma->sem_perm.id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266
  }
7748dbfaa   Nadia Derbey   ipc: unify the sy...
267

f4566f048   Nadia Derbey   ipc: fix wrong co...
268
  /*
3e148c799   Nadia Derbey   fix idr_find() lo...
269
   * Called with sem_ids.rw_mutex and ipcp locked.
f4566f048   Nadia Derbey   ipc: fix wrong co...
270
   */
03f02c765   Nadia Derbey   Storing ipcs into...
271
  static inline int sem_security(struct kern_ipc_perm *ipcp, int semflg)
7748dbfaa   Nadia Derbey   ipc: unify the sy...
272
  {
03f02c765   Nadia Derbey   Storing ipcs into...
273
274
275
276
  	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...
277
  }
f4566f048   Nadia Derbey   ipc: fix wrong co...
278
  /*
3e148c799   Nadia Derbey   fix idr_find() lo...
279
   * Called with sem_ids.rw_mutex and ipcp locked.
f4566f048   Nadia Derbey   ipc: fix wrong co...
280
   */
03f02c765   Nadia Derbey   Storing ipcs into...
281
282
  static inline int sem_more_checks(struct kern_ipc_perm *ipcp,
  				struct ipc_params *params)
7748dbfaa   Nadia Derbey   ipc: unify the sy...
283
  {
03f02c765   Nadia Derbey   Storing ipcs into...
284
285
286
287
  	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...
288
289
290
291
  		return -EINVAL;
  
  	return 0;
  }
d5460c997   Heiko Carstens   [CVE-2009-0029] S...
292
  SYSCALL_DEFINE3(semget, key_t, key, int, nsems, int, semflg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
  {
e38935341   Kirill Korotaev   [PATCH] IPC names...
294
  	struct ipc_namespace *ns;
7748dbfaa   Nadia Derbey   ipc: unify the sy...
295
296
  	struct ipc_ops sem_ops;
  	struct ipc_params sem_params;
e38935341   Kirill Korotaev   [PATCH] IPC names...
297
298
  
  	ns = current->nsproxy->ipc_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299

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

7748dbfaa   Nadia Derbey   ipc: unify the sy...
303
304
305
306
307
308
309
  	sem_ops.getnew = newary;
  	sem_ops.associate = sem_security;
  	sem_ops.more_checks = sem_more_checks;
  
  	sem_params.key = key;
  	sem_params.flg = semflg;
  	sem_params.u.nsems = nsems;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310

7748dbfaa   Nadia Derbey   ipc: unify the sy...
311
  	return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
  /*
   * Determine whether a sequence of semaphore operations would succeed
   * all at once. Return 0 if yes, 1 if need to sleep, else return error code.
   */
  
  static int try_atomic_semop (struct sem_array * sma, struct sembuf * sops,
  			     int nsops, struct sem_undo *un, int pid)
  {
  	int result, sem_op;
  	struct sembuf *sop;
  	struct sem * curr;
  
  	for (sop = sops; sop < sops + nsops; sop++) {
  		curr = sma->sem_base + sop->sem_num;
  		sem_op = sop->sem_op;
  		result = curr->semval;
    
  		if (!sem_op && result)
  			goto would_block;
  
  		result += sem_op;
  		if (result < 0)
  			goto would_block;
  		if (result > SEMVMX)
  			goto out_of_range;
  		if (sop->sem_flg & SEM_UNDO) {
  			int undo = un->semadj[sop->sem_num] - sem_op;
  			/*
  	 		 *	Exceeding the undo range is an error.
  			 */
  			if (undo < (-SEMAEM - 1) || undo > SEMAEM)
  				goto out_of_range;
  		}
  		curr->semval = result;
  	}
  
  	sop--;
  	while (sop >= sops) {
  		sma->sem_base[sop->sem_num].sempid = pid;
  		if (sop->sem_flg & SEM_UNDO)
  			un->semadj[sop->sem_num] -= sop->sem_op;
  		sop--;
  	}
  	
  	sma->sem_otime = get_seconds();
  	return 0;
  
  out_of_range:
  	result = -ERANGE;
  	goto undo;
  
  would_block:
  	if (sop->sem_flg & IPC_NOWAIT)
  		result = -EAGAIN;
  	else
  		result = 1;
  
  undo:
  	sop--;
  	while (sop >= sops) {
  		sma->sem_base[sop->sem_num].semval -= sop->sem_op;
  		sop--;
  	}
  
  	return result;
  }
  
  /* Go through the pending queue for the indicated semaphore
   * looking for tasks that can be completed.
   */
  static void update_queue (struct sem_array * sma)
  {
  	int error;
  	struct sem_queue * q;
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
387
388
  	q = list_entry(sma->sem_pending.next, struct sem_queue, list);
  	while (&q->list != &sma->sem_pending) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
390
391
392
393
394
  		error = try_atomic_semop(sma, q->sops, q->nsops,
  					 q->undo, q->pid);
  
  		/* Does q->sleeper still need to sleep? */
  		if (error <= 0) {
  			struct sem_queue *n;
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
395

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396
397
398
399
400
401
402
403
404
405
  			/*
  			 * Continue scanning. The next operation
  			 * that must be checked depends on the type of the
  			 * completed operation:
  			 * - if the operation modified the array, then
  			 *   restart from the head of the queue and
  			 *   check for threads that might be waiting
  			 *   for semaphore values to become 0.
  			 * - if the operation didn't modify the array,
  			 *   then just continue.
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
406
407
408
409
410
411
  			 * The order of list_del() and reading ->next
  			 * is crucial: In the former case, the list_del()
  			 * must be done first [because we might be the
  			 * first entry in ->sem_pending], in the latter
  			 * case the list_del() must be done last
  			 * [because the list is invalid after the list_del()]
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
  			 */
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
413
414
415
416
417
418
419
420
421
422
423
424
  			if (q->alter) {
  				list_del(&q->list);
  				n = list_entry(sma->sem_pending.next,
  						struct sem_queue, list);
  			} else {
  				n = list_entry(q->list.next, struct sem_queue,
  						list);
  				list_del(&q->list);
  			}
  
  			/* wake up the waiting thread */
  			q->status = IN_WAKEUP;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425
426
427
428
  			wake_up_process(q->sleeper);
  			/* hands-off: q will disappear immediately after
  			 * writing q->status.
  			 */
1224b375e   Linus Torvalds   Fix silly typo ("...
429
  			smp_wmb();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
430
431
432
  			q->status = error;
  			q = n;
  		} else {
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
433
  			q = list_entry(q->list.next, struct sem_queue, list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
  		}
  	}
  }
  
  /* 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
   * This model assumes that a task waits on exactly one semaphore.
   * Since semaphore operations are to be performed atomically, tasks actually
   * wait on a whole sequence of semaphores simultaneously.
   * The counts we return here are a rough approximation, but still
   * warrant that semncnt+semzcnt>0 if the task is on the pending queue.
   */
  static int count_semncnt (struct sem_array * sma, ushort semnum)
  {
  	int semncnt;
  	struct sem_queue * q;
  
  	semncnt = 0;
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
453
  	list_for_each_entry(q, &sma->sem_pending, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
455
456
457
458
459
460
461
462
463
464
  		struct sembuf * sops = q->sops;
  		int nsops = q->nsops;
  		int i;
  		for (i = 0; i < nsops; i++)
  			if (sops[i].sem_num == semnum
  			    && (sops[i].sem_op < 0)
  			    && !(sops[i].sem_flg & IPC_NOWAIT))
  				semncnt++;
  	}
  	return semncnt;
  }
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
465

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
466
467
468
469
470
471
  static int count_semzcnt (struct sem_array * sma, ushort semnum)
  {
  	int semzcnt;
  	struct sem_queue * q;
  
  	semzcnt = 0;
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
472
  	list_for_each_entry(q, &sma->sem_pending, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
474
475
476
477
478
479
480
481
482
483
  		struct sembuf * sops = q->sops;
  		int nsops = q->nsops;
  		int i;
  		for (i = 0; i < nsops; i++)
  			if (sops[i].sem_num == semnum
  			    && (sops[i].sem_op == 0)
  			    && !(sops[i].sem_flg & IPC_NOWAIT))
  				semzcnt++;
  	}
  	return semzcnt;
  }
6d97e2345   Adrian Bunk   ipc/sem.c: make f...
484
  static void free_un(struct rcu_head *head)
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
485
486
487
488
  {
  	struct sem_undo *un = container_of(head, struct sem_undo, rcu);
  	kfree(un);
  }
3e148c799   Nadia Derbey   fix idr_find() lo...
489
490
491
  /* Free a semaphore set. freeary() is called with sem_ids.rw_mutex locked
   * as a writer and the spinlock for this semaphore set hold. sem_ids.rw_mutex
   * remains locked on exit.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
492
   */
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
493
  static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494
  {
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
495
496
  	struct sem_undo *un, *tu;
  	struct sem_queue *q, *tq;
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
497
  	struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
498

380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
499
  	/* Free the existing undo structures for this semaphore set.  */
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
500
  	assert_spin_locked(&sma->sem_perm.lock);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
501
502
503
  	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
504
  		un->semid = -1;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
505
506
507
508
  		list_del_rcu(&un->list_proc);
  		spin_unlock(&un->ulp->lock);
  		call_rcu(&un->rcu, free_un);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
509
510
  
  	/* Wake up all pending processes and let them fail with EIDRM. */
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
511
  	list_for_each_entry_safe(q, tq, &sma->sem_pending, list) {
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
512
  		list_del(&q->list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
514
  		q->status = IN_WAKEUP;
  		wake_up_process(q->sleeper); /* doesn't sleep */
6003a93e7   Manfred Spraul   [PATCH] add missi...
515
  		smp_wmb();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
516
  		q->status = -EIDRM;	/* hands-off q */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
517
  	}
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
518
519
  	/* Remove the semaphore set from the IDR */
  	sem_rmid(ns, sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520
  	sem_unlock(sma);
e38935341   Kirill Korotaev   [PATCH] IPC names...
521
  	ns->used_sems -= sma->sem_nsems;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
  	security_sem_free(sma);
  	ipc_rcu_putref(sma);
  }
  
  static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in, int version)
  {
  	switch(version) {
  	case IPC_64:
  		return copy_to_user(buf, in, sizeof(*in));
  	case IPC_OLD:
  	    {
  		struct semid_ds out;
  
  		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;
  	}
  }
4b9fcb0ec   Pierre Peiffer   IPC/semaphores: c...
547
548
  static int semctl_nolock(struct ipc_namespace *ns, int semid,
  			 int cmd, int version, union semun arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
  {
  	int err = -EINVAL;
  	struct sem_array *sma;
  
  	switch(cmd) {
  	case IPC_INFO:
  	case SEM_INFO:
  	{
  		struct seminfo seminfo;
  		int max_id;
  
  		err = security_sem_semctl(NULL, cmd);
  		if (err)
  			return err;
  		
  		memset(&seminfo,0,sizeof(seminfo));
e38935341   Kirill Korotaev   [PATCH] IPC names...
565
566
567
568
  		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
569
570
571
572
  		seminfo.semvmx = SEMVMX;
  		seminfo.semmnu = SEMMNU;
  		seminfo.semmap = SEMMAP;
  		seminfo.semume = SEMUME;
3e148c799   Nadia Derbey   fix idr_find() lo...
573
  		down_read(&sem_ids(ns).rw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
574
  		if (cmd == SEM_INFO) {
e38935341   Kirill Korotaev   [PATCH] IPC names...
575
576
  			seminfo.semusz = sem_ids(ns).in_use;
  			seminfo.semaem = ns->used_sems;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
577
578
579
580
  		} else {
  			seminfo.semusz = SEMUSZ;
  			seminfo.semaem = SEMAEM;
  		}
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
581
  		max_id = ipc_get_maxid(&sem_ids(ns));
3e148c799   Nadia Derbey   fix idr_find() lo...
582
  		up_read(&sem_ids(ns).rw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
583
584
585
586
  		if (copy_to_user (arg.__buf, &seminfo, sizeof(struct seminfo))) 
  			return -EFAULT;
  		return (max_id < 0) ? 0: max_id;
  	}
4b9fcb0ec   Pierre Peiffer   IPC/semaphores: c...
587
  	case IPC_STAT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
589
590
591
  	case SEM_STAT:
  	{
  		struct semid64_ds tbuf;
  		int id;
4b9fcb0ec   Pierre Peiffer   IPC/semaphores: c...
592
593
594
595
596
597
598
599
600
601
602
  		if (cmd == SEM_STAT) {
  			sma = sem_lock(ns, semid);
  			if (IS_ERR(sma))
  				return PTR_ERR(sma);
  			id = sma->sem_perm.id;
  		} else {
  			sma = sem_lock_check(ns, semid);
  			if (IS_ERR(sma))
  				return PTR_ERR(sma);
  			id = 0;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
603
604
605
606
607
608
609
610
  
  		err = -EACCES;
  		if (ipcperms (&sma->sem_perm, S_IRUGO))
  			goto out_unlock;
  
  		err = security_sem_semctl(sma, cmd);
  		if (err)
  			goto out_unlock;
023a53557   Nadia Derbey   ipc: integrate ip...
611
  		memset(&tbuf, 0, sizeof(tbuf));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
  		kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
  		tbuf.sem_otime  = sma->sem_otime;
  		tbuf.sem_ctime  = sma->sem_ctime;
  		tbuf.sem_nsems  = sma->sem_nsems;
  		sem_unlock(sma);
  		if (copy_semid_to_user (arg.buf, &tbuf, version))
  			return -EFAULT;
  		return id;
  	}
  	default:
  		return -EINVAL;
  	}
  	return err;
  out_unlock:
  	sem_unlock(sma);
  	return err;
  }
e38935341   Kirill Korotaev   [PATCH] IPC names...
629
630
  static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
  		int cmd, int version, union semun arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
631
632
633
634
635
636
637
  {
  	struct sem_array *sma;
  	struct sem* curr;
  	int err;
  	ushort fast_sem_io[SEMMSL_FAST];
  	ushort* sem_io = fast_sem_io;
  	int nsems;
023a53557   Nadia Derbey   ipc: integrate ip...
638
639
640
  	sma = sem_lock_check(ns, semid);
  	if (IS_ERR(sma))
  		return PTR_ERR(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641
642
  
  	nsems = sma->sem_nsems;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
  	err = -EACCES;
  	if (ipcperms (&sma->sem_perm, (cmd==SETVAL||cmd==SETALL)?S_IWUGO:S_IRUGO))
  		goto out_unlock;
  
  	err = security_sem_semctl(sma, cmd);
  	if (err)
  		goto out_unlock;
  
  	err = -EACCES;
  	switch (cmd) {
  	case GETALL:
  	{
  		ushort __user *array = arg.array;
  		int i;
  
  		if(nsems > SEMMSL_FAST) {
6ff379721   Pierre Peiffer   IPC/semaphores: c...
659
  			sem_getref_and_unlock(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660
661
662
  
  			sem_io = ipc_alloc(sizeof(ushort)*nsems);
  			if(sem_io == NULL) {
6ff379721   Pierre Peiffer   IPC/semaphores: c...
663
  				sem_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
664
665
  				return -ENOMEM;
  			}
6ff379721   Pierre Peiffer   IPC/semaphores: c...
666
  			sem_lock_and_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
  			if (sma->sem_perm.deleted) {
  				sem_unlock(sma);
  				err = -EIDRM;
  				goto out_free;
  			}
  		}
  
  		for (i = 0; i < sma->sem_nsems; i++)
  			sem_io[i] = sma->sem_base[i].semval;
  		sem_unlock(sma);
  		err = 0;
  		if(copy_to_user(array, sem_io, nsems*sizeof(ushort)))
  			err = -EFAULT;
  		goto out_free;
  	}
  	case SETALL:
  	{
  		int i;
  		struct sem_undo *un;
6ff379721   Pierre Peiffer   IPC/semaphores: c...
686
  		sem_getref_and_unlock(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
687
688
689
690
  
  		if(nsems > SEMMSL_FAST) {
  			sem_io = ipc_alloc(sizeof(ushort)*nsems);
  			if(sem_io == NULL) {
6ff379721   Pierre Peiffer   IPC/semaphores: c...
691
  				sem_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
692
693
694
695
696
  				return -ENOMEM;
  			}
  		}
  
  		if (copy_from_user (sem_io, arg.array, nsems*sizeof(ushort))) {
6ff379721   Pierre Peiffer   IPC/semaphores: c...
697
  			sem_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
698
699
700
701
702
703
  			err = -EFAULT;
  			goto out_free;
  		}
  
  		for (i = 0; i < nsems; i++) {
  			if (sem_io[i] > SEMVMX) {
6ff379721   Pierre Peiffer   IPC/semaphores: c...
704
  				sem_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
705
706
707
708
  				err = -ERANGE;
  				goto out_free;
  			}
  		}
6ff379721   Pierre Peiffer   IPC/semaphores: c...
709
  		sem_lock_and_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
710
711
712
713
714
715
716
717
  		if (sma->sem_perm.deleted) {
  			sem_unlock(sma);
  			err = -EIDRM;
  			goto out_free;
  		}
  
  		for (i = 0; i < nsems; i++)
  			sma->sem_base[i].semval = sem_io[i];
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
718
719
720
  
  		assert_spin_locked(&sma->sem_perm.lock);
  		list_for_each_entry(un, &sma->list_id, list_id) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
721
722
  			for (i = 0; i < nsems; i++)
  				un->semadj[i] = 0;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
723
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
724
725
726
727
728
729
  		sma->sem_ctime = get_seconds();
  		/* maybe some queued-up processes were waiting for this */
  		update_queue(sma);
  		err = 0;
  		goto out_unlock;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
  	/* GETVAL, GETPID, GETNCTN, GETZCNT, SETVAL: fall-through */
  	}
  	err = -EINVAL;
  	if(semnum < 0 || semnum >= nsems)
  		goto out_unlock;
  
  	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:
  		err = count_semncnt(sma,semnum);
  		goto out_unlock;
  	case GETZCNT:
  		err = count_semzcnt(sma,semnum);
  		goto out_unlock;
  	case SETVAL:
  	{
  		int val = arg.val;
  		struct sem_undo *un;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
755

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
756
757
758
  		err = -ERANGE;
  		if (val > SEMVMX || val < 0)
  			goto out_unlock;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
759
760
  		assert_spin_locked(&sma->sem_perm.lock);
  		list_for_each_entry(un, &sma->list_id, list_id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
761
  			un->semadj[semnum] = 0;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
762

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
763
  		curr->semval = val;
b488893a3   Pavel Emelyanov   pid namespaces: c...
764
  		curr->sempid = task_tgid_vnr(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
765
766
767
768
769
770
771
772
773
774
775
776
777
778
  		sma->sem_ctime = get_seconds();
  		/* maybe some queued-up processes were waiting for this */
  		update_queue(sma);
  		err = 0;
  		goto out_unlock;
  	}
  	}
  out_unlock:
  	sem_unlock(sma);
  out_free:
  	if(sem_io != fast_sem_io)
  		ipc_free(sem_io, sizeof(ushort)*nsems);
  	return err;
  }
016d7132f   Pierre Peiffer   IPC: get rid of t...
779
780
  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
781
782
783
  {
  	switch(version) {
  	case IPC_64:
016d7132f   Pierre Peiffer   IPC: get rid of t...
784
  		if (copy_from_user(out, buf, sizeof(*out)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
785
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
786
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
787
788
789
790
791
792
  	case IPC_OLD:
  	    {
  		struct semid_ds tbuf_old;
  
  		if(copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
  			return -EFAULT;
016d7132f   Pierre Peiffer   IPC: get rid of t...
793
794
795
  		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
796
797
798
799
800
801
802
  
  		return 0;
  	    }
  	default:
  		return -EINVAL;
  	}
  }
522bb2a2b   Pierre Peiffer   IPC/semaphores: m...
803
804
805
806
807
  /*
   * This function handles some semctl commands which require the rw_mutex
   * to be held in write mode.
   * NOTE: no locks must be held, the rw_mutex is taken inside this function.
   */
21a4826a7   Pierre Peiffer   IPC/semaphores: r...
808
809
  static int semctl_down(struct ipc_namespace *ns, int semid,
  		       int cmd, int version, union semun arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
810
811
812
  {
  	struct sem_array *sma;
  	int err;
016d7132f   Pierre Peiffer   IPC: get rid of t...
813
  	struct semid64_ds semid64;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
814
815
816
  	struct kern_ipc_perm *ipcp;
  
  	if(cmd == IPC_SET) {
016d7132f   Pierre Peiffer   IPC: get rid of t...
817
  		if (copy_semid_from_user(&semid64, arg.buf, version))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
818
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
819
  	}
073115d6b   Steve Grubb   [PATCH] Rework of...
820

a5f75e7f2   Pierre Peiffer   IPC: consolidate ...
821
822
823
  	ipcp = ipcctl_pre_down(&sem_ids(ns), semid, cmd, &semid64.sem_perm, 0);
  	if (IS_ERR(ipcp))
  		return PTR_ERR(ipcp);
073115d6b   Steve Grubb   [PATCH] Rework of...
824

a5f75e7f2   Pierre Peiffer   IPC: consolidate ...
825
  	sma = container_of(ipcp, struct sem_array, sem_perm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
826
827
828
829
830
831
832
  
  	err = security_sem_semctl(sma, cmd);
  	if (err)
  		goto out_unlock;
  
  	switch(cmd){
  	case IPC_RMID:
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
833
  		freeary(ns, ipcp);
522bb2a2b   Pierre Peiffer   IPC/semaphores: m...
834
  		goto out_up;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
835
  	case IPC_SET:
8f4a3809c   Pierre Peiffer   IPC: introduce ip...
836
  		ipc_update_perm(&semid64.sem_perm, ipcp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
837
  		sma->sem_ctime = get_seconds();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
838
839
  		break;
  	default:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
840
  		err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
841
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
842
843
844
  
  out_unlock:
  	sem_unlock(sma);
522bb2a2b   Pierre Peiffer   IPC/semaphores: m...
845
846
  out_up:
  	up_write(&sem_ids(ns).rw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
847
848
  	return err;
  }
6673e0c3f   Heiko Carstens   [CVE-2009-0029] S...
849
  SYSCALL_DEFINE(semctl)(int semid, int semnum, int cmd, union semun arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
850
851
852
  {
  	int err = -EINVAL;
  	int version;
e38935341   Kirill Korotaev   [PATCH] IPC names...
853
  	struct ipc_namespace *ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
854
855
856
857
858
  
  	if (semid < 0)
  		return -EINVAL;
  
  	version = ipc_parse_version(&cmd);
e38935341   Kirill Korotaev   [PATCH] IPC names...
859
  	ns = current->nsproxy->ipc_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
860
861
862
863
  
  	switch(cmd) {
  	case IPC_INFO:
  	case SEM_INFO:
4b9fcb0ec   Pierre Peiffer   IPC/semaphores: c...
864
  	case IPC_STAT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
865
  	case SEM_STAT:
4b9fcb0ec   Pierre Peiffer   IPC/semaphores: c...
866
  		err = semctl_nolock(ns, semid, cmd, version, arg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
867
868
869
870
871
872
  		return err;
  	case GETALL:
  	case GETVAL:
  	case GETPID:
  	case GETNCNT:
  	case GETZCNT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
873
874
  	case SETVAL:
  	case SETALL:
e38935341   Kirill Korotaev   [PATCH] IPC names...
875
  		err = semctl_main(ns,semid,semnum,cmd,version,arg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
876
877
878
  		return err;
  	case IPC_RMID:
  	case IPC_SET:
21a4826a7   Pierre Peiffer   IPC/semaphores: r...
879
  		err = semctl_down(ns, semid, cmd, version, arg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
880
881
882
883
884
  		return err;
  	default:
  		return -EINVAL;
  	}
  }
6673e0c3f   Heiko Carstens   [CVE-2009-0029] S...
885
886
887
888
889
890
891
  #ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
  asmlinkage long SyS_semctl(int semid, int semnum, int cmd, union semun arg)
  {
  	return SYSC_semctl((int) semid, (int) semnum, (int) cmd, arg);
  }
  SYSCALL_ALIAS(sys_semctl, SyS_semctl);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
892

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
893
894
895
896
897
898
899
900
901
902
903
904
905
906
  /* 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
907
908
909
  
  	undo_list = current->sysvsem.undo_list;
  	if (!undo_list) {
2453a3062   Matt Helsley   [PATCH] ipc: repl...
910
  		undo_list = kzalloc(sizeof(*undo_list), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
911
912
  		if (undo_list == NULL)
  			return -ENOMEM;
00a5dfdb9   Ingo Molnar   [PATCH] Fix semun...
913
  		spin_lock_init(&undo_list->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
914
  		atomic_set(&undo_list->refcnt, 1);
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
915
  		INIT_LIST_HEAD(&undo_list->list_proc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
916
917
918
919
920
921
922
923
  		current->sysvsem.undo_list = undo_list;
  	}
  	*undo_listp = undo_list;
  	return 0;
  }
  
  static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid)
  {
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
924
  	struct sem_undo *walk;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
925

380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
926
  	list_for_each_entry_rcu(walk, &ulp->list_proc, list_proc) {
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
927
928
  		if (walk->semid == semid)
  			return walk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
929
  	}
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
930
  	return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
931
  }
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
932
933
934
935
936
937
938
939
  /**
   * find_alloc_undo - Lookup (and if not present create) undo array
   * @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...
940
941
   * Lifetime-rules: sem_undo is rcu-protected, on success, the function
   * performs a rcu_read_lock().
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
942
943
   */
  static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
944
945
946
947
948
949
950
951
952
953
  {
  	struct sem_array *sma;
  	struct sem_undo_list *ulp;
  	struct sem_undo *un, *new;
  	int nsems;
  	int error;
  
  	error = get_undo_list(&ulp);
  	if (error)
  		return ERR_PTR(error);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
954
  	rcu_read_lock();
c530c6ac7   Pierre Peiffer   IPC: cleanup some...
955
  	spin_lock(&ulp->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
956
  	un = lookup_undo(ulp, semid);
c530c6ac7   Pierre Peiffer   IPC: cleanup some...
957
  	spin_unlock(&ulp->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
958
959
  	if (likely(un!=NULL))
  		goto out;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
960
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
961
962
  
  	/* no undo structure around - allocate one. */
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
963
  	/* step 1: figure out the size of the semaphore array */
023a53557   Nadia Derbey   ipc: integrate ip...
964
965
966
  	sma = sem_lock_check(ns, semid);
  	if (IS_ERR(sma))
  		return ERR_PTR(PTR_ERR(sma));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
967
  	nsems = sma->sem_nsems;
6ff379721   Pierre Peiffer   IPC/semaphores: c...
968
  	sem_getref_and_unlock(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
969

4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
970
  	/* step 2: allocate new undo structure */
4668edc33   Burman Yan   [PATCH] kernel co...
971
  	new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
972
  	if (!new) {
6ff379721   Pierre Peiffer   IPC/semaphores: c...
973
  		sem_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
974
975
  		return ERR_PTR(-ENOMEM);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
976

380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
977
  	/* step 3: Acquire the lock on semaphore array */
6ff379721   Pierre Peiffer   IPC/semaphores: c...
978
  	sem_lock_and_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
979
980
  	if (sma->sem_perm.deleted) {
  		sem_unlock(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
981
982
983
984
  		kfree(new);
  		un = ERR_PTR(-EIDRM);
  		goto out;
  	}
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
985
986
987
988
989
990
991
992
993
994
  	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...
995
996
  	/* step 5: initialize & link new undo structure */
  	new->semadj = (short *) &new[1];
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
997
  	new->ulp = ulp;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
998
999
  	new->semid = semid;
  	assert_spin_locked(&ulp->lock);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1000
  	list_add_rcu(&new->list_proc, &ulp->list_proc);
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1001
1002
  	assert_spin_locked(&sma->sem_perm.lock);
  	list_add(&new->list_id, &sma->list_id);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1003
  	un = new;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1004

380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1005
  success:
c530c6ac7   Pierre Peiffer   IPC: cleanup some...
1006
  	spin_unlock(&ulp->lock);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1007
1008
  	rcu_read_lock();
  	sem_unlock(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1009
1010
1011
  out:
  	return un;
  }
d5460c997   Heiko Carstens   [CVE-2009-0029] S...
1012
1013
  SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
  		unsigned, nsops, const struct timespec __user *, timeout)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1014
1015
1016
1017
1018
1019
  {
  	int error = -EINVAL;
  	struct sem_array *sma;
  	struct sembuf fast_sops[SEMOPM_FAST];
  	struct sembuf* sops = fast_sops, *sop;
  	struct sem_undo *un;
b78755abc   Manfred Spraul   [PATCH] ipcsem: r...
1020
  	int undos = 0, alter = 0, max;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1021
1022
  	struct sem_queue queue;
  	unsigned long jiffies_left = 0;
e38935341   Kirill Korotaev   [PATCH] IPC names...
1023
1024
1025
  	struct ipc_namespace *ns;
  
  	ns = current->nsproxy->ipc_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1026
1027
1028
  
  	if (nsops < 1 || semid < 0)
  		return -EINVAL;
e38935341   Kirill Korotaev   [PATCH] IPC names...
1029
  	if (nsops > ns->sc_semopm)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
  		return -E2BIG;
  	if(nsops > SEMOPM_FAST) {
  		sops = kmalloc(sizeof(*sops)*nsops,GFP_KERNEL);
  		if(sops==NULL)
  			return -ENOMEM;
  	}
  	if (copy_from_user (sops, tsops, nsops * sizeof(*tsops))) {
  		error=-EFAULT;
  		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...
1058
1059
  			undos = 1;
  		if (sop->sem_op != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1060
1061
  			alter = 1;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1062

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1063
  	if (undos) {
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1064
  		un = find_alloc_undo(ns, semid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1065
1066
1067
1068
1069
1070
  		if (IS_ERR(un)) {
  			error = PTR_ERR(un);
  			goto out_free;
  		}
  	} else
  		un = NULL;
023a53557   Nadia Derbey   ipc: integrate ip...
1071
1072
  	sma = sem_lock_check(ns, semid);
  	if (IS_ERR(sma)) {
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1073
1074
  		if (un)
  			rcu_read_unlock();
023a53557   Nadia Derbey   ipc: integrate ip...
1075
  		error = PTR_ERR(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1076
  		goto out_free;
023a53557   Nadia Derbey   ipc: integrate ip...
1077
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1078
  	/*
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1079
  	 * semid identifiers are not unique - find_alloc_undo may have
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1080
  	 * allocated an undo structure, it was invalidated by an RMID
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1081
  	 * and now a new array with received the same id. Check and fail.
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1082
1083
  	 * This case can be detected checking un->semid. The existance of
  	 * "un" itself is guaranteed by rcu.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1084
  	 */
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1085
  	error = -EIDRM;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
  	if (un) {
  		if (un->semid == -1) {
  			rcu_read_unlock();
  			goto out_unlock_free;
  		} else {
  			/*
  			 * rcu lock can be released, "un" cannot disappear:
  			 * - sem_lock is acquired, thus IPC_RMID is
  			 *   impossible.
  			 * - exit_sem is impossible, it always operates on
  			 *   current (or a dead task).
  			 */
  
  			rcu_read_unlock();
  		}
  	}
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1102

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
  	error = -EFBIG;
  	if (max >= sma->sem_nsems)
  		goto out_unlock_free;
  
  	error = -EACCES;
  	if (ipcperms(&sma->sem_perm, alter ? S_IWUGO : S_IRUGO))
  		goto out_unlock_free;
  
  	error = security_sem_semop(sma, sops, nsops, alter);
  	if (error)
  		goto out_unlock_free;
b488893a3   Pavel Emelyanov   pid namespaces: c...
1114
  	error = try_atomic_semop (sma, sops, nsops, un, task_tgid_vnr(current));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
  	if (error <= 0) {
  		if (alter && error == 0)
  			update_queue (sma);
  		goto out_unlock_free;
  	}
  
  	/* We need to sleep on this operation, so we put the current
  	 * task into the pending queue and go to sleep.
  	 */
  		
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1125
1126
1127
  	queue.sops = sops;
  	queue.nsops = nsops;
  	queue.undo = un;
b488893a3   Pavel Emelyanov   pid namespaces: c...
1128
  	queue.pid = task_tgid_vnr(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1129
1130
  	queue.alter = alter;
  	if (alter)
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
1131
  		list_add_tail(&queue.list, &sma->sem_pending);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1132
  	else
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
1133
  		list_add(&queue.list, &sma->sem_pending);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
  
  	queue.status = -EINTR;
  	queue.sleeper = current;
  	current->state = TASK_INTERRUPTIBLE;
  	sem_unlock(sma);
  
  	if (timeout)
  		jiffies_left = schedule_timeout(jiffies_left);
  	else
  		schedule();
  
  	error = queue.status;
  	while(unlikely(error == IN_WAKEUP)) {
  		cpu_relax();
  		error = queue.status;
  	}
  
  	if (error != -EINTR) {
  		/* fast path: update_queue already obtained all requested
  		 * resources */
  		goto out_free;
  	}
e38935341   Kirill Korotaev   [PATCH] IPC names...
1156
  	sma = sem_lock(ns, semid);
023a53557   Nadia Derbey   ipc: integrate ip...
1157
  	if (IS_ERR(sma)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
  		error = -EIDRM;
  		goto out_free;
  	}
  
  	/*
  	 * If queue.status != -EINTR we are woken up by another process
  	 */
  	error = queue.status;
  	if (error != -EINTR) {
  		goto out_unlock_free;
  	}
  
  	/*
  	 * If an interrupt occurred we have to clean up the queue
  	 */
  	if (timeout && jiffies_left == 0)
  		error = -EAGAIN;
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
1175
  	list_del(&queue.list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1176
1177
1178
1179
1180
1181
1182
1183
  
  out_unlock_free:
  	sem_unlock(sma);
  out_free:
  	if(sops != fast_sops)
  		kfree(sops);
  	return error;
  }
d5460c997   Heiko Carstens   [CVE-2009-0029] S...
1184
1185
  SYSCALL_DEFINE3(semop, int, semid, struct sembuf __user *, tsops,
  		unsigned, nsops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1186
1187
1188
1189
1190
1191
  {
  	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
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
   */
  
  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
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
  		atomic_inc(&undo_list->refcnt);
  		tsk->sysvsem.undo_list = undo_list;
  	} else 
  		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...
1225
  	struct sem_undo_list *ulp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1226

4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1227
1228
  	ulp = tsk->sysvsem.undo_list;
  	if (!ulp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1229
  		return;
9edff4ab1   Manfred Spraul   ipc: sysvsem: imp...
1230
  	tsk->sysvsem.undo_list = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1231

4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1232
  	if (!atomic_dec_and_test(&ulp->refcnt))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1233
  		return;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1234
  	for (;;) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1235
  		struct sem_array *sma;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1236
1237
  		struct sem_undo *un;
  		int semid;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1238
  		int i;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1239
1240
1241
1242
1243
1244
1245
1246
  		rcu_read_lock();
  		un = list_entry(rcu_dereference(ulp->list_proc.next),
  					struct sem_undo, list_proc);
  		if (&un->list_proc == &ulp->list_proc)
  			semid = -1;
  		 else
  			semid = un->semid;
  		rcu_read_unlock();
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1247

380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1248
1249
  		if (semid == -1)
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1250

380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1251
  		sma = sem_lock_check(tsk->nsproxy->ipc_ns, un->semid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1252

380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1253
1254
1255
  		/* exit_sem raced with IPC_RMID, nothing to do */
  		if (IS_ERR(sma))
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1256

380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
  		un = lookup_undo(ulp, semid);
  		if (un == NULL) {
  			/* exit_sem raced with IPC_RMID+semget() that created
  			 * exactly the same semid. Nothing to do.
  			 */
  			sem_unlock(sma);
  			continue;
  		}
  
  		/* remove un from the linked lists */
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1267
1268
  		assert_spin_locked(&sma->sem_perm.lock);
  		list_del(&un->list_id);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1269
1270
1271
  		spin_lock(&ulp->lock);
  		list_del_rcu(&un->list_proc);
  		spin_unlock(&ulp->lock);
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1272
1273
  		/* perform adjustments registered in un */
  		for (i = 0; i < sma->sem_nsems; i++) {
5f921ae96   Ingo Molnar   [PATCH] sem2mutex...
1274
  			struct sem * semaphore = &sma->sem_base[i];
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1275
1276
  			if (un->semadj[i]) {
  				semaphore->semval += un->semadj[i];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
  				/*
  				 * 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.
  				 *
  				 * 	Manfred <manfred@colorfullife.com>
  				 */
5f921ae96   Ingo Molnar   [PATCH] sem2mutex...
1290
1291
1292
1293
  				if (semaphore->semval < 0)
  					semaphore->semval = 0;
  				if (semaphore->semval > SEMVMX)
  					semaphore->semval = SEMVMX;
b488893a3   Pavel Emelyanov   pid namespaces: c...
1294
  				semaphore->sempid = task_tgid_vnr(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1295
1296
1297
1298
1299
  			}
  		}
  		sma->sem_otime = get_seconds();
  		/* maybe some queued-up processes were waiting for this */
  		update_queue(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1300
  		sem_unlock(sma);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1301
1302
  
  		call_rcu(&un->rcu, free_un);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1303
  	}
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1304
  	kfree(ulp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1305
1306
1307
  }
  
  #ifdef CONFIG_PROC_FS
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1308
  static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1309
  {
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1310
1311
1312
1313
1314
1315
  	struct sem_array *sma = it;
  
  	return seq_printf(s,
  			  "%10d %10d  %4o %10lu %5u %5u %5u %5u %10lu %10lu
  ",
  			  sma->sem_perm.key,
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
1316
  			  sma->sem_perm.id,
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1317
1318
1319
1320
1321
1322
1323
1324
  			  sma->sem_perm.mode,
  			  sma->sem_nsems,
  			  sma->sem_perm.uid,
  			  sma->sem_perm.gid,
  			  sma->sem_perm.cuid,
  			  sma->sem_perm.cgid,
  			  sma->sem_otime,
  			  sma->sem_ctime);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1325
1326
  }
  #endif