Skip to content

Commit

Permalink
Merge tag '5.0-rc1-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6
Browse files Browse the repository at this point in the history
Pull cifs fixes from Steve French:
 "A set of cifs/smb3 fixes, 4 for stable, most from Pavel. His patches
  fix an important set of crediting (flow control) problems, and also
  two problems in cifs_writepages, ddressing some large i/o and also
  compounding issues"

* tag '5.0-rc1-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: update internal module version number
  CIFS: Fix error paths in writeback code
  CIFS: Move credit processing to mid callbacks for SMB3
  CIFS: Fix credits calculation for cancelled requests
  cifs: Fix potential OOB access of lock element array
  cifs: Limit memory used by lock request calls to a page
  cifs: move large array from stack to heap
  CIFS: Do not hide EINTR after sending network packets
  CIFS: Fix credit computation for compounded requests
  CIFS: Do not set credits to 1 if the server didn't grant anything
  CIFS: Fix adjustment of credits for MTU requests
  cifs: Fix a tiny potential memory leak
  cifs: Fix a debug message
  • Loading branch information
torvalds committed Jan 13, 2019
2 parents e170672 + 48d2ba6 commit 0f9d140
Show file tree
Hide file tree
Showing 10 changed files with 211 additions and 65 deletions.
2 changes: 1 addition & 1 deletion fs/cifs/cifsfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,5 +150,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
extern const struct export_operations cifs_export_ops;
#endif /* CONFIG_CIFS_NFSD_EXPORT */

#define CIFS_VERSION "2.15"
#define CIFS_VERSION "2.16"
#endif /* _CIFSFS_H */
20 changes: 20 additions & 0 deletions fs/cifs/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -1438,6 +1438,7 @@ struct mid_q_entry {
int mid_state; /* wish this were enum but can not pass to wait_event */
unsigned int mid_flags;
__le16 command; /* smb command code */
unsigned int optype; /* operation type */
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 */
Expand Down Expand Up @@ -1574,6 +1575,25 @@ static inline void free_dfs_info_array(struct dfs_info3_param *param,
kfree(param);
}

static inline bool is_interrupt_error(int error)
{
switch (error) {
case -EINTR:
case -ERESTARTSYS:
case -ERESTARTNOHAND:
case -ERESTARTNOINTR:
return true;
}
return false;
}

static inline bool is_retryable_error(int error)
{
if (is_interrupt_error(error) || error == -EAGAIN)
return true;
return false;
}

#define MID_FREE 0
#define MID_REQUEST_ALLOCATED 1
#define MID_REQUEST_SUBMITTED 2
Expand Down
30 changes: 20 additions & 10 deletions fs/cifs/cifssmb.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,24 +128,31 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc,
int rc;
struct dfs_cache_tgt_list tl;
struct dfs_cache_tgt_iterator *it = NULL;
char tree[MAX_TREE_SIZE + 1];
char *tree;
const char *tcp_host;
size_t tcp_host_len;
const char *dfs_host;
size_t dfs_host_len;

tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL);
if (!tree)
return -ENOMEM;

if (tcon->ipc) {
snprintf(tree, sizeof(tree), "\\\\%s\\IPC$",
snprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$",
tcon->ses->server->hostname);
return CIFSTCon(0, tcon->ses, tree, tcon, nlsc);
rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc);
goto out;
}

if (!tcon->dfs_path)
return CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nlsc);
if (!tcon->dfs_path) {
rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nlsc);
goto out;
}

rc = dfs_cache_noreq_find(tcon->dfs_path + 1, NULL, &tl);
if (rc)
return rc;
goto out;

extract_unc_hostname(tcon->ses->server->hostname, &tcp_host,
&tcp_host_len);
Expand All @@ -165,7 +172,7 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc,
continue;
}

snprintf(tree, sizeof(tree), "\\%s", tgt);
snprintf(tree, MAX_TREE_SIZE, "\\%s", tgt);

rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc);
if (!rc)
Expand All @@ -182,6 +189,8 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc,
rc = -ENOENT;
}
dfs_cache_free_tgts(&tl);
out:
kfree(tree);
return rc;
}
#else
Expand Down Expand Up @@ -2114,7 +2123,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata)

