Date: Wed, 28 Jan 2009 19:45:00 +0100 From: Attilio Rao <attilio@freebsd.org> To: FreeBSD Current <freebsd-current@freebsd.org>, freebsd-fs@freebsd.org Cc: bp@freebsd.org, Robert Watson <rwatson@freebsd.org> Subject: [PATCH] improving netncp locking Message-ID: <3bbf2fe10901281045s7e3280dep5a26df6541595cfe@mail.gmail.com>
index | next in thread | raw e-mail
[-- Attachment #1 --]
Attached there is a patch that fixes netncp locking.
Actually, netncp tries to drain connections without any protection for
the nc_id field (which is supposed to discriminate between a valid
connection and one under draining).
Also, the drain still uses loose ending LK_DRAIN.
This patch adds correct locking for draining path adding an interlock,
control flags, a refcount and using it accordingly.
Ultimately 2 locks are switched to be sx as they don't rely on any
particular lockmgr feature.
The patch compiles and boots ok, but if someone could test and review
it I would appreciate a lot.
Thanks,
Attilio
--
Peace can only be achieved by understanding - A. Einstein
[-- Attachment #2 --]
--- /usr/src/sys/netncp/ncp_conn.c 2009-01-10 13:39:48.000000000 +0100
+++ sys/netncp/ncp_conn.c 2009-01-28 19:20:02.000000000 +0100
@@ -38,11 +38,13 @@
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
+#include <sys/lock.h>
#include <sys/malloc.h>
+#include <sys/mutex.h>
#include <sys/priv.h>
#include <sys/proc.h>
-#include <sys/lock.h>
#include <sys/sysctl.h>
+#include <sys/sx.h>
#include <netncp/ncp.h>
#include <netncp/nwerror.h>
@@ -51,6 +53,17 @@
#include <netncp/ncp_sock.h>
#include <netncp/ncp_ncp.h>
+#define NCP_REFLCNT(ncp) ((ncp)->nc_lockcnt++)
+#define NCP_RELLCNT(ncp) do { \
+ (ncp)->nc_lockcnt--; \
+ if ((ncp)->nc_lockcnt == 0 && \
+ ((ncp)->flags & NCPFL_DRAINING) != 0) { \
+ MPASS((ncp)->nc_id == 0); \
+ (ncp)->flags &= ~NCPFL_DRAINING; \
+ wakeup(&(ncp)->nc_lockcnt); \
+ } \
+} while(0)
+
SLIST_HEAD(ncp_handle_head,ncp_handle);
int ncp_burst_enabled = 1;
@@ -58,15 +71,16 @@
struct ncp_conn_head conn_list={NULL};
static int ncp_conn_cnt = 0;
static int ncp_next_ref = 1;
-static struct lock listlock;
+static struct sx listlock;
struct ncp_handle_head lhlist={NULL};
static int ncp_next_handle = 1;
-static struct lock lhlock;
+static struct sx lhlock;
static int ncp_sysctl_connstat(SYSCTL_HANDLER_ARGS);
static int ncp_conn_lock_any(struct ncp_conn *conn, struct thread *td,
struct ucred *cred);
+static void ncp_conn_lock_assert(struct ncp_conn *conn, int what);
SYSCTL_DECL(_net_ncp);
SYSCTL_INT (_net_ncp, OID_AUTO, burst_enabled, CTLFLAG_RD, &ncp_burst_enabled, 0, "");
@@ -79,8 +93,8 @@
int
ncp_conn_init(void)
{
- lockinit(&listlock, PSOCK, "ncpll", 0, 0);
- lockinit(&lhlock, PSOCK, "ncplh", 0, 0);
+ sx_init_flags(&listlock, "ncpll", SX_RECURSE);
+ sx_init(&lhlock, "ncplh");
return 0;
}
@@ -91,21 +105,51 @@
NCPERROR("There are %d connections active\n", ncp_conn_cnt);
return EBUSY;
}
- lockdestroy(&listlock);
- lockdestroy(&lhlock);
+ sx_destroy(&listlock);
+ sx_destroy(&lhlock);
return 0;
}
+void
+ncp_conn_slocklist()
+{
+
+ sx_slock(&listlock);
+}
+
+void
+ncp_conn_xlocklist()
+{
+
+ sx_xlock(&listlock);
+}
+
int
-ncp_conn_locklist(int flags, struct thread *td)
+ncp_conn_try_slocklist()
{
- return lockmgr(&listlock, flags | LK_CANRECURSE, 0);
+
+ return (sx_try_slock(&listlock));
+}
+
+int
+ncp_conn_try_xlocklist()
+{
+
+ return (sx_try_xlock(&listlock));
}
void
-ncp_conn_unlocklist(struct thread *td)
+ncp_conn_sunlocklist()
{
- lockmgr(&listlock, LK_RELEASE, 0);
+
+ sx_sunlock(&listlock);
+}
+
+void
+ncp_conn_xunlocklist()
+{
+
+ sx_xunlock(&listlock);
}
int
@@ -128,17 +172,31 @@
{
int error;
- if (conn->nc_id == 0) return EACCES;
- error = lockmgr(&conn->nc_lock, LK_EXCLUSIVE | LK_CANRECURSE, 0);
- if (error == ERESTART)
+ NCP_ILOCK(conn);
+ if (conn->nc_id == 0)
+ return EACCES;
+ NCP_REFLCNT(conn);
+ error = lockmgr(&conn->nc_lock, LK_EXCLUSIVE | LK_CANRECURSE |
+ LK_INTERLOCK, NCP_MTX(conn));
+ if (error == ERESTART) {
+ NCP_ILOCK(conn);
+ NCP_RELLCNT(conn);
+ NCP_IUNLOCK(conn);
return EINTR;
+ }
error = ncp_chkintr(conn, td);
if (error) {
+ NCP_ILOCK(conn);
+ NCP_RELLCNT(conn);
+ NCP_IUNLOCK(conn);
lockmgr(&conn->nc_lock, LK_RELEASE, 0);
return error;
}
if (conn->nc_id == 0) {
+ NCP_ILOCK(conn);
+ NCP_RELLCNT(conn);
+ NCP_IUNLOCK(conn);
lockmgr(&conn->nc_lock, LK_RELEASE, 0);
return EACCES;
}
@@ -147,6 +205,13 @@
return 0;
}
+static void
+ncp_conn_lock_assert(struct ncp_conn *conn, int what)
+{
+
+ lockmgr_assert(&conn->nc_lock, what);
+}
+
int
ncp_conn_lock(struct ncp_conn *conn, struct thread *td, struct ucred *cred, int mode)
{
@@ -167,11 +232,11 @@
error = ncp_conn_access(conn, cred, mode);
if (error) {
- ncp_conn_unlocklist(td);
+ ncp_conn_sunlocklist();
return error;
}
conn->nc_lwant++;
- ncp_conn_unlocklist(td);
+ ncp_conn_sunlocklist();
error = ncp_conn_lock_any(conn, td, cred);
conn->nc_lwant--;
if (conn->nc_lwant == 0) {
@@ -183,19 +248,11 @@
void
ncp_conn_unlock(struct ncp_conn *conn, struct thread *td)
{
- /*
- * note, that LK_RELASE will do wakeup() instead of wakeup_one().
- * this will do a little overhead
- */
- lockmgr(&conn->nc_lock, LK_RELEASE, 0);
-}
-int
-ncp_conn_assert_locked(struct ncp_conn *conn, const char *checker, struct thread *td)
-{
- if (lockstatus(&conn->nc_lock) == LK_EXCLUSIVE) return 0;
- printf("%s: connection isn't locked!\n", checker);
- return EIO;
+ NCP_ILOCK(conn);
+ NCP_RELLCNT(conn);
+ NCP_IUNLOCK(conn);
+ lockmgr(&conn->nc_lock, LK_RELEASE, 0);
}
void
@@ -242,7 +299,9 @@
M_NCPDATA, M_WAITOK | M_ZERO);
error = 0;
lockinit(&ncp->nc_lock, PZERO, "ncplck", 0, 0);
+ mtx_init(&ncp->nc_ilock, "ncpilck", NULL, 0);
ncp_conn_cnt++;
+ ncp->nc_lockcnt = 0;
ncp->nc_id = ncp_next_ref++;
ncp->nc_owner = owner;
ncp->seq = 0;
@@ -257,9 +316,9 @@
ncp->li.timeout = NCP_RETRY_TIMEOUT;
ncp_conn_lock_any(ncp, td, ncp->nc_owner);
*conn = ncp;
- ncp_conn_locklist(LK_EXCLUSIVE, td);
+ ncp_conn_xlocklist();
SLIST_INSERT_HEAD(&conn_list,ncp,nc_next);
- ncp_conn_unlocklist(td);
+ ncp_conn_xunlocklist();
return (error);
}
@@ -270,20 +329,12 @@
ncp_conn_free(struct ncp_conn *ncp)
{
struct thread *td;
- int error;
- if (ncp == NULL) {
- NCPFATAL("ncp == NULL\n");
- return 0;
- }
- if (ncp->nc_id == 0) {
- NCPERROR("nc_id == 0\n");
- return EACCES;
- }
+ MPASS(ncp != NULL);
+ MPASS(ncp->nc_id != 0);
+ ncp_conn_lock_assert(ncp, KA_XLOCKED);
+
td = ncp->td;
- error = ncp_conn_assert_locked(ncp, __func__, td);
- if (error)
- return error;
if (ncp->ref_cnt != 0 || (ncp->flags & NCPFL_PERMANENT))
return EBUSY;
if (ncp_conn_access(ncp, ncp->ucred, NCPM_WRITE))
@@ -292,26 +343,28 @@
if (ncp->flags & NCPFL_ATTACHED)
ncp_ncp_disconnect(ncp);
ncp_sock_disconnect(ncp);
+ ncp_conn_unlock(ncp, td);
/*
* Mark conn as dead and wait for other process
*/
+ NCP_ILOCK(ncp);
ncp->nc_id = 0;
- ncp_conn_unlock(ncp, td);
- /*
- * if signal is raised - how I do react ?
- */
- lockmgr(&ncp->nc_lock, LK_DRAIN, 0);
- lockmgr(&ncp->nc_lock, LK_RELEASE, 0);
+ if (ncp->nc_lockcnt != 0) {
+ ncp->flags |= NCPFL_DRAINING;
+ msleep(&ncp->nc_lockcnt, NCP_MTX(ncp), PSOCK,
+ "ncp connection drain", 0);
+ }
+ NCP_IUNLOCK(ncp);
lockdestroy(&ncp->nc_lock);
while (ncp->nc_lwant) {
printf("lwant = %d\n", ncp->nc_lwant);
tsleep(&ncp->nc_lwant, PZERO,"ncpdr",2*hz);
}
- ncp_conn_locklist(LK_EXCLUSIVE, td);
+ ncp_conn_xlocklist();
SLIST_REMOVE(&conn_list, ncp, ncp_conn, nc_next);
ncp_conn_cnt--;
- ncp_conn_unlocklist(td);
+ ncp_conn_xunlocklist();
if (ncp->li.user)
free(ncp->li.user, M_NCPDATA);
if (ncp->li.password)
@@ -390,11 +443,11 @@
struct ncp_conn *ncp;
int error = 0;
- ncp_conn_locklist(LK_SHARED, td);
+ ncp_conn_slocklist();
SLIST_FOREACH(ncp, &conn_list, nc_next)
if (ncp->nc_id == ref) break;
if (ncp == NULL) {
- ncp_conn_unlocklist(td);
+ ncp_conn_sunlocklist();
return(EBADF);
}
error = ncp_conn_lock2(ncp, td, cred, mode);
@@ -412,7 +465,7 @@
struct ncp_conn *ncp, *ncp2 = NULL;
int error = 0;
- ncp_conn_locklist(LK_SHARED, td);
+ ncp_conn_slocklist();
SLIST_FOREACH(ncp, &conn_list, nc_next) {
if ((ncp->flags & NCPFL_LOGGED) != 0 ||
strcmp(ncp->li.server,li->server) != 0 ||
@@ -428,7 +481,7 @@
}
if (ncp == NULL) ncp = ncp2;
if (ncp == NULL) {
- ncp_conn_unlocklist(td);
+ ncp_conn_sunlocklist();
return(EBADF);
}
error = ncp_conn_lock2(ncp, td, cred, mode);
@@ -456,7 +509,7 @@
partial = (li == NULL || li->server[0] == 0 || li->user == NULL);
haveserv = (li && li->server[0]);
- ncp_conn_locklist(LK_SHARED, td);
+ ncp_conn_slocklist();
SLIST_FOREACH(ncp, &conn_list, nc_next) {
if (partial) {
if (cred->cr_uid == ncp->nc_owner->cr_uid) {
@@ -486,7 +539,7 @@
}
if (ncp == NULL) ncp = ncp2;
if (ncp == NULL) {
- ncp_conn_unlocklist(td);
+ ncp_conn_sunlocklist();
return(EBADF);
}
error = ncp_conn_lock2(ncp, td, cred,mode);
@@ -507,12 +560,12 @@
if (conn->ucred->cr_uid != conn->nc_owner->cr_uid)
return EACCES;
- ncp_conn_locklist(LK_SHARED, conn->td);
+ ncp_conn_slocklist();
SLIST_FOREACH(ncp, &conn_list, nc_next) {
if (conn->ucred->cr_uid == ncp->nc_owner->cr_uid)
ncp->flags &= ~NCPFL_PRIMARY;
}
- ncp_conn_unlocklist(conn->td);
+ ncp_conn_sunlocklist();
if (on)
conn->flags |= NCPFL_PRIMARY;
return 0;
@@ -526,14 +579,14 @@
{
struct ncp_handle *refp;
- lockmgr(&lhlock, LK_EXCLUSIVE, 0);
+ sx_xlock(&lhlock);
SLIST_FOREACH(refp, &lhlist, nh_next)
if (refp->nh_conn == conn && td == refp->nh_td) break;
if (refp) {
conn->ref_cnt++;
refp->nh_ref++;
*handle = refp;
- lockmgr(&lhlock, LK_RELEASE, 0);
+ sx_xunlock(&lhlock);
return 0;
}
refp = malloc(sizeof(struct ncp_handle),M_NCPDATA,
@@ -545,7 +598,7 @@
refp->nh_id = ncp_next_handle++;
*handle = refp;
conn->ref_cnt++;
- lockmgr(&lhlock, LK_RELEASE, 0);
+ sx_xunlock(&lhlock);
return 0;
}
/*
@@ -556,7 +609,7 @@
{
struct ncp_handle *refp = handle;
- lockmgr(&lhlock, LK_EXCLUSIVE, 0);
+ sx_xlock(&lhlock);
refp->nh_ref--;
refp->nh_conn->ref_cnt--;
if (force) {
@@ -567,7 +620,7 @@
SLIST_REMOVE(&lhlist, refp, ncp_handle, nh_next);
free(refp, M_NCPDATA);
}
- lockmgr(&lhlock, LK_RELEASE, 0);
+ sx_xunlock(&lhlock);
return 0;
}
/*
@@ -577,10 +630,10 @@
ncp_conn_findhandle(int connHandle, struct thread *td, struct ncp_handle **handle) {
struct ncp_handle *refp;
- lockmgr(&lhlock, LK_SHARED, 0);
+ sx_slock(&lhlock);
SLIST_FOREACH(refp, &lhlist, nh_next)
if (refp->nh_td == td && refp->nh_id == connHandle) break;
- lockmgr(&lhlock, LK_RELEASE, 0);
+ sx_sunlock(&lhlock);
if (refp == NULL) {
return EBADF;
}
@@ -596,7 +649,7 @@
struct ncp_handle *hp, *nhp;
int haveone = 0;
- lockmgr(&lhlock, LK_EXCLUSIVE, 0);
+ sx_xlock(&lhlock);
for (hp = SLIST_FIRST(&lhlist); hp; hp = nhp) {
nhp = SLIST_NEXT(hp, nh_next);
if (hp->nh_td != td) continue;
@@ -605,7 +658,7 @@
SLIST_REMOVE(&lhlist, hp, ncp_handle, nh_next);
free(hp, M_NCPDATA);
}
- lockmgr(&lhlock, LK_RELEASE, 0);
+ sx_xunlock(&lhlock);
return haveone;
}
/*
@@ -616,11 +669,11 @@
ncp_conn_list_rm_ref(pid_t pid) {
struct ncp_conn *ncp;
- ncp_conn_locklist(LK_SHARED, NULL);
+ ncp_conn_slocklist();
SLIST_FOREACH(ncp, &conn_list, nc_next) {
ncp_conn_rm_ref(ncp,pid,1);
}
- ncp_conn_unlocklist(NULL);
+ ncp_conn_sunlocklist();
return;
}
*/
@@ -653,7 +706,7 @@
error = sysctl_wire_old_buffer(req, 0);
if (error != 0)
return (error);
- ncp_conn_locklist(LK_SHARED, req->td);
+ ncp_conn_slocklist();
error = SYSCTL_OUT(req, &ncp_conn_cnt, sizeof(ncp_conn_cnt));
SLIST_FOREACH(ncp, &conn_list, nc_next) {
if (error) break;
@@ -663,6 +716,6 @@
ncp->nc_lwant--;
error = SYSCTL_OUT(req, &ncs, sizeof(ncs));
}
- ncp_conn_unlocklist(req->td);
+ ncp_conn_sunlocklist();
return(error);
}
--- /usr/src/sys/netncp/ncp_conn.h 2008-03-12 14:55:40.000000000 +0100
+++ sys/netncp/ncp_conn.h 2009-01-28 19:20:02.000000000 +0100
@@ -63,6 +63,7 @@
#define NCPFL_WASLOGGED 0x0200 /* there was at least one successfull login */
#define NCPFL_SIGNACTIVE 0x1000 /* packet signing active */
#define NCPFL_SIGNWANTED 0x2000 /* signing should start */
+#define NCPFL_DRAINING 0x4000 /* structure under draining */
/* access mode for connection */
#define NCPM_READ 0400 /* able to fetch conn params */
@@ -72,6 +73,9 @@
#define NCP_DEFAULT_OWNER ((uid_t)-1)
#define NCP_DEFAULT_GROUP ((uid_t)-1)
+#define NCP_ILOCK(ncp) mtx_lock(&(ncp)->nc_ilock)
+#define NCP_IUNLOCK(ncp) mtx_unlock(&(ncp)->nc_ilock)
+#define NCP_MTX(ncp) (&(ncp)->nc_ilock)
/* args used to create connection */
#define ncp_conn_loginfo ncp_conn_args
@@ -173,6 +177,8 @@
int ref_cnt; /* how many handles leased */
SLIST_HEAD(ncp_ref_hd,ncp_ref) ref_list;/* list of handles */
struct lock nc_lock; /* excl locks */
+ struct mtx nc_ilock; /* connection interlock */
+ int nc_lockcnt; /* lock refcount */
int nc_lwant; /* number of wanted locks */
struct thread *td; /* pid currently operates */
struct ucred *ucred; /* usr currently operates */
@@ -201,7 +207,6 @@
int ncp_conn_access(struct ncp_conn *conn,struct ucred *cred,mode_t mode);
int ncp_conn_lock(struct ncp_conn *conn,struct thread *td, struct ucred *cred,int mode);
void ncp_conn_unlock(struct ncp_conn *conn,struct thread *td);
-int ncp_conn_assert_locked(struct ncp_conn *conn,const char *checker,struct thread *td);
void ncp_conn_invalidate(struct ncp_conn *ncp);
int ncp_conn_invalid(struct ncp_conn *ncp);
/*int ncp_conn_ref(struct ncp_conn *conn, pid_t pid);
@@ -212,8 +217,12 @@
int ncp_conn_getbyli(struct ncp_conn_loginfo *li,struct thread *td, struct ucred *cred,
int mode, struct ncp_conn **connpp);
int ncp_conn_setprimary(struct ncp_conn *conn, int on);
-int ncp_conn_locklist(int flags, struct thread *td);
-void ncp_conn_unlocklist(struct thread *td);
+void ncp_conn_slocklist(void);
+void ncp_conn_xlocklist(void);
+int ncp_conn_try_slocklist(void);
+int ncp_conn_try_xlocklist(void);
+void ncp_conn_sunlocklist(void);
+void ncp_conn_xunlocklist(void);
int ncp_conn_gethandle(struct ncp_conn *conn, struct thread *td, struct ncp_handle **handle);
int ncp_conn_puthandle(struct ncp_handle *handle, struct thread *td, int force);
int ncp_conn_findhandle(int connHandle, struct thread *td, struct ncp_handle **handle);
--- /usr/src/sys/netncp/ncp_subr.c 2009-01-10 13:39:48.000000000 +0100
+++ sys/netncp/ncp_subr.c 2009-01-28 19:24:08.000000000 +0100
@@ -90,7 +90,7 @@
if (ncp_conn_putprochandles(td) == 0)
continue;
- ncp_conn_locklist(LK_EXCLUSIVE, td);
+ ncp_conn_xlocklist();
for (ncp = SLIST_FIRST(&conn_list); ncp; ncp = nncp) {
nncp = SLIST_NEXT(ncp, nc_next);
if (ncp_conn_lock(ncp, td, td->td_ucred,
@@ -99,7 +99,7 @@
if (ncp_conn_free(ncp) != 0)
ncp_conn_unlock(ncp, td);
}
- ncp_conn_unlocklist(td);
+ ncp_conn_xunlocklist();
}
mtx_unlock(&Giant);
}
@@ -134,10 +134,10 @@
{
struct ncp_conn *conn;
- if(ncp_conn_locklist(LK_SHARED | LK_NOWAIT, NULL) == 0) {
+ if (ncp_conn_try_slocklist()) {
SLIST_FOREACH(conn, &conn_list, nc_next)
ncp_check_conn(conn);
- ncp_conn_unlocklist(NULL);
+ ncp_conn_sunlocklist();
}
ncp_timer_handle = timeout(ncp_timer, NULL, NCP_TIMER_TICK);
}
help
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?3bbf2fe10901281045s7e3280dep5a26df6541595cfe>
