2012/2/3 Jeff Layton <jlayton@xxxxxxxxxx>: > Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx> > --- > fs/cifs/cifsproto.h | 2 + > fs/cifs/cifssmb.c | 4 +- > fs/cifs/file.c | 191 ++++++++++++++++++++++++++++++++------------------- > 3 files changed, 126 insertions(+), 71 deletions(-) > > diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h > index bceadc5..a131ded 100644 > --- a/fs/cifs/cifsproto.h > +++ b/fs/cifs/cifsproto.h > @@ -473,6 +473,8 @@ int cifs_async_readv(struct cifs_readdata *rdata); > /* asynchronous write support */ > struct cifs_writedata { > struct kref refcount; > + struct list_head list; > + struct completion done; > enum writeback_sync_modes sync_mode; > struct work_struct work; > struct cifsFileInfo *cfile; > diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c > index 113b5af..6e93c9e 100644 > --- a/fs/cifs/cifssmb.c > +++ b/fs/cifs/cifssmb.c > @@ -2034,8 +2034,10 @@ cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete) > wdata = kzalloc(sizeof(*wdata) + > sizeof(struct page *) * (nr_pages - 1), GFP_NOFS); > if (wdata != NULL) { > - INIT_WORK(&wdata->work, complete); > kref_init(&wdata->refcount); > + INIT_LIST_HEAD(&wdata->list); > + init_completion(&wdata->done); > + INIT_WORK(&wdata->work, complete); > } > return wdata; > } > diff --git a/fs/cifs/file.c b/fs/cifs/file.c > index c7e47a2..ef25fc5 100644 > --- a/fs/cifs/file.c > +++ b/fs/cifs/file.c > @@ -2057,23 +2057,55 @@ size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len) > return num_pages; > } > > +static void > +cifs_uncached_marshal_iov(struct kvec *iov, struct cifs_writedata *wdata) > +{ > + int i; > + size_t bytes = wdata->bytes; > + > + /* marshal up the pages into iov array */ > + for (i = 0; i < wdata->nr_pages; i++) { > + iov[i + 1].iov_len = min(bytes, PAGE_SIZE); > + iov[i + 1].iov_base = kmap(wdata->pages[i]); > + bytes -= iov[i + 1].iov_len; > + } > +} > + > +static void > +cifs_uncached_writev_complete(struct work_struct *work) > +{ > + int i; > + struct cifs_writedata *wdata = container_of(work, > + struct cifs_writedata, work); > + > + complete(&wdata->done); > + > + if (wdata->result != -EAGAIN) { > + for (i = 0; i < wdata->nr_pages; i++) > + put_page(wdata->pages[i]); > + } > + > + kref_put(&wdata->refcount, cifs_writedata_release); > +} > + > static ssize_t > cifs_iovec_write(struct file *file, const struct iovec *iov, > unsigned long nr_segs, loff_t *poffset) > { > - unsigned int written; > - unsigned long num_pages, npages, i; > + unsigned long nr_pages, i; > size_t copied, len, cur_len; > ssize_t total_written = 0; > - struct kvec *to_send; > - struct page **pages; > + loff_t offset = *poffset; > struct iov_iter it; > - struct inode *inode; > + struct inode *inode = file->f_path.dentry->d_inode; > + struct cifsInodeInfo *cifsi = CIFS_I(inode); > struct cifsFileInfo *open_file; > struct cifs_tcon *tcon; > struct cifs_sb_info *cifs_sb; > - int xid, rc; > - __u32 pid; > + struct cifs_writedata *wdata, *tmp; > + struct list_head wdata_list; > + int rc; > + pid_t pid; > > len = iov_length(iov, nr_segs); > if (!len) > @@ -2083,103 +2115,122 @@ cifs_iovec_write(struct file *file, const struct iovec *iov, > if (rc) > return rc; > > + INIT_LIST_HEAD(&wdata_list); > 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; > + tcon = tlink_tcon(open_file->tlink); > > if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) > pid = open_file->pid; > else > pid = current->tgid; > > - tcon = 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); > + size_t save_len; > + > + nr_pages = get_numpages(cifs_sb->wsize, len, &cur_len); > + wdata = cifs_writedata_alloc(nr_pages, > + cifs_uncached_writev_complete); > + if (!wdata) { > + rc = -ENOMEM; > + break; > + } > + > + rc = cifs_write_allocate_pages(wdata->pages, nr_pages); > + if (rc) { > + kfree(wdata); > + break; > + } > + > + save_len = cur_len; > + for (i = 0; i < nr_pages; i++) { > + copied = min_t(const size_t, cur_len, PAGE_SIZE); > + copied = iov_iter_copy_from_user(wdata->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; > > + wdata->sync_mode = WB_SYNC_ALL; > + wdata->nr_pages = nr_pages; > + wdata->offset = (__u64)offset; > + wdata->cfile = cifsFileInfo_get(open_file); > + wdata->pid = pid; > + wdata->bytes = cur_len; > + wdata->marshal_iov = cifs_uncached_marshal_iov; > do { > if (open_file->invalidHandle) { > rc = cifs_reopen_file(open_file, false); > if (rc != 0) > break; > } When we reopen a file, we do it with the current pid, but in cfile->pid leaves with the old value (and pid variable too).Should we do reopen with cfile->pid to save the original state of the file (now we don't restore locks, if we do it - it will be the problem)? > - io_parms.netfid = open_file->netfid; > - io_parms.pid = pid; > - io_parms.tcon = tcon; > - io_parms.offset = *poffset; > - io_parms.length = cur_len; > - rc = CIFSSMBWrite2(xid, &io_parms, &written, to_send, > - npages, 0); > + rc = cifs_async_writev(wdata); > } 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; > + if (rc) { > + kref_put(&wdata->refcount, cifs_writedata_release); > break; > } > > - /* get length and number of kvecs of the next write */ > - npages = get_numpages(cifs_sb->wsize, len, &cur_len); > + list_add_tail(&wdata->list, &wdata_list); > + offset += cur_len; > + len -= cur_len; > } while (len > 0); > > + /* > + * If at least one write was successfully sent, then discard any rc > + * value from the later writes. If the other write succeeds, then > + * we'll end up returning whatever was written. If it fails, then > + * we'll get a new rc value from that. > + */ > + if (!list_empty(&wdata_list)) > + rc = 0; > + > + /* > + * Wait for and collect replies for any successful sends in order of > + * increasing offset. Once an error is hit or we get a fatal signal > + * while waiting, then return without waiting for any more replies. > + */ > +restart_loop: > + list_for_each_entry_safe(wdata, tmp, &wdata_list, list) { > + if (!rc) { > + /* FIXME: freezable too? */ > + rc = wait_for_completion_killable(&wdata->done); > + if (!rc) { > + if (wdata->result) > + rc = wdata->result; > + else > + total_written += wdata->bytes; > + } else if (rc == -EAGAIN) { > + do { > + if (open_file->invalidHandle) { > + rc = cifs_reopen_file(open_file, > + false); > + if (rc != 0) > + break; > + } > + rc = cifs_async_writev(wdata); > + } while (rc == -EAGAIN); > + goto restart_loop; > + } > + } > + list_del_init(&wdata->list); > + kref_put(&wdata->refcount, cifs_writedata_release); > + } > + > if (total_written > 0) { > + cifs_update_eof(cifsi, *poffset, total_written); > + *poffset += total_written; > spin_lock(&inode->i_lock); > - if (*poffset > inode->i_size) > - i_size_write(inode, *poffset); > + if (offset > inode->i_size) > + i_size_write(inode, cifsi->server_eof); > spin_unlock(&inode->i_lock); > } > > cifs_stats_bytes_written(tcon, 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; > + return total_written ? total_written : (ssize_t)rc; > } > > ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov, > -- > 1.7.7.6 > The patch itself looks correct. Reviewed-by: Pavel Shilovsky <piastry@xxxxxxxxxxx> -- Best regards, Pavel Shilovsky. -- To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html