|
@@ -28,6 +28,7 @@
|
|
|
#include <linux/delay.h>
|
|
|
#include <linux/freezer.h>
|
|
|
#include <linux/tcp.h>
|
|
|
+#include <linux/highmem.h>
|
|
|
#include <asm/uaccess.h>
|
|
|
#include <asm/processor.h>
|
|
|
#include <linux/mempool.h>
|
|
@@ -240,6 +241,38 @@ smb_send_kvec(struct TCP_Server_Info *server, struct kvec *iov, size_t n_vec,
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * rqst_page_to_kvec - Turn a slot in the smb_rqst page array into a kvec
|
|
|
+ * @rqst: pointer to smb_rqst
|
|
|
+ * @idx: index into the array of the page
|
|
|
+ * @iov: pointer to struct kvec that will hold the result
|
|
|
+ *
|
|
|
+ * Helper function to convert a slot in the rqst->rq_pages array into a kvec.
|
|
|
+ * The page will be kmapped and the address placed into iov_base. The length
|
|
|
+ * will then be adjusted according to the ptailoff.
|
|
|
+ */
|
|
|
+static void
|
|
|
+cifs_rqst_page_to_kvec(struct smb_rqst *rqst, unsigned int idx,
|
|
|
+ struct kvec *iov)
|
|
|
+{
|
|
|
+ /*
|
|
|
+ * FIXME: We could avoid this kmap altogether if we used
|
|
|
+ * kernel_sendpage instead of kernel_sendmsg. That will only
|
|
|
+ * work if signing is disabled though as sendpage inlines the
|
|
|
+ * page directly into the fraglist. If userspace modifies the
|
|
|
+ * page after we calculate the signature, then the server will
|
|
|
+ * reject it and may break the connection. kernel_sendmsg does
|
|
|
+ * an extra copy of the data and avoids that issue.
|
|
|
+ */
|
|
|
+ iov->iov_base = kmap(rqst->rq_pages[idx]);
|
|
|
+
|
|
|
+ /* if last page, don't send beyond this offset into page */
|
|
|
+ if (idx == (rqst->rq_npages - 1))
|
|
|
+ iov->iov_len = rqst->rq_tailsz;
|
|
|
+ else
|
|
|
+ iov->iov_len = rqst->rq_pagesz;
|
|
|
+}
|
|
|
+
|
|
|
static int
|
|
|
smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)
|
|
|
{
|
|
@@ -247,7 +280,8 @@ smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)
|
|
|
struct kvec *iov = rqst->rq_iov;
|
|
|
int n_vec = rqst->rq_nvec;
|
|
|
unsigned int smb_buf_length = get_rfc1002_length(iov[0].iov_base);
|
|
|
- size_t total_len;
|
|
|
+ unsigned int i;
|
|
|
+ size_t total_len = 0, sent;
|
|
|
struct socket *ssocket = server->ssocket;
|
|
|
int val = 1;
|
|
|
|
|
@@ -258,8 +292,26 @@ smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)
|
|
|
kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,
|
|
|
(char *)&val, sizeof(val));
|
|
|
|
|
|
- rc = smb_send_kvec(server, iov, n_vec, &total_len);
|
|
|
+ rc = smb_send_kvec(server, iov, n_vec, &sent);
|
|
|
+ if (rc < 0)
|
|
|
+ goto uncork;
|
|
|
+
|
|
|
+ total_len += sent;
|
|
|
+
|
|
|
+ /* now walk the page array and send each page in it */
|
|
|
+ for (i = 0; i < rqst->rq_npages; i++) {
|
|
|
+ struct kvec p_iov;
|
|
|
+
|
|
|
+ cifs_rqst_page_to_kvec(rqst, i, &p_iov);
|
|
|
+ rc = smb_send_kvec(server, &p_iov, 1, &sent);
|
|
|
+ kunmap(rqst->rq_pages[i]);
|
|
|
+ if (rc < 0)
|
|
|
+ break;
|
|
|
+
|
|
|
+ total_len += sent;
|
|
|
+ }
|
|
|
|
|
|
+uncork:
|
|
|
/* uncork it */
|
|
|
val = 0;
|
|
|
kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,
|