Blame view

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
90
  #include <asm/uaccess.h>
  #include "util.h"
e57940d71   Manfred Spraul   ipc/sem.c: remove...
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
  /* One semaphore structure for each semaphore in the system. */
  struct sem {
  	int	semval;		/* current value */
  	int	sempid;		/* pid of last operation */
  	struct list_head sem_pending; /* pending single-sop operations */
  };
  
  /* One queue for each sleeping process in the system. */
  struct sem_queue {
  	struct list_head	simple_list; /* queue of pending operations */
  	struct list_head	list;	 /* queue of pending operations */
  	struct task_struct	*sleeper; /* this process */
  	struct sem_undo		*undo;	 /* undo structure */
  	int			pid;	 /* process id of requesting process */
  	int			status;	 /* completion status of operation */
  	struct sembuf		*sops;	 /* array of pending operations */
  	int			nsops;	 /* number of operations */
  	int			alter;	 /* does *sops alter the array? */
  };
  
  /* Each task has a list of undo requests. They are executed automatically
   * when the process exits.
   */
  struct sem_undo {
  	struct list_head	list_proc;	/* per-process list: *
  						 * all undos from one process
  						 * rcu protected */
  	struct rcu_head		rcu;		/* rcu struct for sem_undo */
  	struct sem_undo_list	*ulp;		/* back ptr to sem_undo_list */
  	struct list_head	list_id;	/* per semaphore array list:
  						 * all undos for one array */
  	int			semid;		/* semaphore set identifier */
  	short			*semadj;	/* array of adjustments */
  						/* one per semaphore */
  };
  
  /* sem_undo_list controls shared access to the list of sem_undo structures
   * that may be shared among all a CLONE_SYSVSEM task group.
   */
  struct sem_undo_list {
  	atomic_t		refcnt;
  	spinlock_t		lock;
  	struct list_head	list_proc;
  };
ed2ddbf88   Pierre Peiffer   IPC: make struct ...
135
  #define sem_ids(ns)	((ns)->ids[IPC_SEM_IDS])
e38935341   Kirill Korotaev   [PATCH] IPC names...
136

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

7748dbfaa   Nadia Derbey   ipc: unify the sy...
140
  static int newary(struct ipc_namespace *, struct ipc_params *);
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
141
  static void freeary(struct ipc_namespace *, struct kern_ipc_perm *);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
  #ifdef CONFIG_PROC_FS
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
143
  static int sysvipc_sem_proc_show(struct seq_file *s, void *it);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
145
146
147
148
149
150
151
152
153
154
155
156
  #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...
157
158
159
160
  #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 ...
161
  void sem_init_ns(struct ipc_namespace *ns)
e38935341   Kirill Korotaev   [PATCH] IPC names...
162
  {
e38935341   Kirill Korotaev   [PATCH] IPC names...
163
164
165
166
167
  	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 ...
168
  	ipc_init_ids(&ns->ids[IPC_SEM_IDS]);
e38935341   Kirill Korotaev   [PATCH] IPC names...
169
  }
ae5e1b22f   Pavel Emelyanov   namespaces: move ...
170
  #ifdef CONFIG_IPC_NS
e38935341   Kirill Korotaev   [PATCH] IPC names...
171
172
  void sem_exit_ns(struct ipc_namespace *ns)
  {
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
173
  	free_ipcs(ns, &sem_ids(ns), freeary);
7d6feeb28   Serge E. Hallyn   ipc ns: fix memor...
174
  	idr_destroy(&ns->ids[IPC_SEM_IDS].ipcs_idr);
e38935341   Kirill Korotaev   [PATCH] IPC names...
175
  }
ae5e1b22f   Pavel Emelyanov   namespaces: move ...
176
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
178
179
  
  void __init sem_init (void)
  {
ed2ddbf88   Pierre Peiffer   IPC: make struct ...
180
  	sem_init_ns(&init_ipc_ns);
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
181
182
183
  	ipc_init_proc_interface("sysvipc/sem",
  				"       key      semid perms      nsems   uid   gid  cuid  cgid      otime      ctime
  ",
e38935341   Kirill Korotaev   [PATCH] IPC names...
184
  				IPC_SEM_IDS, sysvipc_sem_proc_show);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
  }
3e148c799   Nadia Derbey   fix idr_find() lo...
186
  /*
3e148c799   Nadia Derbey   fix idr_find() lo...
187
188
189
   * sem_lock_(check_) routines are called in the paths where the rw_mutex
   * is not held.
   */
023a53557   Nadia Derbey   ipc: integrate ip...
190
191
  static inline struct sem_array *sem_lock(struct ipc_namespace *ns, int id)
  {
03f02c765   Nadia Derbey   Storing ipcs into...
192
  	struct kern_ipc_perm *ipcp = ipc_lock(&sem_ids(ns), id);
b1ed88b47   Pierre Peiffer   IPC: fix error ch...
193
194
  	if (IS_ERR(ipcp))
  		return (struct sem_array *)ipcp;
03f02c765   Nadia Derbey   Storing ipcs into...
195
  	return container_of(ipcp, struct sem_array, sem_perm);
023a53557   Nadia Derbey   ipc: integrate ip...
196
197
198
199
200
  }
  
  static inline struct sem_array *sem_lock_check(struct ipc_namespace *ns,
  						int id)
  {
03f02c765   Nadia Derbey   Storing ipcs into...
201
  	struct kern_ipc_perm *ipcp = ipc_lock_check(&sem_ids(ns), id);
b1ed88b47   Pierre Peiffer   IPC: fix error ch...
202
203
  	if (IS_ERR(ipcp))
  		return (struct sem_array *)ipcp;
03f02c765   Nadia Derbey   Storing ipcs into...
204
  	return container_of(ipcp, struct sem_array, sem_perm);
023a53557   Nadia Derbey   ipc: integrate ip...
205
  }
6ff379721   Pierre Peiffer   IPC/semaphores: c...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
  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...
224
225
226
227
  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
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
  /*
   * 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...
243
   *   	  performing any operation on the sem array.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
   *   	* 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...
261
262
263
264
265
  /**
   * 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...
266
   * Called with sem_ids.rw_mutex held (as a writer)
f4566f048   Nadia Derbey   ipc: fix wrong co...
267
   */
