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

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