diff options
author | Pavel Shilovsky <pshilovsky@samba.org> | 2012-09-18 19:20:29 -0400 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2012-09-24 22:46:28 -0400 |
commit | 33319141252fd14b58cf13685156c23dcaac2527 (patch) | |
tree | eebda8dab0fd4e759f58111f7a3d2fcef3e90b7f /fs | |
parent | c9de5c80d536e556568ccd45b9599870d4993b7d (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.c | 4 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 47 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 18 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 26 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 1 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 123 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 30 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 1 |
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 | ||
90 | struct workqueue_struct *cifsiod_wq; | 90 | struct workqueue_struct *cifsiod_wq; |
91 | 91 | ||
92 | #ifdef CONFIG_HIGHMEM | ||
93 | DEFINE_MUTEX(cifs_kmap_mutex); | ||
94 | #endif | ||
95 | |||
92 | static int | 96 | static int |
93 | cifs_read_super(struct super_block *sb) | 97 | cifs_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 | |||
594 | extern struct mutex cifs_kmap_mutex; | ||
595 | |||
596 | static inline void | ||
597 | cifs_kmap_lock(void) | ||
598 | { | ||
599 | mutex_lock(&cifs_kmap_mutex); | ||
600 | } | ||
601 | |||
602 | static inline void | ||
603 | cifs_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 | ||
921 | struct cifs_writedata; | ||
922 | |||
923 | /* asynchronous write support */ | ||
924 | struct 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); | |||
468 | int cifs_async_readv(struct cifs_readdata *rdata); | 468 | int cifs_async_readv(struct cifs_readdata *rdata); |
469 | int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid); | 469 | int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid); |
470 | 470 | ||
471 | /* asynchronous write support */ | ||
472 | struct 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 | |||
489 | int cifs_async_writev(struct cifs_writedata *wdata); | 471 | int cifs_async_writev(struct cifs_writedata *wdata); |
490 | void cifs_writev_complete(struct work_struct *work); | 472 | void cifs_writev_complete(struct work_struct *work); |
491 | struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages, | 473 | struct 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 | */ | ||
97 | static DEFINE_MUTEX(cifs_kmap_mutex); | ||
98 | |||
99 | static inline void | ||
100 | cifs_kmap_lock(void) | ||
101 | { | ||
102 | mutex_lock(&cifs_kmap_mutex); | ||
103 | } | ||
104 | |||
105 | static inline void | ||
106 | cifs_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 | ||
439 | struct smb_version_values smb21_values = { | 440 | struct 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 | */ | ||
1336 | static void | ||
1337 | smb2_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 */ | ||
1385 | int | ||
1386 | smb2_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 | |||
1448 | async_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 | |||
502 | struct 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 | |||
518 | struct 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 | |||
499 | struct smb2_echo_req { | 529 | struct 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); |
100 | extern int smb2_async_readv(struct cifs_readdata *rdata); | 100 | extern int smb2_async_readv(struct cifs_readdata *rdata); |
101 | extern int smb2_async_writev(struct cifs_writedata *wdata); | ||
101 | extern int SMB2_echo(struct TCP_Server_Info *server); | 102 | extern int SMB2_echo(struct TCP_Server_Info *server); |
102 | 103 | ||
103 | #endif /* _SMB2PROTO_H */ | 104 | #endif /* _SMB2PROTO_H */ |