diff options
author | Pavel Shilovsky <piastryyy@gmail.com> | 2011-01-24 14:16:35 -0500 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2011-01-25 14:30:13 -0500 |
commit | 72432ffcf555decbbae47f1be338e1d2f210aa69 (patch) | |
tree | 4293cb6e5b880071099756b7523f8f36bb7e16cc /fs/cifs | |
parent | 93c100c0b423266c0ee28497e90fdf27c05e6b8e (diff) |
CIFS: Implement cifs_strict_writev (try #4)
If we don't have Exclusive oplock we write a data to the server.
Also set invalidate_mapping flag on the inode if we wrote something
to the server. Add cifs_iovec_write to let the client write iovec
buffers through CIFSSMBWrite2.
Signed-off-by: Pavel Shilovsky <piastryyy@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/cifsfs.c | 15 | ||||
-rw-r--r-- | fs/cifs/cifsfs.h | 4 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 2 | ||||
-rw-r--r-- | fs/cifs/file.c | 202 |
4 files changed, 217 insertions, 6 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index a8323f1dc1c4..f2970136d17d 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c | |||
@@ -600,10 +600,17 @@ static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov, | |||
600 | { | 600 | { |
601 | struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode; | 601 | struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode; |
602 | ssize_t written; | 602 | ssize_t written; |
603 | int rc; | ||
603 | 604 | ||
604 | written = generic_file_aio_write(iocb, iov, nr_segs, pos); | 605 | written = generic_file_aio_write(iocb, iov, nr_segs, pos); |
605 | if (!CIFS_I(inode)->clientCanCacheAll) | 606 | |
606 | filemap_fdatawrite(inode->i_mapping); | 607 | if (CIFS_I(inode)->clientCanCacheAll) |
608 | return written; | ||
609 | |||
610 | rc = filemap_fdatawrite(inode->i_mapping); | ||
611 | if (rc) | ||
612 | cFYI(1, "cifs_file_aio_write: %d rc on %p inode", rc, inode); | ||
613 | |||
607 | return written; | 614 | return written; |
608 | } | 615 | } |
609 | 616 | ||
@@ -737,7 +744,7 @@ const struct file_operations cifs_file_strict_ops = { | |||
737 | .read = do_sync_read, | 744 | .read = do_sync_read, |
738 | .write = do_sync_write, | 745 | .write = do_sync_write, |
739 | .aio_read = cifs_strict_readv, | 746 | .aio_read = cifs_strict_readv, |
740 | .aio_write = cifs_file_aio_write, | 747 | .aio_write = cifs_strict_writev, |
741 | .open = cifs_open, | 748 | .open = cifs_open, |
742 | .release = cifs_close, | 749 | .release = cifs_close, |
743 | .lock = cifs_lock, | 750 | .lock = cifs_lock, |
@@ -793,7 +800,7 @@ const struct file_operations cifs_file_strict_nobrl_ops = { | |||
793 | .read = do_sync_read, | 800 | .read = do_sync_read, |
794 | .write = do_sync_write, | 801 | .write = do_sync_write, |
795 | .aio_read = cifs_strict_readv, | 802 | .aio_read = cifs_strict_readv, |
796 | .aio_write = cifs_file_aio_write, | 803 | .aio_write = cifs_strict_writev, |
797 | .open = cifs_open, | 804 | .open = cifs_open, |
798 | .release = cifs_close, | 805 | .release = cifs_close, |
799 | .fsync = cifs_strict_fsync, | 806 | .fsync = cifs_strict_fsync, |
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index f23206d46531..14789a97304e 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h | |||
@@ -85,7 +85,9 @@ extern ssize_t cifs_user_read(struct file *file, char __user *read_data, | |||
85 | extern ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov, | 85 | extern ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov, |
86 | unsigned long nr_segs, loff_t pos); | 86 | unsigned long nr_segs, loff_t pos); |
87 | extern ssize_t cifs_user_write(struct file *file, const char __user *write_data, | 87 | extern ssize_t cifs_user_write(struct file *file, const char __user *write_data, |
88 | size_t write_size, loff_t *poffset); | 88 | size_t write_size, loff_t *poffset); |
89 | extern ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov, | ||
90 | unsigned long nr_segs, loff_t pos); | ||
89 | extern int cifs_lock(struct file *, int, struct file_lock *); | 91 | extern int cifs_lock(struct file *, int, struct file_lock *); |
90 | extern int cifs_fsync(struct file *, int); | 92 | extern int cifs_fsync(struct file *, int); |
91 | extern int cifs_strict_fsync(struct file *, int); | 93 | extern int cifs_strict_fsync(struct file *, int); |
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 982895fa7615..35c989f4924f 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h | |||
@@ -85,6 +85,8 @@ extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length); | |||
85 | extern bool is_valid_oplock_break(struct smb_hdr *smb, | 85 | extern bool is_valid_oplock_break(struct smb_hdr *smb, |
86 | struct TCP_Server_Info *); | 86 | struct TCP_Server_Info *); |
87 | extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof); | 87 | extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof); |
88 | extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, | ||
89 | unsigned int bytes_written); | ||
88 | extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool); | 90 | extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool); |
89 | extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool); | 91 | extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool); |
90 | extern unsigned int smbCalcSize(struct smb_hdr *ptr); | 92 | extern unsigned int smbCalcSize(struct smb_hdr *ptr); |
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index d7d65a70678e..0de17c1db608 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
@@ -848,7 +848,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) | |||
848 | } | 848 | } |
849 | 849 | ||
850 | /* update the file size (if needed) after a write */ | 850 | /* update the file size (if needed) after a write */ |
851 | static void | 851 | void |
852 | cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, | 852 | cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, |
853 | unsigned int bytes_written) | 853 | unsigned int bytes_written) |
854 | { | 854 | { |
@@ -1619,6 +1619,206 @@ int cifs_flush(struct file *file, fl_owner_t id) | |||
1619 | return rc; | 1619 | return rc; |
1620 | } | 1620 | } |
1621 | 1621 | ||
1622 | static int | ||
1623 | cifs_write_allocate_pages(struct page **pages, unsigned long num_pages) | ||
1624 | { | ||
1625 | int rc = 0; | ||
1626 | unsigned long i; | ||
1627 | |||
1628 | for (i = 0; i < num_pages; i++) { | ||
1629 | pages[i] = alloc_page(__GFP_HIGHMEM); | ||
1630 | if (!pages[i]) { | ||
1631 | /* | ||
1632 | * save number of pages we have already allocated and | ||
1633 | * return with ENOMEM error | ||
1634 | */ | ||
1635 | num_pages = i; | ||
1636 | rc = -ENOMEM; | ||
1637 | goto error; | ||
1638 | } | ||
1639 | } | ||
1640 | |||
1641 | return rc; | ||
1642 | |||
1643 | error: | ||
1644 | for (i = 0; i < num_pages; i++) | ||
1645 | put_page(pages[i]); | ||
1646 | return rc; | ||
1647 | } | ||
1648 | |||
1649 | static inline | ||
1650 | size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len) | ||
1651 | { | ||
1652 | size_t num_pages; | ||
1653 | size_t clen; | ||
1654 | |||
1655 | clen = min_t(const size_t, len, wsize); | ||
1656 | num_pages = clen / PAGE_CACHE_SIZE; | ||
1657 | if (clen % PAGE_CACHE_SIZE) | ||
1658 | num_pages++; | ||
1659 | |||
1660 | if (cur_len) | ||
1661 | *cur_len = clen; | ||
1662 | |||
1663 | return num_pages; | ||
1664 | } | ||
1665 | |||
1666 | static ssize_t | ||
1667 | cifs_iovec_write(struct file *file, const struct iovec *iov, | ||
1668 | unsigned long nr_segs, loff_t *poffset) | ||
1669 | { | ||
1670 | size_t total_written = 0, written = 0; | ||
1671 | unsigned long num_pages, npages; | ||
1672 | size_t copied, len, cur_len, i; | ||
1673 | struct kvec *to_send; | ||
1674 | struct page **pages; | ||
1675 | struct iov_iter it; | ||
1676 | struct inode *inode; | ||
1677 | struct cifsFileInfo *open_file; | ||
1678 | struct cifsTconInfo *pTcon; | ||
1679 | struct cifs_sb_info *cifs_sb; | ||
1680 | int xid, rc; | ||
1681 | |||
1682 | len = iov_length(iov, nr_segs); | ||
1683 | if (!len) | ||
1684 | return 0; | ||
1685 | |||
1686 | rc = generic_write_checks(file, poffset, &len, 0); | ||
1687 | if (rc) | ||
1688 | return rc; | ||
1689 | |||
1690 | cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); | ||
1691 | num_pages = get_numpages(cifs_sb->wsize, len, &cur_len); | ||
1692 | |||
1693 | pages = kmalloc(sizeof(struct pages *)*num_pages, GFP_KERNEL); | ||
1694 | if (!pages) | ||
1695 | return -ENOMEM; | ||
1696 | |||
1697 | to_send = kmalloc(sizeof(struct kvec)*(num_pages + 1), GFP_KERNEL); | ||
1698 | if (!to_send) { | ||
1699 | kfree(pages); | ||
1700 | return -ENOMEM; | ||
1701 | } | ||
1702 | |||
1703 | rc = cifs_write_allocate_pages(pages, num_pages); | ||
1704 | if (rc) { | ||
1705 | kfree(pages); | ||
1706 | kfree(to_send); | ||
1707 | return rc; | ||
1708 | } | ||
1709 | |||
1710 | xid = GetXid(); | ||
1711 | open_file = file->private_data; | ||
1712 | pTcon = tlink_tcon(open_file->tlink); | ||
1713 | inode = file->f_path.dentry->d_inode; | ||
1714 | |||
1715 | iov_iter_init(&it, iov, nr_segs, len, 0); | ||
1716 | npages = num_pages; | ||
1717 | |||
1718 | do { | ||
1719 | size_t save_len = cur_len; | ||
1720 | for (i = 0; i < npages; i++) { | ||
1721 | copied = min_t(const size_t, cur_len, PAGE_CACHE_SIZE); | ||
1722 | copied = iov_iter_copy_from_user(pages[i], &it, 0, | ||
1723 | copied); | ||
1724 | cur_len -= copied; | ||
1725 | iov_iter_advance(&it, copied); | ||
1726 | to_send[i+1].iov_base = kmap(pages[i]); | ||
1727 | to_send[i+1].iov_len = copied; | ||
1728 | } | ||
1729 | |||
1730 | cur_len = save_len - cur_len; | ||
1731 | |||
1732 | do { | ||
1733 | if (open_file->invalidHandle) { | ||
1734 | rc = cifs_reopen_file(open_file, false); | ||
1735 | if (rc != 0) | ||
1736 | break; | ||
1737 | } | ||
1738 | rc = CIFSSMBWrite2(xid, pTcon, open_file->netfid, | ||
1739 | cur_len, *poffset, &written, | ||
1740 | to_send, npages, 0); | ||
1741 | } while (rc == -EAGAIN); | ||
1742 | |||
1743 | for (i = 0; i < npages; i++) | ||
1744 | kunmap(pages[i]); | ||
1745 | |||
1746 | if (written) { | ||
1747 | len -= written; | ||
1748 | total_written += written; | ||
1749 | cifs_update_eof(CIFS_I(inode), *poffset, written); | ||
1750 | *poffset += written; | ||
1751 | } else if (rc < 0) { | ||
1752 | if (!total_written) | ||
1753 | total_written = rc; | ||
1754 | break; | ||
1755 | } | ||
1756 | |||
1757 | /* get length and number of kvecs of the next write */ | ||
1758 | npages = get_numpages(cifs_sb->wsize, len, &cur_len); | ||
1759 | } while (len > 0); | ||
1760 | |||
1761 | if (total_written > 0) { | ||
1762 | spin_lock(&inode->i_lock); | ||
1763 | if (*poffset > inode->i_size) | ||
1764 | i_size_write(inode, *poffset); | ||
1765 | spin_unlock(&inode->i_lock); | ||
1766 | } | ||
1767 | |||
1768 | cifs_stats_bytes_written(pTcon, total_written); | ||
1769 | mark_inode_dirty_sync(inode); | ||
1770 | |||
1771 | for (i = 0; i < num_pages; i++) | ||
1772 | put_page(pages[i]); | ||
1773 | kfree(to_send); | ||
1774 | kfree(pages); | ||
1775 | FreeXid(xid); | ||
1776 | return total_written; | ||
1777 | } | ||
1778 | |||
1779 | static ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov, | ||
1780 | unsigned long nr_segs, loff_t pos) | ||
1781 | { | ||
1782 | ssize_t written; | ||
1783 | struct inode *inode; | ||
1784 | |||
1785 | inode = iocb->ki_filp->f_path.dentry->d_inode; | ||
1786 | |||
1787 | /* | ||
1788 | * BB - optimize the way when signing is disabled. We can drop this | ||
1789 | * extra memory-to-memory copying and use iovec buffers for constructing | ||
1790 | * write request. | ||
1791 | */ | ||
1792 | |||
1793 | written = cifs_iovec_write(iocb->ki_filp, iov, nr_segs, &pos); | ||
1794 | if (written > 0) { | ||
1795 | CIFS_I(inode)->invalid_mapping = true; | ||
1796 | iocb->ki_pos = pos; | ||
1797 | } | ||
1798 | |||
1799 | return written; | ||
1800 | } | ||
1801 | |||
1802 | ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov, | ||
1803 | unsigned long nr_segs, loff_t pos) | ||
1804 | { | ||
1805 | struct inode *inode; | ||
1806 | |||
1807 | inode = iocb->ki_filp->f_path.dentry->d_inode; | ||
1808 | |||
1809 | if (CIFS_I(inode)->clientCanCacheAll) | ||
1810 | return generic_file_aio_write(iocb, iov, nr_segs, pos); | ||
1811 | |||
1812 | /* | ||
1813 | * In strict cache mode we need to write the data to the server exactly | ||
1814 | * from the pos to pos+len-1 rather than flush all affected pages | ||
1815 | * because it may cause a error with mandatory locks on these pages but | ||
1816 | * not on the region from pos to ppos+len-1. | ||
1817 | */ | ||
1818 | |||
1819 | return cifs_user_writev(iocb, iov, nr_segs, pos); | ||
1820 | } | ||
1821 | |||
1622 | static ssize_t | 1822 | static ssize_t |
1623 | cifs_iovec_read(struct file *file, const struct iovec *iov, | 1823 | cifs_iovec_read(struct file *file, const struct iovec *iov, |
1624 | unsigned long nr_segs, loff_t *poffset) | 1824 | unsigned long nr_segs, loff_t *poffset) |