aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/cifs/transport.c56
1 files changed, 54 insertions, 2 deletions
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 8ff4c5f36702..381dbb778478 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -28,6 +28,7 @@
28#include <linux/delay.h> 28#include <linux/delay.h>
29#include <linux/freezer.h> 29#include <linux/freezer.h>
30#include <linux/tcp.h> 30#include <linux/tcp.h>
31#include <linux/highmem.h>
31#include <asm/uaccess.h> 32#include <asm/uaccess.h>
32#include <asm/processor.h> 33#include <asm/processor.h>
33#include <linux/mempool.h> 34#include <linux/mempool.h>
@@ -240,6 +241,38 @@ smb_send_kvec(struct TCP_Server_Info *server, struct kvec *iov, size_t n_vec,
240 return rc; 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 */
254static void
255cifs_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
243static int 276static int
244smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst) 277smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)
245{ 278{
@@ -247,7 +280,8 @@ smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)
247 struct kvec *iov = rqst->rq_iov; 280 struct kvec *iov = rqst->rq_iov;
248 int n_vec = rqst->rq_nvec; 281 int n_vec = rqst->rq_nvec;
249 unsigned int smb_buf_length = get_rfc1002_length(iov[0].iov_base); 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 struct socket *ssocket = server->ssocket; 285 struct socket *ssocket = server->ssocket;
252 int val = 1; 286 int val = 1;
253 287
@@ -258,8 +292,26 @@ smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)
258 kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK, 292 kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,
259 (char *)&val, sizeof(val)); 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;
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 }
262 313
314uncork:
263 /* uncork it */ 315 /* uncork it */
264 val = 0; 316 val = 0;
265 kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK, 317 kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,