summaryrefslogtreecommitdiff
path: root/fs/dlm/config.c
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2012-07-26 12:44:30 -0500
committerDavid Teigland <teigland@redhat.com>2012-08-08 11:33:35 -0500
commit36b71a8bfbc92e1ba164e9aec840c0180ee933b5 (patch)
tree6cda578ba4e7e4c9de16e333ffdc032717348b81 /fs/dlm/config.c
parent42a579a0f960081cd16fc945036e4780c3ad3202 (diff)
dlm: fix deadlock between dlm_send and dlm_controld
A deadlock sometimes occurs between dlm_controld closing a lowcomms connection through configfs and dlm_send looking up the address for a new connection in configfs. dlm_controld does a configfs rmdir which calls dlm_lowcomms_close which waits for dlm_send to cancel work on the workqueues. The dlm_send workqueue thread has called tcp_connect_to_sock which calls dlm_nodeid_to_addr which does a configfs lookup and blocks on a lock held by dlm_controld in the rmdir path. The solution here is to save the node addresses within the lowcomms code so that the lowcomms workqueue does not need to step through configfs to get a node address. dlm_controld: wait_for_completion+0x1d/0x20 __cancel_work_timer+0x1b3/0x1e0 cancel_work_sync+0x10/0x20 dlm_lowcomms_close+0x4c/0xb0 [dlm] drop_comm+0x22/0x60 [dlm] client_drop_item+0x26/0x50 [configfs] configfs_rmdir+0x180/0x230 [configfs] vfs_rmdir+0xbd/0xf0 do_rmdir+0x103/0x120 sys_rmdir+0x16/0x20 dlm_send: mutex_lock+0x2b/0x50 get_comm+0x34/0x140 [dlm] dlm_nodeid_to_addr+0x18/0xd0 [dlm] tcp_connect_to_sock+0xf4/0x2d0 [dlm] process_send_sockets+0x1d2/0x260 [dlm] worker_thread+0x170/0x2a0 Signed-off-by: David Teigland <teigland@redhat.com>
Diffstat (limited to 'fs/dlm/config.c')
-rw-r--r--fs/dlm/config.c79
1 files changed, 15 insertions, 64 deletions
diff --git a/fs/dlm/config.c b/fs/dlm/config.c
index 9ccf7346834a..a0387dd8b1f0 100644
--- a/fs/dlm/config.c
+++ b/fs/dlm/config.c
@@ -750,6 +750,7 @@ static ssize_t comm_local_write(struct dlm_comm *cm, const char *buf,
static ssize_t comm_addr_write(struct dlm_comm *cm, const char *buf, size_t len)
{
struct sockaddr_storage *addr;
+ int rv;
if (len != sizeof(struct sockaddr_storage))
return -EINVAL;
@@ -762,6 +763,13 @@ static ssize_t comm_addr_write(struct dlm_comm *cm, const char *buf, size_t len)
return -ENOMEM;
memcpy(addr, buf, len);
+
+ rv = dlm_lowcomms_addr(cm->nodeid, addr, len);
+ if (rv) {
+ kfree(addr);
+ return rv;
+ }
+
cm->addr[cm->addr_count++] = addr;
return len;
}
@@ -878,34 +886,7 @@ static void put_space(struct dlm_space *sp)
config_item_put(&sp->group.cg_item);
}
-static int addr_compare(struct sockaddr_storage *x, struct sockaddr_storage *y)
-{
- switch (x->ss_family) {
- case AF_INET: {
- struct sockaddr_in *sinx = (struct sockaddr_in *)x;
- struct sockaddr_in *siny = (struct sockaddr_in *)y;
- if (sinx->sin_addr.s_addr != siny->sin_addr.s_addr)
- return 0;
- if (sinx->sin_port != siny->sin_port)
- return 0;
- break;
- }
- case AF_INET6: {
- struct sockaddr_in6 *sinx = (struct sockaddr_in6 *)x;
- struct sockaddr_in6 *siny = (struct sockaddr_in6 *)y;
- if (!ipv6_addr_equal(&sinx->sin6_addr, &siny->sin6_addr))
- return 0;
- if (sinx->sin6_port != siny->sin6_port)
- return 0;
- break;
- }
- default:
- return 0;
- }
- return 1;
-}
-
-static struct dlm_comm *get_comm(int nodeid, struct sockaddr_storage *addr)
+static struct dlm_comm *get_comm(int nodeid)
{
struct config_item *i;
struct dlm_comm *cm = NULL;
@@ -919,19 +900,11 @@ static struct dlm_comm *get_comm(int nodeid, struct sockaddr_storage *addr)
list_for_each_entry(i, &comm_list->cg_children, ci_entry) {
cm = config_item_to_comm(i);
- if (nodeid) {
- if (cm->nodeid != nodeid)
- continue;
- found = 1;
- config_item_get(i);
- break;
- } else {
- if (!cm->addr_count || !addr_compare(cm->addr[0], addr))
- continue;
- found = 1;
- config_item_get(i);
- break;
- }
+ if (cm->nodeid != nodeid)
+ continue;
+ found = 1;
+ config_item_get(i);
+ break;
}
mutex_unlock(&clusters_root.subsys.su_mutex);
@@ -995,7 +968,7 @@ int dlm_config_nodes(char *lsname, struct dlm_config_node **nodes_out,
int dlm_comm_seq(int nodeid, uint32_t *seq)
{
- struct dlm_comm *cm = get_comm(nodeid, NULL);
+ struct dlm_comm *cm = get_comm(nodeid);
if (!cm)
return -EEXIST;
*seq = cm->seq;
@@ -1003,28 +976,6 @@ int dlm_comm_seq(int nodeid, uint32_t *seq)
return 0;
}
-int dlm_nodeid_to_addr(int nodeid, struct sockaddr_storage *addr)
-{
- struct dlm_comm *cm = get_comm(nodeid, NULL);
- if (!cm)
- return -EEXIST;
- if (!cm->addr_count)
- return -ENOENT;
- memcpy(addr, cm->addr[0], sizeof(*addr));
- put_comm(cm);
- return 0;
-}
-
-int dlm_addr_to_nodeid(struct sockaddr_storage *addr, int *nodeid)
-{
- struct dlm_comm *cm = get_comm(0, addr);
- if (!cm)
- return -EEXIST;
- *nodeid = cm->nodeid;
- put_comm(cm);
- return 0;
-}
-
int dlm_our_nodeid(void)
{
return local_comm ? local_comm->nodeid : 0;