aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/smb2pdu.c
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/cifs/smb2pdu.c
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/cifs/smb2pdu.c')
-rw-r--r--fs/cifs/smb2pdu.c123
1 files changed, 123 insertions, 0 deletions
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}