All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] V1 1/2] ipc: let message queues use SIGEV_THREAD_ID with mq_notify
@ 2014-08-24  3:34 Steven Stewart-Gallus
  2014-08-24  3:36 ` [PATCH] V1 2/2] " Steven Stewart-Gallus
  0 siblings, 1 reply; 3+ messages in thread
From: Steven Stewart-Gallus @ 2014-08-24  3:34 UTC (permalink / raw)
  To: linux-kernel; +Cc: akpm, davidlohr, manfred, bfields, dledford

From: Steven Stewart-Gallus <sstewartgallus00@mylangara.bc.ca>

Currently the only thread-safe way of using mq_notify with message
queues is to use the SIGEV_THREAD option. Unfortunately, existing
wrappers around such functionality spawn a thread each time a
notification happens instead of caching threads which is slow and
inconvenient for debugging. This change lets message queues use
SIGEV_THREAD_ID with mq_notify and so target notifications to only one
thread at a time which is thread-safe.

Signed-off-by: Steven Stewart-Gallus <sstewartgallus00@mylangara.bc.ca>
---

Hello! This is a simple patch I made more for the purpose of learning
how the kernel works then for any serious purpose. Currently, in my
own program I now simply use a thread pool and poll for asynchronously
running message sends and receives but a thread-safe mq_notify would
have been useful for me and possibly saved me some effort. So, I won't
be seriously bothered if this patch is rejected but I think other
people might find it useful nonetheless. I also think this behaviour
seems quite natural and appropriate to support.

I am sending this patch for review but I am pretty sure it is mistaken
in some ways. In particular, I believe this patch is wrong because I
don't believe it filters out thread ids that belong to other processes
and I am not sure how to do that.

Thank you,
Steven Stewart-Gallus

diff --git a/man3/mq_notify.3 b/man3/mq_notify.3
index a71aac4..41a1b96 100644
--- a/man3/mq_notify.3
+++ b/man3/mq_notify.3
@@ -95,6 +95,16 @@ as if it were the start function of a new thread.
 See
 .BR sigevent (7)
 for details.
+.TP
+.BR SIGEV_THREAD_ID " (Linux-specific)"
+As for
+.BR SIGEV_SIGNAL ,
+but the signal is targeted at the thread whose ID is given in
+.IR sigev_notify_thread_id ,
+which must be a thread in the same process as the caller.
+See
+.BR sigevent (7)
+for general details.
 .PP
 Only one process can be registered to receive notification
 from a message queue.
diff --git a/man7/sigevent.7 b/man7/sigevent.7
index 9cec77e..15da9fe 100644
--- a/man7/sigevent.7
+++ b/man7/sigevent.7
@@ -125,8 +125,10 @@ structure that defines attributes for the new thread (see
 .TP
 .BR SIGEV_THREAD_ID " (Linux-specific)"
 .\" | SIGEV_SIGNAL vs not?
-Currently used only by POSIX timers; see
-.BR timer_create (2).
+Currently used only by POSIX timers and message queues; see
+.BR timer_create (2)
+and
+.BR mq_notify (2).
 .SH CONFORMING TO
 POSIX.1-2001.
 .SH SEE ALSO


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH] V1 2/2] ipc: let message queues use SIGEV_THREAD_ID with mq_notify
  2014-08-24  3:34 [PATCH] V1 1/2] ipc: let message queues use SIGEV_THREAD_ID with mq_notify Steven Stewart-Gallus
@ 2014-08-24  3:36 ` Steven Stewart-Gallus
  2014-08-24  3:39   ` Steven Stewart-Gallus
  0 siblings, 1 reply; 3+ messages in thread
From: Steven Stewart-Gallus @ 2014-08-24  3:36 UTC (permalink / raw)
  To: Steven Stewart-Gallus
  Cc: akpm, davidlohr, linux-kernel, manfred, bfields, dledford

diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 4fcf39a..8ca70a2 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -88,7 +88,7 @@ struct mqueue_inode_info {
 static const struct inode_operations mqueue_dir_inode_operations;
 static const struct file_operations mqueue_file_operations;
 static const struct super_operations mqueue_super_ops;
-static void remove_notification(struct mqueue_inode_info *info);
+static bool maybe_remove_notification(struct mqueue_inode_info *info);
 
 static struct kmem_cache *mqueue_inode_cachep;
 
@@ -495,7 +495,8 @@ static ssize_t mqueue_read_file(struct file *filp, char
__user *u_data,
 			info->qsize,
 			info->notify_owner ? info->notify.sigev_notify : 0,
 			(info->notify_owner &&
-			 info->notify.sigev_notify == SIGEV_SIGNAL) ?
+			 (info->notify.sigev_notify == SIGEV_SIGNAL ||
+			  info->notify.sigev_notify == SIGEV_THREAD_ID)) ?
 				info->notify.sigev_signo : 0,
 			pid_vnr(info->notify_owner));
 	spin_unlock(&info->lock);
@@ -515,8 +516,8 @@ static int mqueue_flush_file(struct file *filp, fl_owner_t id)
 	struct mqueue_inode_info *info = MQUEUE_I(file_inode(filp));
 
 	spin_lock(&info->lock);
-	if (task_tgid(current) == info->notify_owner)
-		remove_notification(info);
+
+	maybe_remove_notification(info);
 
 	spin_unlock(&info->lock);
 	return 0;
@@ -642,6 +643,7 @@ static void __do_notify(struct mqueue_inode_info *info)
 		case SIGEV_NONE:
 			break;
 		case SIGEV_SIGNAL:
+		case SIGEV_THREAD_ID:
 			/* sends signal */
 
 			sig_i.si_signo = info->notify.sigev_signo;
@@ -684,8 +686,24 @@ static int prepare_timeout(const struct timespec __user
*u_abs_timeout,
 	return 0;
 }
 
-static void remove_notification(struct mqueue_inode_info *info)
+static bool maybe_remove_notification(struct mqueue_inode_info *info)
 {
+	struct pid *pid;
+
+	switch (info->notify.sigev_notify) {
+	case SIGEV_THREAD_ID:
+		pid = task_pid(current);
+		break;
+
+	default:
+		pid = task_tgid(current);
+		break;
+	}
+
+	if (pid != info->notify_owner) {
+		return false;
+	}
+
 	if (info->notify_owner != NULL &&
 	    info->notify.sigev_notify == SIGEV_THREAD) {
 		set_cookie(info->notify_cookie, NOTIFY_REMOVED);
@@ -695,6 +713,8 @@ static void remove_notification(struct mqueue_inode_info *info)
 	put_user_ns(info->notify_user_ns);
 	info->notify_owner = NULL;
 	info->notify_user_ns = NULL;
+
+	return true;
 }
 
 static int mq_attr_ok(struct ipc_namespace *ipc_ns, struct mq_attr *attr)
@@ -1203,12 +1223,18 @@ SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes,
 	if (u_notification != NULL) {
 		if (unlikely(notification.sigev_notify != SIGEV_NONE &&
 			     notification.sigev_notify != SIGEV_SIGNAL &&
-			     notification.sigev_notify != SIGEV_THREAD))
+			     notification.sigev_notify != SIGEV_THREAD &&
+			     notification.sigev_notify != SIGEV_THREAD_ID))
 			return -EINVAL;
-		if (notification.sigev_notify == SIGEV_SIGNAL &&
+		if ((notification.sigev_notify == SIGEV_SIGNAL ||
+		     notification.sigev_notify == SIGEV_THREAD_ID) &&
 			!valid_signal(notification.sigev_signo)) {
 			return -EINVAL;
 		}
+		if (notification.sigev_notify == SIGEV_THREAD_ID &&
+			notification.sigev_notify_thread_id <= 0) {
+			return -EINVAL;
+		}
 		if (notification.sigev_notify == SIGEV_THREAD) {
 			long timeo;
 
@@ -1270,8 +1296,7 @@ retry:
 	ret = 0;
 	spin_lock(&info->lock);
 	if (u_notification == NULL) {
-		if (info->notify_owner == task_tgid(current)) {
-			remove_notification(info);
+		if (maybe_remove_notification(info)) {
 			inode->i_atime = inode->i_ctime = CURRENT_TIME;
 		}
 	} else if (info->notify_owner != NULL) {
@@ -1286,16 +1311,39 @@ retry:
 			info->notify_cookie = nc;
 			sock = NULL;
 			nc = NULL;
+			info->notify_owner = get_pid(task_tgid(current));
 			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_owner = get_pid(task_tgid(current));
 			info->notify.sigev_notify = SIGEV_SIGNAL;
 			break;
+		case SIGEV_THREAD_ID:
+		{
+			struct pid *tid;
+			pid_t tid_nr;
+
+			tid_nr = notification.sigev_notify_thread_id;
+
+			info->notify.sigev_signo = notification.sigev_signo;
+			info->notify.sigev_value = notification.sigev_value;
+
+			tid = find_get_pid(tid_nr);
+			if (NULL == tid) {
+				ret = -ESRCH;
+				goto out_fput;
+			}
+
+			info->notify.sigev_notify_thread_id = tid_nr;
+
+			info->notify_owner = tid;
+			info->notify.sigev_notify = SIGEV_THREAD_ID;
+			break;
+		}
 		}
 
-		info->notify_owner = get_pid(task_tgid(current));
 		info->notify_user_ns = get_user_ns(current_user_ns());
 		inode->i_atime = inode->i_ctime = CURRENT_TIME;
 	}


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] V1 2/2] ipc: let message queues use SIGEV_THREAD_ID with mq_notify
  2014-08-24  3:36 ` [PATCH] V1 2/2] " Steven Stewart-Gallus
