diff options
author | Pavel Shilovsky <pshilovsky@samba.org> | 2012-09-18 16:20:29 -0700 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2012-09-24 21:46:27 -0500 |
commit | 09a4707e7638247302c6d798061aed117141fb74 (patch) | |
tree | d31b23d8b91941b30425d6a4d8235d9e91d7afef /fs/cifs/smb2pdu.c | |
parent | fc9c59662e0cd37577556d0de865268baeb9b293 (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/smb2pdu.c')
-rw-r--r-- | fs/cifs/smb2pdu.c | 137 |
1 files changed, 137 insertions, 0 deletions
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 @@ #include <linux/fs.h> #include <linux/kernel.h> #include <linux/vfs.h> +#include <linux/task_io_accounting_ops.h> #include <linux/uaccess.h> #include <linux/xattr.h> #include "smb2pdu.h" @@ -42,6 +43,7 @@ #include "cifs_debug.h" #include "ntlmssp.h" #include "smb2status.h" +#include "smb2glob.h" /* * 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, free_rsp_buf(resp_buftype, iov[0].iov_base); return rc; } + +/* + * To form a chain of read requests, any read requests after the first should + * have the end_of_chain boolean set to true. + */ +static int +smb2_new_read_req(struct kvec *iov, struct cifs_io_parms *io_parms, + unsigned int remaining_bytes, int request_type) +{ + int rc = -EACCES; + struct smb2_read_req *req = NULL; + + rc = small_smb2_init(SMB2_READ, io_parms->tcon, (void **) &req); + if (rc) + return rc; + if (io_parms->tcon->ses->server == NULL) + return -ECONNABORTED; + + req->hdr.ProcessId = cpu_to_le32(io_parms->pid); + + req->PersistentFileId = io_parms->persistent_fid; + req->VolatileFileId = io_parms->volatile_fid; + req->ReadChannelInfoOffset = 0; /* reserved */ + req->ReadChannelInfoLength = 0; /* reserved */ + req->Channel = 0; /* reserved */ + req->MinimumCount = 0; + req->Length = cpu_to_le32(io_parms->length); + req->Offset = cpu_to_le64(io_parms->offset); + + if (request_type & CHAINED_REQUEST) { + if (!(request_type & END_OF_CHAIN)) { + /* 4 for rfc1002 length field */ + req->hdr.NextCommand = + cpu_to_le32(get_rfc1002_length(req) + 4); + } else /* END_OF_CHAIN */ + req->hdr.NextCommand = 0; + if (request_type & RELATED_REQUEST) { + req->hdr.Flags |= SMB2_FLAGS_RELATED_OPERATIONS; + /* + * Related requests use info from previous read request + * in chain. + */ + req->hdr.SessionId = 0xFFFFFFFF; + req->hdr.TreeId = 0xFFFFFFFF; + req->PersistentFileId = 0xFFFFFFFF; + req->VolatileFileId = 0xFFFFFFFF; + } + } + if (remaining_bytes > io_parms->length) + req->RemainingBytes = cpu_to_le32(remaining_bytes); + else + req->RemainingBytes = 0; + + iov[0].iov_base = (char *)req; + /* 4 for rfc1002 length field */ + iov[0].iov_len = get_rfc1002_length(req) + 4; + return rc; +} + +static void +smb2_readv_callback(struct mid_q_entry *mid) +{ + struct cifs_readdata *rdata = mid->callback_data; + struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); + struct TCP_Server_Info *server = tcon->ses->server; + struct smb2_hdr *buf = (struct smb2_hdr *)rdata->iov[0].iov_base; + unsigned int credits_received = 1; + + cFYI(1, "%s: mid=%llu state=%d result=%d bytes=%u", __func__, + mid->mid, mid->mid_state, rdata->result, rdata->bytes); + + switch (mid->mid_state) { + case MID_RESPONSE_RECEIVED: + credits_received = le16_to_cpu(buf->CreditRequest); + /* result already set, check signature */ + /* if (server->sec_mode & + (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + if (smb2_verify_signature(mid->resp_buf, server)) + cERROR(1, "Unexpected SMB signature"); */ + /* FIXME: should this be counted toward the initiating task? */ + task_io_account_read(rdata->bytes); + cifs_stats_bytes_read(tcon, rdata->bytes); + break; + case MID_REQUEST_SUBMITTED: + case MID_RETRY_NEEDED: + rdata->result = -EAGAIN; + break; + default: + if (rdata->result != -ENODATA) + rdata->result = -EIO; + } + + if (rdata->result) + cifs_stats_fail_inc(tcon, SMB2_READ_HE); + + queue_work(cifsiod_wq, &rdata->work); + DeleteMidQEntry(mid); + add_credits(server, credits_received, 0); +} + +/* smb2_async_readv - send an async write, and set up mid to handle result */ +int +smb2_async_readv(struct cifs_readdata *rdata) +{ + int rc; + struct smb2_hdr *buf; + struct cifs_io_parms io_parms; + + cFYI(1, "%s: offset=%llu bytes=%u", __func__, + rdata->offset, rdata->bytes); + + io_parms.tcon = tlink_tcon(rdata->cfile->tlink); + io_parms.offset = rdata->offset; + io_parms.length = rdata->bytes; + io_parms.persistent_fid = rdata->cfile->fid.persistent_fid; + io_parms.volatile_fid = rdata->cfile->fid.volatile_fid; + io_parms.pid = rdata->pid; + rc = smb2_new_read_req(&rdata->iov[0], &io_parms, 0, 0); + if (rc) + return rc; + + buf = (struct smb2_hdr *)rdata->iov[0].iov_base; + /* 4 for rfc1002 length field */ + rdata->iov[0].iov_len = get_rfc1002_length(rdata->iov[0].iov_base) + 4; + + kref_get(&rdata->refcount); + rc = cifs_call_async(io_parms.tcon->ses->server, rdata->iov, 1, + cifs_readv_receive, smb2_readv_callback, + rdata, 0); + if (rc) + kref_put(&rdata->refcount, cifs_readdata_release); + + cifs_small_buf_release(buf); + return rc; +} |