Commit 21b26d26 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag '5.5-rc-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull cifs updates from Steve French:
 "Various smb3 fixes (including 12 for stable) and also features
  (addition of multichannel support)"

* tag '5.5-rc-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6: (41 commits)
  CIFS: fix a white space issue in cifs_get_inode_info()
  cifs: update internal module version number
  cifs: Always update signing key of first channel
  cifs: Fix retrieval of DFS referrals in cifs_mount()
  cifs: Fix potential softlockups while refreshing DFS cache
  cifs: Fix lookup of root ses in DFS referral cache
  cifs: Fix use-after-free bug in cifs_reconnect()
  cifs: dump channel info in DebugData
  smb3: dump in_send and num_waiters stats counters by default
  cifs: try harder to open new channels
  CIFS: Properly process SMB3 lease breaks
  cifs: move cifsFileInfo_put logic into a work-queue
  cifs: try opening channels after mounting
  CIFS: refactor cifs_get_inode_info()
  cifs: switch servers depending on binding state
  cifs: add server param
  cifs: add multichannel mount options and data structs
  cifs: sort interface list by speed
  CIFS: Fix SMB2 oplock break processing
  cifs: don't use 'pre:' for MODULE_SOFTDEP
  ...
parents 8f45533e 68464b88
...@@ -121,6 +121,27 @@ static void cifs_debug_tcon(struct seq_file *m, struct cifs_tcon *tcon) ...@@ -121,6 +121,27 @@ static void cifs_debug_tcon(struct seq_file *m, struct cifs_tcon *tcon)
seq_putc(m, '\n'); seq_putc(m, '\n');
} }
static void
cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan)
{
struct TCP_Server_Info *server = chan->server;
seq_printf(m, "\t\tChannel %d Number of credits: %d Dialect 0x%x "
"TCP status: %d Instance: %d Local Users To Server: %d "
"SecMode: 0x%x Req On Wire: %d In Send: %d "
"In MaxReq Wait: %d\n",
i+1,
server->credits,
server->dialect,
server->tcpStatus,
server->reconnect_instance,
server->srv_count,
server->sec_mode,
in_flight(server),
atomic_read(&server->in_send),
atomic_read(&server->num_waiters));
}
static void static void
cifs_dump_iface(struct seq_file *m, struct cifs_server_iface *iface) cifs_dump_iface(struct seq_file *m, struct cifs_server_iface *iface)
{ {
...@@ -256,6 +277,11 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -256,6 +277,11 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
if (!server->rdma) if (!server->rdma)
goto skip_rdma; goto skip_rdma;
if (!server->smbd_conn) {
seq_printf(m, "\nSMBDirect transport not available");
goto skip_rdma;
}
seq_printf(m, "\nSMBDirect (in hex) protocol version: %x " seq_printf(m, "\nSMBDirect (in hex) protocol version: %x "
"transport status: %x", "transport status: %x",
server->smbd_conn->protocol, server->smbd_conn->protocol,
...@@ -360,11 +386,10 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -360,11 +386,10 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
server->srv_count, server->srv_count,
server->sec_mode, in_flight(server)); server->sec_mode, in_flight(server));
#ifdef CONFIG_CIFS_STATS2
seq_printf(m, " In Send: %d In MaxReq Wait: %d", seq_printf(m, " In Send: %d In MaxReq Wait: %d",
atomic_read(&server->in_send), atomic_read(&server->in_send),
atomic_read(&server->num_waiters)); atomic_read(&server->num_waiters));
#endif
/* dump session id helpful for use with network trace */ /* dump session id helpful for use with network trace */
seq_printf(m, " SessionId: 0x%llx", ses->Suid); seq_printf(m, " SessionId: 0x%llx", ses->Suid);
if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
...@@ -372,6 +397,13 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -372,6 +397,13 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
if (ses->sign) if (ses->sign)
seq_puts(m, " signed"); seq_puts(m, " signed");
if (ses->chan_count > 1) {
seq_printf(m, "\n\n\tExtra Channels: %zu\n",
ses->chan_count-1);
for (j = 1; j < ses->chan_count; j++)
cifs_dump_channel(m, j, &ses->chans[j]);
}
seq_puts(m, "\n\tShares:"); seq_puts(m, "\n\tShares:");
j = 0; j = 0;
...@@ -410,8 +442,13 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -410,8 +442,13 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
seq_printf(m, "\n\tServer interfaces: %zu\n", seq_printf(m, "\n\tServer interfaces: %zu\n",
ses->iface_count); ses->iface_count);
for (j = 0; j < ses->iface_count; j++) { for (j = 0; j < ses->iface_count; j++) {
struct cifs_server_iface *iface;
iface = &ses->iface_list[j];
seq_printf(m, "\t%d)", j); seq_printf(m, "\t%d)", j);
cifs_dump_iface(m, &ses->iface_list[j]); cifs_dump_iface(m, iface);
if (is_ses_using_iface(ses, iface))
seq_puts(m, "\t\t[CONNECTED]\n");
} }
spin_unlock(&ses->iface_lock); spin_unlock(&ses->iface_lock);
} }
......
...@@ -98,7 +98,7 @@ struct key_type cifs_spnego_key_type = { ...@@ -98,7 +98,7 @@ struct key_type cifs_spnego_key_type = {
struct key * struct key *
cifs_get_spnego_key(struct cifs_ses *sesInfo) cifs_get_spnego_key(struct cifs_ses *sesInfo)
{ {
struct TCP_Server_Info *server = sesInfo->server; struct TCP_Server_Info *server = cifs_ses_server(sesInfo);
struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr; struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr;
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr; struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr;
char *description, *dp; char *description, *dp;
......
...@@ -39,8 +39,6 @@ static const struct cifs_sid sid_everyone = { ...@@ -39,8 +39,6 @@ static const struct cifs_sid sid_everyone = {
/* security id for Authenticated Users system group */ /* security id for Authenticated Users system group */
static const struct cifs_sid sid_authusers = { static const struct cifs_sid sid_authusers = {
1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11)} }; 1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11)} };
/* group users */
static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} };
/* S-1-22-1 Unmapped Unix users */ /* S-1-22-1 Unmapped Unix users */
static const struct cifs_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22}, static const struct cifs_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22},
......
...@@ -119,6 +119,7 @@ extern mempool_t *cifs_mid_poolp; ...@@ -119,6 +119,7 @@ extern mempool_t *cifs_mid_poolp;
struct workqueue_struct *cifsiod_wq; struct workqueue_struct *cifsiod_wq;
struct workqueue_struct *decrypt_wq; struct workqueue_struct *decrypt_wq;
struct workqueue_struct *fileinfo_put_wq;
struct workqueue_struct *cifsoplockd_wq; struct workqueue_struct *cifsoplockd_wq;
__u32 cifs_lock_secret; __u32 cifs_lock_secret;
...@@ -613,6 +614,10 @@ cifs_show_options(struct seq_file *s, struct dentry *root) ...@@ -613,6 +614,10 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
/* convert actimeo and display it in seconds */ /* convert actimeo and display it in seconds */
seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ); seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ);
if (tcon->ses->chan_max > 1)
seq_printf(s, ",multichannel,max_channel=%zu",
tcon->ses->chan_max);
return 0; return 0;
} }
...@@ -1219,6 +1224,7 @@ const struct file_operations cifs_file_ops = { ...@@ -1219,6 +1224,7 @@ const struct file_operations cifs_file_ops = {
.open = cifs_open, .open = cifs_open,
.release = cifs_close, .release = cifs_close,
.lock = cifs_lock, .lock = cifs_lock,
.flock = cifs_flock,
.fsync = cifs_fsync, .fsync = cifs_fsync,
.flush = cifs_flush, .flush = cifs_flush,
.mmap = cifs_file_mmap, .mmap = cifs_file_mmap,
...@@ -1238,6 +1244,7 @@ const struct file_operations cifs_file_strict_ops = { ...@@ -1238,6 +1244,7 @@ const struct file_operations cifs_file_strict_ops = {
.open = cifs_open, .open = cifs_open,
.release = cifs_close, .release = cifs_close,
.lock = cifs_lock, .lock = cifs_lock,
.flock = cifs_flock,
.fsync = cifs_strict_fsync, .fsync = cifs_strict_fsync,
.flush = cifs_flush, .flush = cifs_flush,
.mmap = cifs_file_strict_mmap, .mmap = cifs_file_strict_mmap,
...@@ -1257,6 +1264,7 @@ const struct file_operations cifs_file_direct_ops = { ...@@ -1257,6 +1264,7 @@ const struct file_operations cifs_file_direct_ops = {
.open = cifs_open, .open = cifs_open,
.release = cifs_close, .release = cifs_close,
.lock = cifs_lock, .lock = cifs_lock,
.flock = cifs_flock,
.fsync = cifs_fsync, .fsync = cifs_fsync,
.flush = cifs_flush, .flush = cifs_flush,
.mmap = cifs_file_mmap, .mmap = cifs_file_mmap,
...@@ -1554,11 +1562,18 @@ init_cifs(void) ...@@ -1554,11 +1562,18 @@ init_cifs(void)
goto out_destroy_cifsiod_wq; goto out_destroy_cifsiod_wq;
} }
fileinfo_put_wq = alloc_workqueue("cifsfileinfoput",
WQ_UNBOUND|WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
if (!fileinfo_put_wq) {
rc = -ENOMEM;
goto out_destroy_decrypt_wq;
}
cifsoplockd_wq = alloc_workqueue("cifsoplockd", cifsoplockd_wq = alloc_workqueue("cifsoplockd",
WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
if (!cifsoplockd_wq) { if (!cifsoplockd_wq) {
rc = -ENOMEM; rc = -ENOMEM;
goto out_destroy_decrypt_wq; goto out_destroy_fileinfo_put_wq;
} }
rc = cifs_fscache_register(); rc = cifs_fscache_register();
...@@ -1624,6 +1639,8 @@ init_cifs(void) ...@@ -1624,6 +1639,8 @@ init_cifs(void)
cifs_fscache_unregister(); cifs_fscache_unregister();
out_destroy_cifsoplockd_wq: out_destroy_cifsoplockd_wq:
destroy_workqueue(cifsoplockd_wq); destroy_workqueue(cifsoplockd_wq);
out_destroy_fileinfo_put_wq:
destroy_workqueue(fileinfo_put_wq);
out_destroy_decrypt_wq: out_destroy_decrypt_wq:
destroy_workqueue(decrypt_wq); destroy_workqueue(decrypt_wq);
out_destroy_cifsiod_wq: out_destroy_cifsiod_wq:
...@@ -1653,6 +1670,7 @@ exit_cifs(void) ...@@ -1653,6 +1670,7 @@ exit_cifs(void)
cifs_fscache_unregister(); cifs_fscache_unregister();
destroy_workqueue(cifsoplockd_wq); destroy_workqueue(cifsoplockd_wq);
destroy_workqueue(decrypt_wq); destroy_workqueue(decrypt_wq);
destroy_workqueue(fileinfo_put_wq);
destroy_workqueue(cifsiod_wq); destroy_workqueue(cifsiod_wq);
cifs_proc_clean(); cifs_proc_clean();
} }
...@@ -1663,17 +1681,17 @@ MODULE_DESCRIPTION ...@@ -1663,17 +1681,17 @@ MODULE_DESCRIPTION
("VFS to access SMB3 servers e.g. Samba, Macs, Azure and Windows (and " ("VFS to access SMB3 servers e.g. Samba, Macs, Azure and Windows (and "
"also older servers complying with the SNIA CIFS Specification)"); "also older servers complying with the SNIA CIFS Specification)");
MODULE_VERSION(CIFS_VERSION); MODULE_VERSION(CIFS_VERSION);
MODULE_SOFTDEP("pre: ecb"); MODULE_SOFTDEP("ecb");
MODULE_SOFTDEP("pre: hmac"); MODULE_SOFTDEP("hmac");
MODULE_SOFTDEP("pre: md4"); MODULE_SOFTDEP("md4");
MODULE_SOFTDEP("pre: md5"); MODULE_SOFTDEP("md5");
MODULE_SOFTDEP("pre: nls"); MODULE_SOFTDEP("nls");
MODULE_SOFTDEP("pre: aes"); MODULE_SOFTDEP("aes");
MODULE_SOFTDEP("pre: cmac"); MODULE_SOFTDEP("cmac");
MODULE_SOFTDEP("pre: sha256"); MODULE_SOFTDEP("sha256");
MODULE_SOFTDEP("pre: sha512"); MODULE_SOFTDEP("sha512");
MODULE_SOFTDEP("pre: aead2"); MODULE_SOFTDEP("aead2");
MODULE_SOFTDEP("pre: ccm"); MODULE_SOFTDEP("ccm");
MODULE_SOFTDEP("pre: gcm"); MODULE_SOFTDEP("gcm");
module_init(init_cifs) module_init(init_cifs)
module_exit(exit_cifs) module_exit(exit_cifs)
...@@ -108,6 +108,7 @@ extern ssize_t cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to); ...@@ -108,6 +108,7 @@ extern ssize_t cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to);
extern ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from); extern ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from);
extern ssize_t cifs_direct_writev(struct kiocb *iocb, struct iov_iter *from); extern ssize_t cifs_direct_writev(struct kiocb *iocb, struct iov_iter *from);
extern ssize_t cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from); extern ssize_t cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from);
extern int cifs_flock(struct file *pfile, int cmd, struct file_lock *plock);
extern int cifs_lock(struct file *, int, struct file_lock *); extern int cifs_lock(struct file *, int, struct file_lock *);
extern int cifs_fsync(struct file *, loff_t, loff_t, int); extern int cifs_fsync(struct file *, loff_t, loff_t, int);
extern int cifs_strict_fsync(struct file *, loff_t, loff_t, int); extern int cifs_strict_fsync(struct file *, loff_t, loff_t, int);
...@@ -152,5 +153,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); ...@@ -152,5 +153,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
extern const struct export_operations cifs_export_ops; extern const struct export_operations cifs_export_ops;
#endif /* CONFIG_CIFS_NFSD_EXPORT */ #endif /* CONFIG_CIFS_NFSD_EXPORT */
#define CIFS_VERSION "2.23" #define CIFS_VERSION "2.24"
#endif /* _CIFSFS_H */ #endif /* _CIFSFS_H */
...@@ -230,6 +230,7 @@ struct smb_version_operations { ...@@ -230,6 +230,7 @@ struct smb_version_operations {
bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *); bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *);
/* setup request: allocate mid, sign message */ /* setup request: allocate mid, sign message */
struct mid_q_entry *(*setup_request)(struct cifs_ses *, struct mid_q_entry *(*setup_request)(struct cifs_ses *,
struct TCP_Server_Info *,
struct smb_rqst *); struct smb_rqst *);
/* setup async request: allocate mid, sign message */ /* setup async request: allocate mid, sign message */
struct mid_q_entry *(*setup_async_request)(struct TCP_Server_Info *, struct mid_q_entry *(*setup_async_request)(struct TCP_Server_Info *,
...@@ -268,8 +269,9 @@ struct smb_version_operations { ...@@ -268,8 +269,9 @@ struct smb_version_operations {
int (*check_message)(char *, unsigned int, struct TCP_Server_Info *); int (*check_message)(char *, unsigned int, struct TCP_Server_Info *);
bool (*is_oplock_break)(char *, struct TCP_Server_Info *); bool (*is_oplock_break)(char *, struct TCP_Server_Info *);
int (*handle_cancelled_mid)(char *, struct TCP_Server_Info *); int (*handle_cancelled_mid)(char *, struct TCP_Server_Info *);
void (*downgrade_oplock)(struct TCP_Server_Info *, void (*downgrade_oplock)(struct TCP_Server_Info *server,
struct cifsInodeInfo *, bool); struct cifsInodeInfo *cinode, __u32 oplock,
unsigned int epoch, bool *purge_cache);
/* process transaction2 response */ /* process transaction2 response */
bool (*check_trans2)(struct mid_q_entry *, struct TCP_Server_Info *, bool (*check_trans2)(struct mid_q_entry *, struct TCP_Server_Info *,
char *, int); char *, int);
...@@ -591,6 +593,10 @@ struct smb_vol { ...@@ -591,6 +593,10 @@ struct smb_vol {
bool resilient:1; /* noresilient not required since not fored for CA */ bool resilient:1; /* noresilient not required since not fored for CA */
bool domainauto:1; bool domainauto:1;
bool rdma:1; bool rdma:1;
bool multichannel:1;
bool use_client_guid:1;
/* reuse existing guid for multichannel */
u8 client_guid[SMB2_CLIENT_GUID_SIZE];
unsigned int bsize; unsigned int bsize;
unsigned int rsize; unsigned int rsize;
unsigned int wsize; unsigned int wsize;
...@@ -607,6 +613,7 @@ struct smb_vol { ...@@ -607,6 +613,7 @@ struct smb_vol {
__u64 snapshot_time; /* needed for timewarp tokens */ __u64 snapshot_time; /* needed for timewarp tokens */
__u32 handle_timeout; /* persistent and durable handle timeout in ms */ __u32 handle_timeout; /* persistent and durable handle timeout in ms */
unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */ unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
unsigned int max_channels;
__u16 compression; /* compression algorithm 0xFFFF default 0=disabled */ __u16 compression; /* compression algorithm 0xFFFF default 0=disabled */
bool rootfs:1; /* if it's a SMB root file system */ bool rootfs:1; /* if it's a SMB root file system */
}; };
...@@ -736,12 +743,12 @@ struct TCP_Server_Info { ...@@ -736,12 +743,12 @@ struct TCP_Server_Info {
/* Total size of this PDU. Only valid from cifs_demultiplex_thread */ /* Total size of this PDU. Only valid from cifs_demultiplex_thread */
unsigned int pdu_size; unsigned int pdu_size;
unsigned int total_read; /* total amount of data read in this pass */ unsigned int total_read; /* total amount of data read in this pass */
atomic_t in_send; /* requests trying to send */
atomic_t num_waiters; /* blocked waiting to get in sendrecv */
#ifdef CONFIG_CIFS_FSCACHE #ifdef CONFIG_CIFS_FSCACHE
struct fscache_cookie *fscache; /* client index cache cookie */ struct fscache_cookie *fscache; /* client index cache cookie */
#endif #endif
#ifdef CONFIG_CIFS_STATS2 #ifdef CONFIG_CIFS_STATS2
atomic_t in_send; /* requests trying to send */
atomic_t num_waiters; /* blocked waiting to get in sendrecv */
atomic_t num_cmds[NUMBER_OF_SMB2_COMMANDS]; /* total requests by cmd */ atomic_t num_cmds[NUMBER_OF_SMB2_COMMANDS]; /* total requests by cmd */
atomic_t smb2slowcmd[NUMBER_OF_SMB2_COMMANDS]; /* count resps > 1 sec */ atomic_t smb2slowcmd[NUMBER_OF_SMB2_COMMANDS]; /* count resps > 1 sec */
__u64 time_per_cmd[NUMBER_OF_SMB2_COMMANDS]; /* total time per cmd */ __u64 time_per_cmd[NUMBER_OF_SMB2_COMMANDS]; /* total time per cmd */
...@@ -953,6 +960,11 @@ struct cifs_server_iface { ...@@ -953,6 +960,11 @@ struct cifs_server_iface {
struct sockaddr_storage sockaddr; struct sockaddr_storage sockaddr;
}; };
struct cifs_chan {
struct TCP_Server_Info *server;
__u8 signkey[SMB3_SIGN_KEY_SIZE];
};
/* /*
* Session structure. One of these for each uid session with a particular host * Session structure. One of these for each uid session with a particular host
*/ */
...@@ -983,12 +995,15 @@ struct cifs_ses { ...@@ -983,12 +995,15 @@ struct cifs_ses {
bool sign; /* is signing required? */ bool sign; /* is signing required? */
bool need_reconnect:1; /* connection reset, uid now invalid */ bool need_reconnect:1; /* connection reset, uid now invalid */
bool domainAuto:1; bool domainAuto:1;
bool binding:1; /* are we binding the session? */
__u16 session_flags; __u16 session_flags;
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
__u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE]; __u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE];
__u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE]; __u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
__u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE]; __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
__u8 binding_preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
/* /*
* Network interfaces available on the server this session is * Network interfaces available on the server this session is
* connected to. * connected to.
...@@ -1002,8 +1017,37 @@ struct cifs_ses { ...@@ -1002,8 +1017,37 @@ struct cifs_ses {
struct cifs_server_iface *iface_list; struct cifs_server_iface *iface_list;
size_t iface_count; size_t iface_count;
unsigned long iface_last_update; /* jiffies */ unsigned long iface_last_update; /* jiffies */
#define CIFS_MAX_CHANNELS 16
struct cifs_chan chans[CIFS_MAX_CHANNELS];
size_t chan_count;
size_t chan_max;
atomic_t chan_seq; /* round robin state */
}; };
/*
* When binding a new channel, we need to access the channel which isn't fully
* established yet (one past the established count)
*/
static inline
struct cifs_chan *cifs_ses_binding_channel(struct cifs_ses *ses)
{
if (ses->binding)
return &ses->chans[ses->chan_count];
else
return NULL;
}
static inline
struct TCP_Server_Info *cifs_ses_server(struct cifs_ses *ses)
{
if (ses->binding)
return ses->chans[ses->chan_count].server;
else
return ses->server;
}
static inline bool static inline bool
cap_unix(struct cifs_ses *ses) cap_unix(struct cifs_ses *ses)
{ {
...@@ -1260,11 +1304,14 @@ struct cifsFileInfo { ...@@ -1260,11 +1304,14 @@ struct cifsFileInfo {
unsigned int f_flags; unsigned int f_flags;
bool invalidHandle:1; /* file closed via session abend */ bool invalidHandle:1; /* file closed via session abend */
bool oplock_break_cancelled:1; bool oplock_break_cancelled:1;
unsigned int oplock_epoch; /* epoch from the lease break */
__u32 oplock_level; /* oplock/lease level from the lease break */
int count; int count;
spinlock_t file_info_lock; /* protects four flag/count fields above */ spinlock_t file_info_lock; /* protects four flag/count fields above */
struct mutex fh_mutex; /* prevents reopen race after dead ses*/ struct mutex fh_mutex; /* prevents reopen race after dead ses*/
struct cifs_search_info srch_inf; struct cifs_search_info srch_inf;
struct work_struct oplock_break; /* work for oplock breaks */ struct work_struct oplock_break; /* work for oplock breaks */
struct work_struct put; /* work for the final part of _put */
}; };
struct cifs_io_parms { struct cifs_io_parms {
...@@ -1370,7 +1417,8 @@ cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file) ...@@ -1370,7 +1417,8 @@ cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file)
} }
struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file); struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file);
void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_hdlr); void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_hdlr,
bool offload);
void cifsFileInfo_put(struct cifsFileInfo *cifs_file); void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
#define CIFS_CACHE_READ_FLG 1 #define CIFS_CACHE_READ_FLG 1
...@@ -1405,7 +1453,7 @@ struct cifsInodeInfo { ...@@ -1405,7 +1453,7 @@ struct cifsInodeInfo {
unsigned int epoch; /* used to track lease state changes */ unsigned int epoch; /* used to track lease state changes */
#define CIFS_INODE_PENDING_OPLOCK_BREAK (0) /* oplock break in progress */ #define CIFS_INODE_PENDING_OPLOCK_BREAK (0) /* oplock break in progress */
#define CIFS_INODE_PENDING_WRITERS (1) /* Writes in progress */ #define CIFS_INODE_PENDING_WRITERS (1) /* Writes in progress */
#define CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2 (2) /* Downgrade oplock to L2 */ #define CIFS_INODE_FLAG_UNUSED (2) /* Unused flag */
#define CIFS_INO_DELETE_PENDING (3) /* delete pending on server */ #define CIFS_INO_DELETE_PENDING (3) /* delete pending on server */
#define CIFS_INO_INVALID_MAPPING (4) /* pagecache is invalid */ #define CIFS_INO_INVALID_MAPPING (4) /* pagecache is invalid */
#define CIFS_INO_LOCK (5) /* lock bit for synchronization */ #define CIFS_INO_LOCK (5) /* lock bit for synchronization */
...@@ -1524,6 +1572,7 @@ struct mid_q_entry { ...@@ -1524,6 +1572,7 @@ struct mid_q_entry {
struct TCP_Server_Info *server; /* server corresponding to this mid */ struct TCP_Server_Info *server; /* server corresponding to this mid */
__u64 mid; /* multiplex id */ __u64 mid; /* multiplex id */
__u16 credits; /* number of credits consumed by this mid */ __u16 credits; /* number of credits consumed by this mid */
__u16 credits_received; /* number of credits from the response */
__u32 pid; /* process id */ __u32 pid; /* process id */
__u32 sequence_number; /* for CIFS signing */ __u32 sequence_number; /* for CIFS signing */
unsigned long when_alloc; /* when mid was created */ unsigned long when_alloc; /* when mid was created */
...@@ -1551,12 +1600,12 @@ struct close_cancelled_open { ...@@ -1551,12 +1600,12 @@ struct close_cancelled_open {
struct cifs_fid fid; struct cifs_fid fid;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct work_struct work; struct work_struct work;
__u64 mid;
__u16 cmd;
}; };
/* Make code in transport.c a little cleaner by moving /* Make code in transport.c a little cleaner by moving
update of optional stats into function below */ update of optional stats into function below */
#ifdef CONFIG_CIFS_STATS2
static inline void cifs_in_send_inc(struct TCP_Server_Info *server) static inline void cifs_in_send_inc(struct TCP_Server_Info *server)
{ {
atomic_inc(&server->in_send); atomic_inc(&server->in_send);
...@@ -1577,26 +1626,12 @@ static inline void cifs_num_waiters_dec(struct TCP_Server_Info *server) ...@@ -1577,26 +1626,12 @@ static inline void cifs_num_waiters_dec(struct TCP_Server_Info *server)
atomic_dec(&server->num_waiters); atomic_dec(&server->num_waiters);
} }
#ifdef CONFIG_CIFS_STATS2
static inline void cifs_save_when_sent(struct mid_q_entry *mid) static inline void cifs_save_when_sent(struct mid_q_entry *mid)
{ {
mid->when_sent = jiffies; mid->when_sent = jiffies;
} }
#else #else
static inline void cifs_in_send_inc(struct TCP_Server_Info *server)
{
}
static inline void cifs_in_send_dec(struct TCP_Server_Info *server)
{
}
static inline void cifs_num_waiters_inc(struct TCP_Server_Info *server)
{
}
static inline void cifs_num_waiters_dec(struct TCP_Server_Info *server)
{
}
static inline void cifs_save_when_sent(struct mid_q_entry *mid) static inline void cifs_save_when_sent(struct mid_q_entry *mid)
{ {
} }
...@@ -1907,6 +1942,7 @@ void cifs_queue_oplock_break(struct cifsFileInfo *cfile); ...@@ -1907,6 +1942,7 @@ void cifs_queue_oplock_break(struct cifsFileInfo *cfile);
extern const struct slow_work_ops cifs_oplock_break_ops; extern const struct slow_work_ops cifs_oplock_break_ops;
extern struct workqueue_struct *cifsiod_wq; extern struct workqueue_struct *cifsiod_wq;
extern struct workqueue_struct *decrypt_wq; extern struct workqueue_struct *decrypt_wq;
extern struct workqueue_struct *fileinfo_put_wq;
extern struct workqueue_struct *cifsoplockd_wq; extern struct workqueue_struct *cifsoplockd_wq;
extern __u32 cifs_lock_secret; extern __u32 cifs_lock_secret;
...@@ -1937,4 +1973,10 @@ extern struct smb_version_values smb302_values; ...@@ -1937,4 +1973,10 @@ extern struct smb_version_values smb302_values;
#define ALT_SMB311_VERSION_STRING "3.11" #define ALT_SMB311_VERSION_STRING "3.11"
extern struct smb_version_operations smb311_operations; extern struct smb_version_operations smb311_operations;
extern struct smb_version_values smb311_values; extern struct smb_version_values smb311_values;
static inline bool is_smb1_server(struct TCP_Server_Info *server)
{
return strcmp(server->vals->version_string, SMB1_VERSION_STRING) == 0;
}
#endif /* _CIFS_GLOB_H */ #endif /* _CIFS_GLOB_H */
...@@ -109,6 +109,7 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *, ...@@ -109,6 +109,7 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *,
extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
char *in_buf, int flags); char *in_buf, int flags);
extern struct mid_q_entry *cifs_setup_request(struct cifs_ses *, extern struct mid_q_entry *cifs_setup_request(struct cifs_ses *,
struct TCP_Server_Info *,
struct smb_rqst *); struct smb_rqst *);
extern struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *, extern struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *,
struct smb_rqst *); struct smb_rqst *);
...@@ -242,6 +243,7 @@ extern void cifs_add_pending_open_locked(struct cifs_fid *fid, ...@@ -242,6 +243,7 @@ extern void cifs_add_pending_open_locked(struct cifs_fid *fid,
struct tcon_link *tlink, struct tcon_link *tlink,
struct cifs_pending_open *open); struct cifs_pending_open *open);
extern void cifs_del_pending_open(struct cifs_pending_open *open); extern void cifs_del_pending_open(struct cifs_pending_open *open);
extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb_vol *vol);
extern void cifs_put_tcp_session(struct TCP_Server_Info *server, extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
int from_reconnect); int from_reconnect);
extern void cifs_put_tcon(struct cifs_tcon *tcon); extern void cifs_put_tcon(struct cifs_tcon *tcon);
...@@ -584,6 +586,12 @@ void cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc); ...@@ -584,6 +586,12 @@ void cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc);
extern void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page, extern void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
unsigned int *len, unsigned int *offset); unsigned int *len, unsigned int *offset);
int cifs_try_adding_channels(struct cifs_ses *ses);
int cifs_ses_add_channel(struct cifs_ses *ses,
struct cifs_server_iface *iface);
bool is_server_using_iface(struct TCP_Server_Info *server,
struct cifs_server_iface *iface);
bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface);
void extract_unc_hostname(const char *unc, const char **h, size_t *len); void extract_unc_hostname(const char *unc, const char **h, size_t *len);
int copy_path_name(char *dst, const char *src); int copy_path_name(char *dst, const char *src);
......
This diff is collapsed.
...@@ -1317,7 +1317,6 @@ static struct cifs_ses *find_root_ses(struct dfs_cache_vol_info *vi, ...@@ -1317,7 +1317,6 @@ static struct cifs_ses *find_root_ses(struct dfs_cache_vol_info *vi,
int rc; int rc;
struct dfs_info3_param ref = {0}; struct dfs_info3_param ref = {0};
char *mdata = NULL, *devname = NULL; char *mdata = NULL, *devname = NULL;
bool is_smb3 = tcon->ses->server->vals->header_preamble_size == 0;
struct TCP_Server_Info *server; struct TCP_Server_Info *server;
struct cifs_ses *ses; struct cifs_ses *ses;
struct smb_vol vol; struct smb_vol vol;
...@@ -1344,7 +1343,7 @@ static struct cifs_ses *find_root_ses(struct dfs_cache_vol_info *vi, ...@@ -1344,7 +1343,7 @@ static struct cifs_ses *find_root_ses(struct dfs_cache_vol_info *vi,
goto out; goto out;
} }
rc = cifs_setup_volume_info(&vol, mdata, devname, is_smb3); rc = cifs_setup_volume_info(&vol, mdata, devname, false);
kfree(devname); kfree(devname);
if (rc) { if (rc) {
......
...@@ -244,10 +244,8 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, ...@@ -244,10 +244,8 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
*oplock = REQ_OPLOCK; *oplock = REQ_OPLOCK;
full_path = build_path_from_dentry(direntry); full_path = build_path_from_dentry(direntry);
if (full_path == NULL) { if (!full_path)
rc = -ENOMEM; return -ENOMEM;
goto out;
}
if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open && if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open &&
(CIFS_UNIX_POSIX_PATH_OPS_CAP & (CIFS_UNIX_POSIX_PATH_OPS_CAP &
......
...@@ -288,6 +288,8 @@ cifs_down_write(struct rw_semaphore *sem) ...@@ -288,6 +288,8 @@ cifs_down_write(struct rw_semaphore *sem)
msleep(10); msleep(10);
} }
static void cifsFileInfo_put_work(struct work_struct *work);
struct cifsFileInfo * struct cifsFileInfo *
cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
struct tcon_link *tlink, __u32 oplock) struct tcon_link *tlink, __u32 oplock)
...@@ -325,6 +327,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, ...@@ -325,6 +327,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
cfile->invalidHandle = false; cfile->invalidHandle = false;
cfile->tlink = cifs_get_tlink(tlink); cfile->tlink = cifs_get_tlink(tlink);
INIT_WORK(&cfile->oplock_break, cifs_oplock_break); INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
INIT_WORK(&cfile->put, cifsFileInfo_put_work);
mutex_init(&cfile->fh_mutex); mutex_init(&cfile->fh_mutex);
spin_lock_init(&cfile->file_info_lock); spin_lock_init(&cfile->file_info_lock);
...@@ -375,6 +378,41 @@ cifsFileInfo_get(struct cifsFileInfo *cifs_file) ...@@ -375,6 +378,41 @@ cifsFileInfo_get(struct cifsFileInfo *cifs_file)
return cifs_file; return cifs_file;
} }
static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file)
{
struct inode *inode = d_inode(cifs_file->dentry);
struct cifsInodeInfo *cifsi = CIFS_I(inode);
struct cifsLockInfo *li, *tmp;
struct super_block *sb = inode->i_sb;
/*
* Delete any outstanding lock records. We'll lose them when the file
* is closed anyway.
*/
cifs_down_write(&cifsi->lock_sem);
list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) {
list_del(&li->llist);
cifs_del_lock_waiters(li);
kfree(li);
}
list_del(&cifs_file->llist->llist);
kfree(cifs_file->llist);
up_write(&cifsi->lock_sem);
cifs_put_tlink(cifs_file->tlink);
dput(cifs_file->dentry);
cifs_sb_deactive(sb);
kfree(cifs_file);
}
static void cifsFileInfo_put_work(struct work_struct *work)
{
struct cifsFileInfo *cifs_file = container_of(work,
struct cifsFileInfo, put);
cifsFileInfo_put_final(cifs_file);
}
/** /**
* cifsFileInfo_put - release a reference of file priv data * cifsFileInfo_put - release a reference of file priv data
* *
...@@ -382,15 +420,15 @@ cifsFileInfo_get(struct cifsFileInfo *cifs_file) ...@@ -382,15 +420,15 @@ cifsFileInfo_get(struct cifsFileInfo *cifs_file)
*/ */
void cifsFileInfo_put(struct cifsFileInfo *cifs_file) void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
{ {
_cifsFileInfo_put(cifs_file, true); _cifsFileInfo_put(cifs_file, true, true);
} }
/** /**
* _cifsFileInfo_put - release a reference of file priv data * _cifsFileInfo_put - release a reference of file priv data
* *
* This may involve closing the filehandle @cifs_file out on the * This may involve closing the filehandle @cifs_file out on the
* server. Must be called without holding tcon->open_file_lock and * server. Must be called without holding tcon->open_file_lock,
* cifs_file->file_info_lock. * cinode->open_file_lock and cifs_file->file_info_lock.
* *
* If @wait_for_oplock_handler is true and we are releasing the last * If @wait_for_oplock_handler is true and we are releasing the last
* reference, wait for any running oplock break handler of the file * reference, wait for any running oplock break handler of the file
...@@ -398,7 +436,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) ...@@ -398,7 +436,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
* oplock break handler, you need to pass false. * oplock break handler, you need to pass false.
* *
*/ */
void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler) void _cifsFileInfo_put(struct cifsFileInfo *cifs_file,
bool wait_oplock_handler, bool offload)
{ {
struct inode *inode = d_inode(cifs_file->dentry); struct inode *inode = d_inode(cifs_file->dentry);
struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink); struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink);
...@@ -406,7 +445,6 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler) ...@@ -406,7 +445,6 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
struct cifsInodeInfo *cifsi = CIFS_I(inode); struct cifsInodeInfo *cifsi = CIFS_I(inode);
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct cifsLockInfo *li, *tmp;
struct cifs_fid fid; struct cifs_fid fid;
struct cifs_pending_open open; struct cifs_pending_open open;
bool oplock_break_cancelled; bool oplock_break_cancelled;
...@@ -467,24 +505,10 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler) ...@@ -467,24 +505,10 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
cifs_del_pending_open(&open); cifs_del_pending_open(&open);
/* if (offload)
* Delete any outstanding lock records. We'll lose them when the file queue_work(fileinfo_put_wq, &cifs_file->put);
* is closed anyway. else
*/ cifsFileInfo_put_final(cifs_file);
cifs_down_write(&cifsi->lock_sem);
list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) {
list_del(&li->llist);
cifs_del_lock_waiters(li);
kfree(li);
}
list_del(&cifs_file->llist->llist);
kfree(cifs_file->llist);
up_write(&cifsi->lock_sem);
cifs_put_tlink(cifs_file->tlink);
dput(cifs_file->dentry);
cifs_sb_deactive(sb);
kfree(cifs_file);
} }
int cifs_open(struct inode *inode, struct file *file) int cifs_open(struct inode *inode, struct file *file)
...@@ -728,6 +752,13 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) ...@@ -728,6 +752,13 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
if (backup_cred(cifs_sb)) if (backup_cred(cifs_sb))
create_options |= CREATE_OPEN_BACKUP_INTENT; create_options |= CREATE_OPEN_BACKUP_INTENT;
/* O_SYNC also has bit for O_DSYNC so following check picks up either */
if (cfile->f_flags & O_SYNC)
create_options |= CREATE_WRITE_THROUGH;
if (cfile->f_flags & O_DIRECT)
create_options |= CREATE_NO_BUFFER;
if (server->ops->get_lease_key) if (server->ops->get_lease_key)
server->ops->get_lease_key(inode, &cfile->fid); server->ops->get_lease_key(inode, &cfile->fid);
...@@ -808,7 +839,7 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) ...@@ -808,7 +839,7 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
int cifs_close(struct inode *inode, struct file *file) int cifs_close(struct inode *inode, struct file *file)
{ {
if (file->private_data != NULL) { if (file->private_data != NULL) {
cifsFileInfo_put(file->private_data); _cifsFileInfo_put(file->private_data, true, false);
file->private_data = NULL; file->private_data = NULL;
} }
...@@ -1681,7 +1712,7 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type, ...@@ -1681,7 +1712,7 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
rc = server->ops->mand_unlock_range(cfile, flock, xid); rc = server->ops->mand_unlock_range(cfile, flock, xid);
out: out:
if (flock->fl_flags & FL_POSIX) { if ((flock->fl_flags & FL_POSIX) || (flock->fl_flags & FL_FLOCK)) {
/* /*
* If this is a request to remove all locks because we * If this is a request to remove all locks because we
* are closing the file, it doesn't matter if the * are closing the file, it doesn't matter if the
...@@ -1698,6 +1729,52 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type, ...@@ -1698,6 +1729,52 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
return rc; return rc;
} }
int cifs_flock(struct file *file, int cmd, struct file_lock *fl)
{
int rc, xid;
int lock = 0, unlock = 0;
bool wait_flag = false;
bool posix_lck = false;
struct cifs_sb_info *cifs_sb;
struct cifs_tcon *tcon;
struct cifsFileInfo *cfile;
__u32 type;
rc = -EACCES;
xid = get_xid();
if (!(fl->fl_flags & FL_FLOCK))
return -ENOLCK;
cfile = (struct cifsFileInfo *)file->private_data;
tcon = tlink_tcon(cfile->tlink);
cifs_read_flock(fl, &type, &lock, &unlock, &wait_flag,
tcon->ses->server);
cifs_sb = CIFS_FILE_SB(file);
if (cap_unix(tcon->ses) &&
(CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
posix_lck = true;
if (!lock && !unlock) {
/*
* if no lock or unlock then nothing to do since we do not
* know what it is
*/
free_xid(xid);
return -EOPNOTSUPP;
}
rc = cifs_setlk(file, fl, type, wait_flag, posix_lck, lock, unlock,
xid);
free_xid(xid);
return rc;
}
int cifs_lock(struct file *file, int cmd, struct file_lock *flock) int cifs_lock(struct file *file, int cmd, struct file_lock *flock)
{ {
int rc, xid; int rc, xid;
...@@ -2757,10 +2834,18 @@ cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list, ...@@ -2757,10 +2834,18 @@ cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list,
if (!rc) { if (!rc) {
if (wdata->cfile->invalidHandle) if (wdata->cfile->invalidHandle)
rc = -EAGAIN; rc = -EAGAIN;
else else {
#ifdef CONFIG_CIFS_SMB_DIRECT
if (wdata->mr) {
wdata->mr->need_invalidate = true;
smbd_deregister_mr(wdata->mr);
wdata->mr = NULL;
}
#endif
rc = server->ops->async_writev(wdata, rc = server->ops->async_writev(wdata,
cifs_uncached_writedata_release); cifs_uncached_writedata_release);
} }
}
/* If the write was successfully sent, we are done */ /* If the write was successfully sent, we are done */
if (!rc) { if (!rc) {
...@@ -3482,9 +3567,17 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata, ...@@ -3482,9 +3567,17 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata,
if (!rc) { if (!rc) {
if (rdata->cfile->invalidHandle) if (rdata->cfile->invalidHandle)
rc = -EAGAIN; rc = -EAGAIN;
else else {
#ifdef CONFIG_CIFS_SMB_DIRECT
if (rdata->mr) {
rdata->mr->need_invalidate = true;
smbd_deregister_mr(rdata->mr);
rdata->mr = NULL;
}
#endif
rc = server->ops->async_readv(rdata); rc = server->ops->async_readv(rdata);
} }
}
/* If the read was successfully sent, we are done */ /* If the read was successfully sent, we are done */
if (!rc) { if (!rc) {
...@@ -4637,12 +4730,13 @@ void cifs_oplock_break(struct work_struct *work) ...@@ -4637,12 +4730,13 @@ void cifs_oplock_break(struct work_struct *work)
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
struct TCP_Server_Info *server = tcon->ses->server; struct TCP_Server_Info *server = tcon->ses->server;
int rc = 0; int rc = 0;
bool purge_cache = false;
wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS, wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS,
TASK_UNINTERRUPTIBLE); TASK_UNINTERRUPTIBLE);
server->ops->downgrade_oplock(server, cinode, server->ops->downgrade_oplock(server, cinode, cfile->oplock_level,
test_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, &cinode->flags)); cfile->oplock_epoch, &purge_cache);
if (!CIFS_CACHE_WRITE(cinode) && CIFS_CACHE_READ(cinode) && if (!CIFS_CACHE_WRITE(cinode) && CIFS_CACHE_READ(cinode) &&
cifs_has_mand_locks(cinode)) { cifs_has_mand_locks(cinode)) {
...@@ -4657,18 +4751,21 @@ void cifs_oplock_break(struct work_struct *work) ...@@ -4657,18 +4751,21 @@ void cifs_oplock_break(struct work_struct *work)
else else
break_lease(inode, O_WRONLY); break_lease(inode, O_WRONLY);
rc = filemap_fdatawrite(inode->i_mapping); rc = filemap_fdatawrite(inode->i_mapping);
if (!CIFS_CACHE_READ(cinode)) { if (!CIFS_CACHE_READ(cinode) || purge_cache) {
rc = filemap_fdatawait(inode->i_mapping); rc = filemap_fdatawait(inode->i_mapping);
mapping_set_error(inode->i_mapping, rc); mapping_set_error(inode->i_mapping, rc);
cifs_zap_mapping(inode); cifs_zap_mapping(inode);
} }
cifs_dbg(FYI, "Oplock flush inode %p rc %d\n", inode, rc); cifs_dbg(FYI, "Oplock flush inode %p rc %d\n", inode, rc);
if (CIFS_CACHE_WRITE(cinode))
goto oplock_break_ack;
} }
rc = cifs_push_locks(cfile); rc = cifs_push_locks(cfile);
if (rc) if (rc)
cifs_dbg(VFS, "Push locks rc = %d\n", rc); cifs_dbg(VFS, "Push locks rc = %d\n", rc);
oplock_break_ack:
/* /*
* releasing stale oplock after recent reconnect of smb session using * releasing stale oplock after recent reconnect of smb session using
* a now incorrect file handle is not a data integrity issue but do * a now incorrect file handle is not a data integrity issue but do
...@@ -4680,7 +4777,7 @@ void cifs_oplock_break(struct work_struct *work) ...@@ -4680,7 +4777,7 @@ void cifs_oplock_break(struct work_struct *work)
cinode); cinode);
cifs_dbg(FYI, "Oplock release rc = %d\n", rc); cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
} }
_cifsFileInfo_put(cfile, false /* do not wait for ourself */); _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);
cifs_done_oplock_break(cinode); cifs_done_oplock_break(cinode);
} }
......
This diff is collapsed.
...@@ -488,21 +488,10 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) ...@@ -488,21 +488,10 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK,
&pCifsInode->flags); &pCifsInode->flags);
/* netfile->oplock_epoch = 0;
* Set flag if the server downgrades the oplock netfile->oplock_level = pSMB->OplockLevel;
* to L2 else clear.
*/
if (pSMB->OplockLevel)
set_bit(
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&pCifsInode->flags);
else
clear_bit(
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&pCifsInode->flags);
cifs_queue_oplock_break(netfile);
netfile->oplock_break_cancelled = false; netfile->oplock_break_cancelled = false;
cifs_queue_oplock_break(netfile);
spin_unlock(&tcon->open_file_lock); spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
......
...@@ -31,6 +31,231 @@ ...@@ -31,6 +31,231 @@
#include <linux/utsname.h> #include <linux/utsname.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "cifs_spnego.h" #include "cifs_spnego.h"
#include "smb2proto.h"
bool
is_server_using_iface(struct TCP_Server_Info *server,
struct cifs_server_iface *iface)
{
struct sockaddr_in *i4 = (struct sockaddr_in *)&iface->sockaddr;
struct sockaddr_in6 *i6 = (struct sockaddr_in6 *)&iface->sockaddr;
struct sockaddr_in *s4 = (struct sockaddr_in *)&server->dstaddr;
struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&server->dstaddr;
if (server->dstaddr.ss_family != iface->sockaddr.ss_family)
return false;
if (server->dstaddr.ss_family == AF_INET) {
if (s4->sin_addr.s_addr != i4->sin_addr.s_addr)
return false;
} else if (server->dstaddr.ss_family == AF_INET6) {
if (memcmp(&s6->sin6_addr, &i6->sin6_addr,
sizeof(i6->sin6_addr)) != 0)
return false;
} else {
/* unknown family.. */
return false;
}
return true;
}
bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
{
int i;
for (i = 0; i < ses->chan_count; i++) {
if (is_server_using_iface(ses->chans[i].server, iface))
return true;
}
return false;
}
/* returns number of channels added */
int cifs_try_adding_channels(struct cifs_ses *ses)
{
int old_chan_count = ses->chan_count;
int left = ses->chan_max - ses->chan_count;
int i = 0;
int rc = 0;
int tries = 0;
if (left <= 0) {
cifs_dbg(FYI,
"ses already at max_channels (%zu), nothing to open\n",
ses->chan_max);
return 0;
}
if (ses->server->dialect < SMB30_PROT_ID) {
cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n");
return 0;
}
/*
* Keep connecting to same, fastest, iface for all channels as
* long as its RSS. Try next fastest one if not RSS or channel
* creation fails.
*/
while (left > 0) {
struct cifs_server_iface *iface;
tries++;
if (tries > 3*ses->chan_max) {
cifs_dbg(FYI, "too many attempt at opening channels (%d channels left to open)\n",
left);
break;
}
iface = &ses->iface_list[i];
if (is_ses_using_iface(ses, iface) && !iface->rss_capable) {
i = (i+1) % ses->iface_count;
continue;
}
rc = cifs_ses_add_channel(ses, iface);
if (rc) {
cifs_dbg(FYI, "failed to open extra channel on iface#%d rc=%d\n",
i, rc);
i = (i+1) % ses->iface_count;
continue;
}
cifs_dbg(FYI, "successfully opened new channel on iface#%d\n",
i);
left--;
}
return ses->chan_count - old_chan_count;
}
int
cifs_ses_add_channel(struct cifs_ses *ses, struct cifs_server_iface *iface)
{
struct cifs_chan *chan;
struct smb_vol vol = {NULL};
static const char unc_fmt[] = "\\%s\\foo";
char unc[sizeof(unc_fmt)+SERVER_NAME_LEN_WITH_NULL] = {0};
struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr;
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr;
int rc;
unsigned int xid = get_xid();
cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ",
ses, iface->speed, iface->rdma_capable ? "yes" : "no");
if (iface->sockaddr.ss_family == AF_INET)
cifs_dbg(FYI, "ip:%pI4)\n", &ipv4->sin_addr);
else
cifs_dbg(FYI, "ip:%pI6)\n", &ipv6->sin6_addr);
/*
* Setup a smb_vol with mostly the same info as the existing
* session and overwrite it with the requested iface data.
*
* We need to setup at least the fields used for negprot and
* sesssetup.
*
* We only need the volume here, so we can reuse memory from
* the session and server without caring about memory
* management.
*/
/* Always make new connection for now (TODO?) */
vol.nosharesock = true;
/* Auth */
vol.domainauto = ses->domainAuto;
vol.domainname = ses->domainName;
vol.username = ses->user_name;
vol.password = ses->password;
vol.sectype = ses->sectype;
vol.sign = ses->sign;
/* UNC and paths */
/* XXX: Use ses->server->hostname? */
sprintf(unc, unc_fmt, ses->serverName);
vol.UNC = unc;
vol.prepath = "";
/* Re-use same version as master connection */
vol.vals = ses->server->vals;
vol.ops = ses->server->ops;
vol.noblocksnd = ses->server->noblocksnd;
vol.noautotune = ses->server->noautotune;
vol.sockopt_tcp_nodelay = ses->server->tcp_nodelay;
vol.echo_interval = ses->server->echo_interval / HZ;
/*
* This will be used for encoding/decoding user/domain/pw
* during sess setup auth.
*
* XXX: We use the default for simplicity but the proper way
* would be to use the one that ses used, which is not
* stored. This might break when dealing with non-ascii
* strings.
*/
vol.local_nls = load_nls_default();
/* Use RDMA if possible */
vol.rdma = iface->rdma_capable;
memcpy(&vol.dstaddr, &iface->sockaddr, sizeof(struct sockaddr_storage));
/* reuse master con client guid */
memcpy(&vol.client_guid, ses->server->client_guid,
SMB2_CLIENT_GUID_SIZE);
vol.use_client_guid = true;
mutex_lock(&ses->session_mutex);
chan = &ses->chans[ses->chan_count];
chan->server = cifs_get_tcp_session(&vol);
if (IS_ERR(chan->server)) {
rc = PTR_ERR(chan->server);
chan->server = NULL;
goto out;
}
/*
* We need to allocate the server crypto now as we will need
* to sign packets before we generate the channel signing key
* (we sign with the session key)
*/
rc = smb311_crypto_shash_allocate(chan->server);
if (rc) {
cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
goto out;
}
ses->binding = true;
rc = cifs_negotiate_protocol(xid, ses);
if (rc)
goto out;
rc = cifs_setup_session(xid, ses, vol.local_nls);
if (rc)
goto out;
/* success, put it on the list
* XXX: sharing ses between 2 tcp server is not possible, the
* way "internal" linked lists works in linux makes element
* only able to belong to one list
*
* the binding session is already established so the rest of
* the code should be able to look it up, no need to add the
* ses to the new server.
*/
ses->chan_count++;
atomic_set(&ses->chan_seq, 0);
out:
ses->binding = false;
mutex_unlock(&ses->session_mutex);
if (rc && chan->server)
cifs_put_tcp_session(chan->server, 0);
unload_nls(vol.local_nls);
return rc;
}
static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB) static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB)
{ {
...@@ -342,6 +567,7 @@ int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, ...@@ -342,6 +567,7 @@ int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
struct cifs_ses *ses) struct cifs_ses *ses)
{ {
struct TCP_Server_Info *server = cifs_ses_server(ses);
NEGOTIATE_MESSAGE *sec_blob = (NEGOTIATE_MESSAGE *)pbuffer; NEGOTIATE_MESSAGE *sec_blob = (NEGOTIATE_MESSAGE *)pbuffer;
__u32 flags; __u32 flags;
...@@ -354,9 +580,9 @@ void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, ...@@ -354,9 +580,9 @@ void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC |
NTLMSSP_NEGOTIATE_SEAL; NTLMSSP_NEGOTIATE_SEAL;
if (ses->server->sign) if (server->sign)
flags |= NTLMSSP_NEGOTIATE_SIGN; flags |= NTLMSSP_NEGOTIATE_SIGN;
if (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess) if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
flags |= NTLMSSP_NEGOTIATE_KEY_XCH; flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
sec_blob->NegotiateFlags = cpu_to_le32(flags); sec_blob->NegotiateFlags = cpu_to_le32(flags);
......
...@@ -369,12 +369,10 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr) ...@@ -369,12 +369,10 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr)
static void static void
cifs_downgrade_oplock(struct TCP_Server_Info *server, cifs_downgrade_oplock(struct TCP_Server_Info *server,
struct cifsInodeInfo *cinode, bool set_level2) struct cifsInodeInfo *cinode, __u32 oplock,
unsigned int epoch, bool *purge_cache)
{ {
if (set_level2) cifs_set_oplock_level(cinode, oplock);
cifs_set_oplock_level(cinode, OPLOCK_READ);
else
cifs_set_oplock_level(cinode, 0);
} }
static bool static bool
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "cifs_unicode.h" #include "cifs_unicode.h"
#include "smb2status.h" #include "smb2status.h"
#include "smb2glob.h" #include "smb2glob.h"
#include "nterr.h"
static int static int
check_smb2_hdr(struct smb2_sync_hdr *shdr, __u64 mid) check_smb2_hdr(struct smb2_sync_hdr *shdr, __u64 mid)
...@@ -249,16 +250,10 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *srvr) ...@@ -249,16 +250,10 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *srvr)
* of junk. Other servers match RFC1001 len to actual * of junk. Other servers match RFC1001 len to actual
* SMB2/SMB3 frame length (header + smb2 response specific data) * SMB2/SMB3 frame length (header + smb2 response specific data)
* Some windows servers also pad up to 8 bytes when compounding. * Some windows servers also pad up to 8 bytes when compounding.
* If pad is longer than eight bytes, log the server behavior
* (once), since may indicate a problem but allow it and continue
* since the frame is parseable.
*/ */
if (clc_len < len) { if (clc_len < len)
pr_warn_once(
"srv rsp padded more than expected. Length %d not %d for cmd:%d mid:%llu\n",
len, clc_len, command, mid);
return 0; return 0;
}
pr_warn_once( pr_warn_once(
"srv rsp too short, len %d not %d. cmd:%d mid:%llu\n", "srv rsp too short, len %d not %d. cmd:%d mid:%llu\n",
len, clc_len, command, mid); len, clc_len, command, mid);
...@@ -534,7 +529,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp, ...@@ -534,7 +529,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
cifs_dbg(FYI, "found in the open list\n"); cifs_dbg(FYI, "found in the open list\n");
cifs_dbg(FYI, "lease key match, lease break 0x%x\n", cifs_dbg(FYI, "lease key match, lease break 0x%x\n",
le32_to_cpu(rsp->NewLeaseState)); lease_state);
if (ack_req) if (ack_req)
cfile->oplock_break_cancelled = false; cfile->oplock_break_cancelled = false;
...@@ -543,17 +538,8 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp, ...@@ -543,17 +538,8 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags); set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags);
/* cfile->oplock_epoch = le16_to_cpu(rsp->Epoch);
* Set or clear flags depending on the lease state being READ. cfile->oplock_level = lease_state;
* HANDLE caching flag should be added when the client starts
* to defer closing remote file handles with HANDLE leases.
*/
if (lease_state & SMB2_LEASE_READ_CACHING_HE)
set_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&cinode->flags);
else
clear_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&cinode->flags);
cifs_queue_oplock_break(cfile); cifs_queue_oplock_break(cfile);
kfree(lw); kfree(lw);
...@@ -576,7 +562,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp, ...@@ -576,7 +562,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, "found in the pending open list\n");
cifs_dbg(FYI, "lease key match, lease break 0x%x\n", cifs_dbg(FYI, "lease key match, lease break 0x%x\n",
le32_to_cpu(rsp->NewLeaseState)); lease_state);
open->oplock = lease_state; open->oplock = lease_state;
} }
...@@ -673,10 +659,10 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) ...@@ -673,10 +659,10 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each(tmp, &server->smb_ses_list) { list_for_each(tmp, &server->smb_ses_list) {
ses = list_entry(tmp, struct cifs_ses, smb_ses_list); ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
list_for_each(tmp1, &ses->tcon_list) { list_for_each(tmp1, &ses->tcon_list) {
tcon = list_entry(tmp1, struct cifs_tcon, tcon_list); tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
spin_lock(&tcon->open_file_lock); spin_lock(&tcon->open_file_lock);
list_for_each(tmp2, &tcon->openFileList) { list_for_each(tmp2, &tcon->openFileList) {
cfile = list_entry(tmp2, struct cifsFileInfo, cfile = list_entry(tmp2, struct cifsFileInfo,
...@@ -688,6 +674,8 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) ...@@ -688,6 +674,8 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
continue; continue;
cifs_dbg(FYI, "file id match, oplock break\n"); cifs_dbg(FYI, "file id match, oplock break\n");
cifs_stats_inc(
&tcon->stats.cifs_stats.num_oplock_brks);
cinode = CIFS_I(d_inode(cfile->dentry)); cinode = CIFS_I(d_inode(cfile->dentry));
spin_lock(&cfile->file_info_lock); spin_lock(&cfile->file_info_lock);
if (!CIFS_CACHE_WRITE(cinode) && if (!CIFS_CACHE_WRITE(cinode) &&
...@@ -699,18 +687,9 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) ...@@ -699,18 +687,9 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK,
&cinode->flags); &cinode->flags);
/* cfile->oplock_epoch = 0;
* Set flag if the server downgrades the oplock cfile->oplock_level = rsp->OplockLevel;
* to L2 else clear.
*/
if (rsp->OplockLevel)
set_bit(
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&cinode->flags);
else
clear_bit(
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&cinode->flags);
spin_unlock(&cfile->file_info_lock); spin_unlock(&cfile->file_info_lock);
cifs_queue_oplock_break(cfile); cifs_queue_oplock_break(cfile);
...@@ -720,9 +699,6 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) ...@@ -720,9 +699,6 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
return true; return true;
} }
spin_unlock(&tcon->open_file_lock); spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock);
cifs_dbg(FYI, "No matching file for oplock break\n");
return true;
} }
} }
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
...@@ -735,45 +711,98 @@ smb2_cancelled_close_fid(struct work_struct *work) ...@@ -735,45 +711,98 @@ smb2_cancelled_close_fid(struct work_struct *work)
{ {
struct close_cancelled_open *cancelled = container_of(work, struct close_cancelled_open *cancelled = container_of(work,
struct close_cancelled_open, work); struct close_cancelled_open, work);
struct cifs_tcon *tcon = cancelled->tcon;
int rc;
cifs_dbg(VFS, "Close unmatched open\n"); if (cancelled->mid)
cifs_tcon_dbg(VFS, "Close unmatched open for MID:%llx\n",
cancelled->mid);
else
cifs_tcon_dbg(VFS, "Close interrupted close\n");
SMB2_close(0, cancelled->tcon, cancelled->fid.persistent_fid, rc = SMB2_close(0, tcon, cancelled->fid.persistent_fid,
cancelled->fid.volatile_fid); cancelled->fid.volatile_fid);
cifs_put_tcon(cancelled->tcon); if (rc)
cifs_tcon_dbg(VFS, "Close cancelled mid failed rc:%d\n", rc);
cifs_put_tcon(tcon);
kfree(cancelled); kfree(cancelled);
} }
/*
* Caller should already has an extra reference to @tcon
* This function is used to queue work to close a handle to prevent leaks
* on the server.
* We handle two cases. If an open was interrupted after we sent the
* SMB2_CREATE to the server but before we processed the reply, and second
* if a close was interrupted before we sent the SMB2_CLOSE to the server.
*/
static int
__smb2_handle_cancelled_cmd(struct cifs_tcon *tcon, __u16 cmd, __u64 mid,
__u64 persistent_fid, __u64 volatile_fid)
{
struct close_cancelled_open *cancelled;
cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL);
if (!cancelled)
return -ENOMEM;
cancelled->fid.persistent_fid = persistent_fid;
cancelled->fid.volatile_fid = volatile_fid;
cancelled->tcon = tcon;
cancelled->cmd = cmd;
cancelled->mid = mid;
INIT_WORK(&cancelled->work, smb2_cancelled_close_fid);
WARN_ON(queue_work(cifsiod_wq, &cancelled->work) == false);
return 0;
}
int
smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid,
__u64 volatile_fid)
{
int rc;
cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count);
spin_lock(&cifs_tcp_ses_lock);
tcon->tc_count++;
spin_unlock(&cifs_tcp_ses_lock);
rc = __smb2_handle_cancelled_cmd(tcon, SMB2_CLOSE_HE, 0,
persistent_fid, volatile_fid);
if (rc)
cifs_put_tcon(tcon);
return rc;
}
int int
smb2_handle_cancelled_mid(char *buffer, struct TCP_Server_Info *server) smb2_handle_cancelled_mid(char *buffer, struct TCP_Server_Info *server)
{ {
struct smb2_sync_hdr *sync_hdr = (struct smb2_sync_hdr *)buffer; struct smb2_sync_hdr *sync_hdr = (struct smb2_sync_hdr *)buffer;
struct smb2_create_rsp *rsp = (struct smb2_create_rsp *)buffer; struct smb2_create_rsp *rsp = (struct smb2_create_rsp *)buffer;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct close_cancelled_open *cancelled; int rc;
if (sync_hdr->Command != SMB2_CREATE || if (sync_hdr->Command != SMB2_CREATE ||
sync_hdr->Status != STATUS_SUCCESS) sync_hdr->Status != STATUS_SUCCESS)
return 0; return 0;
cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL);
if (!cancelled)
return -ENOMEM;
tcon = smb2_find_smb_tcon(server, sync_hdr->SessionId, tcon = smb2_find_smb_tcon(server, sync_hdr->SessionId,
sync_hdr->TreeId); sync_hdr->TreeId);
if (!tcon) { if (!tcon)
kfree(cancelled);
return -ENOENT; return -ENOENT;
}
cancelled->fid.persistent_fid = rsp->PersistentFileId; rc = __smb2_handle_cancelled_cmd(tcon,
cancelled->fid.volatile_fid = rsp->VolatileFileId; le16_to_cpu(sync_hdr->Command),
cancelled->tcon = tcon; le64_to_cpu(sync_hdr->MessageId),
INIT_WORK(&cancelled->work, smb2_cancelled_close_fid); rsp->PersistentFileId,
queue_work(cifsiod_wq, &cancelled->work); rsp->VolatileFileId);
if (rc)
cifs_put_tcon(tcon);
return 0; return rc;
} }
/** /**
...@@ -788,23 +817,37 @@ smb311_update_preauth_hash(struct cifs_ses *ses, struct kvec *iov, int nvec) ...@@ -788,23 +817,37 @@ smb311_update_preauth_hash(struct cifs_ses *ses, struct kvec *iov, int nvec)
int i, rc; int i, rc;
struct sdesc *d; struct sdesc *d;
struct smb2_sync_hdr *hdr; struct smb2_sync_hdr *hdr;
struct TCP_Server_Info *server = cifs_ses_server(ses);
if (ses->server->tcpStatus == CifsGood) { hdr = (struct smb2_sync_hdr *)iov[0].iov_base;
/* skip non smb311 connections */ /* neg prot are always taken */
if (ses->server->dialect != SMB311_PROT_ID) if (hdr->Command == SMB2_NEGOTIATE)
goto ok;
/*
* If we process a command which wasn't a negprot it means the
* neg prot was already done, so the server dialect was set
* and we can test it. Preauth requires 3.1.1 for now.
*/
if (server->dialect != SMB311_PROT_ID)
return 0;
if (hdr->Command != SMB2_SESSION_SETUP)
return 0; return 0;
/* skip last sess setup response */ /* skip last sess setup response */
hdr = (struct smb2_sync_hdr *)iov[0].iov_base; if ((hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)
if (hdr->Flags & SMB2_FLAGS_SIGNED) && (hdr->Status == NT_STATUS_OK
|| (hdr->Status !=
cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))))
return 0; return 0;
}
rc = smb311_crypto_shash_allocate(ses->server); ok:
rc = smb311_crypto_shash_allocate(server);
if (rc) if (rc)
return rc; return rc;
d = ses->server->secmech.sdescsha512; d = server->secmech.sdescsha512;
rc = crypto_shash_init(&d->shash); rc = crypto_shash_init(&d->shash);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: could not init sha512 shash\n", __func__); cifs_dbg(VFS, "%s: could not init sha512 shash\n", __func__);
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/falloc.h> #include <linux/falloc.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/uuid.h> #include <linux/uuid.h>
#include <linux/sort.h>
#include <crypto/aead.h> #include <crypto/aead.h>
#include "cifsglob.h" #include "cifsglob.h"
#include "smb2pdu.h" #include "smb2pdu.h"
...@@ -151,13 +152,7 @@ smb2_get_credits_field(struct TCP_Server_Info *server, const int optype) ...@@ -151,13 +152,7 @@ smb2_get_credits_field(struct TCP_Server_Info *server, const int optype)
static unsigned int static unsigned int
smb2_get_credits(struct mid_q_entry *mid) smb2_get_credits(struct mid_q_entry *mid)
{ {
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)mid->resp_buf; return mid->credits_received;
if (mid->mid_state == MID_RESPONSE_RECEIVED
|| mid->mid_state == MID_RESPONSE_MALFORMED)
return le16_to_cpu(shdr->CreditRequest);
return 0;
} }
static int static int
...@@ -315,7 +310,7 @@ smb2_negotiate(const unsigned int xid, struct cifs_ses *ses) ...@@ -315,7 +310,7 @@ smb2_negotiate(const unsigned int xid, struct cifs_ses *ses)
{ {
int rc; int rc;
ses->server->CurrentMid = 0; cifs_ses_server(ses)->CurrentMid = 0;
rc = SMB2_negotiate(xid, ses); rc = SMB2_negotiate(xid, ses);
/* BB we probably don't need to retry with modern servers */ /* BB we probably don't need to retry with modern servers */
if (rc == -EAGAIN) if (rc == -EAGAIN)
...@@ -558,6 +553,13 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, ...@@ -558,6 +553,13 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
return rc; return rc;
} }
static int compare_iface(const void *ia, const void *ib)
{
const struct cifs_server_iface *a = (struct cifs_server_iface *)ia;
const struct cifs_server_iface *b = (struct cifs_server_iface *)ib;
return a->speed == b->speed ? 0 : (a->speed > b->speed ? -1 : 1);
}
static int static int
SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon) SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
...@@ -587,6 +589,9 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon) ...@@ -587,6 +589,9 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
if (rc) if (rc)
goto out; goto out;
/* sort interfaces from fastest to slowest */
sort(iface_list, iface_count, sizeof(*iface_list), compare_iface, NULL);
spin_lock(&ses->iface_lock); spin_lock(&ses->iface_lock);
kfree(ses->iface_list); kfree(ses->iface_list);
ses->iface_list = iface_list; ses->iface_list = iface_list;
...@@ -1402,15 +1407,10 @@ smb2_ioctl_query_info(const unsigned int xid, ...@@ -1402,15 +1407,10 @@ smb2_ioctl_query_info(const unsigned int xid,
if (smb3_encryption_required(tcon)) if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ; flags |= CIFS_TRANSFORM_REQ;
buffer = kmalloc(qi.output_buffer_length, GFP_KERNEL); buffer = memdup_user(arg + sizeof(struct smb_query_info),
if (buffer == NULL) qi.output_buffer_length);
return -ENOMEM; if (IS_ERR(buffer))
return PTR_ERR(buffer);
if (copy_from_user(buffer, arg + sizeof(struct smb_query_info),
qi.output_buffer_length)) {
rc = -EFAULT;
goto iqinf_exit;
}
/* Open */ /* Open */
memset(&open_iov, 0, sizeof(open_iov)); memset(&open_iov, 0, sizeof(open_iov));
...@@ -1529,35 +1529,32 @@ smb2_ioctl_query_info(const unsigned int xid, ...@@ -1529,35 +1529,32 @@ smb2_ioctl_query_info(const unsigned int xid,
if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length) if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length)
qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount); qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount);
if (qi.input_buffer_length > 0 && if (qi.input_buffer_length > 0 &&
le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length > rsp_iov[1].iov_len) { le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length
rc = -EFAULT; > rsp_iov[1].iov_len)
goto iqinf_exit; goto e_fault;
}
if (copy_to_user(&pqi->input_buffer_length, &qi.input_buffer_length, if (copy_to_user(&pqi->input_buffer_length,
sizeof(qi.input_buffer_length))) { &qi.input_buffer_length,
rc = -EFAULT; sizeof(qi.input_buffer_length)))
goto iqinf_exit; goto e_fault;
}
if (copy_to_user((void __user *)pqi + sizeof(struct smb_query_info), if (copy_to_user((void __user *)pqi + sizeof(struct smb_query_info),
(const void *)io_rsp + le32_to_cpu(io_rsp->OutputOffset), (const void *)io_rsp + le32_to_cpu(io_rsp->OutputOffset),
qi.input_buffer_length)) { qi.input_buffer_length))
rc = -EFAULT; goto e_fault;
goto iqinf_exit;
}
} else { } else {
pqi = (struct smb_query_info __user *)arg; pqi = (struct smb_query_info __user *)arg;
qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
if (le32_to_cpu(qi_rsp->OutputBufferLength) < qi.input_buffer_length) if (le32_to_cpu(qi_rsp->OutputBufferLength) < qi.input_buffer_length)
qi.input_buffer_length = le32_to_cpu(qi_rsp->OutputBufferLength); qi.input_buffer_length = le32_to_cpu(qi_rsp->OutputBufferLength);
if (copy_to_user(&pqi->input_buffer_length, &qi.input_buffer_length, if (copy_to_user(&pqi->input_buffer_length,
sizeof(qi.input_buffer_length))) { &qi.input_buffer_length,
rc = -EFAULT; sizeof(qi.input_buffer_length)))
goto iqinf_exit; goto e_fault;
}
if (copy_to_user(pqi + 1, qi_rsp->Buffer, qi.input_buffer_length)) { if (copy_to_user(pqi + 1, qi_rsp->Buffer,
rc = -EFAULT; qi.input_buffer_length))
goto iqinf_exit; goto e_fault;
}
} }
iqinf_exit: iqinf_exit:
...@@ -1573,6 +1570,10 @@ smb2_ioctl_query_info(const unsigned int xid, ...@@ -1573,6 +1570,10 @@ smb2_ioctl_query_info(const unsigned int xid,
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
return rc; return rc;
e_fault:
rc = -EFAULT;
goto iqinf_exit;
} }
static ssize_t static ssize_t
...@@ -3281,22 +3282,38 @@ static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode, ...@@ -3281,22 +3282,38 @@ static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode,
static void static void
smb2_downgrade_oplock(struct TCP_Server_Info *server, smb2_downgrade_oplock(struct TCP_Server_Info *server,
struct cifsInodeInfo *cinode, bool set_level2) struct cifsInodeInfo *cinode, __u32 oplock,
unsigned int epoch, bool *purge_cache)
{ {
if (set_level2) server->ops->set_oplock_level(cinode, oplock, 0, NULL);
server->ops->set_oplock_level(cinode, SMB2_OPLOCK_LEVEL_II,
0, NULL);
else
server->ops->set_oplock_level(cinode, 0, 0, NULL);
} }
static void static void
smb21_downgrade_oplock(struct TCP_Server_Info *server, smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
struct cifsInodeInfo *cinode, bool set_level2) unsigned int epoch, bool *purge_cache);
static void
smb3_downgrade_oplock(struct TCP_Server_Info *server,
struct cifsInodeInfo *cinode, __u32 oplock,
unsigned int epoch, bool *purge_cache)
{ {
server->ops->set_oplock_level(cinode, unsigned int old_state = cinode->oplock;
set_level2 ? SMB2_LEASE_READ_CACHING_HE : unsigned int old_epoch = cinode->epoch;
0, 0, NULL); unsigned int new_state;
if (epoch > old_epoch) {
smb21_set_oplock_level(cinode, oplock, 0, NULL);
cinode->epoch = epoch;
}
new_state = cinode->oplock;
*purge_cache = false;
if ((old_state & CIFS_CACHE_READ_FLG) != 0 &&
(new_state & CIFS_CACHE_READ_FLG) == 0)
*purge_cache = true;
else if (old_state == new_state && (epoch - old_epoch > 1))
*purge_cache = true;
} }
static void static void
...@@ -3598,15 +3615,17 @@ smb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key) ...@@ -3598,15 +3615,17 @@ smb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key)
u8 *ses_enc_key; u8 *ses_enc_key;
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
if (ses->Suid != ses_id) if (ses->Suid == ses_id) {
continue;
ses_enc_key = enc ? ses->smb3encryptionkey : ses_enc_key = enc ? ses->smb3encryptionkey :
ses->smb3decryptionkey; ses->smb3decryptionkey;
memcpy(key, ses_enc_key, SMB3_SIGN_KEY_SIZE); memcpy(key, ses_enc_key, SMB3_SIGN_KEY_SIZE);
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return 0; return 0;
} }
}
}
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return 1; return 1;
...@@ -4556,7 +4575,7 @@ struct smb_version_operations smb21_operations = { ...@@ -4556,7 +4575,7 @@ struct smb_version_operations smb21_operations = {
.print_stats = smb2_print_stats, .print_stats = smb2_print_stats,
.is_oplock_break = smb2_is_valid_oplock_break, .is_oplock_break = smb2_is_valid_oplock_break,
.handle_cancelled_mid = smb2_handle_cancelled_mid, .handle_cancelled_mid = smb2_handle_cancelled_mid,
.downgrade_oplock = smb21_downgrade_oplock, .downgrade_oplock = smb2_downgrade_oplock,
.need_neg = smb2_need_neg, .need_neg = smb2_need_neg,
.negotiate = smb2_negotiate, .negotiate = smb2_negotiate,
.negotiate_wsize = smb2_negotiate_wsize, .negotiate_wsize = smb2_negotiate_wsize,
...@@ -4656,7 +4675,7 @@ struct smb_version_operations smb30_operations = { ...@@ -4656,7 +4675,7 @@ struct smb_version_operations smb30_operations = {
.dump_share_caps = smb2_dump_share_caps, .dump_share_caps = smb2_dump_share_caps,
.is_oplock_break = smb2_is_valid_oplock_break, .is_oplock_break = smb2_is_valid_oplock_break,
.handle_cancelled_mid = smb2_handle_cancelled_mid, .handle_cancelled_mid = smb2_handle_cancelled_mid,
.downgrade_oplock = smb21_downgrade_oplock, .downgrade_oplock = smb3_downgrade_oplock,
.need_neg = smb2_need_neg, .need_neg = smb2_need_neg,
.negotiate = smb2_negotiate, .negotiate = smb2_negotiate,
.negotiate_wsize = smb3_negotiate_wsize, .negotiate_wsize = smb3_negotiate_wsize,
...@@ -4764,7 +4783,7 @@ struct smb_version_operations smb311_operations = { ...@@ -4764,7 +4783,7 @@ struct smb_version_operations smb311_operations = {
.dump_share_caps = smb2_dump_share_caps, .dump_share_caps = smb2_dump_share_caps,
.is_oplock_break = smb2_is_valid_oplock_break, .is_oplock_break = smb2_is_valid_oplock_break,
.handle_cancelled_mid = smb2_handle_cancelled_mid, .handle_cancelled_mid = smb2_handle_cancelled_mid,
.downgrade_oplock = smb21_downgrade_oplock, .downgrade_oplock = smb3_downgrade_oplock,
.need_neg = smb2_need_neg, .need_neg = smb2_need_neg,
.negotiate = smb2_negotiate, .negotiate = smb2_negotiate,
.negotiate_wsize = smb3_negotiate_wsize, .negotiate_wsize = smb3_negotiate_wsize,
......
...@@ -252,7 +252,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) ...@@ -252,7 +252,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
if (tcon == NULL) if (tcon == NULL)
return 0; return 0;
if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL) if (smb2_command == SMB2_TREE_CONNECT)
return 0; return 0;
if (tcon->tidStatus == CifsExiting) { if (tcon->tidStatus == CifsExiting) {
...@@ -426,16 +426,9 @@ fill_small_buf(__le16 smb2_command, struct cifs_tcon *tcon, void *buf, ...@@ -426,16 +426,9 @@ fill_small_buf(__le16 smb2_command, struct cifs_tcon *tcon, void *buf,
* SMB information in the SMB header. If the return code is zero, this * SMB information in the SMB header. If the return code is zero, this
* function must have filled in request_buf pointer. * function must have filled in request_buf pointer.
*/ */
static int static int __smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
void **request_buf, unsigned int *total_len) void **request_buf, unsigned int *total_len)
{ {
int rc;
rc = smb2_reconnect(smb2_command, tcon);
if (rc)
return rc;
/* BB eventually switch this to SMB2 specific small buf size */ /* BB eventually switch this to SMB2 specific small buf size */
if (smb2_command == SMB2_SET_INFO) if (smb2_command == SMB2_SET_INFO)
*request_buf = cifs_buf_get(); *request_buf = cifs_buf_get();
...@@ -456,7 +449,31 @@ smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, ...@@ -456,7 +449,31 @@ smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
cifs_stats_inc(&tcon->num_smbs_sent); cifs_stats_inc(&tcon->num_smbs_sent);
} }
return 0;
}
static int smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
void **request_buf, unsigned int *total_len)
{
int rc;
rc = smb2_reconnect(smb2_command, tcon);
if (rc)
return rc; return rc;
return __smb2_plain_req_init(smb2_command, tcon, request_buf,
total_len);
}
static int smb2_ioctl_req_init(u32 opcode, struct cifs_tcon *tcon,
void **request_buf, unsigned int *total_len)
{
/* Skip reconnect only for FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs */
if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) {
return __smb2_plain_req_init(SMB2_IOCTL, tcon, request_buf,
total_len);
}
return smb2_plain_req_init(SMB2_IOCTL, tcon, request_buf, total_len);
} }
/* For explanation of negotiate contexts see MS-SMB2 section 2.2.3.1 */ /* For explanation of negotiate contexts see MS-SMB2 section 2.2.3.1 */
...@@ -791,7 +808,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) ...@@ -791,7 +808,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
struct kvec rsp_iov; struct kvec rsp_iov;
int rc = 0; int rc = 0;
int resp_buftype; int resp_buftype;
struct TCP_Server_Info *server = ses->server; struct TCP_Server_Info *server = cifs_ses_server(ses);
int blob_offset, blob_length; int blob_offset, blob_length;
char *security_blob; char *security_blob;
int flags = CIFS_NEG_OP; int flags = CIFS_NEG_OP;
...@@ -813,7 +830,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) ...@@ -813,7 +830,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
if (strcmp(ses->server->vals->version_string, if (strcmp(server->vals->version_string,
SMB3ANY_VERSION_STRING) == 0) { SMB3ANY_VERSION_STRING) == 0) {
req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
...@@ -829,7 +846,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) ...@@ -829,7 +846,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
total_len += 8; total_len += 8;
} else { } else {
/* otherwise send specific dialect */ /* otherwise send specific dialect */
req->Dialects[0] = cpu_to_le16(ses->server->vals->protocol_id); req->Dialects[0] = cpu_to_le16(server->vals->protocol_id);
req->DialectCount = cpu_to_le16(1); req->DialectCount = cpu_to_le16(1);
total_len += 2; total_len += 2;
} }
...@@ -1171,7 +1188,7 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) ...@@ -1171,7 +1188,7 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
int rc; int rc;
struct cifs_ses *ses = sess_data->ses; struct cifs_ses *ses = sess_data->ses;
struct smb2_sess_setup_req *req; struct smb2_sess_setup_req *req;
struct TCP_Server_Info *server = ses->server; struct TCP_Server_Info *server = cifs_ses_server(ses);
unsigned int total_len; unsigned int total_len;
rc = smb2_plain_req_init(SMB2_SESSION_SETUP, NULL, (void **) &req, rc = smb2_plain_req_init(SMB2_SESSION_SETUP, NULL, (void **) &req,
...@@ -1179,13 +1196,21 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) ...@@ -1179,13 +1196,21 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
if (rc) if (rc)
return rc; return rc;
if (sess_data->ses->binding) {
req->sync_hdr.SessionId = sess_data->ses->Suid;
req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
req->PreviousSessionId = 0;
req->Flags = SMB2_SESSION_REQ_FLAG_BINDING;
} else {
/* First session, not a reauthenticate */ /* First session, not a reauthenticate */
req->sync_hdr.SessionId = 0; req->sync_hdr.SessionId = 0;
/*
/* if reconnect, we need to send previous sess id, otherwise it is 0 */ * if reconnect, we need to send previous sess id
* otherwise it is 0
*/
req->PreviousSessionId = sess_data->previous_session; req->PreviousSessionId = sess_data->previous_session;
req->Flags = 0; /* MBZ */ req->Flags = 0; /* MBZ */
}
/* enough to enable echos and oplocks and one max size write */ /* enough to enable echos and oplocks and one max size write */
req->sync_hdr.CreditRequest = cpu_to_le16(130); req->sync_hdr.CreditRequest = cpu_to_le16(130);
...@@ -1258,28 +1283,33 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data) ...@@ -1258,28 +1283,33 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data)
{ {
int rc = 0; int rc = 0;
struct cifs_ses *ses = sess_data->ses; struct cifs_ses *ses = sess_data->ses;
struct TCP_Server_Info *server = cifs_ses_server(ses);
mutex_lock(&ses->server->srv_mutex); mutex_lock(&server->srv_mutex);
if (ses->server->ops->generate_signingkey) { if (server->ops->generate_signingkey) {
rc = ses->server->ops->generate_signingkey(ses); rc = server->ops->generate_signingkey(ses);
if (rc) { if (rc) {
cifs_dbg(FYI, cifs_dbg(FYI,
"SMB3 session key generation failed\n"); "SMB3 session key generation failed\n");
mutex_unlock(&ses->server->srv_mutex); mutex_unlock(&server->srv_mutex);
return rc; return rc;
} }
} }
if (!ses->server->session_estab) { if (!server->session_estab) {
ses->server->sequence_number = 0x2; server->sequence_number = 0x2;
ses->server->session_estab = true; server->session_estab = true;
} }
mutex_unlock(&ses->server->srv_mutex); mutex_unlock(&server->srv_mutex);
cifs_dbg(FYI, "SMB2/3 session established successfully\n"); cifs_dbg(FYI, "SMB2/3 session established successfully\n");
/* keep existing ses state if binding */
if (!ses->binding) {
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
ses->status = CifsGood; ses->status = CifsGood;
ses->need_reconnect = false; ses->need_reconnect = false;
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
}
return rc; return rc;
} }
...@@ -1317,6 +1347,8 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) ...@@ -1317,6 +1347,8 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
goto out_put_spnego_key; goto out_put_spnego_key;
} }
/* keep session key if binding */
if (!ses->binding) {
ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
GFP_KERNEL); GFP_KERNEL);
if (!ses->auth_key.response) { if (!ses->auth_key.response) {
...@@ -1327,6 +1359,7 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) ...@@ -1327,6 +1359,7 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
goto out_put_spnego_key; goto out_put_spnego_key;
} }
ses->auth_key.len = msg->sesskey_len; ses->auth_key.len = msg->sesskey_len;
}
sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; sess_data->iov[1].iov_base = msg->data + msg->sesskey_len;
sess_data->iov[1].iov_len = msg->secblob_len; sess_data->iov[1].iov_len = msg->secblob_len;
...@@ -1336,9 +1369,11 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) ...@@ -1336,9 +1369,11 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
goto out_put_spnego_key; goto out_put_spnego_key;
rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
/* keep session id and flags if binding */
if (!ses->binding) {
ses->Suid = rsp->sync_hdr.SessionId; ses->Suid = rsp->sync_hdr.SessionId;
ses->session_flags = le16_to_cpu(rsp->SessionFlags); ses->session_flags = le16_to_cpu(rsp->SessionFlags);
}
rc = SMB2_sess_establish_session(sess_data); rc = SMB2_sess_establish_session(sess_data);
out_put_spnego_key: out_put_spnego_key:
...@@ -1432,9 +1467,11 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) ...@@ -1432,9 +1467,11 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
/* keep existing ses id and flags if binding */
if (!ses->binding) {
ses->Suid = rsp->sync_hdr.SessionId; ses->Suid = rsp->sync_hdr.SessionId;
ses->session_flags = le16_to_cpu(rsp->SessionFlags); ses->session_flags = le16_to_cpu(rsp->SessionFlags);
}
out: out:
kfree(ntlmssp_blob); kfree(ntlmssp_blob);
...@@ -1491,8 +1528,11 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data) ...@@ -1491,8 +1528,11 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
/* keep existing ses id and flags if binding */
if (!ses->binding) {
ses->Suid = rsp->sync_hdr.SessionId; ses->Suid = rsp->sync_hdr.SessionId;
ses->session_flags = le16_to_cpu(rsp->SessionFlags); ses->session_flags = le16_to_cpu(rsp->SessionFlags);
}
rc = SMB2_sess_establish_session(sess_data); rc = SMB2_sess_establish_session(sess_data);
out: out:
...@@ -1509,7 +1549,7 @@ SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data) ...@@ -1509,7 +1549,7 @@ SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data)
{ {
int type; int type;
type = smb2_select_sectype(ses->server, ses->sectype); type = smb2_select_sectype(cifs_ses_server(ses), ses->sectype);
cifs_dbg(FYI, "sess setup type %d\n", type); cifs_dbg(FYI, "sess setup type %d\n", type);
if (type == Unspecified) { if (type == Unspecified) {
cifs_dbg(VFS, cifs_dbg(VFS,
...@@ -1537,7 +1577,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, ...@@ -1537,7 +1577,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
const struct nls_table *nls_cp) const struct nls_table *nls_cp)
{ {
int rc = 0; int rc = 0;
struct TCP_Server_Info *server = ses->server; struct TCP_Server_Info *server = cifs_ses_server(ses);
struct SMB2_sess_data *sess_data; struct SMB2_sess_data *sess_data;
cifs_dbg(FYI, "Session Setup\n"); cifs_dbg(FYI, "Session Setup\n");
...@@ -1563,7 +1603,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, ...@@ -1563,7 +1603,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
/* /*
* Initialize the session hash with the server one. * Initialize the session hash with the server one.
*/ */
memcpy(ses->preauth_sha_hash, ses->server->preauth_sha_hash, memcpy(ses->preauth_sha_hash, server->preauth_sha_hash,
SMB2_PREAUTH_HASH_SIZE); SMB2_PREAUTH_HASH_SIZE);
while (sess_data->func) while (sess_data->func)
...@@ -1807,6 +1847,8 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) ...@@ -1807,6 +1847,8 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
if ((tcon->need_reconnect) || (tcon->ses->need_reconnect)) if ((tcon->need_reconnect) || (tcon->ses->need_reconnect))
return 0; return 0;
close_shroot(&tcon->crfid);
rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, (void **) &req, rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, (void **) &req,
&total_len); &total_len);
if (rc) if (rc)
...@@ -2661,7 +2703,7 @@ SMB2_ioctl_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, ...@@ -2661,7 +2703,7 @@ SMB2_ioctl_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
int rc; int rc;
char *in_data_buf; char *in_data_buf;
rc = smb2_plain_req_init(SMB2_IOCTL, tcon, (void **) &req, &total_len); rc = smb2_ioctl_req_init(opcode, tcon, (void **) &req, &total_len);
if (rc) if (rc)
return rc; return rc;
...@@ -2972,7 +3014,21 @@ int ...@@ -2972,7 +3014,21 @@ int
SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid) u64 persistent_fid, u64 volatile_fid)
{ {
return SMB2_close_flags(xid, tcon, persistent_fid, volatile_fid, 0); int rc;
int tmp_rc;
rc = SMB2_close_flags(xid, tcon, persistent_fid, volatile_fid, 0);
/* retry close in a worker thread if this one is interrupted */
if (rc == -EINTR) {
tmp_rc = smb2_handle_cancelled_close(tcon, persistent_fid,
volatile_fid);
if (tmp_rc)
cifs_dbg(VFS, "handle cancelled close fid 0x%llx returned error %d\n",
persistent_fid, tmp_rc);
}
return rc;
} }
int int
......
...@@ -1386,7 +1386,7 @@ struct smb2_oplock_break { ...@@ -1386,7 +1386,7 @@ struct smb2_oplock_break {
struct smb2_lease_break { struct smb2_lease_break {
struct smb2_sync_hdr sync_hdr; struct smb2_sync_hdr sync_hdr;
__le16 StructureSize; /* Must be 44 */ __le16 StructureSize; /* Must be 44 */
__le16 Reserved; __le16 Epoch;
__le32 Flags; __le32 Flags;
__u8 LeaseKey[16]; __u8 LeaseKey[16];
__le32 CurrentLeaseState; __le32 CurrentLeaseState;
......
...@@ -46,6 +46,7 @@ extern int smb2_verify_signature(struct smb_rqst *, struct TCP_Server_Info *); ...@@ -46,6 +46,7 @@ extern int smb2_verify_signature(struct smb_rqst *, struct TCP_Server_Info *);
extern int smb2_check_receive(struct mid_q_entry *mid, extern int smb2_check_receive(struct mid_q_entry *mid,
struct TCP_Server_Info *server, bool log_error); struct TCP_Server_Info *server, bool log_error);
extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses, extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses,
struct TCP_Server_Info *,
struct smb_rqst *rqst); struct smb_rqst *rqst);
extern struct mid_q_entry *smb2_setup_async_request( extern struct mid_q_entry *smb2_setup_async_request(
struct TCP_Server_Info *server, struct smb_rqst *rqst); struct TCP_Server_Info *server, struct smb_rqst *rqst);
...@@ -212,6 +213,9 @@ extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -212,6 +213,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, extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
const u64 persistent_fid, const u64 volatile_fid, const u64 persistent_fid, const u64 volatile_fid,
const __u8 oplock_level); const __u8 oplock_level);
extern int smb2_handle_cancelled_close(struct cifs_tcon *tcon,
__u64 persistent_fid,
__u64 volatile_fid);
extern int smb2_handle_cancelled_mid(char *buffer, extern int smb2_handle_cancelled_mid(char *buffer,
struct TCP_Server_Info *server); struct TCP_Server_Info *server);
void smb2_cancelled_close_fid(struct work_struct *work); void smb2_cancelled_close_fid(struct work_struct *work);
......
...@@ -98,6 +98,61 @@ smb311_crypto_shash_allocate(struct TCP_Server_Info *server) ...@@ -98,6 +98,61 @@ smb311_crypto_shash_allocate(struct TCP_Server_Info *server)
return rc; return rc;
} }
static
int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key)
{
struct cifs_chan *chan;
struct cifs_ses *ses = NULL;
int i;
int rc = 0;
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
if (ses->Suid == ses_id)
goto found;
}
}
cifs_server_dbg(VFS, "%s: Could not find session 0x%llx\n",
__func__, ses_id);
rc = -ENOENT;
goto out;
found:
if (ses->binding) {
/*
* If we are in the process of binding a new channel
* to an existing session, use the master connection
* session key
*/
memcpy(key, ses->smb3signingkey, SMB3_SIGN_KEY_SIZE);
goto out;
}
/*
* Otherwise, use the channel key.
*/
for (i = 0; i < ses->chan_count; i++) {
chan = ses->chans + i;
if (chan->server == server) {
memcpy(key, chan->signkey, SMB3_SIGN_KEY_SIZE);
goto out;
}
}
cifs_dbg(VFS,
"%s: Could not find channel signing key for session 0x%llx\n",
__func__, ses_id);
rc = -ENOENT;
out:
spin_unlock(&cifs_tcp_ses_lock);
return rc;
}
static struct cifs_ses * static struct cifs_ses *
smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id) smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id)
{ {
...@@ -328,21 +383,45 @@ generate_smb3signingkey(struct cifs_ses *ses, ...@@ -328,21 +383,45 @@ generate_smb3signingkey(struct cifs_ses *ses,
{ {
int rc; int rc;
/*
* All channels use the same encryption/decryption keys but
* they have their own signing key.
*
* When we generate the keys, check if it is for a new channel
* (binding) in which case we only need to generate a signing
* key and store it in the channel as to not overwrite the
* master connection signing key stored in the session
*/
if (ses->binding) {
rc = generate_key(ses, ptriplet->signing.label, rc = generate_key(ses, ptriplet->signing.label,
ptriplet->signing.context, ses->smb3signingkey, ptriplet->signing.context,
cifs_ses_binding_channel(ses)->signkey,
SMB3_SIGN_KEY_SIZE); SMB3_SIGN_KEY_SIZE);
if (rc) if (rc)
return rc; return rc;
} else {
rc = generate_key(ses, ptriplet->encryption.label, rc = generate_key(ses, ptriplet->signing.label,
ptriplet->encryption.context, ses->smb3encryptionkey, ptriplet->signing.context,
ses->smb3signingkey,
SMB3_SIGN_KEY_SIZE); SMB3_SIGN_KEY_SIZE);
if (rc) if (rc)
return rc; return rc;
memcpy(ses->chans[0].signkey, ses->smb3signingkey,
SMB3_SIGN_KEY_SIZE);
rc = generate_key(ses, ptriplet->encryption.label,
ptriplet->encryption.context,
ses->smb3encryptionkey,
SMB3_SIGN_KEY_SIZE);
rc = generate_key(ses, ptriplet->decryption.label, rc = generate_key(ses, ptriplet->decryption.label,
ptriplet->decryption.context, ptriplet->decryption.context,
ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE); ses->smb3decryptionkey,
SMB3_SIGN_KEY_SIZE);
if (rc)
return rc;
}
if (rc) if (rc)
return rc; return rc;
...@@ -431,21 +510,19 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -431,21 +510,19 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
unsigned char *sigptr = smb3_signature; unsigned char *sigptr = smb3_signature;
struct kvec *iov = rqst->rq_iov; struct kvec *iov = rqst->rq_iov;
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base; struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base;
struct cifs_ses *ses;
struct shash_desc *shash = &server->secmech.sdesccmacaes->shash; struct shash_desc *shash = &server->secmech.sdesccmacaes->shash;
struct smb_rqst drqst; struct smb_rqst drqst;
u8 key[SMB3_SIGN_KEY_SIZE];
ses = smb2_find_smb_ses(server, shdr->SessionId); rc = smb2_get_sign_key(shdr->SessionId, server, key);
if (!ses) { if (rc)
cifs_server_dbg(VFS, "%s: Could not find session\n", __func__);
return 0; return 0;
}
memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE); memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE);
memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE); memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
rc = crypto_shash_setkey(server->secmech.cmacaes, rc = crypto_shash_setkey(server->secmech.cmacaes,
ses->smb3signingkey, SMB2_CMACAES_SIZE); key, SMB2_CMACAES_SIZE);
if (rc) { if (rc) {
cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__); cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__);
return rc; return rc;
...@@ -494,16 +571,25 @@ static int ...@@ -494,16 +571,25 @@ static int
smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server) smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
{ {
int rc = 0; int rc = 0;
struct smb2_sync_hdr *shdr = struct smb2_sync_hdr *shdr;
(struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base; struct smb2_sess_setup_req *ssr;
bool is_binding;
bool is_signed;
if (!(shdr->Flags & SMB2_FLAGS_SIGNED) || shdr = (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
server->tcpStatus == CifsNeedNegotiate) ssr = (struct smb2_sess_setup_req *)shdr;
return rc;
if (!server->session_estab) { is_binding = shdr->Command == SMB2_SESSION_SETUP &&
(ssr->Flags & SMB2_SESSION_REQ_FLAG_BINDING);
is_signed = shdr->Flags & SMB2_FLAGS_SIGNED;
if (!is_signed)
return 0;
if (server->tcpStatus == CifsNeedNegotiate)
return 0;
if (!is_binding && !server->session_estab) {
strncpy(shdr->Signature, "BSRSPYL", 8); strncpy(shdr->Signature, "BSRSPYL", 8);
return rc; return 0;
} }
rc = server->ops->calc_signature(rqst, server); rc = server->ops->calc_signature(rqst, server);
...@@ -610,18 +696,18 @@ smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr, ...@@ -610,18 +696,18 @@ smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr,
} }
static int static int
smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_sync_hdr *shdr, smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server,
struct mid_q_entry **mid) struct smb2_sync_hdr *shdr, struct mid_q_entry **mid)
{ {
if (ses->server->tcpStatus == CifsExiting) if (server->tcpStatus == CifsExiting)
return -ENOENT; return -ENOENT;
if (ses->server->tcpStatus == CifsNeedReconnect) { if (server->tcpStatus == CifsNeedReconnect) {
cifs_dbg(FYI, "tcp session dead - return to caller to retry\n"); cifs_dbg(FYI, "tcp session dead - return to caller to retry\n");
return -EAGAIN; return -EAGAIN;
} }
if (ses->server->tcpStatus == CifsNeedNegotiate && if (server->tcpStatus == CifsNeedNegotiate &&
shdr->Command != SMB2_NEGOTIATE) shdr->Command != SMB2_NEGOTIATE)
return -EAGAIN; return -EAGAIN;
...@@ -638,11 +724,11 @@ smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_sync_hdr *shdr, ...@@ -638,11 +724,11 @@ smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_sync_hdr *shdr,
/* else ok - we are shutting down the session */ /* else ok - we are shutting down the session */
} }
*mid = smb2_mid_entry_alloc(shdr, ses->server); *mid = smb2_mid_entry_alloc(shdr, server);
if (*mid == NULL) if (*mid == NULL)
return -ENOMEM; return -ENOMEM;
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
list_add_tail(&(*mid)->qhead, &ses->server->pending_mid_q); list_add_tail(&(*mid)->qhead, &server->pending_mid_q);
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
return 0; return 0;
...@@ -675,24 +761,25 @@ smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, ...@@ -675,24 +761,25 @@ smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
} }
struct mid_q_entry * struct mid_q_entry *
smb2_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst) smb2_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server,
struct smb_rqst *rqst)
{ {
int rc; int rc;
struct smb2_sync_hdr *shdr = struct smb2_sync_hdr *shdr =
(struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base; (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
struct mid_q_entry *mid; struct mid_q_entry *mid;
smb2_seq_num_into_buf(ses->server, shdr); smb2_seq_num_into_buf(server, shdr);
rc = smb2_get_mid_entry(ses, shdr, &mid); rc = smb2_get_mid_entry(ses, server, shdr, &mid);
if (rc) { if (rc) {
revert_current_mid_from_hdr(ses->server, shdr); revert_current_mid_from_hdr(server, shdr);
return ERR_PTR(rc); return ERR_PTR(rc);
} }
rc = smb2_sign_rqst(rqst, ses->server); rc = smb2_sign_rqst(rqst, server);
if (rc) { if (rc) {
revert_current_mid_from_hdr(ses->server, shdr); revert_current_mid_from_hdr(server, shdr);
cifs_delete_mid(mid); cifs_delete_mid(mid);
return ERR_PTR(rc); return ERR_PTR(rc);
} }
......
...@@ -1069,7 +1069,7 @@ static int smbd_post_send_data( ...@@ -1069,7 +1069,7 @@ static int smbd_post_send_data(
if (n_vec > SMBDIRECT_MAX_SGE) { if (n_vec > SMBDIRECT_MAX_SGE) {
cifs_dbg(VFS, "Can't fit data to SGL, n_vec=%d\n", n_vec); cifs_dbg(VFS, "Can't fit data to SGL, n_vec=%d\n", n_vec);
return -ENOMEM; return -EINVAL;
} }
sg_init_table(sgl, n_vec); sg_init_table(sgl, n_vec);
...@@ -1476,6 +1476,7 @@ void smbd_destroy(struct TCP_Server_Info *server) ...@@ -1476,6 +1476,7 @@ void smbd_destroy(struct TCP_Server_Info *server)
info->transport_status = SMBD_DESTROYED; info->transport_status = SMBD_DESTROYED;
destroy_workqueue(info->workqueue); destroy_workqueue(info->workqueue);
log_rdma_event(INFO, "rdma session destroyed\n");
kfree(info); kfree(info);
} }
...@@ -1505,8 +1506,9 @@ int smbd_reconnect(struct TCP_Server_Info *server) ...@@ -1505,8 +1506,9 @@ int smbd_reconnect(struct TCP_Server_Info *server)
log_rdma_event(INFO, "creating rdma session\n"); log_rdma_event(INFO, "creating rdma session\n");
server->smbd_conn = smbd_get_connection( server->smbd_conn = smbd_get_connection(
server, (struct sockaddr *) &server->dstaddr); server, (struct sockaddr *) &server->dstaddr);
log_rdma_event(INFO, "created rdma session info=%p\n",
server->smbd_conn); if (server->smbd_conn)
cifs_dbg(VFS, "RDMA transport re-established\n");
return server->smbd_conn ? 0 : -ENOENT; return server->smbd_conn ? 0 : -ENOENT;
} }
...@@ -1970,7 +1972,7 @@ static int smbd_recv_buf(struct smbd_connection *info, char *buf, ...@@ -1970,7 +1972,7 @@ static int smbd_recv_buf(struct smbd_connection *info, char *buf,
if (info->transport_status != SMBD_CONNECTED) { if (info->transport_status != SMBD_CONNECTED) {
log_read(ERR, "disconnected\n"); log_read(ERR, "disconnected\n");
return 0; return -ECONNABORTED;
} }
goto again; goto again;
...@@ -2269,12 +2271,7 @@ static void smbd_mr_recovery_work(struct work_struct *work) ...@@ -2269,12 +2271,7 @@ static void smbd_mr_recovery_work(struct work_struct *work)
int rc; int rc;
list_for_each_entry(smbdirect_mr, &info->mr_list, list) { list_for_each_entry(smbdirect_mr, &info->mr_list, list) {
if (smbdirect_mr->state == MR_INVALIDATED) if (smbdirect_mr->state == MR_ERROR) {
ib_dma_unmap_sg(
info->id->device, smbdirect_mr->sgl,
smbdirect_mr->sgl_count,
smbdirect_mr->dir);
else if (smbdirect_mr->state == MR_ERROR) {
/* recover this MR entry */ /* recover this MR entry */
rc = ib_dereg_mr(smbdirect_mr->mr); rc = ib_dereg_mr(smbdirect_mr->mr);
...@@ -2602,9 +2599,18 @@ int smbd_deregister_mr(struct smbd_mr *smbdirect_mr) ...@@ -2602,9 +2599,18 @@ int smbd_deregister_mr(struct smbd_mr *smbdirect_mr)
*/ */
smbdirect_mr->state = MR_INVALIDATED; smbdirect_mr->state = MR_INVALIDATED;
if (smbdirect_mr->state == MR_INVALIDATED) {
ib_dma_unmap_sg(
info->id->device, smbdirect_mr->sgl,
smbdirect_mr->sgl_count,
smbdirect_mr->dir);
smbdirect_mr->state = MR_READY;
if (atomic_inc_return(&info->mr_ready_count) == 1)
wake_up_interruptible(&info->wait_mr);
} else
/* /*
* Schedule the work to do MR recovery for future I/Os * Schedule the work to do MR recovery for future I/Os MR
* MR recovery is slow and we don't want it to block the current I/O * recovery is slow and don't want it to block current I/O
*/ */
queue_work(info->workqueue, &info->mr_recovery_work); queue_work(info->workqueue, &info->mr_recovery_work);
......
...@@ -93,8 +93,14 @@ static void _cifs_mid_q_entry_release(struct kref *refcount) ...@@ -93,8 +93,14 @@ static void _cifs_mid_q_entry_release(struct kref *refcount)
__u16 smb_cmd = le16_to_cpu(midEntry->command); __u16 smb_cmd = le16_to_cpu(midEntry->command);
unsigned long now; unsigned long now;
unsigned long roundtrip_time; unsigned long roundtrip_time;
struct TCP_Server_Info *server = midEntry->server;
#endif #endif
struct TCP_Server_Info *server = midEntry->server;
if (midEntry->resp_buf && (midEntry->mid_flags & MID_WAIT_CANCELLED) &&
midEntry->mid_state == MID_RESPONSE_RECEIVED &&
server->ops->handle_cancelled_mid)
server->ops->handle_cancelled_mid(midEntry->resp_buf, server);
midEntry->mid_state = MID_FREE; midEntry->mid_state = MID_FREE;
atomic_dec(&midCount); atomic_dec(&midCount);
if (midEntry->large_buf) if (midEntry->large_buf)
...@@ -319,7 +325,10 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, ...@@ -319,7 +325,10 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
int val = 1; int val = 1;
__be32 rfc1002_marker; __be32 rfc1002_marker;
if (cifs_rdma_enabled(server) && server->smbd_conn) { if (cifs_rdma_enabled(server)) {
/* return -EAGAIN when connecting or reconnecting */
rc = -EAGAIN;
if (server->smbd_conn)
rc = smbd_send(server, num_rqst, rqst); rc = smbd_send(server, num_rqst, rqst);
goto smbd_done; goto smbd_done;
} }
...@@ -927,7 +936,8 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, ...@@ -927,7 +936,8 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
} }
struct mid_q_entry * struct mid_q_entry *
cifs_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst) cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *ignored,
struct smb_rqst *rqst)
{ {
int rc; int rc;
struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
...@@ -999,7 +1009,18 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, ...@@ -999,7 +1009,18 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
return -EIO; return -EIO;
} }
server = ses->server; if (!ses->binding) {
uint index = 0;
if (ses->chan_count > 1) {
index = (uint)atomic_inc_return(&ses->chan_seq);
index %= ses->chan_count;
}
server = ses->chans[index].server;
} else {
server = cifs_ses_server(ses);
}
if (server->tcpStatus == CifsExiting) if (server->tcpStatus == CifsExiting)
return -ENOENT; return -ENOENT;
...@@ -1044,7 +1065,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, ...@@ -1044,7 +1065,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
} }
for (i = 0; i < num_rqst; i++) { for (i = 0; i < num_rqst; i++) {
midQ[i] = server->ops->setup_request(ses, &rqst[i]); midQ[i] = server->ops->setup_request(ses, server, &rqst[i]);
if (IS_ERR(midQ[i])) { if (IS_ERR(midQ[i])) {
revert_current_mid(server, i); revert_current_mid(server, i);
for (j = 0; j < i; j++) for (j = 0; j < i; j++)
...@@ -1119,8 +1140,8 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, ...@@ -1119,8 +1140,8 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
midQ[i]->mid, le16_to_cpu(midQ[i]->command)); midQ[i]->mid, le16_to_cpu(midQ[i]->command));
send_cancel(server, &rqst[i], midQ[i]); send_cancel(server, &rqst[i], midQ[i]);
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED) {
midQ[i]->mid_flags |= MID_WAIT_CANCELLED; midQ[i]->mid_flags |= MID_WAIT_CANCELLED;
if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED) {
midQ[i]->callback = cifs_cancelled_callback; midQ[i]->callback = cifs_cancelled_callback;
cancelled_mid[i] = true; cancelled_mid[i] = true;
credits[i].value = 0; credits[i].value = 0;
...@@ -1287,7 +1308,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, ...@@ -1287,7 +1308,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
rc = allocate_mid(ses, in_buf, &midQ); rc = allocate_mid(ses, in_buf, &midQ);
if (rc) { if (rc) {
mutex_unlock(&ses->server->srv_mutex); mutex_unlock(&server->srv_mutex);
/* Update # of requests on wire to server */ /* Update # of requests on wire to server */
add_credits(server, &credits, 0); add_credits(server, &credits, 0);
return rc; 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