From e8a5d94ca8dcd53355b2d0f49b0ebf2aab76a2a6 Mon Sep 17 00:00:00 2001 From: jmarcus Date: Mon, 3 Dec 2001 15:53:39 +0000 Subject: [PATCH] MFH: Fix CNID DB deadlock problem. --- libatalk/cnid/cnid_add.c | 84 ++++++++++++++++++++++++------------ libatalk/cnid/cnid_close.c | 2 +- libatalk/cnid/cnid_delete.c | 4 +- libatalk/cnid/cnid_get.c | 2 +- libatalk/cnid/cnid_lookup.c | 2 +- libatalk/cnid/cnid_nextid.c | 2 +- libatalk/cnid/cnid_open.c | 4 +- libatalk/cnid/cnid_private.h | 2 +- libatalk/cnid/cnid_resolve.c | 2 +- libatalk/cnid/cnid_update.c | 27 ++++++------ 10 files changed, 80 insertions(+), 51 deletions(-) diff --git a/libatalk/cnid/cnid_add.c b/libatalk/cnid/cnid_add.c index f966ac2e..a8236258 100644 --- a/libatalk/cnid/cnid_add.c +++ b/libatalk/cnid/cnid_add.c @@ -1,5 +1,5 @@ /* - * $Id: cnid_add.c,v 1.14.2.1 2001-12-03 05:05:45 jmarcus Exp $ + * $Id: cnid_add.c,v 1.14.2.2 2001-12-03 15:53:39 jmarcus Exp $ * * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu) * All Rights Reserved. See COPYRIGHT. @@ -26,6 +26,9 @@ #endif /* HAVE_FCNTL_H */ #include #include +#ifdef HAVE_SYS_TIME_H +#include +#endif /* HAVE_SYS_TIME_H */ #include #include @@ -36,6 +39,8 @@ #include "cnid_private.h" +#define MAX_ABORTS 255 + /* add an entry to the CNID databases. we do this as a transaction * to prevent messiness. */ static int add_cnid(CNID_private *db, DB_TXN *ptid, DBT *key, DBT *data) { @@ -44,28 +49,32 @@ static int add_cnid(CNID_private *db, DB_TXN *ptid, DBT *key, DBT *data) { /* We create rc here because using errno is bad. Why? Well, if you * use errno once, then call another function which resets it, you're * screwed. */ - int rc; + int rc, ret, aborts = 0; memset(&altkey, 0, sizeof(altkey)); memset(&altdata, 0, sizeof(altdata)); + if (0) { retry: + if ((rc = txn_abort(tid)) != 0) { + return rc; + } + if (++aborts > MAX_ABORTS) { + return DB_LOCK_DEADLOCK; + } + } + if ((rc = txn_begin(db->dbenv, ptid, &tid, 0)) != 0) { return rc; } - /* main database */ if ((rc = db->db_cnid->put(db->db_cnid, tid, key, data, DB_NOOVERWRITE))) { - int ret; - if ((ret = txn_abort(tid)) != 0) { - return ret; - } if (rc == DB_LOCK_DEADLOCK) { goto retry; } - return rc; + goto abort; } @@ -75,15 +84,11 @@ retry: altdata.data = key->data; altdata.size = key->size; if ((rc = db->db_devino->put(db->db_devino, tid, &altkey, &altdata, 0))) { - int ret; - if ((ret = txn_abort(tid)) != 0) { - return ret; - } if (rc == DB_LOCK_DEADLOCK) { goto retry; } - return rc; + goto abort; } @@ -91,15 +96,11 @@ retry: altkey.data = (char *) data->data + CNID_DEVINO_LEN; altkey.size = data->size - CNID_DEVINO_LEN; if ((rc = db->db_didname->put(db->db_didname, tid, &altkey, &altdata, 0))) { - int ret; - if ((ret = txn_abort(tid)) != 0) { - return ret; - } if (rc == DB_LOCK_DEADLOCK) { goto retry; } - return rc; + goto abort; } if ((rc = txn_commit(tid, 0)) != 0) { @@ -108,6 +109,13 @@ retry: return rc; } return 0; + +abort: + if ((ret = txn_abort(tid)) != 0) { + return ret; + } + return rc; + } cnid_t cnid_add(void *CNID, const struct stat *st, @@ -117,6 +125,7 @@ cnid_t cnid_add(void *CNID, const struct stat *st, CNID_private *db; DBT key, data, rootinfo_key, rootinfo_data; DB_TXN *tid; + struct timeval t; cnid_t id, save; int rc; @@ -172,26 +181,25 @@ cnid_t cnid_add(void *CNID, const struct stat *st, } } + /* We need to create a random sleep interval to prevent deadlocks. */ + (void)srand(getpid() ^ time(NULL)); + t.tv_sec = 0; + memset(&rootinfo_key, 0, sizeof(rootinfo_key)); memset(&rootinfo_data, 0, sizeof(rootinfo_data)); rootinfo_key.data = ROOTINFO_KEY; rootinfo_key.size = ROOTINFO_KEYLEN; -retry: - if ((rc = txn_begin(db->dbenv, NULL, &tid, 0)) != 0) { - syslog(LOG_ERR, "cnid_add: Failed to begin transaction: %s", - db_strerror(rc)); - goto cleanup_err; - } /* Get the key. */ - switch (rc = db->db_didname->get(db->db_didname, tid, &rootinfo_key, - &rootinfo_data, DB_RMW)) { +retry_get: + switch (rc = db->db_didname->get(db->db_didname, NULL, &rootinfo_key, + &rootinfo_data, 0)) { case DB_LOCK_DEADLOCK: if ((rc = txn_abort(tid)) != 0) { syslog(LOG_ERR, "cnid_add: txn_abort: %s", db_strerror(rc)); goto cleanup_err; } - goto retry; + goto retry_get; case 0: memcpy(&hint, rootinfo_data.data, sizeof(hint)); #ifdef DEBUG @@ -208,7 +216,20 @@ retry: default: syslog(LOG_ERR, "cnid_add: Unable to lookup rootinfo: %s", db_strerror(rc)); - goto cleanup_abort; + goto cleanup_err; + } + + +retry: + t.tv_usec = rand() % 1000000; +#ifdef DEBUG + syslog(LOG_INFO, "cnid_add: Hitting MAX_ABORTS, sleeping"); +#endif + (void)select(0, NULL, NULL, NULL, &t); + if ((rc = txn_begin(db->dbenv, NULL, &tid, 0)) != 0) { + syslog(LOG_ERR, "cnid_add: Failed to begin transaction: %s", + db_strerror(rc)); + goto cleanup_err; } /* Search for a new id. We keep the first id around to check for @@ -220,6 +241,13 @@ retry: if (++id < CNID_START) { id = CNID_START; } + if (rc == DB_LOCK_DEADLOCK) { + if ((rc = txn_abort(tid)) != 0) { + syslog(LOG_ERR, "cnid_add: txn_abort: %s", db_strerror(rc)); + goto cleanup_err; + } + goto retry; + } if ((rc != DB_KEYEXIST) || (save == id)) { syslog(LOG_ERR, "cnid_add: Unable to add CNID %u: %s", diff --git a/libatalk/cnid/cnid_close.c b/libatalk/cnid/cnid_close.c index 772ffef2..dde16a7e 100644 --- a/libatalk/cnid/cnid_close.c +++ b/libatalk/cnid/cnid_close.c @@ -1,5 +1,5 @@ /* - * $Id: cnid_close.c,v 1.12.2.1 2001-12-03 05:05:46 jmarcus Exp $ + * $Id: cnid_close.c,v 1.12.2.2 2001-12-03 15:53:39 jmarcus Exp $ */ #ifdef HAVE_CONFIG_H diff --git a/libatalk/cnid/cnid_delete.c b/libatalk/cnid/cnid_delete.c index 1028ecf8..a4b0f347 100644 --- a/libatalk/cnid/cnid_delete.c +++ b/libatalk/cnid/cnid_delete.c @@ -1,5 +1,5 @@ /* - * $Id: cnid_delete.c,v 1.9.2.1 2001-12-03 05:05:47 jmarcus Exp $ + * $Id: cnid_delete.c,v 1.9.2.2 2001-12-03 15:53:39 jmarcus Exp $ * * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu) * All Rights Reserved. See COPYRIGHT. @@ -47,7 +47,7 @@ retry: /* Get from ain CNID database. */ key.data = (cnid_t *)&id; key.size = sizeof(id); - if ((rc = db->db_cnid->get(db->db_cnid, tid, &key, &data, DB_RMW))) { + if ((rc = db->db_cnid->get(db->db_cnid, tid, &key, &data, 0))) { int ret; if ((ret = txn_abort(tid)) != 0) { syslog(LOG_ERR, "cnid_delete: txn_abort: %s", db_strerror(ret)); diff --git a/libatalk/cnid/cnid_get.c b/libatalk/cnid/cnid_get.c index b5c534bb..c10faf72 100644 --- a/libatalk/cnid/cnid_get.c +++ b/libatalk/cnid/cnid_get.c @@ -1,5 +1,5 @@ /* - * $Id: cnid_get.c,v 1.9.2.1 2001-12-03 05:05:47 jmarcus Exp $ + * $Id: cnid_get.c,v 1.9.2.2 2001-12-03 15:53:39 jmarcus Exp $ */ #ifdef HAVE_CONFIG_H diff --git a/libatalk/cnid/cnid_lookup.c b/libatalk/cnid/cnid_lookup.c index c772d00c..7e1d9c6c 100644 --- a/libatalk/cnid/cnid_lookup.c +++ b/libatalk/cnid/cnid_lookup.c @@ -1,5 +1,5 @@ /* - * $Id: cnid_lookup.c,v 1.9.2.1 2001-12-03 05:05:48 jmarcus Exp $ + * $Id: cnid_lookup.c,v 1.9.2.2 2001-12-03 15:53:39 jmarcus Exp $ */ #ifdef HAVE_CONFIG_H diff --git a/libatalk/cnid/cnid_nextid.c b/libatalk/cnid/cnid_nextid.c index f8627262..9d049141 100644 --- a/libatalk/cnid/cnid_nextid.c +++ b/libatalk/cnid/cnid_nextid.c @@ -1,5 +1,5 @@ /* - * $Id: cnid_nextid.c,v 1.6.2.1 2001-12-03 05:05:48 jmarcus Exp $ + * $Id: cnid_nextid.c,v 1.6.2.2 2001-12-03 15:53:39 jmarcus Exp $ */ #ifdef unused diff --git a/libatalk/cnid/cnid_open.c b/libatalk/cnid/cnid_open.c index 35dec112..bdf86bbf 100644 --- a/libatalk/cnid/cnid_open.c +++ b/libatalk/cnid/cnid_open.c @@ -1,5 +1,5 @@ /* - * $Id: cnid_open.c,v 1.19.2.1 2001-12-03 05:05:48 jmarcus Exp $ + * $Id: cnid_open.c,v 1.19.2.2 2001-12-03 15:53:39 jmarcus Exp $ * * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu) * All Rights Reserved. See COPYRIGHT. @@ -285,7 +285,7 @@ void *cnid_open(const char *dir) { /* We can't get a full transactional environment, so multi-access * is out of the question. Let's assume a read-only environment, - * and trry to at least get a shared memory pool. */ + * and try to at least get a shared memory pool. */ if ((rc = db->dbenv->open(db->dbenv, path, DB_INIT_MPOOL, 0666)) != 0) { /* Nope, not a MPOOL, either. Last-ditch effort: we'll try to * open the environment with no flags. */ diff --git a/libatalk/cnid/cnid_private.h b/libatalk/cnid/cnid_private.h index c8497cd8..8c19c9ca 100644 --- a/libatalk/cnid/cnid_private.h +++ b/libatalk/cnid/cnid_private.h @@ -1,5 +1,5 @@ /* - * $Id: cnid_private.h,v 1.3.2.1 2001-12-03 05:05:50 jmarcus Exp $ + * $Id: cnid_private.h,v 1.3.2.2 2001-12-03 15:53:39 jmarcus Exp $ */ #ifndef LIBATALK_CNID_PRIVATE_H diff --git a/libatalk/cnid/cnid_resolve.c b/libatalk/cnid/cnid_resolve.c index ffb3f8cd..41a7dbee 100644 --- a/libatalk/cnid/cnid_resolve.c +++ b/libatalk/cnid/cnid_resolve.c @@ -1,5 +1,5 @@ /* - * $Id: cnid_resolve.c,v 1.8.2.1 2001-12-03 05:05:51 jmarcus Exp $ + * $Id: cnid_resolve.c,v 1.8.2.2 2001-12-03 15:53:39 jmarcus Exp $ */ #ifdef HAVE_CONFIG_H diff --git a/libatalk/cnid/cnid_update.c b/libatalk/cnid/cnid_update.c index de1d7872..d83753b0 100644 --- a/libatalk/cnid/cnid_update.c +++ b/libatalk/cnid/cnid_update.c @@ -1,5 +1,5 @@ /* - * $Id: cnid_update.c,v 1.12.2.1 2001-12-03 05:05:51 jmarcus Exp $ + * $Id: cnid_update.c,v 1.12.2.2 2001-12-03 15:53:39 jmarcus Exp $ */ #ifdef HAVE_CONFIG_H @@ -97,6 +97,19 @@ retry: data.data = make_cnid_data(st, did, name, len); data.size = CNID_HEADER_LEN + len + 1; + /* Update the old CNID with the new info. */ + key.data = (cnid_t *) &id; + key.size = sizeof(id); + if ((rc = db->db_cnid->put(db->db_cnid, tid, &key, &data, 0))) { + txn_abort(tid); + switch (rc) { + case DB_LOCK_DEADLOCK: + goto retry; + default: + goto update_err; + } + } + /* Put in a new dev/ino mapping. */ key.data = data.data; key.size = CNID_DEVINO_LEN; @@ -125,18 +138,6 @@ retry: } } - /* Update the old CNID with the new info. */ - key.data = (cnid_t *) &id; - key.size = sizeof(id); - if ((rc = db->db_cnid->put(db->db_cnid, tid, &key, &data, 0))) { - txn_abort(tid); - switch (rc) { - case DB_LOCK_DEADLOCK: - goto retry; - default: - goto update_err; - } - } return txn_commit(tid, 0); -- 2.39.2