Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f6dfb4fd authored by Davide Libenzi's avatar Davide Libenzi Committed by Linus Torvalds
Browse files

[PATCH] Add epoll compat_ code to fs/compat.c



IA64 and ARM-OABI are currently using their own version of epoll compat_
code.

An architecture needs epoll_event translation if alignof(u64) in 32 bit
mode is different from alignof(u64) in 64 bit mode.  If an architecture
needs epoll_event translation, it must define struct compat_epoll_event in
asm/compat.h and set CONFIG_HAVE_COMPAT_EPOLL_EVENT and use
compat_sys_epoll_ctl and compat_sys_epoll_wait.

All 64 bit architecture should use compat_sys_epoll_pwait.

[sfr: restructure and move to fs/compat.c, remove MIPS version
of compat_sys_epoll_pwait, use __put_user_unaligned]

Signed-off-by: default avatarStephen Rothwell <sfr@canb.auug.org.au>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Russell King <rmk@arm.linux.org.uk>
Cc: "Luck, Tony" <tony.luck@intel.com>
Cc: "David S. Miller" <davem@davemloft.net>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent b40df574
Loading
Loading
Loading
Loading
+0 −46
Original line number Original line Diff line number Diff line
@@ -564,49 +564,3 @@ _sys32_clone(nabi_no_regargs struct pt_regs regs)
	return do_fork(clone_flags, newsp, &regs, 0,
	return do_fork(clone_flags, newsp, &regs, 0,
	               parent_tidptr, child_tidptr);
	               parent_tidptr, child_tidptr);
}
}

/*
 * Implement the event wait interface for the eventpoll file. It is the kernel
 * part of the user space epoll_pwait(2).
 */
asmlinkage long compat_sys_epoll_pwait(int epfd,
	struct epoll_event __user *events, int maxevents, int timeout,
	const compat_sigset_t __user *sigmask, size_t sigsetsize)
{
	int error;
	sigset_t ksigmask, sigsaved;

	/*
	 * If the caller wants a certain signal mask to be set during the wait,
	 * we apply it here.
	 */
	if (sigmask) {
		if (sigsetsize != sizeof(sigset_t))
			return -EINVAL;
		if (!access_ok(VERIFY_READ, sigmask, sizeof(ksigmask)))
			return -EFAULT;
		if (__copy_conv_sigset_from_user(&ksigmask, sigmask))
			return -EFAULT;
		sigdelsetmask(&ksigmask, sigmask(SIGKILL) | sigmask(SIGSTOP));
		sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
	}

	error = sys_epoll_wait(epfd, events, maxevents, timeout);

	/*
	 * If we changed the signal mask, we need to restore the original one.
	 * In case we've got a signal while waiting, we do not restore the
	 * signal mask yet, and we allow do_signal() to deliver the signal on
	 * the way back to userspace, before the signal mask is restored.
	 */
	if (sigmask) {
		if (error == -EINTR) {
			memcpy(&current->saved_sigmask, &sigsaved,
				sizeof(sigsaved));
			set_thread_flag(TIF_RESTORE_SIGMASK);
		} else
			sigprocmask(SIG_SETMASK, &sigsaved, NULL);
	}

	return error;
}
+100 −0
Original line number Original line Diff line number Diff line
@@ -48,6 +48,7 @@
#include <linux/highmem.h>
#include <linux/highmem.h>
#include <linux/poll.h>
#include <linux/poll.h>
#include <linux/mm.h>
#include <linux/mm.h>
#include <linux/eventpoll.h>


#include <net/sock.h>		/* siocdevprivate_ioctl */
#include <net/sock.h>		/* siocdevprivate_ioctl */


@@ -2235,3 +2236,102 @@ long asmlinkage compat_sys_nfsservctl(int cmd, void *notused, void *notused2)
	return sys_ni_syscall();
	return sys_ni_syscall();
}
}
#endif
#endif

#ifdef CONFIG_EPOLL

#ifdef CONFIG_HAS_COMPAT_EPOLL_EVENT
asmlinkage long compat_sys_epoll_ctl(int epfd, int op, int fd,
			struct compat_epoll_event __user *event)
{
	long err = 0;
	struct compat_epoll_event user;
	struct epoll_event __user *kernel = NULL;

	if (event) {
		if (copy_from_user(&user, event, sizeof(user)))
			return -EFAULT;
		kernel = compat_alloc_user_space(sizeof(struct epoll_event));
		err |= __put_user(user.events, &kernel->events);
		err |= __put_user(user.data, &kernel->data);
	}

