Commit 023f78b0 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6

Pull CIFS updates from Steve French:
 "The most visible change in this set is the additional of multi-credit
  support for SMB2/SMB3 which dramatically improves the large file i/o
  performance for these dialects and significantly increases the maximum
  i/o size used on the wire for SMB2/SMB3.

  Also reconnection behavior after network failure is improved"

* 'for-next' of git://git.samba.org/sfrench/cifs-2.6: (35 commits)
  Add worker function to set allocation size
  [CIFS] Fix incorrect hex vs. decimal in some debug print statements
  update CIFS TODO list
  Add Pavel to contributor list in cifs AUTHORS file
  Update cifs version
  CIFS: Fix STATUS_CANNOT_DELETE error mapping for SMB2
  CIFS: Optimize readpages in a short read case on reconnects
  CIFS: Optimize cifs_user_read() in a short read case on reconnects
  CIFS: Improve indentation in cifs_user_read()
  CIFS: Fix possible buffer corruption in cifs_user_read()
  CIFS: Count got bytes in read_into_pages()
  CIFS: Use separate var for the number of bytes got in async read
  CIFS: Indicate reconnect with ECONNABORTED error code
  CIFS: Use multicredits for SMB 2.1/3 reads
  CIFS: Fix rsize usage for sync read
  CIFS: Fix rsize usage in user read
  CIFS: Separate page reading from user read
  CIFS: Fix rsize usage in readpages
  CIFS: Separate page search from readpages
  CIFS: Use multicredits for SMB 2.1/3 writes
  ...
parents 63b12bdb f29ebb47
......@@ -40,6 +40,7 @@ Gunter Kukkukk (testing and suggestions for support of old servers)
Igor Mammedov (DFS support)
Jeff Layton (many, many fixes, as well as great work on the cifs Kerberos code)
Scott Lovenberg
Pavel Shilovsky (for great work adding SMB2 support, and various SMB3 features)
Test case and Bug Report contributors
-------------------------------------
......
Version 1.53 May 20, 2008
Version 2.03 August 1, 2014
A Partial List of Missing Features
==================================
......@@ -7,63 +7,49 @@ Contributions are welcome. There are plenty of opportunities
for visible, important contributions to this module. Here
is a partial list of the known problems and missing features:
a) Support for SecurityDescriptors(Windows/CIFS ACLs) for chmod/chgrp/chown
so that these operations can be supported to Windows servers
a) SMB3 (and SMB3.02) missing optional features:
- RDMA
- multichannel (started)
- directory leases (improved metadata caching)
- T10 copy offload (copy chunk is only mechanism supported)
- encrypted shares
b) Mapping POSIX ACLs (and eventually NFSv4 ACLs) to CIFS
SecurityDescriptors
b) improved sparse file support
c) Better pam/winbind integration (e.g. to handle uid mapping
better)
d) Cleanup now unneeded SessSetup code in
fs/cifs/connect.c and add back in NTLMSSP code if any servers
need it
e) fix NTLMv2 signing when two mounts with different users to same
server.
f) Directory entry caching relies on a 1 second timer, rather than
c) Directory entry caching relies on a 1 second timer, rather than
using FindNotify or equivalent. - (started)
g) quota support (needs minor kernel change since quota calls
d) quota support (needs minor kernel change since quota calls
to make it to network filesystems or deviceless filesystems)
h) investigate sync behavior (including syncpage) and check
for proper behavior of intr/nointr
i) improve support for very old servers (OS/2 and Win9x for example)
e) improve support for very old servers (OS/2 and Win9x for example)
Including support for changing the time remotely (utimes command).
j) hook lower into the sockets api (as NFS/SunRPC does) to avoid the
f) hook lower into the sockets api (as NFS/SunRPC does) to avoid the
extra copy in/out of the socket buffers in some cases.
k) Better optimize open (and pathbased setfilesize) to reduce the
g) Better optimize open (and pathbased setfilesize) to reduce the
oplock breaks coming from windows srv. Piggyback identical file
opens on top of each other by incrementing reference count rather
than resending (helps reduce server resource utilization and avoid
spurious oplock breaks).
l) Improve performance of readpages by sending more than one read
at a time when 8 pages or more are requested. In conjuntion
add support for async_cifs_readpages.
m) Add support for storing symlink info to Windows servers
h) Add support for storing symlink info to Windows servers
in the Extended Attribute format their SFU clients would recognize.
n) Finish fcntl D_NOTIFY support so kde and gnome file list windows
i) Finish inotify support so kde and gnome file list windows
will autorefresh (partially complete by Asser). Needs minor kernel
vfs change to support removing D_NOTIFY on a file.
o) Add GUI tool to configure /proc/fs/cifs settings and for display of
j) Add GUI tool to configure /proc/fs/cifs settings and for display of
the CIFS statistics (started)
p) implement support for security and trusted categories of xattrs
k) implement support for security and trusted categories of xattrs
(requires minor protocol extension) to enable better support for SELINUX
q) Implement O_DIRECT flag on open (already supported on mount)
l) Implement O_DIRECT flag on open (already supported on mount)
r) Create UID mapping facility so server UIDs can be mapped on a per
m) Create UID mapping facility so server UIDs can be mapped on a per
mount or a per server basis to client UIDs or nobody if no mapping
exists. This is helpful when Unix extensions are negotiated to
allow better permission checking when UIDs differ on the server
......@@ -71,28 +57,29 @@ and client. Add new protocol request to the CIFS protocol
standard for asking the server for the corresponding name of a
particular uid.
s) Add support for CIFS Unix and also the newer POSIX extensions to the
server side for Samba 4.
n) DOS attrs - returned as pseudo-xattr in Samba format (check VFAT and NTFS for this too)
o) mount check for unmatched uids
t) In support for OS/2 (LANMAN 1.2 and LANMAN2.1 based SMB servers)
need to add ability to set time to server (utimes command)
p) Add support for new vfs entry point for fallocate
u) DOS attrs - returned as pseudo-xattr in Samba format (check VFAT and NTFS for this too)
q) Add tools to take advantage of cifs/smb3 specific ioctls and features
such as "CopyChunk" (fast server side file copy)
v) mount check for unmatched uids
r) encrypted file support
w) Add support for new vfs entry point for fallocate
s) improved stats gathering, tools (perhaps integration with nfsometer?)
x) Fix Samba 3 server to handle Linux kernel aio so dbench with lots of
processes can proceed better in parallel (on the server)
t) allow setting more NTFS/SMB3 file attributes remotely (currently limited to compressed
file attribute via chflags)
y) Fix Samba 3 to handle reads/writes over 127K (and remove the cifs mount
restriction of wsize max being 127K)
u) mount helper GUI (to simplify the various configuration options on mount)
KNOWN BUGS (updated April 24, 2007)
KNOWN BUGS
====================================
See http://bugzilla.samba.org - search on product "CifsVFS" for
current bug list.
current bug list. Also check http://bugzilla.kernel.org (Product = File System, Component = CIFS)
1) existing symbolic links (Windows reparse points) are recognized but
can not be created remotely. They are implemented for Samba and those that
......@@ -100,30 +87,18 @@ support the CIFS Unix extensions, although earlier versions of Samba
overly restrict the pathnames.
2) follow_link and readdir code does not follow dfs junctions
but recognizes them
3) create of new files to FAT partitions on Windows servers can
succeed but still return access denied (appears to be Windows
server not cifs client problem) and has not been reproduced recently.
NTFS partitions do not have this problem.
4) Unix/POSIX capabilities are reset after reconnection, and affect
a few fields in the tree connection but we do do not know which
superblocks to apply these changes to. We should probably walk
the list of superblocks to set these. Also need to check the
flags on the second mount to the same share, and see if we
can do the same trick that NFS does to remount duplicate shares.
Misc testing to do
==================
1) check out max path names and max path name components against various server
types. Try nested symlinks (8 deep). Return max path name in stat -f information
2) Modify file portion of ltp so it can run against a mounted network
share and run it against cifs vfs in automated fashion.
2) Improve xfstest's cifs enablement and adapt xfstests where needed to test
cifs better
3) Additional performance testing and optimization using iozone and similar -
there are some easy changes that can be done to parallelize sequential writes,
and when signing is disabled to request larger read sizes (larger than
negotiated size) and send larger write sizes to modern servers.
4) More exhaustively test against less common servers. More testing
against Windows 9x, Windows ME servers.
4) More exhaustively test against less common servers
......@@ -213,7 +213,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
tcon->nativeFileSystem);
}
seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x"
"\n\tPathComponentMax: %d Status: 0x%d",
"\n\tPathComponentMax: %d Status: %d",
le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics),
le32_to_cpu(tcon->fsAttrInfo.Attributes),
le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength),
......
......@@ -136,5 +136,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.03"
#define CIFS_VERSION "2.04"
#endif /* _CIFSFS_H */
......@@ -404,6 +404,11 @@ struct smb_version_operations {
const struct cifs_fid *, u32 *);
int (*set_acl)(struct cifs_ntsd *, __u32, struct inode *, const char *,
int);
/* writepages retry size */
unsigned int (*wp_retry_size)(struct inode *);
/* get mtu credits */
int (*wait_mtu_credits)(struct TCP_Server_Info *, unsigned int,
unsigned int *, unsigned int *);
};
struct smb_version_values {
......@@ -639,6 +644,16 @@ add_credits(struct TCP_Server_Info *server, const unsigned int add,
server->ops->add_credits(server, add, optype);
}
static inline void
add_credits_and_wake_if(struct TCP_Server_Info *server, const unsigned int add,
const int optype)
{
if (add) {
server->ops->add_credits(server, add, optype);
wake_up(&server->request_q);
}
}
static inline void
set_credits(struct TCP_Server_Info *server, const int val)
{
......@@ -1044,6 +1059,7 @@ struct cifs_readdata {
struct address_space *mapping;
__u64 offset;
unsigned int bytes;
unsigned int got_bytes;
pid_t pid;
int result;
struct work_struct work;
......@@ -1053,6 +1069,7 @@ struct cifs_readdata {
struct kvec iov;
unsigned int pagesz;
unsigned int tailsz;
unsigned int credits;
unsigned int nr_pages;
struct page *pages[];
};
......@@ -1073,6 +1090,7 @@ struct cifs_writedata {
int result;
unsigned int pagesz;
unsigned int tailsz;
unsigned int credits;
unsigned int nr_pages;
struct page *pages[];
};
......@@ -1398,6 +1416,7 @@ static inline void free_dfs_info_array(struct dfs_info3_param *param,
#define CIFS_OBREAK_OP 0x0100 /* oplock break request */
#define CIFS_NEG_OP 0x0200 /* negotiate request */
#define CIFS_OP_MASK 0x0380 /* mask request type */
#define CIFS_HAS_CREDITS 0x0400 /* already has credits */
/* Security Flags: indicate type of session setup needed */
#define CIFSSEC_MAY_SIGN 0x00001
......
......@@ -36,6 +36,7 @@ extern struct smb_hdr *cifs_buf_get(void);
extern void cifs_buf_release(void *);
extern struct smb_hdr *cifs_small_buf_get(void);
extern void cifs_small_buf_release(void *);
extern void free_rsp_buf(int, void *);
extern void cifs_rqst_page_to_kvec(struct smb_rqst *rqst, unsigned int idx,
struct kvec *iov);
extern int smb_send(struct TCP_Server_Info *, struct smb_hdr *,
......@@ -89,6 +90,9 @@ extern struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *,
struct smb_rqst *);
extern int cifs_check_receive(struct mid_q_entry *mid,
struct TCP_Server_Info *server, bool log_error);
extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server,
unsigned int size, unsigned int *num,
unsigned int *credits);
extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *,
struct kvec *, int /* nvec to send */,
int * /* type of buf returned */ , const int flags);
......
......@@ -196,10 +196,6 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
if (rc)
goto out;
/*
* FIXME: check if wsize needs updated due to negotiated smb buffer
* size shrinking
*/
atomic_inc(&tconInfoReconnectCount);
/* tell server Unix caps we support */
......@@ -1517,7 +1513,6 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
return length;
server->total_read += length;
rdata->bytes = length;
cifs_dbg(FYI, "total_read=%u buflen=%u remaining=%u\n",
server->total_read, buflen, data_len);
......@@ -1560,12 +1555,18 @@ cifs_readv_callback(struct mid_q_entry *mid)
rc);
}
/* FIXME: should this be counted toward the initiating task? */
task_io_account_read(rdata->bytes);
cifs_stats_bytes_read(tcon, rdata->bytes);
task_io_account_read(rdata->got_bytes);
cifs_stats_bytes_read(tcon, rdata->got_bytes);
break;
case MID_REQUEST_SUBMITTED:
case MID_RETRY_NEEDED:
rdata->result = -EAGAIN;
if (server->sign && rdata->got_bytes)
/* reset bytes number since we can not check a sign */
rdata->got_bytes = 0;
/* FIXME: should this be counted toward the initiating task? */
task_io_account_read(rdata->got_bytes);
cifs_stats_bytes_read(tcon, rdata->got_bytes);
break;
default:
rdata->result = -EIO;
......@@ -1734,10 +1735,7 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms,
/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
if (*buf) {
if (resp_buf_type == CIFS_SMALL_BUFFER)
cifs_small_buf_release(iov[0].iov_base);
else if (resp_buf_type == CIFS_LARGE_BUFFER)
cifs_buf_release(iov[0].iov_base);
free_rsp_buf(resp_buf_type, iov[0].iov_base);
} else if (resp_buf_type != CIFS_NO_BUFFER) {
/* return buffer to caller to free */
*buf = iov[0].iov_base;
......@@ -1899,28 +1897,80 @@ cifs_writedata_release(struct kref *refcount)
static void
cifs_writev_requeue(struct cifs_writedata *wdata)
{
int i, rc;
int i, rc = 0;
struct inode *inode = wdata->cfile->dentry->d_inode;
struct TCP_Server_Info *server;
unsigned int rest_len;
for (i = 0; i < wdata->nr_pages; i++) {
lock_page(wdata->pages[i]);
clear_page_dirty_for_io(wdata->pages[i]);
}
server = tlink_tcon(wdata->cfile->tlink)->ses->server;
i = 0;
rest_len = wdata->bytes;
do {
server = tlink_tcon(wdata->cfile->tlink)->ses->server;
rc = server->ops->async_writev(wdata, cifs_writedata_release);
} while (rc == -EAGAIN);
struct cifs_writedata *wdata2;
unsigned int j, nr_pages, wsize, tailsz, cur_len;
wsize = server->ops->wp_retry_size(inode);
if (wsize < rest_len) {
nr_pages = wsize / PAGE_CACHE_SIZE;
if (!nr_pages) {
rc = -ENOTSUPP;
break;
}
cur_len = nr_pages * PAGE_CACHE_SIZE;
tailsz = PAGE_CACHE_SIZE;
} else {
nr_pages = DIV_ROUND_UP(rest_len, PAGE_CACHE_SIZE);
cur_len = rest_len;
tailsz = rest_len - (nr_pages - 1) * PAGE_CACHE_SIZE;
}
for (i = 0; i < wdata->nr_pages; i++) {
unlock_page(wdata->pages[i]);
if (rc != 0) {
SetPageError(wdata->pages[i]);
end_page_writeback(wdata->pages[i]);
page_cache_release(wdata->pages[i]);
wdata2 = cifs_writedata_alloc(nr_pages, cifs_writev_complete);
if (!wdata2) {
rc = -ENOMEM;
break;
}
}
for (j = 0; j < nr_pages; j++) {
wdata2->pages[j] = wdata->pages[i + j];
lock_page(wdata2->pages[j]);
clear_page_dirty_for_io(wdata2->pages[j]);
}
wdata2->sync_mode = wdata->sync_mode;
wdata2->nr_pages = nr_pages;
wdata2->offset = page_offset(wdata2->pages[0]);
wdata2->pagesz = PAGE_CACHE_SIZE;
wdata2->tailsz = tailsz;
wdata2->bytes = cur_len;
wdata2->cfile = find_writable_file(CIFS_I(inode), false);
if (!wdata2->cfile) {
cifs_dbg(VFS, "No writable handles for inode\n");
rc = -EBADF;
break;
}
wdata2->pid = wdata2->cfile->pid;
rc = server->ops->async_writev(wdata2, cifs_writedata_release);
for (j = 0; j < nr_pages; j++) {
unlock_page(wdata2->pages[j]);
if (rc != 0 && rc != -EAGAIN) {
SetPageError(wdata2->pages[j]);
end_page_writeback(wdata2->pages[j]);
page_cache_release(wdata2->pages[j]);
}
}
if (rc) {
kref_put(&wdata2->refcount, cifs_writedata_release);
if (rc == -EAGAIN)
continue;
break;
}
rest_len -= cur_len;
i += nr_pages;
} while (i < wdata->nr_pages);
mapping_set_error(inode->i_mapping, rc);
kref_put(&wdata->refcount, cifs_writedata_release);
......@@ -2203,10 +2253,7 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
}
/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
if (resp_buf_type == CIFS_SMALL_BUFFER)
cifs_small_buf_release(iov[0].iov_base);
else if (resp_buf_type == CIFS_LARGE_BUFFER)
cifs_buf_release(iov[0].iov_base);
free_rsp_buf(resp_buf_type, iov[0].iov_base);
/* Note: On -EAGAIN error only caller can retry on handle based calls
since file handle passed in no longer valid */
......@@ -2451,10 +2498,7 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,
if (pSMB)
cifs_small_buf_release(pSMB);
if (resp_buf_type == CIFS_SMALL_BUFFER)
cifs_small_buf_release(iov[0].iov_base);
else if (resp_buf_type == CIFS_LARGE_BUFFER)
cifs_buf_release(iov[0].iov_base);
free_rsp_buf(resp_buf_type, iov[0].iov_base);
/* Note: On -EAGAIN error only caller can retry on handle based calls
since file handle passed in no longer valid */
......@@ -3838,10 +3882,7 @@ CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid,
}
}
qsec_out:
if (buf_type == CIFS_SMALL_BUFFER)
cifs_small_buf_release(iov[0].iov_base);
else if (buf_type == CIFS_LARGE_BUFFER)
cifs_buf_release(iov[0].iov_base);
free_rsp_buf(buf_type, iov[0].iov_base);
/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
return rc;
}
......
......@@ -557,7 +557,7 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct kvec *iov_orig,
try_to_freeze();
if (server_unresponsive(server)) {
total_read = -EAGAIN;
total_read = -ECONNABORTED;
break;
}
......@@ -571,7 +571,7 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct kvec *iov_orig,
break;
} else if (server->tcpStatus == CifsNeedReconnect) {
cifs_reconnect(server);
total_read = -EAGAIN;
total_read = -ECONNABORTED;
break;
} else if (length == -ERESTARTSYS ||
length == -EAGAIN ||
......@@ -588,7 +588,7 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct kvec *iov_orig,
cifs_dbg(FYI, "Received no data or error: expecting %d\n"
"got %d", to_read, length);
cifs_reconnect(server);
total_read = -EAGAIN;
total_read = -ECONNABORTED;
break;
}
}
......@@ -786,7 +786,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
cifs_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length);
cifs_reconnect(server);
wake_up(&server->response_q);
return -EAGAIN;
return -ECONNABORTED;
}
/* switch to large buffer if too big for a small one */
......
This diff is collapsed.
......@@ -226,6 +226,15 @@ cifs_small_buf_release(void *buf_to_free)
return;
}
void
free_rsp_buf(int resp_buftype, void *rsp)
{
if (resp_buftype == CIFS_SMALL_BUFFER)
cifs_small_buf_release(rsp);
else if (resp_buftype == CIFS_LARGE_BUFFER)
cifs_buf_release(rsp);
}
/* NB: MID can not be set if treeCon not passed in, in that
case it is responsbility of caller to set the mid */
void
......@@ -414,7 +423,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
return true;
}
if (pSMBr->hdr.Status.CifsError) {
cifs_dbg(FYI, "notify err 0x%d\n",
cifs_dbg(FYI, "notify err 0x%x\n",
pSMBr->hdr.Status.CifsError);
return true;
}
......@@ -441,7 +450,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
if (pSMB->hdr.WordCount != 8)
return false;
cifs_dbg(FYI, "oplock type 0x%d level 0x%d\n",
cifs_dbg(FYI, "oplock type 0x%x level 0x%x\n",
pSMB->LockType, pSMB->OplockLevel);
if (!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE))
return false;
......
This diff is collapsed.
......@@ -1009,6 +1009,12 @@ cifs_is_read_op(__u32 oplock)
return oplock == OPLOCK_READ;
}
static unsigned int
cifs_wp_retry_size(struct inode *inode)
{
return CIFS_SB(inode->i_sb)->wsize;
}
struct smb_version_operations smb1_operations = {
.send_cancel = send_nt_cancel,
.compare_fids = cifs_compare_fids,
......@@ -1019,6 +1025,7 @@ struct smb_version_operations smb1_operations = {
.set_credits = cifs_set_credits,
.get_credits_field = cifs_get_credits_field,
.get_credits = cifs_get_credits,
.wait_mtu_credits = cifs_wait_mtu_credits,
.get_next_mid = cifs_get_next_mid,
.read_data_offset = cifs_read_data_offset,
.read_data_length = cifs_read_data_length,
......@@ -1078,6 +1085,7 @@ struct smb_version_operations smb1_operations = {
.query_mf_symlink = cifs_query_mf_symlink,
.create_mf_symlink = cifs_create_mf_symlink,
.is_read_op = cifs_is_read_op,
.wp_retry_size = cifs_wp_retry_size,
#ifdef CONFIG_CIFS_XATTR
.query_all_EAs = CIFSSMBQAllEAs,
.set_EA = CIFSSMBSetEA,
......
......@@ -91,7 +91,7 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
case SMB2_OP_SET_EOF:
tmprc = SMB2_set_eof(xid, tcon, fid.persistent_fid,
fid.volatile_fid, current->tgid,
(__le64 *)data);
(__le64 *)data, false);
break;
case SMB2_OP_SET_INFO:
tmprc = SMB2_set_info(xid, tcon, fid.persistent_fid,
......
......@@ -605,7 +605,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = {
{STATUS_MAPPED_FILE_SIZE_ZERO, -EIO, "STATUS_MAPPED_FILE_SIZE_ZERO"},
{STATUS_TOO_MANY_OPENED_FILES, -EMFILE, "STATUS_TOO_MANY_OPENED_FILES"},
{STATUS_CANCELLED, -EIO, "STATUS_CANCELLED"},
{STATUS_CANNOT_DELETE, -EIO, "STATUS_CANNOT_DELETE"},
{STATUS_CANNOT_DELETE, -EACCES, "STATUS_CANNOT_DELETE"},
{STATUS_INVALID_COMPUTER_NAME, -EIO, "STATUS_INVALID_COMPUTER_NAME"},
{STATUS_FILE_DELETED, -EIO, "STATUS_FILE_DELETED"},
{STATUS_SPECIAL_ACCOUNT, -EIO, "STATUS_SPECIAL_ACCOUNT"},
......
......@@ -437,7 +437,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
continue;
cifs_dbg(FYI, "found in the open list\n");
cifs_dbg(FYI, "lease key match, lease break 0x%d\n",
cifs_dbg(FYI, "lease key match, lease break 0x%x\n",
le32_to_cpu(rsp->NewLeaseState));
server->ops->set_oplock_level(cinode, lease_state, 0, NULL);
......@@ -467,7 +467,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
}
cifs_dbg(FYI, "found in the pending open list\n");
cifs_dbg(FYI, "lease key match, lease break 0x%d\n",
cifs_dbg(FYI, "lease key match, lease break 0x%x\n",
le32_to_cpu(rsp->NewLeaseState));
open->oplock = lease_state;
......@@ -546,7 +546,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
return false;
}
cifs_dbg(FYI, "oplock level 0x%d\n", rsp->OplockLevel);
cifs_dbg(FYI, "oplock level 0x%x\n", rsp->OplockLevel);
/* look up tcon based on tid & uid */
spin_lock(&cifs_tcp_ses_lock);
......
......@@ -19,6 +19,7 @@
#include <linux/pagemap.h>
#include <linux/vfs.h>
#include <linux/falloc.h>
#include "cifsglob.h"
#include "smb2pdu.h"
#include "smb2proto.h"
......@@ -112,6 +113,53 @@ smb2_get_credits(struct mid_q_entry *mid)
return le16_to_cpu(((struct smb2_hdr *)mid->resp_buf)->CreditRequest);
}
static int
smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
unsigned int *num, unsigned int *credits)
{
int rc = 0;
unsigned int scredits;
spin_lock(&server->req_lock);
while (1) {
if (server->credits <= 0) {
spin_unlock(&server->req_lock);
cifs_num_waiters_inc(server);
rc = wait_event_killable(server->request_q,
has_credits(server, &server->credits));
cifs_num_waiters_dec(server);
if (rc)
return rc;
spin_lock(&server->req_lock);
} else {
if (server->tcpStatus == CifsExiting) {
spin_unlock(&server->req_lock);
return -ENOENT;
}
scredits = server->credits;
/* can deadlock with reopen */
if (scredits == 1) {
*num = SMB2_MAX_BUFFER_SIZE;
*credits = 0;
break;
}
/* leave one credit for a possible reopen */
scredits--;
*num = min_t(unsigned int, size,
scredits * SMB2_MAX_BUFFER_SIZE);
*credits = DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE);
server->credits -= *credits;
server->in_flight++;
break;
}
}
spin_unlock(&server->req_lock);
return rc;
}
static __u64
smb2_get_next_mid(struct TCP_Server_Info *server)
{
......@@ -182,8 +230,9 @@ smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
/* start with specified wsize, or default */
wsize = volume_info->wsize ? volume_info->wsize : CIFS_DEFAULT_IOSIZE;
wsize = min_t(unsigned int, wsize, server->max_write);
/* set it to the maximum buffer size value we can send with 1 credit */
wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE);
if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE);
return wsize;
}
......@@ -197,8 +246,9 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
/* start with specified rsize, or default */
rsize = volume_info->rsize ? volume_info->rsize : CIFS_DEFAULT_IOSIZE;
rsize = min_t(unsigned int, rsize, server->max_read);
/* set it to the maximum buffer size value we can send with 1 credit */
rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE);
if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE);
return rsize;
}
......@@ -687,7 +737,7 @@ smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon,
{
__le64 eof = cpu_to_le64(size);
return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, cfile->pid, &eof);
cfile->fid.volatile_fid, cfile->pid, &eof, false);
}
static int
......@@ -1104,6 +1154,13 @@ smb3_parse_lease_buf(void *buf, unsigned int *epoch)
return le32_to_cpu(lc->lcontext.LeaseState);
}
static unsigned int
smb2_wp_retry_size(struct inode *inode)
{
return min_t(unsigned int, CIFS_SB(inode->i_sb)->wsize,
SMB2_MAX_BUFFER_SIZE);
}
struct smb_version_operations smb20_operations = {
.compare_fids = smb2_compare_fids,
.setup_request = smb2_setup_request,
......@@ -1113,6 +1170,7 @@ struct smb_version_operations smb20_operations = {
.set_credits = smb2_set_credits,
.get_credits_field = smb2_get_credits_field,
.get_credits = smb2_get_credits,
.wait_mtu_credits = cifs_wait_mtu_credits,
.get_next_mid = smb2_get_next_mid,
.read_data_offset = smb2_read_data_offset,
.read_data_length = smb2_read_data_length,
......@@ -1177,6 +1235,7 @@ struct smb_version_operations smb20_operations = {
.create_lease_buf = smb2_create_lease_buf,
.parse_lease_buf = smb2_parse_lease_buf,
.clone_range = smb2_clone_range,
.wp_retry_size = smb2_wp_retry_size,
};
struct smb_version_operations smb21_operations = {
......@@ -1188,6 +1247,7 @@ struct smb_version_operations smb21_operations = {
.set_credits = smb2_set_credits,
.get_credits_field = smb2_get_credits_field,
.get_credits = smb2_get_credits,
.wait_mtu_credits = smb2_wait_mtu_credits,
.get_next_mid = smb2_get_next_mid,
.read_data_offset = smb2_read_data_offset,
.read_data_length = smb2_read_data_length,
......@@ -1252,6 +1312,7 @@ struct smb_version_operations smb21_operations = {
.create_lease_buf = smb2_create_lease_buf,
.parse_lease_buf = smb2_parse_lease_buf,
.clone_range = smb2_clone_range,
.wp_retry_size = smb2_wp_retry_size,
};
struct smb_version_operations smb30_operations = {
......@@ -1263,6 +1324,7 @@ struct smb_version_operations smb30_operations = {
.set_credits = smb2_set_credits,
.get_credits_field = smb2_get_credits_field,
.get_credits = smb2_get_credits,
.wait_mtu_credits = smb2_wait_mtu_credits,
.get_next_mid = smb2_get_next_mid,
.read_data_offset = smb2_read_data_offset,
.read_data_length = smb2_read_data_length,
......@@ -1330,6 +1392,7 @@ struct smb_version_operations smb30_operations = {
.parse_lease_buf = smb3_parse_lease_buf,
.clone_range = smb2_clone_range,
.validate_negotiate = smb3_validate_negotiate,
.wp_retry_size = smb2_wp_retry_size,
};
struct smb_version_values smb20_values = {
......
......@@ -108,7 +108,6 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ ,
if (!tcon)
goto out;
/* BB FIXME when we do write > 64K add +1 for every 64K in req or rsp */
/* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */
/* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */
if ((tcon->ses) &&
......@@ -245,10 +244,6 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
if (rc)
goto out;
atomic_inc(&tconInfoReconnectCount);
/*
* BB FIXME add code to check if wsize needs update due to negotiated
* smb buffer size shrinking.
*/
out:
/*
* Check if handle based operation so we know whether we can continue
......@@ -309,16 +304,6 @@ small_smb2_init(__le16 smb2_command, struct cifs_tcon *tcon,
return rc;
}
static void
free_rsp_buf(int resp_buftype, void *rsp)
{
if (resp_buftype == CIFS_SMALL_BUFFER)
cifs_small_buf_release(rsp);
else if (resp_buftype == CIFS_LARGE_BUFFER)
cifs_buf_release(rsp);
}
/*
*
* SMB2 Worker functions follow:
......@@ -1738,12 +1723,18 @@ smb2_readv_callback(struct mid_q_entry *mid)
rc);
}
/* FIXME: should this be counted toward the initiating task? */
task_io_account_read(rdata->bytes);
cifs_stats_bytes_read(tcon, rdata->bytes);
task_io_account_read(rdata->got_bytes);
cifs_stats_bytes_read(tcon, rdata->got_bytes);
break;
case MID_REQUEST_SUBMITTED:
case MID_RETRY_NEEDED:
rdata->result = -EAGAIN;
if (server->sign && rdata->got_bytes)
/* reset bytes number since we can not check a sign */
rdata->got_bytes = 0;
/* FIXME: should this be counted toward the initiating task? */
task_io_account_read(rdata->got_bytes);
cifs_stats_bytes_read(tcon, rdata->got_bytes);
break;
default:
if (rdata->result != -ENODATA)
......@@ -1762,11 +1753,12 @@ smb2_readv_callback(struct mid_q_entry *mid)
int
smb2_async_readv(struct cifs_readdata *rdata)
{
int rc;
int rc, flags = 0;
struct smb2_hdr *buf;
struct cifs_io_parms io_parms;
struct smb_rqst rqst = { .rq_iov = &rdata->iov,
.rq_nvec = 1 };
struct TCP_Server_Info *server;
cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n",
__func__, rdata->offset, rdata->bytes);
......@@ -1777,18 +1769,41 @@ smb2_async_readv(struct cifs_readdata *rdata)
io_parms.persistent_fid = rdata->cfile->fid.persistent_fid;
io_parms.volatile_fid = rdata->cfile->fid.volatile_fid;
io_parms.pid = rdata->pid;
server = io_parms.tcon->ses->server;
rc = smb2_new_read_req(&rdata->iov, &io_parms, 0, 0);
if (rc)
if (rc) {
if (rc == -EAGAIN && rdata->credits) {
/* credits was reset by reconnect */
rdata->credits = 0;
/* reduce in_flight value since we won't send the req */
spin_lock(&server->req_lock);
server->in_flight--;
spin_unlock(&server->req_lock);
}
return rc;
}
buf = (struct smb2_hdr *)rdata->iov.iov_base;
/* 4 for rfc1002 length field */
rdata->iov.iov_len = get_rfc1002_length(rdata->iov.iov_base) + 4;
if (rdata->credits) {
buf->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
SMB2_MAX_BUFFER_SIZE));
spin_lock(&server->req_lock);
server->credits += rdata->credits -
le16_to_cpu(buf->CreditCharge);
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
flags = CIFS_HAS_CREDITS;
}
kref_get(&rdata->refcount);
rc = cifs_call_async(io_parms.tcon->ses->server, &rqst,
cifs_readv_receive, smb2_readv_callback,
rdata, 0);
rdata, flags);
if (rc) {
kref_put(&rdata->refcount, cifs_readdata_release);
cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE);
......@@ -1906,15 +1921,25 @@ int
smb2_async_writev(struct cifs_writedata *wdata,
void (*release)(struct kref *kref))
{
int rc = -EACCES;
int rc = -EACCES, flags = 0;
struct smb2_write_req *req = NULL;
struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
struct TCP_Server_Info *server = tcon->ses->server;
struct kvec iov;
struct smb_rqst rqst;
rc = small_smb2_init(SMB2_WRITE, tcon, (void **) &req);
if (rc)
if (rc) {
if (rc == -EAGAIN && wdata->credits) {
/* credits was reset by reconnect */
wdata->credits = 0;
/* reduce in_flight value since we won't send the req */
spin_lock(&server->req_lock);
server->in_flight--;
spin_unlock(&server->req_lock);
}
goto async_writev_out;
}
req->hdr.ProcessId = cpu_to_le32(wdata->cfile->pid);
......@@ -1947,9 +1972,20 @@ smb2_async_writev(struct cifs_writedata *wdata,
inc_rfc1001_len(&req->hdr, wdata->bytes - 1 /* Buffer */);
if (wdata->credits) {
req->hdr.CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes,
SMB2_MAX_BUFFER_SIZE));
spin_lock(&server->req_lock);
server->credits += wdata->credits -
le16_to_cpu(req->hdr.CreditCharge);
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
flags = CIFS_HAS_CREDITS;
}
kref_get(&wdata->refcount);
rc = cifs_call_async(tcon->ses->server, &rqst, NULL,
smb2_writev_callback, wdata, 0);
rc = cifs_call_async(server, &rqst, NULL, smb2_writev_callback, wdata,
flags);
if (rc) {
kref_put(&wdata->refcount, release);
......@@ -2325,7 +2361,7 @@ SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
int
SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
u64 volatile_fid, u32 pid, __le64 *eof)
u64 volatile_fid, u32 pid, __le64 *eof, bool is_falloc)
{
struct smb2_file_eof_info info;
void *data;
......@@ -2336,8 +2372,12 @@ SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
data = &info;
size = sizeof(struct smb2_file_eof_info);
return send_set_info(xid, tcon, persistent_fid, volatile_fid, pid,
FILE_END_OF_FILE_INFORMATION, 1, &data, &size);
if (is_falloc)
return send_set_info(xid, tcon, persistent_fid, volatile_fid,
pid, FILE_ALLOCATION_INFORMATION, 1, &data, &size);
else
return send_set_info(xid, tcon, persistent_fid, volatile_fid,
pid, FILE_END_OF_FILE_INFORMATION, 1, &data, &size);
}
int
......
......@@ -139,7 +139,7 @@ extern int SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
__le16 *target_file);
extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u32 pid,
__le64 *eof);
__le64 *eof, bool is_fallocate);
extern int SMB2_set_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid,
FILE_BASIC_INFO *buf);
......
......@@ -466,7 +466,12 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
static inline void
smb2_seq_num_into_buf(struct TCP_Server_Info *server, struct smb2_hdr *hdr)
{
unsigned int i, num = le16_to_cpu(hdr->CreditCharge);
hdr->MessageId = get_next_mid64(server);
/* skip message numbers according to CreditCharge field */
for (i = 1; i < num; i++)
get_next_mid(server);
}
static struct mid_q_entry *
......
......@@ -448,6 +448,15 @@ wait_for_free_request(struct TCP_Server_Info *server, const int timeout,
return wait_for_free_credits(server, timeout, val);
}
int
cifs_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
unsigned int *num, unsigned int *credits)
{
*num = size;
*credits = 0;
return 0;
}
static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
struct mid_q_entry **ppmidQ)
{
......@@ -531,20 +540,23 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
{
int rc, timeout, optype;
struct mid_q_entry *mid;
unsigned int credits = 0;
timeout = flags & CIFS_TIMEOUT_MASK;
optype = flags & CIFS_OP_MASK;
rc = wait_for_free_request(server, timeout, optype);
if (rc)
return rc;
if ((flags & CIFS_HAS_CREDITS) == 0) {
rc = wait_for_free_request(server, timeout, optype);
if (rc)
return rc;
credits = 1;
}
mutex_lock(&server->srv_mutex);
mid = server->ops->setup_async_request(server, rqst);
if (IS_ERR(mid)) {
mutex_unlock(&server->srv_mutex);
add_credits(server, 1, optype);
wake_up(&server->request_q);
add_credits_and_wake_if(server, credits, optype);
return PTR_ERR(mid);
}
......@@ -572,8 +584,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
return 0;
cifs_delete_mid(mid);
add_credits(server, 1, optype);
wake_up(&server->request_q);
add_credits_and_wake_if(server, credits, optype);
return rc;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment