Skip to content

Commit

Permalink
Merge git://git.linux-nfs.org/pub/linux/nfs-2.6
Browse files Browse the repository at this point in the history
* git://git.linux-nfs.org/pub/linux/nfs-2.6:
  NFS: Clean up new multi-segment direct I/O changes
  NFS: Ensure we return zero if applications attempt to write zero bytes
  NFS: Support multiple segment iovecs in the NFS direct I/O path
  NFS: Introduce iovec I/O helpers to fs/nfs/direct.c
  SUNRPC: Add missing "space" to net/sunrpc/auth_gss.c
  SUNRPC: make sunrpc/xprtsock.c:xs_setup_{udp,tcp}() static
  NFS: fs/nfs/dir.c should #include "internal.h"
  NFS: make nfs_wb_page_priority() static
  NFS: mount failure causes bad page state
  SUNRPC: remove NFS/RDMA client's binary sysctls
  kernel BUG at fs/nfs/namespace.c:108! - can be triggered by bad server
  sunrpc: rpc_pipe_poll may miss available data in some cases
  sunrpc: return error if unsupported enctype or cksumtype is encountered
  sunrpc: gss_pipe_downcall(), don't assume all errors are transient
  NFS: Fix the ustat() regression
  • Loading branch information
Linus Torvalds committed Nov 27, 2007
2 parents 0685ab4 + 02fe494 commit 423eaf8
Show file tree
Hide file tree
Showing 14 changed files with 155 additions and 124 deletions.
1 change: 1 addition & 0 deletions fs/nfs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "nfs4_fs.h"
#include "delegation.h"
#include "iostat.h"
#include "internal.h"

/* #define NFS_DEBUG_VERBOSE 1 */

Expand Down
142 changes: 99 additions & 43 deletions fs/nfs/direct.c
Original file line number Diff line number Diff line change
Expand Up @@ -263,17 +263,19 @@ static const struct rpc_call_ops nfs_read_direct_ops = {
* handled automatically by nfs_direct_read_result(). Otherwise, if
* no requests have been sent, just return an error.
*/
static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos)
static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
const struct iovec *iov,
loff_t pos)
{
struct nfs_open_context *ctx = dreq->ctx;
struct inode *inode = ctx->path.dentry->d_inode;
unsigned long user_addr = (unsigned long)iov->iov_base;
size_t count = iov->iov_len;
size_t rsize = NFS_SERVER(inode)->rsize;
unsigned int pgbase;
int result;
ssize_t started = 0;

get_dreq(dreq);

do {
struct nfs_read_data *data;
size_t bytes;
Expand Down Expand Up @@ -347,15 +349,46 @@ static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned lo
count -= bytes;
} while (count != 0);

if (started)
return started;
return result < 0 ? (ssize_t) result : -EFAULT;
}

static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
const struct iovec *iov,
unsigned long nr_segs,
loff_t pos)
{
ssize_t result = -EINVAL;
size_t requested_bytes = 0;
unsigned long seg;

get_dreq(dreq);

for (seg = 0; seg < nr_segs; seg++) {
const struct iovec *vec = &iov[seg];
result = nfs_direct_read_schedule_segment(dreq, vec, pos);
if (result < 0)
break;
requested_bytes += result;
if ((size_t)result < vec->iov_len)
break;
pos += vec->iov_len;
}

if (put_dreq(dreq))
nfs_direct_complete(dreq);

if (started)
if (requested_bytes != 0)
return 0;
return result < 0 ? (ssize_t) result : -EFAULT;

if (result < 0)
return result;
return -EIO;
}

static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos)
static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
ssize_t result = 0;
sigset_t oldset;
Expand All @@ -372,9 +405,8 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size
if (!is_sync_kiocb(iocb))
dreq->iocb = iocb;