7748dbfaa   Nadia Derbey   ipc: unify the sy...
268
  static int newary(struct ipc_namespace *ns, struct ipc_params *params)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
271
272
273
  {
  	int id;
  	int retval;
  	struct sem_array *sma;
  	int size;
7748dbfaa   Nadia Derbey   ipc: unify the sy...
274
275
276
  	key_t key = params->key;
  	int nsems = params->u.nsems;
  	int semflg = params->flg;
b97e820ff   Manfred Spraul   ipc/sem.c: add a ...
277
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
279
280
  
  	if (!nsems)
  		return -EINVAL;
e38935341   Kirill Korotaev   [PATCH] IPC names...
281
  	if (ns->used_sems + nsems > ns->sc_semmns)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
  		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...
300
  	id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni);
283bb7fad   Pierre Peiffer   IPC: fix error ca...
301
  	if (id < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
303
  		security_sem_free(sma);
  		ipc_rcu_putref(sma);
283bb7fad   Pierre Peiffer   IPC: fix error ca...
304
  		return id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305
  	}
e38935341   Kirill Korotaev   [PATCH] IPC names...
306
  	ns->used_sems += nsems;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
308
  
  	sma->sem_base = (struct sem *) &sma[1];
b97e820ff   Manfred Spraul   ipc/sem.c: add a ...
309
310
311
312
313
  
  	for (i = 0; i < nsems; i++)
  		INIT_LIST_HEAD(&sma->sem_base[i].sem_pending);
  
  	sma->complex_count = 0;
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
314
  	INIT_LIST_HEAD(&sma->sem_pending);
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
315
  	INIT_LIST_HEAD(&sma->list_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
317
318
  	sma->sem_nsems = nsems;
  	sma->sem_ctime = get_seconds();
  	sem_unlock(sma);
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
319
  	return sma->sem_perm.id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
  }
7748dbfaa   Nadia Derbey   ipc: unify the sy...
321

f4566f048   Nadia Derbey   ipc: fix wrong co...
322
  /*
3e148c799   Nadia Derbey   fix idr_find() lo...
323
   * Called with sem_ids.rw_mutex and ipcp locked.
f4566f048   Nadia Derbey   ipc: fix wrong co...
324
   */
03f02c765   Nadia Derbey   Storing ipcs into...
325
  static inline int sem_security(struct kern_ipc_perm *ipcp, int semflg)
7748dbfaa   Nadia Derbey   ipc: unify the sy...
326
  {
03f02c765   Nadia Derbey   Storing ipcs into...
327
328
329
330
  	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...
331
  }
f4566f048   Nadia Derbey   ipc: fix wrong co...
332
  /*
3e148c799   Nadia Derbey   fix idr_find() lo...
333
   * Called with sem_ids.rw_mutex and ipcp locked.
f4566f048   Nadia Derbey   ipc: fix wrong co...
334
   */
03f02c765   Nadia Derbey   Storing ipcs into...
335
336
  static inline int sem_more_checks(struct kern_ipc_perm *ipcp,
  				struct ipc_params *params)
7748dbfaa   Nadia Derbey   ipc: unify the sy...
337
  {
03f02c765   Nadia Derbey   Storing ipcs into...
338
339
340
341
  	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...
342
343
344
345
  		return -EINVAL;
  
  	return 0;
  }
