fs: dlm: fix mark setting deadlock

This patch fixes an deadlock issue when dlm_lowcomms_close() is called.
When dlm_lowcomms_close() is called the clusters_root.subsys.su_mutex is
held to remove configfs items. At this time we flushing (e.g.
cancel_work_sync()) the workers of send and recv workqueue. Due the fact
that we accessing configfs items (mark values), these workers will lock
clusters_root.subsys.su_mutex as well which are already hold by
dlm_lowcomms_close() and ends in a deadlock situation.

[67170.703046] ======================================================
[67170.703965] WARNING: possible circular locking dependency detected
[67170.704758] 5.11.0-rc4+ #22 Tainted: G        W
[67170.705433] ------------------------------------------------------
[67170.706228] dlm_controld/280 is trying to acquire lock:
[67170.706915] ffff9f2f475a6948 ((wq_completion)dlm_recv){+.+.}-{0:0}, at: __flush_work+0x203/0x4c0
[67170.708026]
               but task is already holding lock:
[67170.708758] ffffffffa132f878 (&clusters_root.subsys.su_mutex){+.+.}-{3:3}, at: configfs_rmdir+0x29b/0x310
[67170.710016]
               which lock already depends on the new lock.

The new behaviour adds the mark value to the node address configuration
which doesn't require to held the clusters_root.subsys.su_mutex by
accessing mark values in a separate datastructure. However the mark
values can be set now only after a node address was set which is the
case when the user is using dlm_controld.

Signed-off-by: Alexander Aring <aahringo@redhat.com>
Signed-off-by: David Teigland <teigland@redhat.com>
This commit is contained in:
Alexander Aring 2021-03-01 17:05:09 -05:00 коммит произвёл David Teigland
Родитель 92c48950b4
Коммит e125fbeb53
4 изменённых файлов: 45 добавлений и 35 удалений

Просмотреть файл

