From owner-svn-src-head@freebsd.org Thu Feb 22 15:29:21 2018 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id F2905F0EE61; Thu, 22 Feb 2018 15:29:20 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id A087368D5F; Thu, 22 Feb 2018 15:29:20 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 7E9D11681; Thu, 22 Feb 2018 15:29:20 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id w1MFTKOd062452; Thu, 22 Feb 2018 15:29:20 GMT (envelope-from hselasky@FreeBSD.org) Received: (from hselasky@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id w1MFTJJh062446; Thu, 22 Feb 2018 15:29:19 GMT (envelope-from hselasky@FreeBSD.org) Message-Id: <201802221529.w1MFTJJh062446@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: hselasky set sender to hselasky@FreeBSD.org using -f From: Hans Petter Selasky Date: Thu, 22 Feb 2018 15:29:19 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r329825 - in head/sys/compat/linuxkpi/common: include/linux src X-SVN-Group: head X-SVN-Commit-Author: hselasky X-SVN-Commit-Paths: in head/sys/compat/linuxkpi/common: include/linux src X-SVN-Commit-Revision: 329825 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.25 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 22 Feb 2018 15:29:21 -0000 Author: hselasky Date: Thu Feb 22 15:29:19 2018 New Revision: 329825 URL: https://svnweb.freebsd.org/changeset/base/329825 Log: Return correct error code to user-space when a system call receives a signal in the LinuxKPI. The read(), write() and mmap() system calls can return either EINTR or ERESTART upon receiving a signal. Add code to figure out the correct return value by temporarily storing the return code from the relevant FreeBSD kernel APIs in the Linux task structure. MFC after: 3 days Sponsored by: Mellanox Technologies Modified: head/sys/compat/linuxkpi/common/include/linux/mutex.h head/sys/compat/linuxkpi/common/include/linux/rwsem.h head/sys/compat/linuxkpi/common/include/linux/sched.h head/sys/compat/linuxkpi/common/src/linux_compat.c head/sys/compat/linuxkpi/common/src/linux_lock.c head/sys/compat/linuxkpi/common/src/linux_schedule.c Modified: head/sys/compat/linuxkpi/common/include/linux/mutex.h ============================================================================== --- head/sys/compat/linuxkpi/common/include/linux/mutex.h Thu Feb 22 13:32:31 2018 (r329824) +++ head/sys/compat/linuxkpi/common/include/linux/mutex.h Thu Feb 22 15:29:19 2018 (r329825) @@ -63,7 +63,7 @@ typedef struct mutex { #define mutex_lock_interruptible(_m) ({ \ MUTEX_SKIP() ? 0 : \ - (sx_xlock_sig(&(_m)->sx) ? -EINTR : 0); \ + linux_mutex_lock_interruptible(_m); \ }) #define mutex_unlock(_m) do { \ @@ -142,5 +142,7 @@ linux_mutex_destroy(mutex_t *m) mutex_unlock(m); sx_destroy(&m->sx); } + +extern int linux_mutex_lock_interruptible(mutex_t *m); #endif /* _LINUX_MUTEX_H_ */ Modified: head/sys/compat/linuxkpi/common/include/linux/rwsem.h ============================================================================== --- head/sys/compat/linuxkpi/common/include/linux/rwsem.h Thu Feb 22 13:32:31 2018 (r329824) +++ head/sys/compat/linuxkpi/common/include/linux/rwsem.h Thu Feb 22 15:29:19 2018 (r329825) @@ -47,7 +47,7 @@ struct rw_semaphore { #define up_read(_rw) sx_sunlock(&(_rw)->sx) #define down_read_trylock(_rw) !!sx_try_slock(&(_rw)->sx) #define down_write_trylock(_rw) !!sx_try_xlock(&(_rw)->sx) -#define down_write_killable(_rw) !!sx_xlock_sig(&(_rw)->sx) +#define down_write_killable(_rw) linux_down_write_killable(_rw) #define downgrade_write(_rw) sx_downgrade(&(_rw)->sx) #define down_read_nested(_rw, _sc) down_read(_rw) #define init_rwsem(_rw) linux_init_rwsem(_rw, rwsem_name("lnxrwsem")) @@ -78,5 +78,7 @@ linux_init_rwsem(struct rw_semaphore *rw, const char * memset(rw, 0, sizeof(*rw)); sx_init_flags(&rw->sx, name, SX_NOWITNESS); } + +extern int linux_down_write_killable(struct rw_semaphore *); #endif /* _LINUX_RWSEM_H_ */ Modified: head/sys/compat/linuxkpi/common/include/linux/sched.h ============================================================================== --- head/sys/compat/linuxkpi/common/include/linux/sched.h Thu Feb 22 13:32:31 2018 (r329824) +++ head/sys/compat/linuxkpi/common/include/linux/sched.h Thu Feb 22 15:29:19 2018 (r329825) @@ -2,7 +2,7 @@ * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. - * Copyright (c) 2013-2017 Mellanox Technologies, Ltd. + * Copyright (c) 2013-2018 Mellanox Technologies, Ltd. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -77,6 +77,7 @@ struct task_struct { struct completion exited; TAILQ_ENTRY(task_struct) rcu_entry; int rcu_recurse; + int bsd_interrupt_value; }; #define current ({ \ @@ -127,11 +128,25 @@ void linux_send_sig(int signo, struct task_struct *tas #define signal_pending_state(state, task) \ linux_signal_pending_state(state, task) #define send_sig(signo, task, priv) do { \ - CTASSERT(priv == 0); \ + CTASSERT((priv) == 0); \ linux_send_sig(signo, task); \ } while (0) int linux_schedule_timeout(int timeout); + +static inline void +linux_schedule_save_interrupt_value(struct task_struct *task, int value) +{ + task->bsd_interrupt_value = value; +} + +static inline int +linux_schedule_get_interrupt_value(struct task_struct *task) +{ + int value = task->bsd_interrupt_value; + task->bsd_interrupt_value = 0; + return (value); +} #define schedule() \ (void)linux_schedule_timeout(MAX_SCHEDULE_TIMEOUT) Modified: head/sys/compat/linuxkpi/common/src/linux_compat.c ============================================================================== --- head/sys/compat/linuxkpi/common/src/linux_compat.c Thu Feb 22 13:32:31 2018 (r329824) +++ head/sys/compat/linuxkpi/common/src/linux_compat.c Thu Feb 22 15:29:19 2018 (r329825) @@ -2,7 +2,7 @@ * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. - * Copyright (c) 2013-2017 Mellanox Technologies, Ltd. + * Copyright (c) 2013-2018 Mellanox Technologies, Ltd. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -828,10 +828,27 @@ linux_access_ok(int rw, const void *uaddr, size_t len) (eaddr > saddr && eaddr <= VM_MAXUSER_ADDRESS)); } +/* + * This function should return either EINTR or ERESTART depending on + * the signal type sent to this thread: + */ static int +linux_get_error(struct task_struct *task, int error) +{ + /* check for signal type interrupt code */ + if (error == EINTR || error == ERESTARTSYS || error == ERESTART) { + error = -linux_schedule_get_interrupt_value(task); + if (error == 0) + error = EINTR; + } + return (error); +} + +static int linux_file_ioctl_sub(struct file *fp, struct linux_file *filp, u_long cmd, caddr_t data, struct thread *td) { + struct task_struct *task = current; unsigned size; int error; @@ -844,8 +861,8 @@ linux_file_ioctl_sub(struct file *fp, struct linux_fil * Background: Linux code expects a user-space address * while FreeBSD supplies a kernel-space address. */ - current->bsd_ioctl_data = data; - current->bsd_ioctl_len = size; + task->bsd_ioctl_data = data; + task->bsd_ioctl_len = size; data = (void *)LINUX_IOCTL_MIN_PTR; } else { /* fetch user-space pointer */ @@ -869,16 +886,17 @@ linux_file_ioctl_sub(struct file *fp, struct linux_fil else error = ENOTTY; if (size > 0) { - current->bsd_ioctl_data = NULL; - current->bsd_ioctl_len = 0; + task->bsd_ioctl_data = NULL; + task->bsd_ioctl_len = 0; } if (error == EWOULDBLOCK) { /* update kqfilter status, if any */ linux_file_kqfilter_poll(filp, LINUX_KQ_FLAG_HAS_READ | LINUX_KQ_FLAG_HAS_WRITE); - } else if (error == ERESTARTSYS) - error = ERESTART; + } else { + error = linux_get_error(task, error); + } return (error); } @@ -1111,6 +1129,7 @@ linux_file_mmap_single(struct file *fp, vm_ooffset_t * vm_size_t size, struct vm_object **object, int nprot, struct thread *td) { + struct task_struct *task; struct vm_area_struct *vmap; struct mm_struct *mm; struct linux_file *filp; @@ -1132,7 +1151,8 @@ linux_file_mmap_single(struct file *fp, vm_ooffset_t * * The atomic reference below makes sure the mm_struct is * available as long as the vmap is in the linux_vma_head. */ - mm = current->mm; + task = current; + mm = task->mm; if (atomic_inc_not_zero(&mm->mm_users) == 0) return (EINVAL); @@ -1147,11 +1167,10 @@ linux_file_mmap_single(struct file *fp, vm_ooffset_t * vmap->vm_mm = mm; if (unlikely(down_write_killable(&vmap->vm_mm->mmap_sem))) { - error = EINTR; + error = linux_get_error(task, EINTR); } else { error = -OPW(fp, td, filp->f_op->mmap(filp, vmap)); - if (error == ERESTARTSYS) - error = ERESTART; + error = linux_get_error(task, error); up_write(&vmap->vm_mm->mmap_sem); } @@ -1290,9 +1309,7 @@ linux_file_read(struct file *file, struct uio *uio, st uio->uio_iov->iov_len -= bytes; uio->uio_resid -= bytes; } else { - error = -bytes; - if (error == ERESTARTSYS) - error = ERESTART; + error = linux_get_error(current, -bytes); } } else error = ENXIO; @@ -1329,9 +1346,7 @@ linux_file_write(struct file *file, struct uio *uio, s uio->uio_iov->iov_len -= bytes; uio->uio_resid -= bytes; } else { - error = -bytes; - if (error == ERESTARTSYS) - error = ERESTART; + error = linux_get_error(current, -bytes); } } else error = ENXIO; @@ -1780,6 +1795,7 @@ linux_complete_common(struct completion *c, int all) int linux_wait_for_common(struct completion *c, int flags) { + struct task_struct *task; int error; if (SCHEDULER_STOPPED()) @@ -1787,6 +1803,8 @@ linux_wait_for_common(struct completion *c, int flags) DROP_GIANT(); + task = current; + if (flags != 0) flags = SLEEPQ_INTERRUPTIBLE | SLEEPQ_SLEEP; else @@ -1798,7 +1816,9 @@ linux_wait_for_common(struct completion *c, int flags) break; sleepq_add(c, NULL, "completion", flags, 0); if (flags & SLEEPQ_INTERRUPTIBLE) { - if (sleepq_wait_sig(c, 0) != 0) { + error = -sleepq_wait_sig(c, 0); + if (error != 0) { + linux_schedule_save_interrupt_value(task, error); error = -ERESTARTSYS; goto intr; } @@ -1820,22 +1840,22 @@ intr: int linux_wait_for_timeout_common(struct completion *c, int timeout, int flags) { + struct task_struct *task; int end = jiffies + timeout; int error; - int ret; if (SCHEDULER_STOPPED()) return (0); DROP_GIANT(); + task = current; + if (flags != 0) flags = SLEEPQ_INTERRUPTIBLE | SLEEPQ_SLEEP; else flags = SLEEPQ_SLEEP; - error = 0; - ret = 0; for (;;) { sleepq_lock(c); if (c->done) @@ -1843,26 +1863,30 @@ linux_wait_for_timeout_common(struct completion *c, in sleepq_add(c, NULL, "completion", flags, 0); sleepq_set_timeout(c, linux_timer_jiffies_until(end)); if (flags & SLEEPQ_INTERRUPTIBLE) - ret = sleepq_timedwait_sig(c, 0); + error = -sleepq_timedwait_sig(c, 0); else - ret = sleepq_timedwait(c, 0); - if (ret != 0) { - /* check for timeout or signal */ - if (ret == EWOULDBLOCK) - error = 0; - else + error = -sleepq_timedwait(c, 0); + if (error != 0) { + /* check for timeout */ + if (error == -EWOULDBLOCK) { + error = 0; /* timeout */ + } else { + /* signal happened */ + linux_schedule_save_interrupt_value(task, error); error = -ERESTARTSYS; - goto intr; + } + goto done; } } c->done--; sleepq_release(c); -intr: + /* return how many jiffies are left */ + error = linux_timer_jiffies_until(end); +done: PICKUP_GIANT(); - /* return how many jiffies are left */ - return (ret != 0 ? error : linux_timer_jiffies_until(end)); + return (error); } int Modified: head/sys/compat/linuxkpi/common/src/linux_lock.c ============================================================================== --- head/sys/compat/linuxkpi/common/src/linux_lock.c Thu Feb 22 13:32:31 2018 (r329824) +++ head/sys/compat/linuxkpi/common/src/linux_lock.c Thu Feb 22 15:29:19 2018 (r329825) @@ -28,6 +28,7 @@ #include +#include #include struct ww_mutex_thread { @@ -72,10 +73,13 @@ linux_ww_unlock(void) int linux_ww_mutex_lock_sub(struct ww_mutex *lock, int catch_signal) { + struct task_struct *task; struct ww_mutex_thread entry; struct ww_mutex_thread *other; int retval = 0; + task = current; + linux_ww_lock(); if (unlikely(sx_try_xlock(&lock->base.sx) == 0)) { entry.thread = curthread; @@ -105,7 +109,9 @@ linux_ww_mutex_lock_sub(struct ww_mutex *lock, int cat } } if (catch_signal) { - if (cv_wait_sig(&lock->condvar, &ww_mutex_global) != 0) { + retval = -cv_wait_sig(&lock->condvar, &ww_mutex_global); + if (retval != 0) { + linux_schedule_save_interrupt_value(task, retval); retval = -EINTR; goto done; } @@ -133,4 +139,30 @@ linux_ww_mutex_unlock_sub(struct ww_mutex *lock) /* wakeup a lock waiter, if any */ cv_signal(&lock->condvar); linux_ww_unlock(); +} + +int +linux_mutex_lock_interruptible(mutex_t *m) +{ + int error; + + error = -sx_xlock_sig(&m->sx); + if (error != 0) { + linux_schedule_save_interrupt_value(current, error); + error = -EINTR; + } + return (error); +} + +int +linux_down_write_killable(struct rw_semaphore *rw) +{ + int error; + + error = -sx_xlock_sig(&rw->sx); + if (error != 0) { + linux_schedule_save_interrupt_value(current, error); + error = -EINTR; + } + return (error); } Modified: head/sys/compat/linuxkpi/common/src/linux_schedule.c ============================================================================== --- head/sys/compat/linuxkpi/common/src/linux_schedule.c Thu Feb 22 13:32:31 2018 (r329824) +++ head/sys/compat/linuxkpi/common/src/linux_schedule.c Thu Feb 22 15:29:19 2018 (r329825) @@ -41,7 +41,8 @@ __FBSDID("$FreeBSD$"); #include static int -linux_add_to_sleepqueue(void *wchan, const char *wmesg, int timeout, int state) +linux_add_to_sleepqueue(void *wchan, struct task_struct *task, + const char *wmesg, int timeout, int state) { int flags, ret; @@ -66,8 +67,10 @@ linux_add_to_sleepqueue(void *wchan, const char *wmesg ret = -sleepq_timedwait(wchan, 0); } /* filter return value */ - if (ret != 0 && ret != -EWOULDBLOCK) + if (ret != 0 && ret != -EWOULDBLOCK) { + linux_schedule_save_interrupt_value(task, ret); ret = -ERESTARTSYS; + } return (ret); } @@ -235,10 +238,10 @@ linux_wait_event_common(wait_queue_head_t *wqh, wait_q PHOLD(task->task_thread->td_proc); sleepq_lock(task); if (atomic_read(&task->state) != TASK_WAKING) { - ret = linux_add_to_sleepqueue(task, "wevent", timeout, state); + ret = linux_add_to_sleepqueue(task, task, "wevent", timeout, state); } else { sleepq_release(task); - ret = linux_signal_pending_state(state, task) ? -ERESTARTSYS : 0; + ret = 0; } PRELE(task->task_thread->td_proc); @@ -253,6 +256,7 @@ int linux_schedule_timeout(int timeout) { struct task_struct *task; + int ret; int state; int remainder; @@ -270,10 +274,12 @@ linux_schedule_timeout(int timeout) sleepq_lock(task); state = atomic_read(&task->state); - if (state != TASK_WAKING) - (void)linux_add_to_sleepqueue(task, "sched", timeout, state); - else + if (state != TASK_WAKING) { + ret = linux_add_to_sleepqueue(task, task, "sched", timeout, state); + } else { sleepq_release(task); + ret = 0; + } set_task_state(task, TASK_RUNNING); PICKUP_GIANT(); @@ -283,7 +289,11 @@ linux_schedule_timeout(int timeout) /* range check return value */ remainder -= ticks; - if (remainder < 0) + + /* range check return value */ + if (ret == -ERESTARTSYS && remainder < 1) + remainder = 1; + else if (remainder < 0) remainder = 0; else if (remainder > timeout) remainder = timeout; @@ -337,7 +347,7 @@ linux_wait_on_bit_timeout(unsigned long *word, int bit break; } set_task_state(task, state); - ret = linux_add_to_sleepqueue(wchan, "wbit", timeout, state); + ret = linux_add_to_sleepqueue(wchan, task, "wbit", timeout, state); if (ret != 0) break; } @@ -374,7 +384,7 @@ linux_wait_on_atomic_t(atomic_t *a, unsigned int state break; } set_task_state(task, state); - ret = linux_add_to_sleepqueue(wchan, "watomic", 0, state); + ret = linux_add_to_sleepqueue(wchan, task, "watomic", 0, state); if (ret != 0) break; }