Skip to content

Commit

Permalink
NFS: Ensure that setattr and getattr wait for O_DIRECT write completion
Browse files Browse the repository at this point in the history
Use the same mechanism as the block devices are using, but move the
helper functions from fs/direct-io.c into fs/inode.c to remove the
dependency on CONFIG_BLOCK.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Fred Isaman <iisaman@netapp.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Trond Myklebust authored and Linus Torvalds committed May 31, 2012
1 parent 2d11740 commit 1d59d61
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 54 deletions.
44 changes: 0 additions & 44 deletions fs/direct-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,50 +145,6 @@ struct dio {

static struct kmem_cache *dio_cache __read_mostly;

static void __inode_dio_wait(struct inode *inode)
{
wait_queue_head_t *wq = bit_waitqueue(&inode->i_state, __I_DIO_WAKEUP);
DEFINE_WAIT_BIT(q, &inode->i_state, __I_DIO_WAKEUP);

do {
prepare_to_wait(wq, &q.wait, TASK_UNINTERRUPTIBLE);
if (atomic_read(&inode->i_dio_count))
schedule();
} while (atomic_read(&inode->i_dio_count));
finish_wait(wq, &q.wait);
}

/**
* inode_dio_wait - wait for outstanding DIO requests to finish
* @inode: inode to wait for
*
* Waits for all pending direct I/O requests to finish so that we can
* proceed with a truncate or equivalent operation.
*
* Must be called under a lock that serializes taking new references
* to i_dio_count, usually by inode->i_mutex.
*/
void inode_dio_wait(struct inode *inode)
{
if (atomic_read(&inode->i_dio_count))
__inode_dio_wait(inode);
}
EXPORT_SYMBOL(inode_dio_wait);

/*
* inode_dio_done - signal finish of a direct I/O requests
* @inode: inode the direct I/O happens on
*
* This is called once we've finished processing a direct I/O request,
* and is used to wake up callers waiting for direct I/O to be quiesced.
*/
void inode_dio_done(struct inode *inode)
{
if (atomic_dec_and_test(&inode->i_dio_count))
wake_up_bit(&inode->i_state, __I_DIO_WAKEUP);
}
EXPORT_SYMBOL(inode_dio_done);

/*
* How many pages are in the queue?
*/
Expand Down
47 changes: 47 additions & 0 deletions fs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1748,3 +1748,50 @@ bool inode_owner_or_capable(const struct inode *inode)
return false;
}
EXPORT_SYMBOL(inode_owner_or_capable);

/*
* Direct i/o helper functions
*/
static void __inode_dio_wait(struct inode *inode)
{
wait_queue_head_t *wq = bit_waitqueue(&inode->i_state, __I_DIO_WAKEUP);
DEFINE_WAIT_BIT(q, &inode->i_state, __I_DIO_WAKEUP);

do {
prepare_to_wait(wq, &q.wait, TASK_UNINTERRUPTIBLE);
if (atomic_read(&inode->i_dio_count))
schedule();
} while (atomic_read(&inode->i_dio_count));
finish_wait(wq, &q.wait);
}

/**
* inode_dio_wait - wait for outstanding DIO requests to finish
* @inode: inode to wait for
*
* Waits for all pending direct I/O requests to finish so that we can
* proceed with a truncate or equivalent operation.
*
* Must be called under a lock that serializes taking new references
* to i_dio_count, usually by inode->i_mutex.
*/
void inode_dio_wait(struct inode *inode)
{
if (atomic_read(&inode->i_dio_count))
__inode_dio_wait(inode);
}
EXPORT_SYMBOL(inode_dio_wait);

/*
* inode_dio_done - signal finish of a direct I/O requests
* @inode: inode the direct I/O happens on
*
* This is called once we've finished processing a direct I/O request,
* and is used to wake up callers waiting for direct I/O to be quiesced.
*/
void inode_dio_done(struct inode *inode)
{
if (atomic_dec_and_test(&inode->i_dio_count))
wake_up_bit(&inode->i_state, __I_DIO_WAKEUP);
}
EXPORT_SYMBOL(inode_dio_done);
15 changes: 12 additions & 3 deletions fs/nfs/direct.c
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,12 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov,
return result;
}