@ -688,6 +688,7 @@ static ssize_t comm_mark_show(struct config_item *item, char *buf)
static ssize_t comm_mark_store(struct config_item *item, const char *buf,
size_t len)
{
struct dlm_comm *comm;
unsigned int mark;
int rc;
@ -695,7 +696,15 @@ static ssize_t comm_mark_store(struct config_item *item, const char *buf,
if (rc)
return rc;
config_item_to_comm(item)->mark = mark;
if (mark == 0)
mark = dlm_config.ci_mark;
comm = config_item_to_comm(item);
rc = dlm_lowcomms_nodes_set_mark(comm->nodeid, mark);
if (rc)
return rc;
comm->mark = mark;
return len;
}
@ -870,24 +879,6 @@ int dlm_comm_seq(int nodeid, uint32_t *seq)
return 0;
}
void dlm_comm_mark(int nodeid, unsigned int *mark)
{
struct dlm_comm *cm;
cm = get_comm(nodeid);
if (!cm) {
*mark = dlm_config.ci_mark;
return;
}
if (cm->mark)
*mark = cm->mark;
else
*mark = dlm_config.ci_mark;
put_comm(cm);
}
int dlm_our_nodeid(void)
{
return local_comm ? local_comm->nodeid : 0;

Просмотреть файл

@ -48,7 +48,6 @@ void dlm_config_exit(void);
int dlm_config_nodes(char *lsname, struct dlm_config_node **nodes_out,
int *count_out);
int dlm_comm_seq(int nodeid, uint32_t *seq);
void dlm_comm_mark(int nodeid, unsigned int *mark);
int dlm_our_nodeid(void);
int dlm_our_addr(struct sockaddr_storage *addr, int num);

Просмотреть файл

@ -116,6 +116,7 @@ struct writequeue_entry {
struct dlm_node_addr {
struct list_head list;
int nodeid;
int mark;
int addr_count;
int curr_addr_index;
struct sockaddr_storage *addr[DLM_MAX_ADDR_COUNT];
@ -303,7 +304,8 @@ static int addr_compare(const struct sockaddr_storage *x,
}
static int nodeid_to_addr(int nodeid, struct sockaddr_storage *sas_out,
struct sockaddr *sa_out, bool try_new_addr)
struct sockaddr *sa_out, bool try_new_addr,
unsigned int *mark)
{
struct sockaddr_storage sas;
struct dlm_node_addr *na;
@ -331,6 +333,8 @@ static int nodeid_to_addr(int nodeid, struct sockaddr_storage *sas_out,
if (!na->addr_count)
return -ENOENT;
*mark = na->mark;
if (sas_out)
memcpy(sas_out, &sas, sizeof(struct sockaddr_storage));
@ -350,7 +354,8 @@ static int nodeid_to_addr(int nodeid, struct sockaddr_storage *sas_out,
return 0;
}
static int addr_to_nodeid(struct sockaddr_storage *addr, int *nodeid)
static int addr_to_nodeid(struct sockaddr_storage *addr, int *nodeid,
unsigned int *mark)
{
struct dlm_node_addr *na;
int rv = -EEXIST;
@ -364,6 +369,7 @@ static int addr_to_nodeid(struct sockaddr_storage *addr, int *nodeid)
for (addr_i = 0; addr_i < na->addr_count; addr_i++) {
if (addr_compare(na->addr[addr_i], addr)) {
*nodeid = na->nodeid;
*mark = na->mark;
rv = 0;
goto unlock;
}
@ -412,6 +418,7 @@ int dlm_lowcomms_addr(int nodeid, struct sockaddr_storage *addr, int len)
new_node->nodeid = nodeid;
new_node->addr[0] = new_addr;
new_node->addr_count = 1;
new_node->mark = dlm_config.ci_mark;
list_add(&new_node->list, &dlm_node_addrs);
spin_unlock(&dlm_node_addrs_spin);
return 0;
@ -519,6 +526,23 @@ int dlm_lowcomms_connect_node(int nodeid)
return 0;
}
int dlm_lowcomms_nodes_set_mark(int nodeid, unsigned int mark)
{
struct dlm_node_addr *na;
spin_lock(&dlm_node_addrs_spin);
na = find_node_addr(nodeid);
if (!na) {
spin_unlock(&dlm_node_addrs_spin);
return -ENOENT;
}
na->mark = mark;
spin_unlock(&dlm_node_addrs_spin);
return 0;
}
static void lowcomms_error_report(struct sock *sk)
{
struct connection *con;
@ -867,7 +891,7 @@ static int accept_from_sock(struct listen_connection *con)
/* Get the new node's NODEID */
make_sockaddr(&peeraddr, 0, &len);
if (addr_to_nodeid(&peeraddr, &nodeid)) {
if (addr_to_nodeid(&peeraddr, &nodeid, &mark)) {
unsigned char *b=(unsigned char *)&peeraddr;
log_print("connect from non cluster node");
print_hex_dump_bytes("ss: ", DUMP_PREFIX_NONE,
@ -876,9 +900,6 @@ static int accept_from_sock(struct listen_connection *con)
return -1;
}
dlm_comm_mark(nodeid, &mark);
sock_set_mark(newsock->sk, mark);
log_print("got connection from %d", nodeid);
/* Check to see if we already have a connection to this node. This
@ -892,6 +913,8 @@ static int accept_from_sock(struct listen_connection *con)
goto accept_err;
}
sock_set_mark(newsock->sk, mark);
mutex_lock(&newcon->sock_mutex);
if (newcon->sock) {
struct connection *othercon = newcon->othercon;
@ -1015,8 +1038,6 @@ static void sctp_connect_to_sock(struct connection *con)
struct socket *sock;
unsigned int mark;
dlm_comm_mark(con->nodeid, &mark);
mutex_lock(&con->sock_mutex);
/* Some odd races can cause double-connects, ignore them */
@ -1029,7 +1050,7 @@ static void sctp_connect_to_sock(struct connection *con)
}
memset(&daddr, 0, sizeof(daddr));
result = nodeid_to_addr(con->nodeid, &daddr, NULL, true);
result = nodeid_to_addr(con->nodeid, &daddr, NULL, true, &mark);
if (result < 0) {
log_print("no address for nodeid %d", con->nodeid);
goto out;
@ -1104,13 +1125,11 @@ out:
static void tcp_connect_to_sock(struct connection *con)
{
struct sockaddr_storage saddr, src_addr;
unsigned int mark;
int addr_len;
struct socket *sock = NULL;
unsigned int mark;
int result;
dlm_comm_mark(con->nodeid, &mark);
mutex_lock(&con->sock_mutex);
if (con->retries++ > MAX_CONNECT_RETRIES)
goto out;
@ -1125,15 +1144,15 @@ static void tcp_connect_to_sock(struct connection *con)
if (result < 0)
goto out_err;
sock_set_mark(sock->sk, mark);
memset(&saddr, 0, sizeof(saddr));
result = nodeid_to_addr(con->nodeid, &saddr, NULL, false);
result = nodeid_to_addr(con->nodeid, &saddr, NULL, false, &mark);
if (result < 0) {
log_print("no address for nodeid %d", con->nodeid);
goto out_err;
}
sock_set_mark(sock->sk, mark);
add_sock(sock, con);
/* Bind to our cluster-known address connecting to avoid

Просмотреть файл

@ -21,6 +21,7 @@ int dlm_lowcomms_close(int nodeid);
void *dlm_lowcomms_get_buffer(int nodeid, int len, gfp_t allocation, char **ppc);
void dlm_lowcomms_commit_buffer(void *mh);
int dlm_lowcomms_connect_node(int nodeid);
int dlm_lowcomms_nodes_set_mark(int nodeid, unsigned int mark);
int dlm_lowcomms_addr(int nodeid, struct sockaddr_storage *addr, int len);
#endif /* __LOWCOMMS_DOT_H__ */