for (j = 0; j < nr_pages; j++) {
unlock_page(wdata2->pages[j]);
if (rc != 0 && rc != -EAGAIN) {
if (rc != 0 && !is_retryable_error(rc)) {
SetPageError(wdata2->pages[j]);
end_page_writeback(wdata2->pages[j]);
put_page(wdata2->pages[j]);
Expand All @@ -2123,7 +2132,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata)

if (rc) {
kref_put(&wdata2->refcount, cifs_writedata_release);
if (rc == -EAGAIN)
if (is_retryable_error(rc))
continue;
break;
}
Expand All @@ -2132,7 +2141,8 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
i += nr_pages;
} while (i < wdata->nr_pages);

mapping_set_error(inode->i_mapping, rc);
if (rc != 0 && !is_retryable_error(rc))
mapping_set_error(inode->i_mapping, rc);
kref_put(&wdata->refcount, cifs_writedata_release);
}

Expand Down
7 changes: 4 additions & 3 deletions fs/cifs/connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -433,9 +433,10 @@ static void reconn_inval_dfs_target(struct TCP_Server_Info *server,
kfree(server->hostname);

server->hostname = extract_hostname(name);
if (!server->hostname) {
cifs_dbg(FYI, "%s: failed to extract hostname from target: %d\n",
__func__, -ENOMEM);
if (IS_ERR(server->hostname)) {
cifs_dbg(FYI,
"%s: failed to extract hostname from target: %ld\n",
__func__, PTR_ERR(server->hostname));
}
}

Expand Down
1 change: 1 addition & 0 deletions fs/cifs/dfs_cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,7 @@ static int get_tgt_list(const struct dfs_cache_entry *ce,
it->it_name = kstrndup(t->t_name, strlen(t->t_name),
GFP_KERNEL);
if (!it->it_name) {
kfree(it);
rc = -ENOMEM;
goto err_free_it;
}
Expand Down
45 changes: 35 additions & 10 deletions fs/cifs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,8 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)

if (can_flush) {
rc = filemap_write_and_wait(inode->i_mapping);
mapping_set_error(inode->i_mapping, rc);
if (!is_interrupt_error(rc))
mapping_set_error(inode->i_mapping, rc);

if (tcon->unix_ext)
rc = cifs_get_inode_info_unix(&inode, full_path,
Expand Down Expand Up @@ -1132,14 +1133,18 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)

/*
* Accessing maxBuf is racy with cifs_reconnect - need to store value
* and check it for zero before using.
* and check it before using.
*/
max_buf = tcon->ses->server->maxBuf;
if (!max_buf) {
if (max_buf < (sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE))) {
free_xid(xid);
return -EINVAL;
}

BUILD_BUG_ON(sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE) >
PAGE_SIZE);
max_buf = min_t(unsigned int, max_buf - sizeof(struct smb_hdr),
PAGE_SIZE);
max_num = (max_buf - sizeof(struct smb_hdr)) /
sizeof(LOCKING_ANDX_RANGE);
buf = kcalloc(max_num, sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL);
Expand Down Expand Up @@ -1472,12 +1477,16 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,

/*
* Accessing maxBuf is racy with cifs_reconnect - need to store value
* and check it for zero before using.
* and check it before using.
*/
max_buf = tcon->ses->server->maxBuf;
if (!max_buf)
if (max_buf < (sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE)))
return -EINVAL;

