diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 7d1f68008435224d40a34388599a8352112bb216..d84389a88244d7c549e0b7fb5e37cbe9dd8113c1 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1619,6 +1619,7 @@ struct mid_q_entry { unsigned int mid_flags; __le16 command; /* smb command code */ unsigned int optype; /* operation type */ + spinlock_t mid_lock; bool large_buf:1; /* if valid response, is pointer to large buf */ bool multiRsp:1; /* multiple trans2 responses for one request */ bool multiEnd:1; /* both received */ @@ -2122,4 +2123,20 @@ static inline struct scatterlist *cifs_sg_set_buf(struct scatterlist *sg, return sg; } +/* + * Execute mid callback atomically - ensures callback runs exactly once + * and prevents sleeping in atomic context. + */ +static inline void mid_execute_callback(struct mid_q_entry *mid) +{ + void (*callback)(struct mid_q_entry *mid); + + spin_lock(&mid->mid_lock); + callback = mid->callback; + mid->callback = NULL; /* Mark as executed, */ + spin_unlock(&mid->mid_lock); + + if (callback) + callback(mid); +} #endif /* _CIFS_GLOB_H */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 23798ab5d5f1ad31cbf12128cfd90459dc1c3d5e..287230b70e5113057a5c0d80c266685223e909e4 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -504,7 +504,7 @@ cifs_reconnect(struct TCP_Server_Info *server) list_for_each_safe(tmp, tmp2, &retry_list) { mid_entry = list_entry(tmp, struct mid_q_entry, qhead); list_del_init(&mid_entry->qhead); - mid_entry->callback(mid_entry); + mid_execute_callback(mid_entry); cifs_mid_q_entry_release(mid_entry); } @@ -944,7 +944,7 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server) mid_entry = list_entry(tmp, struct mid_q_entry, qhead); cifs_dbg(FYI, "Callback mid 0x%llx\n", mid_entry->mid); list_del_init(&mid_entry->qhead); - mid_entry->callback(mid_entry); + mid_execute_callback(mid_entry); cifs_mid_q_entry_release(mid_entry); } /* 1/8th of sec is more than enough time for them to exit */ @@ -1195,7 +1195,7 @@ cifs_demultiplex_thread(void *p) mids[i]->resp_buf_size = server->pdu_size; if (!mids[i]->multiRsp || mids[i]->multiEnd) - mids[i]->callback(mids[i]); + mid_execute_callback(mids[i]); cifs_mid_q_entry_release(mids[i]); } else if (server->ops->is_oplock_break && diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 16245e8b6f28185fcddec409e6de289bd961b9ef..2bc268401d919e89a3d980b15568fe9b813787b5 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -4788,13 +4788,13 @@ static void smb2_decrypt_offload(struct work_struct *work) #ifdef CONFIG_CIFS_STATS2 mid->when_received = jiffies; #endif - mid->callback(mid); + mid_execute_callback(mid); } else { spin_lock(&GlobalMid_Lock); if (dw->server->tcpStatus == CifsNeedReconnect) { mid->mid_state = MID_RETRY_NEEDED; spin_unlock(&GlobalMid_Lock); - mid->callback(mid); + mid_execute_callback(mid); } else { mid->mid_state = MID_REQUEST_SUBMITTED; mid->mid_flags &= ~(MID_DELETED); diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index d659eb70df76dfb048139eeb4a8dd09a7e0db99b..d76e2c3d6b2992f3a17b5932b9b131cf96f35761 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -716,6 +716,7 @@ smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr, temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS); memset(temp, 0, sizeof(struct mid_q_entry)); kref_init(&temp->refcount); + spin_lock_init(&temp->mid_lock); temp->mid = le64_to_cpu(shdr->MessageId); temp->credits = credits > 0 ? credits : 1; temp->pid = current->pid; diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index d634a7692e139544b60a542456f94893c610e671..b5b17aa2fac47ede31d9e04da67c4d89e8dd10f3 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -65,6 +65,7 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS); memset(temp, 0, sizeof(struct mid_q_entry)); kref_init(&temp->refcount); + spin_lock_init(&temp->mid_lock); temp->mid = get_mid(smb_buffer); temp->pid = current->pid; temp->command = cpu_to_le16(smb_buffer->Command); @@ -1173,15 +1174,14 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, cifs_server_dbg(FYI, "Cancelling wait for mid %llu cmd: %d\n", midQ[i]->mid, le16_to_cpu(midQ[i]->command)); send_cancel(server, &rqst[i], midQ[i]); - spin_lock(&GlobalMid_Lock); + spin_lock(&midQ[i]->mid_lock); midQ[i]->mid_flags |= MID_WAIT_CANCELLED; - if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED || - midQ[i]->mid_state == MID_RESPONSE_RECEIVED) { + if (midQ[i]->callback) { midQ[i]->callback = cifs_cancelled_callback; cancelled_mid[i] = true; credits[i].value = 0; } - spin_unlock(&GlobalMid_Lock); + spin_unlock(&midQ[i]->mid_lock); } } @@ -1375,16 +1375,15 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, rc = wait_for_response(server, midQ); if (rc != 0) { send_cancel(server, &rqst, midQ); - spin_lock(&GlobalMid_Lock); - if (midQ->mid_state == MID_REQUEST_SUBMITTED || - midQ->mid_state == MID_RESPONSE_RECEIVED) { + spin_lock(&midQ->mid_lock); + if (midQ->callback) { /* no longer considered to be "in-flight" */ midQ->callback = DeleteMidQEntry; - spin_unlock(&GlobalMid_Lock); + spin_unlock(&midQ->mid_lock); add_credits(server, &credits, 0); return rc; } - spin_unlock(&GlobalMid_Lock); + spin_unlock(&midQ->mid_lock); } rc = cifs_sync_mid_result(midQ, server); @@ -1551,15 +1550,14 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, rc = wait_for_response(server, midQ); if (rc) { send_cancel(server, &rqst, midQ); - spin_lock(&GlobalMid_Lock); - if (midQ->mid_state == MID_REQUEST_SUBMITTED || - midQ->mid_state == MID_RESPONSE_RECEIVED) { + spin_lock(&midQ->mid_lock); + if (midQ->callback) { /* no longer considered to be "in-flight" */ midQ->callback = DeleteMidQEntry; - spin_unlock(&GlobalMid_Lock); + spin_unlock(&midQ->mid_lock); return rc; } - spin_unlock(&GlobalMid_Lock); + spin_unlock(&midQ->mid_lock); } /* We got the response - restart system call. */