merged into cifs-2.6.git for-next On Fri, Mar 3, 2017 at 6:01 PM, Pavel Shilovsky <piastryyy@xxxxxxxxx> wrote: > 2017-03-03 15:41 GMT-08:00 Sachin Prabhu <sprabhu@xxxxxxxxxx>: >> A signal can interrupt a SendReceive call which result in incoming >> responses to the call being ignored. This is a problem for calls such as >> open which results in the successful response being ignored. This >> results in an open file resource on the server. >> >> The patch looks into responses which were cancelled after being sent and >> in case of successful open closes the open fids. >> >> For this patch, the check is only done in SendReceive2() >> >> RH-bz: 1403319 >> >> v2: Search for sess and tcon while under spinlock >> v3: Allocate struct cancelled before we pick the tcon >> Remove smb2_find_smb_sess_tcon() of the global functions >> v4: rebase against latest upstream. >> >> Signed-off-by: Sachin Prabhu <sprabhu@xxxxxxxxxx> >> --- >> fs/cifs/cifsglob.h | 11 ++++++++ >> fs/cifs/connect.c | 9 +++++-- >> fs/cifs/smb2misc.c | 47 ++++++++++++++++++++++++++++++++++ >> fs/cifs/smb2ops.c | 4 +++ >> fs/cifs/smb2proto.h | 7 ++++++ >> fs/cifs/smb2transport.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++--- >> fs/cifs/transport.c | 3 +++ >> 7 files changed, 142 insertions(+), 6 deletions(-) >> >> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h >> index d42dd32..c34bdb1 100644 >> --- a/fs/cifs/cifsglob.h >> +++ b/fs/cifs/cifsglob.h >> @@ -243,6 +243,7 @@ struct smb_version_operations { >> /* verify the message */ >> int (*check_message)(char *, unsigned int, struct TCP_Server_Info *); >> bool (*is_oplock_break)(char *, struct TCP_Server_Info *); >> + int (*handle_cancelled_mid)(char *, struct TCP_Server_Info *); >> void (*downgrade_oplock)(struct TCP_Server_Info *, >> struct cifsInodeInfo *, bool); >> /* process transaction2 response */ >> @@ -1343,6 +1344,7 @@ struct mid_q_entry { >> void *callback_data; /* general purpose pointer for callback */ >> void *resp_buf; /* pointer to received SMB header */ >> int mid_state; /* wish this were enum but can not pass to wait_event */ >> + unsigned int mid_flags; >> __le16 command; /* smb command code */ >> bool large_buf:1; /* if valid response, is pointer to large buf */ >> bool multiRsp:1; /* multiple trans2 responses for one request */ >> @@ -1350,6 +1352,12 @@ struct mid_q_entry { >> bool decrypted:1; /* decrypted entry */ >> }; >> >> +struct close_cancelled_open { >> + struct cifs_fid fid; >> + struct cifs_tcon *tcon; >> + struct work_struct work; >> +}; >> + >> /* Make code in transport.c a little cleaner by moving >> update of optional stats into function below */ >> #ifdef CONFIG_CIFS_STATS2 >> @@ -1481,6 +1489,9 @@ static inline void free_dfs_info_array(struct dfs_info3_param *param, >> #define MID_RESPONSE_MALFORMED 0x10 >> #define MID_SHUTDOWN 0x20 >> >> +/* Flags */ >> +#define MID_WAIT_CANCELLED 1 /* Cancelled while waiting for response */ >> + >> /* Types of response buffer returned from SendReceive2 */ >> #define CIFS_NO_BUFFER 0 /* Response buffer not returned */ >> #define CIFS_SMALL_BUFFER 1 >> diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c >> index de4c56e..6180166 100644 >> --- a/fs/cifs/connect.c >> +++ b/fs/cifs/connect.c >> @@ -903,10 +903,15 @@ cifs_demultiplex_thread(void *p) >> >> server->lstrp = jiffies; >> if (mid_entry != NULL) { >> + if ((mid_entry->mid_flags & MID_WAIT_CANCELLED) && >> + (server->ops->handle_cancelled_mid)) >> + server->ops->handle_cancelled_mid(buf, server); >> if (!mid_entry->multiRsp || mid_entry->multiEnd) >> mid_entry->callback(mid_entry); >> - } else if (!server->ops->is_oplock_break || >> - !server->ops->is_oplock_break(buf, server)) { >> + } else if (server->ops->is_oplock_break && >> + server->ops->is_oplock_break(buf, server)) { >> + cifs_dbg(FYI, "Received oplock break\n"); >> + } else { >> cifs_dbg(VFS, "No task to wake, unknown frame received! NumMids %d\n", >> atomic_read(&midCount)); >> cifs_dump_mem("Received Data is: ", buf, >> diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c >> index fd516ea..33e66cf 100644 >> --- a/fs/cifs/smb2misc.c >> +++ b/fs/cifs/smb2misc.c >> @@ -659,3 +659,50 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) >> cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n"); >> return false; >> } >> + >> +void >> +smb2_cancelled_close_fid(struct work_struct *work) >> +{ >> + struct close_cancelled_open *cancelled = container_of(work, >> + struct close_cancelled_open, work); >> + >> + cifs_dbg(VFS, "Close unmatched open\n"); >> + >> + SMB2_close(0, cancelled->tcon, cancelled->fid.persistent_fid, >> + cancelled->fid.volatile_fid); >> + cifs_put_tcon(cancelled->tcon); >> + kfree(cancelled); >> +} >> + >> +int >> +smb2_handle_cancelled_mid(char *buffer, struct TCP_Server_Info *server) >> +{ >> + struct smb2_sync_hdr *sync_hdr = get_sync_hdr(buffer); >> + struct smb2_create_rsp *rsp = (struct smb2_create_rsp *)buffer; >> + struct cifs_tcon *tcon; >> + struct close_cancelled_open *cancelled; >> + >> + if ((sync_hdr->Command != SMB2_CREATE) || >> + (sync_hdr->Status != STATUS_SUCCESS)) >> + return 0; >> + >> + cancelled = (struct close_cancelled_open *) >> + kzalloc(sizeof(struct close_cancelled_open), >> + GFP_KERNEL); >> + if (!cancelled) >> + return -ENOMEM; >> + >> + tcon = smb2_find_smb_tcon(server, sync_hdr->SessionId, sync_hdr->TreeId);; >> + if (!tcon) { >> + kfree(cancelled); >> + return -ENOENT; >> + } >> + >> + cancelled->fid.persistent_fid = rsp->PersistentFileId; >> + cancelled->fid.volatile_fid = rsp->VolatileFileId; >> + cancelled->tcon = tcon; >> + INIT_WORK(&cancelled->work, smb2_cancelled_close_fid); >> + queue_work(cifsiod_wq, &cancelled->work); >> + >> + return 0; >> +} >> diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c >> index 0231108..c6d14cc 100644 >> --- a/fs/cifs/smb2ops.c >> +++ b/fs/cifs/smb2ops.c >> @@ -2322,6 +2322,7 @@ struct smb_version_operations smb20_operations = { >> .clear_stats = smb2_clear_stats, >> .print_stats = smb2_print_stats, >> .is_oplock_break = smb2_is_valid_oplock_break, >> + .handle_cancelled_mid = smb2_handle_cancelled_mid, >> .downgrade_oplock = smb2_downgrade_oplock, >> .need_neg = smb2_need_neg, >> .negotiate = smb2_negotiate, >> @@ -2404,6 +2405,7 @@ struct smb_version_operations smb21_operations = { >> .clear_stats = smb2_clear_stats, >> .print_stats = smb2_print_stats, >> .is_oplock_break = smb2_is_valid_oplock_break, >> + .handle_cancelled_mid = smb2_handle_cancelled_mid, >> .downgrade_oplock = smb2_downgrade_oplock, >> .need_neg = smb2_need_neg, >> .negotiate = smb2_negotiate, >> @@ -2488,6 +2490,7 @@ struct smb_version_operations smb30_operations = { >> .print_stats = smb2_print_stats, >> .dump_share_caps = smb2_dump_share_caps, >> .is_oplock_break = smb2_is_valid_oplock_break, >> + .handle_cancelled_mid = smb2_handle_cancelled_mid, >> .downgrade_oplock = smb2_downgrade_oplock, >> .need_neg = smb2_need_neg, >> .negotiate = smb2_negotiate, >> @@ -2582,6 +2585,7 @@ struct smb_version_operations smb311_operations = { >> .print_stats = smb2_print_stats, >> .dump_share_caps = smb2_dump_share_caps, >> .is_oplock_break = smb2_is_valid_oplock_break, >> + .handle_cancelled_mid = smb2_handle_cancelled_mid, >> .downgrade_oplock = smb2_downgrade_oplock, >> .need_neg = smb2_need_neg, >> .negotiate = smb2_negotiate, >> diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h >> index 69e3587..6853454 100644 >> --- a/fs/cifs/smb2proto.h >> +++ b/fs/cifs/smb2proto.h >> @@ -48,6 +48,10 @@ extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses, >> struct smb_rqst *rqst); >> extern struct mid_q_entry *smb2_setup_async_request( >> struct TCP_Server_Info *server, struct smb_rqst *rqst); >> +extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server, >> + __u64 ses_id); >> +extern struct cifs_tcon *smb2_find_smb_tcon(struct TCP_Server_Info *server, >> + __u64 ses_id, __u32 tid); >> extern int smb2_calc_signature(struct smb_rqst *rqst, >> struct TCP_Server_Info *server); >> extern int smb3_calc_signature(struct smb_rqst *rqst, >> @@ -164,6 +168,9 @@ extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, >> extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, >> const u64 persistent_fid, const u64 volatile_fid, >> const __u8 oplock_level); >> +extern int smb2_handle_cancelled_mid(char *buffer, >> + struct TCP_Server_Info *server); >> +void smb2_cancelled_close_fid(struct work_struct *work); >> extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, >> u64 persistent_file_id, u64 volatile_file_id, >> struct kstatfs *FSData); >> diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c >> index 7c3bb1b..78b4135 100644 >> --- a/fs/cifs/smb2transport.c >> +++ b/fs/cifs/smb2transport.c >> @@ -115,23 +115,81 @@ smb3_crypto_shash_allocate(struct TCP_Server_Info *server) >> return 0; >> } >> >> -struct cifs_ses * >> -smb2_find_smb_ses(struct TCP_Server_Info *server, __u64 ses_id) >> +static struct cifs_ses * >> +_smb2_find_smb_ses(struct TCP_Server_Info *server, __u64 ses_id) >> { >> struct cifs_ses *ses; >> >> - spin_lock(&cifs_tcp_ses_lock); >> list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { >> if (ses->Suid != ses_id) >> continue; >> - spin_unlock(&cifs_tcp_ses_lock); >> return ses; >> } >> + >> + return NULL; >> +} >> + >> +struct cifs_ses * >> +smb2_find_smb_ses(struct TCP_Server_Info *server, __u64 ses_id) >> +{ >> + struct cifs_ses *ses; >> + >> + spin_lock(&cifs_tcp_ses_lock); >> + ses = _smb2_find_smb_ses(server, ses_id); >> spin_unlock(&cifs_tcp_ses_lock); >> >> + return ses; >> +} >> + >> +static struct cifs_tcon * >> +_smb2_find_smb_sess_tcon(struct cifs_ses *ses, __u32 tid) >> +{ >> + struct list_head *tmp; >> + struct cifs_tcon *tcon; >> + >> + list_for_each(tmp, &ses->tcon_list) { >> + tcon = list_entry(tmp, struct cifs_tcon, tcon_list); >> + if (tcon->tid != tid) >> + continue; >> + ++tcon->tc_count; >> + return tcon; >> + } >> + >> return NULL; >> } >> >> +/* >> + * Obtain tcon corresponding to the tid in the given >> + * cifs_ses >> + */ >> +struct cifs_tcon * >> +smb2_find_smb_sess_tcon(struct cifs_ses *ses, __u32 tid) >> +{ >> + struct cifs_tcon *tcon; >> + >> + spin_lock(&cifs_tcp_ses_lock); >> + tcon = _smb2_find_smb_sess_tcon(ses, tid); >> + spin_unlock(&cifs_tcp_ses_lock); >> + >> + return tcon; >> +} >> + >> +struct cifs_tcon * >> +smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid) >> +{ >> + struct cifs_ses *ses; >> + struct cifs_tcon *tcon; >> + >> + spin_lock(&cifs_tcp_ses_lock); >> + ses = _smb2_find_smb_ses(server, ses_id ); >> + if (!ses) >> + return NULL; >> + tcon = _smb2_find_smb_sess_tcon(ses, tid); >> + spin_unlock(&cifs_tcp_ses_lock); >> + >> + return tcon; >> +} >> + >> int >> smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) >> { >> @@ -511,6 +569,7 @@ smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr, >> >> atomic_inc(&midCount); >> temp->mid_state = MID_REQUEST_ALLOCATED; >> + temp->mid_flags = 0; >> return temp; >> } >> >> diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c >> index 526f053..d82a4ae 100644 >> --- a/fs/cifs/transport.c >> +++ b/fs/cifs/transport.c >> @@ -752,9 +752,12 @@ cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, >> >> rc = wait_for_response(ses->server, midQ); >> if (rc != 0) { >> + cifs_dbg(FYI, "Cancelling wait for mid %lu\n", >> + (unsigned long)midQ->mid); >> send_cancel(ses->server, rqst, midQ); >> spin_lock(&GlobalMid_Lock); >> if (midQ->mid_state == MID_REQUEST_SUBMITTED) { >> + midQ->mid_flags |= MID_WAIT_CANCELLED; >> midQ->callback = DeleteMidQEntry; >> spin_unlock(&GlobalMid_Lock); >> add_credits(ses->server, 1, optype); >> -- >> 2.9.3 >> >> -- >> To unsubscribe from this list: send the line "unsubscribe linux-cifs" in >> the body of a message to majordomo@xxxxxxxxxxxxxxx >> More majordomo info at http://vger.kernel.org/majordomo-info.html > > Reviewed-by: Pavel Shilovsky <pshilov@xxxxxxxxxxxxx> > > -- > Best regards, > Pavel Shilovsky > -- > To unsubscribe from this list: send the line "unsubscribe linux-cifs" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html -- Thanks, Steve -- To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html