BUILD_BUG_ON(sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE) >
PAGE_SIZE);
max_buf = min_t(unsigned int, max_buf - sizeof(struct smb_hdr),
PAGE_SIZE);
max_num = (max_buf - sizeof(struct smb_hdr)) /
sizeof(LOCKING_ANDX_RANGE);
buf = kcalloc(max_num, sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL);
Expand Down Expand Up @@ -2110,6 +2119,7 @@ static int cifs_writepages(struct address_space *mapping,
pgoff_t end, index;
struct cifs_writedata *wdata;
int rc = 0;
int saved_rc = 0;
unsigned int xid;

/*
Expand Down Expand Up @@ -2138,15 +2148,18 @@ static int cifs_writepages(struct address_space *mapping,

rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize,
&wsize, &credits);
if (rc)
if (rc != 0) {
done = true;
break;
}

tofind = min((wsize / PAGE_SIZE) - 1, end - index) + 1;

wdata = wdata_alloc_and_fillpages(tofind, mapping, end, &index,
&found_pages);
if (!wdata) {
rc = -ENOMEM;
done = true;
add_credits_and_wake_if(server, credits, 0);
break;
}
Expand Down Expand Up @@ -2175,15 +2188,15 @@ static int cifs_writepages(struct address_space *mapping,
if (rc != 0) {
add_credits_and_wake_if(server, wdata->credits, 0);
for (i = 0; i < nr_pages; ++i) {
if (rc == -EAGAIN)
if (is_retryable_error(rc))
redirty_page_for_writepage(wbc,
wdata->pages[i]);
else
SetPageError(wdata->pages[i]);
end_page_writeback(wdata->pages[i]);
put_page(wdata->pages[i]);
}
if (rc != -EAGAIN)
if (!is_retryable_error(rc))
mapping_set_error(mapping, rc);
}
kref_put(&wdata->refcount, cifs_writedata_release);
Expand All @@ -2193,6 +2206,15 @@ static int cifs_writepages(struct address_space *mapping,
continue;
}

/* Return immediately if we received a signal during writing */
if (is_interrupt_error(rc)) {
done = true;
break;
}

if (rc != 0 && saved_rc == 0)
saved_rc = rc;

wbc->nr_to_write -= nr_pages;
if (wbc->nr_to_write <= 0)
done = true;
Expand All @@ -2210,6 +2232,9 @@ static int cifs_writepages(struct address_space *mapping,
goto retry;
}

if (saved_rc != 0)
rc = saved_rc;

if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0))
mapping->writeback_index = index;

Expand Down Expand Up @@ -2242,8 +2267,8 @@ cifs_writepage_locked(struct page *page, struct writeback_control *wbc)
set_page_writeback(page);
retry_write:
rc = cifs_partialpagewrite(page, 0, PAGE_SIZE);
if (rc == -EAGAIN) {
if (wbc->sync_mode == WB_SYNC_ALL)
if (is_retryable_error(rc)) {
if (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN)
goto retry_write;
redirty_page_for_writepage(wbc, page);
} else if (rc != 0) {
Expand Down
10 changes: 10 additions & 0 deletions fs/cifs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -2257,6 +2257,11 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
* the flush returns error?
*/
rc = filemap_write_and_wait(inode->i_mapping);
if (is_interrupt_error(rc)) {
rc = -ERESTARTSYS;
goto out;
}

mapping_set_error(inode->i_mapping, rc);
rc = 0;

Expand Down Expand Up @@ -2400,6 +2405,11 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
* the flush returns error?
*/
rc = filemap_write_and_wait(inode->i_mapping);
if (is_interrupt_error(rc)) {
rc = -ERESTARTSYS;
goto cifs_setattr_exit;
}

mapping_set_error(inode->i_mapping, rc);
rc = 0;

Expand Down
8 changes: 6 additions & 2 deletions fs/cifs/smb2file.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,14 @@ smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,

/*
* Accessing maxBuf is racy with cifs_reconnect - need to store value
* and check it for zero before using.
* and check it before using.
*/
max_buf = tcon->ses->server->maxBuf;
if (!max_buf)
if (max_buf < sizeof(struct smb2_lock_element))
return -EINVAL;

BUILD_BUG_ON(sizeof(struct smb2_lock_element) > PAGE_SIZE);
max_buf = min_t(unsigned int, max_buf, PAGE_SIZE);
max_num = max_buf / sizeof(struct smb2_lock_element);
buf = kcalloc(max_num, sizeof(struct smb2_lock_element), GFP_KERNEL);
if (!buf)
Expand Down Expand Up @@ -264,6 +266,8 @@ smb2_push_mandatory_locks(struct cifsFileInfo *cfile)
return -EINVAL;
}

BUILD_BUG_ON(sizeof(struct smb2_lock_element) > PAGE_SIZE);
max_buf = min_t(unsigned int, max_buf, PAGE_SIZE);
max_num = max_buf / sizeof(struct smb2_lock_element);
buf = kcalloc(max_num, sizeof(struct smb2_lock_element), GFP_KERNEL);
if (!buf) {
Expand Down
Loading

0 comments on commit 0f9d140

Please sign in to comment.