Blame view

ipc/sem.c 33.7 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
61
62
63
   * Enforced range limit on SEM_UNDO
   * (c) 2001 Red Hat Inc <alan@redhat.com>
   * 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
292
293
  		return -EINVAL;
  
  	return 0;
  }
  
  asmlinkage long sys_semget(key_t key, int nsems, int semflg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
  {
e38935341   Kirill Korotaev   [PATCH] IPC names...
295
  	struct ipc_namespace *ns;
7748dbfaa   Nadia Derbey   ipc: unify the sy...
296
297
  	struct ipc_ops sem_ops;
  	struct ipc_params sem_params;
e38935341   Kirill Korotaev   [PATCH] IPC names...
298
299
  
  	ns = current->nsproxy->ipc_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300

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

7748dbfaa   Nadia Derbey   ipc: unify the sy...
304
305
306
307
308
309
310
  	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
311

7748dbfaa   Nadia Derbey   ipc: unify the sy...
312
  	return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
387
  /*
   * 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...
388
389
  	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
390
391
392
393
394
395
  		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...
396

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397
398
399
400
401
402
403
404
405
406
  			/*
  			 * 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...
407
408
409
410
411
412
  			 * 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
413
  			 */
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
414
415
416
417
418
419
420
421
422
423
424
425
  			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
426
427
428
429
  			wake_up_process(q->sleeper);
  			/* hands-off: q will disappear immediately after
  			 * writing q->status.
  			 */
1224b375e   Linus Torvalds   Fix silly typo ("...
430
  			smp_wmb();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
432
433
  			q->status = error;
  			q = n;
  		} else {
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
434
  			q = list_entry(q->list.next, struct sem_queue, list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
  		}
  	}
  }
  
  /* 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...
454
  	list_for_each_entry(q, &sma->sem_pending, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
455
456
457
458
459
460
461
462
463
464
465
  		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...
466

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
468
469
470
471
472
  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...
473
  	list_for_each_entry(q, &sma->sem_pending, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
474
475
476
477
478
479
480
481
482
483
484
  		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;
  }
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
485
486
487
488
489
  void free_un(struct rcu_head *head)
  {
  	struct sem_undo *un = container_of(head, struct sem_undo, rcu);
  	kfree(un);
  }
3e148c799   Nadia Derbey   fix idr_find() lo...
490
491
492
  /* 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
493
   */
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
494
  static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
  {
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
496
497
  	struct sem_undo *un, *tu;
  	struct sem_queue *q, *tq;
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
498
  	struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
757
758
759
  		err = -ERANGE;
  		if (val > SEMVMX || val < 0)
  			goto out_unlock;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
760
761
  		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
762
  			un->semadj[semnum] = 0;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
763

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
764
  		curr->semval = val;
b488893a3   Pavel Emelyanov   pid namespaces: c...
765
  		curr->sempid = task_tgid_vnr(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
766
767
768
769
770
771
772
773
774
775
776
777
778
779
  		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...
780
781
  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
782
783
784
  {
  	switch(version) {
  	case IPC_64:
016d7132f   Pierre Peiffer   IPC: get rid of t...
785
  		if (copy_from_user(out, buf, sizeof(*out)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
786
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
787
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788
789
790
791
792
793
  	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...
794
795
796
  		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
797
798
799
800
801
802
803
  
  		return 0;
  	    }
  	default:
  		return -EINVAL;
  	}
  }
522bb2a2b   Pierre Peiffer   IPC/semaphores: m...
804
805
806
807
808
  /*
   * 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...
809
810
  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
811
812
813
  {
  	struct sem_array *sma;
  	int err;
016d7132f   Pierre Peiffer   IPC: get rid of t...
814
  	struct semid64_ds semid64;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
815
816
817
  	struct kern_ipc_perm *ipcp;
  
  	if(cmd == IPC_SET) {
016d7132f   Pierre Peiffer   IPC: get rid of t...
818
  		if (copy_semid_from_user(&semid64, arg.buf, version))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
819
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
820
  	}
073115d6b   Steve Grubb   [PATCH] Rework of...
821

a5f75e7f2   Pierre Peiffer   IPC: consolidate ...
822
823
824
  	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...
825

a5f75e7f2   Pierre Peiffer   IPC: consolidate ...
826
  	sma = container_of(ipcp, struct sem_array, sem_perm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
827
828
829
830
831
832
833
  
  	err = security_sem_semctl(sma, cmd);
  	if (err)
  		goto out_unlock;
  
  	switch(cmd){
  	case IPC_RMID:
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
834
  		freeary(ns, ipcp);
522bb2a2b   Pierre Peiffer   IPC/semaphores: m...
835
  		goto out_up;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
836
  	case IPC_SET:
8f4a3809c   Pierre Peiffer   IPC: introduce ip...
837
  		ipc_update_perm(&semid64.sem_perm, ipcp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
838
  		sma->sem_ctime = get_seconds();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
839
840
  		break;
  	default:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
841
  		err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
842
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
843
844
845
  
  out_unlock:
  	sem_unlock(sma);
522bb2a2b   Pierre Peiffer   IPC/semaphores: m...
846
847
  out_up:
  	up_write(&sem_ids(ns).rw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
848
849
850
851
852
853
854
  	return err;
  }
  
  asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg)
  {
  	int err = -EINVAL;
  	int version;
e38935341   Kirill Korotaev   [PATCH] IPC names...
855
  	struct ipc_namespace *ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
856
857
858
859
860
  
  	if (semid < 0)
  		return -EINVAL;
  
  	version = ipc_parse_version(&cmd);
e38935341   Kirill Korotaev   [PATCH] IPC names...
861
  	ns = current->nsproxy->ipc_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862
863
864
865
  
  	switch(cmd) {
  	case IPC_INFO:
  	case SEM_INFO:
4b9fcb0ec   Pierre Peiffer   IPC/semaphores: c...
866
  	case IPC_STAT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
867
  	case SEM_STAT:
4b9fcb0ec   Pierre Peiffer   IPC/semaphores: c...
868
  		err = semctl_nolock(ns, semid, cmd, version, arg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
869
870
871
872
873
874
  		return err;
  	case GETALL:
  	case GETVAL:
  	case GETPID:
  	case GETNCNT:
  	case GETZCNT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
875
876
  	case SETVAL:
  	case SETALL:
e38935341   Kirill Korotaev   [PATCH] IPC names...
877
  		err = semctl_main(ns,semid,semnum,cmd,version,arg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
878
879
880
  		return err;
  	case IPC_RMID:
  	case IPC_SET:
21a4826a7   Pierre Peiffer   IPC/semaphores: r...
881
  		err = semctl_down(ns, semid, cmd, version, arg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
882
883
884
885
886
  		return err;
  	default:
  		return -EINVAL;
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
887
888
889
890
891
892
893
894
895
896
897
898
899
900
  /* 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
901
902
903
  
  	undo_list = current->sysvsem.undo_list;
  	if (!undo_list) {
2453a3062   Matt Helsley   [PATCH] ipc: repl...
904
  		undo_list = kzalloc(sizeof(*undo_list), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
905
906
  		if (undo_list == NULL)
  			return -ENOMEM;
00a5dfdb9   Ingo Molnar   [PATCH] Fix semun...
907
  		spin_lock_init(&undo_list->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
908
  		atomic_set(&undo_list->refcnt, 1);
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
909
  		INIT_LIST_HEAD(&undo_list->list_proc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
910
911
912
913
914
915
916
917
  		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...
918
  	struct sem_undo *walk;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
919

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

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

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

380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
999
  success:
c530c6ac7   Pierre Peiffer   IPC: cleanup some...
1000
  	spin_unlock(&ulp->lock);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1001
1002
  	rcu_read_lock();
  	sem_unlock(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
  out:
  	return un;
  }
  
  asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops,
  			unsigned nsops, const struct timespec __user *timeout)
  {
  	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...
1015
  	int undos = 0, alter = 0, max;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1016
1017
  	struct sem_queue queue;
  	unsigned long jiffies_left = 0;
e38935341   Kirill Korotaev   [PATCH] IPC names...
1018
1019
1020
  	struct ipc_namespace *ns;
  
  	ns = current->nsproxy->ipc_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1021
1022
1023
  
  	if (nsops < 1 || semid < 0)
  		return -EINVAL;
e38935341   Kirill Korotaev   [PATCH] IPC names...
1024
  	if (nsops > ns->sc_semopm)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
  		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...
1053
1054
  			undos = 1;
  		if (sop->sem_op != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1055
1056
  			alter = 1;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1057

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1058
  	if (undos) {
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1059
  		un = find_alloc_undo(ns, semid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1060
1061
1062
1063
1064
1065
  		if (IS_ERR(un)) {
  			error = PTR_ERR(un);
  			goto out_free;
  		}
  	} else
  		un = NULL;
023a53557   Nadia Derbey   ipc: integrate ip...
1066
1067
  	sma = sem_lock_check(ns, semid);
  	if (IS_ERR(sma)) {
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1068
1069
  		if (un)
  			rcu_read_unlock();
023a53557   Nadia Derbey   ipc: integrate ip...
1070
  		error = PTR_ERR(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1071
  		goto out_free;
023a53557   Nadia Derbey   ipc: integrate ip...
1072
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1073
  	/*
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1074
  	 * semid identifiers are not unique - find_alloc_undo may have
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1075
  	 * allocated an undo structure, it was invalidated by an RMID
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1076
  	 * and now a new array with received the same id. Check and fail.
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1077
1078
  	 * 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
1079
  	 */
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1080
  	error = -EIDRM;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
  	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...
1097

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
  	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...
1109
  	error = try_atomic_semop (sma, sops, nsops, un, task_tgid_vnr(current));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
  	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
1120
1121
1122
  	queue.sops = sops;
  	queue.nsops = nsops;
  	queue.undo = un;
b488893a3   Pavel Emelyanov   pid namespaces: c...
1123
  	queue.pid = task_tgid_vnr(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1124
1125
  	queue.alter = alter;
  	if (alter)
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
1126
  		list_add_tail(&queue.list, &sma->sem_pending);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1127
  	else
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
1128
  		list_add(&queue.list, &sma->sem_pending);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
  
  	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...
1151
  	sma = sem_lock(ns, semid);
023a53557   Nadia Derbey   ipc: integrate ip...
1152
  	if (IS_ERR(sma)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
  		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...
1170
  	list_del(&queue.list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
  	goto out_unlock_free;
  
  out_unlock_free:
  	sem_unlock(sma);
  out_free:
  	if(sops != fast_sops)
  		kfree(sops);
  	return error;
  }
  
  asmlinkage long sys_semop (int semid, struct sembuf __user *tsops, unsigned nsops)
  {
  	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
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
   */
  
  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
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
  		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...
1221
  	struct sem_undo_list *ulp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1222

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

4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1228
  	if (!atomic_dec_and_test(&ulp->refcnt))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1229
  		return;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1230
  	for (;;) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1231
  		struct sem_array *sma;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1232
1233
  		struct sem_undo *un;
  		int semid;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1234
  		int i;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1235
1236
1237
1238
1239
1240
1241
1242
  		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...
1243

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

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

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

380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
  		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...
1263
1264
  		assert_spin_locked(&sma->sem_perm.lock);
  		list_del(&un->list_id);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1265
1266
1267
  		spin_lock(&ulp->lock);
  		list_del_rcu(&un->list_proc);
  		spin_unlock(&ulp->lock);
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1268
1269
  		/* perform adjustments registered in un */
  		for (i = 0; i < sma->sem_nsems; i++) {
5f921ae96   Ingo Molnar   [PATCH] sem2mutex...
1270
  			struct sem * semaphore = &sma->sem_base[i];
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1271
1272
  			if (un->semadj[i]) {
  				semaphore->semval += un->semadj[i];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
  				/*
  				 * 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...
1286
1287
1288
1289
  				if (semaphore->semval < 0)
  					semaphore->semval = 0;
  				if (semaphore->semval > SEMVMX)
  					semaphore->semval = SEMVMX;
b488893a3   Pavel Emelyanov   pid namespaces: c...
1290
  				semaphore->sempid = task_tgid_vnr(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1291
1292
1293
1294
1295
  			}
  		}
  		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
1296
  		sem_unlock(sma);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1297
1298
  
  		call_rcu(&un->rcu, free_un);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1299
  	}
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1300
  	kfree(ulp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1301
1302
1303
  }
  
  #ifdef CONFIG_PROC_FS
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1304
  static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1305
  {
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1306
1307
1308
1309
1310
1311
  	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...
1312
  			  sma->sem_perm.id,
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1313
1314
1315
1316
1317
1318
1319
1320
  			  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
1321
1322
  }
  #endif