	return err ? err : sys_epoll_ctl(epfd, op, fd, kernel);
}


asmlinkage long compat_sys_epoll_wait(int epfd,
			struct compat_epoll_event __user *events,
			int maxevents, int timeout)
{
	long i, ret, err = 0;
	struct epoll_event __user *kbuf;
	struct epoll_event ev;

	if ((maxevents <= 0) ||
			(maxevents > (INT_MAX / sizeof(struct epoll_event))))
		return -EINVAL;
	kbuf = compat_alloc_user_space(sizeof(struct epoll_event) * maxevents);
	ret = sys_epoll_wait(epfd, kbuf, maxevents, timeout);
	for (i = 0; i < ret; i++) {
		err |= __get_user(ev.events, &kbuf[i].events);
		err |= __get_user(ev.data, &kbuf[i].data);
		err |= __put_user(ev.events, &events->events);
		err |= __put_user_unaligned(ev.data, &events->data);
		events++;
	}

	return err ? -EFAULT: ret;
}
#endif	/* CONFIG_HAS_COMPAT_EPOLL_EVENT */

#ifdef TIF_RESTORE_SIGMASK
asmlinkage long compat_sys_epoll_pwait(int epfd,
			struct compat_epoll_event __user *events,
			int maxevents, int timeout,
			const compat_sigset_t __user *sigmask,
			compat_size_t sigsetsize)
{
	long err;
	compat_sigset_t csigmask;
	sigset_t ksigmask, sigsaved;

	/*
	 * If the caller wants a certain signal mask to be set during the wait,
	 * we apply it here.
	 */
	if (sigmask) {
		if (sigsetsize != sizeof(compat_sigset_t))
			return -EINVAL;
		if (copy_from_user(&csigmask, sigmask, sizeof(csigmask)))
			return -EFAULT;
		sigset_from_compat(&ksigmask, &csigmask);
		sigdelsetmask(&ksigmask, sigmask(SIGKILL) | sigmask(SIGSTOP));
		sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
	}

#ifdef CONFIG_HAS_COMPAT_EPOLL_EVENT
	err = compat_sys_epoll_wait(epfd, events, maxevents, timeout);
#else
	err = sys_epoll_wait(epfd, events, maxevents, timeout);
#endif

	/*
	 * If we changed the signal mask, we need to restore the original one.
	 * In case we've got a signal while waiting, we do not restore the
	 * signal mask yet, and we allow do_signal() to deliver the signal on
	 * the way back to userspace, before the signal mask is restored.
	 */
	if (sigmask) {
		if (err == -EINTR) {
			memcpy(&current->saved_sigmask, &sigsaved,
			       sizeof(sigsaved));
			set_thread_flag(TIF_RESTORE_SIGMASK);
		} else
			sigprocmask(SIG_SETMASK, &sigsaved, NULL);
	}

	return err;
}
#endif /* TIF_RESTORE_SIGMASK */

#endif /* CONFIG_EPOLL */
+19 −0
Original line number Original line Diff line number Diff line
@@ -234,5 +234,24 @@ asmlinkage long compat_sys_migrate_pages(compat_pid_t pid,
		compat_ulong_t maxnode, const compat_ulong_t __user *old_nodes,
		compat_ulong_t maxnode, const compat_ulong_t __user *old_nodes,
		const compat_ulong_t __user *new_nodes);
		const compat_ulong_t __user *new_nodes);


/*
 * epoll (fs/eventpoll.c) compat bits follow ...
 */
#ifndef CONFIG_HAS_COMPAT_EPOLL_EVENT
struct epoll_event;
#define compat_epoll_event	epoll_event
#else
asmlinkage long compat_sys_epoll_ctl(int epfd, int op, int fd,
			struct compat_epoll_event __user *event);
asmlinkage long compat_sys_epoll_wait(int epfd,
			struct compat_epoll_event __user *events,
			int maxevents, int timeout);
#endif
asmlinkage long compat_sys_epoll_pwait(int epfd,
			struct compat_epoll_event __user *events,
			int maxevents, int timeout,
			const compat_sigset_t __user *sigmask,
			compat_size_t sigsetsize);

#endif /* CONFIG_COMPAT */
#endif /* CONFIG_COMPAT */
#endif /* _LINUX_COMPAT_H */
#endif /* _LINUX_COMPAT_H */