static int do_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local) { struct inode *inode = filp->f_mapping->host; struct nfs_lock_context *l_ctx; int status; /* * Flush all pending writes before doing anything * with locks.. */ vfs_fsync(filp, 0); l_ctx = nfs_get_lock_context(nfs_file_open_context(filp)); if (!IS_ERR(l_ctx)) { status = nfs_iocounter_wait(&l_ctx->io_count); nfs_put_lock_context(l_ctx); if (status < 0) return status; } /* NOTE: special case * If we're signalled while cleaning up locks on process exit, we * still need to complete the unlock. */ /* * Use local locking if mounted with "-onolock" or with appropriate * "-olocal_lock=" */ if (!is_local) status = NFS_PROTO(inode)->lock(filp, cmd, fl); else status = do_vfs_lock(filp, fl); return status; }
/* * Flush any dirty pages for this process, and check for write errors. * The return status from this call provides a reliable indication of * whether any write errors occurred for this process. * * Notice that it clears the NFS_CONTEXT_ERROR_WRITE before synching to * disk, but it retrieves and clears ctx->error after synching, despite * the two being set at the same time in nfs_context_set_write_error(). * This is because the former is used to notify the _next_ call to * nfs_file_write() that a write error occurred, and hence cause it to * fall back to doing a synchronous write. */ int nfs_file_fsync_commit(struct file *file, loff_t start, loff_t end, int datasync) { struct dentry *dentry = file->f_path.dentry; struct nfs_open_context *ctx = nfs_file_open_context(file); struct inode *inode = dentry->d_inode; int have_error, status; int ret = 0; dprintk("NFS: fsync file(%s/%s) datasync %d\n", dentry->d_parent->d_name.name, dentry->d_name.name, datasync); nfs_inc_stats(inode, NFSIOS_VFSFSYNC); have_error = test_and_clear_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); status = nfs_commit_inode(inode, FLUSH_SYNC); if (status >= 0 && ret < 0) status = ret; have_error |= test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); if (have_error) ret = xchg(&ctx->error, 0); if (!ret && status < 0) ret = status; return ret; }
int nfs_flush_incompatible(struct file *file, struct page *page) { struct nfs_open_context *ctx = nfs_file_open_context(file); struct nfs_page *req; int do_flush, status; /* * Look for a request corresponding to this page. If there * is one, and it belongs to another file, we flush it out * before we try to copy anything into the page. Do this * due to the lack of an ACCESS-type call in NFSv2. * Also do the same if we find a request from an existing * dropped page. */ do { req = nfs_page_find_request(page); if (req == NULL) return 0; do_flush = req->wb_page != page || req->wb_context != ctx; nfs_release_request(req); if (!do_flush) return 0; status = nfs_wb_page(page->mapping->host, page); } while (status == 0); return status; }
/* * Flush any dirty pages for this process, and check for write errors. * The return status from this call provides a reliable indication of * whether any write errors occurred for this process. * * Notice that it clears the NFS_CONTEXT_ERROR_WRITE before synching to * disk, but it retrieves and clears ctx->error after synching, despite * the two being set at the same time in nfs_context_set_write_error(). * This is because the former is used to notify the _next_ call to * nfs_file_write() that a write error occurred, and hence cause it to * fall back to doing a synchronous write. */ static int nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync) { struct dentry *dentry = file->f_path.dentry; struct nfs_open_context *ctx = nfs_file_open_context(file); struct inode *inode = dentry->d_inode; int have_error, status; int ret = 0; dprintk("NFS: fsync file(%s/%s) datasync %d\n", dentry->d_parent->d_name.name, dentry->d_name.name, datasync); ret = filemap_write_and_wait_range(inode->i_mapping, start, end); mutex_lock(&inode->i_mutex); nfs_inc_stats(inode, NFSIOS_VFSFSYNC); have_error = test_and_clear_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); status = nfs_commit_inode(inode, FLUSH_SYNC); if (status >= 0 && ret < 0) status = ret; have_error |= test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); if (have_error) ret = xchg(&ctx->error, 0); if (!ret && status < 0) ret = status; if (!ret && !datasync) /* application has asked for meta-data sync */ ret = pnfs_layoutcommit_inode(inode, true); mutex_unlock(&inode->i_mutex); return ret; }
static struct nfs_commit_data * cbio_init(struct ploop_io * io, void * priv) { struct inode *inode = io->files.inode; struct nfs_open_context *ctx; struct nfs_commit_data * creq; creq = nfsio_cbio_alloc(); if (unlikely(creq == NULL)) return NULL; ctx = nfs_file_open_context(io->files.file); creq->inode = inode; creq->cred = ctx->cred; creq->mds_ops = &nfsio_commit_ops; creq->dreq = priv; creq->args.fh = NFS_FH(inode); creq->args.offset = 0; creq->args.count = 0; creq->context = nfsio_get_open_context(ctx); creq->res.fattr = &creq->fattr; creq->res.verf = &creq->verf; return creq; }
/* * Flush any dirty pages for this process, and check for write errors. * The return status from this call provides a reliable indication of * whether any write errors occurred for this process. * * Notice that it clears the NFS_CONTEXT_ERROR_WRITE before synching to * disk, but it retrieves and clears ctx->error after synching, despite * the two being set at the same time in nfs_context_set_write_error(). * This is because the former is used to notify the _next_ call to * nfs_file_write() that a write error occurred, and hence cause it to * fall back to doing a synchronous write. */ int nfs_file_fsync_commit(struct file *file, loff_t start, loff_t end, int datasync) { struct nfs_open_context *ctx = nfs_file_open_context(file); struct inode *inode = file_inode(file); int have_error, do_resend, status; int ret = 0; dprintk("NFS: fsync file(%pD2) datasync %d\n", file, datasync); nfs_inc_stats(inode, NFSIOS_VFSFSYNC); do_resend = test_and_clear_bit(NFS_CONTEXT_RESEND_WRITES, &ctx->flags); have_error = test_and_clear_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); status = nfs_commit_inode(inode, FLUSH_SYNC); have_error |= test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); if (have_error) { ret = xchg(&ctx->error, 0); if (ret) goto out; } if (status < 0) { ret = status; goto out; } do_resend |= test_bit(NFS_CONTEXT_RESEND_WRITES, &ctx->flags); if (do_resend) ret = -EAGAIN; out: return ret; }
static ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe, struct file *filp, loff_t *ppos, size_t count, unsigned int flags) { struct dentry *dentry = filp->f_path.dentry; struct inode *inode = dentry->d_inode; ssize_t ret; dprintk("NFS splice_write(%s/%s, %lu@%llu)\n", dentry->d_parent->d_name.name, dentry->d_name.name, (unsigned long) count, (unsigned long long) *ppos); /* * The combination of splice and an O_APPEND destination is disallowed. */ nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, count); ret = generic_file_splice_write(pipe, filp, ppos, count, flags); if (ret >= 0 && nfs_need_sync_write(filp, inode)) { int err = nfs_do_fsync(nfs_file_open_context(filp), inode); if (err < 0) ret = err; } return ret; }
static int nfs_need_check_write(struct file *filp, struct inode *inode) { struct nfs_open_context *ctx; ctx = nfs_file_open_context(filp); if (test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags) || nfs_ctx_key_to_expire(ctx)) return 1; return 0; }
static int nfs_need_sync_write(struct file *filp, struct inode *inode) { struct nfs_open_context *ctx; if (IS_SYNC(inode) || (filp->f_flags & O_SYNC)) return 1; ctx = nfs_file_open_context(filp); if (test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags)) return 1; return 0; }
/* * Flush any dirty pages for this process, and check for write errors. * The return status from this call provides a reliable indication of * whether any write errors occurred for this process. */ static int nfs_fsync(struct file *file, struct dentry *dentry, int datasync) { struct nfs_open_context *ctx = nfs_file_open_context(file); struct inode *inode = dentry->d_inode; dfprintk(VFS, "nfs: fsync(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino); nfs_inc_stats(inode, NFSIOS_VFSFSYNC); return nfs_do_fsync(ctx, inode); }
/* * Flush any dirty pages for this process, and check for write errors. * The return status from this call provides a reliable indication of * whether any write errors occurred for this process. */ static int nfs_file_fsync(struct file *file, struct dentry *dentry, int datasync) { struct nfs_open_context *ctx = nfs_file_open_context(file); struct inode *inode = dentry->d_inode; dprintk("NFS: fsync file(%s/%s) datasync %d\n", dentry->d_parent->d_name.name, dentry->d_name.name, datasync); nfs_inc_stats(inode, NFSIOS_VFSFSYNC); return nfs_do_fsync(ctx, inode); }
static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) { struct dentry * dentry = iocb->ki_filp->f_path.dentry; struct inode * inode = dentry->d_inode; unsigned long written = 0; ssize_t result; size_t count = iov_length(iov, nr_segs); if (iocb->ki_filp->f_flags & O_DIRECT) return nfs_file_direct_write(iocb, iov, nr_segs, pos); dprintk("NFS: write(%s/%s, %lu@%Ld)\n", dentry->d_parent->d_name.name, dentry->d_name.name, (unsigned long) count, (long long) pos); result = -EBUSY; if (IS_SWAPFILE(inode)) goto out_swapfile; /* * O_APPEND implies that we must revalidate the file length. */ if (iocb->ki_filp->f_flags & O_APPEND) { result = nfs_revalidate_file_size(inode, iocb->ki_filp); if (result) goto out; } result = count; if (!count) goto out; result = generic_file_aio_write(iocb, iov, nr_segs, pos); if (result > 0) written = result; /* Return error values for O_DSYNC and IS_SYNC() */ if (result >= 0 && nfs_need_sync_write(iocb->ki_filp, inode)) { int err = nfs_do_fsync(nfs_file_open_context(iocb->ki_filp), inode); if (err < 0) result = err; } if (result > 0) nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, written); out: return result; out_swapfile: printk(KERN_INFO "NFS: attempt to write to active swap file!\n"); goto out; }
static int nfs_write_end(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, struct page *page, void *fsdata) { unsigned offset = pos & (PAGE_CACHE_SIZE - 1); struct nfs_open_context *ctx = nfs_file_open_context(file); int status; dfprintk(PAGECACHE, "NFS: write_end(%pD2(%lu), %u@%lld)\n", file, mapping->host->i_ino, len, (long long) pos); /* * Zero any uninitialised parts of the page, and then mark the page * as up to date if it turns out that we're extending the file. */ if (!PageUptodate(page)) { unsigned pglen = nfs_page_length(page); unsigned end = offset + len; if (pglen == 0) { zero_user_segments(page, 0, offset, end, PAGE_CACHE_SIZE); SetPageUptodate(page); } else if (end >= pglen) { zero_user_segment(page, end, PAGE_CACHE_SIZE); if (offset == 0) SetPageUptodate(page); } else zero_user_segment(page, pglen, PAGE_CACHE_SIZE); } status = nfs_updatepage(file, page, offset, copied); unlock_page(page); page_cache_release(page); if (status < 0) return status; NFS_I(mapping->host)->write_io += copied; if (nfs_ctx_key_to_expire(ctx)) { status = nfs_wb_all(mapping->host); if (status < 0) return status; } return copied; }
/* * Flush all dirty pages, and check for write errors. */ static int nfs_file_flush(struct file *file, fl_owner_t id) { struct nfs_open_context *ctx = nfs_file_open_context(file); struct dentry *dentry = file->f_path.dentry; struct inode *inode = dentry->d_inode; dprintk("NFS: flush(%s/%s)\n", dentry->d_parent->d_name.name, dentry->d_name.name); if ((file->f_mode & FMODE_WRITE) == 0) return 0; nfs_inc_stats(inode, NFSIOS_VFSFLUSH); /* Flush writes to the server and return any errors */ return nfs_do_fsync(ctx, inode); }
/* * Flush all dirty pages, and check for write errors. * */ static int nfs_file_flush(struct file *file, fl_owner_t id) { struct nfs_open_context *ctx = nfs_file_open_context(file); struct inode *inode = file->f_path.dentry->d_inode; int status; dfprintk(VFS, "nfs: flush(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino); if ((file->f_mode & FMODE_WRITE) == 0) return 0; nfs_inc_stats(inode, NFSIOS_VFSFLUSH); /* Ensure that data+attribute caches are up to date after close() */ status = nfs_do_fsync(ctx, inode); if (!status) nfs_revalidate_inode(NFS_SERVER(inode), inode); return status; }
static int nfs42_set_rw_stateid(nfs4_stateid *dst, struct file *file, fmode_t fmode) { struct nfs_open_context *open; struct nfs_lock_context *lock; int ret; open = get_nfs_open_context(nfs_file_open_context(file)); lock = nfs_get_lock_context(open); if (IS_ERR(lock)) { put_nfs_open_context(open); return PTR_ERR(lock); } ret = nfs4_set_rw_stateid(dst, open, lock, fmode); nfs_put_lock_context(lock); put_nfs_open_context(open); return ret; }
static int nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl) { struct inode *inode = file_inode(filp); struct nfs_lock_context *l_ctx = NULL; struct nfs_open_context *ctx = nfs_file_open_context(filp); int status; if (fl->fl_flags & FL_CLOSE) { l_ctx = nfs_get_lock_context(ctx); if (IS_ERR(l_ctx)) l_ctx = NULL; else set_bit(NFS_CONTEXT_UNLOCK, &ctx->flags); } status = nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl, l_ctx); if (l_ctx) nfs_put_lock_context(l_ctx); return status; }
/* * Update and possibly write a cached page of an NFS file. * * XXX: Keep an eye on generic_file_read to make sure it doesn't do bad * things with a page scheduled for an RPC call (e.g. invalidate it). */ int nfs_updatepage(struct file *file, struct page *page, unsigned int offset, unsigned int count) { struct nfs_open_context *ctx = nfs_file_open_context(file); struct inode *inode = page->mapping->host; int status = 0; nfs_inc_stats(inode, NFSIOS_VFSUPDATEPAGE); dprintk("NFS: nfs_updatepage(%s/%s %d@%lld)\n", file->f_path.dentry->d_parent->d_name.name, file->f_path.dentry->d_name.name, count, (long long)(page_offset(page) + offset)); /* If we're not using byte range locks, and we know the page * is up to date, it may be more efficient to extend the write * to cover the entire page in order to avoid fragmentation * inefficiencies. */ if (nfs_write_pageuptodate(page, inode) && inode->i_flock == NULL && !(file->f_flags & O_DSYNC)) { count = max(count + offset, nfs_page_length(page)); offset = 0; } status = nfs_writepage_setup(ctx, page, offset, count); if (status < 0) nfs_set_pageerror(page); else __set_page_dirty_nobuffers(page); dprintk("NFS: nfs_updatepage returns %d (isize %lld)\n", status, (long long)i_size_read(inode)); return status; }
static int rbio_submit(struct ploop_io * io, struct nfs_read_data * nreq, const struct rpc_call_ops * cb) { struct nfs_open_context *ctx = nfs_file_open_context(io->files.file); struct inode *inode = io->files.inode; struct rpc_task *task; struct rpc_message msg = { .rpc_cred = ctx->cred, }; struct rpc_task_setup task_setup_data = { .rpc_client = NFS_CLIENT(inode), .rpc_message = &msg, .callback_ops = cb, #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25) .workqueue = nfsio_workqueue, #endif .flags = RPC_TASK_ASYNC, }; nreq->res.count = nreq->args.count; nreq->header->cred = msg.rpc_cred; nreq->args.context = ctx; task_setup_data.task = &nreq->task; task_setup_data.callback_data = nreq; msg.rpc_argp = &nreq->args; msg.rpc_resp = &nreq->res; NFS_PROTO(inode)->read_setup(nreq, &msg); task = rpc_run_task(&task_setup_data); if (unlikely(IS_ERR(task))) return PTR_ERR(task); rpc_put_task(task); return 0; } #else static int rbio_submit(struct ploop_io * io, struct nfs_read_data * nreq, const struct rpc_call_ops * cb) { struct nfs_open_context *ctx = nfs_file_open_context(io->files.file); struct inode *inode = io->files.inode; nreq->res.count = nreq->args.count; nreq->cred = ctx->cred; nreq->args.context = ctx; rpc_init_task(&nreq->task, NFS_CLIENT(inode), RPC_TASK_ASYNC, cb, nreq); NFS_PROTO(inode)->read_setup(nreq); nreq->task.tk_cookie = (unsigned long) inode; lock_kernel(); rpc_execute(&nreq->task); unlock_kernel(); return 0; } #endif static void nfsio_submit_read(struct ploop_io *io, struct ploop_request * preq, struct bio_list *sbl, iblock_t iblk, unsigned int size) { struct inode *inode = io->files.inode; size_t rsize = NFS_SERVER(inode)->rsize; struct nfs_read_data *nreq = NULL; loff_t pos; unsigned int prev_end; struct bio * b; ploop_prepare_io_request(preq); pos = sbl->head->bi_sector; pos = ((loff_t)iblk << preq->plo->cluster_log) | (pos & ((1<<preq->plo->cluster_log) - 1)); pos <<= 9; prev_end = PAGE_SIZE; for (b = sbl->head; b != NULL; b = b->bi_next) { int bv_idx; for (bv_idx = 0; bv_idx < b->bi_vcnt; bv_idx++) { struct bio_vec * bv = &b->bi_io_vec[bv_idx]; if (nreq && nreq->args.count + bv->bv_len <= rsize) { if (nreq->pages.pagevec[nreq->pages.npages-1] == bv->bv_page && prev_end == bv->bv_offset) { nreq->args.count += bv->bv_len; pos += bv->bv_len; prev_end += bv->bv_len; continue; } if (nreq->pages.npages < MAX_NBIO_PAGES && bv->bv_offset == 0 && prev_end == PAGE_SIZE) { nreq->args.count += bv->bv_len; nreq->pages.pagevec[nreq->pages.npages] = bv->bv_page; nreq->pages.npages++; pos += bv->bv_len; prev_end = bv->bv_offset + bv->bv_len; continue; } } if (nreq) { int err; atomic_inc(&preq->io_count); err = rbio_submit(io, nreq, &nfsio_read_ops); if (err) { PLOOP_REQ_SET_ERROR(preq, err); ploop_complete_io_request(preq); goto out; } } nreq = rbio_init(pos, bv->bv_page, bv->bv_offset, bv->bv_len, preq, inode); if (nreq == NULL) { PLOOP_REQ_SET_ERROR(preq, -ENOMEM); goto out; } pos += bv->bv_len; prev_end = bv->bv_offset + bv->bv_len; } } if (nreq) { int err; atomic_inc(&preq->io_count); err = rbio_submit(io, nreq, &nfsio_read_ops); if (err) { PLOOP_REQ_SET_ERROR(preq, err); ploop_complete_io_request(preq); goto out; } } out: ploop_complete_io_request(preq); } static void nfsio_write_result(struct rpc_task *task, void *calldata) { struct nfs_write_data *data = calldata; struct nfs_writeargs *argp = &data->args; struct nfs_writeres *resp = &data->res; int status; status = NFS_PROTO(data->header->inode)->write_done(task, data); if (status != 0) return; if (task->tk_status >= 0 && resp->count < argp->count) task->tk_status = -EIO; } static void nfsio_write_release(void *calldata) { struct nfs_write_data *nreq = calldata; struct ploop_request *preq = (struct ploop_request *) nreq->header->req; int status = nreq->task.tk_status; if (unlikely(status < 0)) PLOOP_REQ_SET_ERROR(preq, status); if (!preq->error && nreq->res.verf->committed != NFS_FILE_SYNC) { if (!test_and_set_bit(PLOOP_REQ_UNSTABLE, &preq->state)) memcpy(&preq->verf, &nreq->res.verf->verifier, 8); } nfsio_complete_io_request(preq); nfsio_wbio_release(calldata); } static const struct rpc_call_ops nfsio_write_ops = { .rpc_call_done = nfsio_write_result, .rpc_release = nfsio_write_release, }; static struct nfs_write_data * wbio_init(loff_t pos, struct page * page, unsigned int off, unsigned int len, void * priv, struct inode * inode) { struct nfs_write_data * nreq; nreq = nfsio_wbio_alloc(MAX_NBIO_PAGES); if (unlikely(nreq == NULL)) return NULL; nreq->args.offset = pos; nreq->args.pgbase = off; nreq->args.count = len; nreq->pages.pagevec[0] = page; nreq->pages.npages = 1; nreq->header->req = priv; nreq->header->inode = inode; nreq->args.fh = NFS_FH(inode); nreq->args.pages = nreq->pages.pagevec; nreq->args.stable = NFS_UNSTABLE; nreq->res.fattr = &nreq->fattr; nreq->res.verf = &nreq->verf; return nreq; } #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18) static int wbio_submit(struct ploop_io * io, struct nfs_write_data *nreq, const struct rpc_call_ops * cb) { struct nfs_open_context *ctx = nfs_file_open_context(io->files.file); struct inode *inode = io->files.inode; struct rpc_task *task; struct rpc_message msg = { .rpc_cred = ctx->cred, }; struct rpc_task_setup task_setup_data = { .rpc_client = NFS_CLIENT(inode), .rpc_message = &msg, .callback_ops = cb, #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25) .workqueue = nfsio_workqueue, #endif .flags = RPC_TASK_ASYNC, }; if (verify_bounce(nreq)) return -ENOMEM; nreq->res.count = nreq->args.count; nreq->args.context = ctx; nreq->header->cred = msg.rpc_cred; task_setup_data.task = &nreq->task; task_setup_data.callback_data = nreq; msg.rpc_argp = &nreq->args; msg.rpc_resp = &nreq->res; NFS_PROTO(inode)->write_setup(nreq, &msg); task = rpc_run_task(&task_setup_data); if (unlikely(IS_ERR(task))) return PTR_ERR(task); rpc_put_task(task); return 0; } #else static int wbio_submit(struct ploop_io * io, struct nfs_write_data *nreq, const struct rpc_call_ops * cb) { struct nfs_open_context *ctx = nfs_file_open_context(io->files.file); struct inode *inode = io->files.inode; if (verify_bounce(nreq)) return -ENOMEM; nreq->res.count = nreq->args.count; nreq->args.context = ctx; nreq->cred = ctx->cred; rpc_init_task(&nreq->task, NFS_CLIENT(inode), RPC_TASK_ASYNC, cb, nreq); NFS_PROTO(inode)->write_setup(nreq, NFS_UNSTABLE); nreq->task.tk_priority = RPC_PRIORITY_NORMAL; nreq->task.tk_cookie = (unsigned long) inode; lock_kernel(); rpc_execute(&nreq->task); unlock_kernel(); return 0; } #endif static void nfsio_submit_write(struct ploop_io *io, struct ploop_request * preq, struct bio_list *sbl, iblock_t iblk, unsigned int size) { struct inode *inode = io->files.inode; size_t wsize = NFS_SERVER(inode)->wsize; struct nfs_write_data *nreq = NULL; loff_t pos; struct bio * b; unsigned int prev_end; nfsio_prepare_io_request(preq); pos = sbl->head->bi_sector; pos = ((loff_t)iblk << preq->plo->cluster_log) | (pos & ((1<<preq->plo->cluster_log) - 1)); ploop_prepare_tracker(preq, pos); pos <<= 9; prev_end = PAGE_SIZE; for (b = sbl->head; b != NULL; b = b->bi_next) { int bv_idx; for (bv_idx = 0; bv_idx < b->bi_vcnt; bv_idx++) { struct bio_vec * bv = &b->bi_io_vec[bv_idx]; if (nreq && nreq->args.count + bv->bv_len <= wsize) { if (nreq->pages.pagevec[nreq->pages.npages-1] == bv->bv_page && prev_end == bv->bv_offset) { nreq->args.count += bv->bv_len; pos += bv->bv_len; prev_end += bv->bv_len; continue; } if (nreq->pages.npages < MAX_NBIO_PAGES && bv->bv_offset == 0 && prev_end == PAGE_SIZE) { nreq->args.count += bv->bv_len; nreq->pages.pagevec[nreq->pages.npages] = bv->bv_page; nreq->pages.npages++; pos += bv->bv_len; prev_end = bv->bv_offset + bv->bv_len; continue; } } if (nreq) { int err; atomic_inc(&preq->io_count); err = wbio_submit(io, nreq, &nfsio_write_ops); if (err) { PLOOP_REQ_SET_ERROR(preq, err); nfsio_complete_io_request(preq); goto out; } } nreq = wbio_init(pos, bv->bv_page, bv->bv_offset, bv->bv_len, preq, inode); if (nreq == NULL) { PLOOP_REQ_SET_ERROR(preq, -ENOMEM); goto out; } prev_end = bv->bv_offset + bv->bv_len; pos += bv->bv_len; } } if (nreq) { int err; atomic_inc(&preq->io_count); err = wbio_submit(io, nreq, &nfsio_write_ops); if (err) { PLOOP_REQ_SET_ERROR(preq, err); nfsio_complete_io_request(preq); } } out: nfsio_complete_io_request(preq); } static void nfsio_submit(struct ploop_io *io, struct ploop_request * preq, unsigned long rw, struct bio_list *sbl, iblock_t iblk, unsigned int size) { if (iblk == PLOOP_ZERO_INDEX) iblk = 0; if (rw & (1<<BIO_RW)) nfsio_submit_write(io, preq, sbl, iblk, size); else nfsio_submit_read(io, preq, sbl, iblk, size); } struct bio_list_walk { struct bio * cur; int idx; int bv_off; }; static void nfsio_submit_write_pad(struct ploop_io *io, struct ploop_request * preq, struct bio_list *sbl, iblock_t iblk, unsigned int size) { struct inode *inode = io->files.inode; size_t wsize = NFS_SERVER(inode)->wsize; struct nfs_write_data *nreq = NULL; struct bio_list_walk bw; unsigned prev_end; loff_t pos, end_pos, start, end; /* pos..end_pos is the range which we are going to write */ pos = (loff_t)iblk << (preq->plo->cluster_log + 9); end_pos = pos + (1 << (preq->plo->cluster_log + 9)); /* start..end is data that we have. The rest must be zero padded. */ start = pos + ((sbl->head->bi_sector & ((1<<preq->plo->cluster_log) - 1)) << 9); end = start + (size << 9); nfsio_prepare_io_request(preq); ploop_prepare_tracker(preq, start >> 9); prev_end = PAGE_SIZE; #if 1 /* GCC, shut up! */ bw.cur = sbl->head; bw.idx = 0; bw.bv_off = 0; BUG_ON(bw.cur->bi_io_vec[0].bv_len & 511); #endif while (pos < end_pos) { struct page * page; unsigned int poff, plen; if (pos < start) { page = ZERO_PAGE(0); poff = 0; plen = start - pos; if (plen > PAGE_SIZE) plen = PAGE_SIZE; } else if (pos >= end) { page = ZERO_PAGE(0); poff = 0; plen = end_pos - pos; if (plen > PAGE_SIZE) plen = PAGE_SIZE; } else { /* pos >= start && pos < end */ struct bio_vec * bv; if (pos == start) { bw.cur = sbl->head; bw.idx = 0; bw.bv_off = 0; BUG_ON(bw.cur->bi_io_vec[0].bv_len & 511); } bv = bw.cur->bi_io_vec + bw.idx; if (bw.bv_off >= bv->bv_len) { bw.idx++; bv++; bw.bv_off = 0; if (bw.idx >= bw.cur->bi_vcnt) { bw.cur = bw.cur->bi_next; bw.idx = 0; bw.bv_off = 0; bv = bw.cur->bi_io_vec; } BUG_ON(bv->bv_len & 511); } page = bv->bv_page; poff = bv->bv_offset + bw.bv_off; plen = bv->bv_len - bw.bv_off; } if (nreq && nreq->args.count + plen <= wsize) { if (nreq->pages.pagevec[nreq->pages.npages-1] == page && prev_end == poff) { nreq->args.count += plen; pos += plen; bw.bv_off += plen; prev_end += plen; continue; } if (nreq->pages.npages < MAX_NBIO_PAGES && poff == 0 && prev_end == PAGE_SIZE) { nreq->args.count += plen; nreq->pages.pagevec[nreq->pages.npages] = page; nreq->pages.npages++; pos += plen; bw.bv_off += plen; prev_end = poff + plen; continue; } } if (nreq) { int err; atomic_inc(&preq->io_count); err = wbio_submit(io, nreq, &nfsio_write_ops); if (err) { PLOOP_REQ_SET_ERROR(preq, err); nfsio_complete_io_request(preq); goto out; } } nreq = wbio_init(pos, page, poff, plen, preq, inode); if (nreq == NULL) { PLOOP_REQ_SET_ERROR(preq, -ENOMEM); goto out; } prev_end = poff + plen; pos += plen; bw.bv_off += plen; } if (nreq) { int err; atomic_inc(&preq->io_count); err = wbio_submit(io, nreq, &nfsio_write_ops); if (err) { PLOOP_REQ_SET_ERROR(preq, err); nfsio_complete_io_request(preq); } } out: nfsio_complete_io_request(preq); } static void nfsio_submit_alloc(struct ploop_io *io, struct ploop_request * preq, struct bio_list * sbl, unsigned int size) { iblock_t iblk = io->alloc_head++; if (!(io->files.file->f_mode & FMODE_WRITE)) { PLOOP_FAIL_REQUEST(preq, -EBADF); return; } preq->iblock = iblk; preq->eng_state = PLOOP_E_DATA_WBI; nfsio_submit_write_pad(io, preq, sbl, iblk, size); } static void nfsio_destroy(struct ploop_io * io) { if (io->fsync_thread) { kthread_stop(io->fsync_thread); io->fsync_thread = NULL; } if (io->files.file) { struct file * file = io->files.file; mutex_lock(&io->plo->sysfs_mutex); io->files.file = NULL; if (io->files.mapping) (void)invalidate_inode_pages2(io->files.mapping); mutex_unlock(&io->plo->sysfs_mutex); fput(file); } } static int nfsio_sync(struct ploop_io * io) { return 0; } static int nfsio_stop(struct ploop_io * io) { return 0; } static int nfsio_init(struct ploop_io * io) { INIT_LIST_HEAD(&io->fsync_queue); init_waitqueue_head(&io->fsync_waitq); return 0; }
static int _nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep, struct nfs_lock_context *lock, loff_t offset, loff_t len) { struct inode *inode = file_inode(filep); struct nfs_server *server = NFS_SERVER(inode); struct nfs42_falloc_args args = { .falloc_fh = NFS_FH(inode), .falloc_offset = offset, .falloc_length = len, .falloc_bitmask = server->cache_consistency_bitmask, }; struct nfs42_falloc_res res = { .falloc_server = server, }; int status; msg->rpc_argp = &args; msg->rpc_resp = &res; status = nfs4_set_rw_stateid(&args.falloc_stateid, lock->open_context, lock, FMODE_WRITE); if (status) return status; res.falloc_fattr = nfs_alloc_fattr(); if (!res.falloc_fattr) return -ENOMEM; status = nfs4_call_sync(server->client, server, msg, &args.seq_args, &res.seq_res, 0); if (status == 0) status = nfs_post_op_update_inode(inode, res.falloc_fattr); kfree(res.falloc_fattr); return status; } static int nfs42_proc_fallocate(struct rpc_message *msg, struct file *filep, loff_t offset, loff_t len) { struct nfs_server *server = NFS_SERVER(file_inode(filep)); struct nfs4_exception exception = { }; struct nfs_lock_context *lock; int err; lock = nfs_get_lock_context(nfs_file_open_context(filep)); if (IS_ERR(lock)) return PTR_ERR(lock); exception.inode = file_inode(filep); exception.state = lock->open_context->state; do { err = _nfs42_proc_fallocate(msg, filep, lock, offset, len); if (err == -ENOTSUPP) { err = -EOPNOTSUPP; break; } err = nfs4_handle_exception(server, err, &exception); } while (exception.retry); nfs_put_lock_context(lock); return err; } int nfs42_proc_allocate(struct file *filep, loff_t offset, loff_t len) { struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ALLOCATE], }; struct inode *inode = file_inode(filep); int err; if (!nfs_server_capable(inode, NFS_CAP_ALLOCATE)) return -EOPNOTSUPP; inode_lock(inode); err = nfs42_proc_fallocate(&msg, filep, offset, len); if (err == -EOPNOTSUPP) NFS_SERVER(inode)->caps &= ~NFS_CAP_ALLOCATE; inode_unlock(inode); return err; } int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len) { struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DEALLOCATE], }; struct inode *inode = file_inode(filep); int err; if (!nfs_server_capable(inode, NFS_CAP_DEALLOCATE)) return -EOPNOTSUPP; inode_lock(inode); err = nfs_sync_inode(inode); if (err) goto out_unlock; err = nfs42_proc_fallocate(&msg, filep, offset, len); if (err == 0) truncate_pagecache_range(inode, offset, (offset + len) -1); if (err == -EOPNOTSUPP) NFS_SERVER(inode)->caps &= ~NFS_CAP_DEALLOCATE; out_unlock: inode_unlock(inode); return err; } static ssize_t _nfs42_proc_copy(struct file *src, loff_t pos_src, struct nfs_lock_context *src_lock, struct file *dst, loff_t pos_dst, struct nfs_lock_context *dst_lock, size_t count) { struct nfs42_copy_args args = { .src_fh = NFS_FH(file_inode(src)), .src_pos = pos_src, .dst_fh = NFS_FH(file_inode(dst)), .dst_pos = pos_dst, .count = count, }; struct nfs42_copy_res res; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COPY], .rpc_argp = &args, .rpc_resp = &res, }; struct inode *dst_inode = file_inode(dst); struct nfs_server *server = NFS_SERVER(dst_inode); int status; status = nfs4_set_rw_stateid(&args.src_stateid, src_lock->open_context, src_lock, FMODE_READ); if (status) return status; status = nfs_filemap_write_and_wait_range(file_inode(src)->i_mapping, pos_src, pos_src + (loff_t)count - 1); if (status) return status; status = nfs4_set_rw_stateid(&args.dst_stateid, dst_lock->open_context, dst_lock, FMODE_WRITE); if (status) return status; status = nfs_sync_inode(dst_inode); if (status) return status; status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); if (status == -ENOTSUPP) server->caps &= ~NFS_CAP_COPY; if (status) return status; if (res.write_res.verifier.committed != NFS_FILE_SYNC) { status = nfs_commit_file(dst, &res.write_res.verifier.verifier); if (status) return status; } truncate_pagecache_range(dst_inode, pos_dst, pos_dst + res.write_res.count); return res.write_res.count; } ssize_t nfs42_proc_copy(struct file *src, loff_t pos_src, struct file *dst, loff_t pos_dst, size_t count) { struct nfs_server *server = NFS_SERVER(file_inode(dst)); struct nfs_lock_context *src_lock; struct nfs_lock_context *dst_lock; struct nfs4_exception src_exception = { }; struct nfs4_exception dst_exception = { }; ssize_t err, err2; if (!nfs_server_capable(file_inode(dst), NFS_CAP_COPY)) return -EOPNOTSUPP; src_lock = nfs_get_lock_context(nfs_file_open_context(src)); if (IS_ERR(src_lock)) return PTR_ERR(src_lock); src_exception.inode = file_inode(src); src_exception.state = src_lock->open_context->state; dst_lock = nfs_get_lock_context(nfs_file_open_context(dst)); if (IS_ERR(dst_lock)) { err = PTR_ERR(dst_lock); goto out_put_src_lock; } dst_exception.inode = file_inode(dst); dst_exception.state = dst_lock->open_context->state; do { inode_lock(file_inode(dst)); err = _nfs42_proc_copy(src, pos_src, src_lock, dst, pos_dst, dst_lock, count); inode_unlock(file_inode(dst)); if (err == -ENOTSUPP) { err = -EOPNOTSUPP; break; } err2 = nfs4_handle_exception(server, err, &src_exception); err = nfs4_handle_exception(server, err, &dst_exception); if (!err) err = err2; } while (src_exception.retry || dst_exception.retry); nfs_put_lock_context(dst_lock); out_put_src_lock: nfs_put_lock_context(src_lock); return err; } static loff_t _nfs42_proc_llseek(struct file *filep, struct nfs_lock_context *lock, loff_t offset, int whence) { struct inode *inode = file_inode(filep); struct nfs42_seek_args args = { .sa_fh = NFS_FH(inode), .sa_offset = offset, .sa_what = (whence == SEEK_HOLE) ? NFS4_CONTENT_HOLE : NFS4_CONTENT_DATA, }; struct nfs42_seek_res res; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEEK], .rpc_argp = &args, .rpc_resp = &res, }; struct nfs_server *server = NFS_SERVER(inode); int status; if (!nfs_server_capable(inode, NFS_CAP_SEEK)) return -ENOTSUPP; status = nfs4_set_rw_stateid(&args.sa_stateid, lock->open_context, lock, FMODE_READ); if (status) return status; status = nfs_filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); if (status) return status; status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); if (status == -ENOTSUPP) server->caps &= ~NFS_CAP_SEEK; if (status) return status; return vfs_setpos(filep, res.sr_offset, inode->i_sb->s_maxbytes); } loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence) { struct nfs_server *server = NFS_SERVER(file_inode(filep)); struct nfs4_exception exception = { }; struct nfs_lock_context *lock; loff_t err; lock = nfs_get_lock_context(nfs_file_open_context(filep)); if (IS_ERR(lock)) return PTR_ERR(lock); exception.inode = file_inode(filep); exception.state = lock->open_context->state; do { err = _nfs42_proc_llseek(filep, lock, offset, whence); if (err >= 0) break; if (err == -ENOTSUPP) { err = -EOPNOTSUPP; break; } err = nfs4_handle_exception(server, err, &exception); } while (exception.retry); nfs_put_lock_context(lock); return err; } static void nfs42_layoutstat_prepare(struct rpc_task *task, void *calldata) { struct nfs42_layoutstat_data *data = calldata; struct inode *inode = data->inode; struct nfs_server *server = NFS_SERVER(inode); struct pnfs_layout_hdr *lo; spin_lock(&inode->i_lock); lo = NFS_I(inode)->layout; if (!pnfs_layout_is_valid(lo)) { spin_unlock(&inode->i_lock); rpc_exit(task, 0); return; } nfs4_stateid_copy(&data->args.stateid, &lo->plh_stateid); spin_unlock(&inode->i_lock); nfs41_setup_sequence(nfs4_get_session(server), &data->args.seq_args, &data->res.seq_res, task); } static void nfs42_layoutstat_done(struct rpc_task *task, void *calldata) { struct nfs42_layoutstat_data *data = calldata; struct inode *inode = data->inode; struct pnfs_layout_hdr *lo; if (!nfs4_sequence_done(task, &data->res.seq_res)) return; switch (task->tk_status) { case 0: break; case -NFS4ERR_EXPIRED: case -NFS4ERR_ADMIN_REVOKED: case -NFS4ERR_DELEG_REVOKED: case -NFS4ERR_STALE_STATEID: case -NFS4ERR_BAD_STATEID: spin_lock(&inode->i_lock); lo = NFS_I(inode)->layout; if (pnfs_layout_is_valid(lo) && nfs4_stateid_match(&data->args.stateid, &lo->plh_stateid)) { LIST_HEAD(head); /* * Mark the bad layout state as invalid, then retry * with the current stateid. */ pnfs_mark_layout_stateid_invalid(lo, &head); spin_unlock(&inode->i_lock); pnfs_free_lseg_list(&head); } else spin_unlock(&inode->i_lock); break; case -NFS4ERR_OLD_STATEID: spin_lock(&inode->i_lock); lo = NFS_I(inode)->layout; if (pnfs_layout_is_valid(lo) && nfs4_stateid_match_other(&data->args.stateid, &lo->plh_stateid)) { /* Do we need to delay before resending? */ if (!nfs4_stateid_is_newer(&lo->plh_stateid, &data->args.stateid)) rpc_delay(task, HZ); rpc_restart_call_prepare(task); } spin_unlock(&inode->i_lock); break; case -ENOTSUPP: case -EOPNOTSUPP: NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTSTATS; } dprintk("%s server returns %d\n", __func__, task->tk_status); } static void nfs42_layoutstat_release(void *calldata) { struct nfs42_layoutstat_data *data = calldata; struct nfs_server *nfss = NFS_SERVER(data->args.inode); if (nfss->pnfs_curr_ld->cleanup_layoutstats) nfss->pnfs_curr_ld->cleanup_layoutstats(data); pnfs_put_layout_hdr(NFS_I(data->args.inode)->layout); smp_mb__before_atomic(); clear_bit(NFS_INO_LAYOUTSTATS, &NFS_I(data->args.inode)->flags); smp_mb__after_atomic(); nfs_iput_and_deactive(data->inode); kfree(data->args.devinfo); kfree(data); } static const struct rpc_call_ops nfs42_layoutstat_ops = { .rpc_call_prepare = nfs42_layoutstat_prepare, .rpc_call_done = nfs42_layoutstat_done, .rpc_release = nfs42_layoutstat_release, }; int nfs42_proc_layoutstats_generic(struct nfs_server *server, struct nfs42_layoutstat_data *data) { struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTSTATS], .rpc_argp = &data->args, .rpc_resp = &data->res, }; struct rpc_task_setup task_setup = { .rpc_client = server->client, .rpc_message = &msg, .callback_ops = &nfs42_layoutstat_ops, .callback_data = data, .flags = RPC_TASK_ASYNC, }; struct rpc_task *task; data->inode = nfs_igrab_and_active(data->args.inode); if (!data->inode) { nfs42_layoutstat_release(data); return -EAGAIN; } nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0); task = rpc_run_task(&task_setup); if (IS_ERR(task)) return PTR_ERR(task); rpc_put_task(task); return 0; } static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f, struct file *dst_f, struct nfs_lock_context *src_lock, struct nfs_lock_context *dst_lock, loff_t src_offset, loff_t dst_offset, loff_t count) { struct inode *src_inode = file_inode(src_f); struct inode *dst_inode = file_inode(dst_f); struct nfs_server *server = NFS_SERVER(dst_inode); struct nfs42_clone_args args = { .src_fh = NFS_FH(src_inode), .dst_fh = NFS_FH(dst_inode), .src_offset = src_offset, .dst_offset = dst_offset, .count = count, .dst_bitmask = server->cache_consistency_bitmask, }; struct nfs42_clone_res res = { .server = server, }; int status; msg->rpc_argp = &args; msg->rpc_resp = &res; status = nfs4_set_rw_stateid(&args.src_stateid, src_lock->open_context, src_lock, FMODE_READ); if (status) return status; status = nfs4_set_rw_stateid(&args.dst_stateid, dst_lock->open_context, dst_lock, FMODE_WRITE); if (status) return status; res.dst_fattr = nfs_alloc_fattr(); if (!res.dst_fattr) return -ENOMEM; status = nfs4_call_sync(server->client, server, msg, &args.seq_args, &res.seq_res, 0); if (status == 0) status = nfs_post_op_update_inode(dst_inode, res.dst_fattr); kfree(res.dst_fattr); return status; } int nfs42_proc_clone(struct file *src_f, struct file *dst_f, loff_t src_offset, loff_t dst_offset, loff_t count) { struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLONE], }; struct inode *inode = file_inode(src_f); struct nfs_server *server = NFS_SERVER(file_inode(src_f)); struct nfs_lock_context *src_lock; struct nfs_lock_context *dst_lock; struct nfs4_exception src_exception = { }; struct nfs4_exception dst_exception = { }; int err, err2; if (!nfs_server_capable(inode, NFS_CAP_CLONE)) return -EOPNOTSUPP; src_lock = nfs_get_lock_context(nfs_file_open_context(src_f)); if (IS_ERR(src_lock)) return PTR_ERR(src_lock); src_exception.inode = file_inode(src_f); src_exception.state = src_lock->open_context->state; dst_lock = nfs_get_lock_context(nfs_file_open_context(dst_f)); if (IS_ERR(dst_lock)) { err = PTR_ERR(dst_lock); goto out_put_src_lock; } dst_exception.inode = file_inode(dst_f); dst_exception.state = dst_lock->open_context->state; do { err = _nfs42_proc_clone(&msg, src_f, dst_f, src_lock, dst_lock, src_offset, dst_offset, count); if (err == -ENOTSUPP || err == -EOPNOTSUPP) { NFS_SERVER(inode)->caps &= ~NFS_CAP_CLONE; err = -EOPNOTSUPP; break; } err2 = nfs4_handle_exception(server, err, &src_exception); err = nfs4_handle_exception(server, err, &dst_exception); if (!err) err = err2; } while (src_exception.retry || dst_exception.retry); nfs_put_lock_context(dst_lock); out_put_src_lock: nfs_put_lock_context(src_lock); return err; }