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