Merge tag 'modules-for-v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/jeyu...
[linux-2.6-microblaze.git] / fs / cifs / smb2ops.c
index 21ef51d..e4c8f60 100644 (file)
@@ -388,7 +388,9 @@ smb2_negotiate(const unsigned int xid, struct cifs_ses *ses)
 {
        int rc;
 
+       spin_lock(&GlobalMid_Lock);
        cifs_ses_server(ses)->CurrentMid = 0;
+       spin_unlock(&GlobalMid_Lock);
        rc = SMB2_negotiate(xid, ses);
        /* BB we probably don't need to retry with modern servers */
        if (rc == -EAGAIN)
@@ -2325,6 +2327,7 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
        struct smb2_query_directory_rsp *qd_rsp = NULL;
        struct smb2_create_rsp *op_rsp = NULL;
        struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
+       int retry_count = 0;
 
        utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
        if (!utf16_path)
@@ -2372,10 +2375,14 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
 
        smb2_set_related(&rqst[1]);
 
+again:
        rc = compound_send_recv(xid, tcon->ses, server,
                                flags, 2, rqst,
                                resp_buftype, rsp_iov);
 
+       if (rc == -EAGAIN && retry_count++ < 10)
+               goto again;
+
        /* If the open failed there is nothing to do */
        op_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
        if (op_rsp == NULL || op_rsp->sync_hdr.Status != STATUS_SUCCESS) {
@@ -3601,6 +3608,119 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
        return rc;
 }
 
+static int smb3_simple_fallocate_write_range(unsigned int xid,
+                                            struct cifs_tcon *tcon,
+                                            struct cifsFileInfo *cfile,
+                                            loff_t off, loff_t len,
+                                            char *buf)
+{
+       struct cifs_io_parms io_parms = {0};
+       int nbytes;
+       struct kvec iov[2];
+
+       io_parms.netfid = cfile->fid.netfid;
+       io_parms.pid = current->tgid;
+       io_parms.tcon = tcon;
+       io_parms.persistent_fid = cfile->fid.persistent_fid;
+       io_parms.volatile_fid = cfile->fid.volatile_fid;
+       io_parms.offset = off;
+       io_parms.length = len;
+
+       /* iov[0] is reserved for smb header */
+       iov[1].iov_base = buf;
+       iov[1].iov_len = io_parms.length;
+       return SMB2_write(xid, &io_parms, &nbytes, iov, 1);
+}
+
+static int smb3_simple_fallocate_range(unsigned int xid,
+                                      struct cifs_tcon *tcon,
+                                      struct cifsFileInfo *cfile,
+                                      loff_t off, loff_t len)
+{
+       struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data;
+       u32 out_data_len;
+       char *buf = NULL;
+       loff_t l;
+       int rc;
+
+       in_data.file_offset = cpu_to_le64(off);
+       in_data.length = cpu_to_le64(len);
+       rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
+                       cfile->fid.volatile_fid,
+                       FSCTL_QUERY_ALLOCATED_RANGES, true,
+                       (char *)&in_data, sizeof(in_data),
+                       1024 * sizeof(struct file_allocated_range_buffer),
+                       (char **)&out_data, &out_data_len);
+       if (rc)
+               goto out;
+       /*
+        * It is already all allocated
+        */
+       if (out_data_len == 0)
+               goto out;
+
+       buf = kzalloc(1024 * 1024, GFP_KERNEL);
+       if (buf == NULL) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       tmp_data = out_data;
+       while (len) {
+               /*
+                * The rest of the region is unmapped so write it all.
+                */
+               if (out_data_len == 0) {
+                       rc = smb3_simple_fallocate_write_range(xid, tcon,
+                                              cfile, off, len, buf);
+                       goto out;
+               }
+
+               if (out_data_len < sizeof(struct file_allocated_range_buffer)) {
+                       rc = -EINVAL;
+                       goto out;
+               }
+
+               if (off < le64_to_cpu(tmp_data->file_offset)) {
+                       /*
+                        * We are at a hole. Write until the end of the region
+                        * or until the next allocated data,
+                        * whichever comes next.
+                        */
+                       l = le64_to_cpu(tmp_data->file_offset) - off;
+                       if (len < l)
+                               l = len;
+                       rc = smb3_simple_fallocate_write_range(xid, tcon,
+                                              cfile, off, l, buf);
+                       if (rc)
+                               goto out;
+                       off = off + l;
+                       len = len - l;
+                       if (len == 0)
+                               goto out;
+               }
+               /*
+                * We are at a section of allocated data, just skip forward
+                * until the end of the data or the end of the region
+                * we are supposed to fallocate, whichever comes first.
+                */
+               l = le64_to_cpu(tmp_data->length);
+               if (len < l)
+                       l = len;
+               off += l;
+               len -= l;
+
+               tmp_data = &tmp_data[1];
+               out_data_len -= sizeof(struct file_allocated_range_buffer);
+       }
+
+ out:
+       kfree(out_data);
+       kfree(buf);
+       return rc;
+}
+
+
 static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
                            loff_t off, loff_t len, bool keep_size)
 {
@@ -3661,6 +3781,26 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
        }
 
        if ((keep_size == true) || (i_size_read(inode) >= off + len)) {
+               /*
+                * At this point, we are trying to fallocate an internal
+                * regions of a sparse file. Since smb2 does not have a
+                * fallocate command we have two otions on how to emulate this.
+                * We can either turn the entire file to become non-sparse
+                * which we only do if the fallocate is for virtually
+                * the whole file,  or we can overwrite the region with zeroes
+                * using SMB2_write, which could be prohibitevly expensive
+                * if len is large.
+                */
+               /*
+                * We are only trying to fallocate a small region so
+                * just write it with zero.
+                */
+               if (len <= 1024 * 1024) {
+                       rc = smb3_simple_fallocate_range(xid, tcon, cfile,
+                                                        off, len);
+                       goto out;
+               }
+
                /*
                 * Check if falloc starts within first few pages of file
                 * and ends within a few pages of the end of file to