diff options
author | Maxim Patlasov <MPatlasov@parallels.com> | 2013-12-26 10:51:11 -0500 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2014-04-02 09:38:48 -0400 |
commit | b0aa760652179072119582375f8dc896ed5b5dfd (patch) | |
tree | cbc990b308b9894eba032101db994b05e75ad444 /fs/fuse | |
parent | 8373200b124d03de7fa2e99be56de8642e604e9e (diff) |
fuse: Trust kernel i_mtime only
Let the kernel maintain i_mtime locally:
- clear S_NOCMTIME
- implement i_op->update_time()
- flush mtime on fsync and last close
- update i_mtime explicitly on truncate and fallocate
Fuse inode flag FUSE_I_MTIME_DIRTY serves as indication that local i_mtime
should be flushed to the server eventually.
Signed-off-by: Maxim Patlasov <MPatlasov@parallels.com>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Diffstat (limited to 'fs/fuse')
-rw-r--r-- | fs/fuse/dir.c | 108 | ||||
-rw-r--r-- | fs/fuse/file.c | 30 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 6 | ||||
-rw-r--r-- | fs/fuse/inode.c | 13 |
4 files changed, 132 insertions, 25 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index c52f143da9ad..5b4e035b364c 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -842,8 +842,11 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr, | |||
842 | struct fuse_conn *fc = get_fuse_conn(inode); | 842 | struct fuse_conn *fc = get_fuse_conn(inode); |
843 | 843 | ||
844 | /* see the comment in fuse_change_attributes() */ | 844 | /* see the comment in fuse_change_attributes() */ |
845 | if (fc->writeback_cache && S_ISREG(inode->i_mode)) | 845 | if (fc->writeback_cache && S_ISREG(inode->i_mode)) { |
846 | attr->size = i_size_read(inode); | 846 | attr->size = i_size_read(inode); |
847 | attr->mtime = inode->i_mtime.tv_sec; | ||
848 | attr->mtimensec = inode->i_mtime.tv_nsec; | ||
849 | } | ||
847 | 850 | ||
848 | stat->dev = inode->i_sb->s_dev; | 851 | stat->dev = inode->i_sb->s_dev; |
849 | stat->ino = attr->ino; | 852 | stat->ino = attr->ino; |
@@ -1482,12 +1485,16 @@ static long fuse_dir_compat_ioctl(struct file *file, unsigned int cmd, | |||
1482 | FUSE_IOCTL_COMPAT | FUSE_IOCTL_DIR); | 1485 | FUSE_IOCTL_COMPAT | FUSE_IOCTL_DIR); |
1483 | } | 1486 | } |
1484 | 1487 | ||
1485 | static bool update_mtime(unsigned ivalid) | 1488 | static bool update_mtime(unsigned ivalid, bool trust_local_mtime) |
1486 | { | 1489 | { |
1487 | /* Always update if mtime is explicitly set */ | 1490 | /* Always update if mtime is explicitly set */ |
1488 | if (ivalid & ATTR_MTIME_SET) | 1491 | if (ivalid & ATTR_MTIME_SET) |
1489 | return true; | 1492 | return true; |
1490 | 1493 | ||
1494 | /* Or if kernel i_mtime is the official one */ | ||
1495 | if (trust_local_mtime) | ||
1496 | return true; | ||
1497 | |||
1491 | /* 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 */ |
1492 | if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE))) | 1499 | if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE))) |
1493 | return false; | 1500 | return false; |
@@ -1496,7 +1503,8 @@ static bool update_mtime(unsigned ivalid) | |||
1496 | return true; | 1503 | return true; |
1497 | } | 1504 | } |
1498 | 1505 | ||
1499 | 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) | ||
1500 | { | 1508 | { |
1501 | unsigned ivalid = iattr->ia_valid; | 1509 | unsigned ivalid = iattr->ia_valid; |
1502 | 1510 | ||
@@ -1515,11 +1523,11 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) | |||
1515 | if (!(ivalid & ATTR_ATIME_SET)) | 1523 | if (!(ivalid & ATTR_ATIME_SET)) |
1516 | arg->valid |= FATTR_ATIME_NOW; | 1524 | arg->valid |= FATTR_ATIME_NOW; |
1517 | } | 1525 | } |
1518 | if ((ivalid & ATTR_MTIME) && update_mtime(ivalid)) { | 1526 | if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, trust_local_mtime)) { |
1519 | arg->valid |= FATTR_MTIME; | 1527 | arg->valid |= FATTR_MTIME; |
1520 | arg->mtime = iattr->ia_mtime.tv_sec; | 1528 | arg->mtime = iattr->ia_mtime.tv_sec; |
1521 | arg->mtimensec = iattr->ia_mtime.tv_nsec; | 1529 | arg->mtimensec = iattr->ia_mtime.tv_nsec; |
1522 | if (!(ivalid & ATTR_MTIME_SET)) | 1530 | if (!(ivalid & ATTR_MTIME_SET) && !trust_local_mtime) |
1523 | arg->valid |= FATTR_MTIME_NOW; | 1531 | arg->valid |= FATTR_MTIME_NOW; |
1524 | } | 1532 | } |
1525 | } | 1533 | } |
@@ -1568,6 +1576,63 @@ void fuse_release_nowrite(struct inode *inode) | |||
1568 | spin_unlock(&fc->lock); | 1576 | spin_unlock(&fc->lock); |
1569 | } | 1577 | } |
1570 | 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 | |||
1571 | /* | 1636 | /* |
1572 | * Set attributes, and at the same time refresh them. | 1637 | * Set attributes, and at the same time refresh them. |
1573 | * | 1638 | * |
@@ -1588,6 +1653,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, | |||
1588 | bool is_wb = fc->writeback_cache; | 1653 | bool is_wb = fc->writeback_cache; |
1589 | loff_t oldsize; | 1654 | loff_t oldsize; |
1590 | int err; | 1655 | int err; |
1656 | bool trust_local_mtime = is_wb && S_ISREG(inode->i_mode); | ||
1591 | 1657 | ||
1592 | if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS)) | 1658 | if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS)) |
1593 | attr->ia_valid |= ATTR_FORCE; | 1659 | attr->ia_valid |= ATTR_FORCE; |
@@ -1616,7 +1682,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, | |||
1616 | 1682 | ||
1617 | memset(&inarg, 0, sizeof(inarg)); | 1683 | memset(&inarg, 0, sizeof(inarg)); |
1618 | memset(&outarg, 0, sizeof(outarg)); | 1684 | memset(&outarg, 0, sizeof(outarg)); |
1619 | iattr_to_fattr(attr, &inarg); | 1685 | iattr_to_fattr(attr, &inarg, trust_local_mtime); |
1620 | if (file) { | 1686 | if (file) { |
1621 | struct fuse_file *ff = file->private_data; | 1687 | struct fuse_file *ff = file->private_data; |
1622 | inarg.valid |= FATTR_FH; | 1688 | inarg.valid |= FATTR_FH; |
@@ -1627,17 +1693,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, | |||
1627 | inarg.valid |= FATTR_LOCKOWNER; | 1693 | inarg.valid |= FATTR_LOCKOWNER; |
1628 | inarg.lock_owner = fuse_lock_owner_id(fc, current->files); | 1694 | inarg.lock_owner = fuse_lock_owner_id(fc, current->files); |
1629 | } | 1695 | } |
1630 | req->in.h.opcode = FUSE_SETATTR; | 1696 | fuse_setattr_fill(fc, req, inode, &inarg, &outarg); |
1631 | req->in.h.nodeid = get_node_id(inode); | ||
1632 | req->in.numargs = 1; | ||
1633 | req->in.args[0].size = sizeof(inarg); | ||
1634 | req->in.args[0].value = &inarg; | ||
1635 | req->out.numargs = 1; | ||
1636 | if (fc->minor < 9) | ||
1637 | req->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE; | ||
1638 | else | ||
1639 | req->out.args[0].size = sizeof(outarg); | ||
1640 | req->out.args[0].value = &outarg; | ||
1641 | fuse_request_send(fc, req); | 1697 | fuse_request_send(fc, req); |
1642 | err = req->out.h.error; | 1698 | err = req->out.h.error; |
1643 | fuse_put_request(fc, req); | 1699 | fuse_put_request(fc, req); |
@@ -1654,6 +1710,12 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, | |||
1654 | } | 1710 | } |
1655 | 1711 | ||
1656 | spin_lock(&fc->lock); | 1712 | spin_lock(&fc->lock); |
1713 | /* the kernel maintains i_mtime locally */ | ||
1714 | if (trust_local_mtime && (attr->ia_valid & ATTR_MTIME)) { | ||
1715 | inode->i_mtime = attr->ia_mtime; | ||
1716 | clear_bit(FUSE_I_MTIME_DIRTY, &fi->state); | ||
1717 | } | ||
1718 | |||
1657 | fuse_change_attributes_common(inode, &outarg.attr, | 1719 | fuse_change_attributes_common(inode, &outarg.attr, |
1658 | attr_timeout(&outarg)); | 1720 | attr_timeout(&outarg)); |
1659 | oldsize = inode->i_size; | 1721 | oldsize = inode->i_size; |
@@ -1884,6 +1946,17 @@ static int fuse_removexattr(struct dentry *entry, const char *name) | |||
1884 | return err; | 1946 | return err; |
1885 | } | 1947 | } |
1886 | 1948 | ||
1949 | static int fuse_update_time(struct inode *inode, struct timespec *now, | ||
1950 | int flags) | ||
1951 | { | ||
1952 | if (flags & S_MTIME) { | ||
1953 | inode->i_mtime = *now; | ||
1954 | set_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state); | ||
1955 | BUG_ON(!S_ISREG(inode->i_mode)); | ||
1956 | } | ||
1957 | return 0; | ||
1958 | } | ||
1959 | |||
1887 | static const struct inode_operations fuse_dir_inode_operations = { | 1960 | static const struct inode_operations fuse_dir_inode_operations = { |
1888 | .lookup = fuse_lookup, | 1961 | .lookup = fuse_lookup, |
1889 | .mkdir = fuse_mkdir, | 1962 | .mkdir = fuse_mkdir, |
@@ -1923,6 +1996,7 @@ static const struct inode_operations fuse_common_inode_operations = { | |||
1923 | .getxattr = fuse_getxattr, | 1996 | .getxattr = fuse_getxattr, |
1924 | .listxattr = fuse_listxattr, | 1997 | .listxattr = fuse_listxattr, |
1925 | .removexattr = fuse_removexattr, | 1998 | .removexattr = fuse_removexattr, |
1999 | .update_time = fuse_update_time, | ||
1926 | }; | 2000 | }; |
1927 | 2001 | ||
1928 | static const struct inode_operations fuse_symlink_inode_operations = { | 2002 | static const struct inode_operations fuse_symlink_inode_operations = { |
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index c091a17d3ffc..69de9b860c39 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
@@ -308,6 +308,9 @@ static int fuse_open(struct inode *inode, struct file *file) | |||
308 | 308 | ||
309 | static int fuse_release(struct inode *inode, struct file *file) | 309 | static int fuse_release(struct inode *inode, struct file *file) |
310 | { | 310 | { |
311 | if (test_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state)) | ||
312 | fuse_flush_mtime(file, true); | ||
313 | |||
311 | fuse_release_common(file, FUSE_RELEASE); | 314 | fuse_release_common(file, FUSE_RELEASE); |
312 | 315 | ||
313 | /* return value is ignored by VFS */ | 316 | /* return value is ignored by VFS */ |
@@ -475,6 +478,12 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end, | |||
475 | 478 | ||
476 | fuse_sync_writes(inode); | 479 | fuse_sync_writes(inode); |
477 | 480 | ||
481 | if (test_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state)) { | ||
482 | int err = fuse_flush_mtime(file, false); | ||
483 | if (err) | ||
484 | goto out; | ||
485 | } | ||
486 | |||
478 | req = fuse_get_req_nopages(fc); | 487 | req = fuse_get_req_nopages(fc); |
479 | if (IS_ERR(req)) { | 488 | if (IS_ERR(req)) { |
480 | err = PTR_ERR(req); | 489 | err = PTR_ERR(req); |
@@ -960,16 +969,21 @@ static size_t fuse_send_write(struct fuse_req *req, struct fuse_io_priv *io, | |||
960 | return req->misc.write.out.size; | 969 | return req->misc.write.out.size; |
961 | } | 970 | } |
962 | 971 | ||
963 | void fuse_write_update_size(struct inode *inode, loff_t pos) | 972 | bool fuse_write_update_size(struct inode *inode, loff_t pos) |
964 | { | 973 | { |
965 | struct fuse_conn *fc = get_fuse_conn(inode); | 974 | struct fuse_conn *fc = get_fuse_conn(inode); |
966 | struct fuse_inode *fi = get_fuse_inode(inode); | 975 | struct fuse_inode *fi = get_fuse_inode(inode); |
976 | bool ret = false; | ||
967 | 977 | ||
968 | spin_lock(&fc->lock); | 978 | spin_lock(&fc->lock); |
969 | fi->attr_version = ++fc->attr_version; | 979 | fi->attr_version = ++fc->attr_version; |
970 | if (pos > inode->i_size) | 980 | if (pos > inode->i_size) { |
971 | i_size_write(inode, pos); | 981 | i_size_write(inode, pos); |
982 | ret = true; | ||
983 | } | ||
972 | spin_unlock(&fc->lock); | 984 | spin_unlock(&fc->lock); |
985 | |||
986 | return ret; | ||
973 | } | 987 | } |
974 | 988 | ||
975 | static size_t fuse_send_write_pages(struct fuse_req *req, struct file *file, | 989 | static size_t fuse_send_write_pages(struct fuse_req *req, struct file *file, |
@@ -2877,8 +2891,16 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, | |||
2877 | goto out; | 2891 | goto out; |
2878 | 2892 | ||
2879 | /* we could have extended the file */ | 2893 | /* we could have extended the file */ |
2880 | if (!(mode & FALLOC_FL_KEEP_SIZE)) | 2894 | if (!(mode & FALLOC_FL_KEEP_SIZE)) { |
2881 | fuse_write_update_size(inode, offset + length); | 2895 | bool changed = fuse_write_update_size(inode, offset + length); |
2896 | |||
2897 | if (changed && fc->writeback_cache) { | ||
2898 | struct fuse_inode *fi = get_fuse_inode(inode); | ||
2899 | |||
2900 | inode->i_mtime = current_fs_time(inode->i_sb); | ||
2901 | set_bit(FUSE_I_MTIME_DIRTY, &fi->state); | ||
2902 | } | ||
2903 | } | ||
2882 | 2904 | ||
2883 | if (mode & FALLOC_FL_PUNCH_HOLE) | 2905 | if (mode & FALLOC_FL_PUNCH_HOLE) |
2884 | truncate_pagecache_range(inode, offset, offset + length - 1); | 2906 | truncate_pagecache_range(inode, offset, offset + length - 1); |
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 374a8be014fd..1e6ad6d43051 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
@@ -119,6 +119,8 @@ enum { | |||
119 | FUSE_I_INIT_RDPLUS, | 119 | FUSE_I_INIT_RDPLUS, |
120 | /** An operation changing file size is in progress */ | 120 | /** An operation changing file size is in progress */ |
121 | FUSE_I_SIZE_UNSTABLE, | 121 | FUSE_I_SIZE_UNSTABLE, |
122 | /** i_mtime has been updated locally; a flush to userspace needed */ | ||
123 | FUSE_I_MTIME_DIRTY, | ||
122 | }; | 124 | }; |
123 | 125 | ||
124 | struct fuse_conn; | 126 | struct fuse_conn; |
@@ -876,7 +878,9 @@ long fuse_ioctl_common(struct file *file, unsigned int cmd, | |||
876 | unsigned fuse_file_poll(struct file *file, poll_table *wait); | 878 | unsigned fuse_file_poll(struct file *file, poll_table *wait); |
877 | int fuse_dev_release(struct inode *inode, struct file *file); | 879 | int fuse_dev_release(struct inode *inode, struct file *file); |
878 | 880 | ||
879 | void fuse_write_update_size(struct inode *inode, loff_t pos); | 881 | bool fuse_write_update_size(struct inode *inode, loff_t pos); |
882 | |||
883 | int fuse_flush_mtime(struct file *file, bool nofail); | ||
880 | 884 | ||
881 | int fuse_do_setattr(struct inode *inode, struct iattr *attr, | 885 | int fuse_do_setattr(struct inode *inode, struct iattr *attr, |
882 | struct file *file); | 886 | struct file *file); |
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index c668c8436894..1061b0d9b86d 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c | |||
@@ -170,8 +170,11 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, | |||
170 | inode->i_blocks = attr->blocks; | 170 | inode->i_blocks = attr->blocks; |
171 | inode->i_atime.tv_sec = attr->atime; | 171 | inode->i_atime.tv_sec = attr->atime; |
172 | inode->i_atime.tv_nsec = attr->atimensec; | 172 | inode->i_atime.tv_nsec = attr->atimensec; |
173 | inode->i_mtime.tv_sec = attr->mtime; | 173 | /* mtime from server may be stale due to local buffered write */ |
174 | inode->i_mtime.tv_nsec = attr->mtimensec; | 174 | if (!fc->writeback_cache || !S_ISREG(inode->i_mode)) { |
175 | inode->i_mtime.tv_sec = attr->mtime; | ||
176 | inode->i_mtime.tv_nsec = attr->mtimensec; | ||
177 | } | ||
175 | inode->i_ctime.tv_sec = attr->ctime; | 178 | inode->i_ctime.tv_sec = attr->ctime; |
176 | inode->i_ctime.tv_nsec = attr->ctimensec; | 179 | inode->i_ctime.tv_nsec = attr->ctimensec; |
177 | 180 | ||
@@ -250,6 +253,8 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) | |||
250 | { | 253 | { |
251 | inode->i_mode = attr->mode & S_IFMT; | 254 | inode->i_mode = attr->mode & S_IFMT; |
252 | inode->i_size = attr->size; | 255 | inode->i_size = attr->size; |
256 | inode->i_mtime.tv_sec = attr->mtime; | ||
257 | inode->i_mtime.tv_nsec = attr->mtimensec; | ||
253 | if (S_ISREG(inode->i_mode)) { | 258 | if (S_ISREG(inode->i_mode)) { |
254 | fuse_init_common(inode); | 259 | fuse_init_common(inode); |
255 | fuse_init_file_inode(inode); | 260 | fuse_init_file_inode(inode); |
@@ -296,7 +301,9 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid, | |||
296 | return NULL; | 301 | return NULL; |
297 | 302 | ||
298 | if ((inode->i_state & I_NEW)) { | 303 | if ((inode->i_state & I_NEW)) { |
299 | inode->i_flags |= S_NOATIME|S_NOCMTIME; | 304 | inode->i_flags |= S_NOATIME; |
305 | if (!fc->writeback_cache || !S_ISREG(inode->i_mode)) | ||
306 | inode->i_flags |= S_NOCMTIME; | ||
300 | inode->i_generation = generation; | 307 | inode->i_generation = generation; |
301 | inode->i_data.backing_dev_info = &fc->bdi; | 308 | inode->i_data.backing_dev_info = &fc->bdi; |
302 | fuse_init_inode(inode, attr); | 309 | fuse_init_inode(inode, attr); |