home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / KERNEL-S / V1.0 / LINUX-1.0 / LINUX-1 / linux / ipc / sem.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-27  |  12.1 KB  |  509 lines

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