aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs
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:27 -0400
commit09a4707e7638247302c6d798061aed117141fb74 (patch)
treed31b23d8b91941b30425d6a4d8235d9e91d7afef /fs/cifs
parentfc9c59662e0cd37577556d0de865268baeb9b293 (diff)
CIFS: Add SMB2 support for cifs_iovec_read
Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org> Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/cifsglob.h25
-rw-r--r--fs/cifs/cifsproto.h20
-rw-r--r--fs/cifs/cifssmb.c2
-rw-r--r--fs/cifs/file.c4
-rw-r--r--fs/cifs/smb2glob.h6
-rw-r--r--fs/cifs/smb2misc.c3
-rw-r--r--fs/cifs/smb2ops.c19
-rw-r--r--fs/cifs/smb2pdu.c137
-rw-r--r--fs/cifs/smb2pdu.h28
-rw-r--r--fs/cifs/smb2proto.h1
10 files changed, 225 insertions, 20 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index fcf81c05635f..93dd582bb8d1 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -857,12 +857,37 @@ struct cifsFileInfo {
857 857
858struct cifs_io_parms { 858struct cifs_io_parms {
859 __u16 netfid; 859 __u16 netfid;
860#ifdef CONFIG_CIFS_SMB2
861 __u64 persistent_fid; /* persist file id for smb2 */
862 __u64 volatile_fid; /* volatile file id for smb2 */
863#endif
860 __u32 pid; 864 __u32 pid;
861 __u64 offset; 865 __u64 offset;
862 unsigned int length; 866 unsigned int length;
863 struct cifs_tcon *tcon; 867 struct cifs_tcon *tcon;
864}; 868};
865 869
870struct cifs_readdata;
871
872/* asynchronous read support */
873struct cifs_readdata {
874 struct kref refcount;
875 struct list_head list;
876 struct completion done;
877 struct cifsFileInfo *cfile;
878 struct address_space *mapping;
879 __u64 offset;
880 unsigned int bytes;
881 pid_t pid;
882 int result;
883 struct list_head pages;
884 struct work_struct work;
885 int (*marshal_iov) (struct cifs_readdata *rdata,
886 unsigned int remaining);
887 unsigned int nr_iov;
888 struct kvec iov[1];
889};
890
866/* 891/*
867 * Take a reference on the file private data. Must be called with 892 * Take a reference on the file private data. Must be called with
868 * cifs_file_list_lock held. 893 * cifs_file_list_lock held.
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 2c6ad78a16cc..6656eb5dbf70 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -464,27 +464,9 @@ extern int E_md4hash(const unsigned char *passwd, unsigned char *p16,
464extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8, 464extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8,
465 unsigned char *p24); 465 unsigned char *p24);
466 466
467/* asynchronous read support */
468struct cifs_readdata {
469 struct kref refcount;
470 struct list_head list;
471 struct completion done;
472 struct cifsFileInfo *cfile;
473 struct address_space *mapping;
474 __u64 offset;
475 unsigned int bytes;
476 pid_t pid;
477 int result;
478 struct list_head pages;
479 struct work_struct work;
480 int (*marshal_iov) (struct cifs_readdata *rdata,
481 unsigned int remaining);
482 unsigned int nr_iov;
483 struct kvec iov[1];
484};
485
486void cifs_readdata_release(struct kref *refcount); 467void cifs_readdata_release(struct kref *refcount);
487int 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);
488 470
489/* asynchronous write support */ 471/* asynchronous write support */
490struct cifs_writedata { 472struct cifs_writedata {
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 4c48b9c60b26..8a07f218266f 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -1440,7 +1440,7 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
1440 return 0; 1440 return 0;
1441} 1441}
1442 1442
1443static int 1443int
1444cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) 1444cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
1445{ 1445{
1446 int length, len; 1446 int length, len;
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index ec7c2e6bcbdf..29ac8ee46039 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -2732,6 +2732,10 @@ restart_loop:
2732 cifs_stats_bytes_read(tcon, total_read); 2732 cifs_stats_bytes_read(tcon, total_read);
2733 *poffset += total_read; 2733 *poffset += total_read;
2734 2734
2735 /* mask nodata case */
2736 if (rc == -ENODATA)
2737 rc = 0;
2738
2735 return total_read ? total_read : rc; 2739 return total_read ? total_read : rc;
2736} 2740}
2737 2741
diff --git a/fs/cifs/smb2glob.h b/fs/cifs/smb2glob.h
index 33c1d89090c0..11505d73ff32 100644
--- a/fs/cifs/smb2glob.h
+++ b/fs/cifs/smb2glob.h
@@ -41,4 +41,10 @@
41#define SMB2_OP_RENAME 6 41#define SMB2_OP_RENAME 6
42#define SMB2_OP_DELETE 7 42#define SMB2_OP_DELETE 7
43 43
44/* Used when constructing chained read requests. */
45#define CHAINED_REQUEST 1
46#define START_OF_CHAIN 2
47#define END_OF_CHAIN 4
48#define RELATED_REQUEST 8
49
44#endif /* _SMB2_GLOB_H */ 50#endif /* _SMB2_GLOB_H */
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index e4d3b9964167..9275883c8530 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -244,6 +244,9 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)
244 ((struct smb2_query_info_rsp *)hdr)->OutputBufferLength); 244 ((struct smb2_query_info_rsp *)hdr)->OutputBufferLength);
245 break; 245 break;
246 case SMB2_READ: 246 case SMB2_READ:
247 *off = ((struct smb2_read_rsp *)hdr)->DataOffset;
248 *len = le32_to_cpu(((struct smb2_read_rsp *)hdr)->DataLength);
249 break;
247 case SMB2_QUERY_DIRECTORY: 250 case SMB2_QUERY_DIRECTORY:
248 case SMB2_IOCTL: 251 case SMB2_IOCTL:
249 case SMB2_CHANGE_NOTIFY: 252 case SMB2_CHANGE_NOTIFY:
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 20afb756e97a..d9ca357d9809 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -379,6 +379,20 @@ smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon,
379 return SMB2_flush(xid, tcon, fid->persistent_fid, fid->volatile_fid); 379 return SMB2_flush(xid, tcon, fid->persistent_fid, fid->volatile_fid);
380} 380}
381 381
382static unsigned int
383smb2_read_data_offset(char *buf)
384{
385 struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf;
386 return rsp->DataOffset;
387}
388
389static unsigned int
390smb2_read_data_length(char *buf)
391{
392 struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf;
393 return le32_to_cpu(rsp->DataLength);
394}
395
382struct smb_version_operations smb21_operations = { 396struct smb_version_operations smb21_operations = {
383 .setup_request = smb2_setup_request, 397 .setup_request = smb2_setup_request,
384 .setup_async_request = smb2_setup_async_request, 398 .setup_async_request = smb2_setup_async_request,
@@ -388,6 +402,9 @@ struct smb_version_operations smb21_operations = {
388 .get_credits_field = smb2_get_credits_field, 402 .get_credits_field = smb2_get_credits_field,
389 .get_credits = smb2_get_credits, 403 .get_credits = smb2_get_credits,
390 .get_next_mid = smb2_get_next_mid, 404 .get_next_mid = smb2_get_next_mid,
405 .read_data_offset = smb2_read_data_offset,
406 .read_data_length = smb2_read_data_length,
407 .map_error = map_smb2_to_linux_error,
391 .find_mid = smb2_find_mid, 408 .find_mid = smb2_find_mid,
392 .check_message = smb2_check_message, 409 .check_message = smb2_check_message,
393 .dump_detail = smb2_dump_detail, 410 .dump_detail = smb2_dump_detail,
@@ -416,12 +433,14 @@ struct smb_version_operations smb21_operations = {
416 .set_fid = smb2_set_fid, 433 .set_fid = smb2_set_fid,
417 .close = smb2_close_file, 434 .close = smb2_close_file,
418 .flush = smb2_flush_file, 435 .flush = smb2_flush_file,
436 .async_readv = smb2_async_readv,
419}; 437};
420 438
421struct smb_version_values smb21_values = { 439struct smb_version_values smb21_values = {
422 .version_string = SMB21_VERSION_STRING, 440 .version_string = SMB21_VERSION_STRING,
423 .header_size = sizeof(struct smb2_hdr), 441 .header_size = sizeof(struct smb2_hdr),
424 .max_header_size = MAX_SMB2_HDR_SIZE, 442 .max_header_size = MAX_SMB2_HDR_SIZE,
443 .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
425 .lock_cmd = SMB2_LOCK, 444 .lock_cmd = SMB2_LOCK,
426 .cap_unix = 0, 445 .cap_unix = 0,
427 .cap_nt_find = SMB2_NT_FIND, 446 .cap_nt_find = SMB2_NT_FIND,
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index ff374063f4e2..e18671852d41 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -31,6 +31,7 @@
31#include <linux/fs.h> 31#include <linux/fs.h>
32#include <linux/kernel.h> 32#include <linux/kernel.h>
33#include <linux/vfs.h> 33#include <linux/vfs.h>
34#include <linux/task_io_accounting_ops.h>
34#include <linux/uaccess.h> 35#include <linux/uaccess.h>
35#include <linux/xattr.h> 36#include <linux/xattr.h>
36#include "smb2pdu.h" 37#include "smb2pdu.h"
@@ -42,6 +43,7 @@
42#include "cifs_debug.h" 43#include "cifs_debug.h"
43#include "ntlmssp.h" 44#include "ntlmssp.h"
44#include "smb2status.h" 45#include "smb2status.h"
46#include "smb2glob.h"
45 47
46/* 48/*
47 * The following table defines the expected "StructureSize" of SMB2 requests 49 * The following table defines the expected "StructureSize" of SMB2 requests
@@ -1190,3 +1192,138 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
1190 free_rsp_buf(resp_buftype, iov[0].iov_base); 1192 free_rsp_buf(resp_buftype, iov[0].iov_base);
1191 return rc; 1193 return rc;
1192} 1194}
1195
1196/*
1197 * To form a chain of read requests, any read requests after the first should
1198 * have the end_of_chain boolean set to true.
1199 */
1200static int
1201smb2_new_read_req(struct kvec *iov, struct cifs_io_parms *io_parms,
1202 unsigned int remaining_bytes, int request_type)
1203{
1204 int rc = -EACCES;
1205 struct smb2_read_req *req = NULL;
1206
1207 rc = small_smb2_init(SMB2_READ, io_parms->tcon, (void **) &req);
1208 if (rc)
1209 return rc;
1210 if (io_parms->tcon->ses->server == NULL)
1211 return -ECONNABORTED;
1212
1213 req->hdr.ProcessId = cpu_to_le32(io_parms->pid);
1214
1215 req->PersistentFileId = io_parms->persistent_fid;
1216 req->VolatileFileId = io_parms->volatile_fid;
1217 req->ReadChannelInfoOffset = 0; /* reserved */
1218 req->ReadChannelInfoLength = 0; /* reserved */
1219 req->Channel = 0; /* reserved */
1220 req->MinimumCount = 0;
1221 req->Length = cpu_to_le32(io_parms->length);
1222 req->Offset = cpu_to_le64(io_parms->offset);
1223
1224 if (request_type & CHAINED_REQUEST) {
1225 if (!(request_type & END_OF_CHAIN)) {
1226 /* 4 for rfc1002 length field */
1227 req->hdr.NextCommand =
1228 cpu_to_le32(get_rfc1002_length(req) + 4);
1229 } else /* END_OF_CHAIN */
1230 req->hdr.NextCommand = 0;
1231 if (request_type & RELATED_REQUEST) {
1232 req->hdr.Flags |= SMB2_FLAGS_RELATED_OPERATIONS;
1233 /*
1234 * Related requests use info from previous read request
1235 * in chain.
1236 */
1237 req->hdr.SessionId = 0xFFFFFFFF;
1238 req->hdr.TreeId = 0xFFFFFFFF;
1239 req->PersistentFileId = 0xFFFFFFFF;
1240 req->VolatileFileId = 0xFFFFFFFF;
1241 }
1242 }
1243 if (remaining_bytes > io_parms->length)
1244 req->RemainingBytes = cpu_to_le32(remaining_bytes);
1245 else
1246 req->RemainingBytes = 0;
1247
1248 iov[0].iov_base = (char *)req;
1249 /* 4 for rfc1002 length field */
1250 iov[0].iov_len = get_rfc1002_length(req) + 4;
1251 return rc;
1252}
1253
1254static void
1255smb2_readv_callback(struct mid_q_entry *mid)
1256{
1257 struct cifs_readdata *rdata = mid->callback_data;
1258 struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
1259 struct TCP_Server_Info *server = tcon->ses->server;
1260 struct smb2_hdr *buf = (struct smb2_hdr *)rdata->iov[0].iov_base;
1261 unsigned int credits_received = 1;
1262
1263 cFYI(1, "%s: mid=%llu state=%d result=%d bytes=%u", __func__,
1264 mid->mid, mid->mid_state, rdata->result, rdata->bytes);
1265
1266 switch (mid->mid_state) {
1267 case MID_RESPONSE_RECEIVED:
1268 credits_received = le16_to_cpu(buf->CreditRequest);
1269 /* result already set, check signature */
1270 /* if (server->sec_mode &
1271 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
1272 if (smb2_verify_signature(mid->resp_buf, server))
1273 cERROR(1, "Unexpected SMB signature"); */
1274 /* FIXME: should this be counted toward the initiating task? */
1275 task_io_account_read(rdata->bytes);
1276 cifs_stats_bytes_read(tcon, rdata->bytes);
1277 break;
1278 case MID_REQUEST_SUBMITTED:
1279 case MID_RETRY_NEEDED:
1280 rdata->result = -EAGAIN;
1281 break;
1282 default:
1283 if (rdata->result != -ENODATA)
1284 rdata->result = -EIO;
1285 }
1286
1287 if (rdata->result)
1288 cifs_stats_fail_inc(tcon, SMB2_READ_HE);
1289
1290 queue_work(cifsiod_wq, &rdata->work);
1291 DeleteMidQEntry(mid);
1292 add_credits(server, credits_received, 0);
1293}
1294
1295/* smb2_async_readv - send an async write, and set up mid to handle result */
1296int
1297smb2_async_readv(struct cifs_readdata *rdata)
1298{
1299 int rc;
1300 struct smb2_hdr *buf;
1301 struct cifs_io_parms io_parms;
1302
1303 cFYI(1, "%s: offset=%llu bytes=%u", __func__,
1304 rdata->offset, rdata->bytes);
1305
1306 io_parms.tcon = tlink_tcon(rdata->cfile->tlink);
1307 io_parms.offset = rdata->offset;
1308 io_parms.length = rdata->bytes;
1309 io_parms.persistent_fid = rdata->cfile->fid.persistent_fid;
1310 io_parms.volatile_fid = rdata->cfile->fid.volatile_fid;
1311 io_parms.pid = rdata->pid;
1312 rc = smb2_new_read_req(&rdata->iov[0], &io_parms, 0, 0);
1313 if (rc)
1314 return rc;
1315
1316 buf = (struct smb2_hdr *)rdata->iov[0].iov_base;
1317 /* 4 for rfc1002 length field */
1318 rdata->iov[0].iov_len = get_rfc1002_length(rdata->iov[0].iov_base) + 4;
1319
1320 kref_get(&rdata->refcount);
1321 rc = cifs_call_async(io_parms.tcon->ses->server, rdata->iov, 1,
1322 cifs_readv_receive, smb2_readv_callback,
1323 rdata, 0);
1324 if (rc)
1325 kref_put(&rdata->refcount, cifs_readdata_release);
1326
1327 cifs_small_buf_release(buf);
1328 return rc;
1329}
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index f5bf63f66971..4abb58106809 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -468,6 +468,34 @@ struct smb2_flush_rsp {
468 __le16 Reserved; 468 __le16 Reserved;
469} __packed; 469} __packed;
470 470
471struct smb2_read_req {
472 struct smb2_hdr hdr;
473 __le16 StructureSize; /* Must be 49 */
474 __u8 Padding; /* offset from start of SMB2 header to place read */
475 __u8 Reserved;
476 __le32 Length;
477 __le64 Offset;
478 __u64 PersistentFileId; /* opaque endianness */
479 __u64 VolatileFileId; /* opaque endianness */
480 __le32 MinimumCount;
481 __le32 Channel; /* Reserved MBZ */
482 __le32 RemainingBytes;
483 __le16 ReadChannelInfoOffset; /* Reserved MBZ */
484 __le16 ReadChannelInfoLength; /* Reserved MBZ */
485 __u8 Buffer[1];
486} __packed;
487
488struct smb2_read_rsp {
489 struct smb2_hdr hdr;
490 __le16 StructureSize; /* Must be 17 */
491 __u8 DataOffset;
492 __u8 Reserved;
493 __le32 DataLength;
494 __le32 DataRemaining;
495 __u32 Reserved2;
496 __u8 Buffer[1];
497} __packed;
498
471struct smb2_echo_req { 499struct smb2_echo_req {
472 struct smb2_hdr hdr; 500 struct smb2_hdr hdr;
473 __le16 StructureSize; /* Must be 4 */ 501 __le16 StructureSize; /* Must be 4 */
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 51e6cd185c79..f442e4699974 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -97,6 +97,7 @@ extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
97extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, 97extern 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_echo(struct TCP_Server_Info *server); 101extern int SMB2_echo(struct TCP_Server_Info *server);
101 102
102#endif /* _SMB2PROTO_H */ 103#endif /* _SMB2PROTO_H */