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