2012/6/20 Jeff Layton <jlayton@xxxxxxxxx>: > On Tue, 19 Jun 2012 16:39:52 +0400 > Pavel Shilovsky <pshilovsky@xxxxxxxxx> wrote: > >> From: Pavel Shilovsky <piastry@xxxxxxxxxxx> >> >> Signed-off-by: Pavel Shilovsky <piastry@xxxxxxxxxxx> >> --- >> fs/cifs/Makefile | 2 +- >> fs/cifs/cifsglob.h | 5 ++ >> fs/cifs/cifsproto.h | 3 +- >> fs/cifs/smb1ops.c | 1 + >> fs/cifs/smb2ops.c | 18 ++++++ >> fs/cifs/smb2pdu.h | 59 ++++++++++++++++++ >> fs/cifs/smb2proto.h | 5 ++ >> fs/cifs/smb2transport.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++ >> fs/cifs/transport.c | 13 ++-- >> 9 files changed, 248 insertions(+), 9 deletions(-) >> create mode 100644 fs/cifs/smb2transport.c >> >> diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile >> index 4a77271..a73d7f8 100644 >> --- a/fs/cifs/Makefile >> +++ b/fs/cifs/Makefile >> @@ -16,4 +16,4 @@ cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o >> >> cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o >> >> -cifs-$(CONFIG_CIFS_SMB2) += smb2ops.o smb2maperror.o >> +cifs-$(CONFIG_CIFS_SMB2) += smb2ops.o smb2maperror.o smb2transport.o >> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h >> index 1266e2e..ae27ab8 100644 >> --- a/fs/cifs/cifsglob.h >> +++ b/fs/cifs/cifsglob.h >> @@ -22,6 +22,7 @@ >> #include <linux/in.h> >> #include <linux/in6.h> >> #include <linux/slab.h> >> +#include <linux/mempool.h> >> #include <linux/workqueue.h> >> #include "cifs_fs_sb.h" >> #include "cifsacl.h" >> @@ -218,6 +219,7 @@ struct smb_version_values { >> size_t header_size; >> size_t max_header_size; >> size_t read_rsp_size; >> + __le16 lock_cmd; >> }; >> >> #define HEADER_SIZE(server) (server->vals->header_size) >> @@ -812,6 +814,7 @@ typedef void (mid_callback_t)(struct mid_q_entry *mid); >> /* one of these for every pending CIFS request to the server */ >> struct mid_q_entry { >> struct list_head qhead; /* mids waiting on reply from this server */ >> + struct TCP_Server_Info *server; /* server corresponding to this mid */ >> __u64 mid; /* multiplex id */ >> __u32 pid; /* process id */ >> __u32 sequence_number; /* for CIFS signing */ >> @@ -1153,6 +1156,8 @@ void cifs_oplock_break(struct work_struct *work); >> extern const struct slow_work_ops cifs_oplock_break_ops; >> extern struct workqueue_struct *cifsiod_wq; >> >> +extern mempool_t *cifs_mid_poolp; >> + >> /* Operations for different SMB versions */ >> #define SMB1_VERSION_STRING "1.0" >> extern struct smb_version_operations smb1_operations; >> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h >> index 2f4f661..c45adfa 100644 >> --- a/fs/cifs/cifsproto.h >> +++ b/fs/cifs/cifsproto.h >> @@ -36,7 +36,7 @@ extern void cifs_buf_release(void *); >> extern struct smb_hdr *cifs_small_buf_get(void); >> extern void cifs_small_buf_release(void *); >> extern int smb_send(struct TCP_Server_Info *, struct smb_hdr *, >> - unsigned int /* length */); >> + unsigned int /* length */); > > Unneeded change? Yes :) > >> extern unsigned int _GetXid(void); >> extern void _FreeXid(unsigned int); >> #define GetXid() \ >> @@ -68,6 +68,7 @@ extern char *cifs_compose_mount_options(const char *sb_mountdata, >> extern struct mid_q_entry *AllocMidQEntry(const struct smb_hdr *smb_buffer, >> struct TCP_Server_Info *server); >> extern void DeleteMidQEntry(struct mid_q_entry *midEntry); >> +extern void cifs_wake_up_task(struct mid_q_entry *mid); >> extern int cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov, >> unsigned int nvec, mid_receive_t *receive, >> mid_callback_t *callback, void *cbdata, >> diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c >> index 1a3f08c..cd7fbfd 100644 >> --- a/fs/cifs/smb1ops.c >> +++ b/fs/cifs/smb1ops.c >> @@ -445,4 +445,5 @@ struct smb_version_values smb1_values = { >> .header_size = sizeof(struct smb_hdr), >> .max_header_size = MAX_CIFS_HDR_SIZE, >> .read_rsp_size = sizeof(READ_RSP), >> + .lock_cmd = cpu_to_le16(SMB_COM_LOCKING_ANDX), >> }; >> diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c >> index f065e89..80d9674 100644 >> --- a/fs/cifs/smb2ops.c >> +++ b/fs/cifs/smb2ops.c >> @@ -18,10 +18,28 @@ >> */ >> >> #include "cifsglob.h" >> +#include "smb2pdu.h" >> +#include "smb2proto.h" >> + >> +static __u64 >> +smb2_get_next_mid(struct TCP_Server_Info *server) >> +{ >> + __u64 mid; >> + /* for SMB2 we need the current value */ >> + spin_lock(&GlobalMid_Lock); >> + mid = server->CurrentMid; >> + server->CurrentMid++; > > mid = server->CurrentMid++; > >> + spin_unlock(&GlobalMid_Lock); >> + return mid; >> +} >> >> struct smb_version_operations smb21_operations = { >> + .setup_request = smb2_setup_request, >> + .check_receive = smb2_check_receive, >> + .get_next_mid = smb2_get_next_mid, >> }; >> >> struct smb_version_values smb21_values = { >> .version_string = SMB21_VERSION_STRING, >> + .lock_cmd = SMB2_LOCK, >> }; >> diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h >> index d35ac68..ed73201 100644 >> --- a/fs/cifs/smb2pdu.h >> +++ b/fs/cifs/smb2pdu.h >> @@ -27,6 +27,65 @@ >> #include <net/sock.h> >> >> /* >> + * Note that, due to trying to use names similar to the protocol specifications, >> + * there are many mixed case field names in the structures below. Although >> + * this does not match typical Linux kernel style, it is necessary to be >> + * be able to match against the protocol specfication. >> + * >> + * SMB2 commands >> + * Some commands have minimal (wct=0,bcc=0), or uninteresting, responses >> + * (ie no useful data other than the SMB error code itself) and are marked such. >> + * Knowing this helps avoid response buffer allocations and copy in some cases. >> + */ >> + >> +/* List of commands in host endian */ >> +#define SMB2NEGOTIATE 0x0000 >> +#define SMB2SESSION_SETUP 0x0001 >> +#define SMB2LOGOFF 0x0002 /* trivial request/resp */ >> +#define SMB2TREE_CONNECT 0x0003 >> +#define SMB2TREE_DISCONNECT 0x0004 /* trivial req/resp */ >> +#define SMB2CREATE 0x0005 >> +#define SMB2CLOSE 0x0006 >> +#define SMB2FLUSH 0x0007 /* trivial resp */ >> +#define SMB2READ 0x0008 >> +#define SMB2WRITE 0x0009 >> +#define SMB2LOCK 0x000A >> +#define SMB2IOCTL 0x000B >> +#define SMB2CANCEL 0x000C >> +#define SMB2ECHO 0x000D >> +#define SMB2QUERY_DIRECTORY 0x000E >> +#define SMB2CHANGE_NOTIFY 0x000F >> +#define SMB2QUERY_INFO 0x0010 >> +#define SMB2SET_INFO 0x0011 >> +#define SMB2OPLOCK_BREAK 0x0012 >> + > > I know that this is bikeshedding, but there's little difference between > these names and the ones below. That is going to result in bugs when we > get these mixed up (and we will). > > Perhaps you should give the ones above more distinct names -- > SMB2_NEGOTIATE_HE or something? Where _HE means host-endian? > >> +/* The same list in little endian */ >> +#define SMB2_NEGOTIATE cpu_to_le16(SMB2NEGOTIATE) >> +#define SMB2_SESSION_SETUP cpu_to_le16(SMB2SESSION_SETUP) >> +#define SMB2_LOGOFF cpu_to_le16(SMB2LOGOFF) >> +#define SMB2_TREE_CONNECT cpu_to_le16(SMB2TREE_CONNECT) >> +#define SMB2_TREE_DISCONNECT cpu_to_le16(SMB2TREE_DISCONNECT) >> +#define SMB2_CREATE cpu_to_le16(SMB2CREATE) >> +#define SMB2_CLOSE cpu_to_le16(SMB2CLOSE) >> +#define SMB2_FLUSH cpu_to_le16(SMB2FLUSH) >> +#define SMB2_READ cpu_to_le16(SMB2READ) >> +#define SMB2_WRITE cpu_to_le16(SMB2WRITE) >> +#define SMB2_LOCK cpu_to_le16(SMB2LOCK) >> +#define SMB2_IOCTL cpu_to_le16(SMB2IOCTL) >> +#define SMB2_CANCEL cpu_to_le16(SMB2CANCEL) >> +#define SMB2_ECHO cpu_to_le16(SMB2ECHO) >> +#define SMB2_QUERY_DIRECTORY cpu_to_le16(SMB2QUERY_DIRECTORY) >> +#define SMB2_CHANGE_NOTIFY cpu_to_le16(SMB2CHANGE_NOTIFY) >> +#define SMB2_QUERY_INFO cpu_to_le16(SMB2QUERY_INFO) >> +#define SMB2_SET_INFO cpu_to_le16(SMB2SET_INFO) >> +#define SMB2_OPLOCK_BREAK cpu_to_le16(SMB2OPLOCK_BREAK) >> + >> +#define NUMBER_OF_SMB2_COMMANDS 0x0013 >> + >> +/* BB FIXME - analyze following length BB */ >> +#define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */ >> + > > Would it be better to define this in terms of sizeof(SMB2_LARGEST_HEADER_STRUCT) ? Now we only define smb2_hdr structure - we can fix this value further than SMB2_LARGEST_HEADER_STRUCT appears. > >> +/* >> * SMB2 Header Definition >> * >> * "MBZ" : Must be Zero >> diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h >> index 08249ee..0e59afb 100644 >> --- a/fs/cifs/smb2proto.h >> +++ b/fs/cifs/smb2proto.h >> @@ -34,4 +34,9 @@ struct statfs; >> */ >> extern int map_smb2_to_linux_error(char *buf, bool log_err); >> >> +extern int smb2_check_receive(struct mid_q_entry *mid, >> + struct TCP_Server_Info *server, bool log_error); >> +extern int smb2_setup_request(struct cifs_ses *ses, struct kvec *iov, >> + unsigned int nvec, struct mid_q_entry **ret_mid); >> + >> #endif /* _SMB2PROTO_H */ >> diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c >> new file mode 100644 >> index 0000000..74830f2 >> --- /dev/null >> +++ b/fs/cifs/smb2transport.c >> @@ -0,0 +1,151 @@ >> +/* >> + * fs/cifs/smb2transport.c >> + * >> + * Copyright (C) International Business Machines Corp., 2002, 2011 >> + * Etersoft, 2012 >> + * Author(s): Steve French (sfrench@xxxxxxxxxx) >> + * Jeremy Allison (jra@xxxxxxxxx) 2006 >> + * Pavel Shilovsky (pshilovsky@xxxxxxxxx) 2012 >> + * >> + * This library is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU Lesser General Public License as published >> + * by the Free Software Foundation; either version 2.1 of the License, or >> + * (at your option) any later version. >> + * >> + * This library is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See >> + * the GNU Lesser General Public License for more details. >> + * >> + * You should have received a copy of the GNU Lesser General Public License >> + * along with this library; if not, write to the Free Software >> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA >> + */ >> + >> +#include <linux/fs.h> >> +#include <linux/list.h> >> +#include <linux/wait.h> >> +#include <linux/net.h> >> +#include <linux/delay.h> >> +#include <linux/uaccess.h> >> +#include <asm/processor.h> >> +#include <linux/mempool.h> >> +#include "smb2pdu.h" >> +#include "cifsglob.h" >> +#include "cifsproto.h" >> +#include "smb2proto.h" >> +#include "cifs_debug.h" >> +#include "smb2status.h" >> + >> +/* >> + * Set message id for the request. Should be called after wait_for_free_request >> + * and when srv_mutex is held. iov array must have at least 1 element. >> + */ >> +static inline void >> +smb2_seq_num_into_buf(struct TCP_Server_Info *server, struct kvec *iov) > > Why pass in a kvec here? Maybe a smb2_hdr pointer instead? Yes, it makes sense. > >> +{ >> + ((struct smb2_hdr *)iov[0].iov_base)->MessageId = get_next_mid(server); >> +} >> + >> +static struct mid_q_entry * >> +smb2_mid_entry_alloc(const struct smb2_hdr *smb_buffer, >> + struct TCP_Server_Info *server) >> +{ >> + struct mid_q_entry *temp; >> + >> + if (server == NULL) { >> + cERROR(1, "Null TCP session in smb2_mid_entry_alloc"); >> + return NULL; >> + } >> + >> + temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS); >> + if (temp == NULL) >> + return temp; >> + else { >> + memset(temp, 0, sizeof(struct mid_q_entry)); >> + temp->mid = smb_buffer->MessageId; /* always LE */ >> + temp->pid = current->pid; >> + temp->command = smb_buffer->Command; /* Always LE */ >> + temp->when_alloc = jiffies; >> + temp->server = server; >> + >> + /* >> + * The default is for the mid to be synchronous, so the >> + * default callback just wakes up the current task. >> + */ >> + temp->callback = cifs_wake_up_task; >> + temp->callback_data = current; >> + } >> + >> + atomic_inc(&midCount); >> + temp->mid_state = MID_REQUEST_ALLOCATED; >> + return temp; >> +} >> + >> +static int >> +smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_hdr *buf, >> + struct mid_q_entry **mid) >> +{ >> + if (ses->server->tcpStatus == CifsExiting) >> + return -ENOENT; >> + >> + if (ses->server->tcpStatus == CifsNeedReconnect) { >> + cFYI(1, "tcp session dead - return to caller to retry"); >> + return -EAGAIN; >> + } >> + >> + if (ses->status != CifsGood) { >> + /* check if SMB2 session is bad because we are setting it up */ >> + if ((buf->Command != SMB2_SESSION_SETUP) && >> + (buf->Command != SMB2_NEGOTIATE)) >> + return -EAGAIN; >> + /* else ok - we are setting up session */ >> + } >> + *mid = smb2_mid_entry_alloc(buf, ses->server); >> + if (*mid == NULL) >> + return -ENOMEM; >> + spin_lock(&GlobalMid_Lock); >> + list_add_tail(&(*mid)->qhead, &ses->server->pending_mid_q); >> + spin_unlock(&GlobalMid_Lock); >> + return 0; >> +} >> + >> +int >> +smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, >> + bool log_error) >> +{ >> + unsigned int len = get_rfc1002_length(mid->resp_buf); >> + >> + dump_smb(mid->resp_buf, min_t(u32, 80, len)); >> + /* convert the length into a more usable form */ >> + /* BB - uncomment with SMB2 signing implementation */ >> + /* if ((len > 24) && >> + (server->sec_mode & (SECMODE_SIGN_REQUIRED|SECMODE_SIGN_ENABLED))) { >> + if (smb2_verify_signature(mid->resp_buf, server)) >> + cERROR(1, "Unexpected SMB signature"); >> + } */ >> + >> + return map_smb2_to_linux_error(mid->resp_buf, log_error); >> +} >> + >> +int >> +smb2_setup_request(struct cifs_ses *ses, struct kvec *iov, >> + unsigned int nvec, struct mid_q_entry **ret_mid) >> +{ >> + int rc; >> + struct smb2_hdr *hdr = (struct smb2_hdr *)iov[0].iov_base; >> + struct mid_q_entry *mid; >> + >> + smb2_seq_num_into_buf(ses->server, iov); >> + >> + rc = smb2_get_mid_entry(ses, hdr, &mid); >> + if (rc) >> + return rc; >> + /* rc = smb2_sign_smb2(iov, nvec, ses->server); >> + if (rc) >> + delete_mid(mid); */ >> + *ret_mid = mid; >> + return rc; >> +} >> + >> +/* BB add missing functions here */ >> diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c >> index 54cd8dd..ea86753 100644 >> --- a/fs/cifs/transport.c >> +++ b/fs/cifs/transport.c >> @@ -35,10 +35,8 @@ >> #include "cifsproto.h" >> #include "cifs_debug.h" >> >> -extern mempool_t *cifs_mid_poolp; >> - >> -static void >> -wake_up_task(struct mid_q_entry *mid) >> +void >> +cifs_wake_up_task(struct mid_q_entry *mid) >> { >> wake_up_process(mid->callback_data); >> } >> @@ -65,12 +63,13 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) >> /* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */ >> /* when mid allocated can be before when sent */ >> temp->when_alloc = jiffies; >> + temp->server = server; >> >> /* >> * The default is for the mid to be synchronous, so the >> * default callback just wakes up the current task. >> */ >> - temp->callback = wake_up_task; >> + temp->callback = cifs_wake_up_task; >> temp->callback_data = current; >> } >> >> @@ -83,6 +82,7 @@ void >> DeleteMidQEntry(struct mid_q_entry *midEntry) >> { >> #ifdef CONFIG_CIFS_STATS2 >> + __le16 command = midEntry->server->vals->lock_cmd; >> unsigned long now; >> #endif >> midEntry->mid_state = MID_FREE; >> @@ -96,8 +96,7 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) >> /* commands taking longer than one second are indications that >> something is wrong, unless it is quite a slow link or server */ >> if ((now - midEntry->when_alloc) > HZ) { >> - if ((cifsFYI & CIFS_TIMER) && >> - (midEntry->command != cpu_to_le16(SMB_COM_LOCKING_ANDX))) { >> + if ((cifsFYI & CIFS_TIMER) && (midEntry->command != command)) { >> printk(KERN_DEBUG " CIFS slow rsp: cmd %d mid %llu", >> midEntry->command, midEntry->mid); >> printk(" A: 0x%lx S: 0x%lx R: 0x%lx\n", > > > -- > Jeff Layton <jlayton@xxxxxxxxx> -- Best regards, Pavel Shilovsky. -- To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html