]> Git Repo - linux.git/blobdiff - fs/cifs/smb2ops.c
cifs: do not send close in compound create+close requests
[linux.git] / fs / cifs / smb2ops.c
index f19274857292b5d271970a35797fd3372e8a9838..9bae7e8deb09e07bc28332f8c41767a239858ccd 100644 (file)
@@ -63,17 +63,19 @@ smb2_add_credits(struct TCP_Server_Info *server,
                 const struct cifs_credits *credits, const int optype)
 {
        int *val, rc = -1;
+       int scredits, in_flight;
        unsigned int add = credits->value;
        unsigned int instance = credits->instance;
        bool reconnect_detected = false;
+       bool reconnect_with_invalid_credits = false;
 
        spin_lock(&server->req_lock);
        val = server->ops->get_credits_field(server, optype);
 
        /* eg found case where write overlapping reconnect messed up credits */
        if (((optype & CIFS_OP_MASK) == CIFS_NEG_OP) && (*val != 0))
-               trace_smb3_reconnect_with_invalid_credits(server->CurrentMid,
-                       server->hostname, *val, add);
+               reconnect_with_invalid_credits = true;
+
        if ((instance == 0) || (instance == server->reconnect_instance))
                *val += add;
        else
@@ -84,7 +86,9 @@ smb2_add_credits(struct TCP_Server_Info *server,
                pr_warn_once("server overflowed SMB3 credits\n");
        }
        server->in_flight--;
-       if (server->in_flight == 0 && (optype & CIFS_OP_MASK) != CIFS_NEG_OP)
+       if (server->in_flight == 0 &&
+          ((optype & CIFS_OP_MASK) != CIFS_NEG_OP) &&
+          ((optype & CIFS_OP_MASK) != CIFS_SESS_OP))
                rc = change_conf(server);
        /*
         * Sometimes server returns 0 credits on oplock break ack - we need to
@@ -97,14 +101,26 @@ smb2_add_credits(struct TCP_Server_Info *server,
                        server->oplock_credits++;
                }
        }
+       scredits = *val;
+       in_flight = server->in_flight;
        spin_unlock(&server->req_lock);
        wake_up(&server->request_q);
 
        if (reconnect_detected) {
+               trace_smb3_reconnect_detected(server->CurrentMid,
+                       server->conn_id, server->hostname, scredits, add, in_flight);
+
                cifs_dbg(FYI, "trying to put %d credits from the old server instance %d\n",
                         add, instance);
        }
 
+       if (reconnect_with_invalid_credits) {
+               trace_smb3_reconnect_with_invalid_credits(server->CurrentMid,
+                       server->conn_id, server->hostname, scredits, add, in_flight);
+               cifs_dbg(FYI, "Negotiate operation when server credits is non-zero. Optype: %d, server credits: %d, credits added: %d\n",
+                        optype, scredits, add);
+       }
+
        if (server->tcpStatus == CifsNeedReconnect
            || server->tcpStatus == CifsExiting)
                return;
@@ -123,23 +139,30 @@ smb2_add_credits(struct TCP_Server_Info *server,
                cifs_dbg(FYI, "disabling oplocks\n");
                break;
        default:
-               trace_smb3_add_credits(server->CurrentMid,
-                       server->hostname, rc, add);
-               cifs_dbg(FYI, "%s: added %u credits total=%d\n", __func__, add, rc);
+               /* change_conf rebalanced credits for different types */
+               break;
        }
+
+       trace_smb3_add_credits(server->CurrentMid,
+                       server->conn_id, server->hostname, scredits, add, in_flight);
+       cifs_dbg(FYI, "%s: added %u credits total=%d\n", __func__, add, scredits);
 }
 
 static void
 smb2_set_credits(struct TCP_Server_Info *server, const int val)
 {
+       int scredits, in_flight;
+
        spin_lock(&server->req_lock);
        server->credits = val;
        if (val == 1)
                server->reconnect_instance++;
+       scredits = server->credits;
+       in_flight = server->in_flight;
        spin_unlock(&server->req_lock);
 
        trace_smb3_set_credits(server->CurrentMid,
-                       server->hostname, val, val);
+                       server->conn_id, server->hostname, scredits, val, in_flight);
        cifs_dbg(FYI, "%s: set %u credits\n", __func__, val);
 
        /* don't log while holding the lock */
@@ -171,7 +194,7 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
                      unsigned int *num, struct cifs_credits *credits)
 {
        int rc = 0;
-       unsigned int scredits;
+       unsigned int scredits, in_flight;
 
        spin_lock(&server->req_lock);
        while (1) {
@@ -208,17 +231,18 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
                                DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE);
                        credits->instance = server->reconnect_instance;
                        server->credits -= credits->value;
-                       scredits = server->credits;
                        server->in_flight++;
                        if (server->in_flight > server->max_in_flight)
                                server->max_in_flight = server->in_flight;
                        break;
                }
        }
+       scredits = server->credits;
+       in_flight = server->in_flight;
        spin_unlock(&server->req_lock);
 
        trace_smb3_add_credits(server->CurrentMid,
-                       server->hostname, scredits, -(credits->value));
+                       server->conn_id, server->hostname, scredits, -(credits->value), in_flight);
        cifs_dbg(FYI, "%s: removed %u credits total=%d\n",
                        __func__, credits->value, scredits);
 
@@ -231,14 +255,14 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
                    const unsigned int payload_size)
 {
        int new_val = DIV_ROUND_UP(payload_size, SMB2_MAX_BUFFER_SIZE);
-       int scredits;
+       int scredits, in_flight;
 
        if (!credits->value || credits->value == new_val)
                return 0;
 
        if (credits->value < new_val) {
                trace_smb3_too_many_credits(server->CurrentMid,
-                               server->hostname, 0, credits->value - new_val);
+                               server->conn_id, server->hostname, 0, credits->value - new_val, 0);
                cifs_server_dbg(VFS, "request has less credits (%d) than required (%d)",
                                credits->value, new_val);
 
@@ -248,9 +272,13 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
        spin_lock(&server->req_lock);
 
        if (server->reconnect_instance != credits->instance) {
+               scredits = server->credits;
+               in_flight = server->in_flight;
                spin_unlock(&server->req_lock);
+
                trace_smb3_reconnect_detected(server->CurrentMid,
-                       server->hostname, 0, 0);
+                       server->conn_id, server->hostname, scredits,
+                       credits->value - new_val, in_flight);
                cifs_server_dbg(VFS, "trying to return %d credits to old session\n",
                         credits->value - new_val);
                return -EAGAIN;
@@ -258,15 +286,18 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
 
        server->credits += credits->value - new_val;
        scredits = server->credits;
+       in_flight = server->in_flight;
        spin_unlock(&server->req_lock);
        wake_up(&server->request_q);
-       credits->value = new_val;
 
        trace_smb3_add_credits(server->CurrentMid,
-                       server->hostname, scredits, credits->value - new_val);
+                       server->conn_id, server->hostname, scredits,
+                       credits->value - new_val, in_flight);
        cifs_dbg(FYI, "%s: adjust added %u credits total=%d\n",
                        __func__, credits->value - new_val, scredits);
 
+       credits->value = new_val;
+
        return 0;
 }
 
@@ -1164,7 +1195,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
        struct TCP_Server_Info *server = cifs_pick_channel(ses);
        __le16 *utf16_path = NULL;
        int ea_name_len = strlen(ea_name);
-       int flags = 0;
+       int flags = CIFS_CP_CREATE_CLOSE_OP;
        int len;
        struct smb_rqst rqst[3];
        int resp_buftype[3];
@@ -1542,7 +1573,7 @@ smb2_ioctl_query_info(const unsigned int xid,
        struct smb_query_info qi;
        struct smb_query_info __user *pqi;
        int rc = 0;
-       int flags = 0;
+       int flags = CIFS_CP_CREATE_CLOSE_OP;
        struct smb2_query_info_rsp *qi_rsp = NULL;
        struct smb2_ioctl_rsp *io_rsp = NULL;
        void *buffer = NULL;
@@ -2369,7 +2400,7 @@ static bool
 smb2_is_status_pending(char *buf, struct TCP_Server_Info *server)
 {
        struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
-       int scredits;
+       int scredits, in_flight;
 
        if (shdr->Status != STATUS_PENDING)
                return false;
@@ -2378,11 +2409,13 @@ smb2_is_status_pending(char *buf, struct TCP_Server_Info *server)
                spin_lock(&server->req_lock);
                server->credits += le16_to_cpu(shdr->CreditRequest);
                scredits = server->credits;
+               in_flight = server->in_flight;
                spin_unlock(&server->req_lock);
                wake_up(&server->request_q);
 
                trace_smb3_add_credits(server->CurrentMid,
-                               server->hostname, scredits, le16_to_cpu(shdr->CreditRequest));
+                               server->conn_id, server->hostname, scredits,
+                               le16_to_cpu(shdr->CreditRequest), in_flight);
                cifs_dbg(FYI, "%s: status pending add %u credits total=%d\n",
                                __func__, le16_to_cpu(shdr->CreditRequest), scredits);
        }
