Commit 4c538514 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Fix mq_notify with SIGEV_NONE notification

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.
parent 798e2cde
......@@ -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 @@ asmlinkage ssize_t sys_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr,
}
/*
* 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 @@ asmlinkage long sys_mq_notify(mqd_t mqdes,
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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment