]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] Fix mq_notify with SIGEV_NONE notification
authorAndrew Morton <akpm@osdl.org>
Thu, 15 Apr 2004 00:02:37 +0000 (17:02 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Thu, 15 Apr 2004 00:02:37 +0000 (17:02 -0700)
From: Jakub Jelinek <jakub@redhat.com>

mq_notify (q, NULL)

and
struct sigevent ev = { .sigev_notify = SIGEV_NONE };
mq_notify (q, &ev)

are not the same thing in POSIX, yet the kernel treats them the same.  Only
the former makes the notification available to other processes immediately,
see

http://www.opengroup.org/onlinepubs/007904975/functions/mq_notify.html

Without the patch below,

http://sources.redhat.com/ml/libc-hacker/2004-04/msg00028.html

glibc test fails.

I looked at mq in Solaris and they behave the same in this regard as Linux
with this patch.  Kernel with this patch passes both Intel POSIX testsuite
(with testsuite fixes from Ulrich) and glibc mq testsuite.

ipc/mqueue.c

index f81441d63564b048e4b65d7e19441a14a39b7a94..631de851edf061344378418f8752272ada01604e 100644 (file)
@@ -65,8 +65,8 @@ struct mqueue_inode_info {
        struct msg_msg **messages;
        struct mq_attr attr;
 
-       struct sigevent notify; /* notify.sigev_notify == SIGEV_NONE means */
-       pid_t notify_owner;     /*           no notification registered */
+       struct sigevent notify;
+       pid_t notify_owner;
        struct sock *notify_sock;
        struct sk_buff *notify_cookie;
 
@@ -122,7 +122,7 @@ static struct inode *mqueue_get_inode(struct super_block *sb, int mode)
                        init_waitqueue_head(&info->wait_q);
                        INIT_LIST_HEAD(&info->e_wait_q[0].list);
                        INIT_LIST_HEAD(&info->e_wait_q[1].list);
-                       info->notify.sigev_notify = SIGEV_NONE;
+                       info->notify_owner = 0;
                        info->qsize = 0;
                        memset(&info->attr, 0, sizeof(info->attr));
                        info->attr.mq_maxmsg = DFLT_MSGMAX;
@@ -286,11 +286,11 @@ static ssize_t mqueue_read_file(struct file *filp, char __user *u_data,
        snprintf(buffer, sizeof(buffer),
                        "QSIZE:%-10lu NOTIFY:%-5d SIGNO:%-5d NOTIFY_PID:%-6d\n",
                        info->qsize,
-                       info->notify.sigev_notify,
-                       (info->notify.sigev_notify == SIGEV_SIGNAL ) ?
+                       info->notify_owner ? info->notify.sigev_notify : 0,
+                       (info->notify_owner &&
+                        info->notify.sigev_notify == SIGEV_SIGNAL) ?
                                info->notify.sigev_signo : 0,
-                       (info->notify.sigev_notify != SIGEV_NONE) ?
-                               info->notify_owner : 0);
+                       info->notify_owner);
        spin_unlock(&info->lock);
        buffer[sizeof(buffer)-1] = '\0';
        slen = strlen(buffer)+1;
@@ -315,8 +315,7 @@ static int mqueue_flush_file(struct file *filp)
        struct mqueue_inode_info *info = MQUEUE_I(filp->f_dentry->d_inode);
 
        spin_lock(&info->lock);
-       if (info->notify.sigev_notify != SIGEV_NONE &&
-                       current->tgid == info->notify_owner)
+       if (current->tgid == info->notify_owner)
                remove_notification(info);
 
        spin_unlock(&info->lock);
@@ -455,11 +454,14 @@ static void __do_notify(struct mqueue_inode_info *info)
         * waiting synchronously for message AND state of queue changed from
         * empty to not empty. Here we are sure that no one is waiting
         * synchronously. */
-       if (info->notify.sigev_notify != SIGEV_NONE &&
-                       info->attr.mq_curmsgs == 1) {
-               /* sends signal */
-               if (info->notify.sigev_notify == SIGEV_SIGNAL) {
-                       struct siginfo sig_i;
+       if (info->notify_owner &&
+           info->attr.mq_curmsgs == 1) {
+               struct siginfo sig_i;
+               switch (info->notify.sigev_notify) {
+               case SIGEV_NONE:
+                       break;
+               case SIGEV_SIGNAL:
+                       /* sends signal */
 
                        sig_i.si_signo = info->notify.sigev_signo;
                        sig_i.si_errno = 0;
@@ -470,13 +472,15 @@ static void __do_notify(struct mqueue_inode_info *info)
 
                        kill_proc_info(info->notify.sigev_signo,
                                       &sig_i, info->notify_owner);
-               } else if (info->notify.sigev_notify == SIGEV_THREAD) {
+                       break;
+               case SIGEV_THREAD:
                        set_cookie(info->notify_cookie, NOTIFY_WOKENUP);
                        netlink_sendskb(info->notify_sock,
                                        info->notify_cookie, 0);
+                       break;
                }
                /* after notification unregisters process */
-               info->notify.sigev_notify = SIGEV_NONE;
+               info->notify_owner = 0;
        }
        wake_up(&info->wait_q);
 }
@@ -514,11 +518,12 @@ static long prepare_timeout(const struct timespec __user *u_arg)
 
 static void remove_notification(struct mqueue_inode_info *info)
 {
-       if (info->notify.sigev_notify == SIGEV_THREAD) {
+       if (info->notify_owner != 0 &&
+           info->notify.sigev_notify == SIGEV_THREAD) {
                set_cookie(info->notify_cookie, NOTIFY_REMOVED);
                netlink_sendskb(info->notify_sock, info->notify_cookie, 0);
        }
-       info->notify.sigev_notify = SIGEV_NONE;
+       info->notify_owner = 0;
 }
 
 /*
@@ -908,9 +913,9 @@ out:
 }
 
 /*
- * Notes: the case when user wants us to deregister (with NULL as pointer
- * or SIGEV_NONE) and he isn't currently owner of notification will be
- * silently discarded. It isn't explicitly defined in the POSIX.
+ * Notes: the case when user wants us to deregister (with NULL as pointer)
+ * and he isn't currently owner of notification, will be silently discarded.
+ * It isn't explicitly defined in the POSIX.
  */
 asmlinkage long sys_mq_notify(mqd_t mqdes,
                                const struct sigevent __user *u_notification)
@@ -925,9 +930,7 @@ asmlinkage long sys_mq_notify(mqd_t mqdes,
 
        nc = NULL;
        sock = NULL;
-       if (u_notification == NULL) {
-               notification.sigev_notify = SIGEV_NONE;
-       } else {
+       if (u_notification != NULL) {
                if (copy_from_user(&notification, u_notification,
                                        sizeof(struct sigevent)))
                        return -EFAULT;
@@ -993,35 +996,31 @@ retry:
 
        ret = 0;
        spin_lock(&info->lock);
-       switch (notification.sigev_notify) {
-       case SIGEV_NONE:
-               if (info->notify.sigev_notify != SIGEV_NONE &&
-                               info->notify_owner == current->tgid) {
+       if (u_notification == NULL) {
+               if (info->notify_owner == current->tgid) {
                        remove_notification(info);
                        inode->i_atime = inode->i_ctime = CURRENT_TIME;
                }
-               break;
-       case SIGEV_THREAD:
-               if (info->notify.sigev_notify != SIGEV_NONE) {
-                       ret = -EBUSY;
+       } else if (info->notify_owner != 0) {
+               ret = -EBUSY;
+       } else {
+               switch (notification.sigev_notify) {
+               case SIGEV_NONE:
+                       info->notify.sigev_notify = SIGEV_NONE;
                        break;
-               }
-               info->notify_sock = sock;
-               info->notify_cookie = nc;
-               sock = NULL;
-               nc = NULL;
-               info->notify.sigev_notify = SIGEV_THREAD;
-               info->notify_owner = current->tgid;
-               inode->i_atime = inode->i_ctime = CURRENT_TIME;
-               break;
-       case SIGEV_SIGNAL:
-               if (info->notify.sigev_notify != SIGEV_NONE) {
-                       ret = -EBUSY;
+               case SIGEV_THREAD:
+                       info->notify_sock = sock;
+                       info->notify_cookie = nc;
+                       sock = NULL;
+                       nc = NULL;
+                       info->notify.sigev_notify = SIGEV_THREAD;
+                       break;
+               case SIGEV_SIGNAL:
+                       info->notify.sigev_signo = notification.sigev_signo;
+                       info->notify.sigev_value = notification.sigev_value;
+                       info->notify.sigev_notify = SIGEV_SIGNAL;
                        break;
                }
-               info->notify.sigev_signo = notification.sigev_signo;
-               info->notify.sigev_value = notification.sigev_value;
-               info->notify.sigev_notify = SIGEV_SIGNAL;
                info->notify_owner = current->tgid;
                inode->i_atime = inode->i_ctime = CURRENT_TIME;
        }