diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-04 18:34:27 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-04 18:34:27 -0400 |
| commit | d15fee814d8d30bbb4859ef8fef7a1f96327635b (patch) | |
| tree | 1168811b0b28ec6fb9ccb65476019323e5ba9035 | |
| parent | 56c225fe399c27c1f1a098048903f8e74f4ec45f (diff) | |
| parent | f3846266f593595632a07242fcbc6c24bc2ade68 (diff) | |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse
Pull fuse update from Miklos Szeredi:
"This series adds cached writeback support to fuse, improving write
throughput"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
fuse: fix "uninitialized variable" warning
fuse: Turn writeback cache on
fuse: Fix O_DIRECT operations vs cached writeback misorder
fuse: fuse_flush() should wait on writeback
fuse: Implement write_begin/write_end callbacks
fuse: restructure fuse_readpage()
fuse: Flush files on wb close
fuse: Trust kernel i_mtime only
fuse: Trust kernel i_size only
fuse: Connection bit for enabling writeback
fuse: Prepare to handle short reads
fuse: Linking file to inode helper
| -rw-r--r-- | fs/fuse/cuse.c | 5 | ||||
| -rw-r--r-- | fs/fuse/dir.c | 119 | ||||
| -rw-r--r-- | fs/fuse/file.c | 286 | ||||
| -rw-r--r-- | fs/fuse/fuse_i.h | 22 | ||||
| -rw-r--r-- | fs/fuse/inode.c | 29 | ||||
| -rw-r--r-- | include/uapi/linux/fuse.h | 7 |
6 files changed, 384 insertions, 84 deletions
diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index b96a49b37d66..23e363f38302 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c | |||
| @@ -95,7 +95,7 @@ static ssize_t cuse_read(struct file *file, char __user *buf, size_t count, | |||
| 95 | struct iovec iov = { .iov_base = buf, .iov_len = count }; | 95 | struct iovec iov = { .iov_base = buf, .iov_len = count }; |
| 96 | struct fuse_io_priv io = { .async = 0, .file = file }; | 96 | struct fuse_io_priv io = { .async = 0, .file = file }; |
| 97 | 97 | ||
| 98 | return fuse_direct_io(&io, &iov, 1, count, &pos, 0); | 98 | return fuse_direct_io(&io, &iov, 1, count, &pos, FUSE_DIO_CUSE); |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | static ssize_t cuse_write(struct file *file, const char __user *buf, | 101 | static ssize_t cuse_write(struct file *file, const char __user *buf, |
| @@ -109,7 +109,8 @@ static ssize_t cuse_write(struct file *file, const char __user *buf, | |||
| 109 | * No locking or generic_write_checks(), the server is | 109 | * No locking or generic_write_checks(), the server is |
| 110 | * responsible for locking and sanity checks. | 110 | * responsible for locking and sanity checks. |
| 111 | */ | 111 | */ |
| 112 | return fuse_direct_io(&io, &iov, 1, count, &pos, 1); | 112 | return fuse_direct_io(&io, &iov, 1, count, &pos, |
| 113 | FUSE_DIO_WRITE | FUSE_DIO_CUSE); | ||
| 113 | } | 114 | } |
| 114 | 115 | ||
| 115 | static int cuse_open(struct inode *inode, struct file *file) | 116 | static int cuse_open(struct inode *inode, struct file *file) |
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 1d1292c581c3..5b4e035b364c 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
| @@ -839,6 +839,14 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr, | |||
| 839 | struct kstat *stat) | 839 | struct kstat *stat) |
| 840 | { | 840 | { |
| 841 | unsigned int blkbits; | 841 | unsigned int blkbits; |
| 842 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 843 | |||
| 844 | /* see the comment in fuse_change_attributes() */ | ||
| 845 | if (fc->writeback_cache && S_ISREG(inode->i_mode)) { | ||
| 846 | attr->size = i_size_read(inode); | ||
| 847 | attr->mtime = inode->i_mtime.tv_sec; | ||
| 848 | attr->mtimensec = inode->i_mtime.tv_nsec; | ||
| 849 | } | ||
| 842 | 850 | ||
| 843 | stat->dev = inode->i_sb->s_dev; | 851 | stat->dev = inode->i_sb->s_dev; |
| 844 | stat->ino = attr->ino; | 852 | stat->ino = attr->ino; |
| @@ -1477,12 +1485,16 @@ static long fuse_dir_compat_ioctl(struct file *file, unsigned int cmd, | |||
| 1477 | FUSE_IOCTL_COMPAT | FUSE_IOCTL_DIR); | 1485 | FUSE_IOCTL_COMPAT | FUSE_IOCTL_DIR); |
| 1478 | } | 1486 | } |
| 1479 | 1487 | ||
| 1480 | static bool update_mtime(unsigned ivalid) | 1488 | static bool update_mtime(unsigned ivalid, bool trust_local_mtime) |
| 1481 | { | 1489 | { |
| 1482 | /* Always update if mtime is explicitly set */ | 1490 | /* Always update if mtime is explicitly set */ |
| 1483 | if (ivalid & ATTR_MTIME_SET) | 1491 | if (ivalid & ATTR_MTIME_SET) |
| 1484 | return true; | 1492 | return true; |
| 1485 | 1493 | ||
| 1494 | /* Or if kernel i_mtime is the official one */ | ||
| 1495 | if (trust_local_mtime) | ||
| 1496 | return true; | ||
| 1497 | |||
| 1486 | /* If it's an open(O_TRUNC) or an ftruncate(), don't update */ | 1498 | /* If it's an open(O_TRUNC) or an ftruncate(), don't update */ |
| 1487 | if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE))) | 1499 | if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE))) |
| 1488 | return false; | 1500 | return false; |
| @@ -1491,7 +1503,8 @@ static bool update_mtime(unsigned ivalid) | |||
| 1491 | return true; | 1503 | return true; |
| 1492 | } | 1504 | } |
| 1493 | 1505 | ||
| 1494 | static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) | 1506 | static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg, |
| 1507 | bool trust_local_mtime) | ||
| 1495 | { | 1508 | { |
| 1496 | unsigned ivalid = iattr->ia_valid; | 1509 | unsigned ivalid = iattr->ia_valid; |
| 1497 | 1510 | ||
| @@ -1510,11 +1523,11 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) | |||
| 1510 | if (!(ivalid & ATTR_ATIME_SET)) | 1523 | if (!(ivalid & ATTR_ATIME_SET)) |
| 1511 | arg->valid |= FATTR_ATIME_NOW; | 1524 | arg->valid |= FATTR_ATIME_NOW; |
| 1512 | } | 1525 | } |
| 1513 | if ((ivalid & ATTR_MTIME) && update_mtime(ivalid)) { | 1526 | if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, trust_local_mtime)) { |
| 1514 | arg->valid |= FATTR_MTIME; | 1527 | arg->valid |= FATTR_MTIME; |
| 1515 | arg->mtime = iattr->ia_mtime.tv_sec; | 1528 | arg->mtime = iattr->ia_mtime.tv_sec; |
| 1516 | arg->mtimensec = iattr->ia_mtime.tv_nsec; | 1529 | arg->mtimensec = iattr->ia_mtime.tv_nsec; |
| 1517 | if (!(ivalid & ATTR_MTIME_SET)) | 1530 | if (!(ivalid & ATTR_MTIME_SET) && !trust_local_mtime) |
| 1518 | arg->valid |= FATTR_MTIME_NOW; | 1531 | arg->valid |= FATTR_MTIME_NOW; |
| 1519 | } | 1532 | } |
| 1520 | } | 1533 | } |
| @@ -1563,6 +1576,63 @@ void fuse_release_nowrite(struct inode *inode) | |||
| 1563 | spin_unlock(&fc->lock); | 1576 | spin_unlock(&fc->lock); |
| 1564 | } | 1577 | } |
| 1565 | 1578 | ||
| 1579 | static void fuse_setattr_fill(struct fuse_conn *fc, struct fuse_req *req, | ||
| 1580 | struct inode *inode, | ||
| 1581 | struct fuse_setattr_in *inarg_p, | ||
| 1582 | struct fuse_attr_out *outarg_p) | ||
| 1583 | { | ||
| 1584 | req->in.h.opcode = FUSE_SETATTR; | ||
| 1585 | req->in.h.nodeid = get_node_id(inode); | ||
| 1586 | req->in.numargs = 1; | ||
| 1587 | req->in.args[0].size = sizeof(*inarg_p); | ||
| 1588 | req->in.args[0].value = inarg_p; | ||
| 1589 | req->out.numargs = 1; | ||
| 1590 | if (fc->minor < 9) | ||
| 1591 | req->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE; | ||
| 1592 | else | ||
| 1593 | req->out.args[0].size = sizeof(*outarg_p); | ||
| 1594 | req->out.args[0].value = outarg_p; | ||
| 1595 | } | ||
| 1596 | |||
| 1597 | /* | ||
| 1598 | * Flush inode->i_mtime to the server | ||
| 1599 | */ | ||
| 1600 | int fuse_flush_mtime(struct file *file, bool nofail) | ||
| 1601 | { | ||
| 1602 | struct inode *inode = file->f_mapping->host; | ||
| 1603 | struct fuse_inode *fi = get_fuse_inode(inode); | ||
| 1604 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 1605 | struct fuse_req *req = NULL; | ||
| 1606 | struct fuse_setattr_in inarg; | ||
| 1607 | struct fuse_attr_out outarg; | ||
| 1608 | int err; | ||
| 1609 | |||
| 1610 | if (nofail) { | ||
| 1611 | req = fuse_get_req_nofail_nopages(fc, file); | ||
| 1612 | } else { | ||
| 1613 | req = fuse_get_req_nopages(fc); | ||
| 1614 | if (IS_ERR(req)) | ||
| 1615 | return PTR_ERR(req); | ||
| 1616 | } | ||
| 1617 | |||
| 1618 | memset(&inarg, 0, sizeof(inarg)); | ||
| 1619 | memset(&outarg, 0, sizeof(outarg)); | ||
| 1620 | |||
| 1621 | inarg.valid |= FATTR_MTIME; | ||
| 1622 | inarg.mtime = inode->i_mtime.tv_sec; | ||
| 1623 | inarg.mtimensec = inode->i_mtime.tv_nsec; | ||
| 1624 | |||
| 1625 | fuse_setattr_fill(fc, req, inode, &inarg, &outarg); | ||
| 1626 | fuse_request_send(fc, req); | ||
| 1627 | err = req->out.h.error; | ||
| 1628 | fuse_put_request(fc, req); | ||
| 1629 | |||
| 1630 | if (!err) | ||
| 1631 | clear_bit(FUSE_I_MTIME_DIRTY, &fi->state); | ||
| 1632 | |||
| 1633 | return err; | ||
| 1634 | } | ||
| 1635 | |||
| 1566 | /* | 1636 | /* |
| 1567 | * Set attributes, and at the same time refresh them. | 1637 | * Set attributes, and at the same time refresh them. |
| 1568 | * | 1638 | * |
| @@ -1580,8 +1650,10 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, | |||
| 1580 | struct fuse_setattr_in inarg; | 1650 | struct fuse_setattr_in inarg; |
| 1581 | struct fuse_attr_out outarg; | 1651 | struct fuse_attr_out outarg; |
| 1582 | bool is_truncate = false; | 1652 | bool is_truncate = false; |
| 1653 | bool is_wb = fc->writeback_cache; | ||
| 1583 | loff_t oldsize; | 1654 | loff_t oldsize; |
| 1584 | int err; | 1655 | int err; |
| 1656 | bool trust_local_mtime = is_wb && S_ISREG(inode->i_mode); | ||
| 1585 | 1657 | ||
| 1586 | if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS)) | 1658 | if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS)) |
| 1587 | attr->ia_valid |= ATTR_FORCE; | 1659 | attr->ia_valid |= ATTR_FORCE; |
| @@ -1610,7 +1682,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, | |||
| 1610 | 1682 | ||
| 1611 | memset(&inarg, 0, sizeof(inarg)); | 1683 | memset(&inarg, 0, sizeof(inarg)); |
| 1612 | memset(&outarg, 0, sizeof(outarg)); | 1684 | memset(&outarg, 0, sizeof(outarg)); |
| 1613 | iattr_to_fattr(attr, &inarg); | 1685 | iattr_to_fattr(attr, &inarg, trust_local_mtime); |
| 1614 | if (file) { | 1686 | if (file) { |
| 1615 | struct fuse_file *ff = file->private_data; | 1687 | struct fuse_file *ff = file->private_data; |
| 1616 | inarg.valid |= FATTR_FH; | 1688 | inarg.valid |= FATTR_FH; |
| @@ -1621,17 +1693,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, | |||
| 1621 | inarg.valid |= FATTR_LOCKOWNER; | 1693 | inarg.valid |= FATTR_LOCKOWNER; |
| 1622 | inarg.lock_owner = fuse_lock_owner_id(fc, current->files); | ||