nfs_add_stats(inode, NFSIOS_DIRECTREADBYTES, count);
rpc_clnt_sigmask(clnt, &oldset);
result = nfs_direct_read_schedule(dreq, user_addr, count, pos);
result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos);
if (!result)
result = nfs_direct_wait(dreq);
rpc_clnt_sigunmask(clnt, &oldset);
Expand Down Expand Up @@ -601,17 +633,19 @@ static const struct rpc_call_ops nfs_write_direct_ops = {
* handled automatically by nfs_direct_write_result(). Otherwise, if
* no requests have been sent, just return an error.
*/
static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos, int sync)
static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
const struct iovec *iov,
loff_t pos, int sync)
{
struct nfs_open_context *ctx = dreq->ctx;
struct inode *inode = ctx->path.dentry->d_inode;
unsigned long user_addr = (unsigned long)iov->iov_base;
size_t count = iov->iov_len;
size_t wsize = NFS_SERVER(inode)->wsize;
unsigned int pgbase;
int result;
ssize_t started = 0;

get_dreq(dreq);

do {
struct nfs_write_data *data;
size_t bytes;
Expand Down Expand Up @@ -689,15 +723,48 @@ static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned l
count -= bytes;
} while (count != 0);

if (started)
return started;
return result < 0 ? (ssize_t) result : -EFAULT;
}

static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
const struct iovec *iov,
unsigned long nr_segs,
loff_t pos, int sync)
{
ssize_t result = 0;
size_t requested_bytes = 0;
unsigned long seg;

get_dreq(dreq);

for (seg = 0; seg < nr_segs; seg++) {
const struct iovec *vec = &iov[seg];
result = nfs_direct_write_schedule_segment(dreq, vec,
pos, sync);
if (result < 0)
break;
requested_bytes += result;
if ((size_t)result < vec->iov_len)
break;
pos += vec->iov_len;
}

if (put_dreq(dreq))
nfs_direct_write_complete(dreq, inode);
nfs_direct_write_complete(dreq, dreq->inode);

if (started)
if (requested_bytes != 0)
return 0;
return result < 0 ? (ssize_t) result : -EFAULT;

if (result < 0)
return result;
return -EIO;
}

static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos)
static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos,
size_t count)
{
ssize_t result = 0;
sigset_t oldset;
Expand All @@ -720,10 +787,8 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, siz
if (!is_sync_kiocb(iocb))
dreq->iocb = iocb;

nfs_add_stats(inode, NFSIOS_DIRECTWRITTENBYTES, count);

rpc_clnt_sigmask(clnt, &oldset);
result = nfs_direct_write_schedule(dreq, user_addr, count, pos, sync);
result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, sync);
if (!result)
result = nfs_direct_wait(dreq);
rpc_clnt_sigunmask(clnt, &oldset);
Expand Down Expand Up @@ -759,21 +824,16 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
ssize_t retval = -EINVAL;
struct file *file = iocb->ki_filp;
struct address_space *mapping = file->f_mapping;
/* XXX: temporary */
const char __user *buf = iov[0].iov_base;
size_t count = iov[0].iov_len;
size_t count;

count = iov_length(iov, nr_segs);
nfs_add_stats(mapping->host, NFSIOS_DIRECTREADBYTES, count);

dprintk("nfs: direct read(%s/%s, %lu@%Ld)\n",
dprintk("nfs: direct read(%s/%s, %zd@%Ld)\n",
file->f_path.dentry->d_parent->d_name.name,
file->f_path.dentry->d_name.name,
(unsigned long) count, (long long) pos);

if (nr_segs != 1)
goto out;
count, (long long) pos);

retval = -EFAULT;
if (!access_ok(VERIFY_WRITE, buf, count))
goto out;
retval = 0;
if (!count)
goto out;
Expand All @@ -782,7 +842,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
if (retval)
goto out;

retval = nfs_direct_read(iocb, (unsigned long) buf, count, pos);
retval = nfs_direct_read(iocb, iov, nr_segs, pos);
if (retval > 0)
iocb->ki_pos = pos + retval;

Expand Down Expand Up @@ -821,21 +881,21 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
ssize_t retval = -EINVAL;
struct file *file = iocb->ki_filp;
struct address_space *mapping = file->f_mapping;
/* XXX: temporary */
const char __user *buf = iov[0].iov_base;
size_t count = iov[0].iov_len;
size_t count;

count = iov_length(iov, nr_segs);
nfs_add_stats(mapping->host, NFSIOS_DIRECTWRITTENBYTES, count);

dprintk("nfs: direct write(%s/%s, %lu@%Ld)\n",
dfprintk(VFS, "nfs: direct write(%s/%s, %zd@%Ld)\n",
file->f_path.dentry->d_parent->d_name.name,
file->f_path.dentry->d_name.name,
(unsigned long) count, (long long) pos);

if (nr_segs != 1)
goto out;
count, (long long) pos);

