This source file includes following definitions.
- sem_init
- findkey
- newary
- sys_semget
- freeary
- sys_semctl
- sys_semop
- sem_exit
1
2
3
4
5
6 #include <linux/errno.h>
7 #include <asm/segment.h>
8 #include <linux/string.h>
9 #include <linux/sched.h>
10 #include <linux/sem.h>
11 #include <linux/ipc.h>
12
13 extern int ipcperms (struct ipc_perm *ipcp, short semflg);
14 static int newary (key_t, int, int);
15 static int findkey (key_t key);
16 static void freeary (int id);
17
18 static struct semid_ds *semary[SEMMNI];
19 static int used_sems = 0, used_semids = 0;
20 static struct wait_queue *sem_lock = NULL;
21 static int sem_seq = 0;
22 static int max_semid = 0;
23
24 void sem_init (void)
25 {
26 int i=0;
27
28 sem_lock = NULL;
29 used_sems = used_semids = max_semid = sem_seq = 0;
30 for (i=0; i < SEMMNI; i++)
31 semary[i] = (struct semid_ds *) IPC_UNUSED;
32 return;
33 }
34
35 static int findkey (key_t key)
36 {
37 int id;
38 struct semid_ds *sma;
39
40 for (id=0; id <= max_semid; id++) {
41 while ((sma = semary[id]) == IPC_NOID)
42 interruptible_sleep_on (&sem_lock);
43 if (sma == IPC_UNUSED)
44 continue;
45 if (key == sma->sem_perm.key)
46 return id;
47 }
48 return -1;
49 }
50
51 static int newary (key_t key, int nsems, int semflg)
52 {
53 int id;
54 struct semid_ds *sma;
55 struct ipc_perm *ipcp;
56 int size;
57
58 if (!nsems)
59 return -EINVAL;
60 if (used_sems + nsems > SEMMNS)
61 return -ENOSPC;
62 for (id=0; id < SEMMNI; id++)
63 if (semary[id] == IPC_UNUSED) {
64 semary[id] = (struct semid_ds *) IPC_NOID;
65 goto found;
66 }
67 return -ENOSPC;
68 found:
69 size = sizeof (*sma) + nsems * sizeof (struct sem);
70 used_sems += nsems;
71 sma = (struct semid_ds *) kmalloc (size, GFP_KERNEL);
72 if (!sma) {
73 semary[id] = (struct semid_ds *) IPC_UNUSED;
74 used_sems -= nsems;
75 if (sem_lock)
76 wake_up (&sem_lock);
77 return -ENOMEM;
78 }
79 memset (sma, 0, size);
80 sma->sem_base = (struct sem *) &sma[1];
81 ipcp = &sma->sem_perm;
82 ipcp->mode = (semflg & 0x01FF);
83 ipcp->key = key;
84 ipcp->cuid = ipcp->uid = current->euid;
85 ipcp->gid = ipcp->cgid = current->egid;
86 ipcp->seq = sem_seq;
87 sma->eventn = sma->eventz = NULL;
88 sma->sem_nsems = nsems;
89 sma->sem_ctime = CURRENT_TIME;
90 if (id > max_semid)
91 max_semid = id;
92 used_semids++;
93 semary[id] = sma;
94 if (sem_lock)
95 wake_up (&sem_lock);
96 return sem_seq * SEMMNI + id;
97 }
98
99 int sys_semget (key_t key, int nsems, int semflg)
100 {
101 int id;
102 struct semid_ds *sma;
103
104 if (nsems < 0 || nsems > SEMMSL)
105 return -EINVAL;
106 if (key == IPC_PRIVATE)
107 return newary(key, nsems, semflg);
108 if ((id = findkey (key)) == -1) {
109 if (!(semflg & IPC_CREAT))
110 return -ENOENT;
111 return newary(key, nsems, semflg);
112 }
113 if (semflg & IPC_CREAT && semflg & IPC_EXCL)
114 return -EEXIST;
115 sma = semary[id];
116 if (nsems > sma->sem_nsems)
117 return -EINVAL;
118 if (ipcperms(&sma->sem_perm, semflg))
119 return -EACCES;
120 return sma->sem_perm.seq*SEMMNI + id;
121 }
122
123 static void freeary (int id)
124 {
125 struct semid_ds *sma = semary[id];
126 struct sem_undo *un;
127
128 sma->sem_perm.seq++;
129 if ((int)((++sem_seq + 1) * SEMMNI) < 0)
130 sem_seq = 0;
131 used_sems -= sma->sem_nsems;
132 if (id == max_semid)
133 while (max_semid && (semary[--max_semid] == IPC_UNUSED));
134 semary[id] = (struct semid_ds *) IPC_UNUSED;
135 used_semids--;
136 for (un=sma->undo; un; un=un->id_next)
137 un->semadj = 0;
138 while (sma->eventz || sma->eventn) {
139 if (sma->eventz)
140 wake_up (&sma->eventz);
141 if (sma->eventn)
142 wake_up (&sma->eventn);
143 schedule();
144 }
145 kfree_s (sma, sizeof (*sma) + sma->sem_nsems * sizeof (struct sem));
146 return;
147 }
148
149 int sys_semctl (int semid, int semnum, int cmd, void *arg)
150 {
151 int i, id, val = 0;
152 struct semid_ds *sma, *buf = NULL, tbuf;
153 struct ipc_perm *ipcp;
154 struct sem *curr;
155 struct sem_undo *un;
156 ushort nsems, *array = NULL;
157 ushort sem_io[SEMMSL];
158
159 if (semid < 0 || semnum < 0 || cmd < 0)
160 return -EINVAL;
161
162 switch (cmd) {
163 case IPC_INFO:
164 case SEM_INFO:
165 {
166 struct seminfo seminfo, *tmp;
167 if (!arg || ! (tmp = (struct seminfo *) get_fs_long((int *)arg)))
168 return -EFAULT;
169 seminfo.semmni = SEMMNI;
170 seminfo.semmns = SEMMNS;
171 seminfo.semmsl = SEMMSL;
172 seminfo.semopm = SEMOPM;
173 seminfo.semvmx = SEMVMX;
174 seminfo.semmnu = SEMMNU;
175 seminfo.semmap = SEMMAP;
176 seminfo.semume = SEMUME;
177 seminfo.semusz = SEMUSZ;
178 seminfo.semaem = SEMAEM;
179 if (cmd == SEM_INFO) {
180 seminfo.semusz = used_semids;
181 seminfo.semaem = used_sems;
182 }
183 i= verify_area(VERIFY_WRITE, tmp, sizeof(struct seminfo));
184 if (i)
185 return i;
186 memcpy_tofs (tmp, &seminfo, sizeof(struct seminfo));
187 return max_semid;
188 }
189
190 case SEM_STAT:
191 if (!arg || ! (buf = (struct semid_ds *) get_fs_long((int *) arg)))
192 return -EFAULT;
193 i = verify_area (VERIFY_WRITE, buf, sizeof (*sma));
194 if (i)
195 return i;
196 if (semid > max_semid)
197 return -EINVAL;
198 sma = semary[semid];
199 if (sma == IPC_UNUSED || sma == IPC_NOID)
200 return -EINVAL;
201 if (ipcperms (&sma->sem_perm, 0444))
202 return -EACCES;
203 id = semid + sma->sem_perm.seq * SEMMNI;
204 memcpy_tofs (buf, sma, sizeof(*sma));
205 return id;
206 }
207
208 id = semid % SEMMNI;
209 sma = semary [id];
210 if (sma == IPC_UNUSED || sma == IPC_NOID)
211 return -EINVAL;
212 ipcp = &sma->sem_perm;
213 nsems = sma->sem_nsems;
214 if (ipcp->seq != semid / SEMMNI)
215 return -EIDRM;
216 if (semnum >= nsems)
217 return -EINVAL;
218 curr = &sma->sem_base[semnum];
219
220 switch (cmd) {
221 case GETVAL:
222 case GETPID:
223 case GETNCNT:
224 case GETZCNT:
225 case GETALL:
226 if (ipcperms (ipcp, 0444))
227 return -EACCES;
228 switch (cmd) {
229 case GETVAL : return curr->semval;
230 case GETPID : return curr->sempid;
231 case GETNCNT: return curr->semncnt;
232 case GETZCNT: return curr->semzcnt;
233 case GETALL:
234 if (!arg || ! (array = (ushort *) get_fs_long((int *) arg)))
235 return -EFAULT;
236 i = verify_area (VERIFY_WRITE, array, nsems* sizeof(short));
237 if (i)
238 return i;
239 }
240 break;
241 case SETVAL:
242 if (!arg)
243 return -EFAULT;
244 if ((val = (int) get_fs_long ((int *) arg)) > SEMVMX || val < 0)
245 return -ERANGE;
246 break;
247 case IPC_RMID:
248 if (suser() || current->euid == ipcp->cuid ||
249 current->euid == ipcp->uid) {
250 freeary (id);
251 return 0;
252 }
253 return -EPERM;
254 case SETALL:
255 if (!arg || ! (array = (ushort *) get_fs_long ((int *) arg)) )
256 return -EFAULT;
257 if ((i = verify_area (VERIFY_READ, array, sizeof tbuf)))
258 return i;
259 memcpy_fromfs (sem_io, array, nsems*sizeof(ushort));
260 for (i=0; i< nsems; i++)
261 if (sem_io[i] > SEMVMX)
262 return -ERANGE;
263 break;
264 case IPC_STAT:
265 if (!arg || !(buf = (struct semid_ds *) get_fs_long((int *) arg)))
266 return -EFAULT;
267 if ((i = verify_area (VERIFY_WRITE, arg, sizeof tbuf)))
268 return i;
269 break;
270 case IPC_SET:
271 if (!arg || !(buf = (struct semid_ds *) get_fs_long((int *) arg)))
272 return -EFAULT;
273 if ((i = verify_area (VERIFY_READ, buf, sizeof tbuf)))
274 return i;
275 memcpy_fromfs (&tbuf, buf, sizeof tbuf);
276 break;
277 }
278
279 if (semary[id] == IPC_UNUSED || semary[id] == IPC_NOID)
280 return -EIDRM;
281 if (ipcp->seq != semid / SEMMNI)
282 return -EIDRM;
283
284 switch (cmd) {
285 case GETALL:
286 if (ipcperms (ipcp, 0444))
287 return -EACCES;
288 for (i=0; i< sma->sem_nsems; i++)
289 sem_io[i] = sma->sem_base[i].semval;
290 memcpy_tofs (array, sem_io, nsems*sizeof(ushort));
291 break;
292 case SETVAL:
293 if (ipcperms (ipcp, 0222))
294 return -EACCES;
295 for (un = sma->undo; un; un = un->id_next)
296 if (semnum == un->sem_num)
297 un->semadj = 0;
298 sma->sem_ctime = CURRENT_TIME;
299 curr->semval = val;
300 if (sma->eventn)
301 wake_up (&sma->eventn);
302 if (sma->eventz)
303 wake_up (&sma->eventz);
304 break;
305 case IPC_SET:
306 if (suser() || current->euid == ipcp->cuid ||
307 current->euid == ipcp->uid) {
308 ipcp->uid = tbuf.sem_perm.uid;
309 ipcp->gid = tbuf.sem_perm.gid;
310 ipcp->mode = (ipcp->mode & ~0777)
311 | (tbuf.sem_perm.mode & 0777);
312 sma->sem_ctime = CURRENT_TIME;
313 return 0;
314 }
315 return -EPERM;
316 case IPC_STAT:
317 if (ipcperms (ipcp, 0444))
318 return -EACCES;
319 memcpy_tofs (buf, sma, sizeof (*sma));
320 break;
321 case SETALL:
322 if (ipcperms (ipcp, 0222))
323 return -EACCES;
324 for (i=0; i<nsems; i++)
325 sma->sem_base[i].semval = sem_io[i];
326 for (un = sma->undo; un; un = un->id_next)
327 un->semadj = 0;
328 if (sma->eventn)
329 wake_up (&sma->eventn);
330 if (sma->eventz)
331 wake_up (&sma->eventz);
332 sma->sem_ctime = CURRENT_TIME;
333 break;
334 default:
335 return -EINVAL;
336 }
337 return 0;
338 }
339
340 int sys_semop (int semid, struct sembuf *tsops, unsigned nsops)
341 {
342 int i, id;
343 struct semid_ds *sma;
344 struct sem *curr = NULL;
345 struct sembuf sops[SEMOPM], *sop;
346 struct sem_undo *un;
347 int undos = 0, alter = 0, semncnt = 0, semzcnt = 0;
348
349 if (nsops < 1 || semid < 0)
350 return -EINVAL;
351 if (nsops > SEMOPM)
352 return -E2BIG;
353 if (!tsops)
354 return -EFAULT;
355 memcpy_fromfs (sops, tsops, nsops * sizeof(*tsops));
356 id = semid % SEMMNI;
357 if ((sma = semary[id]) == IPC_UNUSED || sma == IPC_NOID)
358 return -EINVAL;
359 for (i=0; i<nsops; i++) {
360 sop = &sops[i];
361 if (sop->sem_num > sma->sem_nsems)
362 return -EFBIG;
363 if (sop->sem_flg & SEM_UNDO)
364 undos++;
365 if (sop->sem_op) {
366 alter++;
367 if (sop->sem_op > 0)
368 semncnt ++;
369 }
370 }
371 if (ipcperms(&sma->sem_perm, alter ? 0222 : 0444))
372 return -EACCES;
373
374
375
376 if (undos) {
377 for (i=0; i<nsops; i++) {
378 if (!(sops[i].sem_flg & SEM_UNDO))
379 continue;
380 for (un = current->semun; un; un = un->proc_next)
381 if ((un->semid == semid) &&
382 (un->sem_num == sops[i].sem_num))
383 break;
384 if (un)
385 continue;
386 un = (struct sem_undo *)
387 kmalloc (sizeof(*un), GFP_ATOMIC);
388 if (!un)
389 return -ENOMEM;
390 un->semid = semid;
391 un->semadj = 0;
392 un->sem_num = sops[i].sem_num;
393 un->proc_next = current->semun;
394 current->semun = un;
395 un->id_next = sma->undo;
396 sma->undo = un;
397 }
398 }
399
400 slept:
401 if (sma->sem_perm.seq != semid / SEMMNI)
402 return -EIDRM;
403 for (i=0; i<nsops; i++) {
404 sop = &sops[i];
405 curr = &sma->sem_base[sop->sem_num];
406 if (sop->sem_op + curr->semval > SEMVMX)
407 return -ERANGE;
408 if (!sop->sem_op && curr->semval) {
409 if (sop->sem_flg & IPC_NOWAIT)
410 return -EAGAIN;
411 if (current->signal & ~current->blocked)
412 return -EINTR;
413 curr->semzcnt++;
414 interruptible_sleep_on (&sma->eventz);
415 curr->semzcnt--;
416 goto slept;
417 }
418 if ((sop->sem_op + curr->semval < 0) ) {
419 if (sop->sem_flg & IPC_NOWAIT)
420 return -EAGAIN;
421 if (current->signal & ~current->blocked)
422 return -EINTR;
423 curr->semncnt++;
424 interruptible_sleep_on (&sma->eventn);
425 curr->semncnt--;
426 goto slept;
427 }
428 }
429
430 for (i=0; i<nsops; i++) {
431 sop = &sops[i];
432 curr = &sma->sem_base[sop->sem_num];
433 curr->sempid = current->pid;
434 if (!(curr->semval += sop->sem_op))
435 semzcnt++;
436 if (!(sop->sem_flg & SEM_UNDO))
437 continue;
438 for (un = current->semun; un; un = un->proc_next)
439 if ((un->semid == semid) &&
440 (un->sem_num == sop->sem_num))
441 break;
442 if (!un) {
443 printk ("semop : no undo for op %d\n", i);
444 continue;
445 }
446 un->semadj -= sop->sem_op;
447 }
448 sma->sem_otime = CURRENT_TIME;
449 if (semncnt && sma->eventn)
450 wake_up(&sma->eventn);
451 if (semzcnt && sma->eventz)
452 wake_up(&sma->eventz);
453 return curr->semval;
454 }
455
456
457
458
459
460
461 void sem_exit (void)
462 {
463 struct sem_undo *u, *un = NULL, **up, **unp;
464 struct semid_ds *sma;
465 struct sem *sem = NULL;
466
467 for (up = ¤t->semun; (u = *up); *up = u->proc_next, kfree(u)) {
468 sma = semary[u->semid % SEMMNI];
469 if (sma == IPC_UNUSED || sma == IPC_NOID)
470 continue;
471 if (sma->sem_perm.seq != u->semid / SEMMNI)
472 continue;
473 for (unp = &sma->undo; (un = *unp); unp = &un->id_next) {
474 if (u == un)
475 goto found;
476 }
477 printk ("sem_exit undo list error id=%d\n", u->semid);
478 break;
479 found:
480 *unp = un->id_next;
481 if (!un->semadj)
482 continue;
483 while (1) {
484 if (sma->sem_perm.seq != un->semid / SEMMNI)
485 break;
486 sem = &sma->sem_base[un->sem_num];
487 if (sem->semval + un->semadj >= 0) {
488 sem->semval += un->semadj;
489 sem->sempid = current->pid;
490 sma->sem_otime = CURRENT_TIME;
491 if (un->semadj > 0 && sma->eventn)
492 wake_up (&sma->eventn);
493 if (!sem->semval && sma->eventz)
494 wake_up (&sma->eventz);
495 break;
496 }
497 if (current->signal & ~current->blocked)
498 break;
499 sem->semncnt++;
500 interruptible_sleep_on (&sma->eventn);
501 sem->semncnt--;
502 }
503 }
504 current->semun = NULL;
505 return;
506 }