* [PATCH 5/6] CIFS: Implement cifs_strict_writev (try #4)
@ 2010-12-27 18:11 Pavel Shilovsky
[not found] ` <1293473503-5533-2-git-send-email-piastryyy-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
0 siblings, 1 reply; 2+ messages in thread
From: Pavel Shilovsky @ 2010-12-27 18:11 UTC (permalink / raw)
To: linux-cifs-u79uwXL29TY76Z2rM5mHXA
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-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
fs/cifs/cifsfs.c | 15 +++-
fs/cifs/cifsfs.h | 4 +-
fs/cifs/cifsproto.h | 2 +
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 0d6b005..76ba04e 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -578,10 +578,17 @@ static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
{
struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode;
ssize_t written;
+ int rc;
written = generic_file_aio_write(iocb, iov, nr_segs, pos);
- if (!CIFS_I(inode)->clientCanCacheAll)
- filemap_fdatawrite(inode->i_mapping);
+
+ if (CIFS_I(inode)->clientCanCacheAll)
+ return written;
+
+ rc = filemap_fdatawrite(inode->i_mapping);
+ if (rc)
+ cFYI(1, "cifs_file_aio_write: %d rc on %p inode", rc, inode);
+
return written;
}
@@ -715,7 +722,7 @@ const struct file_operations cifs_file_strict_ops = {
.read = do_sync_read,
.write = do_sync_write,
.aio_read = cifs_strict_readv,
- .aio_write = cifs_file_aio_write,
+ .aio_write = cifs_strict_writev,
.open = cifs_open,
.release = cifs_close,
.lock = cifs_lock,
@@ -771,7 +778,7 @@ const struct file_operations cifs_file_strict_nobrl_ops = {
.read = do_sync_read,
.write = do_sync_write,
.aio_read = cifs_strict_readv,
- .aio_write = cifs_file_aio_write,
+ .aio_write = cifs_strict_writev,
.open = cifs_open,
.release = cifs_close,
.fsync = cifs_strict_fsync,
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index d97c96e..3396cd0 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,
extern ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos);
extern ssize_t cifs_user_write(struct file *file, const char __user *write_data,
- size_t write_size, loff_t *poffset);
+ size_t write_size, loff_t *poffset);
+extern ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t pos);
extern int cifs_lock(struct file *, int, struct file_lock *);
extern int cifs_fsync(struct file *, int);
extern int cifs_strict_fsync(struct file *, int);
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index e6d1481..e7d6c24 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -79,6 +79,8 @@ extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length);
extern bool is_valid_oplock_break(struct smb_hdr *smb,
struct TCP_Server_Info *);
extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
+extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
+ unsigned int bytes_written);
extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool);
extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool);
extern unsigned int smbCalcSize(struct smb_hdr *ptr);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 0f30095..d78f464 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -924,7 +924,7 @@ cifs_write_timeout(struct cifsInodeInfo *cifsi, loff_t offset)
}
/* update the file size (if needed) after a write */
-static void
+void
cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
unsigned int bytes_written)
{
@@ -1677,6 +1677,206 @@ int cifs_flush(struct file *file, fl_owner_t id)
return rc;
}
+static int
+cifs_write_allocate_pages(struct page **pages, unsigned long num_pages)
+{
+ int rc = 0;
+ unsigned long i;
+
+ for (i = 0; i < num_pages; i++) {
+ pages[i] = alloc_page(__GFP_HIGHMEM);
+ if (!pages[i]) {
+ /*
+ * save number of pages we have already allocated and
+ * return with ENOMEM error
+ */
+ num_pages = i;
+ rc = -ENOMEM;
+ goto error;
+ }
+ }
+
+ return rc;
+
+error:
+ for (i = 0; i < num_pages; i++)
+ put_page(pages[i]);
+ return rc;
+}
+
+static inline
+size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len)
+{
+ size_t num_pages;
+ size_t clen;
+
+ clen = min_t(const size_t, len, wsize);
+ num_pages = clen / PAGE_CACHE_SIZE;
+ if (clen % PAGE_CACHE_SIZE)
+ num_pages++;
+
+ if (cur_len)
+ *cur_len = clen;
+
+ return num_pages;
+}
+
+static ssize_t
+cifs_iovec_write(struct file *file, const struct iovec *iov,
+ unsigned long nr_segs, loff_t *poffset)
+{
+ size_t total_written = 0, written = 0;
+ unsigned long num_pages, npages;
+ size_t copied, len, cur_len, i;
+ struct kvec *to_send;
+ struct page **pages;
+ struct iov_iter it;
+ struct inode *inode;
+ struct cifsFileInfo *open_file;
+ struct cifsTconInfo *pTcon;
+ struct cifs_sb_info *cifs_sb;
+ int xid, rc;
+
+ len = iov_length(iov, nr_segs);
+ if (!len)
+ return 0;
+
+ rc = generic_write_checks(file, poffset, &len, 0);
+ if (rc)
+ return rc;
+
+ cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+ num_pages = get_numpages(cifs_sb->wsize, len, &cur_len);
+
+ pages = kmalloc(sizeof(struct pages *)*num_pages, GFP_KERNEL);
+ if (!pages)
+ return -ENOMEM;
+
+ to_send = kmalloc(sizeof(struct kvec)*(num_pages + 1), GFP_KERNEL);
+ if (!to_send) {
+ kfree(pages);
+ return -ENOMEM;
+ }
+
+ rc = cifs_write_allocate_pages(pages, num_pages);
+ if (rc) {
+ kfree(pages);
+ kfree(to_send);
+ return rc;
+ }
+
+ xid = GetXid();
+ open_file = file->private_data;
+ pTcon = tlink_tcon(open_file->tlink);
+ inode = file->f_path.dentry->d_inode;
+
+ iov_iter_init(&it, iov, nr_segs, len, 0);
+ npages = num_pages;
+
+ do {
+ size_t save_len = cur_len;
+ for (i = 0; i < npages; i++) {
+ copied = min_t(const size_t, cur_len, PAGE_CACHE_SIZE);
+ copied = iov_iter_copy_from_user(pages[i], &it, 0,
+ copied);
+ cur_len -= copied;
+ iov_iter_advance(&it, copied);
+ to_send[i+1].iov_base = kmap(pages[i]);
+ to_send[i+1].iov_len = copied;
+ }
+
+ cur_len = save_len - cur_len;
+
+ do {
+ if (open_file->invalidHandle) {
+ rc = cifs_reopen_file(open_file, false);
+ if (rc != 0)
+ break;
+ }
+ rc = CIFSSMBWrite2(xid, pTcon, open_file->netfid,
+ cur_len, *poffset, &written,
+ to_send, npages, 0);
+ } while (rc == -EAGAIN);
+
+ for (i = 0; i < npages; i++)
+ kunmap(pages[i]);
+
+ if (written) {
+ len -= written;
+ total_written += written;
+ cifs_update_eof(CIFS_I(inode), *poffset, written);
+ *poffset += written;
+ } else if (rc < 0) {
+ if (!total_written)
+ total_written = rc;
+ break;
+ }
+
+ /* get length and number of kvecs of the next write */
+ npages = get_numpages(cifs_sb->wsize, len, &cur_len);
+ } while (len > 0);
+
+ if (total_written > 0) {
+ spin_lock(&inode->i_lock);
+ if (*poffset > inode->i_size)
+ i_size_write(inode, *poffset);
+ spin_unlock(&inode->i_lock);
+ }
+
+ cifs_stats_bytes_written(pTcon, total_written);
+ mark_inode_dirty_sync(inode);
+
+ for (i = 0; i < num_pages; i++)
+ put_page(pages[i]);
+ kfree(to_send);
+ kfree(pages);
+ FreeXid(xid);
+ return total_written;
+}
+
+static ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t pos)
+{
+ ssize_t written;
+ struct inode *inode;
+
+ inode = iocb->ki_filp->f_path.dentry->d_inode;
+
+ /*
+ * BB - optimize the way when signing is disabled. We can drop this
+ * extra memory-to-memory copying and use iovec buffers for constructing
+ * write request.
+ */
+
+ written = cifs_iovec_write(iocb->ki_filp, iov, nr_segs, &pos);
+ if (written > 0) {
+ CIFS_I(inode)->invalid_mapping = true;
+ iocb->ki_pos = pos;
+ }
+
+ return written;
+}
+
+ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t pos)
+{
+ struct inode *inode;
+
+ inode = iocb->ki_filp->f_path.dentry->d_inode;
+
+ if (CIFS_I(inode)->clientCanCacheAll)
+ return generic_file_aio_write(iocb, iov, nr_segs, pos);
+
+ /*
+ * In strict cache mode we need to write the data to the server exactly
+ * from the pos to pos+len-1 rather than flush all affected pages
+ * because it may cause a error with mandatory locks on these pages but
+ * not on the region from pos to ppos+len-1.
+ */
+
+ return cifs_user_writev(iocb, iov, nr_segs, pos);
+}
+
static ssize_t
cifs_iovec_read(struct file *file, const struct iovec *iov,
unsigned long nr_segs, loff_t *poffset)
--
1.7.3.4
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH 5/6] CIFS: Implement cifs_strict_writev (try #4)
[not found] ` <1293473503-5533-2-git-send-email-piastryyy-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
@ 2010-12-28 13:40 ` Jeff Layton
0 siblings, 0 replies; 2+ messages in thread
From: Jeff Layton @ 2010-12-28 13:40 UTC (permalink / raw)
To: Pavel Shilovsky; +Cc: linux-cifs-u79uwXL29TY76Z2rM5mHXA
On Mon, 27 Dec 2010 21:11:43 +0300
Pavel Shilovsky <piastryyy-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> 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-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> ---
> fs/cifs/cifsfs.c | 15 +++-
> fs/cifs/cifsfs.h | 4 +-
> fs/cifs/cifsproto.h | 2 +
> 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 0d6b005..76ba04e 100644
> --- a/fs/cifs/cifsfs.c
> +++ b/fs/cifs/cifsfs.c
> @@ -578,10 +578,17 @@ static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
> {
> struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode;
> ssize_t written;
> + int rc;
>
> written = generic_file_aio_write(iocb, iov, nr_segs, pos);
> - if (!CIFS_I(inode)->clientCanCacheAll)
> - filemap_fdatawrite(inode->i_mapping);
> +
> + if (CIFS_I(inode)->clientCanCacheAll)
> + return written;
> +
> + rc = filemap_fdatawrite(inode->i_mapping);
> + if (rc)
> + cFYI(1, "cifs_file_aio_write: %d rc on %p inode", rc, inode);
> +
> return written;
> }
>
> @@ -715,7 +722,7 @@ const struct file_operations cifs_file_strict_ops = {
> .read = do_sync_read,
> .write = do_sync_write,
> .aio_read = cifs_strict_readv,
> - .aio_write = cifs_file_aio_write,
> + .aio_write = cifs_strict_writev,
> .open = cifs_open,
> .release = cifs_close,
> .lock = cifs_lock,
> @@ -771,7 +778,7 @@ const struct file_operations cifs_file_strict_nobrl_ops = {
> .read = do_sync_read,
> .write = do_sync_write,
> .aio_read = cifs_strict_readv,
> - .aio_write = cifs_file_aio_write,
> + .aio_write = cifs_strict_writev,
> .open = cifs_open,
> .release = cifs_close,
> .fsync = cifs_strict_fsync,
> diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
> index d97c96e..3396cd0 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,
> extern ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
> unsigned long nr_segs, loff_t pos);
> extern ssize_t cifs_user_write(struct file *file, const char __user *write_data,
> - size_t write_size, loff_t *poffset);
> + size_t write_size, loff_t *poffset);
> +extern ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
> + unsigned long nr_segs, loff_t pos);
> extern int cifs_lock(struct file *, int, struct file_lock *);
> extern int cifs_fsync(struct file *, int);
> extern int cifs_strict_fsync(struct file *, int);
> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> index e6d1481..e7d6c24 100644
> --- a/fs/cifs/cifsproto.h
> +++ b/fs/cifs/cifsproto.h
> @@ -79,6 +79,8 @@ extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length);
> extern bool is_valid_oplock_break(struct smb_hdr *smb,
> struct TCP_Server_Info *);
> extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
> +extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
> + unsigned int bytes_written);
> extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool);
> extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool);
> extern unsigned int smbCalcSize(struct smb_hdr *ptr);
> diff --git a/fs/cifs/file.c b/fs/cifs/file.c
> index 0f30095..d78f464 100644
> --- a/fs/cifs/file.c
> +++ b/fs/cifs/file.c
> @@ -924,7 +924,7 @@ cifs_write_timeout(struct cifsInodeInfo *cifsi, loff_t offset)
> }
>
> /* update the file size (if needed) after a write */
> -static void
> +void
> cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
> unsigned int bytes_written)
> {
> @@ -1677,6 +1677,206 @@ int cifs_flush(struct file *file, fl_owner_t id)
> return rc;
> }
>
> +static int
> +cifs_write_allocate_pages(struct page **pages, unsigned long num_pages)
> +{
> + int rc = 0;
> + unsigned long i;
> +
> + for (i = 0; i < num_pages; i++) {
> + pages[i] = alloc_page(__GFP_HIGHMEM);
> + if (!pages[i]) {
> + /*
> + * save number of pages we have already allocated and
> + * return with ENOMEM error
> + */
> + num_pages = i;
> + rc = -ENOMEM;
> + goto error;
> + }
> + }
> +
> + return rc;
> +
> +error:
> + for (i = 0; i < num_pages; i++)
> + put_page(pages[i]);
> + return rc;
> +}
> +
> +static inline
> +size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len)
> +{
> + size_t num_pages;
> + size_t clen;
> +
> + clen = min_t(const size_t, len, wsize);
> + num_pages = clen / PAGE_CACHE_SIZE;
> + if (clen % PAGE_CACHE_SIZE)
> + num_pages++;
> +
> + if (cur_len)
> + *cur_len = clen;
> +
> + return num_pages;
> +}
> +
> +static ssize_t
> +cifs_iovec_write(struct file *file, const struct iovec *iov,
> + unsigned long nr_segs, loff_t *poffset)
> +{
> + size_t total_written = 0, written = 0;
> + unsigned long num_pages, npages;
> + size_t copied, len, cur_len, i;
> + struct kvec *to_send;
> + struct page **pages;
> + struct iov_iter it;
> + struct inode *inode;
> + struct cifsFileInfo *open_file;
> + struct cifsTconInfo *pTcon;
> + struct cifs_sb_info *cifs_sb;
> + int xid, rc;
> +
> + len = iov_length(iov, nr_segs);
> + if (!len)
> + return 0;
> +
> + rc = generic_write_checks(file, poffset, &len, 0);
> + if (rc)
> + return rc;
> +
> + cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
> + num_pages = get_numpages(cifs_sb->wsize, len, &cur_len);
> +
> + pages = kmalloc(sizeof(struct pages *)*num_pages, GFP_KERNEL);
> + if (!pages)
> + return -ENOMEM;
> +
> + to_send = kmalloc(sizeof(struct kvec)*(num_pages + 1), GFP_KERNEL);
> + if (!to_send) {
> + kfree(pages);
> + return -ENOMEM;
> + }
> +
> + rc = cifs_write_allocate_pages(pages, num_pages);
> + if (rc) {
> + kfree(pages);
> + kfree(to_send);
> + return rc;
> + }
> +
> + xid = GetXid();
> + open_file = file->private_data;
> + pTcon = tlink_tcon(open_file->tlink);
> + inode = file->f_path.dentry->d_inode;
> +
> + iov_iter_init(&it, iov, nr_segs, len, 0);
> + npages = num_pages;
> +
> + do {
> + size_t save_len = cur_len;
> + for (i = 0; i < npages; i++) {
> + copied = min_t(const size_t, cur_len, PAGE_CACHE_SIZE);
> + copied = iov_iter_copy_from_user(pages[i], &it, 0,
> + copied);
> + cur_len -= copied;
> + iov_iter_advance(&it, copied);
> + to_send[i+1].iov_base = kmap(pages[i]);
> + to_send[i+1].iov_len = copied;
> + }
> +
> + cur_len = save_len - cur_len;
> +
> + do {
> + if (open_file->invalidHandle) {
> + rc = cifs_reopen_file(open_file, false);
> + if (rc != 0)
> + break;
> + }
> + rc = CIFSSMBWrite2(xid, pTcon, open_file->netfid,
> + cur_len, *poffset, &written,
> + to_send, npages, 0);
> + } while (rc == -EAGAIN);
> +
> + for (i = 0; i < npages; i++)
> + kunmap(pages[i]);
> +
> + if (written) {
> + len -= written;
> + total_written += written;
> + cifs_update_eof(CIFS_I(inode), *poffset, written);
> + *poffset += written;
> + } else if (rc < 0) {
> + if (!total_written)
> + total_written = rc;
> + break;
> + }
> +
> + /* get length and number of kvecs of the next write */
> + npages = get_numpages(cifs_sb->wsize, len, &cur_len);
> + } while (len > 0);
> +
> + if (total_written > 0) {
> + spin_lock(&inode->i_lock);
> + if (*poffset > inode->i_size)
> + i_size_write(inode, *poffset);
> + spin_unlock(&inode->i_lock);
> + }
> +
> + cifs_stats_bytes_written(pTcon, total_written);
> + mark_inode_dirty_sync(inode);
> +
> + for (i = 0; i < num_pages; i++)
> + put_page(pages[i]);
> + kfree(to_send);
> + kfree(pages);
> + FreeXid(xid);
> + return total_written;
> +}
> +
> +static ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
> + unsigned long nr_segs, loff_t pos)
> +{
> + ssize_t written;
> + struct inode *inode;
> +
> + inode = iocb->ki_filp->f_path.dentry->d_inode;
> +
> + /*
> + * BB - optimize the way when signing is disabled. We can drop this
> + * extra memory-to-memory copying and use iovec buffers for constructing
> + * write request.
> + */
> +
> + written = cifs_iovec_write(iocb->ki_filp, iov, nr_segs, &pos);
> + if (written > 0) {
> + CIFS_I(inode)->invalid_mapping = true;
> + iocb->ki_pos = pos;
> + }
> +
> + return written;
> +}
> +
> +ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
> + unsigned long nr_segs, loff_t pos)
> +{
> + struct inode *inode;
> +
> + inode = iocb->ki_filp->f_path.dentry->d_inode;
> +
> + if (CIFS_I(inode)->clientCanCacheAll)
> + return generic_file_aio_write(iocb, iov, nr_segs, pos);
> +
> + /*
> + * In strict cache mode we need to write the data to the server exactly
> + * from the pos to pos+len-1 rather than flush all affected pages
> + * because it may cause a error with mandatory locks on these pages but
> + * not on the region from pos to ppos+len-1.
> + */
> +
> + return cifs_user_writev(iocb, iov, nr_segs, pos);
> +}
> +
> static ssize_t
> cifs_iovec_read(struct file *file, const struct iovec *iov,
> unsigned long nr_segs, loff_t *poffset)
Looks good to me.
Reviewed-by: Jeff Layton <jlayton-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2010-12-28 13:40 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-12-27 18:11 [PATCH 5/6] CIFS: Implement cifs_strict_writev (try #4) Pavel Shilovsky
[not found] ` <1293473503-5533-2-git-send-email-piastryyy-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2010-12-28 13:40 ` Jeff Layton
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox