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] = 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] = 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] = 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
127 sma->sem_perm.seq++;
128 if ((int)((++sem_seq + 1) * SEMMNI) < 0)
129 sem_seq = 0;
130 used_sems -= sma->sem_nsems;
131 if (id == max_semid)
132 while (max_semid && (semary[--max_semid] == IPC_UNUSED));
133 semary[id] = IPC_UNUSED;
134 used_semids--;
135 while (sma->eventz || sma->eventn) {
136 if (sma->eventz)
137 wake_up (&sma->eventz);
138 if (sma->eventn)
139 wake_up (&sma->eventn);
140 schedule();
141 }
142 kfree (sma);
143 return;
144 }
145
146 int sys_semctl (int semid, int semnum, int cmd, void *arg)
147 {
148 int i, id, val = 0;
149 struct semid_ds *sma, *buf, tbuf;
150 struct ipc_perm *ipcp;
151 struct sem *curr;
152 struct sem_undo *un;
153 ushort nsems, *array = NULL;
154 ushort sem_io[SEMMSL];
155
156 if (semid < 0 || semnum < 0 || cmd < 0)
157 return -EINVAL;
158
159 switch (cmd) {
160 case IPC_INFO:
161 case SEM_INFO:
162 {
163 struct seminfo seminfo, *tmp;
164 if (!arg || ! (tmp = (struct seminfo *) get_fs_long (arg)))
165 return -EFAULT;
166 seminfo.semmni = SEMMNI;
167 seminfo.semmns = SEMMNS;
168 seminfo.semmsl = SEMMSL;
169 seminfo.semopm = SEMOPM;
170 seminfo.semvmx = SEMVMX;
171 seminfo.semmnu = SEMMNU;
172 seminfo.semmap = SEMMAP;
173 seminfo.semume = SEMUME;
174 seminfo.semusz = SEMUSZ;
175 seminfo.semaem = SEMAEM;
176 if (cmd == SEM_INFO) {
177 seminfo.semusz = used_semids;
178 seminfo.semaem = used_sems;
179 }
180 i= verify_area(VERIFY_WRITE, tmp, sizeof(struct seminfo));
181 if (i)
182 return i;
183 memcpy_tofs (tmp, &seminfo, sizeof(struct seminfo));
184 return max_semid;
185 }
186
187 case SEM_STAT:
188 if (!arg || ! (buf = (struct semid_ds *) get_fs_long (arg)))
189 return -EFAULT;
190 i = verify_area (VERIFY_WRITE, buf, sizeof (*sma));
191 if (i)
192 return i;
193 if (semid > max_semid)
194 return -EINVAL;
195 sma = semary[semid];
196 if (sma == IPC_UNUSED || sma == IPC_NOID)
197 return -EINVAL;
198 if (ipcperms (&sma->sem_perm, 0444))
199 return -EACCES;
200 id = semid + sma->sem_perm.seq * SEMMNI;
201 memcpy_tofs (buf, sma, sizeof(*sma));
202 return id;
203 }
204
205 id = semid % SEMMNI;
206 sma = semary [id];
207 if (sma == IPC_UNUSED || sma == IPC_NOID)
208 return -EINVAL;
209 ipcp = &sma->sem_perm;
210 nsems = sma->sem_nsems;
211 if (ipcp->seq != semid / SEMMNI)
212 return -EIDRM;
213 if (semnum >= nsems)
214 return -EINVAL;
215 curr = &sma->sem_base[semnum];
216
217 switch (cmd) {
218 case GETVAL:
219 case GETPID:
220 case GETNCNT:
221 case GETZCNT:
222 case GETALL:
223 if (ipcperms (ipcp, 0444))
224 return -EACCES;
225 switch (cmd) {
226 case GETVAL : return curr->semval;
227 case GETPID : return curr->sempid;
228 case GETNCNT: return curr->semncnt;
229 case GETZCNT: return curr->semzcnt;
230 case GETALL:
231 if (!arg || ! (array = (ushort *) get_fs_long (arg)))
232 return -EFAULT;
233 i = verify_area (VERIFY_WRITE, array, nsems* sizeof(short));
234 if (i)
235 return i;
236 }
237 break;
238 case SETVAL:
239 if (!arg)
240 return -EFAULT;
241 if ((val = (int) get_fs_long (arg)) > SEMVMX || val < 0)
242 return -ERANGE;
243 break;
244 case IPC_RMID:
245 if (suser() || current->euid == ipcp->cuid ||
246 current->euid == ipcp->uid) {
247 freeary (id);
248 return 0;
249 }
250 return -EPERM;
251 case SETALL:
252 if (!arg || ! (array = (ushort *) get_fs_long (arg)) )
253 return -EFAULT;
254 if ((i = verify_area (VERIFY_READ, array, sizeof tbuf)))
255 return i;
256 memcpy_fromfs (sem_io, array, nsems*sizeof(ushort));
257 for (i=0; i< nsems; i++)
258 if (sem_io[i] > SEMVMX)
259 return -ERANGE;
260 break;
261 case IPC_STAT:
262 if (!arg || !(buf = (struct semid_ds *) get_fs_long (arg)))
263 return -EFAULT;
264 if ((i = verify_area (VERIFY_WRITE, arg, sizeof tbuf)))
265 return i;
266 break;
267 case IPC_SET:
268 if (!arg || !(buf = (struct semid_ds *) get_fs_long (arg)))
269 return -EFAULT;
270 if ((i = verify_area (VERIFY_READ, buf, sizeof tbuf)))
271 return i;
272 memcpy_fromfs (&tbuf, buf, sizeof tbuf);
273 break;
274 }
275
276 if (semary[id] == IPC_UNUSED || semary[id] == IPC_NOID)
277 return -EIDRM;
278 if (ipcp->seq != semid / SEMMNI)
279 return -EIDRM;
280
281 switch (cmd) {
282 case GETALL:
283 if (ipcperms (ipcp, 0444))
284 return -EACCES;
285 for (i=0; i< sma->sem_nsems; i++)
286 sem_io[i] = sma->sem_base[i].semval;
287 memcpy_tofs (array, sem_io, nsems*sizeof(ushort));
288 break;
289 case SETVAL:
290 if (ipcperms (ipcp, 0222))
291 return -EACCES;
292 for (un = sma->undo; un; un = un->id_next)
293 if (semnum == un->sem_num)
294 un->semadj = 0;
295 sma->sem_ctime = CURRENT_TIME;
296 curr->semval = val;
297 if (sma->eventn)
298 wake_up (&sma->eventn);
299 if (sma->eventz)
300 wake_up (&sma->eventz);
301 break;
302 case IPC_SET:
303 if (suser() || current->euid == ipcp->cuid ||
304 current->euid == ipcp->uid) {
305 ipcp->uid = tbuf.sem_perm.uid;
306 ipcp->gid = tbuf.sem_perm.gid;
307 ipcp->mode = (ipcp->mode & ~0777)
308 | (tbuf.sem_perm.mode & 0777);
309 sma->sem_ctime = CURRENT_TIME;
310 return 0;
311 }
312 return -EPERM;
313 case IPC_STAT:
314 if (ipcperms (ipcp, 0444))
315 return -EACCES;
316 memcpy_tofs (buf, sma, sizeof (*sma));
317 break;
318 case SETALL:
319 if (ipcperms (ipcp, 0222))
320 return -EACCES;
321 for (i=0; i<nsems; i++)
322 sma->sem_base[i].semval = sem_io[i];
323 for (un = sma->undo; un != NULL; un = un->id_next)
324 un->semadj = 0;
325 if (sma->eventn)
326 wake_up (&sma->eventn);
327 if (sma->eventz)
328 wake_up (&sma->eventz);
329 sma->sem_ctime = CURRENT_TIME;
330 break;
331 default:
332 return -EINVAL;
333 }
334 return 0;
335 }
336
337 int sys_semop (int semid, struct sembuf *tsops, unsigned nsops)
338 {
339 int i, id;
340 struct semid_ds *sma;
341 struct sem *curr = NULL;
342 struct sembuf sops[SEMOPM], *sop;
343 struct sem_undo *un;
344 int undos = 0, alter = 0, semncnt = 0, semzcnt = 0;
345
346 if (nsops < 1 || semid < 0)
347 return -EINVAL;
348 if (nsops > SEMOPM)
349 return -E2BIG;
350 if (!tsops)
351 return -EFAULT;
352 memcpy_fromfs (sops, tsops, nsops * sizeof(*tsops));
353 id = semid % SEMMNI;
354 if ((sma = semary[id]) == IPC_UNUSED || sma == IPC_NOID)
355 return -EINVAL;
356 for (i=0; i<nsops; i++) {
357 sop = &sops[i];
358 if (sop->sem_num > sma->sem_nsems)
359 return -EFBIG;
360 if (sop->sem_flg & SEM_UNDO)
361 undos++;
362 if (sop->sem_op) {
363 alter++;
364 if (sop->sem_op > 0)
365 semncnt ++;
366 }
367 }
368 if (ipcperms(&sma->sem_perm, alter ? 0222 : 0444))
369 return -EACCES;
370
371
372
373 if (undos) {
374 for (i=0; i<nsops; i++) {
375 if (!(sops[i].sem_flg & SEM_UNDO))
376 continue;
377 for (un = current->semun; un; un = un->proc_next)
378 if ((un->semid == semid) &&
379 (un->sem_num == sops[i].sem_num))
380 break;
381 if (un)
382 continue;
383 un = (struct sem_undo *)
384 kmalloc (sizeof(*un), GFP_ATOMIC);
385 if (!un)
386 return -ENOMEM;
387 un->semid = semid;
388 un->semadj = 0;
389 un->sem_num = sops[i].sem_num;
390 un->proc_next = current->semun;
391 current->semun = un;
392 un->id_next = sma->undo;
393 if (sma->undo)
394 sma->undo->id_prev = un;
395 sma->undo = un->id_prev = un;
396 }
397 }
398
399 slept:
400 if (sma->sem_perm.seq != semid / SEMMNI)
401 return -EIDRM;
402 for (i=0; i<nsops; i++) {
403 sop = &sops[i];
404 curr = &sma->sem_base[sop->sem_num];
405 if (sop->sem_op + curr->semval > SEMVMX)
406 return -ERANGE;
407 if (!sop->sem_op && curr->semval) {
408 if (sop->sem_flg & IPC_NOWAIT)
409 return -EAGAIN;
410 if (current->signal & ~current->blocked)
411 return -EINTR;
412 curr->semzcnt++;
413 interruptible_sleep_on (&sma->eventz);
414 curr->semzcnt--;
415 goto slept;
416 }
417 if ((sop->sem_op + curr->semval < 0) ) {
418 if (sop->sem_flg & IPC_NOWAIT)
419 return -EAGAIN;
420 if (current->signal & ~current->blocked)
421 return -EINTR;
422 curr->semncnt++;
423 interruptible_sleep_on (&sma->eventn);
424 curr->semncnt--;
425 goto slept;
426 }
427 }
428
429 for (i=0; i<nsops; i++) {
430 sop = &sops[i];
431 curr = &sma->sem_base[sop->sem_num];
432 curr->sempid = current->pid;
433 if (!(curr->semval += sop->sem_op))
434 semzcnt++;
435 if (!(sop->sem_flg & SEM_UNDO))
436 continue;
437 for (un = current->semun; un; un = un->proc_next)
438 if ((un->semid == semid) &&
439 (un->sem_num == sop->sem_num))
440 break;
441 if (!un) {
442 printk ("semop : no undo for op %d\n", i);
443 continue;
444 }
445 un->semadj -= sop->sem_op;
446 }
447 sma->sem_otime = CURRENT_TIME;
448 if (semncnt && sma->eventn)
449 wake_up(&sma->eventn);
450 if (semzcnt && sma->eventz)
451 wake_up(&sma->eventz);
452 return curr->semval;
453 }
454
455
456
457
458 void sem_exit (void)
459 {
460 struct sem_undo *un, *tmp;
461 struct semid_ds *sma;
462 struct sem *sem = NULL;
463
464 for (un = current->semun; un; kfree(un), un = tmp) {
465 un->id_prev->id_next = un->id_next;
466 if (un->id_next)
467 un->id_next->id_prev = un->id_prev;
468 tmp = un->proc_next;
469 if (!un->semadj)
470 continue;
471 sma = semary[un->semid % SEMMNI];
472 if (sma == IPC_UNUSED || sma == IPC_NOID
473 || sma->sem_perm.seq != un->semid / SEMMNI)
474 continue;
475 sem = &sma->sem_base[un->sem_num];
476 if (sem->semval + un->semadj >= 0) {
477 sem->semval += un->semadj;
478 sem->sempid = current->pid;
479 sma->sem_otime = CURRENT_TIME;
480 if (un->semadj > 0 && sma->eventn)
481 wake_up (&sma->eventn);
482 if (!sem->semval && sma->eventz)
483 wake_up (&sma->eventz);
484 }
485 }
486 current->semun = NULL;
487 return;
488 }