Blame view

ipc/sem.c 32.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
260
261
262
263
264
265
  
  	sma->sem_base = (struct sem *) &sma[1];
  	/* sma->sem_pending = NULL; */
  	sma->sem_pending_last = &sma->sem_pending;
  	/* sma->undo = NULL; */
  	sma->sem_nsems = nsems;
  	sma->sem_ctime = get_seconds();
  	sem_unlock(sma);
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
266
  	return sma->sem_perm.id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
  }
7748dbfaa   Nadia Derbey   ipc: unify the sy...
268

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

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

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

7748dbfaa   Nadia Derbey   ipc: unify the sy...
313
  	return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params);
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
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
  }
  
  /* Manage the doubly linked list sma->sem_pending as a FIFO:
   * insert new queue elements at the tail sma->sem_pending_last.
   */
  static inline void append_to_queue (struct sem_array * sma,
  				    struct sem_queue * q)
  {
  	*(q->prev = sma->sem_pending_last) = q;
  	*(sma->sem_pending_last = &q->next) = NULL;
  }
  
  static inline void prepend_to_queue (struct sem_array * sma,
  				     struct sem_queue * q)
  {
  	q->next = sma->sem_pending;
  	*(q->prev = &sma->sem_pending) = q;
  	if (q->next)
  		q->next->prev = &q->next;
  	else /* sma->sem_pending_last == &sma->sem_pending */
  		sma->sem_pending_last = &q->next;
  }
  
  static inline void remove_from_queue (struct sem_array * sma,
  				      struct sem_queue * q)
  {
  	*(q->prev) = q->next;
  	if (q->next)
  		q->next->prev = q->prev;
  	else /* sma->sem_pending_last == &q->next */
  		sma->sem_pending_last = q->prev;
  	q->prev = NULL; /* mark as removed */
  }
  
  /*
   * 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;
  
  	q = sma->sem_pending;
  	while(q) {
  		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;
  			remove_from_queue(sma,q);
  			q->status = IN_WAKEUP;
  			/*
  			 * 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.
  			 */
  			if (q->alter)
  				n = sma->sem_pending;
  			else
  				n = q->next;
  			wake_up_process(q->sleeper);
  			/* hands-off: q will disappear immediately after
  			 * writing q->status.
  			 */
1224b375e   Linus Torvalds   Fix silly typo ("...
452
  			smp_wmb();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
  			q->status = error;
  			q = n;
  		} else {
  			q = q->next;
  		}
  	}
  }
  
  /* 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;
  	for (q = sma->sem_pending; q; q = q->next) {
  		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;
  }
  static int count_semzcnt (struct sem_array * sma, ushort semnum)
  {
  	int semzcnt;
  	struct sem_queue * q;
  
  	semzcnt = 0;
  	for (q = sma->sem_pending; q; q = q->next) {
  		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;
  }
3e148c799   Nadia Derbey   fix idr_find() lo...
506
507
508
  /* 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
509
   */
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
510
  static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
