diff options
| author | Jeff Layton <jlayton@redhat.com> | 2011-05-19 16:22:56 -0400 |
|---|---|---|
| committer | Steve French <sfrench@us.ibm.com> | 2011-05-25 16:38:33 -0400 |
| commit | c28c89fc43e3f81436efc4748837534d4d46f90c (patch) | |
| tree | d7eec9d8aabf41d38dcecab0de6f91b6a8a037c6 /fs | |
| parent | f7910cbd9fa319ee4501074f1f3b5ce23c4b1518 (diff) | |
cifs: add cifs_async_writev
Add the ability for CIFS to do an asynchronous write. The kernel will
set the frame up as it would for a "normal" SMBWrite2 request, and use
cifs_call_async to send it. The mid callback will then be configured to
handle the result.
Reviewed-by: Pavel Shilovsky <piastry@etersoft.ru>
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/cifs/cifsproto.h | 18 | ||||
| -rw-r--r-- | fs/cifs/cifssmb.c | 236 |
2 files changed, 254 insertions, 0 deletions
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index fdc0dc2c083c..8aea8850ffee 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h | |||
| @@ -442,4 +442,22 @@ extern int mdfour(unsigned char *, unsigned char *, int); | |||
| 442 | extern int E_md4hash(const unsigned char *passwd, unsigned char *p16); | 442 | extern int E_md4hash(const unsigned char *passwd, unsigned char *p16); |
| 443 | extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8, | 443 | extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8, |
| 444 | unsigned char *p24); | 444 | unsigned char *p24); |
| 445 | |||
| 446 | /* asynchronous write support */ | ||
| 447 | struct cifs_writedata { | ||
| 448 | struct kref refcount; | ||
| 449 | enum writeback_sync_modes sync_mode; | ||
| 450 | struct work_struct work; | ||
| 451 | struct cifsFileInfo *cfile; | ||
| 452 | __u64 offset; | ||
| 453 | unsigned int bytes; | ||
| 454 | int result; | ||
| 455 | unsigned int nr_pages; | ||
| 456 | struct page *pages[1]; | ||
| 457 | }; | ||
| 458 | |||
| 459 | int cifs_async_writev(struct cifs_writedata *wdata); | ||
| 460 | struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages); | ||
| 461 | void cifs_writedata_release(struct kref *refcount); | ||
| 462 | |||
| 445 | #endif /* _CIFSPROTO_H */ | 463 | #endif /* _CIFSPROTO_H */ |
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index e0d24135b3c6..136df013b0aa 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c | |||
| @@ -32,6 +32,7 @@ | |||
| 32 | #include <linux/vfs.h> | 32 | #include <linux/vfs.h> |
| 33 | #include <linux/slab.h> | 33 | #include <linux/slab.h> |
| 34 | #include <linux/posix_acl_xattr.h> | 34 | #include <linux/posix_acl_xattr.h> |
| 35 | #include <linux/pagemap.h> | ||
| 35 | #include <asm/uaccess.h> | 36 | #include <asm/uaccess.h> |
| 36 | #include "cifspdu.h" | 37 | #include "cifspdu.h" |
| 37 | #include "cifsglob.h" | 38 | #include "cifsglob.h" |
| @@ -1604,6 +1605,241 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, | |||
| 1604 | return rc; | 1605 | return rc; |
| 1605 | } | 1606 | } |
| 1606 | 1607 | ||
| 1608 | void | ||
| 1609 | cifs_writedata_release(struct kref *refcount) | ||
| 1610 | { | ||
| 1611 | struct cifs_writedata *wdata = container_of(refcount, | ||
| 1612 | struct cifs_writedata, refcount); | ||
| 1613 | |||
| 1614 | if (wdata->cfile) | ||
| 1615 | cifsFileInfo_put(wdata->cfile); | ||
| 1616 | |||
| 1617 | kfree(wdata); | ||
| 1618 | } | ||
| 1619 | |||
| 1620 | /* | ||
| 1621 | * Write failed with a retryable error. Resend the write request. It's also | ||
| 1622 | * possible that the page was redirtied so re-clean the page. | ||
| 1623 | */ | ||
| 1624 | static void | ||
| 1625 | cifs_writev_requeue(struct cifs_writedata *wdata) | ||
| 1626 | { | ||
| 1627 | int i, rc; | ||
| 1628 | struct inode *inode = wdata->cfile->dentry->d_inode; | ||
| 1629 | |||
| 1630 | for (i = 0; i < wdata->nr_pages; i++) { | ||
| 1631 | lock_page(wdata->pages[i]); | ||
| 1632 | clear_page_dirty_for_io(wdata->pages[i]); | ||
| 1633 | } | ||
| 1634 | |||
| 1635 | do { | ||
| 1636 | rc = cifs_async_writev(wdata); | ||
| 1637 | } while (rc == -EAGAIN); | ||
| 1638 | |||
| 1639 | for (i = 0; i < wdata->nr_pages; i++) { | ||
| 1640 | if (rc != 0) | ||
| 1641 | SetPageError(wdata->pages[i]); | ||
| 1642 | unlock_page(wdata->pages[i]); | ||
| 1643 | } | ||
| 1644 | |||
| 1645 | mapping_set_error(inode->i_mapping, rc); | ||
| 1646 | kref_put(&wdata->refcount, cifs_writedata_release); | ||
| 1647 | } | ||
| 1648 | |||
| 1649 | static void | ||
| 1650 | cifs_writev_complete(struct work_struct *work) | ||
| 1651 | { | ||
| 1652 | struct cifs_writedata *wdata = container_of(work, | ||
| 1653 | struct cifs_writedata, work); | ||
| 1654 | struct inode *inode = wdata->cfile->dentry->d_inode; | ||
| 1655 | int i = 0; | ||
| 1656 | |||
| 1657 | if (wdata->result == 0) { | ||
| 1658 | cifs_update_eof(CIFS_I(inode), wdata->offset, wdata->bytes); | ||
| 1659 | cifs_stats_bytes_written(tlink_tcon(wdata->cfile->tlink), | ||
| 1660 | wdata->bytes); | ||
| 1661 | } else if (wdata->sync_mode == WB_SYNC_ALL && wdata->result == -EAGAIN) | ||
| 1662 | return cifs_writev_requeue(wdata); | ||
| 1663 | |||
| 1664 | for (i = 0; i < wdata->nr_pages; i++) { | ||
| 1665 | struct page *page = wdata->pages[i]; | ||
| 1666 | if (wdata->result == -EAGAIN) | ||
| 1667 | __set_page_dirty_nobuffers(page); | ||
| 1668 | else if (wdata->result < 0) | ||
| 1669 | SetPageError(page); | ||
| 1670 | end_page_writeback(page); | ||
| 1671 | page_cache_release(page); | ||
| 1672 | } | ||
| 1673 | if (wdata->result != -EAGAIN) | ||
| 1674 | mapping_set_error(inode->i_mapping, wdata->result); | ||
| 1675 | kref_put(&wdata->refcount, cifs_writedata_release); | ||
| 1676 | } | ||
| 1677 | |||
| 1678 | struct cifs_writedata * | ||
| 1679 | cifs_writedata_alloc(unsigned int nr_pages) | ||
| 1680 | { | ||
| 1681 | struct cifs_writedata *wdata; | ||
| 1682 | |||
| 1683 | /* this would overflow */ | ||
| 1684 | if (nr_pages == 0) { | ||
| 1685 | cERROR(1, "%s: called with nr_pages == 0!", __func__); | ||
| 1686 | return NULL; | ||
| 1687 | } | ||
| 1688 | |||
| 1689 | /* writedata + number of page pointers */ | ||
| 1690 | wdata = kzalloc(sizeof(*wdata) + | ||
| 1691 | sizeof(struct page *) * (nr_pages - 1), GFP_NOFS); | ||
| 1692 | if (wdata != NULL) { | ||
| 1693 | INIT_WORK(&wdata->work, cifs_writev_complete); | ||
| 1694 | kref_init(&wdata->refcount); | ||
| 1695 | } | ||
| 1696 | return wdata; | ||
| 1697 | } | ||
| 1698 | |||
| 1699 | /* | ||
| 1700 | * Check the midState and signature on received buffer (if any), and queue the | ||
| 1701 | * workqueue completion task. | ||
| 1702 | */ | ||
| 1703 | static void | ||
| 1704 | cifs_writev_callback(struct mid_q_entry *mid) | ||
| 1705 | { | ||
| 1706 | struct cifs_writedata *wdata = mid->callback_data; | ||
| 1707 | struct cifsTconInfo *tcon = tlink_tcon(wdata->cfile->tlink); | ||
| 1708 | unsigned int written; | ||
| 1709 | WRITE_RSP *smb = (WRITE_RSP *)mid->resp_buf; | ||
| 1710 | |||
| 1711 | switch (mid->midState) { | ||
| 1712 | case MID_RESPONSE_RECEIVED: | ||
| 1713 | wdata->result = cifs_check_receive(mid, tcon->ses->server, 0); | ||
| 1714 | if (wdata->result != 0) | ||
| 1715 | break; | ||
| 1716 | |||
| 1717 | written = le16_to_cpu(smb->CountHigh); | ||
| 1718 | written <<= 16; | ||
| 1719 | written += le16_to_cpu(smb->Count); | ||
| 1720 | /* | ||
| 1721 | * Mask off high 16 bits when bytes written as returned | ||
| 1722 | * by the server is greater than bytes requested by the | ||
| 1723 | * client. OS/2 servers are known to set incorrect | ||
| 1724 | * CountHigh values. | ||
| 1725 | */ | ||
| 1726 | if (written > wdata->bytes) | ||
| 1727 | written &= 0xFFFF; | ||
| 1728 | |||
| 1729 | if (written < wdata->bytes) | ||
| 1730 | wdata->result = -ENOSPC; | ||
| 1731 | else | ||
| 1732 | wdata->bytes = written; | ||
| 1733 | break; | ||
| 1734 | case MID_REQUEST_SUBMITTED: | ||
| 1735 | case MID_RETRY_NEEDED: | ||
| 1736 | wdata->result = -EAGAIN; | ||
| 1737 | break; | ||
| 1738 | default: | ||
| 1739 | wdata->result = -EIO; | ||
| 1740 | break; | ||
| 1741 | } | ||
| 1742 | |||
| 1743 | queue_work(system_nrt_wq, &wdata->work); | ||
| 1744 | DeleteMidQEntry(mid); | ||
| 1745 | atomic_dec(&tcon->ses->server->inFlight); | ||
| 1746 | wake_up(&tcon->ses->server->request_q); | ||
| 1747 | } | ||
| 1748 | |||
| 1749 | /* cifs_async_writev - send an async write, and set up mid to handle result */ | ||
| 1750 | int | ||
| 1751 | cifs_async_writev(struct cifs_writedata *wdata) | ||
| 1752 | { | ||
| 1753 | int i, rc = -EACCES; | ||
| 1754 | WRITE_REQ *smb = NULL; | ||
| 1755 | int wct; | ||
| 1756 | struct cifsTconInfo *tcon = tlink_tcon(wdata->cfile->tlink); | ||
| 1757 | struct inode *inode = wdata->cfile->dentry->d_inode; | ||
| 1758 | struct kvec *iov = NULL; | ||
| 1759 | |||
| 1760 | if (tcon->ses->capabilities & CAP_LARGE_FILES) { | ||
| 1761 | wct = 14; | ||
| 1762 | } else { | ||
| 1763 | wct = 12; | ||
| 1764 | if (wdata->offset >> 32 > 0) { | ||
| 1765 | /* can not handle big offset for old srv */ | ||
| 1766 | return -EIO; | ||
| 1767 | } | ||
| 1768 | } | ||
| 1769 | |||
| 1770 | rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **)&smb); | ||
| 1771 | if (rc) | ||
| 1772 | goto async_writev_out; | ||
| 1773 | |||
| 1774 | /* 1 iov per page + 1 for header */ | ||
| 1775 | iov = kzalloc((wdata->nr_pages + 1) * sizeof(*iov), GFP_NOFS); | ||
| 1776 | if (iov == NULL) { | ||
| 1777 | rc = -ENOMEM; | ||
| 1778 | goto async_writev_out; | ||
| 1779 | } | ||
| 1780 | |||
| 1781 | smb->AndXCommand = 0xFF; /* none */ | ||
| 1782 | smb->Fid = wdata->cfile->netfid; | ||
| 1783 | smb->OffsetLow = cpu_to_le32(wdata->offset & 0xFFFFFFFF); | ||
| 1784 | if (wct == 14) | ||
| 1785 | smb->OffsetHigh = cpu_to_le32(wdata->offset >> 32); | ||
| 1786 | smb->Reserved = 0xFFFFFFFF; | ||
| 1787 | smb->WriteMode = 0; | ||
| 1788 | smb->Remaining = 0; | ||
| 1789 | |||
| 1790 | smb->DataOffset = | ||
| 1791 | cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); | ||
| 1792 | |||
| 1793 | /* 4 for RFC1001 length + 1 for BCC */ | ||
| 1794 | iov[0].iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4 + 1; | ||
| 1795 | iov[0].iov_base = smb; | ||
| 1796 | |||
| 1797 | /* marshal up the pages into iov array */ | ||
| 1798 | wdata->bytes = 0; | ||
| 1799 | for (i = 0; i < wdata->nr_pages; i++) { | ||
| 1800 | iov[i + 1].iov_len = min(inode->i_size - | ||
| 1801 | page_offset(wdata->pages[i]), | ||
| 1802 | (loff_t)PAGE_CACHE_SIZE); | ||
| 1803 | iov[i + 1].iov_base = kmap(wdata->pages[i]); | ||
| 1804 | wdata->bytes += iov[i + 1].iov_len; | ||
| 1805 | } | ||
| 1806 | |||
| 1807 | cFYI(1, "async write at %llu %u bytes", wdata->offset, wdata->bytes); | ||
| 1808 | |||
| 1809 | smb->DataLengthLow = cpu_to_le16(wdata->bytes & 0xFFFF); | ||
| 1810 | smb->DataLengthHigh = cpu_to_le16(wdata->bytes >> 16); | ||
| 1811 | |||
| 1812 | if (wct == 14) { | ||
| 1813 | inc_rfc1001_len(&smb->hdr, wdata->bytes + 1); | ||
| 1814 | put_bcc(wdata->bytes + 1, &smb->hdr); | ||
| 1815 | } else { | ||
| 1816 | /* wct == 12 */ | ||
| 1817 | struct smb_com_writex_req *smbw = | ||
| 1818 | (struct smb_com_writex_req *)smb; | ||
| 1819 | inc_rfc1001_len(&smbw->hdr, wdata->bytes + 5); | ||
| 1820 | put_bcc(wdata->bytes + 5, &smbw->hdr); | ||
| 1821 | iov[0].iov_len += 4; /* pad bigger by four bytes */ | ||
| 1822 | } | ||
| 1823 | |||
| 1824 | kref_get(&wdata->refcount); | ||
| 1825 | rc = cifs_call_async(tcon->ses->server, iov, wdata->nr_pages + 1, | ||
| 1826 | cifs_writev_callback, wdata, false); | ||
| 1827 | |||
| 1828 | if (rc == 0) | ||
| 1829 | cifs_stats_inc(&tcon->num_writes); | ||
| 1830 | else | ||
| 1831 | kref_put(&wdata->refcount, cifs_writedata_release); | ||
| 1832 | |||
| 1833 | /* send is done, unmap pages */ | ||
| 1834 | for (i = 0; i < wdata->nr_pages; i++) | ||
| 1835 | kunmap(wdata->pages[i]); | ||
| 1836 | |||
| 1837 | async_writev_out: | ||
| 1838 | cifs_small_buf_release(smb); | ||
| 1839 | kfree(iov); | ||
| 1840 | return rc; | ||
| 1841 | } | ||
| 1842 | |||
| 1607 | int | 1843 | int |
| 1608 | CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, | 1844 | CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, |
| 1609 | const int netfid, const unsigned int count, | 1845 | const int netfid, const unsigned int count, |
