It seems that most of the problems exposed by the introduction of the
NET_LOCK() are related to the non-recursive nature of the rwlock. Some
known issues involve pflow(4), cloning interfaces an NFS.
Diff below makes use of a recursive-rwlock instead. I just finished a
build on NFS with it, so I'd like to know if it works for your use case.
Please test and report back.
Index: kern/kern_rwlock.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_rwlock.c,v
retrieving revision 1.27
diff -u -p -r1.27 kern_rwlock.c
--- kern/kern_rwlock.c 14 Mar 2015 07:33:42 -0000 1.27
+++ kern/kern_rwlock.c 3 Jan 2017 14:23:56 -0000
@@ -346,3 +346,23 @@ rrw_status(struct rrwlock *rrwl)
{
return (rw_status(&rrwl->rrwl_lock));
}
+
+int
+rrw_release_all(struct rrwlock *rrwl)
+{
+ int rv;
+
+ rv = rrwl->rrwl_wcnt;
+ KASSERT(rv >= 1);
+ rrwl->rrwl_wcnt = 1;
+ rrw_exit(rrwl);
+
+ return (rv);
+}
+
+void
+rrw_acquire_count(struct rrwlock *rrwl, int count, int flags)
+{
+ while (count--)
+ rrw_enter(rrwl, flags);
+}
Index: kern/kern_synch.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_synch.c,v
retrieving revision 1.135
diff -u -p -r1.135 kern_synch.c
--- kern/kern_synch.c 13 Sep 2016 08:32:44 -0000 1.135
+++ kern/kern_synch.c 3 Jan 2017 14:07:23 -0000
@@ -231,27 +231,28 @@ msleep(const volatile void *ident, struc
* entered the sleep queue we drop the it. After sleeping we re-lock.
*/
int
-rwsleep(const volatile void *ident, struct rwlock *wl, int priority,
+rrwsleep(const volatile void *ident, struct rrwlock *rrwl, int priority,
const char *wmesg, int timo)
{
struct sleep_state sls;
int error, error1;
+ int count;
KASSERT((priority & ~(PRIMASK | PCATCH | PNORELOCK)) == 0);
- rw_assert_wrlock(wl);
+ KASSERT(rrw_status(rrwl) == RW_WRITE);
sleep_setup(&sls, ident, priority, wmesg);
sleep_setup_timeout(&sls, timo);
sleep_setup_signal(&sls, priority);
- rw_exit_write(wl);
+ count = rrw_release_all(rrwl);
sleep_finish(&sls, 1);
error1 = sleep_finish_timeout(&sls);
error = sleep_finish_signal(&sls);
if ((priority & PNORELOCK) == 0)
- rw_enter_write(wl);
+ rrw_acquire_count(rrwl, count, RW_WRITE);
/* Signal errors are higher priority than timeouts. */
if (error == 0 && error1 != 0)
Index: kern/uipc_socket.c
===================================================================
RCS file: /cvs/src/sys/kern/uipc_socket.c,v
retrieving revision 1.171
diff -u -p -r1.171 uipc_socket.c
--- kern/uipc_socket.c 29 Dec 2016 12:12:43 -0000 1.171
+++ kern/uipc_socket.c 3 Jan 2017 14:04:42 -0000
@@ -256,7 +256,7 @@ soclose(struct socket *so)
(so->so_state & SS_NBIO))
goto drop;
while (so->so_state & SS_ISCONNECTED) {
- error = tsleep(&so->so_timeo,
+ error = rrwsleep(&so->so_timeo, &netlock,
PSOCK | PCATCH, "netcls",
so->so_linger * hz);
if (error)
@@ -1039,7 +1039,7 @@ sorflush(struct socket *so)
struct sockbuf asb;
sb->sb_flags |= SB_NOINTR;
- (void) sblock(sb, M_WAITOK, NULL);
+ (void) sblock(sb, M_WAITOK, &netlock);
socantrcvmore(so);
sbunlock(sb);
asb = *sb;
Index: kern/uipc_socket2.c
===================================================================
RCS file: /cvs/src/sys/kern/uipc_socket2.c,v
retrieving revision 1.70
diff -u -p -r1.70 uipc_socket2.c
--- kern/uipc_socket2.c 29 Dec 2016 12:12:43 -0000 1.70
+++ kern/uipc_socket2.c 3 Jan 2017 14:06:54 -0000
@@ -53,7 +53,7 @@ u_long sb_max = SB_MAX; /* patchable */
extern struct pool mclpools[];
extern struct pool mbpool;
-int sbsleep(struct sockbuf *, struct rwlock *);
+int sbsleep(struct sockbuf *, struct rrwlock *);
/*
* Procedures to manipulate state flags of socket
@@ -276,18 +276,18 @@ sbwait(struct sockbuf *sb)
NET_ASSERT_LOCKED();
sb->sb_flagsintr |= SB_WAIT;
- return (tsleep(&sb->sb_cc,
+ return (rrwsleep(&sb->sb_cc, &netlock,
(sb->sb_flags & SB_NOINTR) ? PSOCK : PSOCK | PCATCH, "netio",
sb->sb_timeo));
}
int
-sbsleep(struct sockbuf *sb, struct rwlock *lock)
+sbsleep(struct sockbuf *sb, struct rrwlock *rrwl)
{
int error, prio = (sb->sb_flags & SB_NOINTR) ? PSOCK : PSOCK | PCATCH;
- if (lock != NULL)
- error = rwsleep(&sb->sb_flags, lock, prio, "netlck", 0);
+ if (rrwl != NULL)
+ error = rrwsleep(&sb->sb_flags, rrwl, prio, "netlck", 0);
else
error = tsleep(&sb->sb_flags, prio, "netlck", 0);
@@ -295,7 +295,7 @@ sbsleep(struct sockbuf *sb, struct rwloc
}
int
-sblock(struct sockbuf *sb, int wait, struct rwlock *lock)
+sblock(struct sockbuf *sb, int wait, struct rrwlock *rrwl)
{
int error;
@@ -310,7 +310,7 @@ sblock(struct sockbuf *sb, int wait, str
while (sb->sb_flags & SB_LOCK) {
sb->sb_flags |= SB_WANT;
- error = sbsleep(sb, lock);
+ error = sbsleep(sb, rrwl);
if (error)
return (error);
}
Index: kern/uipc_syscalls.c
===================================================================
RCS file: /cvs/src/sys/kern/uipc_syscalls.c,v
retrieving revision 1.144
diff -u -p -r1.144 uipc_syscalls.c
--- kern/uipc_syscalls.c 29 Dec 2016 12:12:43 -0000 1.144
+++ kern/uipc_syscalls.c 3 Jan 2017 14:04:26 -0000
@@ -296,7 +296,7 @@ redo:
head->so_error = ECONNABORTED;
break;
}
- error = tsleep(&head->so_timeo, PSOCK | PCATCH,
+ error = rrwsleep(&head->so_timeo, &netlock, PSOCK | PCATCH,
"netcon", 0);
if (error) {
goto bad;
@@ -436,7 +436,7 @@ sys_connect(struct proc *p, void *v, reg
}
NET_LOCK(s);
while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
- error = tsleep(&so->so_timeo, PSOCK | PCATCH,
+ error = rrwsleep(&so->so_timeo, &netlock, PSOCK | PCATCH,
"netcon2", 0);
if (error) {
if (error == EINTR || error == ERESTART)
Index: net/if.c
===================================================================
RCS file: /cvs/src/sys/net/if.c,v
retrieving revision 1.470
diff -u -p -r1.470 if.c
--- net/if.c 3 Jan 2017 13:11:55 -0000 1.470
+++ net/if.c 3 Jan 2017 14:03:51 -0000
@@ -229,6 +229,12 @@ struct taskq *softnettq;
struct task if_input_task_locked = TASK_INITIALIZER(if_netisr, NULL);
+
+/*
+ * Serialize socket operations to ensure no new sleeping points
+ * are introduced in IP output paths.
+ */
+struct rrwlock netlock = RRWLOCK_INITIALIZER("netlock");
/*
* Network interface utility routines.
*/
Index: sys/rwlock.h
===================================================================
RCS file: /cvs/src/sys/sys/rwlock.h,v
retrieving revision 1.20
diff -u -p -r1.20 rwlock.h
--- sys/rwlock.h 21 Sep 2016 10:19:13 -0000 1.20
+++ sys/rwlock.h 3 Jan 2017 14:03:32 -0000
@@ -114,6 +114,8 @@ void rw_assert_unlocked(struct rwlock *)
#define rw_assert_unlocked(rwl) ((void)0)
#endif
+#define RRWLOCK_INITIALIZER(name) { RWLOCK_INITIALIZER(name), 0 }
+
int rw_enter(struct rwlock *, int);
void rw_exit(struct rwlock *);
int rw_status(struct rwlock *);
@@ -122,6 +124,8 @@ void rrw_init(struct rrwlock *, char *);
int rrw_enter(struct rrwlock *, int);
void rrw_exit(struct rrwlock *);
int rrw_status(struct rrwlock *);
+int rrw_release_all(struct rrwlock *);
+void rrw_acquire_count(struct rrwlock *, int, int);
#endif /* _KERNEL */
Index: sys/socketvar.h
===================================================================
RCS file: /cvs/src/sys/sys/socketvar.h,v
retrieving revision 1.67
diff -u -p -r1.67 socketvar.h
--- sys/socketvar.h 19 Dec 2016 08:36:50 -0000 1.67
+++ sys/socketvar.h 3 Jan 2017 14:05:59 -0000
@@ -222,7 +222,7 @@ struct rwlock;
* Unless SB_NOINTR is set on sockbuf, sleep is interruptible.
* Returns error without lock if sleep is interrupted.
*/
-int sblock(struct sockbuf *, int, struct rwlock *);
+int sblock(struct sockbuf *, int, struct rrwlock *);
/* release lock on sockbuf sb */
void sbunlock(struct sockbuf *);
Index: sys/systm.h
===================================================================
RCS file: /cvs/src/sys/sys/systm.h,v
retrieving revision 1.121
diff -u -p -r1.121 systm.h
--- sys/systm.h 29 Dec 2016 12:12:44 -0000 1.121
+++ sys/systm.h 3 Jan 2017 14:07:33 -0000
@@ -247,13 +247,14 @@ int sleep_finish_signal(struct sleep_sta
void sleep_queue_init(void);
struct mutex;
-struct rwlock;
+struct rrwlock;
void wakeup_n(const volatile void *, int);
void wakeup(const volatile void *);
#define wakeup_one(c) wakeup_n((c), 1)
int tsleep(const volatile void *, int, const char *, int);
int msleep(const volatile void *, struct mutex *, int, const char*, int);
-int rwsleep(const volatile void *, struct rwlock *, int, const char *, int);
+int rrwsleep(const volatile void *, struct rrwlock *, int, const char *,
+ int);
void yield(void);
void wdog_register(int (*)(void *, int), void *);
@@ -291,18 +292,26 @@ int uiomove(void *, size_t, struct uio *
#if defined(_KERNEL)
+#include <sys/rwlock.h>
+
+extern struct rrwlock netlock;
+
#define NET_LOCK(s)
\
do { \
+ rrw_enter(&netlock, RW_WRITE); \
s = splsoftnet(); \
} while (0)
#define NET_UNLOCK(s)
\
do { \
splx(s); \
+ rrw_exit(&netlock); \
} while (0)
#define NET_ASSERT_LOCKED()
\
do { \
+ if (rrw_status(&netlock) != RW_WRITE) \
+ splassert_fail(RW_WRITE, rrw_status(&netlock), __func__);\
splsoftassert(IPL_SOFTNET); \
} while (0)