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 <sys/queue.h>
 
+#include <linux/sched.h>
 #include <linux/ww_mutex.h>
 
 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 <linux/wait.h>
 
 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;
        }
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to