static void nfs_inode_dio_write_done(struct inode *inode)
{
nfs_zap_mapping(inode, inode->i_mapping);
inode_dio_done(inode);
}

#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
{
Expand Down Expand Up @@ -564,7 +570,7 @@ static void nfs_direct_write_schedule_work(struct work_struct *work)
nfs_direct_write_reschedule(dreq);
break;
default:
nfs_zap_mapping(dreq->inode, dreq->inode->i_mapping);
nfs_inode_dio_write_done(dreq->inode);
nfs_direct_complete(dreq);
}
}
Expand All @@ -581,7 +587,7 @@ static void nfs_direct_write_schedule_work(struct work_struct *work)

static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode)
{
nfs_zap_mapping(inode, inode->i_mapping);
nfs_inode_dio_write_done(inode);
nfs_direct_complete(dreq);
}
#endif
Expand Down Expand Up @@ -766,14 +772,16 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
loff_t pos)
{
struct nfs_pageio_descriptor desc;
struct inode *inode = dreq->inode;
ssize_t result = 0;
size_t requested_bytes = 0;
unsigned long seg;

nfs_pageio_init_write(&desc, dreq->inode, FLUSH_COND_STABLE,
nfs_pageio_init_write(&desc, inode, FLUSH_COND_STABLE,
&nfs_direct_write_completion_ops);
desc.pg_dreq = dreq;
get_dreq(dreq);
atomic_inc(&inode->i_dio_count);

for (seg = 0; seg < nr_segs; seg++) {
const struct iovec *vec = &iov[seg];
Expand All @@ -793,6 +801,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
* generic layer handle the completion.
*/
if (requested_bytes == 0) {
inode_dio_done(inode);
nfs_direct_req_release(dreq);
return result < 0 ? result : -EIO;
}
Expand Down
5 changes: 4 additions & 1 deletion fs/nfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -418,8 +418,10 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
return 0;

/* Write all dirty data */
if (S_ISREG(inode->i_mode))
if (S_ISREG(inode->i_mode)) {
nfs_inode_dio_wait(inode);
nfs_wb_all(inode);
}

fattr = nfs_alloc_fattr();
if (fattr == NULL)
Expand Down Expand Up @@ -503,6 +505,7 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)

/* Flush out writes to the server in order to update c/mtime. */
if (S_ISREG(inode->i_mode)) {
nfs_inode_dio_wait(inode);
err = filemap_write_and_wait(inode->i_mapping);
if (err)
goto out;
Expand Down
4 changes: 4 additions & 0 deletions fs/nfs/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,10 @@ extern int nfs_migrate_page(struct address_space *,
/* direct.c */
void nfs_init_cinfo_from_dreq(struct nfs_commit_info *cinfo,
struct nfs_direct_req *dreq);
static inline void nfs_inode_dio_wait(struct inode *inode)
{
inode_dio_wait(inode);
}

/* nfs4proc.c */
extern void __nfs4_read_done_cb(struct nfs_read_data *);
Expand Down
9 changes: 3 additions & 6 deletions include/linux/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -2453,8 +2453,6 @@ enum {
};

void dio_end_io(struct bio *bio, int error);
void inode_dio_wait(struct inode *inode);
void inode_dio_done(struct inode *inode);

ssize_t __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
struct block_device *bdev, const struct iovec *iov, loff_t offset,
Expand All @@ -2469,12 +2467,11 @@ static inline ssize_t blockdev_direct_IO(int rw, struct kiocb *iocb,
offset, nr_segs, get_block, NULL, NULL,
DIO_LOCKING | DIO_SKIP_HOLES);
}
#else
static inline void inode_dio_wait(struct inode *inode)
{
}
#endif

void inode_dio_wait(struct inode *inode);
void inode_dio_done(struct inode *inode);

extern const struct file_operations generic_ro_fops;

#define special_file(m) (S_ISCHR(m)||S_ISBLK(m)||S_ISFIFO(m)||S_ISSOCK(m))
Expand Down

0 comments on commit 1d59d61

Please sign in to comment.