From 4a674f34ba04a002244edaf891b5da7fc1473ae8 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky <skinsbursky@parallels.com> Date: Fri, 4 Jan 2013 15:34:55 -0800 Subject: ipc: introduce message queue copy feature This patch is required for checkpoint/restore in userspace. c/r requires some way to get all pending IPC messages without deleting them from the queue (checkpoint can fail and in this case tasks will be resumed, so queue have to be valid). To achive this, new operation flag MSG_COPY for sys_msgrcv() system call was introduced. If this flag was specified, then mtype is interpreted as number of the message to copy. If MSG_COPY is set, then kernel will allocate dummy message with passed size, and then use new copy_msg() helper function to copy desired message (instead of unlinking it from the queue). Notes: 1) Return -ENOSYS if MSG_COPY is specified, but CONFIG_CHECKPOINT_RESTORE is not set. Signed-off-by: Stanislav Kinsbursky <skinsbursky@parallels.com> Cc: Serge Hallyn <serge.hallyn@canonical.com> Cc: "Eric W. Biederman" <ebiederm@xmission.com> Cc: Pavel Emelyanov <xemul@parallels.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> Cc: Michael Kerrisk <mtk.manpages@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- ipc/msgutil.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'ipc/msgutil.c') diff --git a/ipc/msgutil.c b/ipc/msgutil.c index 6471f1bdae9..7eecdad40ef 100644 --- a/ipc/msgutil.c +++ b/ipc/msgutil.c @@ -102,7 +102,45 @@ out_err: free_msg(msg); return ERR_PTR(err); } +#ifdef CONFIG_CHECKPOINT_RESTORE +struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst) +{ + struct msg_msgseg *dst_pseg, *src_pseg; + int len = src->m_ts; + int alen; + + BUG_ON(dst == NULL); + if (src->m_ts > dst->m_ts) + return ERR_PTR(-EINVAL); + + alen = len; + if (alen > DATALEN_MSG) + alen = DATALEN_MSG; + + dst->next = NULL; + dst->security = NULL; + memcpy(dst + 1, src + 1, alen); + + len -= alen; + dst_pseg = dst->next; + src_pseg = src->next; + while (len > 0) { + alen = len; + if (alen > DATALEN_SEG) + alen = DATALEN_SEG; + memcpy(dst_pseg + 1, src_pseg + 1, alen); + dst_pseg = dst_pseg->next; + len -= alen; + src_pseg = src_pseg->next; + } + + dst->m_type = src->m_type; + dst->m_ts = src->m_ts; + + return dst; +} +#endif int store_msg(void __user *dest, struct msg_msg *msg, int len) { int alen; -- cgit v1.2.3-70-g09d2 From 51eeacaa07d1372a7bc9612548ffe6cd846f4f2f Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky <skinsbursky@parallels.com> Date: Fri, 4 Jan 2013 15:35:01 -0800 Subject: ipc: simplify message copying Remove the redundant and confusing fill_copy(). Also add copy_msg() check for error. In this case exit from the function have to be done instead of break, because further code interprets any error as EAGAIN. Also define copy_msg() for the case when CONFIG_CHECKPOINT_RESTORE is disabled. Signed-off-by: Stanislav Kinsbursky <skinsbursky@parallels.com> Cc: "Eric W. Biederman" <ebiederm@xmission.com> Cc: James Morris <jmorris@namei.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- ipc/msg.c | 25 +++++++++---------------- ipc/msgutil.c | 5 +++++ 2 files changed, 14 insertions(+), 16 deletions(-) (limited to 'ipc/msgutil.c') diff --git a/ipc/msg.c b/ipc/msg.c index 038a7d79eb0..8493e1d7e35 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -770,16 +770,6 @@ static long do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz) } #ifdef CONFIG_CHECKPOINT_RESTORE -static inline struct msg_msg *fill_copy(unsigned long copy_nr, - unsigned long msg_nr, - struct msg_msg *msg, - struct msg_msg *copy) -{ - if (copy_nr == msg_nr) - return copy_msg(msg, copy); - return NULL; -} - static inline struct msg_msg *prepare_copy(void __user *buf, size_t bufsz, int msgflg, long *msgtyp, unsigned long *copy_number) @@ -803,8 +793,6 @@ static inline void free_copy(struct msg_msg *copy) free_msg(copy); } #else -#define fill_copy(copy_nr, msg_nr, msg, copy) NULL - static inline struct msg_msg *prepare_copy(void __user *buf, size_t bufsz, int msgflg, long *msgtyp, unsigned long *copy_number) @@ -868,11 +856,16 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, walk_msg->m_type != 1) { msgtyp = walk_msg->m_type - 1; } else if (msgflg & MSG_COPY) { - msg = fill_copy(copy_number, - msg_counter, - walk_msg, copy); - if (msg) + if (copy_number == msg_counter) { + /* + * Found requested message. + * Copy it. + */ + msg = copy_msg(msg, copy); + if (IS_ERR(msg)) + goto out_unlock; break; + } } else break; msg_counter++; diff --git a/ipc/msgutil.c b/ipc/msgutil.c index 7eecdad40ef..ebfcbfa8b7f 100644 --- a/ipc/msgutil.c +++ b/ipc/msgutil.c @@ -140,6 +140,11 @@ struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst) return dst; } +#else +struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst) +{ + return ERR_PTR(-ENOSYS); +} #endif int store_msg(void __user *dest, struct msg_msg *msg, int len) { -- cgit v1.2.3-70-g09d2