511
512
513
  {
  	struct sem_undo *un;
  	struct sem_queue *q;
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
514
  	struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
  
  	/* Invalidate the existing undo structures for this semaphore set.
  	 * (They will be freed without any further action in exit_sem()
  	 * or during the next semop.)
  	 */
  	for (un = sma->undo; un; un = un->id_next)
  		un->semid = -1;
  
  	/* Wake up all pending processes and let them fail with EIDRM. */
  	q = sma->sem_pending;
  	while(q) {
  		struct sem_queue *n;
  		/* lazy remove_from_queue: we are killing the whole queue */
  		q->prev = NULL;
  		n = q->next;
  		q->status = IN_WAKEUP;
  		wake_up_process(q->sleeper); /* doesn't sleep */
6003a93e7   Manfred Spraul   [PATCH] add missi...
532
  		smp_wmb();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
534
535
  		q->status = -EIDRM;	/* hands-off q */
  		q = n;
  	}
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
536
537
  	/* Remove the semaphore set from the IDR */
  	sem_rmid(ns, sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538
  	sem_unlock(sma);
e38935341   Kirill Korotaev   [PATCH] IPC names...
539
  	ns->used_sems -= sma->sem_nsems;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
  	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...
565
566
  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
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
  {
  	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...
583
584
585
586
  		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
587
588
589
590
  		seminfo.semvmx = SEMVMX;
  		seminfo.semmnu = SEMMNU;
  		seminfo.semmap = SEMMAP;
  		seminfo.semume = SEMUME;
3e148c799   Nadia Derbey   fix idr_find() lo...
591
  		down_read(&sem_ids(ns).rw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
592
  		if (cmd == SEM_INFO) {
e38935341   Kirill Korotaev   [PATCH] IPC names...
593
594
  			seminfo.semusz = sem_ids(ns).in_use;
  			seminfo.semaem = ns->used_sems;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595
596
597
598
  		} else {
  			seminfo.semusz = SEMUSZ;
  			seminfo.semaem = SEMAEM;
  		}
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
599
  		max_id = ipc_get_maxid(&sem_ids(ns));
3e148c799   Nadia Derbey   fix idr_find() lo...
600
  		up_read(&sem_ids(ns).rw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
601
602
603
604
  		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...
605
  	case IPC_STAT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
606
607
608
609
  	case SEM_STAT:
  	{
  		struct semid64_ds tbuf;
  		int id;
4b9fcb0ec   Pierre Peiffer   IPC/semaphores: c...
610
611
612
613
614
615
616
617
618
619
620
  		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
621
622
623
624
625
626
627
628
  
  		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...
629
  		memset(&tbuf, 0, sizeof(tbuf));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
  		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...
647
648
  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
649
650
651
652
653
654
655
  {
  	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...
656
657
658
  	sma = sem_lock_check(ns, semid);
  	if (IS_ERR(sma))
  		return PTR_ERR(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
659
660
  
  	nsems = sma->sem_nsems;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
  	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...
677
  			sem_getref_and_unlock(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
678
679
680
  
  			sem_io = ipc_alloc(sizeof(ushort)*nsems);
  			if(sem_io == NULL) {
6ff379721   Pierre Peiffer   IPC/semaphores: c...
681
  				sem_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
682
683
  				return -ENOMEM;
  			}
6ff379721   Pierre Peiffer   IPC/semaphores: c...
684
  			sem_lock_and_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
  			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...
704
  		sem_getref_and_unlock(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
705
706
707
708
  
  		if(nsems > SEMMSL_FAST) {
  			sem_io = ipc_alloc(sizeof(ushort)*nsems);
  			if(sem_io == NULL) {
6ff379721   Pierre Peiffer   IPC/semaphores: c...
709
  				sem_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
710
711
712
713
714
  				return -ENOMEM;
  			}
  		}
  
  		if (copy_from_user (sem_io, arg.array, nsems*sizeof(ushort))) {
6ff379721   Pierre Peiffer   IPC/semaphores: c...
715
  			sem_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
716
717
718
719
720
721
  			err = -EFAULT;
  			goto out_free;
  		}
  
  		for (i = 0; i < nsems; i++) {
  			if (sem_io[i] > SEMVMX) {
6ff379721   Pierre Peiffer   IPC/semaphores: c...
722
  				sem_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
723
724
725
726
  				err = -ERANGE;
  				goto out_free;
  			}
  		}
6ff379721   Pierre Peiffer   IPC/semaphores: c...
727
  		sem_lock_and_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
  		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];
  		for (un = sma->undo; un; un = un->id_next)
  			for (i = 0; i < nsems; i++)
  				un->semadj[i] = 0;
  		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
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
  	/* 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;
  		err = -ERANGE;
  		if (val > SEMVMX || val < 0)
  			goto out_unlock;
  
  		for (un = sma->undo; un; un = un->id_next)
  			un->semadj[semnum] = 0;
  		curr->semval = val;
b488893a3   Pavel Emelyanov   pid namespaces: c...
777
  		curr->sempid = task_tgid_vnr(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
778
779
780
781
782
783
784
785
786
787
788
789
790
791
  		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...
792
793
  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
794
795
796
  {
  	switch(version) {
  	case IPC_64:
016d7132f   Pierre Peiffer   IPC: get rid of t...
797
  		if (copy_from_user(out, buf, sizeof(*out)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
798
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
799
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
800
801
802
803
804
805
  	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...
806
807
808
  		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
809
810
811
812
813
814
815
  
  		return 0;
  	    }
  	default:
  		return -EINVAL;
  	}
  }
522bb2a2b   Pierre Peiffer   IPC/semaphores: m...
816
817
818
819
820
  /*
   * 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...
821
822
  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
823
824
825
  {
  	struct sem_array *sma;
  	int err;
016d7132f   Pierre Peiffer   IPC: get rid of t...
826
  	struct semid64_ds semid64;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
827
828
829
  	struct kern_ipc_perm *ipcp;
  
  	if(cmd == IPC_SET) {
016d7132f   Pierre Peiffer   IPC: get rid of t...
830
  		if (copy_semid_from_user(&semid64, arg.buf, version))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
831
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
832
  	}
073115d6b   Steve Grubb   [PATCH] Rework of...
833

a5f75e7f2   Pierre Peiffer   IPC: consolidate ...
834
835
836
  	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...
837

a5f75e7f2   Pierre Peiffer   IPC: consolidate ...
838
  	sma = container_of(ipcp, struct sem_array, sem_perm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
839
840
841
842
843
844
845
  
  	err = security_sem_semctl(sma, cmd);
  	if (err)
  		goto out_unlock;
  
  	switch(cmd){
  	case IPC_RMID:
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
846
  		freeary(ns, ipcp);
522bb2a2b   Pierre Peiffer   IPC/semaphores: m...
847
  		goto out_up;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
848
  	case IPC_SET:
8f4a3809c   Pierre Peiffer   IPC: introduce ip...
849
  		ipc_update_perm(&semid64.sem_perm, ipcp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
850
  		sma->sem_ctime = get_seconds();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
851
852
  		break;
  	default:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
853
  		err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
854
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
855
856
857
  
  out_unlock:
  	sem_unlock(sma);
522bb2a2b   Pierre Peiffer   IPC/semaphores: m...
858
859
  out_up:
  	up_write(&sem_ids(ns).rw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
860
861
862
863
864
865
866
  	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...
867
  	struct ipc_namespace *ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
868
869
870
871
872
  
  	if (semid < 0)
  		return -EINVAL;
  
  	version = ipc_parse_version(&cmd);
e38935341   Kirill Korotaev   [PATCH] IPC names...
873
  	ns = current->nsproxy->ipc_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
874
875
876
877
  
  	switch(cmd) {
  	case IPC_INFO:
  	case SEM_INFO:
4b9fcb0ec   Pierre Peiffer   IPC/semaphores: c...
878
  	case IPC_STAT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
879
  	case SEM_STAT:
4b9fcb0ec   Pierre Peiffer   IPC/semaphores: c...
880
  		err = semctl_nolock(ns, semid, cmd, version, arg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
881
882
883
884
885
886
  		return err;
  	case GETALL:
  	case GETVAL:
  	case GETPID:
  	case GETNCNT:
  	case GETZCNT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
887
888
  	case SETVAL:
  	case SETALL:
e38935341   Kirill Korotaev   [PATCH] IPC names...
889
  		err = semctl_main(ns,semid,semnum,cmd,version,arg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
890
891
892
  		return err;
  	case IPC_RMID:
  	case IPC_SET:
21a4826a7   Pierre Peiffer   IPC/semaphores: r...
893
  		err = semctl_down(ns, semid, cmd, version, arg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
894
895
896
897
898
  		return err;
  	default:
  		return -EINVAL;
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
899
900
901
902
903
904
905
906
907
908
909
910
911
912
  /* 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
913
914
915
  
  	undo_list = current->sysvsem.undo_list;
  	if (!undo_list) {
2453a3062   Matt Helsley   [PATCH] ipc: repl...
916
  		undo_list = kzalloc(sizeof(*undo_list), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
917
918
  		if (undo_list == NULL)
  			return -ENOMEM;
00a5dfdb9   Ingo Molnar   [PATCH] Fix semun...
919
  		spin_lock_init(&undo_list->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
  		atomic_set(&undo_list->refcnt, 1);
  		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)
  {
  	struct sem_undo **last, *un;
  
  	last = &ulp->proc_list;
  	un = *last;
  	while(un != NULL) {
  		if(un->semid==semid)
  			break;
  		if(un->semid==-1) {
  			*last=un->proc_next;
  			kfree(un);
  		} else {
  			last=&un->proc_next;
  		}
  		un=*last;
  	}
  	return un;
  }
e38935341   Kirill Korotaev   [PATCH] IPC names...
946
  static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
947
948
949
950
951
952
953
954
955
956
  {
  	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);
c530c6ac7   Pierre Peiffer   IPC: cleanup some...
957
  	spin_lock(&ulp->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
958
  	un = lookup_undo(ulp, semid);
c530c6ac7   Pierre Peiffer   IPC: cleanup some...
959
  	spin_unlock(&ulp->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
960
961
962
963
  	if (likely(un!=NULL))
  		goto out;
  
  	/* no undo structure around - allocate one. */
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

4668edc33   Burman Yan   [PATCH] kernel co...
970
  	new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
971
  	if (!new) {
6ff379721   Pierre Peiffer   IPC/semaphores: c...
972
  		sem_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
973
974
  		return ERR_PTR(-ENOMEM);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
975
976
  	new->semadj = (short *) &new[1];
  	new->semid = semid;
c530c6ac7   Pierre Peiffer   IPC: cleanup some...
977
  	spin_lock(&ulp->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
978
979
  	un = lookup_undo(ulp, semid);
  	if (un) {
c530c6ac7   Pierre Peiffer   IPC: cleanup some...
980
  		spin_unlock(&ulp->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
981
  		kfree(new);
6ff379721   Pierre Peiffer   IPC/semaphores: c...
982
  		sem_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
983
984
  		goto out;
  	}
6ff379721   Pierre Peiffer   IPC/semaphores: c...
985
  	sem_lock_and_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
986
987
  	if (sma->sem_perm.deleted) {
  		sem_unlock(sma);
c530c6ac7   Pierre Peiffer   IPC: cleanup some...
988
  		spin_unlock(&ulp->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
989
990
991
992
993
994
995
996
997
998
  		kfree(new);
  		un = ERR_PTR(-EIDRM);
  		goto out;
  	}
  	new->proc_next = ulp->proc_list;
  	ulp->proc_list = new;
  	new->id_next = sma->undo;
  	sma->undo = new;
  	sem_unlock(sma);
  	un = new;
c530c6ac7   Pierre Peiffer   IPC: cleanup some...
999
  	spin_unlock(&ulp->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
  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...
1012
  	int undos = 0, alter = 0, max;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1013
1014
  	struct sem_queue queue;
  	unsigned long jiffies_left = 0;
e38935341   Kirill Korotaev   [PATCH] IPC names...
1015
1016
1017
  	struct ipc_namespace *ns;
  
  	ns = current->nsproxy->ipc_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1018
1019
1020
  
  	if (nsops < 1 || semid < 0)
  		return -EINVAL;
e38935341   Kirill Korotaev   [PATCH] IPC names...
1021
  	if (nsops > ns->sc_semopm)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1022
1023
1024
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
  		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...
1050
1051
  			undos = 1;
  		if (sop->sem_op != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1052
1053
  			alter = 1;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1054
1055
1056
  
  retry_undos:
  	if (undos) {
e38935341   Kirill Korotaev   [PATCH] IPC names...
1057
  		un = find_undo(ns, semid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1058
1059
1060
1061
1062
1063
  		if (IS_ERR(un)) {
  			error = PTR_ERR(un);
  			goto out_free;
  		}
  	} else
  		un = NULL;
023a53557   Nadia Derbey   ipc: integrate ip...
1064
1065
1066
  	sma = sem_lock_check(ns, semid);
  	if (IS_ERR(sma)) {
  		error = PTR_ERR(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1067
  		goto out_free;
023a53557   Nadia Derbey   ipc: integrate ip...
1068
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1069
  	/*
023a53557   Nadia Derbey   ipc: integrate ip...
1070
  	 * semid identifiers are not unique - find_undo may have
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
  	 * allocated an undo structure, it was invalidated by an RMID
  	 * and now a new array with received the same id. Check and retry.
  	 */
  	if (un && un->semid == -1) {
  		sem_unlock(sma);
  		goto retry_undos;
  	}
  	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...
1089
  	error = try_atomic_semop (sma, sops, nsops, un, task_tgid_vnr(current));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
  	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.
  	 */
  		
  	queue.sma = sma;
  	queue.sops = sops;
  	queue.nsops = nsops;
  	queue.undo = un;
b488893a3   Pavel Emelyanov   pid namespaces: c...
1104
  	queue.pid = task_tgid_vnr(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
  	queue.id = semid;
  	queue.alter = alter;
  	if (alter)
  		append_to_queue(sma ,&queue);
  	else
  		prepend_to_queue(sma ,&queue);
  
  	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...
1133
  	sma = sem_lock(ns, semid);
023a53557   Nadia Derbey   ipc: integrate ip...
1134
  	if (IS_ERR(sma)) {
27315c96a   Eric Sesterhenn   BUG_ON() Conversi...
1135
  		BUG_ON(queue.prev != NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
  		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;
  	remove_from_queue(sma,&queue);
  	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
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
   */
  
  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
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
  		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)
  {
  	struct sem_undo_list *undo_list;
  	struct sem_undo *u, **up;
e38935341   Kirill Korotaev   [PATCH] IPC names...
1206
  	struct ipc_namespace *ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1207
1208
1209
1210
  
  	undo_list = tsk->sysvsem.undo_list;
  	if (!undo_list)
  		return;
9edff4ab1   Manfred Spraul   ipc: sysvsem: imp...
1211
  	tsk->sysvsem.undo_list = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1212
1213
1214
  
  	if (!atomic_dec_and_test(&undo_list->refcnt))
  		return;
e38935341   Kirill Korotaev   [PATCH] IPC names...
1215
  	ns = tsk->nsproxy->ipc_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
  	/* There's no need to hold the semundo list lock, as current
           * is the last task exiting for this undo list.
  	 */
  	for (up = &undo_list->proc_list; (u = *up); *up = u->proc_next, kfree(u)) {
  		struct sem_array *sma;
  		int nsems, i;
  		struct sem_undo *un, **unp;
  		int semid;
  	       
  		semid = u->semid;
  
  		if(semid == -1)
  			continue;
e38935341   Kirill Korotaev   [PATCH] IPC names...
1229
  		sma = sem_lock(ns, semid);
023a53557   Nadia Derbey   ipc: integrate ip...
1230
  		if (IS_ERR(sma))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1231
1232
1233
1234
  			continue;
  
  		if (u->semid == -1)
  			goto next_entry;
1b531f213   Nadia Derbey   ipc: remove unnee...
1235
  		BUG_ON(sem_checkid(sma, u->semid));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
  
  		/* remove u from the sma->undo list */
  		for (unp = &sma->undo; (un = *unp); unp = &un->id_next) {
  			if (u == un)
  				goto found;
  		}
  		printk ("exit_sem undo list error id=%d
  ", u->semid);
  		goto next_entry;
  found:
  		*unp = un->id_next;
  		/* perform adjustments registered in u */
  		nsems = sma->sem_nsems;
  		for (i = 0; i < nsems; i++) {
5f921ae96   Ingo Molnar   [PATCH] sem2mutex...
1250
  			struct sem * semaphore = &sma->sem_base[i];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1251
  			if (u->semadj[i]) {
5f921ae96   Ingo Molnar   [PATCH] sem2mutex...
1252
  				semaphore->semval += u->semadj[i];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
  				/*
  				 * 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...
1266
1267
1268
1269
  				if (semaphore->semval < 0)
  					semaphore->semval = 0;
  				if (semaphore->semval > SEMVMX)
  					semaphore->semval = SEMVMX;
b488893a3   Pavel Emelyanov   pid namespaces: c...
1270
  				semaphore->sempid = task_tgid_vnr(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
  			}
  		}
  		sma->sem_otime = get_seconds();
  		/* maybe some queued-up processes were waiting for this */
  		update_queue(sma);
  next_entry:
  		sem_unlock(sma);
  	}
  	kfree(undo_list);
  }
  
  #ifdef CONFIG_PROC_FS
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1283
  static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1284
  {
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1285
1286
1287
1288
1289
1290
  	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...
1291
  			  sma->sem_perm.id,
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1292
1293
1294
1295
1296
1297
1298
1299
  			  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
1300
1301
  }
  #endif