@@ -1,7 +1,7 @@
/*
* fs/cifs/cifsfs.c
*
- * Copyright (C) International Business Machines Corp., 2002,2008
+ * Copyright (C) International Business Machines Corp., 2002,2011
* Author(s): Steve French (sfrench@us.ibm.com)
*
* Common Internet FileSystem (CIFS) client
@@ -88,6 +88,11 @@ MODULE_PARM_DESC(sign_zero_copy, "Don't copy pages
on write with signing "
extern mempool_t *cifs_sm_req_poolp;
extern mempool_t *cifs_req_poolp;
extern mempool_t *cifs_mid_poolp;
+#ifdef CONFIG_CIFS_SMB2
+extern mempool_t *smb2_mid_poolp;
+mempool_t *smb2_mid_poolp;
+static struct kmem_cache *smb2_mid_cachep;
+#endif /* CONFIG_CIFS_SMB2 */
void
cifs_sb_active(struct super_block *sb)
@@ -968,6 +973,25 @@ cifs_init_mids(void)
return -ENOMEM;
}
+#ifdef CONFIG_CIFS_SMB2
+ smb2_mid_cachep = kmem_cache_create("smb2_mpx_ids",
+ sizeof(struct smb2_mid_entry), 0,
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (smb2_mid_cachep == NULL) {
+ mempool_destroy(cifs_mid_poolp);
+ kmem_cache_destroy(cifs_mid_cachep);
+ return -ENOMEM;
+ }
+
+ smb2_mid_poolp = mempool_create_slab_pool(3, smb2_mid_cachep);
+ if (smb2_mid_poolp == NULL) {
+ mempool_destroy(cifs_mid_poolp);
+ kmem_cache_destroy(cifs_mid_cachep);
+ kmem_cache_destroy(smb2_mid_cachep);
+ return -ENOMEM;
+ }
+#endif /* CONFIG_CIFS_SMB2 */
+
return 0;
}
@@ -591,20 +591,20 @@ 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 */
- __u16 mid; /* multiplex id */
- __u16 pid; /* process id */
- __u32 sequence_number; /* for CIFS signing */
+ int midState; /* wish this were enum but can not pass to wait_event */
unsigned long when_alloc; /* when mid was created */
#ifdef CONFIG_CIFS_STATS2
unsigned long when_sent; /* time when smb send finished */
unsigned long when_received; /* when demux complete (taken off wire) */
#endif
+ bool largeBuf:1; /* if valid response, is pointer to large buf */
mid_callback_t *callback; /* call completion callback */
void *callback_data; /* general purpose pointer for callback */
+ __u16 mid; /* multiplex id */
+ __u32 sequence_number; /* for CIFS signing */
+ __u8 command; /* smb command code */
+ __u16 pid; /* process id */
struct smb_hdr *resp_buf; /* response buffer */
- int midState; /* wish this were enum but can not pass to wait_event */
- __u8 command; /* smb command code */
- bool largeBuf:1; /* if valid response, is pointer to large buf */
bool multiRsp:1; /* multiple trans2 responses for one request */
bool multiEnd:1; /* both received */
};
@@ -160,29 +160,36 @@ struct page_req {
struct mid_q_entry *midq; /* queue structure for demultiplex */
};
+struct smb2_mid_entry;
+
+typedef void (smb2_mid_callback_t)(struct smb2_mid_entry *mid);
+
/* one of these for every pending SMB2 request to the server */
struct smb2_mid_entry {
struct list_head qhead; /* mids waiting on reply from this server */
- __u64 mid; /* multiplex id(s) */
- __u16 pid; /* process id */
- __u32 sequence_number; /* for signing */ /* BB check if needed */
+ int mid_state; /* wish this were enum but can not pass to wait_event */
unsigned long when_alloc; /* when mid was created */
#ifdef CONFIG_CIFS_STATS2
unsigned long when_sent; /* time when smb send finished */
unsigned long when_received; /* when demux complete (taken off wire) */
#endif
- struct task_struct *tsk; /* task waiting for response */
+ bool large_buf:1; /* if valid response, is pointer to large buf */
+ smb2_mid_callback_t *callback;
+ void *callback_data;
+ __u64 mid; /* multiplex id(s), bigger for smb2 */
+ __le16 command; /* smb2 command code */
+ __u32 pid; /* process id - bigger for smb2 than cifs */
struct smb2_hdr *resp_buf; /* response buffer */
+
+ /* Additional fields below needed for handling async smb2 responses
+ and for asynchronous smb2_writepages support have been temporarily
+ removed from the port and will be reenabled as that gets merged in */
+
+#if 0 /* Fields needed for smb2_writepages, compound ops, async support */
char **pagebuf_list; /* response buffer */
int num_pages;
- int mid_state; /* wish this were enum but can not pass to wait_event */
- __le16 command; /* smb command code */
bool async_resp_rcvd:1; /* if server has responded with interim resp */
- bool large_buf:1; /* if valid response, is pointer to large buf */
bool is_kmap_buf:1;
-/* bool multi_rsp:1; BB do we have to account for something in SMB2 like
- we saw multiple trans2 responses for one request (possible in CIFS) */
- /* Async things */
__u64 *mid_list; /* multiplex id(s) */
int *mid_state_list;
short int *large_buf_list;
@@ -196,8 +203,7 @@ struct smb2_mid_entry {
bool complex_mid:1; /* complex entry - consists of several messages */
int result;
unsigned long last_rsp_time;
- int (*callback)(struct smb2_mid_entry * , void *);
- void *callback_data;
+#endif
};
@@ -187,8 +187,6 @@ extern int new_read_req(struct kvec *iov, struct
cifs_tcon *tcon,
const unsigned int count, const __u64 lseek,
unsigned int remaining_bytes,
int request_type);
-extern int allocate_mid(struct cifs_ses *ses, struct smb2_hdr *in_buf,
- struct mid_q_entry **ppmidq);
extern int smb2_sendv(struct TCP_Server_Info *server, struct kvec *iov,
int n_vec);
extern int wait_for_free_request(struct cifs_ses *ses, const int long_op);
@@ -35,6 +35,8 @@
#include "cifs_debug.h"
#include "smb2status.h"
+extern mempool_t *smb2_mid_poolp;
+
/*
* Send an (optionally, already signed) SMB2 request over a socket.
* This socket is already locked (by a mutex) by the caller so we
@@ -137,4 +139,72 @@ smb2_send_complex(const unsigned int xid, struct
cifs_ses *ses,
return rc;
}
+static void
+wake_up_smb2_task(struct smb2_mid_entry *mid)
+{
+ wake_up_process(mid->callback_data);
+}
+
+static struct smb2_mid_entry *
+smb2_mid_entry_alloc(const struct smb2_hdr *smb_buffer,
+ struct TCP_Server_Info *server)
+{
+ struct smb2_mid_entry *temp;
+
+ if (server == NULL) {
+ cERROR(1, "Null TCP session in smb2_mid_entry_alloc");
+ return NULL;
+ }
+
+ temp = mempool_alloc(smb2_mid_poolp, GFP_NOFS);
+ if (temp == NULL)
+ return temp;
+ else {
+ memset(temp, 0, sizeof(struct smb2_mid_entry));
+ temp->mid = smb_buffer->MessageId; /* always LE */
+ temp->pid = current->pid;
+ temp->command = smb_buffer->Command; /* Always LE */
+ temp->when_alloc = jiffies;
+
+ /*
+ * The default is for the mid to be synchronous, so the
+ * default callback just wakes up the current task.
+ */
+ temp->callback = wake_up_smb2_task;
+ temp->callback_data = current;
+ }
+
+ atomic_inc(&midCount);
+ temp->mid_state = MID_REQUEST_ALLOCATED;
+ return temp;
+}
+
+static int get_smb2_mid(struct cifs_ses *ses, struct smb2_hdr *in_buf,
+ struct smb2_mid_entry **ppmidQ)
+{
+ 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 SMB session is bad because we are setting it up */
+ if ((in_buf->Command != SMB2_SESSION_SETUP) &&
+ (in_buf->Command != SMB2_NEGOTIATE))
+ return -EAGAIN;
+ /* else ok - we are setting up session */
+ }
+ *ppmidQ = smb2_mid_entry_alloc(in_buf, ses->server);
+ if (*ppmidQ == NULL)
+ return -ENOMEM;
+ spin_lock(&GlobalMid_Lock);
+ list_add_tail(&(*ppmidQ)->qhead, &ses->server->pending_mid_q);
+ spin_unlock(&GlobalMid_Lock);
+ return 0;
+}
+
+