@ 2014-08-24  3:39   ` Steven Stewart-Gallus
  0 siblings, 0 replies; 3+ messages in thread
From: Steven Stewart-Gallus @ 2014-08-24  3:39 UTC (permalink / raw)
  To: Steven Stewart-Gallus
  Cc: akpm, davidlohr, linux-kernel, manfred, bfields, dledford

Any one who wants a quick way to test the changes can use the
following hacky program that works as an init program.

Thank you,
Steven Stewart-Gallus

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <mqueue.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/reboot.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <unistd.h>

/* GLibc does not expose sigev_notify_thread_id so we hack it in manually */

#ifndef __ARCH_SIGEV_PREAMBLE_SIZE
#define __ARCH_SIGEV_PREAMBLE_SIZE (sizeof(int) * 2 + sizeof(sigval_t))
#endif

#define SIGEV_MAX_SIZE	64
#define SIGEV_PAD_SIZE	((SIGEV_MAX_SIZE - __ARCH_SIGEV_PREAMBLE_SIZE)	\
			 / sizeof(int))

struct my_sigevent {
	sigval_t sigev_value;
	int sigev_signo;
	int sigev_notify;
	union {
		int _pad[SIGEV_PAD_SIZE];
		int _tid;

		struct {
			void (*_function)(sigval_t);
			void *_attribute;	/* really pthread_attr_t */
		} _sigev_thread;
	} _sigev_un;
};

#define sigev_notify_function	_sigev_un._sigev_thread._function
#define sigev_notify_attributes	_sigev_un._sigev_thread._attribute
#define sigev_notify_thread_id	 _sigev_un._tid



struct message {
	int number;
};

static mqd_t mq;

static pid_t gettid(void)
{
	return syscall(__NR_gettid);
}

static void * start_routine(void * arg)
{
	{
		struct message message = {
			.number = 4
		};
		if (-1 == mq_send(mq, (char*)&message, sizeof message, 0)) {
			perror("mq_send");
			_Exit(EXIT_FAILURE);
		}
	}

	{
		struct message message = {
			.number = 5
		};
		if (-1 == mq_send(mq, (char*)&message, sizeof message, 0)) {
			perror("mq_send");
			_Exit(EXIT_FAILURE);
		}
	}

	{
		struct message message = {
			.number = 0
		};
		if (-1 == mq_send(mq, (char*)&message, sizeof message, 0)) {
			perror("mq_send");
			_Exit(EXIT_FAILURE);
		}
	}

	return NULL;
}

int main(int argc, char *argv[])
{
	int errnum;

	/* Fork to allow signals to be received */
	{
		pid_t child = fork();
		if (-1 == child) {
			perror("fork");
			return EXIT_FAILURE;
		}

		if (child != 0) {
			siginfo_t info;
			do {
				errnum = -1 == waitid(P_PID, child, &info, WEXITED) ? errno : 0;
			} while (EINTR == errnum);
			if (errnum != 0) {
				assert(errnum != EINVAL);
				assert(errnum != ECHILD);
				assert(false);
			}
			reboot(RB_POWER_OFF);
		}
	}

	{
		struct mq_attr attr = { 0 };
		attr.mq_maxmsg = 8U;
		attr.mq_msgsize = sizeof (struct message);
		mq = mq_open("/foo", O_RDWR | O_CREAT | O_EXCL | O_NONBLOCK, 0, &attr);
	}
	if (-1 == mq) {
		perror("mq_open");
		return EXIT_FAILURE;
	}


	sigset_t rtset;
	sigemptyset(&rtset);
	sigaddset(&rtset, SIGRTMIN);
	pthread_sigmask(SIG_BLOCK, &rtset, NULL);

	pthread_t worker;
	if ((errnum = pthread_create(&worker, NULL, start_routine, NULL)) != 0) {
		errno = errnum;
		perror("pthread_create");
		return EXIT_FAILURE;
	}

	for (;;) {
		{
			struct my_sigevent sev = { 0 };

			sev.sigev_notify = SIGEV_THREAD_ID;
			sev.sigev_signo = SIGRTMIN;

			sev.sigev_notify_thread_id = gettid();

			if (-1 == mq_notify(mq,(struct sigevent*) &sev)) {
				perror("mq_notify");
				return EXIT_FAILURE;
			}
		}

		for (;;) {
			struct message message;
			if (-1 == mq_receive(mq, (char*)&message, sizeof message, 0)) {
				errnum = errno;
			} else {
				errnum = 0;
			}

			if (EAGAIN == errnum) {
				break;
			}

			if (errnum != 0) {
				errno = errnum;
				perror("mq_receive");
				return EXIT_FAILURE;
			}

			printf("received: %u\n", message.number);

			if (0 == message.number) {
				goto exit_loop;
			}
		}

		{
			int xx;
			if (-1 == sigwait(&rtset, &xx)) {
				perror("sigwait");
				return EXIT_FAILURE;
			}
		}

		/* Flush pending signals */
		pthread_sigmask(SIG_UNBLOCK, &rtset, NULL);
		pthread_sigmask(SIG_BLOCK, &rtset, NULL);
	}
exit_loop:
	return EXIT_SUCCESS;
}


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2014-08-24  3:39 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-08-24  3:34 [PATCH] V1 1/2] ipc: let message queues use SIGEV_THREAD_ID with mq_notify Steven Stewart-Gallus
2014-08-24  3:36 ` [PATCH] V1 2/2] " Steven Stewart-Gallus
2014-08-24  3:39   ` Steven Stewart-Gallus

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.