retval = generic_write_checks(file, &pos, &count, 0);
if (retval)
goto out;
if (!count)
goto out; /* return 0 */

retval = -EINVAL;
if ((ssize_t) count < 0)
Expand All @@ -844,15 +904,11 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
if (!count)
goto out;

retval = -EFAULT;
if (!access_ok(VERIFY_READ, buf, count))
goto out;

retval = nfs_sync_mapping(mapping);
if (retval)
goto out;

retval = nfs_direct_write(iocb, (unsigned long) buf, count, pos);
retval = nfs_direct_write(iocb, iov, nr_segs, pos, count);

if (retval > 0)
iocb->ki_pos = pos + retval;
Expand Down
81 changes: 27 additions & 54 deletions fs/nfs/getroot.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,25 @@

#define NFSDBG_FACILITY NFSDBG_CLIENT

/*
* Set the superblock root dentry.
* Note that this function frees the inode in case of error.
*/
static int nfs_superblock_set_dummy_root(struct super_block *sb, struct inode *inode)
{
/* The mntroot acts as the dummy root dentry for this superblock */
if (sb->s_root == NULL) {
sb->s_root = d_alloc_root(inode);
if (sb->s_root == NULL) {
iput(inode);
return -ENOMEM;
}
/* Circumvent igrab(): we know the inode is not being freed */
atomic_inc(&inode->i_count);
}
return 0;
}

/*
* get an NFS2/NFS3 root dentry from the root filehandle
*/
Expand All @@ -54,33 +73,6 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh)
struct inode *inode;
int error;

/* create a dummy root dentry with dummy inode for this superblock */
if (!sb->s_root) {
struct nfs_fh dummyfh;
struct dentry *root;
struct inode *iroot;

memset(&dummyfh, 0, sizeof(dummyfh));
memset(&fattr, 0, sizeof(fattr));
nfs_fattr_init(&fattr);
fattr.valid = NFS_ATTR_FATTR;
fattr.type = NFDIR;
fattr.mode = S_IFDIR | S_IRUSR | S_IWUSR;
fattr.nlink = 2;

iroot = nfs_fhget(sb, &dummyfh, &fattr);
if (IS_ERR(iroot))
return ERR_PTR(PTR_ERR(iroot));

root = d_alloc_root(iroot);
if (!root) {
iput(iroot);
return ERR_PTR(-ENOMEM);
}

sb->s_root = root;
}

/* get the actual root for this mount */
fsinfo.fattr = &fattr;

Expand All @@ -96,6 +88,10 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh)
return ERR_PTR(PTR_ERR(inode));
}

error = nfs_superblock_set_dummy_root(sb, inode);
if (error != 0)
return ERR_PTR(error);

/* root dentries normally start off anonymous and get spliced in later
* if the dentry tree reaches them; however if the dentry already
* exists, we'll pick it up at this point and use it as the root
Expand Down Expand Up @@ -241,33 +237,6 @@ struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh)

dprintk("--> nfs4_get_root()\n");

/* create a dummy root dentry with dummy inode for this superblock */
if (!sb->s_root) {
struct nfs_fh dummyfh;
struct dentry *root;
struct inode *iroot;

memset(&dummyfh, 0, sizeof(dummyfh));
memset(&fattr, 0, sizeof(fattr));
nfs_fattr_init(&fattr);
fattr.valid = NFS_ATTR_FATTR;
fattr.type = NFDIR;
fattr.mode = S_IFDIR | S_IRUSR | S_IWUSR;
fattr.nlink = 2;

iroot = nfs_fhget(sb, &dummyfh, &fattr);
if (IS_ERR(iroot))
return ERR_PTR(PTR_ERR(iroot));

root = d_alloc_root(iroot);
if (!root) {
iput(iroot);
return ERR_PTR(-ENOMEM);
}

sb->s_root = root;
}

/* get the info about the server and filesystem */
error = nfs4_server_capabilities(server, mntfh);
if (error < 0) {
Expand All @@ -289,6 +258,10 @@ struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh)
return ERR_PTR(PTR_ERR(inode));
}

error = nfs_superblock_set_dummy_root(sb, inode);
if (error != 0)
return ERR_PTR(error);

/* root dentries normally start off anonymous and get spliced in later
* if the dentry tree reaches them; however if the dentry already
* exists, we'll pick it up at this point and use it as the root
Expand Down
Loading

0 comments on commit 423eaf8

Please sign in to comment.