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