d5460c997   Heiko Carstens   [CVE-2009-0029] S...
346
  SYSCALL_DEFINE3(semget, key_t, key, int, nsems, int, semflg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
  {
e38935341   Kirill Korotaev   [PATCH] IPC names...
348
  	struct ipc_namespace *ns;
7748dbfaa   Nadia Derbey   ipc: unify the sy...
349
350
  	struct ipc_ops sem_ops;
  	struct ipc_params sem_params;
e38935341   Kirill Korotaev   [PATCH] IPC names...
351
352
  
  	ns = current->nsproxy->ipc_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353

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

7748dbfaa   Nadia Derbey   ipc: unify the sy...
357
358
359
360
361
362
363
  	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
364

7748dbfaa   Nadia Derbey   ipc: unify the sy...
365
  	return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  /*
   * 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--;
  	}
  	
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
  	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;
  }
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
432
433
434
435
436
  /** wake_up_sem_queue_prepare(q, error): Prepare wake-up
   * @q: queue entry that must be signaled
   * @error: Error value for the signal
   *
   * Prepare the wake-up of the queue entry q.
d4212093d   Nick Piggin   ipc/sem.c: sem pr...
437
   */
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
438
439
  static void wake_up_sem_queue_prepare(struct list_head *pt,
  				struct sem_queue *q, int error)
d4212093d   Nick Piggin   ipc/sem.c: sem pr...
440
  {
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
441
442
443
444
445
446
447
  	if (list_empty(pt)) {
  		/*
  		 * Hold preempt off so that we don't get preempted and have the
  		 * wakee busy-wait until we're scheduled back on.
  		 */
  		preempt_disable();
  	}
d4212093d   Nick Piggin   ipc/sem.c: sem pr...
448
  	q->status = IN_WAKEUP;
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
  	q->pid = error;
  
  	list_add_tail(&q->simple_list, pt);
  }
  
  /**
   * wake_up_sem_queue_do(pt) - do the actual wake-up
   * @pt: list of tasks to be woken up
   *
   * Do the actual wake-up.
   * The function is called without any locks held, thus the semaphore array
   * could be destroyed already and the tasks can disappear as soon as the
   * status is set to the actual return code.
   */
  static void wake_up_sem_queue_do(struct list_head *pt)
  {
  	struct sem_queue *q, *t;
  	int did_something;
  
  	did_something = !list_empty(pt);
  	list_for_each_entry_safe(q, t, pt, simple_list) {
  		wake_up_process(q->sleeper);
  		/* q can disappear immediately after writing q->status. */
  		smp_wmb();
  		q->status = q->pid;
  	}
  	if (did_something)
  		preempt_enable();
d4212093d   Nick Piggin   ipc/sem.c: sem pr...
477
  }
b97e820ff   Manfred Spraul   ipc/sem.c: add a ...
478
479
480
481
482
483
484
485
  static void unlink_queue(struct sem_array *sma, struct sem_queue *q)
  {
  	list_del(&q->list);
  	if (q->nsops == 1)
  		list_del(&q->simple_list);
  	else
  		sma->complex_count--;
  }
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
  /** check_restart(sma, q)
   * @sma: semaphore array
   * @q: the operation that just completed
   *
   * update_queue is O(N^2) when it restarts scanning the whole queue of
   * waiting operations. Therefore this function checks if the restart is
   * really necessary. It is called after a previously waiting operation
   * was completed.
   */
  static int check_restart(struct sem_array *sma, struct sem_queue *q)
  {
  	struct sem *curr;
  	struct sem_queue *h;
  
  	/* if the operation didn't modify the array, then no restart */
  	if (q->alter == 0)
  		return 0;
  
  	/* pending complex operations are too difficult to analyse */
  	if (sma->complex_count)
  		return 1;
  
  	/* we were a sleeping complex operation. Too difficult */
  	if (q->nsops > 1)
  		return 1;
  
  	curr = sma->sem_base + q->sops[0].sem_num;
  
  	/* No-one waits on this queue */
  	if (list_empty(&curr->sem_pending))
  		return 0;
  
  	/* the new semaphore value */
  	if (curr->semval) {
  		/* It is impossible that someone waits for the new value:
  		 * - q is a previously sleeping simple operation that
  		 *   altered the array. It must be a decrement, because
  		 *   simple increments never sleep.
  		 * - The value is not 0, thus wait-for-zero won't proceed.
  		 * - If there are older (higher priority) decrements
  		 *   in the queue, then they have observed the original
  		 *   semval value and couldn't proceed. The operation
  		 *   decremented to value - thus they won't proceed either.
  		 */
  		BUG_ON(q->sops[0].sem_op >= 0);
  		return 0;
  	}
  	/*
  	 * semval is 0. Check if there are wait-for-zero semops.
  	 * They must be the first entries in the per-semaphore simple queue
  	 */
  	h = list_first_entry(&curr->sem_pending, struct sem_queue, simple_list);
  	BUG_ON(h->nsops != 1);
  	BUG_ON(h->sops[0].sem_num != q->sops[0].sem_num);
  
  	/* Yes, there is a wait-for-zero semop. Restart */
  	if (h->sops[0].sem_op == 0)
  		return 1;
  
  	/* Again - no-one is waiting for the new value. */
  	return 0;
  }
636c6be82   Manfred Spraul   ipc/sem.c: optimi...
548
549
550
551
552
  
  /**
   * update_queue(sma, semnum): Look for tasks that can be completed.
   * @sma: semaphore array.
   * @semnum: semaphore that was modified.
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
553
   * @pt: list head for the tasks that must be woken up.
636c6be82   Manfred Spraul   ipc/sem.c: optimi...
554
555
556
557
   *
   * update_queue must be called after a semaphore in a semaphore array
   * was modified. If multiple semaphore were modified, then @semnum
   * must be set to -1.
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
558
559
560
   * The tasks that must be woken up are added to @pt. The return code
   * is stored in q->pid.
   * The function return 1 if at least one semop was completed successfully.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
   */
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
562
  static int update_queue(struct sem_array *sma, int semnum, struct list_head *pt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563
  {
636c6be82   Manfred Spraul   ipc/sem.c: optimi...
564
565
566
567
  	struct sem_queue *q;
  	struct list_head *walk;
  	struct list_head *pending_list;
  	int offset;
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
568
  	int semop_completed = 0;
636c6be82   Manfred Spraul   ipc/sem.c: optimi...
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
  
  	/* if there are complex operations around, then knowing the semaphore
  	 * that was modified doesn't help us. Assume that multiple semaphores
  	 * were modified.
  	 */
  	if (sma->complex_count)
  		semnum = -1;
  
  	if (semnum == -1) {
  		pending_list = &sma->sem_pending;
  		offset = offsetof(struct sem_queue, list);
  	} else {
  		pending_list = &sma->sem_base[semnum].sem_pending;
  		offset = offsetof(struct sem_queue, simple_list);
  	}
9cad200c7   Nick Piggin   ipc/sem.c: sem us...
584
585
  
  again:
636c6be82   Manfred Spraul   ipc/sem.c: optimi...
586
587
  	walk = pending_list->next;
  	while (walk != pending_list) {
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
588
  		int error, restart;
636c6be82   Manfred Spraul   ipc/sem.c: optimi...
589
590
591
  
  		q = (struct sem_queue *)((char *)walk - offset);
  		walk = walk->next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
592

d987f8b21   Manfred Spraul   ipc/sem.c: optimi...
593
594
595
596
597
598
599
600
601
602
  		/* If we are scanning the single sop, per-semaphore list of
  		 * one semaphore and that semaphore is 0, then it is not
  		 * necessary to scan the "alter" entries: simple increments
  		 * that affect only one entry succeed immediately and cannot
  		 * be in the  per semaphore pending queue, and decrements
  		 * cannot be successful if the value is already 0.
  		 */
  		if (semnum != -1 && sma->sem_base[semnum].semval == 0 &&
  				q->alter)
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
603
604
605
606
  		error = try_atomic_semop(sma, q->sops, q->nsops,
  					 q->undo, q->pid);
  
  		/* Does q->sleeper still need to sleep? */
9cad200c7   Nick Piggin   ipc/sem.c: sem us...
607
608
  		if (error > 0)
  			continue;
b97e820ff   Manfred Spraul   ipc/sem.c: add a ...
609
  		unlink_queue(sma, q);
9cad200c7   Nick Piggin   ipc/sem.c: sem us...
610

0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
611
  		if (error) {
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
612
  			restart = 0;
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
613
614
  		} else {
  			semop_completed = 1;
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
615
  			restart = check_restart(sma, q);
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
616
  		}
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
617

0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
618
  		wake_up_sem_queue_prepare(pt, q, error);
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
619
  		if (restart)
9cad200c7   Nick Piggin   ipc/sem.c: sem us...
620
  			goto again;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
621
  	}
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
622
  	return semop_completed;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
623
  }
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
624
625
  /**
   * do_smart_update(sma, sops, nsops, otime, pt) - optimized update_queue
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
626
627
628
   * @sma: semaphore array
   * @sops: operations that were performed
   * @nsops: number of operations
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
629
630
   * @otime: force setting otime
   * @pt: list head of the tasks that must be woken up.
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
631
632
633
   *
   * do_smart_update() does the required called to update_queue, based on the
   * actual changes that were performed on the semaphore array.
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
634
635
636
   * Note that the function does not do the actual wake-up: the caller is
   * responsible for calling wake_up_sem_queue_do(@pt).
   * It is safe to perform this call after dropping all locks.
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
637
   */
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
638
639
  static void do_smart_update(struct sem_array *sma, struct sembuf *sops, int nsops,
  			int otime, struct list_head *pt)
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
640
641
642
643
  {
  	int i;
  
  	if (sma->complex_count || sops == NULL) {
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
644
645
646
  		if (update_queue(sma, -1, pt))
  			otime = 1;
  		goto done;
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
647
648
649
650
651
652
  	}
  
  	for (i = 0; i < nsops; i++) {
  		if (sops[i].sem_op > 0 ||
  			(sops[i].sem_op < 0 &&
  				sma->sem_base[sops[i].sem_num].semval == 0))
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
653
654
  			if (update_queue(sma, sops[i].sem_num, pt))
  				otime = 1;
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
655
  	}
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
656
657
658
  done:
  	if (otime)
  		sma->sem_otime = get_seconds();
fd5db4225   Manfred Spraul   ipc/sem.c: optimi...
659
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
  /* 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...
675
  	list_for_each_entry(q, &sma->sem_pending, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
676
677
678
679
680
681
682
683
684
685
686
  		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...
687

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688
689
690
691
692
693
  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...
694
  	list_for_each_entry(q, &sma->sem_pending, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
695
696
697
698
699
700
701
702
703
704
705
  		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...
706
707
708
  /* 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
709
   */
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
710
  static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
  {
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
712
713
  	struct sem_undo *un, *tu;
  	struct sem_queue *q, *tq;
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
714
  	struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm);
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
715
  	struct list_head tasks;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
716

380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
717
  	/* Free the existing undo structures for this semaphore set.  */
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
718
  	assert_spin_locked(&sma->sem_perm.lock);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
719
720
721
  	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
722
  		un->semid = -1;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
723
724
  		list_del_rcu(&un->list_proc);
  		spin_unlock(&un->ulp->lock);
693a8b6ee   Lai Jiangshan   ipc,rcu: Convert ...
725
  		kfree_rcu(un, rcu);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
726
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
727
728
  
  	/* Wake up all pending processes and let them fail with EIDRM. */
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
729
  	INIT_LIST_HEAD(&tasks);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
730
  	list_for_each_entry_safe(q, tq, &sma->sem_pending, list) {
b97e820ff   Manfred Spraul   ipc/sem.c: add a ...
731
  		unlink_queue(sma, q);
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
732
  		wake_up_sem_queue_prepare(&tasks, q, -EIDRM);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
733
  	}
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
734
735
  	/* Remove the semaphore set from the IDR */
  	sem_rmid(ns, sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
  	sem_unlock(sma);
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
737
  	wake_up_sem_queue_do(&tasks);
e38935341   Kirill Korotaev   [PATCH] IPC names...
738
  	ns->used_sems -= sma->sem_nsems;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
739
740
741
742
743
744
745
746
747
748
749
750
  	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;
982f7c2b2   Dan Rosenberg   sys_semctl: fix k...
751
  		memset(&out, 0, sizeof(out));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
752
753
754
755
756
757
758
759
760
761
762
763
  		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...
764
765
  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
766
  {
e5cc9c7b1   Amerigo Wang   ipc: remove unrea...
767
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
768
769
770
771
772
773
774
775
776
777
778
779
780
781
  	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...
782
783
784
785
  		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
786
787
788
789
  		seminfo.semvmx = SEMVMX;
  		seminfo.semmnu = SEMMNU;
  		seminfo.semmap = SEMMAP;
  		seminfo.semume = SEMUME;
3e148c799   Nadia Derbey   fix idr_find() lo...
790
  		down_read(&sem_ids(ns).rw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791
  		if (cmd == SEM_INFO) {
e38935341   Kirill Korotaev   [PATCH] IPC names...
792
793
  			seminfo.semusz = sem_ids(ns).in_use;
  			seminfo.semaem = ns->used_sems;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
794
795
796
797
  		} else {
  			seminfo.semusz = SEMUSZ;
  			seminfo.semaem = SEMAEM;
  		}
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
798
  		max_id = ipc_get_maxid(&sem_ids(ns));
3e148c799   Nadia Derbey   fix idr_find() lo...
799
  		up_read(&sem_ids(ns).rw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
800
801
802
803
  		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...
804
  	case IPC_STAT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
805
806
807
808
  	case SEM_STAT:
  	{
  		struct semid64_ds tbuf;
  		int id;
4b9fcb0ec   Pierre Peiffer   IPC/semaphores: c...
809
810
811
812
813
814
815
816
817
818
819
  		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
820
821
  
  		err = -EACCES;
b0e77598f   Serge E. Hallyn   userns: user name...
822
  		if (ipcperms(ns, &sma->sem_perm, S_IRUGO))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
823
824
825
826
827
  			goto out_unlock;
  
  		err = security_sem_semctl(sma, cmd);
  		if (err)
  			goto out_unlock;
023a53557   Nadia Derbey   ipc: integrate ip...
828
  		memset(&tbuf, 0, sizeof(tbuf));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
829
830
831
832
833
834
835
836
837
838
839
840
  		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;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
841
842
843
844
  out_unlock:
  	sem_unlock(sma);
  	return err;
  }
e38935341   Kirill Korotaev   [PATCH] IPC names...
845
846
  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
847
848
849
850
851
852
853
  {
  	struct sem_array *sma;
  	struct sem* curr;
  	int err;
  	ushort fast_sem_io[SEMMSL_FAST];
  	ushort* sem_io = fast_sem_io;
  	int nsems;
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
854
  	struct list_head tasks;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
855

023a53557   Nadia Derbey   ipc: integrate ip...
856
857
858
  	sma = sem_lock_check(ns, semid);
  	if (IS_ERR(sma))
  		return PTR_ERR(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
859

0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
860
  	INIT_LIST_HEAD(&tasks);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
861
  	nsems = sma->sem_nsems;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862
  	err = -EACCES;
b0e77598f   Serge E. Hallyn   userns: user name...
863
864
  	if (ipcperms(ns, &sma->sem_perm,
  			(cmd == SETVAL || cmd == SETALL) ? S_IWUGO : S_IRUGO))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
865
866
867
868
869
870
871
872
873
874
875
876
877
878
  		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...
879
  			sem_getref_and_unlock(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
880
881
882
  
  			sem_io = ipc_alloc(sizeof(ushort)*nsems);
  			if(sem_io == NULL) {
6ff379721   Pierre Peiffer   IPC/semaphores: c...
883
  				sem_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
884
885
  				return -ENOMEM;
  			}
6ff379721   Pierre Peiffer   IPC/semaphores: c...
886
  			sem_lock_and_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
  			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...
906
  		sem_getref_and_unlock(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
907
908
909
910
  
  		if(nsems > SEMMSL_FAST) {
  			sem_io = ipc_alloc(sizeof(ushort)*nsems);
  			if(sem_io == NULL) {
6ff379721   Pierre Peiffer   IPC/semaphores: c...
911
  				sem_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
912
913
914
915
916
  				return -ENOMEM;
  			}
  		}
  
  		if (copy_from_user (sem_io, arg.array, nsems*sizeof(ushort))) {
6ff379721   Pierre Peiffer   IPC/semaphores: c...
917
  			sem_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
918
919
920
921
922
923
  			err = -EFAULT;
  			goto out_free;
  		}
  
  		for (i = 0; i < nsems; i++) {
  			if (sem_io[i] > SEMVMX) {
6ff379721   Pierre Peiffer   IPC/semaphores: c...
924
  				sem_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
925
926
927
928
  				err = -ERANGE;
  				goto out_free;
  			}
  		}
6ff379721   Pierre Peiffer   IPC/semaphores: c...
929
  		sem_lock_and_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
930
931
932
933
934
935
936
937
  		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...
938
939
940
  
  		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
941
942
  			for (i = 0; i < nsems; i++)
  				un->semadj[i] = 0;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
943
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
944
945
  		sma->sem_ctime = get_seconds();
  		/* maybe some queued-up processes were waiting for this */
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
946
  		do_smart_update(sma, NULL, 0, 0, &tasks);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
947
948
949
  		err = 0;
  		goto out_unlock;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
  	/* 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...
975

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
976
977
978
  		err = -ERANGE;
  		if (val > SEMVMX || val < 0)
  			goto out_unlock;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
979
980
  		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
981
  			un->semadj[semnum] = 0;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
982

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
983
  		curr->semval = val;
b488893a3   Pavel Emelyanov   pid namespaces: c...
984
  		curr->sempid = task_tgid_vnr(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
985
986
  		sma->sem_ctime = get_seconds();
  		/* maybe some queued-up processes were waiting for this */
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
987
  		do_smart_update(sma, NULL, 0, 0, &tasks);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
988
989
990
991
992
993
  		err = 0;
  		goto out_unlock;
  	}
  	}
  out_unlock:
  	sem_unlock(sma);
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
994
  	wake_up_sem_queue_do(&tasks);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
995
996
997
998
999
  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...
1000
1001
  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
1002
1003
1004
  {
  	switch(version) {
  	case IPC_64:
016d7132f   Pierre Peiffer   IPC: get rid of t...
1005
  		if (copy_from_user(out, buf, sizeof(*out)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1006
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1007
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1008
1009
1010
1011
1012
1013
  	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...
1014
1015
1016
  		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
1017
1018
1019
1020
1021
1022
1023
  
  		return 0;
  	    }
  	default:
  		return -EINVAL;
  	}
  }
522bb2a2b   Pierre Peiffer   IPC/semaphores: m...
1024
1025
1026
1027
1028
  /*
   * 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...
1029
1030
  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
1031
1032
1033
  {
  	struct sem_array *sma;
  	int err;
016d7132f   Pierre Peiffer   IPC: get rid of t...
1034
  	struct semid64_ds semid64;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1035
1036
1037
  	struct kern_ipc_perm *ipcp;
  
  	if(cmd == IPC_SET) {
016d7132f   Pierre Peiffer   IPC: get rid of t...
1038
  		if (copy_semid_from_user(&semid64, arg.buf, version))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1039
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1040
  	}
073115d6b   Steve Grubb   [PATCH] Rework of...
1041

b0e77598f   Serge E. Hallyn   userns: user name...
1042
1043
  	ipcp = ipcctl_pre_down(ns, &sem_ids(ns), semid, cmd,
  			       &semid64.sem_perm, 0);
a5f75e7f2   Pierre Peiffer   IPC: consolidate ...
1044
1045
  	if (IS_ERR(ipcp))
  		return PTR_ERR(ipcp);
073115d6b   Steve Grubb   [PATCH] Rework of...
1046

a5f75e7f2   Pierre Peiffer   IPC: consolidate ...
1047
  	sma = container_of(ipcp, struct sem_array, sem_perm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1048
1049
1050
1051
1052
1053
1054
  
  	err = security_sem_semctl(sma, cmd);
  	if (err)
  		goto out_unlock;
  
  	switch(cmd){
  	case IPC_RMID:
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
1055
  		freeary(ns, ipcp);
522bb2a2b   Pierre Peiffer   IPC/semaphores: m...
1056
  		goto out_up;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1057
  	case IPC_SET:
8f4a3809c   Pierre Peiffer   IPC: introduce ip...
1058
  		ipc_update_perm(&semid64.sem_perm, ipcp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1059
  		sma->sem_ctime = get_seconds();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1060
1061
  		break;
  	default:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1062
  		err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1063
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1064
1065
1066
  
  out_unlock:
  	sem_unlock(sma);
522bb2a2b   Pierre Peiffer   IPC/semaphores: m...
1067
1068
  out_up:
  	up_write(&sem_ids(ns).rw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1069
1070
  	return err;
  }
6673e0c3f   Heiko Carstens   [CVE-2009-0029] S...
1071
  SYSCALL_DEFINE(semctl)(int semid, int semnum, int cmd, union semun arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1072
1073
1074
  {
  	int err = -EINVAL;
  	int version;
e38935341   Kirill Korotaev   [PATCH] IPC names...
1075
  	struct ipc_namespace *ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1076
1077
1078
1079
1080
  
  	if (semid < 0)
  		return -EINVAL;
  
  	version = ipc_parse_version(&cmd);
e38935341   Kirill Korotaev   [PATCH] IPC names...
1081
  	ns = current->nsproxy->ipc_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1082
1083
1084
1085
  
  	switch(cmd) {
  	case IPC_INFO:
  	case SEM_INFO:
4b9fcb0ec   Pierre Peiffer   IPC/semaphores: c...
1086
  	case IPC_STAT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1087
  	case SEM_STAT:
4b9fcb0ec   Pierre Peiffer   IPC/semaphores: c...
1088
  		err = semctl_nolock(ns, semid, cmd, version, arg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1089
1090
1091
1092
1093
1094
  		return err;
  	case GETALL:
  	case GETVAL:
  	case GETPID:
  	case GETNCNT:
  	case GETZCNT:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1095
1096
  	case SETVAL:
  	case SETALL:
e38935341   Kirill Korotaev   [PATCH] IPC names...
1097
  		err = semctl_main(ns,semid,semnum,cmd,version,arg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1098
1099
1100
  		return err;
  	case IPC_RMID:
  	case IPC_SET:
21a4826a7   Pierre Peiffer   IPC/semaphores: r...
1101
  		err = semctl_down(ns, semid, cmd, version, arg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1102
1103
1104
1105
1106
  		return err;
  	default:
  		return -EINVAL;
  	}
  }
6673e0c3f   Heiko Carstens   [CVE-2009-0029] S...
1107
1108
1109
1110
1111
1112
1113
  #ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
  asmlinkage long SyS_semctl(int semid, int semnum, int cmd, union semun arg)
  {
  	return SYSC_semctl((int) semid, (int) semnum, (int) cmd, arg);
  }
  SYSCALL_ALIAS(sys_semctl, SyS_semctl);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1114

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
  /* 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
1129
1130
1131
  
  	undo_list = current->sysvsem.undo_list;
  	if (!undo_list) {
2453a3062   Matt Helsley   [PATCH] ipc: repl...
1132
  		undo_list = kzalloc(sizeof(*undo_list), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1133
1134
  		if (undo_list == NULL)
  			return -ENOMEM;
00a5dfdb9   Ingo Molnar   [PATCH] Fix semun...
1135
  		spin_lock_init(&undo_list->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1136
  		atomic_set(&undo_list->refcnt, 1);
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1137
  		INIT_LIST_HEAD(&undo_list->list_proc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1138
1139
1140
1141
1142
  		current->sysvsem.undo_list = undo_list;
  	}
  	*undo_listp = undo_list;
  	return 0;
  }
bf17bb717   Nick Piggin   ipc/sem.c: sem op...
1143
  static struct sem_undo *__lookup_undo(struct sem_undo_list *ulp, int semid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1144
  {
bf17bb717   Nick Piggin   ipc/sem.c: sem op...
1145
  	struct sem_undo *un;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1146

bf17bb717   Nick Piggin   ipc/sem.c: sem op...
1147
1148
1149
  	list_for_each_entry_rcu(un, &ulp->list_proc, list_proc) {
  		if (un->semid == semid)
  			return un;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1150
  	}
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1151
  	return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1152
  }
bf17bb717   Nick Piggin   ipc/sem.c: sem op...
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
  static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid)
  {
  	struct sem_undo *un;
  
    	assert_spin_locked(&ulp->lock);
  
  	un = __lookup_undo(ulp, semid);
  	if (un) {
  		list_del_rcu(&un->list_proc);
  		list_add_rcu(&un->list_proc, &ulp->list_proc);
  	}
  	return un;
  }
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1166
1167
1168
1169
1170
1171
1172
1173
  /**
   * 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...
1174
1175
   * Lifetime-rules: sem_undo is rcu-protected, on success, the function
   * performs a rcu_read_lock().
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1176
1177
   */
  static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
  {
  	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...
1188
  	rcu_read_lock();
c530c6ac7   Pierre Peiffer   IPC: cleanup some...
1189
  	spin_lock(&ulp->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1190
  	un = lookup_undo(ulp, semid);
c530c6ac7   Pierre Peiffer   IPC: cleanup some...
1191
  	spin_unlock(&ulp->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1192
1193
  	if (likely(un!=NULL))
  		goto out;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1194
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1195
1196
  
  	/* no undo structure around - allocate one. */
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1197
  	/* step 1: figure out the size of the semaphore array */
023a53557   Nadia Derbey   ipc: integrate ip...
1198
1199
  	sma = sem_lock_check(ns, semid);
  	if (IS_ERR(sma))
4de85cd6d   Julia Lawall   ipc/sem.c: use ER...
1200
  		return ERR_CAST(sma);
023a53557   Nadia Derbey   ipc: integrate ip...
1201

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1202
  	nsems = sma->sem_nsems;
6ff379721   Pierre Peiffer   IPC/semaphores: c...
1203
  	sem_getref_and_unlock(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1204

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

380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1212
  	/* step 3: Acquire the lock on semaphore array */
6ff379721   Pierre Peiffer   IPC/semaphores: c...
1213
  	sem_lock_and_putref(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1214
1215
  	if (sma->sem_perm.deleted) {
  		sem_unlock(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1216
1217
1218
1219
  		kfree(new);
  		un = ERR_PTR(-EIDRM);
  		goto out;
  	}
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
  	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...
1230
1231
  	/* step 5: initialize & link new undo structure */
  	new->semadj = (short *) &new[1];
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1232
  	new->ulp = ulp;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1233
1234
  	new->semid = semid;
  	assert_spin_locked(&ulp->lock);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1235
  	list_add_rcu(&new->list_proc, &ulp->list_proc);
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1236
1237
  	assert_spin_locked(&sma->sem_perm.lock);
  	list_add(&new->list_id, &sma->list_id);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1238
  	un = new;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1239

380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1240
  success:
c530c6ac7   Pierre Peiffer   IPC: cleanup some...
1241
  	spin_unlock(&ulp->lock);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1242
1243
  	rcu_read_lock();
  	sem_unlock(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1244
1245
1246
  out:
  	return un;
  }
c61284e99   Manfred Spraul   ipc/sem.c: bugfix...
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
  
  /**
   * get_queue_result - Retrieve the result code from sem_queue
   * @q: Pointer to queue structure
   *
   * Retrieve the return code from the pending queue. If IN_WAKEUP is found in
   * q->status, then we must loop until the value is replaced with the final
   * value: This may happen if a task is woken up by an unrelated event (e.g.
   * signal) and in parallel the task is woken up by another task because it got
   * the requested semaphores.
   *
   * The function can be called with or without holding the semaphore spinlock.
   */
  static int get_queue_result(struct sem_queue *q)
  {
  	int error;
  
  	error = q->status;
  	while (unlikely(error == IN_WAKEUP)) {
  		cpu_relax();
  		error = q->status;
  	}
  
  	return error;
  }
d5460c997   Heiko Carstens   [CVE-2009-0029] S...
1272
1273
  SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
  		unsigned, nsops, const struct timespec __user *, timeout)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1274
1275
1276
1277
1278
1279
  {
  	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...
1280
  	int undos = 0, alter = 0, max;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1281
1282
  	struct sem_queue queue;
  	unsigned long jiffies_left = 0;
e38935341   Kirill Korotaev   [PATCH] IPC names...
1283
  	struct ipc_namespace *ns;
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
1284
  	struct list_head tasks;
e38935341   Kirill Korotaev   [PATCH] IPC names...
1285
1286
  
  	ns = current->nsproxy->ipc_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1287
1288
1289
  
  	if (nsops < 1 || semid < 0)
  		return -EINVAL;
e38935341   Kirill Korotaev   [PATCH] IPC names...
1290
  	if (nsops > ns->sc_semopm)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
  		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...
1319
1320
  			undos = 1;
  		if (sop->sem_op != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1321
1322
  			alter = 1;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1323

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1324
  	if (undos) {
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1325
  		un = find_alloc_undo(ns, semid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1326
1327
1328
1329
1330
1331
  		if (IS_ERR(un)) {
  			error = PTR_ERR(un);
  			goto out_free;
  		}
  	} else
  		un = NULL;
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
1332
  	INIT_LIST_HEAD(&tasks);
023a53557   Nadia Derbey   ipc: integrate ip...
1333
1334
  	sma = sem_lock_check(ns, semid);
  	if (IS_ERR(sma)) {
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1335
1336
  		if (un)
  			rcu_read_unlock();
023a53557   Nadia Derbey   ipc: integrate ip...
1337
  		error = PTR_ERR(sma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1338
  		goto out_free;
023a53557   Nadia Derbey   ipc: integrate ip...
1339
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1340
  	/*
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1341
  	 * semid identifiers are not unique - find_alloc_undo may have
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1342
  	 * allocated an undo structure, it was invalidated by an RMID
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1343
  	 * and now a new array with received the same id. Check and fail.
25985edce   Lucas De Marchi   Fix common misspe...
1344
  	 * This case can be detected checking un->semid. The existence of
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1345
  	 * "un" itself is guaranteed by rcu.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1346
  	 */
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1347
  	error = -EIDRM;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
  	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...
1364

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1365
1366
1367
1368
1369
  	error = -EFBIG;
  	if (max >= sma->sem_nsems)
  		goto out_unlock_free;
  
  	error = -EACCES;
b0e77598f   Serge E. Hallyn   userns: user name...
1370
  	if (ipcperms(ns, &sma->sem_perm, alter ? S_IWUGO : S_IRUGO))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1371
1372
1373
1374
1375
  		goto out_unlock_free;
  
  	error = security_sem_semop(sma, sops, nsops, alter);
  	if (error)
  		goto out_unlock_free;
b488893a3   Pavel Emelyanov   pid namespaces: c...
1376
  	error = try_atomic_semop (sma, sops, nsops, un, task_tgid_vnr(current));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1377
1378
  	if (error <= 0) {
  		if (alter && error == 0)
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
1379
  			do_smart_update(sma, sops, nsops, 1, &tasks);
636c6be82   Manfred Spraul   ipc/sem.c: optimi...
1380

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1381
1382
1383
1384
1385
1386
1387
  		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
1388
1389
1390
  	queue.sops = sops;
  	queue.nsops = nsops;
  	queue.undo = un;
b488893a3   Pavel Emelyanov   pid namespaces: c...
1391
  	queue.pid = task_tgid_vnr(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1392
1393
  	queue.alter = alter;
  	if (alter)
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
1394
  		list_add_tail(&queue.list, &sma->sem_pending);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1395
  	else
a1193f8ec   Manfred Spraul   ipc/sem.c: conver...
1396
  		list_add(&queue.list, &sma->sem_pending);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1397

b97e820ff   Manfred Spraul   ipc/sem.c: add a ...
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
  	if (nsops == 1) {
  		struct sem *curr;
  		curr = &sma->sem_base[sops->sem_num];
  
  		if (alter)
  			list_add_tail(&queue.simple_list, &curr->sem_pending);
  		else
  			list_add(&queue.simple_list, &curr->sem_pending);
  	} else {
  		INIT_LIST_HEAD(&queue.simple_list);
  		sma->complex_count++;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1410
1411
  	queue.status = -EINTR;
  	queue.sleeper = current;
0b0577f60   Manfred Spraul   ipc/sem.c: handle...
1412
1413
  
  sleep_again:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1414
1415
1416
1417
1418
1419
1420
  	current->state = TASK_INTERRUPTIBLE;
  	sem_unlock(sma);
  
  	if (timeout)
  		jiffies_left = schedule_timeout(jiffies_left);
  	else
  		schedule();
c61284e99   Manfred Spraul   ipc/sem.c: bugfix...
1421
  	error = get_queue_result(&queue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1422
1423
1424
  
  	if (error != -EINTR) {
  		/* fast path: update_queue already obtained all requested
c61284e99   Manfred Spraul   ipc/sem.c: bugfix...
1425
1426
1427
1428
1429
1430
1431
  		 * resources.
  		 * Perform a smp_mb(): User space could assume that semop()
  		 * is a memory barrier: Without the mb(), the cpu could
  		 * speculatively read in user space stale data that was
  		 * overwritten by the previous owner of the semaphore.
  		 */
  		smp_mb();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1432
1433
  		goto out_free;
  	}
e38935341   Kirill Korotaev   [PATCH] IPC names...
1434
  	sma = sem_lock(ns, semid);
d694ad62b   Manfred Spraul   ipc/sem.c: fix ra...
1435
1436
1437
1438
1439
1440
1441
1442
1443
  
  	/*
  	 * Wait until it's guaranteed that no wakeup_sem_queue_do() is ongoing.
  	 */
  	error = get_queue_result(&queue);
  
  	/*
  	 * Array removed? If yes, leave without sem_unlock().
  	 */
023a53557   Nadia Derbey   ipc: integrate ip...
1444
  	if (IS_ERR(sma)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1445
1446
  		goto out_free;
  	}
c61284e99   Manfred Spraul   ipc/sem.c: bugfix...
1447

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1448
  	/*
d694ad62b   Manfred Spraul   ipc/sem.c: fix ra...
1449
1450
  	 * If queue.status != -EINTR we are woken up by another process.
  	 * Leave without unlink_queue(), but with sem_unlock().
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1451
  	 */
c61284e99   Manfred Spraul   ipc/sem.c: bugfix...
1452

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1453
1454
1455
1456
1457
1458
1459
1460
1461
  	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;
0b0577f60   Manfred Spraul   ipc/sem.c: handle...
1462
1463
1464
1465
1466
1467
  
  	/*
  	 * If the wakeup was spurious, just retry
  	 */
  	if (error == -EINTR && !signal_pending(current))
  		goto sleep_again;
b97e820ff   Manfred Spraul   ipc/sem.c: add a ...
1468
  	unlink_queue(sma, &queue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1469
1470
1471
  
  out_unlock_free:
  	sem_unlock(sma);
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
1472
1473
  
  	wake_up_sem_queue_do(&tasks);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1474
1475
1476
1477
1478
  out_free:
  	if(sops != fast_sops)
  		kfree(sops);
  	return error;
  }
d5460c997   Heiko Carstens   [CVE-2009-0029] S...
1479
1480
  SYSCALL_DEFINE3(semop, int, semid, struct sembuf __user *, tsops,
  		unsigned, nsops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1481
1482
1483
1484
1485
1486
  {
  	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
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
   */
  
  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
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
  		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...
1520
  	struct sem_undo_list *ulp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1521

4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1522
1523
  	ulp = tsk->sysvsem.undo_list;
  	if (!ulp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1524
  		return;
9edff4ab1   Manfred Spraul   ipc: sysvsem: imp...
1525
  	tsk->sysvsem.undo_list = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1526

4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1527
  	if (!atomic_dec_and_test(&ulp->refcnt))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1528
  		return;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1529
  	for (;;) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1530
  		struct sem_array *sma;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1531
  		struct sem_undo *un;
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
1532
  		struct list_head tasks;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1533
  		int semid;
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1534
  		int i;
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1535
  		rcu_read_lock();
05725f7eb   Jiri Pirko   rculist: use list...
1536
1537
  		un = list_entry_rcu(ulp->list_proc.next,
  				    struct sem_undo, list_proc);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1538
1539
1540
1541
1542
  		if (&un->list_proc == &ulp->list_proc)
  			semid = -1;
  		 else
  			semid = un->semid;
  		rcu_read_unlock();
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1543

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

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

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

bf17bb717   Nick Piggin   ipc/sem.c: sem op...
1553
  		un = __lookup_undo(ulp, semid);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1554
1555
1556
1557
1558
1559
1560
1561
1562
  		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...
1563
1564
  		assert_spin_locked(&sma->sem_perm.lock);
  		list_del(&un->list_id);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1565
1566
1567
  		spin_lock(&ulp->lock);
  		list_del_rcu(&un->list_proc);
  		spin_unlock(&ulp->lock);
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1568
1569
  		/* perform adjustments registered in un */
  		for (i = 0; i < sma->sem_nsems; i++) {
5f921ae96   Ingo Molnar   [PATCH] sem2mutex...
1570
  			struct sem * semaphore = &sma->sem_base[i];
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1571
1572
  			if (un->semadj[i]) {
  				semaphore->semval += un->semadj[i];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
  				/*
  				 * 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...
1586
1587
1588
1589
  				if (semaphore->semval < 0)
  					semaphore->semval = 0;
  				if (semaphore->semval > SEMVMX)
  					semaphore->semval = SEMVMX;
b488893a3   Pavel Emelyanov   pid namespaces: c...
1590
  				semaphore->sempid = task_tgid_vnr(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1591
1592
  			}
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1593
  		/* maybe some queued-up processes were waiting for this */
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
1594
1595
  		INIT_LIST_HEAD(&tasks);
  		do_smart_update(sma, NULL, 0, 1, &tasks);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1596
  		sem_unlock(sma);
0a2b9d4c7   Manfred Spraul   ipc/sem.c: move w...
1597
  		wake_up_sem_queue_do(&tasks);
380af1b33   Manfred Spraul   ipc/sem.c: rewrit...
1598

693a8b6ee   Lai Jiangshan   ipc,rcu: Convert ...
1599
  		kfree_rcu(un, rcu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1600
  	}
4daa28f6d   Manfred Spraul   ipc/sem.c: conver...
1601
  	kfree(ulp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1602
1603
1604
  }
  
  #ifdef CONFIG_PROC_FS
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1605
  static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1606
  {
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1607
1608
1609
  	struct sem_array *sma = it;
  
  	return seq_printf(s,
b97e820ff   Manfred Spraul   ipc/sem.c: add a ...
1610
1611
  			  "%10d %10d  %4o %10u %5u %5u %5u %5u %10lu %10lu
  ",
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1612
  			  sma->sem_perm.key,
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
1613
  			  sma->sem_perm.id,
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1614
1615
1616
1617
1618
1619
1620
1621
  			  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
1622
1623
  }
  #endif