Commit 97bc00b39408a4180eeeaa976d02d37121488997

Authored by Jeff Layton
Committed by Steve French
1 parent b8eed28375

cifs: teach smb_send_rqst how to handle arrays of pages

Add code that allows smb_send_rqst to send an array of pages after the
initial kvec array has been sent. For now, we simply kmap the page
array and send it using the standard smb_send_kvec function. Eventually,
we may want to convert this code to use kernel_sendpage under the hood
and avoid the kmap altogether for the page data.

Reviewed-by: Pavel Shilovsky <pshilovsky@samba.org>
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <smfrench@gmail.com>

Showing 1 changed file with 54 additions and 2 deletions Side-by-side Diff

... ... @@ -28,6 +28,7 @@
28 28 #include <linux/delay.h>
29 29 #include <linux/freezer.h>
30 30 #include <linux/tcp.h>
  31 +#include <linux/highmem.h>
31 32 #include <asm/uaccess.h>
32 33 #include <asm/processor.h>
33 34 #include <linux/mempool.h>
... ... @@ -240,6 +241,38 @@
240 241 return rc;
241 242 }
242 243  
  244 +/**
  245 + * rqst_page_to_kvec - Turn a slot in the smb_rqst page array into a kvec
  246 + * @rqst: pointer to smb_rqst
  247 + * @idx: index into the array of the page
  248 + * @iov: pointer to struct kvec that will hold the result
  249 + *
  250 + * Helper function to convert a slot in the rqst->rq_pages array into a kvec.
  251 + * The page will be kmapped and the address placed into iov_base. The length
  252 + * will then be adjusted according to the ptailoff.
  253 + */
  254 +static void
  255 +cifs_rqst_page_to_kvec(struct smb_rqst *rqst, unsigned int idx,
  256 + struct kvec *iov)
  257 +{
  258 + /*
  259 + * FIXME: We could avoid this kmap altogether if we used
  260 + * kernel_sendpage instead of kernel_sendmsg. That will only
  261 + * work if signing is disabled though as sendpage inlines the
  262 + * page directly into the fraglist. If userspace modifies the
  263 + * page after we calculate the signature, then the server will
  264 + * reject it and may break the connection. kernel_sendmsg does
  265 + * an extra copy of the data and avoids that issue.
  266 + */
  267 + iov->iov_base = kmap(rqst->rq_pages[idx]);
  268 +
  269 + /* if last page, don't send beyond this offset into page */
  270 + if (idx == (rqst->rq_npages - 1))
  271 + iov->iov_len = rqst->rq_tailsz;
  272 + else
  273 + iov->iov_len = rqst->rq_pagesz;
  274 +}
  275 +
243 276 static int
244 277 smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)
245 278 {
... ... @@ -247,7 +280,8 @@
247 280 struct kvec *iov = rqst->rq_iov;
248 281 int n_vec = rqst->rq_nvec;
249 282 unsigned int smb_buf_length = get_rfc1002_length(iov[0].iov_base);
250   - size_t total_len;
  283 + unsigned int i;
  284 + size_t total_len = 0, sent;
251 285 struct socket *ssocket = server->ssocket;
252 286 int val = 1;
253 287  
254 288  
... ... @@ -258,8 +292,26 @@
258 292 kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,
259 293 (char *)&val, sizeof(val));
260 294  
261   - rc = smb_send_kvec(server, iov, n_vec, &total_len);
  295 + rc = smb_send_kvec(server, iov, n_vec, &sent);
  296 + if (rc < 0)
  297 + goto uncork;
262 298  
  299 + total_len += sent;
  300 +
  301 + /* now walk the page array and send each page in it */
  302 + for (i = 0; i < rqst->rq_npages; i++) {
  303 + struct kvec p_iov;
  304 +
  305 + cifs_rqst_page_to_kvec(rqst, i, &p_iov);
  306 + rc = smb_send_kvec(server, &p_iov, 1, &sent);
  307 + kunmap(rqst->rq_pages[i]);
  308 + if (rc < 0)
  309 + break;
  310 +
  311 + total_len += sent;
  312 + }
  313 +
  314 +uncork:
263 315 /* uncork it */
264 316 val = 0;
265 317 kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,