root/ipc/sem.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sem_init
  2. findkey
  3. newary
  4. sys_semget
  5. freeary
  6. sys_semctl
  7. sys_semop
  8. sem_exit

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

/* [previous][next][first][last][top][bottom][index][help] */