aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorPavel Shilovsky <pshilovsky@samba.org>2012-09-18 19:20:29 -0400
committerSteve French <smfrench@gmail.com>2012-09-24 22:46:28 -0400
commit33319141252fd14b58cf13685156c23dcaac2527 (patch)
treeeebda8dab0fd4e759f58111f7a3d2fcef3e90b7f /fs
parentc9de5c80d536e556568ccd45b9599870d4993b7d (diff)
CIFS: Add SMB2 support for cifs_iovec_write
Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org> Signed-off-by: Steve French <smfrench@gmail.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/cifs/cifsfs.c4
-rw-r--r--fs/cifs/cifsglob.h47
-rw-r--r--fs/cifs/cifsproto.h18
-rw-r--r--fs/cifs/cifssmb.c26
-rw-r--r--fs/cifs/smb2ops.c1
-rw-r--r--fs/cifs/smb2pdu.c123
-rw-r--r--fs/cifs/smb2pdu.h30
-rw-r--r--fs/cifs/smb2proto.h1
8 files changed, 206 insertions, 44 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index db8a404a51dd..2829f374dbf7 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -89,6 +89,10 @@ extern mempool_t *cifs_mid_poolp;
89 89
90struct workqueue_struct *cifsiod_wq; 90struct workqueue_struct *cifsiod_wq;
91 91
92#ifdef CONFIG_HIGHMEM
93DEFINE_MUTEX(cifs_kmap_mutex);
94#endif
95
92static int 96static int
93cifs_read_super(struct super_block *sb) 97cifs_read_super(struct super_block *sb)
94{ 98{
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index aef167470654..330f6259bb6d 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -582,6 +582,33 @@ get_next_mid(struct TCP_Server_Info *server)
582#define CIFS_KMAP_SIZE_LIMIT (1<<24) 582#define CIFS_KMAP_SIZE_LIMIT (1<<24)
583#endif /* CONFIG_HIGHMEM */ 583#endif /* CONFIG_HIGHMEM */
584 584
585#ifdef CONFIG_HIGHMEM
586/*
587 * On arches that have high memory, kmap address space is limited. By
588 * serializing the kmap operations on those arches, we ensure that we don't
589 * end up with a bunch of threads in writeback with partially mapped page
590 * arrays, stuck waiting for kmap to come back. That situation prevents
591 * progress and can deadlock.
592 */
593
594extern struct mutex cifs_kmap_mutex;
595
596static inline void
597cifs_kmap_lock(void)
598{
599 mutex_lock(&cifs_kmap_mutex);
600}
601
602static inline void
603cifs_kmap_unlock(void)
604{
605 mutex_unlock(&cifs_kmap_mutex);
606}
607#else /* !CONFIG_HIGHMEM */
608#define cifs_kmap_lock() do { ; } while (0)
609#define cifs_kmap_unlock() do { ; } while (0)
610#endif /* CONFIG_HIGHMEM */
611
585/* 612/*
586 * Macros to allow the TCP_Server_Info->net field and related code to drop out 613 * Macros to allow the TCP_Server_Info->net field and related code to drop out
587 * when CONFIG_NET_NS isn't set. 614 * when CONFIG_NET_NS isn't set.
@@ -891,6 +918,26 @@ struct cifs_readdata {
891 struct kvec iov[1]; 918 struct kvec iov[1];
892}; 919};
893 920
921struct cifs_writedata;
922
923/* asynchronous write support */
924struct cifs_writedata {
925 struct kref refcount;
926 struct list_head list;
927 struct completion done;
928 enum writeback_sync_modes sync_mode;
929 struct work_struct work;
930 struct cifsFileInfo *cfile;
931 __u64 offset;
932 pid_t pid;
933 unsigned int bytes;
934 int result;
935 void (*marshal_iov) (struct kvec *iov,
936 struct cifs_writedata *wdata);
937 unsigned int nr_pages;
938 struct page *pages[1];
939};
940
894/* 941/*
895 * Take a reference on the file private data. Must be called with 942 * Take a reference on the file private data. Must be called with
896 * cifs_file_list_lock held. 943 * cifs_file_list_lock held.
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 6656eb5dbf70..8b320c7b582c 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -468,24 +468,6 @@ void cifs_readdata_release(struct kref *refcount);
468int cifs_async_readv(struct cifs_readdata *rdata); 468int cifs_async_readv(struct cifs_readdata *rdata);
469int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid); 469int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid);
470 470
471/* asynchronous write support */
472struct cifs_writedata {
473 struct kref refcount;
474 struct list_head list;
475 struct completion done;
476 enum writeback_sync_modes sync_mode;
477 struct work_struct work;
478 struct cifsFileInfo *cfile;
479 __u64 offset;
480 pid_t pid;
481 unsigned int bytes;
482 int result;
483 void (*marshal_iov) (struct kvec *iov,
484 struct cifs_writedata *wdata);
485 unsigned int nr_pages;
486 struct page *pages[1];
487};
488
489int cifs_async_writev(struct cifs_writedata *wdata); 471int cifs_async_writev(struct cifs_writedata *wdata);
490void cifs_writev_complete(struct work_struct *work); 472void cifs_writev_complete(struct work_struct *work);
491struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages, 473struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages,
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 2a9b27387708..f27b13ea08b8 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -86,32 +86,6 @@ static struct {
86#endif /* CONFIG_CIFS_WEAK_PW_HASH */ 86#endif /* CONFIG_CIFS_WEAK_PW_HASH */
87#endif /* CIFS_POSIX */ 87#endif /* CIFS_POSIX */
88 88
89#ifdef CONFIG_HIGHMEM
90/*
91 * On arches that have high memory, kmap address space is limited. By
92 * serializing the kmap operations on those arches, we ensure that we don't
93 * end up with a bunch of threads in writeback with partially mapped page
94 * arrays, stuck waiting for kmap to come back. That situation prevents
95 * progress and can deadlock.
96 */
97static DEFINE_MUTEX(cifs_kmap_mutex);
98
99static inline void
100cifs_kmap_lock(void)
101{
102 mutex_lock(&cifs_kmap_mutex);
103}
104
105static inline void
106cifs_kmap_unlock(void)
107{
108 mutex_unlock(&cifs_kmap_mutex);
109}
110#else /* !CONFIG_HIGHMEM */
111#define cifs_kmap_lock() do { ; } while(0)
112#define cifs_kmap_unlock() do { ; } while(0)
113#endif /* CONFIG_HIGHMEM */
114
115/* 89/*
116 * Mark as invalid, all open files on tree connections since they 90 * Mark as invalid, all open files on tree connections since they
117 * were closed when session to server was lost. 91 * were closed when session to server was lost.
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index d9ca357d9809..da31235023fb 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -434,6 +434,7 @@ struct smb_version_operations smb21_operations = {
434 .close = smb2_close_file, 434 .close = smb2_close_file,
435 .flush = smb2_flush_file, 435 .flush = smb2_flush_file,
436 .async_readv = smb2_async_readv, 436 .async_readv = smb2_async_readv,
437 .async_writev = smb2_async_writev,
437}; 438};
438 439
439struct smb_version_values smb21_values = { 440struct smb_version_values smb21_values = {
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index e18671852d41..cb6acc7b1ca8 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -33,6 +33,7 @@
33#include <linux/vfs.h> 33#include <linux/vfs.h>
34#include <linux/task_io_accounting_ops.h> 34#include <linux/task_io_accounting_ops.h>
35#include <linux/uaccess.h> 35#include <linux/uaccess.h>
36#include <linux/pagemap.h>
36#include <linux/xattr.h> 37#include <linux/xattr.h>
37#include "smb2pdu.h" 38#include "smb2pdu.h"
38#include "cifsglob.h" 39#include "cifsglob.h"
@@ -1327,3 +1328,125 @@ smb2_async_readv(struct cifs_readdata *rdata)
1327 cifs_small_buf_release(buf); 1328 cifs_small_buf_release(buf);
1328 return rc; 1329 return rc;
1329} 1330}
1331
1332/*
1333 * Check the mid_state and signature on received buffer (if any), and queue the
1334 * workqueue completion task.
1335 */
1336static void
1337smb2_writev_callback(struct mid_q_entry *mid)
1338{
1339 struct cifs_writedata *wdata = mid->callback_data;
1340 struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
1341 unsigned int written;
1342 struct smb2_write_rsp *rsp = (struct smb2_write_rsp *)mid->resp_buf;
1343 unsigned int credits_received = 1;
1344
1345 switch (mid->mid_state) {
1346 case MID_RESPONSE_RECEIVED:
1347 credits_received = le16_to_cpu(rsp->hdr.CreditRequest);
1348 wdata->result = smb2_check_receive(mid, tcon->ses->server, 0);
1349 if (wdata->result != 0)
1350 break;
1351
1352 written = le32_to_cpu(rsp->DataLength);
1353 /*
1354 * Mask off high 16 bits when bytes written as returned
1355 * by the server is greater than bytes requested by the
1356 * client. OS/2 servers are known to set incorrect
1357 * CountHigh values.
1358 */
1359 if (written > wdata->bytes)
1360 written &= 0xFFFF;
1361
1362 if (written < wdata->bytes)
1363 wdata->result = -ENOSPC;
1364 else
1365 wdata->bytes = written;
1366 break;
1367 case MID_REQUEST_SUBMITTED:
1368 case MID_RETRY_NEEDED:
1369 wdata->result = -EAGAIN;
1370 break;
1371 default:
1372 wdata->result = -EIO;
1373 break;
1374 }
1375
1376 if (wdata->result)
1377 cifs_stats_fail_inc(tcon, SMB2_WRITE_HE);
1378
1379 queue_work(cifsiod_wq, &wdata->work);
1380 DeleteMidQEntry(mid);
1381 add_credits(tcon->ses->server, credits_received, 0);
1382}
1383
1384/* smb2_async_writev - send an async write, and set up mid to handle result */
1385int
1386smb2_async_writev(struct cifs_writedata *wdata)
1387{
1388 int i, rc = -EACCES;
1389 struct smb2_write_req *req = NULL;
1390 struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
1391 struct kvec *iov = NULL;
1392
1393 rc = small_smb2_init(SMB2_WRITE, tcon, (void **) &req);
1394 if (rc)
1395 goto async_writev_out;
1396
1397 /* 1 iov per page + 1 for header */
1398 iov = kzalloc((wdata->nr_pages + 1) * sizeof(*iov), GFP_NOFS);
1399 if (iov == NULL) {
1400 rc = -ENOMEM;
1401 goto async_writev_out;
1402 }
1403
1404 req->hdr.ProcessId = cpu_to_le32(wdata->cfile->pid);
1405
1406 req->PersistentFileId = wdata->cfile->fid.persistent_fid;
1407 req->VolatileFileId = wdata->cfile->fid.volatile_fid;
1408 req->WriteChannelInfoOffset = 0;
1409 req->WriteChannelInfoLength = 0;
1410 req->Channel = 0;
1411 req->Offset = cpu_to_le64(wdata->offset);
1412 /* 4 for rfc1002 length field */
1413 req->DataOffset = cpu_to_le16(
1414 offsetof(struct smb2_write_req, Buffer) - 4);
1415 req->RemainingBytes = 0;
1416
1417 /* 4 for rfc1002 length field and 1 for Buffer */
1418 iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
1419 iov[0].iov_base = (char *)req;
1420
1421 /*
1422 * This function should marshal up the page array into the kvec
1423 * array, reserving [0] for the header. It should kmap the pages
1424 * and set the iov_len properly for each one. It may also set
1425 * wdata->bytes too.
1426 */
1427 cifs_kmap_lock();
1428 wdata->marshal_iov(iov, wdata);
1429 cifs_kmap_unlock();
1430
1431 cFYI(1, "async write at %llu %u bytes", wdata->offset, wdata->bytes);
1432
1433 req->Length = cpu_to_le32(wdata->bytes);
1434
1435 inc_rfc1001_len(&req->hdr, wdata->bytes - 1 /* Buffer */);
1436
1437 kref_get(&wdata->refcount);
1438 rc = cifs_call_async(tcon->ses->server, iov, wdata->nr_pages + 1,
1439 NULL, smb2_writev_callback, wdata, 0);
1440
1441 if (rc)
1442 kref_put(&wdata->refcount, cifs_writedata_release);
1443
1444 /* send is done, unmap pages */
1445 for (i = 0; i < wdata->nr_pages; i++)
1446 kunmap(wdata->pages[i]);
1447
1448async_writev_out:
1449 cifs_small_buf_release(req);
1450 kfree(iov);
1451 return rc;
1452}
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index 4abb58106809..21ec9ed280f9 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -496,6 +496,36 @@ struct smb2_read_rsp {
496 __u8 Buffer[1]; 496 __u8 Buffer[1];
497} __packed; 497} __packed;
498 498
499/* For write request Flags field below the following flag is defined: */
500#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001
501
502struct smb2_write_req {
503 struct smb2_hdr hdr;
504 __le16 StructureSize; /* Must be 49 */
505 __le16 DataOffset; /* offset from start of SMB2 header to write data */
506 __le32 Length;
507 __le64 Offset;
508 __u64 PersistentFileId; /* opaque endianness */
509 __u64 VolatileFileId; /* opaque endianness */
510 __le32 Channel; /* Reserved MBZ */
511 __le32 RemainingBytes;
512 __le16 WriteChannelInfoOffset; /* Reserved MBZ */
513 __le16 WriteChannelInfoLength; /* Reserved MBZ */
514 __le32 Flags;
515 __u8 Buffer[1];
516} __packed;
517
518struct smb2_write_rsp {
519 struct smb2_hdr hdr;
520 __le16 StructureSize; /* Must be 17 */
521 __u8 DataOffset;
522 __u8 Reserved;
523 __le32 DataLength;
524 __le32 DataRemaining;
525 __u32 Reserved2;
526 __u8 Buffer[1];
527} __packed;
528
499struct smb2_echo_req { 529struct smb2_echo_req {
500 struct smb2_hdr hdr; 530 struct smb2_hdr hdr;
501 __le16 StructureSize; /* Must be 4 */ 531 __le16 StructureSize; /* Must be 4 */
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index f442e4699974..ec983be0ba31 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -98,6 +98,7 @@ extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon,
98 u64 persistent_fid, u64 volatile_fid, 98 u64 persistent_fid, u64 volatile_fid,
99 __le64 *uniqueid); 99 __le64 *uniqueid);
100extern int smb2_async_readv(struct cifs_readdata *rdata); 100extern int smb2_async_readv(struct cifs_readdata *rdata);
101extern int smb2_async_writev(struct cifs_writedata *wdata);
101extern int SMB2_echo(struct TCP_Server_Info *server); 102extern int SMB2_echo(struct TCP_Server_Info *server);
102 103
103#endif /* _SMB2PROTO_H */ 104#endif /* _SMB2PROTO_H */