Hi,
I would like to have feedback and testing on this diff. The initial work
was done by ian@.
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).
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.
Thanks.
--
Sebastien Marie
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);