> Date: Wed, 5 Jun 2019 10:16:25 +0200
> From: Sebastien Marie <[email protected]>
> 
> Hi,
> 
> I would like to have feedback and testing on this diff. The initial work
> was done by ian@.

Don't forget to run "make syscalls" in sys/kern when building your own
kernel with this diff!

> It unlocks read(2) and write(2) families, and push the KERNEL_LOCK
> deeper in the code path. With it, read(2) and write(2) on socket will be
> KERNEL_LOCK-free.
> 
> read(2) and write(2) are file type agnostics by using `struct fileops'
> (see sys/file.h) which define effective implementation per file type.
> 
> Currently, we have the following fileops defined:
> - dmabuf      dev/pci/drm/drm_linux.c
> - kqueue      kern/kern_event.c
> - pipe                kern/sys_pipe.c
> - socket      kern/sys_socket.c
> - vnode               kern/vfs_vnops.c
> 
> 
> read(2) family uses dofilereadv(), which calls:
> - fileops::fo_read()
> - FRELE() -> fdrop() -> fileops::fo_close()
> 
> write(2) family uses dofilewritev(), which calls:
> - fileops::fo_write()
> - FRELE() -> fdrop() -> fileops::fo_close()
> 
> 
> The diff pushs the KERNEL_LOCK inside each function, where it is
> effectively needed. But as some file types doesn't need the lock (socket
> for example), it makes read(2) and write(2) lock-free for them, while
> still grabbing the lock for others (vnode for example).

The socket calls will still grab the kernel lock for selwakeup and
sending signals of course.  But some of them will indeed be lock free.

It should be fairly easy to also unlock pipes, but that can come
later.

> fileops::fo_read
> - dmabuf_read fine lock-free (just return ENXIO)
> - kqueue_read fine lock-free (just return ENXIO)
> - pipe_read   KERNEL_LOCK/UNLOCK added (tsleep, wakeup, selwakeup)
> - soo_read    calls soreceive() which is already called without KERNEL_LOCK 
> by recvit() (via recvmsg(2))
> - vn_read     KERNEL_LOCK/UNLOCK added
> 
> fileops::fo_write
> - dmabuf_write        fine lock-free (just return ENXIO)
> - kqueue_write        fine lock-free (just return ENXIO)
> - pipe_write  KERNEL_LOCK/UNLOCK added (tsleep, wakeup, selwakeup)
> - soo_write   calls sosend() which is already called without KERNEL_LOCK by 
> sendit() (via sendmsg(2))
> - vn_write    KERNEL_LOCK/UNLOCK added
> 
> fileops::fo_close
> - dmabuf_close        already take care of it
> - kqueue_close        already take care of it
> - pipe_close  already take care of it
> - soo_close   call soclose() which is already called without KERNEL_LOCK by 
> socketpair(2)
> - vn_closefile        already take care of it
> 
> 
> In dofilewritev(), I take care of calling ptsignal() with the
> KERNEL_LOCK() too, as it requires the lock (it is asserted).
> 
> Others functions should be fine to be called without the KERNEL_LOCK, as
> they are already used in such context.

Looks good to me.  Certainly needs some testing especially on setups
that do a lot of socket communication.  But if testing doesn't reveal
any problems this is ok kettenis@

> Index: kern/sys_generic.c
> ===================================================================
> RCS file: /cvs/src/sys/kern/sys_generic.c,v
> retrieving revision 1.123
> diff -u -p -r1.123 sys_generic.c
> --- kern/sys_generic.c        21 Jan 2019 23:41:26 -0000      1.123
> +++ kern/sys_generic.c        4 Jun 2019 06:19:23 -0000
> @@ -366,8 +366,11 @@ dofilewritev(struct proc *p, int fd, str
>               if (uio->uio_resid != cnt && (error == ERESTART ||
>                   error == EINTR || error == EWOULDBLOCK))
>                       error = 0;
> -             if (error == EPIPE)
> +             if (error == EPIPE) {
> +                     KERNEL_LOCK();
>                       ptsignal(p, SIGPIPE, STHREAD);
> +                     KERNEL_UNLOCK();
> +             }
>       }
>       cnt -= uio->uio_resid;
>  
> Index: kern/sys_pipe.c
> ===================================================================
> RCS file: /cvs/src/sys/kern/sys_pipe.c,v
> retrieving revision 1.87
> diff -u -p -r1.87 sys_pipe.c
> --- kern/sys_pipe.c   13 Nov 2018 13:02:20 -0000      1.87
> +++ kern/sys_pipe.c   5 Jun 2019 08:06:19 -0000
> @@ -314,9 +314,11 @@ pipe_read(struct file *fp, struct uio *u
>       int error;
>       size_t size, nread = 0;
>  
> +     KERNEL_LOCK();
> +
>       error = pipelock(rpipe);
>       if (error)
> -             return (error);
> +             goto done;
>  
>       ++rpipe->pipe_busy;
>  
> @@ -420,6 +422,8 @@ unlocked_error:
>       if ((rpipe->pipe_buffer.size - rpipe->pipe_buffer.cnt) >= PIPE_BUF)
>               pipeselwakeup(rpipe);
>  
> +done:
> +     KERNEL_UNLOCK();
>       return (error);
>  }
>  
> @@ -430,6 +434,8 @@ pipe_write(struct file *fp, struct uio *
>       size_t orig_resid;
>       struct pipe *wpipe, *rpipe;
>  
> +     KERNEL_LOCK();
> +
>       rpipe = fp->f_data;
>       wpipe = rpipe->pipe_peer;
>  
> @@ -437,7 +443,8 @@ pipe_write(struct file *fp, struct uio *
>        * detect loss of pipe read side, issue SIGPIPE if lost.
>        */
>       if ((wpipe == NULL) || (wpipe->pipe_state & PIPE_EOF)) {
> -             return (EPIPE);
> +             error = EPIPE;
> +             goto done;
>       }
>       ++wpipe->pipe_busy;
>  
> @@ -471,7 +478,7 @@ pipe_write(struct file *fp, struct uio *
>                       wpipe->pipe_state &= ~(PIPE_WANT | PIPE_WANTR);
>                       wakeup(wpipe);
>               }
> -             return (error);
> +             goto done;
>       }
>  
>       orig_resid = uio->uio_resid;
> @@ -642,6 +649,8 @@ retrywrite:
>       if (wpipe->pipe_buffer.cnt)
>               pipeselwakeup(wpipe);
>  
> +done:
> +     KERNEL_UNLOCK();
>       return (error);
>  }
>  
> Index: kern/syscalls.master
> ===================================================================
> RCS file: /cvs/src/sys/kern/syscalls.master,v
> retrieving revision 1.191
> diff -u -p -r1.191 syscalls.master
> --- kern/syscalls.master      13 May 2019 22:55:27 -0000      1.191
> +++ kern/syscalls.master      5 Jun 2019 08:06:19 -0000
> @@ -51,8 +51,8 @@
>  0    INDIR           { int sys_syscall(int number, ...); }
>  1    STD             { void sys_exit(int rval); }
>  2    STD             { int sys_fork(void); }
> -3    STD             { ssize_t sys_read(int fd, void *buf, size_t nbyte); }
> -4    STD             { ssize_t sys_write(int fd, const void *buf, \
> +3    STD NOLOCK      { ssize_t sys_read(int fd, void *buf, size_t nbyte); }
> +4    STD NOLOCK      { ssize_t sys_write(int fd, const void *buf, \
>                           size_t nbyte); }
>  5    STD             { int sys_open(const char *path, \
>                           int flags, ... mode_t mode); }
> @@ -247,9 +247,9 @@
>  118  STD             { int sys_getsockopt(int s, int level, int name, \
>                           void *val, socklen_t *avalsize); }
>  119  STD             { int sys_thrkill(pid_t tid, int signum, void *tcb); }
> -120  STD             { ssize_t sys_readv(int fd, \
> +120  STD NOLOCK      { ssize_t sys_readv(int fd, \
>                           const struct iovec *iovp, int iovcnt); }
> -121  STD             { ssize_t sys_writev(int fd, \
> +121  STD NOLOCK      { ssize_t sys_writev(int fd, \
>                           const struct iovec *iovp, int iovcnt); }
>  122  STD             { int sys_kill(int pid, int signum); }
>  123  STD             { int sys_fchown(int fd, uid_t uid, gid_t gid); }
> @@ -316,9 +316,9 @@
>  170  OBSOL           msgsys10
>  171  OBSOL           shmsys10
>  172  UNIMPL
> -173  STD             { ssize_t sys_pread(int fd, void *buf, \
> +173  STD NOLOCK      { ssize_t sys_pread(int fd, void *buf, \
>                         size_t nbyte, int pad, off_t offset); }
> -174  STD             { ssize_t sys_pwrite(int fd, const void *buf, \
> +174  STD NOLOCK      { ssize_t sys_pwrite(int fd, const void *buf, \
>                         size_t nbyte, int pad, off_t offset); }
>  175  UNIMPL          ntp_gettime
>  176  UNIMPL          ntp_adjtime
> @@ -452,10 +452,10 @@
>  264  STD             { int sys_fhopen(const fhandle_t *fhp, int flags); }
>  265  UNIMPL
>  266  UNIMPL
> -267  STD             { ssize_t sys_preadv(int fd, \
> +267  STD NOLOCK      { ssize_t sys_preadv(int fd, \
>                         const struct iovec *iovp, int iovcnt, \
>                         int pad, off_t offset); }
> -268  STD             { ssize_t sys_pwritev(int fd, \
> +268  STD NOLOCK      { ssize_t sys_pwritev(int fd, \
>                         const struct iovec *iovp, int iovcnt, \
>                         int pad, off_t offset); }
>  269  STD             { int sys_kqueue(void); }
> Index: kern/vfs_vnops.c
> ===================================================================
> RCS file: /cvs/src/sys/kern/vfs_vnops.c,v
> retrieving revision 1.97
> diff -u -p -r1.97 vfs_vnops.c
> --- kern/vfs_vnops.c  20 Aug 2018 16:00:22 -0000      1.97
> +++ kern/vfs_vnops.c  5 Jun 2019 08:06:19 -0000
> @@ -343,6 +343,8 @@ vn_read(struct file *fp, struct uio *uio
>       off_t offset;
>       int error;
>  
> +     KERNEL_LOCK();
> +
>       /*
>        * Check below can race.  We can block on the vnode lock
>        * and resume with a different `fp->f_offset' value.
> @@ -353,11 +355,15 @@ vn_read(struct file *fp, struct uio *uio
>               offset = uio->uio_offset;
>  
>       /* no wrap around of offsets except on character devices */
> -     if (vp->v_type != VCHR && count > LLONG_MAX - offset)
> -             return (EINVAL);
> +     if (vp->v_type != VCHR && count > LLONG_MAX - offset) {
> +             error = EINVAL;
> +             goto done;
> +     }
>  
> -     if (vp->v_type == VDIR)
> -             return (EISDIR);
> +     if (vp->v_type == VDIR) {
> +             error = EISDIR;
> +             goto done;
> +     }
>  
>       vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
>       if ((fflags & FO_POSITION) == 0)
> @@ -367,6 +373,8 @@ vn_read(struct file *fp, struct uio *uio
>       if ((fflags & FO_POSITION) == 0)
>               fp->f_offset += count - uio->uio_resid;
>       VOP_UNLOCK(vp);
> +done:
> +     KERNEL_UNLOCK();
>       return (error);
>  }
>  
> @@ -381,6 +389,8 @@ vn_write(struct file *fp, struct uio *ui
>       int error, ioflag = IO_UNIT;
>       size_t count;
>  
> +     KERNEL_LOCK();
> +
>       /* note: pwrite/pwritev are unaffected by O_APPEND */
>       if (vp->v_type == VREG && (fp->f_flag & O_APPEND) &&
>           (fflags & FO_POSITION) == 0)
> @@ -402,6 +412,8 @@ vn_write(struct file *fp, struct uio *ui
>                       fp->f_offset += count - uio->uio_resid;
>       }
>       VOP_UNLOCK(vp);
> +
> +     KERNEL_UNLOCK();
>       return (error);
>  }
>  
> Index: sys/file.h
> ===================================================================
> RCS file: /cvs/src/sys/sys/file.h,v
> retrieving revision 1.53
> diff -u -p -r1.53 file.h
> --- sys/file.h        20 Aug 2018 16:00:22 -0000      1.53
> +++ sys/file.h        5 Jun 2019 08:06:19 -0000
> @@ -46,6 +46,13 @@ struct stat;
>  struct file;
>  struct ucred;
>  
> +/**
> + * File operations.
> + * The following entries could be called without KERNEL_LOCK hold:
> + * - fo_read
> + * - fo_write
> + * - fo_close
> + */
>  struct       fileops {
>       int     (*fo_read)(struct file *, struct uio *, int);
>       int     (*fo_write)(struct file *, struct uio *, int);
> 
> 

Reply via email to