@@ -2418,6 +2451,34 @@ smb2_is_status_io_timeout(char *buf)
                return false;
 }
 
+static void
+smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server)
+{
+       struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
+       struct list_head *tmp, *tmp1;
+       struct cifs_ses *ses;
+       struct cifs_tcon *tcon;
+
+       if (shdr->Status != STATUS_NETWORK_NAME_DELETED)
+               return;
+
+       spin_lock(&cifs_tcp_ses_lock);
+       list_for_each(tmp, &server->smb_ses_list) {
+               ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
+               list_for_each(tmp1, &ses->tcon_list) {
+                       tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
+                       if (tcon->tid == shdr->TreeId) {
+                               tcon->need_reconnect = true;
+                               spin_unlock(&cifs_tcp_ses_lock);
+                               pr_warn_once("Server share %s deleted.\n",
+                                            tcon->treeName);
+                               return;
+                       }
+               }
+       }
+       spin_unlock(&cifs_tcp_ses_lock);
+}
+
 static int
 smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
                     struct cifsInodeInfo *cinode)
@@ -2516,7 +2577,7 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
 {
        struct cifs_ses *ses = tcon->ses;
        struct TCP_Server_Info *server = cifs_pick_channel(ses);
-       int flags = 0;
+       int flags = CIFS_CP_CREATE_CLOSE_OP;
        struct smb_rqst rqst[3];
        int resp_buftype[3];
        struct kvec rsp_iov[3];
@@ -2914,7 +2975,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
        unsigned int sub_offset;
        unsigned int print_len;
        unsigned int print_offset;
-       int flags = 0;
+       int flags = CIFS_CP_CREATE_CLOSE_OP;
        struct smb_rqst rqst[3];
        int resp_buftype[3];
        struct kvec rsp_iov[3];
@@ -3096,7 +3157,7 @@ smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
        struct cifs_open_parms oparms;
        struct cifs_fid fid;
        struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
-       int flags = 0;
+       int flags = CIFS_CP_CREATE_CLOSE_OP;
        struct smb_rqst rqst[3];
        int resp_buftype[3];
        struct kvec rsp_iov[3];
@@ -4605,6 +4666,10 @@ static void smb2_decrypt_offload(struct work_struct *work)
 #ifdef CONFIG_CIFS_STATS2
                        mid->when_received = jiffies;
 #endif
+                       if (dw->server->ops->is_network_name_deleted)
+                               dw->server->ops->is_network_name_deleted(dw->buf,
+                                                                        dw->server);
+
                        mid->callback(mid);
                } else {
                        spin_lock(&GlobalMid_Lock);
@@ -4723,6 +4788,12 @@ non_offloaded_decrypt:
                rc = handle_read_data(server, *mid, buf,
                                      server->vals->read_rsp_size,
                                      pages, npages, len, false);
+               if (rc >= 0) {
+                       if (server->ops->is_network_name_deleted) {
+                               server->ops->is_network_name_deleted(buf,
+                                                               server);
+                       }
+               }
        }
 
 free_pages:
@@ -5072,6 +5143,7 @@ struct smb_version_operations smb20_operations = {
        .fiemap = smb3_fiemap,
        .llseek = smb3_llseek,
        .is_status_io_timeout = smb2_is_status_io_timeout,
+       .is_network_name_deleted = smb2_is_network_name_deleted,
 };
 
 struct smb_version_operations smb21_operations = {
@@ -5173,6 +5245,7 @@ struct smb_version_operations smb21_operations = {
        .fiemap = smb3_fiemap,
        .llseek = smb3_llseek,
        .is_status_io_timeout = smb2_is_status_io_timeout,
+       .is_network_name_deleted = smb2_is_network_name_deleted,
 };
 
 struct smb_version_operations smb30_operations = {
@@ -5286,6 +5359,7 @@ struct smb_version_operations smb30_operations = {
        .fiemap = smb3_fiemap,
        .llseek = smb3_llseek,
        .is_status_io_timeout = smb2_is_status_io_timeout,
+       .is_network_name_deleted = smb2_is_network_name_deleted,
 };
 
 struct smb_version_operations smb311_operations = {
@@ -5399,6 +5473,7 @@ struct smb_version_operations smb311_operations = {
        .fiemap = smb3_fiemap,
        .llseek = smb3_llseek,
        .is_status_io_timeout = smb2_is_status_io_timeout,
+       .is_network_name_deleted = smb2_is_network_name_deleted,
 };
 
 struct smb_version_values smb20_values = {
This page took 0.041795 seconds and 4 git commands to generate.