linux-old/ipc/sem.c
<<
>>
Prefs
   1/*
   2 * linux/ipc/sem.c
   3 * Copyright (C) 1992 Krishna Balasubramanian
   4 * Copyright (C) 1995 Eric Schenk, Bruno Haible
   5 *
   6 * IMPLEMENTATION NOTES ON CODE REWRITE (Eric Schenk, January 1995):
   7 * This code underwent a massive rewrite in order to solve some problems
   8 * with the original code. In particular the original code failed to
   9 * wake up processes that were waiting for semval to go to 0 if the
  10 * value went to 0 and was then incremented rapidly enough. In solving
  11 * this problem I have also modified the implementation so that it
  12 * processes pending operations in a FIFO manner, thus give a guarantee
  13 * that processes waiting for a lock on the semaphore won't starve
  14 * unless another locking process fails to unlock.
  15 * In addition the following two changes in behavior have been introduced:
  16 * - The original implementation of semop returned the value
  17 *   last semaphore element examined on success. This does not
  18 *   match the manual page specifications, and effectively
  19 *   allows the user to read the semaphore even if they do not
  20 *   have read permissions. The implementation now returns 0
  21 *   on success as stated in the manual page.
  22 * - There is some confusion over whether the set of undo adjustments
  23 *   to be performed at exit should be done in an atomic manner.
  24 *   That is, if we are attempting to decrement the semval should we queue
  25 *   up and wait until we can do so legally?
  26 *   The original implementation attempted to do this.
  27 *   The current implementation does not do so. This is because I don't
  28 *   think it is the right thing (TM) to do, and because I couldn't
  29 *   see a clean way to get the old behavior with the new design.
  30 *   The POSIX standard and SVID should be consulted to determine
  31 *   what behavior is mandated.
  32 *
  33 * Further notes on refinement (Christoph Rohland, December 1998):
  34 * - The POSIX standard says, that the undo adjustments simply should
  35 *   redo. So the current implementation is o.K.
  36 * - The previous code had two flaws:
  37 *   1) It actively gave the semaphore to the next waiting process
  38 *      sleeping on the semaphore. Since this process did not have the
  39 *      cpu this led to many unnecessary context switches and bad
  40 *      performance. Now we only check which process should be able to
  41 *      get the semaphore and if this process wants to reduce some
  42 *      semaphore value we simply wake it up without doing the
  43 *      operation. So it has to try to get it later. Thus e.g. the
  44 *      running process may reaquire the semaphore during the current
  45 *      time slice. If it only waits for zero or increases the semaphore,
  46 *      we do the operation in advance and wake it up.
  47 *   2) It did not wake up all zero waiting processes. We try to do
  48 *      better but only get the semops right which only wait for zero or
  49 *      increase. If there are decrement operations in the operations
  50 *      array we do the same as before.
  51 */
  52
  53#include <linux/malloc.h>
  54#include <linux/smp_lock.h>
  55#include <linux/init.h>
  56
  57#include <asm/uaccess.h>
  58
  59extern int ipcperms (struct ipc_perm *ipcp, short semflg);
  60static int newary (key_t, int, int);
  61static int findkey (key_t key);
  62static void freeary (int id);
  63
  64static struct semid_ds *semary[SEMMNI];
  65static int used_sems = 0, used_semids = 0;
  66static struct wait_queue *sem_lock = NULL;
  67static int max_semid = 0;
  68
  69static unsigned short sem_seq = 0;
  70
  71void __init sem_init (void)
  72{
  73        int i;
  74
  75        sem_lock = NULL;
  76        used_sems = used_semids = max_semid = sem_seq = 0;
  77        for (i = 0; i < SEMMNI; i++)
  78                semary[i] = (struct semid_ds *) IPC_UNUSED;
  79        return;
  80}
  81
  82static int findkey (key_t key)
  83{
  84        int id;
  85        struct semid_ds *sma;
  86
  87        for (id = 0; id <= max_semid; id++) {
  88                while ((sma = semary[id]) == IPC_NOID)
  89                        interruptible_sleep_on (&sem_lock);
  90                if (sma == IPC_UNUSED)
  91                        continue;
  92                if (key == sma->sem_perm.key)
  93                        return id;
  94        }
  95        return -1;
  96}
  97
  98static int newary (key_t key, int nsems, int semflg)
  99{
 100        int id;
 101        struct semid_ds *sma;
 102        struct ipc_perm *ipcp;
 103        int size;
 104
 105        if (!nsems)
 106                return -EINVAL;
 107        if (used_sems + nsems > SEMMNS)
 108                return -ENOSPC;
 109        for (id = 0; id < SEMMNI; id++)
 110                if (semary[id] == IPC_UNUSED) {
 111                        semary[id] = (struct semid_ds *) IPC_NOID;
 112                        goto found;
 113                }
 114        return -ENOSPC;
 115found:
 116        size = sizeof (*sma) + nsems * sizeof (struct sem);
 117        used_sems += nsems;
 118        sma = (struct semid_ds *) kmalloc (size, GFP_KERNEL);
 119        if (!sma) {
 120                semary[id] = (struct semid_ds *) IPC_UNUSED;
 121                used_sems -= nsems;
 122                wake_up (&sem_lock);
 123                return -ENOMEM;
 124        }
 125        memset (sma, 0, size);
 126        sma->sem_base = (struct sem *) &sma[1];
 127        ipcp = &sma->sem_perm;
 128        ipcp->mode = (semflg & S_IRWXUGO);
 129        ipcp->key = key;
 130        ipcp->cuid = ipcp->uid = current->euid;
 131        ipcp->gid = ipcp->cgid = current->egid;
 132        sma->sem_perm.seq = sem_seq;
 133        /* sma->sem_pending = NULL; */
 134        sma->sem_pending_last = &sma->sem_pending;
 135        /* sma->undo = NULL; */
 136        sma->sem_nsems = nsems;
 137        sma->sem_ctime = CURRENT_TIME;
 138        if (id > max_semid)
 139                max_semid = id;
 140        used_semids++;
 141        semary[id] = sma;
 142        wake_up (&sem_lock);
 143        return (unsigned int) sma->sem_perm.seq * SEMMNI + id;
 144}
 145
 146asmlinkage int sys_semget (key_t key, int nsems, int semflg)
 147{
 148        int id, err = -EINVAL;
 149        struct semid_ds *sma;
 150
 151        lock_kernel();
 152        if (nsems < 0 || nsems > SEMMSL)
 153                goto out;
 154        if (key == IPC_PRIVATE) {
 155                err = newary(key, nsems, semflg);
 156        } else if ((id = findkey (key)) == -1) {  /* key not used */
 157                if (!(semflg & IPC_CREAT))
 158                        err = -ENOENT;
 159                else
 160                        err = newary(key, nsems, semflg);
 161        } else if (semflg & IPC_CREAT && semflg & IPC_EXCL) {
 162                err = -EEXIST;
 163        } else {
 164                sma = semary[id];
 165                if (nsems > sma->sem_nsems)
 166                        err = -EINVAL;
 167                else if (ipcperms(&sma->sem_perm, semflg))
 168                        err = -EACCES;
 169                else
 170                        err = (int) sma->sem_perm.seq * SEMMNI + id;
 171        }
 172out:
 173        unlock_kernel();
 174        return err;
 175}
 176
 177/* Manage the doubly linked list sma->sem_pending as a FIFO:
 178 * insert new queue elements at the tail sma->sem_pending_last.
 179 */
 180static inline void append_to_queue (struct semid_ds * sma,
 181                                    struct sem_queue * q)
 182{
 183        *(q->prev = sma->sem_pending_last) = q;
 184        *(sma->sem_pending_last = &q->next) = NULL;
 185}
 186
 187static inline void prepend_to_queue (struct semid_ds * sma,
 188                                     struct sem_queue * q)
 189{
 190        q->next = sma->sem_pending;
 191        *(q->prev = &sma->sem_pending) = q;
 192        if (q->next)
 193                q->next->prev = &q->next;
 194        else /* sma->sem_pending_last == &sma->sem_pending */
 195                sma->sem_pending_last = &q->next;
 196}
 197
 198static inline void remove_from_queue (struct semid_ds * sma,
 199                                      struct sem_queue * q)
 200{
 201        *(q->prev) = q->next;
 202        if (q->next)
 203                q->next->prev = q->prev;
 204        else /* sma->sem_pending_last == &q->next */
 205                sma->sem_pending_last = q->prev;
 206        q->prev = NULL; /* mark as removed */
 207}
 208
 209/*
 210 * Determine whether a sequence of semaphore operations would succeed
 211 * all at once. Return 0 if yes, 1 if need to sleep, else return error code.
 212 */
 213
 214static int try_atomic_semop (struct semid_ds * sma, struct sembuf * sops,
 215                             int nsops, struct sem_undo *un, int pid,
 216                             int do_undo)
 217{
 218        int result, sem_op;
 219        struct sembuf *sop;
 220        struct sem * curr;
 221
 222        for (sop = sops; sop < sops + nsops; sop++) {
 223                curr = sma->sem_base + sop->sem_num;
 224                sem_op = sop->sem_op;
 225
 226                if (!sem_op && curr->semval)
 227                        goto would_block;
 228
 229                curr->sempid = (curr->sempid << 16) | pid;
 230                curr->semval += sem_op;
 231                if (sop->sem_flg & SEM_UNDO)
 232                        un->semadj[sop->sem_num] -= sem_op;
 233
 234                if (curr->semval < 0)
 235                        goto would_block;
 236                if (curr->semval > SEMVMX)
 237                        goto out_of_range;
 238        }
 239
 240        if (do_undo)
 241        {
 242                sop--;
 243                result = 0;
 244                goto undo;
 245        }
 246
 247        sma->sem_otime = CURRENT_TIME;
 248        return 0;
 249
 250out_of_range:
 251        result = -ERANGE;
 252        goto undo;
 253
 254would_block:
 255        if (sop->sem_flg & IPC_NOWAIT)
 256                result = -EAGAIN;
 257        else
 258                result = 1;
 259
 260undo:
 261        while (sop >= sops) {
 262                curr = sma->sem_base + sop->sem_num;
 263                curr->semval -= sop->sem_op;
 264                curr->sempid >>= 16;
 265
 266                if (sop->sem_flg & SEM_UNDO)
 267                        un->semadj[sop->sem_num] += sop->sem_op;
 268                sop--;
 269        }
 270
 271        return result;
 272}
 273
 274/* Go through the pending queue for the indicated semaphore
 275 * looking for tasks that can be completed.
 276 */
 277static void update_queue (struct semid_ds * sma)
 278{
 279        int error;
 280        struct sem_queue * q;
 281
 282        for (q = sma->sem_pending; q; q = q->next) {
 283                        
 284                if (q->status == 1)
 285                        continue; /* this one was woken up before */
 286
 287                error = try_atomic_semop(sma, q->sops, q->nsops,
 288                                         q->undo, q->pid, q->alter);
 289
 290                /* Does q->sleeper still need to sleep? */
 291                if (error <= 0) {
 292                                /* Found one, wake it up */
 293                        wake_up_interruptible(&q->sleeper);
 294                        if (error == 0 && q->alter) {
 295                                /* if q-> alter let it self try */
 296                                q->status = 1;
 297                                return;
 298                        }
 299                        q->status = error;
 300                        remove_from_queue(sma,q);
 301                }
 302        }
 303}
 304
 305/* The following counts are associated to each semaphore:
 306 *   semncnt        number of tasks waiting on semval being nonzero
 307 *   semzcnt        number of tasks waiting on semval being zero
 308 * This model assumes that a task waits on exactly one semaphore.
 309 * Since semaphore operations are to be performed atomically, tasks actually
 310 * wait on a whole sequence of semaphores simultaneously.
 311 * The counts we return here are a rough approximation, but still
 312 * warrant that semncnt+semzcnt>0 if the task is on the pending queue.
 313 */
 314static int count_semncnt (struct semid_ds * sma, ushort semnum)
 315{
 316        int semncnt;
 317        struct sem_queue * q;
 318
 319        semncnt = 0;
 320        for (q = sma->sem_pending; q; q = q->next) {
 321                struct sembuf * sops = q->sops;
 322                int nsops = q->nsops;
 323                int i;
 324                for (i = 0; i < nsops; i++)
 325                        if (sops[i].sem_num == semnum
 326                            && (sops[i].sem_op < 0)
 327                            && !(sops[i].sem_flg & IPC_NOWAIT))
 328                                semncnt++;
 329        }
 330        return semncnt;
 331}
 332static int count_semzcnt (struct semid_ds * sma, ushort semnum)
 333{
 334        int semzcnt;
 335        struct sem_queue * q;
 336
 337        semzcnt = 0;
 338        for (q = sma->sem_pending; q; q = q->next) {
 339                struct sembuf * sops = q->sops;
 340                int nsops = q->nsops;
 341                int i;
 342                for (i = 0; i < nsops; i++)
 343                        if (sops[i].sem_num == semnum
 344                            && (sops[i].sem_op == 0)
 345                            && !(sops[i].sem_flg & IPC_NOWAIT))
 346                                semzcnt++;
 347        }
 348        return semzcnt;
 349}
 350
 351/* Free a semaphore set. */
 352static void freeary (int id)
 353{
 354        struct semid_ds *sma = semary[id];
 355        struct sem_undo *un;
 356        struct sem_queue *q;
 357
 358        /* Invalidate this semaphore set */
 359        sma->sem_perm.seq++;
 360        sem_seq = (sem_seq+1) % ((unsigned)(1<<31)/SEMMNI); /* increment, but avoid overflow */
 361        used_sems -= sma->sem_nsems;
 362        if (id == max_semid)
 363                while (max_semid && (semary[--max_semid] == IPC_UNUSED));
 364        semary[id] = (struct semid_ds *) IPC_UNUSED;
 365        used_semids--;
 366
 367        /* Invalidate the existing undo structures for this semaphore set.
 368         * (They will be freed without any further action in sem_exit().)
 369         */
 370        for (un = sma->undo; un; un = un->id_next)
 371                un->semid = -1;
 372
 373        /* Wake up all pending processes and let them fail with EIDRM. */
 374        for (q = sma->sem_pending; q; q = q->next) {
 375                q->status = -EIDRM;
 376                q->prev = NULL;
 377                wake_up_interruptible(&q->sleeper); /* doesn't sleep! */
 378        }
 379
 380        kfree(sma);
 381}
 382
 383asmlinkage int sys_semctl (int semid, int semnum, int cmd, union semun arg)
 384{
 385        struct semid_ds *buf = NULL;
 386        struct semid_ds tbuf;
 387        int i, id, val = 0;
 388        struct semid_ds *sma;
 389        struct ipc_perm *ipcp;
 390        struct sem *curr = NULL;
 391        struct sem_undo *un;
 392        unsigned int nsems;
 393        ushort *array = NULL;
 394        ushort sem_io[SEMMSL];
 395        int err = -EINVAL;
 396
 397        lock_kernel();
 398        if (semid < 0 || semnum < 0 || cmd < 0)
 399                goto out;
 400
 401        switch (cmd) {
 402        case IPC_INFO:
 403        case SEM_INFO:
 404        {
 405                struct seminfo seminfo, *tmp = arg.__buf;
 406                seminfo.semmni = SEMMNI;
 407                seminfo.semmns = SEMMNS;
 408                seminfo.semmsl = SEMMSL;
 409                seminfo.semopm = SEMOPM;
 410                seminfo.semvmx = SEMVMX;
 411                seminfo.semmnu = SEMMNU;
 412                seminfo.semmap = SEMMAP;
 413                seminfo.semume = SEMUME;
 414                seminfo.semusz = SEMUSZ;
 415                seminfo.semaem = SEMAEM;
 416                if (cmd == SEM_INFO) {
 417                        seminfo.semusz = used_semids;
 418                        seminfo.semaem = used_sems;
 419                }
 420                err = -EFAULT;
 421                if (copy_to_user (tmp, &seminfo, sizeof(struct seminfo))) 
 422                        goto out;
 423                err = max_semid;
 424                goto out;
 425        }
 426
 427        case SEM_STAT:
 428                buf = arg.buf;
 429                err = -EINVAL;
 430                if (semid > max_semid)
 431                        goto out;
 432                sma = semary[semid];
 433                if (sma == IPC_UNUSED || sma == IPC_NOID)
 434                        goto out;
 435                err = -EACCES;
 436                if (ipcperms (&sma->sem_perm, S_IRUGO))
 437                        goto out;
 438                id = (unsigned int) sma->sem_perm.seq * SEMMNI + semid;
 439                tbuf.sem_perm   = sma->sem_perm;
 440                tbuf.sem_otime  = sma->sem_otime;
 441                tbuf.sem_ctime  = sma->sem_ctime;
 442                tbuf.sem_nsems  = sma->sem_nsems;
 443                err = -EFAULT;
 444                if (copy_to_user (buf, &tbuf, sizeof(*buf)) == 0)
 445                        err = id;
 446                goto out;
 447        }
 448
 449        id = (unsigned int) semid % SEMMNI;
 450        sma = semary [id];
 451        err = -EINVAL;
 452        if (sma == IPC_UNUSED || sma == IPC_NOID)
 453                goto out;
 454        ipcp = &sma->sem_perm;
 455        nsems = sma->sem_nsems;
 456        err = -EIDRM;
 457        if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI)
 458                goto out;
 459
 460        switch (cmd) {
 461        case GETVAL:
 462        case GETPID:
 463        case GETNCNT:
 464        case GETZCNT:
 465        case SETVAL:
 466                err = -EINVAL;
 467                if (semnum >= nsems)
 468                        goto out;
 469                curr = &sma->sem_base[semnum];
 470                break;
 471        }
 472
 473        switch (cmd) {
 474        case GETVAL:
 475        case GETPID:
 476        case GETNCNT:
 477        case GETZCNT:
 478        case GETALL:
 479                err = -EACCES;
 480                if (ipcperms (ipcp, S_IRUGO))
 481                        goto out;
 482                switch (cmd) {
 483                case GETVAL : err = curr->semval; goto out;
 484                case GETPID : err = curr->sempid & 0xffff; goto out;
 485                case GETNCNT: err = count_semncnt(sma,semnum); goto out;
 486                case GETZCNT: err = count_semzcnt(sma,semnum); goto out;
 487                case GETALL:
 488                        array = arg.array;
 489                        break;
 490                }
 491                break;
 492        case SETVAL:
 493                val = arg.val;
 494                err = -ERANGE;
 495                if (val > SEMVMX || val < 0)
 496                        goto out;
 497                break;
 498        case IPC_RMID:
 499                if (current->euid == ipcp->cuid || 
 500                    current->euid == ipcp->uid || capable(CAP_SYS_ADMIN)) {
 501                        freeary (id);
 502                        err = 0;
 503                        goto out;
 504                }
 505                err = -EPERM;
 506                goto out;
 507        case SETALL: /* arg is a pointer to an array of ushort */
 508                array = arg.array;
 509                err = -EFAULT;
 510                if (copy_from_user (sem_io, array, nsems*sizeof(ushort)))
 511                       goto out;
 512                err = 0;
 513                for (i = 0; i < nsems; i++)
 514                        if (sem_io[i] > SEMVMX) {
 515                                err = -ERANGE;
 516                                goto out;
 517                        }
 518                break;
 519        case IPC_STAT:
 520                buf = arg.buf;
 521                break;
 522        case IPC_SET:
 523                buf = arg.buf;
 524                err = -EFAULT;
 525                if(copy_from_user (&tbuf, buf, sizeof (*buf)))
 526                        goto out;
 527                break;
 528        }
 529
 530        err = -EIDRM;
 531        if ((sma != semary[id]) || (sma->sem_perm.seq != (unsigned int) semid / SEMMNI))
 532                goto out;
 533
 534        switch (cmd) {
 535        case GETALL:
 536                err = -EACCES;
 537                if (ipcperms (ipcp, S_IRUGO))
 538                        goto out;
 539                for (i = 0; i < sma->sem_nsems; i++)
 540                        sem_io[i] = sma->sem_base[i].semval;
 541                err = -EFAULT;
 542                if (copy_to_user (array, sem_io, nsems*sizeof(ushort)))
 543                        goto out;
 544                break;
 545        case SETVAL:
 546                err = -EACCES;
 547                if (ipcperms (ipcp, S_IWUGO))
 548                        goto out;
 549                for (un = sma->undo; un; un = un->id_next)
 550                        un->semadj[semnum] = 0;
 551                curr->semval = val;
 552                sma->sem_ctime = CURRENT_TIME;
 553                /* maybe some queued-up processes were waiting for this */
 554                update_queue(sma);
 555                break;
 556        case IPC_SET:
 557                if (current->euid == ipcp->cuid || 
 558                    current->euid == ipcp->uid || capable(CAP_SYS_ADMIN)) {
 559                        ipcp->uid = tbuf.sem_perm.uid;
 560                        ipcp->gid = tbuf.sem_perm.gid;
 561                        ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
 562                                | (tbuf.sem_perm.mode & S_IRWXUGO);
 563                        sma->sem_ctime = CURRENT_TIME;
 564                        err = 0;
 565                        goto out;
 566                }
 567                err = -EPERM;
 568                goto out;
 569        case IPC_STAT:
 570                err = -EACCES;
 571                if (ipcperms (ipcp, S_IRUGO))
 572                        goto out;
 573                tbuf.sem_perm   = sma->sem_perm;
 574                tbuf.sem_otime  = sma->sem_otime;
 575                tbuf.sem_ctime  = sma->sem_ctime;
 576                tbuf.sem_nsems  = sma->sem_nsems;
 577                err = -EFAULT;
 578                if (copy_to_user (buf, &tbuf, sizeof(*buf)))
 579                        goto out;
 580                break;
 581        case SETALL:
 582                err = -EACCES;
 583                if (ipcperms (ipcp, S_IWUGO))
 584                        goto out;
 585                for (i = 0; i < nsems; i++)
 586                        sma->sem_base[i].semval = sem_io[i];
 587                for (un = sma->undo; un; un = un->id_next)
 588                        for (i = 0; i < nsems; i++)
 589                                un->semadj[i] = 0;
 590                sma->sem_ctime = CURRENT_TIME;
 591                /* maybe some queued-up processes were waiting for this */
 592                update_queue(sma);
 593                break;
 594        default:
 595                err = -EINVAL;
 596                goto out;
 597        }
 598        err = 0;
 599out:
 600        unlock_kernel();
 601        return err;
 602}
 603
 604
 605static struct sem_undo* freeundos(struct sem_undo* un)
 606{
 607        struct sem_undo* u;
 608        struct sem_undo** up;
 609
 610        for (up = &current->semundo;(u=*up);up=&u->proc_next) {
 611                if(un==u) {
 612                        un=u->proc_next;
 613                        *up=un;
 614                        kfree(u);
 615                        return un;
 616                }
 617        }
 618        printk ("freeundos undo list error id=%d\n", un->semid);
 619        return un->proc_next;
 620}
 621
 622
 623asmlinkage int sys_semop (int semid, struct sembuf *tsops, unsigned nsops)
 624{
 625        int id, size, error = -EINVAL;
 626        struct semid_ds *sma;
 627        struct sembuf sops[SEMOPM], *sop;
 628        struct sem_undo *un;
 629        int undos = 0, decrease = 0, alter = 0;
 630        struct sem_queue queue;
 631
 632        lock_kernel();
 633        if (nsops < 1 || semid < 0)
 634                goto out;
 635        error = -E2BIG;
 636        if (nsops > SEMOPM)
 637                goto out;
 638        error = -EFAULT;
 639        if (copy_from_user (sops, tsops, nsops * sizeof(*tsops)))
 640                goto out;
 641        id = (unsigned int) semid % SEMMNI;
 642        error = -EINVAL;
 643        if ((sma = semary[id]) == IPC_UNUSED || sma == IPC_NOID)
 644                goto out;
 645        error = -EIDRM;
 646        if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI)
 647                goto out;
 648
 649                error = -EFBIG;
 650        for (sop = sops; sop < sops + nsops; sop++) {
 651                if (sop->sem_num >= sma->sem_nsems)
 652                        goto out;
 653                if (sop->sem_flg & SEM_UNDO)
 654                        undos++;
 655                if (sop->sem_op < 0)
 656                        decrease = 1;
 657                if (sop->sem_op > 0)
 658                        alter = 1;
 659        }
 660        alter |= decrease;
 661
 662        error = -EACCES;
 663        if (ipcperms(&sma->sem_perm, alter ? S_IWUGO : S_IRUGO))
 664                goto out;
 665        if (undos) {
 666                /* Make sure we have an undo structure
 667                 * for this process and this semaphore set.
 668                 */
 669                un = current->semundo;
 670                while(un != NULL) {
 671                        if(un->semid==semid)
 672                                break;
 673                        if(un->semid==-1)
 674                                un=freeundos(un);
 675                        else
 676                        un=un->proc_next;
 677                }
 678                if (!un) {
 679                        size = sizeof(struct sem_undo) + sizeof(short)*sma->sem_nsems;
 680                        un = (struct sem_undo *) kmalloc(size, GFP_ATOMIC);
 681                        if (!un) {
 682                                error = -ENOMEM;
 683                                goto out;
 684                        }
 685                        memset(un, 0, size);
 686                        un->semadj = (short *) &un[1];
 687                        un->semid = semid;
 688                        un->proc_next = current->semundo;
 689                        current->semundo = un;
 690                        un->id_next = sma->undo;
 691                        sma->undo = un;
 692                }
 693        } else
 694                un = NULL;
 695
 696        error = try_atomic_semop (sma, sops, nsops, un, current->pid, 0);
 697        if (error <= 0)
 698                goto update;
 699
 700        /* We need to sleep on this operation, so we put the current
 701         * task into the pending queue and go to sleep.
 702         */
 703                
 704        queue.sma = sma;
 705        queue.sops = sops;
 706        queue.nsops = nsops;
 707        queue.undo = un;
 708        queue.pid = current->pid;
 709        queue.alter = decrease;
 710        current->semsleeping = &queue;
 711        if (alter)
 712                append_to_queue(sma ,&queue);
 713        else
 714                prepend_to_queue(sma ,&queue);
 715
 716        for (;;) {
 717                queue.status = -EINTR;
 718                queue.sleeper = NULL;
 719                interruptible_sleep_on(&queue.sleeper);
 720
 721                /*
 722                 * If queue.status == 1 we were woken up and
 723                 * have to retry else we simply return.
 724                 * If an interrupt occurred we have to clean up the
 725                 * queue
 726                 *
 727                 */
 728                if (queue.status == 1)
 729                {
 730                        error = try_atomic_semop (sma, sops, nsops, un,
 731                                                  current->pid,0);
 732                        if (error <= 0) 
 733                                break;
 734                } else {
 735                        error = queue.status;;
 736                        if (queue.prev) /* got Interrupt */
 737                                break;
 738                        /* Everything done by update_queue */
 739                        current->semsleeping = NULL;
 740                        goto out;
 741                }
 742        }
 743        current->semsleeping = NULL;
 744        remove_from_queue(sma,&queue);
 745update:
 746        if (alter)
 747                update_queue (sma);
 748out:
 749        unlock_kernel();
 750        return error;
 751}
 752
 753/*
 754 * add semadj values to semaphores, free undo structures.
 755 * undo structures are not freed when semaphore arrays are destroyed
 756 * so some of them may be out of date.
 757 * IMPLEMENTATION NOTE: There is some confusion over whether the
 758 * set of adjustments that needs to be done should be done in an atomic
 759 * manner or not. That is, if we are attempting to decrement the semval
 760 * should we queue up and wait until we can do so legally?
 761 * The original implementation attempted to do this (queue and wait).
 762 * The current implementation does not do so. The POSIX standard
 763 * and SVID should be consulted to determine what behavior is mandated.
 764 */
 765void sem_exit (void)
 766{
 767        struct sem_queue *q;
 768        struct sem_undo *u, *un = NULL, **up, **unp;
 769        struct semid_ds *sma;
 770        int nsems, i;
 771
 772        /* If the current process was sleeping for a semaphore,
 773         * remove it from the queue.
 774         */
 775        if ((q = current->semsleeping)) {
 776                if (q->prev)
 777                        remove_from_queue(q->sma,q);
 778                current->semsleeping = NULL;
 779        }
 780
 781        for (up = &current->semundo; (u = *up); *up = u->proc_next, kfree(u)) {
 782                if (u->semid == -1)
 783                        continue;
 784                sma = semary[(unsigned int) u->semid % SEMMNI];
 785                if (sma == IPC_UNUSED || sma == IPC_NOID)
 786                        continue;
 787                if (sma->sem_perm.seq != (unsigned int) u->semid / SEMMNI)
 788                        continue;
 789                /* remove u from the sma->undo list */
 790                for (unp = &sma->undo; (un = *unp); unp = &un->id_next) {
 791                        if (u == un)
 792                                goto found;
 793                }
 794                printk ("sem_exit undo list error id=%d\n", u->semid);
 795                break;
 796found:
 797                *unp = un->id_next;
 798                /* perform adjustments registered in u */
 799                nsems = sma->sem_nsems;
 800                for (i = 0; i < nsems; i++) {
 801                        struct sem * sem = &sma->sem_base[i];
 802                        sem->semval += u->semadj[i];
 803                        if (sem->semval < 0)
 804                                sem->semval = 0; /* shouldn't happen */
 805                        sem->sempid = current->pid;
 806                }
 807                sma->sem_otime = CURRENT_TIME;
 808                /* maybe some queued-up processes were waiting for this */
 809                update_queue(sma);
 810        }
 811        current->semundo = NULL;
 812}
 813
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.