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 
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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) {  /* key not used */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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: /* arg is a pointer to an array of ushort */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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          * ensure every sop with undo gets an undo structure 
 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; /* freed on exit */
 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  * add semadj values to semaphores, free undo structures.
 458  * undo structures are not freed when semaphore arrays are destroyed
 459  * so some of them may be out of date.
 460  */
 461 void sem_exit (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 462 {
 463         struct sem_undo *u, *un = NULL, **up, **unp;
 464         struct semid_ds *sma;
 465         struct sem *sem = NULL;
 466         
 467         for (up = &current->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 }

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