diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-09-08 01:01:14 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-09-08 01:01:14 -0300 |
commit | e5fd91f1ef340da553f7a79da9540c3db711c937 (patch) | |
tree | b11842027dc6641da63f4bcc524f8678263304a3 /fs | |
parent | 2a9b0348e685a63d97486f6749622b61e9e3292f (diff) |
Linux-libre 4.2-gnu
Diffstat (limited to 'fs')
490 files changed, 17340 insertions, 41977 deletions
diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 620d93489..8aa56bb6e 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -320,31 +320,21 @@ fail_option_alloc: struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, const char *dev_name, char *data) { - int retval = -EINVAL; struct p9_fid *fid; - int rc; + int rc = -ENOMEM; v9ses->uname = kstrdup(V9FS_DEFUSER, GFP_KERNEL); if (!v9ses->uname) - return ERR_PTR(-ENOMEM); + goto err_names; v9ses->aname = kstrdup(V9FS_DEFANAME, GFP_KERNEL); - if (!v9ses->aname) { - kfree(v9ses->uname); - return ERR_PTR(-ENOMEM); - } + if (!v9ses->aname) + goto err_names; init_rwsem(&v9ses->rename_sem); rc = bdi_setup_and_register(&v9ses->bdi, "9p"); - if (rc) { - kfree(v9ses->aname); - kfree(v9ses->uname); - return ERR_PTR(rc); - } - - spin_lock(&v9fs_sessionlist_lock); - list_add(&v9ses->slist, &v9fs_sessionlist); - spin_unlock(&v9fs_sessionlist_lock); + if (rc) + goto err_names; v9ses->uid = INVALID_UID; v9ses->dfltuid = V9FS_DEFUID; @@ -352,10 +342,9 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, v9ses->clnt = p9_client_create(dev_name, data); if (IS_ERR(v9ses->clnt)) { - retval = PTR_ERR(v9ses->clnt); - v9ses->clnt = NULL; + rc = PTR_ERR(v9ses->clnt); p9_debug(P9_DEBUG_ERROR, "problem initializing 9p client\n"); - goto error; + goto err_bdi; } v9ses->flags = V9FS_ACCESS_USER; @@ -368,10 +357,8 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, } rc = v9fs_parse_options(v9ses, data); - if (rc < 0) { - retval = rc; - goto error; - } + if (rc < 0) + goto err_clnt; v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ; @@ -405,10 +392,9 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, INVALID_UID, v9ses->aname); if (IS_ERR(fid)) { - retval = PTR_ERR(fid); - fid = NULL; + rc = PTR_ERR(fid); p9_debug(P9_DEBUG_ERROR, "cannot attach\n"); - goto error; + goto err_clnt; } if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE) @@ -420,12 +406,20 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, /* register the session for caching */ v9fs_cache_session_get_cookie(v9ses); #endif + spin_lock(&v9fs_sessionlist_lock); + list_add(&v9ses->slist, &v9fs_sessionlist); + spin_unlock(&v9fs_sessionlist_lock); return fid; -error: +err_clnt: + p9_client_destroy(v9ses->clnt); +err_bdi: bdi_destroy(&v9ses->bdi); - return ERR_PTR(retval); +err_names: + kfree(v9ses->uname); + kfree(v9ses->aname); + return ERR_PTR(rc); } /** diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h index fb9ffcb43..0923f2cf3 100644 --- a/fs/9p/v9fs.h +++ b/fs/9p/v9fs.h @@ -149,8 +149,6 @@ extern int v9fs_vfs_unlink(struct inode *i, struct dentry *d); extern int v9fs_vfs_rmdir(struct inode *i, struct dentry *d); extern int v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry); -extern void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, - void *p); extern struct inode *v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid, struct super_block *sb, int new); diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 53f1e8a21..b1dc51888 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -1223,100 +1223,43 @@ ino_t v9fs_qid2ino(struct p9_qid *qid) } /** - * v9fs_readlink - read a symlink's location (internal version) + * v9fs_vfs_follow_link - follow a symlink path * @dentry: dentry for symlink - * @buffer: buffer to load symlink location into - * @buflen: length of buffer - * + * @cookie: place to pass the data to put_link() */ -static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) +static const char *v9fs_vfs_follow_link(struct dentry *dentry, void **cookie) { - int retval; - - struct v9fs_session_info *v9ses; - struct p9_fid *fid; + struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry); + struct p9_fid *fid = v9fs_fid_lookup(dentry); struct p9_wstat *st; + char *res; + + p9_debug(P9_DEBUG_VFS, "%pd\n", dentry); - p9_debug(P9_DEBUG_VFS, " %pd\n", dentry); - retval = -EPERM; - v9ses = v9fs_dentry2v9ses(dentry); - fid = v9fs_fid_lookup(dentry); if (IS_ERR(fid)) - return PTR_ERR(fid); + return ERR_CAST(fid); if (!v9fs_proto_dotu(v9ses)) - return -EBADF; + return ERR_PTR(-EBADF); st = p9_client_stat(fid); if (IS_ERR(st)) - return PTR_ERR(st); + return ERR_CAST(st); if (!(st->mode & P9_DMSYMLINK)) { - retval = -EINVAL; - goto done; + p9stat_free(st); + kfree(st); + return ERR_PTR(-EINVAL); } + res = st->extension; + st->extension = NULL; + if (strlen(res) >= PATH_MAX) + res[PATH_MAX - 1] = '\0'; - /* copy extension buffer into buffer */ - retval = min(strlen(st->extension)+1, (size_t)buflen); - memcpy(buffer, st->extension, retval); - - p9_debug(P9_DEBUG_VFS, "%pd -> %s (%.*s)\n", - dentry, st->extension, buflen, buffer); - -done: p9stat_free(st); kfree(st); - return retval; -} - -/** - * v9fs_vfs_follow_link - follow a symlink path - * @dentry: dentry for symlink - * @nd: nameidata - * - */ - -static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - int len = 0; - char *link = __getname(); - - p9_debug(P9_DEBUG_VFS, "%pd\n", dentry); - - if (!link) - link = ERR_PTR(-ENOMEM); - else { - len = v9fs_readlink(dentry, link, PATH_MAX); - - if (len < 0) { - __putname(link); - link = ERR_PTR(len); - } else - link[min(len, PATH_MAX-1)] = 0; - } - nd_set_link(nd, link); - - return NULL; -} - -/** - * v9fs_vfs_put_link - release a symlink path - * @dentry: dentry for symlink - * @nd: nameidata - * @p: unused - * - */ - -void -v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p) -{ - char *s = nd_get_link(nd); - - p9_debug(P9_DEBUG_VFS, " %pd %s\n", - dentry, IS_ERR(s) ? "<error>" : s); - if (!IS_ERR(s)) - __putname(s); + return *cookie = res; } /** @@ -1369,6 +1312,8 @@ v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) return v9fs_vfs_mkspecial(dir, dentry, P9_DMSYMLINK, symname); } +#define U32_MAX_DIGITS 10 + /** * v9fs_vfs_link - create a hardlink * @old_dentry: dentry for file to link to @@ -1382,7 +1327,7 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { int retval; - char *name; + char name[1 + U32_MAX_DIGITS + 2]; /* sign + number + \n + \0 */ struct p9_fid *oldfid; p9_debug(P9_DEBUG_VFS, " %lu,%pd,%pd\n", @@ -1392,20 +1337,12 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, if (IS_ERR(oldfid)) return PTR_ERR(oldfid); - name = __getname(); - if (unlikely(!name)) { - retval = -ENOMEM; - goto clunk_fid; - } - sprintf(name, "%d\n", oldfid->fid); retval = v9fs_vfs_mkspecial(dir, dentry, P9_DMLINK, name); - __putname(name); if (!retval) { v9fs_refresh_inode(oldfid, d_inode(old_dentry)); v9fs_invalidate_inode_attr(dir); } -clunk_fid: p9_client_clunk(oldfid); return retval; } @@ -1424,7 +1361,7 @@ v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rde { struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); int retval; - char *name; + char name[2 + U32_MAX_DIGITS + 1 + U32_MAX_DIGITS + 1]; u32 perm; p9_debug(P9_DEBUG_VFS, " %lu,%pd mode: %hx MAJOR: %u MINOR: %u\n", @@ -1434,26 +1371,16 @@ v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rde if (!new_valid_dev(rdev)) return -EINVAL; - name = __getname(); - if (!name) - return -ENOMEM; /* build extension */ if (S_ISBLK(mode)) sprintf(name, "b %u %u", MAJOR(rdev), MINOR(rdev)); else if (S_ISCHR(mode)) sprintf(name, "c %u %u", MAJOR(rdev), MINOR(rdev)); - else if (S_ISFIFO(mode)) - *name = 0; - else if (S_ISSOCK(mode)) + else *name = 0; - else { - __putname(name); - return -EINVAL; - } perm = unixmode2p9mode(v9ses, mode); retval = v9fs_vfs_mkspecial(dir, dentry, perm, name); - __putname(name); return retval; } @@ -1529,7 +1456,7 @@ static const struct inode_operations v9fs_file_inode_operations = { static const struct inode_operations v9fs_symlink_inode_operations = { .readlink = generic_readlink, .follow_link = v9fs_vfs_follow_link, - .put_link = v9fs_vfs_put_link, + .put_link = kfree_put_link, .getattr = v9fs_vfs_getattr, .setattr = v9fs_vfs_setattr, }; diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index 4d3ecfb55..e8aa57dc8 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -904,41 +904,24 @@ error: /** * v9fs_vfs_follow_link_dotl - follow a symlink path * @dentry: dentry for symlink - * @nd: nameidata - * + * @cookie: place to pass the data to put_link() */ -static void * -v9fs_vfs_follow_link_dotl(struct dentry *dentry, struct nameidata *nd) +static const char * +v9fs_vfs_follow_link_dotl(struct dentry *dentry, void **cookie) { - int retval; - struct p9_fid *fid; - char *link = __getname(); + struct p9_fid *fid = v9fs_fid_lookup(dentry); char *target; + int retval; p9_debug(P9_DEBUG_VFS, "%pd\n", dentry); - if (!link) { - link = ERR_PTR(-ENOMEM); - goto ndset; - } - fid = v9fs_fid_lookup(dentry); - if (IS_ERR(fid)) { - __putname(link); - link = ERR_CAST(fid); - goto ndset; - } + if (IS_ERR(fid)) + return ERR_CAST(fid); retval = p9_client_readlink(fid, &target); - if (!retval) { - strcpy(link, target); - kfree(target); - goto ndset; - } - __putname(link); - link = ERR_PTR(retval); -ndset: - nd_set_link(nd, link); - return NULL; + if (retval) + return ERR_PTR(retval); + return *cookie = target; } int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode) @@ -1005,7 +988,7 @@ const struct inode_operations v9fs_file_inode_operations_dotl = { const struct inode_operations v9fs_symlink_inode_operations_dotl = { .readlink = generic_readlink, .follow_link = v9fs_vfs_follow_link_dotl, - .put_link = v9fs_vfs_put_link, + .put_link = kfree_put_link, .getattr = v9fs_vfs_getattr_dotl, .setattr = v9fs_vfs_setattr_dotl, .setxattr = generic_setxattr, diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index e99a338a4..bf495cede 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -130,11 +130,7 @@ static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags, fid = v9fs_session_init(v9ses, dev_name, data); if (IS_ERR(fid)) { retval = PTR_ERR(fid); - /* - * we need to call session_close to tear down some - * of the data structure setup by session_init - */ - goto close_session; + goto free_session; } sb = sget(fs_type, NULL, v9fs_set_super, flags, v9ses); @@ -195,8 +191,8 @@ static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags, clunk_fid: p9_client_clunk(fid); -close_session: v9fs_session_close(v9ses); +free_session: kfree(v9ses); return ERR_PTR(retval); diff --git a/fs/Kconfig b/fs/Kconfig index b1083f6b6..011f43365 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -218,7 +218,6 @@ source "fs/pstore/Kconfig" source "fs/sysv/Kconfig" source "fs/ufs/Kconfig" source "fs/exofs/Kconfig" -source "fs/aufs/Kconfig" endif # MISC_FILESYSTEMS diff --git a/fs/Makefile b/fs/Makefile index 8c2df123d..cb20e4bf2 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -115,7 +115,6 @@ obj-$(CONFIG_AFS_FS) += afs/ obj-$(CONFIG_NILFS2_FS) += nilfs2/ obj-$(CONFIG_BEFS_FS) += befs/ obj-$(CONFIG_HOSTFS) += hostfs/ -obj-$(CONFIG_HPPFS) += hppfs/ obj-$(CONFIG_CACHEFILES) += cachefiles/ obj-$(CONFIG_DEBUG_FS) += debugfs/ obj-$(CONFIG_TRACING) += tracefs/ @@ -127,4 +126,3 @@ obj-y += exofs/ # Multiple modules obj-$(CONFIG_CEPH_FS) += ceph/ obj-$(CONFIG_PSTORE) += pstore/ obj-$(CONFIG_EFIVAR_FS) += efivarfs/ -obj-$(CONFIG_AUFS_FS) += aufs/ diff --git a/fs/adfs/super.c b/fs/adfs/super.c index a19c31d3f..4d4a0df83 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -242,7 +242,7 @@ static struct kmem_cache *adfs_inode_cachep; static struct inode *adfs_alloc_inode(struct super_block *sb) { struct adfs_inode_info *ei; - ei = (struct adfs_inode_info *)kmem_cache_alloc(adfs_inode_cachep, GFP_KERNEL); + ei = kmem_cache_alloc(adfs_inode_cachep, GFP_KERNEL); if (!ei) return NULL; return &ei->vfs_inode; diff --git a/fs/affs/affs.h b/fs/affs/affs.h index cffe8370f..c69a87eaf 100644 --- a/fs/affs/affs.h +++ b/fs/affs/affs.h @@ -64,7 +64,7 @@ struct affs_inode_info { /* short cut to get to the affs specific inode data */ static inline struct affs_inode_info *AFFS_I(struct inode *inode) { - return list_entry(inode, struct affs_inode_info, vfs_inode); + return container_of(inode, struct affs_inode_info, vfs_inode); } /* diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c index a8f463c02..5fa92bc79 100644 --- a/fs/affs/amigaffs.c +++ b/fs/affs/amigaffs.c @@ -140,7 +140,7 @@ affs_remove_link(struct dentry *dentry) { struct inode *dir, *inode = d_inode(dentry); struct super_block *sb = inode->i_sb; - struct buffer_head *bh = NULL, *link_bh = NULL; + struct buffer_head *bh, *link_bh = NULL; u32 link_ino, ino; int retval; diff --git a/fs/affs/inode.c b/fs/affs/inode.c index a022f4acc..173495005 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -346,7 +346,7 @@ affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s3 { struct super_block *sb = dir->i_sb; struct buffer_head *inode_bh = NULL; - struct buffer_head *bh = NULL; + struct buffer_head *bh; u32 block = 0; int retval; diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c index f39b71c39..ea5b69a18 100644 --- a/fs/affs/symlink.c +++ b/fs/affs/symlink.c @@ -16,14 +16,12 @@ static int affs_symlink_readpage(struct file *file, struct page *page) struct inode *inode = page->mapping->host; char *link = kmap(page); struct slink_front *lf; - int err; int i, j; char c; char lc; pr_debug("follow_link(ino=%lu)\n", inode->i_ino); - err = -EIO; bh = affs_bread(inode->i_sb, inode->i_ino); if (!bh) goto fail; @@ -66,7 +64,7 @@ fail: SetPageError(page); kunmap(page); unlock_page(page); - return err; + return -EIO; } const struct address_space_operations affs_symlink_aops = { diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 3a57a1b0f..b50642870 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -85,7 +85,7 @@ int afs_open_socket(void) return -ENOMEM; } - ret = sock_create_kern(AF_RXRPC, SOCK_DGRAM, PF_INET, &socket); + ret = sock_create_kern(&init_net, AF_RXRPC, SOCK_DGRAM, PF_INET, &socket); if (ret < 0) { destroy_workqueue(afs_async_calls); _leave(" = %d [socket]", ret); diff --git a/fs/aufs/Kconfig b/fs/aufs/Kconfig deleted file mode 100644 index 63560ceda..000000000 --- a/fs/aufs/Kconfig +++ /dev/null @@ -1,185 +0,0 @@ -config AUFS_FS - tristate "Aufs (Advanced multi layered unification filesystem) support" - help - Aufs is a stackable unification filesystem such as Unionfs, - which unifies several directories and provides a merged single - directory. - In the early days, aufs was entirely re-designed and - re-implemented Unionfs Version 1.x series. Introducing many - original ideas, approaches and improvements, it becomes totally - different from Unionfs while keeping the basic features. - -if AUFS_FS -choice - prompt "Maximum number of branches" - default AUFS_BRANCH_MAX_127 - help - Specifies the maximum number of branches (or member directories) - in a single aufs. The larger value consumes more system - resources and has a minor impact to performance. -config AUFS_BRANCH_MAX_127 - bool "127" - help - Specifies the maximum number of branches (or member directories) - in a single aufs. The larger value consumes more system - resources and has a minor impact to performance. -config AUFS_BRANCH_MAX_511 - bool "511" - help - Specifies the maximum number of branches (or member directories) - in a single aufs. The larger value consumes more system - resources and has a minor impact to performance. -config AUFS_BRANCH_MAX_1023 - bool "1023" - help - Specifies the maximum number of branches (or member directories) - in a single aufs. The larger value consumes more system - resources and has a minor impact to performance. -config AUFS_BRANCH_MAX_32767 - bool "32767" - help - Specifies the maximum number of branches (or member directories) - in a single aufs. The larger value consumes more system - resources and has a minor impact to performance. -endchoice - -config AUFS_SBILIST - bool - depends on AUFS_MAGIC_SYSRQ || PROC_FS - default y - help - Automatic configuration for internal use. - When aufs supports Magic SysRq or /proc, enabled automatically. - -config AUFS_HNOTIFY - bool "Detect direct branch access (bypassing aufs)" - help - If you want to modify files on branches directly, eg. bypassing aufs, - and want aufs to detect the changes of them fully, then enable this - option and use 'udba=notify' mount option. - Currently there is only one available configuration, "fsnotify". - It will have a negative impact to the performance. - See detail in aufs.5. - -choice - prompt "method" if AUFS_HNOTIFY - default AUFS_HFSNOTIFY -config AUFS_HFSNOTIFY - bool "fsnotify" - select FSNOTIFY -endchoice - -config AUFS_EXPORT - bool "NFS-exportable aufs" - depends on EXPORTFS - help - If you want to export your mounted aufs via NFS, then enable this - option. There are several requirements for this configuration. - See detail in aufs.5. - -config AUFS_INO_T_64 - bool - depends on AUFS_EXPORT - depends on 64BIT && !(ALPHA || S390) - default y - help - Automatic configuration for internal use. - /* typedef unsigned long/int __kernel_ino_t */ - /* alpha and s390x are int */ - -config AUFS_XATTR - bool "support for XATTR/EA (including Security Labels)" - help - If your branch fs supports XATTR/EA and you want to make them - available in aufs too, then enable this opsion and specify the - branch attributes for EA. - See detail in aufs.5. - -config AUFS_FHSM - bool "File-based Hierarchical Storage Management" - help - Hierarchical Storage Management (or HSM) is a well-known feature - in the storage world. Aufs provides this feature as file-based. - with multiple branches. - These multiple branches are prioritized, ie. the topmost one - should be the fastest drive and be used heavily. - -config AUFS_RDU - bool "Readdir in userspace" - help - Aufs has two methods to provide a merged view for a directory, - by a user-space library and by kernel-space natively. The latter - is always enabled but sometimes large and slow. - If you enable this option, install the library in aufs2-util - package, and set some environment variables for your readdir(3), - then the work will be handled in user-space which generally - shows better performance in most cases. - See detail in aufs.5. - -config AUFS_SHWH - bool "Show whiteouts" - help - If you want to make the whiteouts in aufs visible, then enable - this option and specify 'shwh' mount option. Although it may - sounds like philosophy or something, but in technically it - simply shows the name of whiteout with keeping its behaviour. - -config AUFS_BR_RAMFS - bool "Ramfs (initramfs/rootfs) as an aufs branch" - help - If you want to use ramfs as an aufs branch fs, then enable this - option. Generally tmpfs is recommended. - Aufs prohibited them to be a branch fs by default, because - initramfs becomes unusable after switch_root or something - generally. If you sets initramfs as an aufs branch and boot your - system by switch_root, you will meet a problem easily since the - files in initramfs may be inaccessible. - Unless you are going to use ramfs as an aufs branch fs without - switch_root or something, leave it N. - -config AUFS_BR_FUSE - bool "Fuse fs as an aufs branch" - depends on FUSE_FS - select AUFS_POLL - help - If you want to use fuse-based userspace filesystem as an aufs - branch fs, then enable this option. - It implements the internal poll(2) operation which is - implemented by fuse only (curretnly). - -config AUFS_POLL - bool - help - Automatic configuration for internal use. - -config AUFS_BR_HFSPLUS - bool "Hfsplus as an aufs branch" - depends on HFSPLUS_FS - default y - help - If you want to use hfsplus fs as an aufs branch fs, then enable - this option. This option introduces a small overhead at - copying-up a file on hfsplus. - -config AUFS_BDEV_LOOP - bool - depends on BLK_DEV_LOOP - default y - help - Automatic configuration for internal use. - Convert =[ym] into =y. - -config AUFS_DEBUG - bool "Debug aufs" - help - Enable this to compile aufs internal debug code. - It will have a negative impact to the performance. - -config AUFS_MAGIC_SYSRQ - bool - depends on AUFS_DEBUG && MAGIC_SYSRQ - default y - help - Automatic configuration for internal use. - When aufs supports Magic SysRq, enabled automatically. -endif diff --git a/fs/aufs/Makefile b/fs/aufs/Makefile deleted file mode 100644 index c7a501e37..000000000 --- a/fs/aufs/Makefile +++ /dev/null @@ -1,44 +0,0 @@ - -include ${src}/magic.mk -ifeq (${CONFIG_AUFS_FS},m) -include ${src}/conf.mk -endif --include ${src}/priv_def.mk - -# cf. include/linux/kernel.h -# enable pr_debug -ccflags-y += -DDEBUG -# sparse requires the full pathname -ifdef M -ccflags-y += -include ${M}/../../include/uapi/linux/aufs_type.h -else -ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h -endif - -obj-$(CONFIG_AUFS_FS) += aufs.o -aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \ - wkq.o vfsub.o dcsub.o \ - cpup.o whout.o wbr_policy.o \ - dinfo.o dentry.o \ - dynop.o \ - finfo.o file.o f_op.o \ - dir.o vdir.o \ - iinfo.o inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o \ - mvdown.o ioctl.o - -# all are boolean -aufs-$(CONFIG_PROC_FS) += procfs.o plink.o -aufs-$(CONFIG_SYSFS) += sysfs.o -aufs-$(CONFIG_DEBUG_FS) += dbgaufs.o -aufs-$(CONFIG_AUFS_BDEV_LOOP) += loop.o -aufs-$(CONFIG_AUFS_HNOTIFY) += hnotify.o -aufs-$(CONFIG_AUFS_HFSNOTIFY) += hfsnotify.o -aufs-$(CONFIG_AUFS_EXPORT) += export.o -aufs-$(CONFIG_AUFS_XATTR) += xattr.o -aufs-$(CONFIG_FS_POSIX_ACL) += posix_acl.o -aufs-$(CONFIG_AUFS_FHSM) += fhsm.o -aufs-$(CONFIG_AUFS_POLL) += poll.o -aufs-$(CONFIG_AUFS_RDU) += rdu.o -aufs-$(CONFIG_AUFS_BR_HFSPLUS) += hfsplus.o -aufs-$(CONFIG_AUFS_DEBUG) += debug.o -aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o diff --git a/fs/aufs/aufs.h b/fs/aufs/aufs.h deleted file mode 100644 index d82e9743f..000000000 --- a/fs/aufs/aufs.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * all header files - */ - -#ifndef __AUFS_H__ -#define __AUFS_H__ - -#ifdef __KERNEL__ - -#define AuStub(type, name, body, ...) \ - static inline type name(__VA_ARGS__) { body; } - -#define AuStubVoid(name, ...) \ - AuStub(void, name, , __VA_ARGS__) -#define AuStubInt0(name, ...) \ - AuStub(int, name, return 0, __VA_ARGS__) - -#include "debug.h" - -#include "branch.h" -#include "cpup.h" -#include "dcsub.h" -#include "dbgaufs.h" -#include "dentry.h" -#include "dir.h" -#include "dynop.h" -#include "file.h" -#include "fstype.h" -#include "inode.h" -#include "loop.h" -#include "module.h" -#include "opts.h" -#include "rwsem.h" -#include "spl.h" -#include "super.h" -#include "sysaufs.h" -#include "vfsub.h" -#include "whout.h" -#include "wkq.h" - -#endif /* __KERNEL__ */ -#endif /* __AUFS_H__ */ diff --git a/fs/aufs/branch.c b/fs/aufs/branch.c deleted file mode 100644 index 2ec496438..000000000 --- a/fs/aufs/branch.c +++ /dev/null @@ -1,1414 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * branch management - */ - -#include <linux/compat.h> -#include <linux/statfs.h> -#include "aufs.h" - -/* - * free a single branch - */ -static void au_br_do_free(struct au_branch *br) -{ - int i; - struct au_wbr *wbr; - struct au_dykey **key; - - au_hnotify_fin_br(br); - - if (br->br_xino.xi_file) - fput(br->br_xino.xi_file); - mutex_destroy(&br->br_xino.xi_nondir_mtx); - - AuDebugOn(atomic_read(&br->br_count)); - - wbr = br->br_wbr; - if (wbr) { - for (i = 0; i < AuBrWh_Last; i++) - dput(wbr->wbr_wh[i]); - AuDebugOn(atomic_read(&wbr->wbr_wh_running)); - AuRwDestroy(&wbr->wbr_wh_rwsem); - } - - if (br->br_fhsm) { - au_br_fhsm_fin(br->br_fhsm); - kfree(br->br_fhsm); - } - - key = br->br_dykey; - for (i = 0; i < AuBrDynOp; i++, key++) - if (*key) - au_dy_put(*key); - else - break; - - /* recursive lock, s_umount of branch's */ - lockdep_off(); - path_put(&br->br_path); - lockdep_on(); - kfree(wbr); - kfree(br); -} - -/* - * frees all branches - */ -void au_br_free(struct au_sbinfo *sbinfo) -{ - aufs_bindex_t bmax; - struct au_branch **br; - - AuRwMustWriteLock(&sbinfo->si_rwsem); - - bmax = sbinfo->si_bend + 1; - br = sbinfo->si_branch; - while (bmax--) - au_br_do_free(*br++); -} - -/* - * find the index of a branch which is specified by @br_id. - */ -int au_br_index(struct super_block *sb, aufs_bindex_t br_id) -{ - aufs_bindex_t bindex, bend; - - bend = au_sbend(sb); - for (bindex = 0; bindex <= bend; bindex++) - if (au_sbr_id(sb, bindex) == br_id) - return bindex; - return -1; -} - -/* ---------------------------------------------------------------------- */ - -/* - * add a branch - */ - -static int test_overlap(struct super_block *sb, struct dentry *h_adding, - struct dentry *h_root) -{ - if (unlikely(h_adding == h_root - || au_test_loopback_overlap(sb, h_adding))) - return 1; - if (h_adding->d_sb != h_root->d_sb) - return 0; - return au_test_subdir(h_adding, h_root) - || au_test_subdir(h_root, h_adding); -} - -/* - * returns a newly allocated branch. @new_nbranch is a number of branches - * after adding a branch. - */ -static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch, - int perm) -{ - struct au_branch *add_branch; - struct dentry *root; - struct inode *inode; - int err; - - err = -ENOMEM; - root = sb->s_root; - add_branch = kmalloc(sizeof(*add_branch), GFP_NOFS); - if (unlikely(!add_branch)) - goto out; - - err = au_hnotify_init_br(add_branch, perm); - if (unlikely(err)) - goto out_br; - - add_branch->br_wbr = NULL; - if (au_br_writable(perm)) { - /* may be freed separately at changing the branch permission */ - add_branch->br_wbr = kmalloc(sizeof(*add_branch->br_wbr), - GFP_NOFS); - if (unlikely(!add_branch->br_wbr)) - goto out_hnotify; - } - - add_branch->br_fhsm = NULL; - if (au_br_fhsm(perm)) { - err = au_fhsm_br_alloc(add_branch); - if (unlikely(err)) - goto out_wbr; - } - - err = au_sbr_realloc(au_sbi(sb), new_nbranch); - if (!err) - err = au_di_realloc(au_di(root), new_nbranch); - if (!err) { - inode = d_inode(root); - err = au_ii_realloc(au_ii(inode), new_nbranch); - } - if (!err) - return add_branch; /* success */ - -out_wbr: - kfree(add_branch->br_wbr); -out_hnotify: - au_hnotify_fin_br(add_branch); -out_br: - kfree(add_branch); -out: - return ERR_PTR(err); -} - -/* - * test if the branch permission is legal or not. - */ -static int test_br(struct inode *inode, int brperm, char *path) -{ - int err; - - err = (au_br_writable(brperm) && IS_RDONLY(inode)); - if (!err) - goto out; - - err = -EINVAL; - pr_err("write permission for readonly mount or inode, %s\n", path); - -out: - return err; -} - -/* - * returns: - * 0: success, the caller will add it - * plus: success, it is already unified, the caller should ignore it - * minus: error - */ -static int test_add(struct super_block *sb, struct au_opt_add *add, int remount) -{ - int err; - aufs_bindex_t bend, bindex; - struct dentry *root, *h_dentry; - struct inode *inode, *h_inode; - - root = sb->s_root; - bend = au_sbend(sb); - if (unlikely(bend >= 0 - && au_find_dbindex(root, add->path.dentry) >= 0)) { - err = 1; - if (!remount) { - err = -EINVAL; - pr_err("%s duplicated\n", add->pathname); - } - goto out; - } - - err = -ENOSPC; /* -E2BIG; */ - if (unlikely(AUFS_BRANCH_MAX <= add->bindex - || AUFS_BRANCH_MAX - 1 <= bend)) { - pr_err("number of branches exceeded %s\n", add->pathname); - goto out; - } - - err = -EDOM; - if (unlikely(add->bindex < 0 || bend + 1 < add->bindex)) { - pr_err("bad index %d\n", add->bindex); - goto out; - } - - inode = d_inode(add->path.dentry); - err = -ENOENT; - if (unlikely(!inode->i_nlink)) { - pr_err("no existence %s\n", add->pathname); - goto out; - } - - err = -EINVAL; - if (unlikely(inode->i_sb == sb)) { - pr_err("%s must be outside\n", add->pathname); - goto out; - } - - if (unlikely(au_test_fs_unsuppoted(inode->i_sb))) { - pr_err("unsupported filesystem, %s (%s)\n", - add->pathname, au_sbtype(inode->i_sb)); - goto out; - } - - if (unlikely(inode->i_sb->s_stack_depth)) { - pr_err("already stacked, %s (%s)\n", - add->pathname, au_sbtype(inode->i_sb)); - goto out; - } - - err = test_br(d_inode(add->path.dentry), add->perm, add->pathname); - if (unlikely(err)) - goto out; - - if (bend < 0) - return 0; /* success */ - - err = -EINVAL; - for (bindex = 0; bindex <= bend; bindex++) - if (unlikely(test_overlap(sb, add->path.dentry, - au_h_dptr(root, bindex)))) { - pr_err("%s is overlapped\n", add->pathname); - goto out; - } - - err = 0; - if (au_opt_test(au_mntflags(sb), WARN_PERM)) { - h_dentry = au_h_dptr(root, 0); - h_inode = d_inode(h_dentry); - if ((h_inode->i_mode & S_IALLUGO) != (inode->i_mode & S_IALLUGO) - || !uid_eq(h_inode->i_uid, inode->i_uid) - || !gid_eq(h_inode->i_gid, inode->i_gid)) - pr_warn("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n", - add->pathname, - i_uid_read(inode), i_gid_read(inode), - (inode->i_mode & S_IALLUGO), - i_uid_read(h_inode), i_gid_read(h_inode), - (h_inode->i_mode & S_IALLUGO)); - } - -out: - return err; -} - -/* - * initialize or clean the whiteouts for an adding branch - */ -static int au_br_init_wh(struct super_block *sb, struct au_branch *br, - int new_perm) -{ - int err, old_perm; - aufs_bindex_t bindex; - struct mutex *h_mtx; - struct au_wbr *wbr; - struct au_hinode *hdir; - struct dentry *h_dentry; - - err = vfsub_mnt_want_write(au_br_mnt(br)); - if (unlikely(err)) - goto out; - - wbr = br->br_wbr; - old_perm = br->br_perm; - br->br_perm = new_perm; - hdir = NULL; - h_mtx = NULL; - bindex = au_br_index(sb, br->br_id); - if (0 <= bindex) { - hdir = au_hi(d_inode(sb->s_root), bindex); - au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); - } else { - h_dentry = au_br_dentry(br); - h_mtx = &d_inode(h_dentry)->i_mutex; - mutex_lock_nested(h_mtx, AuLsc_I_PARENT); - } - if (!wbr) - err = au_wh_init(br, sb); - else { - wbr_wh_write_lock(wbr); - err = au_wh_init(br, sb); - wbr_wh_write_unlock(wbr); - } - if (hdir) - au_hn_imtx_unlock(hdir); - else - mutex_unlock(h_mtx); - vfsub_mnt_drop_write(au_br_mnt(br)); - br->br_perm = old_perm; - - if (!err && wbr && !au_br_writable(new_perm)) { - kfree(wbr); - br->br_wbr = NULL; - } - -out: - return err; -} - -static int au_wbr_init(struct au_branch *br, struct super_block *sb, - int perm) -{ - int err; - struct kstatfs kst; - struct au_wbr *wbr; - - wbr = br->br_wbr; - au_rw_init(&wbr->wbr_wh_rwsem); - memset(wbr->wbr_wh, 0, sizeof(wbr->wbr_wh)); - atomic_set(&wbr->wbr_wh_running, 0); - wbr->wbr_bytes = 0; - - /* - * a limit for rmdir/rename a dir - * cf. AUFS_MAX_NAMELEN in include/uapi/linux/aufs_type.h - */ - err = vfs_statfs(&br->br_path, &kst); - if (unlikely(err)) - goto out; - err = -EINVAL; - if (kst.f_namelen >= NAME_MAX) - err = au_br_init_wh(sb, br, perm); - else - pr_err("%pd(%s), unsupported namelen %ld\n", - au_br_dentry(br), - au_sbtype(au_br_dentry(br)->d_sb), kst.f_namelen); - -out: - return err; -} - -/* initialize a new branch */ -static int au_br_init(struct au_branch *br, struct super_block *sb, - struct au_opt_add *add) -{ - int err; - struct inode *h_inode; - - err = 0; - memset(&br->br_xino, 0, sizeof(br->br_xino)); - mutex_init(&br->br_xino.xi_nondir_mtx); - br->br_perm = add->perm; - br->br_path = add->path; /* set first, path_get() later */ - spin_lock_init(&br->br_dykey_lock); - memset(br->br_dykey, 0, sizeof(br->br_dykey)); - atomic_set(&br->br_count, 0); - atomic_set(&br->br_xino_running, 0); - br->br_id = au_new_br_id(sb); - AuDebugOn(br->br_id < 0); - - if (au_br_writable(add->perm)) { - err = au_wbr_init(br, sb, add->perm); - if (unlikely(err)) - goto out_err; - } - - if (au_opt_test(au_mntflags(sb), XINO)) { - h_inode = d_inode(add->path.dentry); - err = au_xino_br(sb, br, h_inode->i_ino, - au_sbr(sb, 0)->br_xino.xi_file, /*do_test*/1); - if (unlikely(err)) { - AuDebugOn(br->br_xino.xi_file); - goto out_err; - } - } - - sysaufs_br_init(br); - path_get(&br->br_path); - goto out; /* success */ - -out_err: - memset(&br->br_path, 0, sizeof(br->br_path)); -out: - return err; -} - -static void au_br_do_add_brp(struct au_sbinfo *sbinfo, aufs_bindex_t bindex, - struct au_branch *br, aufs_bindex_t bend, - aufs_bindex_t amount) -{ - struct au_branch **brp; - - AuRwMustWriteLock(&sbinfo->si_rwsem); - - brp = sbinfo->si_branch + bindex; - memmove(brp + 1, brp, sizeof(*brp) * amount); - *brp = br; - sbinfo->si_bend++; - if (unlikely(bend < 0)) - sbinfo->si_bend = 0; -} - -static void au_br_do_add_hdp(struct au_dinfo *dinfo, aufs_bindex_t bindex, - aufs_bindex_t bend, aufs_bindex_t amount) -{ - struct au_hdentry *hdp; - - AuRwMustWriteLock(&dinfo->di_rwsem); - - hdp = dinfo->di_hdentry + bindex; - memmove(hdp + 1, hdp, sizeof(*hdp) * amount); - au_h_dentry_init(hdp); - dinfo->di_bend++; - if (unlikely(bend < 0)) - dinfo->di_bstart = 0; -} - -static void au_br_do_add_hip(struct au_iinfo *iinfo, aufs_bindex_t bindex, - aufs_bindex_t bend, aufs_bindex_t amount) -{ - struct au_hinode *hip; - - AuRwMustWriteLock(&iinfo->ii_rwsem); - - hip = iinfo->ii_hinode + bindex; - memmove(hip + 1, hip, sizeof(*hip) * amount); - hip->hi_inode = NULL; - au_hn_init(hip); - iinfo->ii_bend++; - if (unlikely(bend < 0)) - iinfo->ii_bstart = 0; -} - -static void au_br_do_add(struct super_block *sb, struct au_branch *br, - aufs_bindex_t bindex) -{ - struct dentry *root, *h_dentry; - struct inode *root_inode, *h_inode; - aufs_bindex_t bend, amount; - - root = sb->s_root; - root_inode = d_inode(root); - bend = au_sbend(sb); - amount = bend + 1 - bindex; - h_dentry = au_br_dentry(br); - au_sbilist_lock(); - au_br_do_add_brp(au_sbi(sb), bindex, br, bend, amount); - au_br_do_add_hdp(au_di(root), bindex, bend, amount); - au_br_do_add_hip(au_ii(root_inode), bindex, bend, amount); - au_set_h_dptr(root, bindex, dget(h_dentry)); - h_inode = d_inode(h_dentry); - au_set_h_iptr(root_inode, bindex, au_igrab(h_inode), /*flags*/0); - au_sbilist_unlock(); -} - -int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount) -{ - int err; - aufs_bindex_t bend, add_bindex; - struct dentry *root, *h_dentry; - struct inode *root_inode; - struct au_branch *add_branch; - - root = sb->s_root; - root_inode = d_inode(root); - IMustLock(root_inode); - err = test_add(sb, add, remount); - if (unlikely(err < 0)) - goto out; - if (err) { - err = 0; - goto out; /* success */ - } - - bend = au_sbend(sb); - add_branch = au_br_alloc(sb, bend + 2, add->perm); - err = PTR_ERR(add_branch); - if (IS_ERR(add_branch)) - goto out; - - err = au_br_init(add_branch, sb, add); - if (unlikely(err)) { - au_br_do_free(add_branch); - goto out; - } - - add_bindex = add->bindex; - if (!remount) - au_br_do_add(sb, add_branch, add_bindex); - else { - sysaufs_brs_del(sb, add_bindex); - au_br_do_add(sb, add_branch, add_bindex); - sysaufs_brs_add(sb, add_bindex); - } - - h_dentry = add->path.dentry; - if (!add_bindex) { - au_cpup_attr_all(root_inode, /*force*/1); - sb->s_maxbytes = h_dentry->d_sb->s_maxbytes; - } else - au_add_nlink(root_inode, d_inode(h_dentry)); - - /* - * this test/set prevents aufs from handling unnecesary notify events - * of xino files, in case of re-adding a writable branch which was - * once detached from aufs. - */ - if (au_xino_brid(sb) < 0 - && au_br_writable(add_branch->br_perm) - && !au_test_fs_bad_xino(h_dentry->d_sb) - && add_branch->br_xino.xi_file - && add_branch->br_xino.xi_file->f_path.dentry->d_parent == h_dentry) - au_xino_brid_set(sb, add_branch->br_id); - -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -static unsigned long long au_farray_cb(void *a, - unsigned long long max __maybe_unused, - void *arg) -{ - unsigned long long n; - struct file **p, *f; - struct au_sphlhead *files; - struct au_finfo *finfo; - struct super_block *sb = arg; - - n = 0; - p = a; - files = &au_sbi(sb)->si_files; - spin_lock(&files->spin); - hlist_for_each_entry(finfo, &files->head, fi_hlist) { - f = finfo->fi_file; - if (file_count(f) - && !special_file(file_inode(f)->i_mode)) { - get_file(f); - *p++ = f; - n++; - AuDebugOn(n > max); - } - } - spin_unlock(&files->spin); - - return n; -} - -static struct file **au_farray_alloc(struct super_block *sb, - unsigned long long *max) -{ - *max = atomic_long_read(&au_sbi(sb)->si_nfiles); - return au_array_alloc(max, au_farray_cb, sb); -} - -static void au_farray_free(struct file **a, unsigned long long max) -{ - unsigned long long ull; - - for (ull = 0; ull < max; ull++) - if (a[ull]) - fput(a[ull]); - au_array_free(a); -} - -/* ---------------------------------------------------------------------- */ - -/* - * delete a branch - */ - -/* to show the line number, do not make it inlined function */ -#define AuVerbose(do_info, fmt, ...) do { \ - if (do_info) \ - pr_info(fmt, ##__VA_ARGS__); \ -} while (0) - -static int au_test_ibusy(struct inode *inode, aufs_bindex_t bstart, - aufs_bindex_t bend) -{ - return (inode && !S_ISDIR(inode->i_mode)) || bstart == bend; -} - -static int au_test_dbusy(struct dentry *dentry, aufs_bindex_t bstart, - aufs_bindex_t bend) -{ - return au_test_ibusy(d_inode(dentry), bstart, bend); -} - -/* - * test if the branch is deletable or not. - */ -static int test_dentry_busy(struct dentry *root, aufs_bindex_t bindex, - unsigned int sigen, const unsigned int verbose) -{ - int err, i, j, ndentry; - aufs_bindex_t bstart, bend; - struct au_dcsub_pages dpages; - struct au_dpage *dpage; - struct dentry *d; - - err = au_dpages_init(&dpages, GFP_NOFS); - if (unlikely(err)) - goto out; - err = au_dcsub_pages(&dpages, root, NULL, NULL); - if (unlikely(err)) - goto out_dpages; - - for (i = 0; !err && i < dpages.ndpage; i++) { - dpage = dpages.dpages + i; - ndentry = dpage->ndentry; - for (j = 0; !err && j < ndentry; j++) { - d = dpage->dentries[j]; - AuDebugOn(au_dcount(d) <= 0); - if (!au_digen_test(d, sigen)) { - di_read_lock_child(d, AuLock_IR); - if (unlikely(au_dbrange_test(d))) { - di_read_unlock(d, AuLock_IR); - continue; - } - } else { - di_write_lock_child(d); - if (unlikely(au_dbrange_test(d))) { - di_write_unlock(d); - continue; - } - err = au_reval_dpath(d, sigen); - if (!err) - di_downgrade_lock(d, AuLock_IR); - else { - di_write_unlock(d); - break; - } - } - - /* AuDbgDentry(d); */ - bstart = au_dbstart(d); - bend = au_dbend(d); - if (bstart <= bindex - && bindex <= bend - && au_h_dptr(d, bindex) - && au_test_dbusy(d, bstart, bend)) { - err = -EBUSY; - AuVerbose(verbose, "busy %pd\n", d); - AuDbgDentry(d); - } - di_read_unlock(d, AuLock_IR); - } - } - -out_dpages: - au_dpages_free(&dpages); -out: - return err; -} - -static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex, - unsigned int sigen, const unsigned int verbose) -{ - int err; - unsigned long long max, ull; - struct inode *i, **array; - aufs_bindex_t bstart, bend; - - array = au_iarray_alloc(sb, &max); - err = PTR_ERR(array); - if (IS_ERR(array)) - goto out; - - err = 0; - AuDbg("b%d\n", bindex); - for (ull = 0; !err && ull < max; ull++) { - i = array[ull]; - if (unlikely(!i)) - break; - if (i->i_ino == AUFS_ROOT_INO) - continue; - - /* AuDbgInode(i); */ - if (au_iigen(i, NULL) == sigen) - ii_read_lock_child(i); - else { - ii_write_lock_child(i); - err = au_refresh_hinode_self(i); - au_iigen_dec(i); - if (!err) - ii_downgrade_lock(i); - else { - ii_write_unlock(i); - break; - } - } - - bstart = au_ibstart(i); - bend = au_ibend(i); - if (bstart <= bindex - && bindex <= bend - && au_h_iptr(i, bindex) - && au_test_ibusy(i, bstart, bend)) { - err = -EBUSY; - AuVerbose(verbose, "busy i%lu\n", i->i_ino); - AuDbgInode(i); - } - ii_read_unlock(i); - } - au_iarray_free(array, max); - -out: - return err; -} - -static int test_children_busy(struct dentry *root, aufs_bindex_t bindex, - const unsigned int verbose) -{ - int err; - unsigned int sigen; - - sigen = au_sigen(root->d_sb); - DiMustNoWaiters(root); - IiMustNoWaiters(d_inode(root)); - di_write_unlock(root); - err = test_dentry_busy(root, bindex, sigen, verbose); - if (!err) - err = test_inode_busy(root->d_sb, bindex, sigen, verbose); - di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */ - - return err; -} - -static int test_dir_busy(struct file *file, aufs_bindex_t br_id, - struct file **to_free, int *idx) -{ - int err; - unsigned char matched, root; - aufs_bindex_t bindex, bend; - struct au_fidir *fidir; - struct au_hfile *hfile; - - err = 0; - root = IS_ROOT(file->f_path.dentry); - if (root) { - get_file(file); - to_free[*idx] = file; - (*idx)++; - goto out; - } - - matched = 0; - fidir = au_fi(file)->fi_hdir; - AuDebugOn(!fidir); - bend = au_fbend_dir(file); - for (bindex = au_fbstart(file); bindex <= bend; bindex++) { - hfile = fidir->fd_hfile + bindex; - if (!hfile->hf_file) - continue; - - if (hfile->hf_br->br_id == br_id) { - matched = 1; - break; - } - } - if (matched) - err = -EBUSY; - -out: - return err; -} - -static int test_file_busy(struct super_block *sb, aufs_bindex_t br_id, - struct file **to_free, int opened) -{ - int err, idx; - unsigned long long ull, max; - aufs_bindex_t bstart; - struct file *file, **array; - struct dentry *root; - struct au_hfile *hfile; - - array = au_farray_alloc(sb, &max); - err = PTR_ERR(array); - if (IS_ERR(array)) - goto out; - - err = 0; - idx = 0; - root = sb->s_root; - di_write_unlock(root); - for (ull = 0; ull < max; ull++) { - file = array[ull]; - if (unlikely(!file)) - break; - - /* AuDbg("%pD\n", file); */ - fi_read_lock(file); - bstart = au_fbstart(file); - if (!d_is_dir(file->f_path.dentry)) { - hfile = &au_fi(file)->fi_htop; - if (hfile->hf_br->br_id == br_id) - err = -EBUSY; - } else - err = test_dir_busy(file, br_id, to_free, &idx); - fi_read_unlock(file); - if (unlikely(err)) - break; - } - di_write_lock_child(root); - au_farray_free(array, max); - AuDebugOn(idx > opened); - -out: - return err; -} - -static void br_del_file(struct file **to_free, unsigned long long opened, - aufs_bindex_t br_id) -{ - unsigned long long ull; - aufs_bindex_t bindex, bstart, bend, bfound; - struct file *file; - struct au_fidir *fidir; - struct au_hfile *hfile; - - for (ull = 0; ull < opened; ull++) { - file = to_free[ull]; - if (unlikely(!file)) - break; - - /* AuDbg("%pD\n", file); */ - AuDebugOn(!d_is_dir(file->f_path.dentry)); - bfound = -1; - fidir = au_fi(file)->fi_hdir; - AuDebugOn(!fidir); - fi_write_lock(file); - bstart = au_fbstart(file); - bend = au_fbend_dir(file); - for (bindex = bstart; bindex <= bend; bindex++) { - hfile = fidir->fd_hfile + bindex; - if (!hfile->hf_file) - continue; - - if (hfile->hf_br->br_id == br_id) { - bfound = bindex; - break; - } - } - AuDebugOn(bfound < 0); - au_set_h_fptr(file, bfound, NULL); - if (bfound == bstart) { - for (bstart++; bstart <= bend; bstart++) - if (au_hf_dir(file, bstart)) { - au_set_fbstart(file, bstart); - break; - } - } - fi_write_unlock(file); - } -} - -static void au_br_do_del_brp(struct au_sbinfo *sbinfo, - const aufs_bindex_t bindex, - const aufs_bindex_t bend) -{ - struct au_branch **brp, **p; - - AuRwMustWriteLock(&sbinfo->si_rwsem); - - brp = sbinfo->si_branch + bindex; - if (bindex < bend) - memmove(brp, brp + 1, sizeof(*brp) * (bend - bindex)); - sbinfo->si_branch[0 + bend] = NULL; - sbinfo->si_bend--; - - p = krealloc(sbinfo->si_branch, sizeof(*p) * bend, AuGFP_SBILIST); - if (p) - sbinfo->si_branch = p; - /* harmless error */ -} - -static void au_br_do_del_hdp(struct au_dinfo *dinfo, const aufs_bindex_t bindex, - const aufs_bindex_t bend) -{ - struct au_hdentry *hdp, *p; - - AuRwMustWriteLock(&dinfo->di_rwsem); - - hdp = dinfo->di_hdentry; - if (bindex < bend) - memmove(hdp + bindex, hdp + bindex + 1, - sizeof(*hdp) * (bend - bindex)); - hdp[0 + bend].hd_dentry = NULL; - dinfo->di_bend--; - - p = krealloc(hdp, sizeof(*p) * bend, AuGFP_SBILIST); - if (p) - dinfo->di_hdentry = p; - /* harmless error */ -} - -static void au_br_do_del_hip(struct au_iinfo *iinfo, const aufs_bindex_t bindex, - const aufs_bindex_t bend) -{ - struct au_hinode *hip, *p; - - AuRwMustWriteLock(&iinfo->ii_rwsem); - - hip = iinfo->ii_hinode + bindex; - if (bindex < bend) - memmove(hip, hip + 1, sizeof(*hip) * (bend - bindex)); - iinfo->ii_hinode[0 + bend].hi_inode = NULL; - au_hn_init(iinfo->ii_hinode + bend); - iinfo->ii_bend--; - - p = krealloc(iinfo->ii_hinode, sizeof(*p) * bend, AuGFP_SBILIST); - if (p) - iinfo->ii_hinode = p; - /* harmless error */ -} - -static void au_br_do_del(struct super_block *sb, aufs_bindex_t bindex, - struct au_branch *br) -{ - aufs_bindex_t bend; - struct au_sbinfo *sbinfo; - struct dentry *root, *h_root; - struct inode *inode, *h_inode; - struct au_hinode *hinode; - - SiMustWriteLock(sb); - - root = sb->s_root; - inode = d_inode(root); - sbinfo = au_sbi(sb); - bend = sbinfo->si_bend; - - h_root = au_h_dptr(root, bindex); - hinode = au_hi(inode, bindex); - h_inode = au_igrab(hinode->hi_inode); - au_hiput(hinode); - - au_sbilist_lock(); - au_br_do_del_brp(sbinfo, bindex, bend); - au_br_do_del_hdp(au_di(root), bindex, bend); - au_br_do_del_hip(au_ii(inode), bindex, bend); - au_sbilist_unlock(); - - dput(h_root); - iput(h_inode); - au_br_do_free(br); -} - -static unsigned long long empty_cb(void *array, unsigned long long max, - void *arg) -{ - return max; -} - -int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount) -{ - int err, rerr, i; - unsigned long long opened; - unsigned int mnt_flags; - aufs_bindex_t bindex, bend, br_id; - unsigned char do_wh, verbose; - struct au_branch *br; - struct au_wbr *wbr; - struct dentry *root; - struct file **to_free; - - err = 0; - opened = 0; - to_free = NULL; - root = sb->s_root; - bindex = au_find_dbindex(root, del->h_path.dentry); - if (bindex < 0) { - if (remount) - goto out; /* success */ - err = -ENOENT; - pr_err("%s no such branch\n", del->pathname); - goto out; - } - AuDbg("bindex b%d\n", bindex); - - err = -EBUSY; - mnt_flags = au_mntflags(sb); - verbose = !!au_opt_test(mnt_flags, VERBOSE); - bend = au_sbend(sb); - if (unlikely(!bend)) { - AuVerbose(verbose, "no more branches left\n"); - goto out; - } - br = au_sbr(sb, bindex); - AuDebugOn(!path_equal(&br->br_path, &del->h_path)); - - br_id = br->br_id; - opened = atomic_read(&br->br_count); - if (unlikely(opened)) { - to_free = au_array_alloc(&opened, empty_cb, NULL); - err = PTR_ERR(to_free); - if (IS_ERR(to_free)) - goto out; - - err = test_file_busy(sb, br_id, to_free, opened); - if (unlikely(err)) { - AuVerbose(verbose, "%llu file(s) opened\n", opened); - goto out; - } - } - - wbr = br->br_wbr; - do_wh = wbr && (wbr->wbr_whbase || wbr->wbr_plink || wbr->wbr_orph); - if (do_wh) { - /* instead of WbrWhMustWriteLock(wbr) */ - SiMustWriteLock(sb); - for (i = 0; i < AuBrWh_Last; i++) { - dput(wbr->wbr_wh[i]); - wbr->wbr_wh[i] = NULL; - } - } - - err = test_children_busy(root, bindex, verbose); - if (unlikely(err)) { - if (do_wh) - goto out_wh; - goto out; - } - - err = 0; - if (to_free) { - /* - * now we confirmed the branch is deletable. - * let's free the remaining opened dirs on the branch. - */ - di_write_unlock(root); - br_del_file(to_free, opened, br_id); - di_write_lock_child(root); - } - - if (!remount) - au_br_do_del(sb, bindex, br); - else { - sysaufs_brs_del(sb, bindex); - au_br_do_del(sb, bindex, br); - sysaufs_brs_add(sb, bindex); - } - - if (!bindex) { - au_cpup_attr_all(d_inode(root), /*force*/1); - sb->s_maxbytes = au_sbr_sb(sb, 0)->s_maxbytes; - } else - au_sub_nlink(d_inode(root), d_inode(del->h_path.dentry)); - if (au_opt_test(mnt_flags, PLINK)) - au_plink_half_refresh(sb, br_id); - - if (au_xino_brid(sb) == br_id) - au_xino_brid_set(sb, -1); - goto out; /* success */ - -out_wh: - /* revert */ - rerr = au_br_init_wh(sb, br, br->br_perm); - if (rerr) - pr_warn("failed re-creating base whiteout, %s. (%d)\n", - del->pathname, rerr); -out: - if (to_free) - au_farray_free(to_free, opened); - return err; -} - -/* ---------------------------------------------------------------------- */ - -static int au_ibusy(struct super_block *sb, struct aufs_ibusy __user *arg) -{ - int err; - aufs_bindex_t bstart, bend; - struct aufs_ibusy ibusy; - struct inode *inode, *h_inode; - - err = -EPERM; - if (unlikely(!capable(CAP_SYS_ADMIN))) - goto out; - - err = copy_from_user(&ibusy, arg, sizeof(ibusy)); - if (!err) - err = !access_ok(VERIFY_WRITE, &arg->h_ino, sizeof(arg->h_ino)); - if (unlikely(err)) { - err = -EFAULT; - AuTraceErr(err); - goto out; - } - - err = -EINVAL; - si_read_lock(sb, AuLock_FLUSH); - if (unlikely(ibusy.bindex < 0 || ibusy.bindex > au_sbend(sb))) - goto out_unlock; - - err = 0; - ibusy.h_ino = 0; /* invalid */ - inode = ilookup(sb, ibusy.ino); - if (!inode - || inode->i_ino == AUFS_ROOT_INO - || is_bad_inode(inode)) - goto out_unlock; - - ii_read_lock_child(inode); - bstart = au_ibstart(inode); - bend = au_ibend(inode); - if (bstart <= ibusy.bindex && ibusy.bindex <= bend) { - h_inode = au_h_iptr(inode, ibusy.bindex); - if (h_inode && au_test_ibusy(inode, bstart, bend)) - ibusy.h_ino = h_inode->i_ino; - } - ii_read_unlock(inode); - iput(inode); - -out_unlock: - si_read_unlock(sb); - if (!err) { - err = __put_user(ibusy.h_ino, &arg->h_ino); - if (unlikely(err)) { - err = -EFAULT; - AuTraceErr(err); - } - } -out: - return err; -} - -long au_ibusy_ioctl(struct file *file, unsigned long arg) -{ - return au_ibusy(file->f_path.dentry->d_sb, (void __user *)arg); -} - -#ifdef CONFIG_COMPAT -long au_ibusy_compat_ioctl(struct file *file, unsigned long arg) -{ - return au_ibusy(file->f_path.dentry->d_sb, compat_ptr(arg)); -} -#endif - -/* ---------------------------------------------------------------------- */ - -/* - * change a branch permission - */ - -static void au_warn_ima(void) -{ -#ifdef CONFIG_IMA - /* since it doesn't support mark_files_ro() */ - AuWarn1("RW -> RO makes IMA to produce wrong message\n"); -#endif -} - -static int do_need_sigen_inc(int a, int b) -{ - return au_br_whable(a) && !au_br_whable(b); -} - -static int need_sigen_inc(int old, int new) -{ - return do_need_sigen_inc(old, new) - || do_need_sigen_inc(new, old); -} - -static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex) -{ - int err, do_warn; - unsigned int mnt_flags; - unsigned long long ull, max; - aufs_bindex_t br_id; - unsigned char verbose, writer; - struct file *file, *hf, **array; - struct au_hfile *hfile; - - mnt_flags = au_mntflags(sb); - verbose = !!au_opt_test(mnt_flags, VERBOSE); - - array = au_farray_alloc(sb, &max); - err = PTR_ERR(array); - if (IS_ERR(array)) - goto out; - - do_warn = 0; - br_id = au_sbr_id(sb, bindex); - for (ull = 0; ull < max; ull++) { - file = array[ull]; - if (unlikely(!file)) - break; - - /* AuDbg("%pD\n", file); */ - fi_read_lock(file); - if (unlikely(au_test_mmapped(file))) { - err = -EBUSY; - AuVerbose(verbose, "mmapped %pD\n", file); - AuDbgFile(file); - FiMustNoWaiters(file); - fi_read_unlock(file); - goto out_array; - } - - hfile = &au_fi(file)->fi_htop; - hf = hfile->hf_file; - if (!d_is_reg(file->f_path.dentry) - || !(file->f_mode & FMODE_WRITE) - || hfile->hf_br->br_id != br_id - || !(hf->f_mode & FMODE_WRITE)) - array[ull] = NULL; - else { - do_warn = 1; - get_file(file); - } - - FiMustNoWaiters(file); - fi_read_unlock(file); - fput(file); - } - - err = 0; - if (do_warn) - au_warn_ima(); - - for (ull = 0; ull < max; ull++) { - file = array[ull]; - if (!file) - continue; - - /* todo: already flushed? */ - /* - * fs/super.c:mark_files_ro() is gone, but aufs keeps its - * approach which resets f_mode and calls mnt_drop_write() and - * file_release_write() for each file, because the branch - * attribute in aufs world is totally different from the native - * fs rw/ro mode. - */ - /* fi_read_lock(file); */ - hfile = &au_fi(file)->fi_htop; - hf = hfile->hf_file; - /* fi_read_unlock(file); */ - spin_lock(&hf->f_lock); - writer = !!(hf->f_mode & FMODE_WRITER); - hf->f_mode &= ~(FMODE_WRITE | FMODE_WRITER); - spin_unlock(&hf->f_lock); - if (writer) { - put_write_access(file_inode(hf)); - __mnt_drop_write(hf->f_path.mnt); - } - } - -out_array: - au_farray_free(array, max); -out: - AuTraceErr(err); - return err; -} - -int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, - int *do_refresh) -{ - int err, rerr; - aufs_bindex_t bindex; - struct dentry *root; - struct au_branch *br; - struct au_br_fhsm *bf; - - root = sb->s_root; - bindex = au_find_dbindex(root, mod->h_root); - if (bindex < 0) { - if (remount) - return 0; /* success */ - err = -ENOENT; - pr_err("%s no such branch\n", mod->path); - goto out; - } - AuDbg("bindex b%d\n", bindex); - - err = test_br(d_inode(mod->h_root), mod->perm, mod->path); - if (unlikely(err)) - goto out; - - br = au_sbr(sb, bindex); - AuDebugOn(mod->h_root != au_br_dentry(br)); - if (br->br_perm == mod->perm) - return 0; /* success */ - - /* pre-allocate for non-fhsm --> fhsm */ - bf = NULL; - if (!au_br_fhsm(br->br_perm) && au_br_fhsm(mod->perm)) { - err = au_fhsm_br_alloc(br); - if (unlikely(err)) - goto out; - bf = br->br_fhsm; - br->br_fhsm = NULL; - } - - if (au_br_writable(br->br_perm)) { - /* remove whiteout base */ - err = au_br_init_wh(sb, br, mod->perm); - if (unlikely(err)) - goto out_bf; - - if (!au_br_writable(mod->perm)) { - /* rw --> ro, file might be mmapped */ - DiMustNoWaiters(root); - IiMustNoWaiters(d_inode(root)); - di_write_unlock(root); - err = au_br_mod_files_ro(sb, bindex); - /* aufs_write_lock() calls ..._child() */ - di_write_lock_child(root); - - if (unlikely(err)) { - rerr = -ENOMEM; - br->br_wbr = kmalloc(sizeof(*br->br_wbr), - GFP_NOFS); - if (br->br_wbr) - rerr = au_wbr_init(br, sb, br->br_perm); - if (unlikely(rerr)) { - AuIOErr("nested error %d (%d)\n", - rerr, err); - br->br_perm = mod->perm; - } - } - } - } else if (au_br_writable(mod->perm)) { - /* ro --> rw */ - err = -ENOMEM; - br->br_wbr = kmalloc(sizeof(*br->br_wbr), GFP_NOFS); - if (br->br_wbr) { - err = au_wbr_init(br, sb, mod->perm); - if (unlikely(err)) { - kfree(br->br_wbr); - br->br_wbr = NULL; - } - } - } - if (unlikely(err)) - goto out_bf; - - if (au_br_fhsm(br->br_perm)) { - if (!au_br_fhsm(mod->perm)) { - /* fhsm --> non-fhsm */ - au_br_fhsm_fin(br->br_fhsm); - kfree(br->br_fhsm); - br->br_fhsm = NULL; - } - } else if (au_br_fhsm(mod->perm)) - /* non-fhsm --> fhsm */ - br->br_fhsm = bf; - - *do_refresh |= need_sigen_inc(br->br_perm, mod->perm); - br->br_perm = mod->perm; - goto out; /* success */ - -out_bf: - kfree(bf); -out: - AuTraceErr(err); - return err; -} - -/* ---------------------------------------------------------------------- */ - -int au_br_stfs(struct au_branch *br, struct aufs_stfs *stfs) -{ - int err; - struct kstatfs kstfs; - - err = vfs_statfs(&br->br_path, &kstfs); - if (!err) { - stfs->f_blocks = kstfs.f_blocks; - stfs->f_bavail = kstfs.f_bavail; - stfs->f_files = kstfs.f_files; - stfs->f_ffree = kstfs.f_ffree; - } - - return err; -} diff --git a/fs/aufs/branch.h b/fs/aufs/branch.h deleted file mode 100644 index 3d026f57a..000000000 --- a/fs/aufs/branch.h +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * branch filesystems and xino for them - */ - -#ifndef __AUFS_BRANCH_H__ -#define __AUFS_BRANCH_H__ - -#ifdef __KERNEL__ - -#include <linux/mount.h> -#include "dynop.h" -#include "rwsem.h" -#include "super.h" - -/* ---------------------------------------------------------------------- */ - -/* a xino file */ -struct au_xino_file { - struct file *xi_file; - struct mutex xi_nondir_mtx; - - /* todo: make xino files an array to support huge inode number */ - -#ifdef CONFIG_DEBUG_FS - struct dentry *xi_dbgaufs; -#endif -}; - -/* File-based Hierarchical Storage Management */ -struct au_br_fhsm { -#ifdef CONFIG_AUFS_FHSM - struct mutex bf_lock; - unsigned long bf_jiffy; - struct aufs_stfs bf_stfs; - int bf_readable; -#endif -}; - -/* members for writable branch only */ -enum {AuBrWh_BASE, AuBrWh_PLINK, AuBrWh_ORPH, AuBrWh_Last}; -struct au_wbr { - struct au_rwsem wbr_wh_rwsem; - struct dentry *wbr_wh[AuBrWh_Last]; - atomic_t wbr_wh_running; -#define wbr_whbase wbr_wh[AuBrWh_BASE] /* whiteout base */ -#define wbr_plink wbr_wh[AuBrWh_PLINK] /* pseudo-link dir */ -#define wbr_orph wbr_wh[AuBrWh_ORPH] /* dir for orphans */ - - /* mfs mode */ - unsigned long long wbr_bytes; -}; - -/* ext2 has 3 types of operations at least, ext3 has 4 */ -#define AuBrDynOp (AuDyLast * 4) - -#ifdef CONFIG_AUFS_HFSNOTIFY -/* support for asynchronous destruction */ -struct au_br_hfsnotify { - struct fsnotify_group *hfsn_group; -}; -#endif - -/* sysfs entries */ -struct au_brsysfs { - char name[16]; - struct attribute attr; -}; - -enum { - AuBrSysfs_BR, - AuBrSysfs_BRID, - AuBrSysfs_Last -}; - -/* protected by superblock rwsem */ -struct au_branch { - struct au_xino_file br_xino; - - aufs_bindex_t br_id; - - int br_perm; - struct path br_path; - spinlock_t br_dykey_lock; - struct au_dykey *br_dykey[AuBrDynOp]; - atomic_t br_count; - - struct au_wbr *br_wbr; - struct au_br_fhsm *br_fhsm; - - /* xino truncation */ - atomic_t br_xino_running; - -#ifdef CONFIG_AUFS_HFSNOTIFY - struct au_br_hfsnotify *br_hfsn; -#endif - -#ifdef CONFIG_SYSFS - /* entries under sysfs per mount-point */ - struct au_brsysfs br_sysfs[AuBrSysfs_Last]; -#endif -}; - -/* ---------------------------------------------------------------------- */ - -static inline struct vfsmount *au_br_mnt(struct au_branch *br) -{ - return br->br_path.mnt; -} - -static inline struct dentry *au_br_dentry(struct au_branch *br) -{ - return br->br_path.dentry; -} - -static inline struct super_block *au_br_sb(struct au_branch *br) -{ - return au_br_mnt(br)->mnt_sb; -} - -static inline int au_br_rdonly(struct au_branch *br) -{ - return ((au_br_sb(br)->s_flags & MS_RDONLY) - || !au_br_writable(br->br_perm)) - ? -EROFS : 0; -} - -static inline int au_br_hnotifyable(int brperm __maybe_unused) -{ -#ifdef CONFIG_AUFS_HNOTIFY - return !(brperm & AuBrPerm_RR); -#else - return 0; -#endif -} - -static inline int au_br_test_oflag(int oflag, struct au_branch *br) -{ - int err, exec_flag; - - err = 0; - exec_flag = oflag & __FMODE_EXEC; - if (unlikely(exec_flag && (au_br_mnt(br)->mnt_flags & MNT_NOEXEC))) - err = -EACCES; - - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* branch.c */ -struct au_sbinfo; -void au_br_free(struct au_sbinfo *sinfo); -int au_br_index(struct super_block *sb, aufs_bindex_t br_id); -struct au_opt_add; -int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount); -struct au_opt_del; -int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount); -long au_ibusy_ioctl(struct file *file, unsigned long arg); -#ifdef CONFIG_COMPAT -long au_ibusy_compat_ioctl(struct file *file, unsigned long arg); -#endif -struct au_opt_mod; -int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, - int *do_refresh); -struct aufs_stfs; -int au_br_stfs(struct au_branch *br, struct aufs_stfs *stfs); - -/* xino.c */ -static const loff_t au_loff_max = LLONG_MAX; - -int au_xib_trunc(struct super_block *sb); -ssize_t xino_fread(vfs_readf_t func, struct file *file, void *buf, size_t size, - loff_t *pos); -ssize_t xino_fwrite(vfs_writef_t func, struct file *file, void *buf, - size_t size, loff_t *pos); -struct file *au_xino_create2(struct file *base_file, struct file *copy_src); -struct file *au_xino_create(struct super_block *sb, char *fname, int silent); -ino_t au_xino_new_ino(struct super_block *sb); -void au_xino_delete_inode(struct inode *inode, const int unlinked); -int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, - ino_t ino); -int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, - ino_t *ino); -int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t hino, - struct file *base_file, int do_test); -int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex); - -struct au_opt_xino; -int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount); -void au_xino_clr(struct super_block *sb); -struct file *au_xino_def(struct super_block *sb); -int au_xino_path(struct seq_file *seq, struct file *file); - -/* ---------------------------------------------------------------------- */ - -/* Superblock to branch */ -static inline -aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex) -{ - return au_sbr(sb, bindex)->br_id; -} - -static inline -struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex) -{ - return au_br_mnt(au_sbr(sb, bindex)); -} - -static inline -struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex) -{ - return au_br_sb(au_sbr(sb, bindex)); -} - -static inline void au_sbr_put(struct super_block *sb, aufs_bindex_t bindex) -{ - atomic_dec(&au_sbr(sb, bindex)->br_count); -} - -static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex) -{ - return au_sbr(sb, bindex)->br_perm; -} - -static inline int au_sbr_whable(struct super_block *sb, aufs_bindex_t bindex) -{ - return au_br_whable(au_sbr_perm(sb, bindex)); -} - -/* ---------------------------------------------------------------------- */ - -/* - * wbr_wh_read_lock, wbr_wh_write_lock - * wbr_wh_read_unlock, wbr_wh_write_unlock, wbr_wh_downgrade_lock - */ -AuSimpleRwsemFuncs(wbr_wh, struct au_wbr *wbr, &wbr->wbr_wh_rwsem); - -#define WbrWhMustNoWaiters(wbr) AuRwMustNoWaiters(&wbr->wbr_wh_rwsem) -#define WbrWhMustAnyLock(wbr) AuRwMustAnyLock(&wbr->wbr_wh_rwsem) -#define WbrWhMustWriteLock(wbr) AuRwMustWriteLock(&wbr->wbr_wh_rwsem) - -/* ---------------------------------------------------------------------- */ - -#ifdef CONFIG_AUFS_FHSM -static inline void au_br_fhsm_init(struct au_br_fhsm *brfhsm) -{ - mutex_init(&brfhsm->bf_lock); - brfhsm->bf_jiffy = 0; - brfhsm->bf_readable = 0; -} - -static inline void au_br_fhsm_fin(struct au_br_fhsm *brfhsm) -{ - mutex_destroy(&brfhsm->bf_lock); -} -#else -AuStubVoid(au_br_fhsm_init, struct au_br_fhsm *brfhsm) -AuStubVoid(au_br_fhsm_fin, struct au_br_fhsm *brfhsm) -#endif - -#endif /* __KERNEL__ */ -#endif /* __AUFS_BRANCH_H__ */ diff --git a/fs/aufs/conf.mk b/fs/aufs/conf.mk deleted file mode 100644 index 0bbb2d3a5..000000000 --- a/fs/aufs/conf.mk +++ /dev/null @@ -1,38 +0,0 @@ - -AuConfStr = CONFIG_AUFS_FS=${CONFIG_AUFS_FS} - -define AuConf -ifdef ${1} -AuConfStr += ${1}=${${1}} -endif -endef - -AuConfAll = BRANCH_MAX_127 BRANCH_MAX_511 BRANCH_MAX_1023 BRANCH_MAX_32767 \ - SBILIST \ - HNOTIFY HFSNOTIFY \ - EXPORT INO_T_64 \ - XATTR \ - FHSM \ - RDU \ - SHWH \ - BR_RAMFS \ - BR_FUSE POLL \ - BR_HFSPLUS \ - BDEV_LOOP \ - DEBUG MAGIC_SYSRQ -$(foreach i, ${AuConfAll}, \ - $(eval $(call AuConf,CONFIG_AUFS_${i}))) - -AuConfName = ${obj}/conf.str -${AuConfName}.tmp: FORCE - @echo ${AuConfStr} | tr ' ' '\n' | sed -e 's/^/"/' -e 's/$$/\\n"/' > $@ -${AuConfName}: ${AuConfName}.tmp - @diff -q $< $@ > /dev/null 2>&1 || { \ - echo ' GEN ' $@; \ - cp -p $< $@; \ - } -FORCE: -clean-files += ${AuConfName} ${AuConfName}.tmp -${obj}/sysfs.o: ${AuConfName} - --include ${srctree}/${src}/conf_priv.mk diff --git a/fs/aufs/cpup.c b/fs/aufs/cpup.c deleted file mode 100644 index 3ea177a5d..000000000 --- a/fs/aufs/cpup.c +++ /dev/null @@ -1,1319 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * copy-up functions, see wbr_policy.c for copy-down - */ - -#include <linux/fs_stack.h> -#include <linux/mm.h> -#include "aufs.h" - -void au_cpup_attr_flags(struct inode *dst, unsigned int iflags) -{ - const unsigned int mask = S_DEAD | S_SWAPFILE | S_PRIVATE - | S_NOATIME | S_NOCMTIME | S_AUTOMOUNT; - - BUILD_BUG_ON(sizeof(iflags) != sizeof(dst->i_flags)); - - dst->i_flags |= iflags & ~mask; - if (au_test_fs_notime(dst->i_sb)) - dst->i_flags |= S_NOATIME | S_NOCMTIME; -} - -void au_cpup_attr_timesizes(struct inode *inode) -{ - struct inode *h_inode; - - h_inode = au_h_iptr(inode, au_ibstart(inode)); - fsstack_copy_attr_times(inode, h_inode); - fsstack_copy_inode_size(inode, h_inode); -} - -void au_cpup_attr_nlink(struct inode *inode, int force) -{ - struct inode *h_inode; - struct super_block *sb; - aufs_bindex_t bindex, bend; - - sb = inode->i_sb; - bindex = au_ibstart(inode); - h_inode = au_h_iptr(inode, bindex); - if (!force - && !S_ISDIR(h_inode->i_mode) - && au_opt_test(au_mntflags(sb), PLINK) - && au_plink_test(inode)) - return; - - /* - * 0 can happen in revalidating. - * h_inode->i_mutex may not be held here, but it is harmless since once - * i_nlink reaches 0, it will never become positive except O_TMPFILE - * case. - * todo: O_TMPFILE+linkat(AT_SYMLINK_FOLLOW) bypassing aufs may cause - * the incorrect link count. - */ - set_nlink(inode, h_inode->i_nlink); - - /* - * fewer nlink makes find(1) noisy, but larger nlink doesn't. - * it may includes whplink directory. - */ - if (S_ISDIR(h_inode->i_mode)) { - bend = au_ibend(inode); - for (bindex++; bindex <= bend; bindex++) { - h_inode = au_h_iptr(inode, bindex); - if (h_inode) - au_add_nlink(inode, h_inode); - } - } -} - -void au_cpup_attr_changeable(struct inode *inode) -{ - struct inode *h_inode; - - h_inode = au_h_iptr(inode, au_ibstart(inode)); - inode->i_mode = h_inode->i_mode; - inode->i_uid = h_inode->i_uid; - inode->i_gid = h_inode->i_gid; - au_cpup_attr_timesizes(inode); - au_cpup_attr_flags(inode, h_inode->i_flags); -} - -void au_cpup_igen(struct inode *inode, struct inode *h_inode) -{ - struct au_iinfo *iinfo = au_ii(inode); - - IiMustWriteLock(inode); - - iinfo->ii_higen = h_inode->i_generation; - iinfo->ii_hsb1 = h_inode->i_sb; -} - -void au_cpup_attr_all(struct inode *inode, int force) -{ - struct inode *h_inode; - - h_inode = au_h_iptr(inode, au_ibstart(inode)); - au_cpup_attr_changeable(inode); - if (inode->i_nlink > 0) - au_cpup_attr_nlink(inode, force); - inode->i_rdev = h_inode->i_rdev; - inode->i_blkbits = h_inode->i_blkbits; - au_cpup_igen(inode, h_inode); -} - -/* ---------------------------------------------------------------------- */ - -/* Note: dt_dentry and dt_h_dentry are not dget/dput-ed */ - -/* keep the timestamps of the parent dir when cpup */ -void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, - struct path *h_path) -{ - struct inode *h_inode; - - dt->dt_dentry = dentry; - dt->dt_h_path = *h_path; - h_inode = d_inode(h_path->dentry); - dt->dt_atime = h_inode->i_atime; - dt->dt_mtime = h_inode->i_mtime; - /* smp_mb(); */ -} - -void au_dtime_revert(struct au_dtime *dt) -{ - struct iattr attr; - int err; - - attr.ia_atime = dt->dt_atime; - attr.ia_mtime = dt->dt_mtime; - attr.ia_valid = ATTR_FORCE | ATTR_MTIME | ATTR_MTIME_SET - | ATTR_ATIME | ATTR_ATIME_SET; - - /* no delegation since this is a directory */ - err = vfsub_notify_change(&dt->dt_h_path, &attr, /*delegated*/NULL); - if (unlikely(err)) - pr_warn("restoring timestamps failed(%d). ignored\n", err); -} - -/* ---------------------------------------------------------------------- */ - -/* internal use only */ -struct au_cpup_reg_attr { - int valid; - struct kstat st; - unsigned int iflags; /* inode->i_flags */ -}; - -static noinline_for_stack -int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src, - struct au_cpup_reg_attr *h_src_attr) -{ - int err, sbits, icex; - unsigned int mnt_flags; - unsigned char verbose; - struct iattr ia; - struct path h_path; - struct inode *h_isrc, *h_idst; - struct kstat *h_st; - struct au_branch *br; - - h_path.dentry = au_h_dptr(dst, bindex); - h_idst = d_inode(h_path.dentry); - br = au_sbr(dst->d_sb, bindex); - h_path.mnt = au_br_mnt(br); - h_isrc = d_inode(h_src); - ia.ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID - | ATTR_ATIME | ATTR_MTIME - | ATTR_ATIME_SET | ATTR_MTIME_SET; - if (h_src_attr && h_src_attr->valid) { - h_st = &h_src_attr->st; - ia.ia_uid = h_st->uid; - ia.ia_gid = h_st->gid; - ia.ia_atime = h_st->atime; - ia.ia_mtime = h_st->mtime; - if (h_idst->i_mode != h_st->mode - && !S_ISLNK(h_idst->i_mode)) { - ia.ia_valid |= ATTR_MODE; - ia.ia_mode = h_st->mode; - } - sbits = !!(h_st->mode & (S_ISUID | S_ISGID)); - au_cpup_attr_flags(h_idst, h_src_attr->iflags); - } else { - ia.ia_uid = h_isrc->i_uid; - ia.ia_gid = h_isrc->i_gid; - ia.ia_atime = h_isrc->i_atime; - ia.ia_mtime = h_isrc->i_mtime; - if (h_idst->i_mode != h_isrc->i_mode - && !S_ISLNK(h_idst->i_mode)) { - ia.ia_valid |= ATTR_MODE; - ia.ia_mode = h_isrc->i_mode; - } - sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID)); - au_cpup_attr_flags(h_idst, h_isrc->i_flags); - } - /* no delegation since it is just created */ - err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL); - - /* is this nfs only? */ - if (!err && sbits && au_test_nfs(h_path.dentry->d_sb)) { - ia.ia_valid = ATTR_FORCE | ATTR_MODE; - ia.ia_mode = h_isrc->i_mode; - err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL); - } - - icex = br->br_perm & AuBrAttr_ICEX; - if (!err) { - mnt_flags = au_mntflags(dst->d_sb); - verbose = !!au_opt_test(mnt_flags, VERBOSE); - err = au_cpup_xattr(h_path.dentry, h_src, icex, verbose); - } - - return err; -} - -/* ---------------------------------------------------------------------- */ - -static int au_do_copy_file(struct file *dst, struct file *src, loff_t len, - char *buf, unsigned long blksize) -{ - int err; - size_t sz, rbytes, wbytes; - unsigned char all_zero; - char *p, *zp; - struct mutex *h_mtx; - /* reduce stack usage */ - struct iattr *ia; - - zp = page_address(ZERO_PAGE(0)); - if (unlikely(!zp)) - return -ENOMEM; /* possible? */ - - err = 0; - all_zero = 0; - while (len) { - AuDbg("len %lld\n", len); - sz = blksize; - if (len < blksize) - sz = len; - - rbytes = 0; - /* todo: signal_pending? */ - while (!rbytes || err == -EAGAIN || err == -EINTR) { - rbytes = vfsub_read_k(src, buf, sz, &src->f_pos); - err = rbytes; - } - if (unlikely(err < 0)) - break; - - all_zero = 0; - if (len >= rbytes && rbytes == blksize) - all_zero = !memcmp(buf, zp, rbytes); - if (!all_zero) { - wbytes = rbytes; - p = buf; - while (wbytes) { - size_t b; - - b = vfsub_write_k(dst, p, wbytes, &dst->f_pos); - err = b; - /* todo: signal_pending? */ - if (unlikely(err == -EAGAIN || err == -EINTR)) - continue; - if (unlikely(err < 0)) - break; - wbytes -= b; - p += b; - } - if (unlikely(err < 0)) - break; - } else { - loff_t res; - - AuLabel(hole); - res = vfsub_llseek(dst, rbytes, SEEK_CUR); - err = res; - if (unlikely(res < 0)) - break; - } - len -= rbytes; - err = 0; - } - - /* the last block may be a hole */ - if (!err && all_zero) { - AuLabel(last hole); - - err = 1; - if (au_test_nfs(dst->f_path.dentry->d_sb)) { - /* nfs requires this step to make last hole */ - /* is this only nfs? */ - do { - /* todo: signal_pending? */ - err = vfsub_write_k(dst, "\0", 1, &dst->f_pos); - } while (err == -EAGAIN || err == -EINTR); - if (err == 1) - dst->f_pos--; - } - - if (err == 1) { - ia = (void *)buf; - ia->ia_size = dst->f_pos; - ia->ia_valid = ATTR_SIZE | ATTR_FILE; - ia->ia_file = dst; - h_mtx = &file_inode(dst)->i_mutex; - mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); - /* no delegation since it is just created */ - err = vfsub_notify_change(&dst->f_path, ia, - /*delegated*/NULL); - mutex_unlock(h_mtx); - } - } - - return err; -} - -int au_copy_file(struct file *dst, struct file *src, loff_t len) -{ - int err; - unsigned long blksize; - unsigned char do_kfree; - char *buf; - - err = -ENOMEM; - blksize = dst->f_path.dentry->d_sb->s_blocksize; - if (!blksize || PAGE_SIZE < blksize) - blksize = PAGE_SIZE; - AuDbg("blksize %lu\n", blksize); - do_kfree = (blksize != PAGE_SIZE && blksize >= sizeof(struct iattr *)); - if (do_kfree) - buf = kmalloc(blksize, GFP_NOFS); - else - buf = (void *)__get_free_page(GFP_NOFS); - if (unlikely(!buf)) - goto out; - - if (len > (1 << 22)) - AuDbg("copying a large file %lld\n", (long long)len); - - src->f_pos = 0; - dst->f_pos = 0; - err = au_do_copy_file(dst, src, len, buf, blksize); - if (do_kfree) - kfree(buf); - else - free_page((unsigned long)buf); - -out: - return err; -} - -/* - * to support a sparse file which is opened with O_APPEND, - * we need to close the file. - */ -static int au_cp_regular(struct au_cp_generic *cpg) -{ - int err, i; - enum { SRC, DST }; - struct { - aufs_bindex_t bindex; - unsigned int flags; - struct dentry *dentry; - int force_wr; - struct file *file; - void *label; - } *f, file[] = { - { - .bindex = cpg->bsrc, - .flags = O_RDONLY | O_NOATIME | O_LARGEFILE, - .label = &&out - }, - { - .bindex = cpg->bdst, - .flags = O_WRONLY | O_NOATIME | O_LARGEFILE, - .force_wr = !!au_ftest_cpup(cpg->flags, RWDST), - .label = &&out_src - } - }; - struct super_block *sb; - - /* bsrc branch can be ro/rw. */ - sb = cpg->dentry->d_sb; - f = file; - for (i = 0; i < 2; i++, f++) { - f->dentry = au_h_dptr(cpg->dentry, f->bindex); - f->file = au_h_open(cpg->dentry, f->bindex, f->flags, - /*file*/NULL, f->force_wr); - err = PTR_ERR(f->file); - if (IS_ERR(f->file)) - goto *f->label; - } - - /* try stopping to update while we copyup */ - IMustLock(d_inode(file[SRC].dentry)); - err = au_copy_file(file[DST].file, file[SRC].file, cpg->len); - - fput(file[DST].file); - au_sbr_put(sb, file[DST].bindex); - -out_src: - fput(file[SRC].file); - au_sbr_put(sb, file[SRC].bindex); -out: - return err; -} - -static int au_do_cpup_regular(struct au_cp_generic *cpg, - struct au_cpup_reg_attr *h_src_attr) -{ - int err, rerr; - loff_t l; - struct path h_path; - struct inode *h_src_inode, *h_dst_inode; - - err = 0; - h_src_inode = au_h_iptr(d_inode(cpg->dentry), cpg->bsrc); - l = i_size_read(h_src_inode); - if (cpg->len == -1 || l < cpg->len) - cpg->len = l; - if (cpg->len) { - /* try stopping to update while we are referencing */ - mutex_lock_nested(&h_src_inode->i_mutex, AuLsc_I_CHILD); - au_pin_hdir_unlock(cpg->pin); - - h_path.dentry = au_h_dptr(cpg->dentry, cpg->bsrc); - h_path.mnt = au_sbr_mnt(cpg->dentry->d_sb, cpg->bsrc); - h_src_attr->iflags = h_src_inode->i_flags; - if (!au_test_nfs(h_src_inode->i_sb)) - err = vfs_getattr(&h_path, &h_src_attr->st); - else { - mutex_unlock(&h_src_inode->i_mutex); - err = vfs_getattr(&h_path, &h_src_attr->st); - mutex_lock_nested(&h_src_inode->i_mutex, AuLsc_I_CHILD); - } - if (unlikely(err)) { - mutex_unlock(&h_src_inode->i_mutex); - goto out; - } - h_src_attr->valid = 1; - err = au_cp_regular(cpg); - mutex_unlock(&h_src_inode->i_mutex); - rerr = au_pin_hdir_relock(cpg->pin); - if (!err && rerr) - err = rerr; - } - if (!err && (h_src_inode->i_state & I_LINKABLE)) { - h_path.dentry = au_h_dptr(cpg->dentry, cpg->bdst); - h_dst_inode = d_inode(h_path.dentry); - spin_lock(&h_dst_inode->i_lock); - h_dst_inode->i_state |= I_LINKABLE; - spin_unlock(&h_dst_inode->i_lock); - } - -out: - return err; -} - -static int au_do_cpup_symlink(struct path *h_path, struct dentry *h_src, - struct inode *h_dir) -{ - int err, symlen; - mm_segment_t old_fs; - union { - char *k; - char __user *u; - } sym; - struct inode *h_inode = d_inode(h_src); - const struct inode_operations *h_iop = h_inode->i_op; - - err = -ENOSYS; - if (unlikely(!h_iop->readlink)) - goto out; - - err = -ENOMEM; - sym.k = (void *)__get_free_page(GFP_NOFS); - if (unlikely(!sym.k)) - goto out; - - /* unnecessary to support mmap_sem since symlink is not mmap-able */ - old_fs = get_fs(); - set_fs(KERNEL_DS); - symlen = h_iop->readlink(h_src, sym.u, PATH_MAX); - err = symlen; - set_fs(old_fs); - - if (symlen > 0) { - sym.k[symlen] = 0; - err = vfsub_symlink(h_dir, h_path, sym.k); - } - free_page((unsigned long)sym.k); - -out: - return err; -} - -static noinline_for_stack -int cpup_entry(struct au_cp_generic *cpg, struct dentry *dst_parent, - struct au_cpup_reg_attr *h_src_attr) -{ - int err; - umode_t mode; - unsigned int mnt_flags; - unsigned char isdir, isreg, force; - const unsigned char do_dt = !!au_ftest_cpup(cpg->flags, DTIME); - struct au_dtime dt; - struct path h_path; - struct dentry *h_src, *h_dst, *h_parent; - struct inode *h_inode, *h_dir, *dir, *inode; - struct super_block *sb; - - /* bsrc branch can be ro/rw. */ - h_src = au_h_dptr(cpg->dentry, cpg->bsrc); - h_inode = d_inode(h_src); - AuDebugOn(h_inode != au_h_iptr(d_inode(cpg->dentry), cpg->bsrc)); - - /* try stopping to be referenced while we are creating */ - h_dst = au_h_dptr(cpg->dentry, cpg->bdst); - if (au_ftest_cpup(cpg->flags, RENAME)) - AuDebugOn(strncmp(h_dst->d_name.name, AUFS_WH_PFX, - AUFS_WH_PFX_LEN)); - h_parent = h_dst->d_parent; /* dir inode is locked */ - h_dir = d_inode(h_parent); - IMustLock(h_dir); - AuDebugOn(h_parent != h_dst->d_parent); - - sb = cpg->dentry->d_sb; - h_path.mnt = au_sbr_mnt(sb, cpg->bdst); - if (do_dt) { - h_path.dentry = h_parent; - au_dtime_store(&dt, dst_parent, &h_path); - } - h_path.dentry = h_dst; - - isreg = 0; - isdir = 0; - mode = h_inode->i_mode; - switch (mode & S_IFMT) { - case S_IFREG: - isreg = 1; - err = vfsub_create(h_dir, &h_path, mode | S_IWUSR, - /*want_excl*/true); - if (!err) - err = au_do_cpup_regular(cpg, h_src_attr); - break; - case S_IFDIR: - isdir = 1; - err = vfsub_mkdir(h_dir, &h_path, mode); - if (!err) { - /* - * strange behaviour from the users view, - * particularry setattr case - */ - dir = d_inode(dst_parent); - if (au_ibstart(dir) == cpg->bdst) - au_cpup_attr_nlink(dir, /*force*/1); - inode = d_inode(cpg->dentry); - au_cpup_attr_nlink(inode, /*force*/1); - } - break; - case S_IFLNK: - err = au_do_cpup_symlink(&h_path, h_src, h_dir); - break; - case S_IFCHR: - case S_IFBLK: - AuDebugOn(!capable(CAP_MKNOD)); - /*FALLTHROUGH*/ - case S_IFIFO: - case S_IFSOCK: - err = vfsub_mknod(h_dir, &h_path, mode, h_inode->i_rdev); - break; - default: - AuIOErr("Unknown inode type 0%o\n", mode); - err = -EIO; - } - - mnt_flags = au_mntflags(sb); - if (!au_opt_test(mnt_flags, UDBA_NONE) - && !isdir - && au_opt_test(mnt_flags, XINO) - && (h_inode->i_nlink == 1 - || (h_inode->i_state & I_LINKABLE)) - /* todo: unnecessary? */ - /* && d_inode(cpg->dentry)->i_nlink == 1 */ - && cpg->bdst < cpg->bsrc - && !au_ftest_cpup(cpg->flags, KEEPLINO)) - au_xino_write(sb, cpg->bsrc, h_inode->i_ino, /*ino*/0); - /* ignore this error */ - - if (!err) { - force = 0; - if (isreg) { - force = !!cpg->len; - if (cpg->len == -1) - force = !!i_size_read(h_inode); - } - au_fhsm_wrote(sb, cpg->bdst, force); - } - - if (do_dt) - au_dtime_revert(&dt); - return err; -} - -static int au_do_ren_after_cpup(struct au_cp_generic *cpg, struct path *h_path) -{ - int err; - struct dentry *dentry, *h_dentry, *h_parent, *parent; - struct inode *h_dir; - aufs_bindex_t bdst; - - dentry = cpg->dentry; - bdst = cpg->bdst; - h_dentry = au_h_dptr(dentry, bdst); - if (!au_ftest_cpup(cpg->flags, OVERWRITE)) { - dget(h_dentry); - au_set_h_dptr(dentry, bdst, NULL); - err = au_lkup_neg(dentry, bdst, /*wh*/0); - if (!err) - h_path->dentry = dget(au_h_dptr(dentry, bdst)); - au_set_h_dptr(dentry, bdst, h_dentry); - } else { - err = 0; - parent = dget_parent(dentry); - h_parent = au_h_dptr(parent, bdst); - dput(parent); - h_path->dentry = vfsub_lkup_one(&dentry->d_name, h_parent); - if (IS_ERR(h_path->dentry)) - err = PTR_ERR(h_path->dentry); - } - if (unlikely(err)) - goto out; - - h_parent = h_dentry->d_parent; /* dir inode is locked */ - h_dir = d_inode(h_parent); - IMustLock(h_dir); - AuDbg("%pd %pd\n", h_dentry, h_path->dentry); - /* no delegation since it is just created */ - err = vfsub_rename(h_dir, h_dentry, h_dir, h_path, /*delegated*/NULL); - dput(h_path->dentry); - -out: - return err; -} - -/* - * copyup the @dentry from @bsrc to @bdst. - * the caller must set the both of lower dentries. - * @len is for truncating when it is -1 copyup the entire file. - * in link/rename cases, @dst_parent may be different from the real one. - * basic->bsrc can be larger than basic->bdst. - */ -static int au_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) -{ - int err, rerr; - aufs_bindex_t old_ibstart; - unsigned char isdir, plink; - struct dentry *h_src, *h_dst, *h_parent; - struct inode *dst_inode, *h_dir, *inode, *delegated, *src_inode; - struct super_block *sb; - struct au_branch *br; - /* to reuduce stack size */ - struct { - struct au_dtime dt; - struct path h_path; - struct au_cpup_reg_attr h_src_attr; - } *a; - - err = -ENOMEM; - a = kmalloc(sizeof(*a), GFP_NOFS); - if (unlikely(!a)) - goto out; - a->h_src_attr.valid = 0; - - sb = cpg->dentry->d_sb; - br = au_sbr(sb, cpg->bdst); - a->h_path.mnt = au_br_mnt(br); - h_dst = au_h_dptr(cpg->dentry, cpg->bdst); - h_parent = h_dst->d_parent; /* dir inode is locked */ - h_dir = d_inode(h_parent); - IMustLock(h_dir); - - h_src = au_h_dptr(cpg->dentry, cpg->bsrc); - inode = d_inode(cpg->dentry); - - if (!dst_parent) - dst_parent = dget_parent(cpg->dentry); - else - dget(dst_parent); - - plink = !!au_opt_test(au_mntflags(sb), PLINK); - dst_inode = au_h_iptr(inode, cpg->bdst); - if (dst_inode) { - if (unlikely(!plink)) { - err = -EIO; - AuIOErr("hi%lu(i%lu) exists on b%d " - "but plink is disabled\n", - dst_inode->i_ino, inode->i_ino, cpg->bdst); - goto out_parent; - } - - if (dst_inode->i_nlink) { - const int do_dt = au_ftest_cpup(cpg->flags, DTIME); - - h_src = au_plink_lkup(inode, cpg->bdst); - err = PTR_ERR(h_src); - if (IS_ERR(h_src)) - goto out_parent; - if (unlikely(d_is_negative(h_src))) { - err = -EIO; - AuIOErr("i%lu exists on a upper branch " - "but not pseudo-linked\n", - inode->i_ino); - dput(h_src); - goto out_parent; - } - - if (do_dt) { - a->h_path.dentry = h_parent; - au_dtime_store(&a->dt, dst_parent, &a->h_path); - } - - a->h_path.dentry = h_dst; - delegated = NULL; - err = vfsub_link(h_src, h_dir, &a->h_path, &delegated); - if (!err && au_ftest_cpup(cpg->flags, RENAME)) - err = au_do_ren_after_cpup(cpg, &a->h_path); - if (do_dt) - au_dtime_revert(&a->dt); - if (unlikely(err == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal link\n"); - iput(delegated); - } - dput(h_src); - goto out_parent; - } else - /* todo: cpup_wh_file? */ - /* udba work */ - au_update_ibrange(inode, /*do_put_zero*/1); - } - - isdir = S_ISDIR(inode->i_mode); - old_ibstart = au_ibstart(inode); - err = cpup_entry(cpg, dst_parent, &a->h_src_attr); - if (unlikely(err)) - goto out_rev; - dst_inode = d_inode(h_dst); - mutex_lock_nested(&dst_inode->i_mutex, AuLsc_I_CHILD2); - /* todo: necessary? */ - /* au_pin_hdir_unlock(cpg->pin); */ - - err = cpup_iattr(cpg->dentry, cpg->bdst, h_src, &a->h_src_attr); - if (unlikely(err)) { - /* todo: necessary? */ - /* au_pin_hdir_relock(cpg->pin); */ /* ignore an error */ - mutex_unlock(&dst_inode->i_mutex); - goto out_rev; - } - - if (cpg->bdst < old_ibstart) { - if (S_ISREG(inode->i_mode)) { - err = au_dy_iaop(inode, cpg->bdst, dst_inode); - if (unlikely(err)) { - /* ignore an error */ - /* au_pin_hdir_relock(cpg->pin); */ - mutex_unlock(&dst_inode->i_mutex); - goto out_rev; - } - } - au_set_ibstart(inode, cpg->bdst); - } else - au_set_ibend(inode, cpg->bdst); - au_set_h_iptr(inode, cpg->bdst, au_igrab(dst_inode), - au_hi_flags(inode, isdir)); - - /* todo: necessary? */ - /* err = au_pin_hdir_relock(cpg->pin); */ - mutex_unlock(&dst_inode->i_mutex); - if (unlikely(err)) - goto out_rev; - - src_inode = d_inode(h_src); - if (!isdir - && (src_inode->i_nlink > 1 - || src_inode->i_state & I_LINKABLE) - && plink) - au_plink_append(inode, cpg->bdst, h_dst); - - if (au_ftest_cpup(cpg->flags, RENAME)) { - a->h_path.dentry = h_dst; - err = au_do_ren_after_cpup(cpg, &a->h_path); - } - if (!err) - goto out_parent; /* success */ - - /* revert */ -out_rev: - a->h_path.dentry = h_parent; - au_dtime_store(&a->dt, dst_parent, &a->h_path); - a->h_path.dentry = h_dst; - rerr = 0; - if (d_is_positive(h_dst)) { - if (!isdir) { - /* no delegation since it is just created */ - rerr = vfsub_unlink(h_dir, &a->h_path, - /*delegated*/NULL, /*force*/0); - } else - rerr = vfsub_rmdir(h_dir, &a->h_path); - } - au_dtime_revert(&a->dt); - if (rerr) { - AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr); - err = -EIO; - } -out_parent: - dput(dst_parent); - kfree(a); -out: - return err; -} - -#if 0 /* reserved */ -struct au_cpup_single_args { - int *errp; - struct au_cp_generic *cpg; - struct dentry *dst_parent; -}; - -static void au_call_cpup_single(void *args) -{ - struct au_cpup_single_args *a = args; - - au_pin_hdir_acquire_nest(a->cpg->pin); - *a->errp = au_cpup_single(a->cpg, a->dst_parent); - au_pin_hdir_release(a->cpg->pin); -} -#endif - -/* - * prevent SIGXFSZ in copy-up. - * testing CAP_MKNOD is for generic fs, - * but CAP_FSETID is for xfs only, currently. - */ -static int au_cpup_sio_test(struct au_pin *pin, umode_t mode) -{ - int do_sio; - struct super_block *sb; - struct inode *h_dir; - - do_sio = 0; - sb = au_pinned_parent(pin)->d_sb; - if (!au_wkq_test() - && (!au_sbi(sb)->si_plink_maint_pid - || au_plink_maint(sb, AuLock_NOPLM))) { - switch (mode & S_IFMT) { - case S_IFREG: - /* no condition about RLIMIT_FSIZE and the file size */ - do_sio = 1; - break; - case S_IFCHR: - case S_IFBLK: - do_sio = !capable(CAP_MKNOD); - break; - } - if (!do_sio) - do_sio = ((mode & (S_ISUID | S_ISGID)) - && !capable(CAP_FSETID)); - /* this workaround may be removed in the future */ - if (!do_sio) { - h_dir = au_pinned_h_dir(pin); - do_sio = h_dir->i_mode & S_ISVTX; - } - } - - return do_sio; -} - -#if 0 /* reserved */ -int au_sio_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) -{ - int err, wkq_err; - struct dentry *h_dentry; - - h_dentry = au_h_dptr(cpg->dentry, cpg->bsrc); - if (!au_cpup_sio_test(pin, d_inode(h_dentry)->i_mode)) - err = au_cpup_single(cpg, dst_parent); - else { - struct au_cpup_single_args args = { - .errp = &err, - .cpg = cpg, - .dst_parent = dst_parent - }; - wkq_err = au_wkq_wait(au_call_cpup_single, &args); - if (unlikely(wkq_err)) - err = wkq_err; - } - - return err; -} -#endif - -/* - * copyup the @dentry from the first active lower branch to @bdst, - * using au_cpup_single(). - */ -static int au_cpup_simple(struct au_cp_generic *cpg) -{ - int err; - unsigned int flags_orig; - struct dentry *dentry; - - AuDebugOn(cpg->bsrc < 0); - - dentry = cpg->dentry; - DiMustWriteLock(dentry); - - err = au_lkup_neg(dentry, cpg->bdst, /*wh*/1); - if (!err) { - flags_orig = cpg->flags; - au_fset_cpup(cpg->flags, RENAME); - err = au_cpup_single(cpg, NULL); - cpg->flags = flags_orig; - if (!err) - return 0; /* success */ - - /* revert */ - au_set_h_dptr(dentry, cpg->bdst, NULL); - au_set_dbstart(dentry, cpg->bsrc); - } - - return err; -} - -struct au_cpup_simple_args { - int *errp; - struct au_cp_generic *cpg; -}; - -static void au_call_cpup_simple(void *args) -{ - struct au_cpup_simple_args *a = args; - - au_pin_hdir_acquire_nest(a->cpg->pin); - *a->errp = au_cpup_simple(a->cpg); - au_pin_hdir_release(a->cpg->pin); -} - -static int au_do_sio_cpup_simple(struct au_cp_generic *cpg) -{ - int err, wkq_err; - struct dentry *dentry, *parent; - struct file *h_file; - struct inode *h_dir; - - dentry = cpg->dentry; - h_file = NULL; - if (au_ftest_cpup(cpg->flags, HOPEN)) { - AuDebugOn(cpg->bsrc < 0); - h_file = au_h_open_pre(dentry, cpg->bsrc, /*force_wr*/0); - err = PTR_ERR(h_file); - if (IS_ERR(h_file)) - goto out; - } - - parent = dget_parent(dentry); - h_dir = au_h_iptr(d_inode(parent), cpg->bdst); - if (!au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE) - && !au_cpup_sio_test(cpg->pin, d_inode(dentry)->i_mode)) - err = au_cpup_simple(cpg); - else { - struct au_cpup_simple_args args = { - .errp = &err, - .cpg = cpg - }; - wkq_err = au_wkq_wait(au_call_cpup_simple, &args); - if (unlikely(wkq_err)) - err = wkq_err; - } - - dput(parent); - if (h_file) - au_h_open_post(dentry, cpg->bsrc, h_file); - -out: - return err; -} - -int au_sio_cpup_simple(struct au_cp_generic *cpg) -{ - aufs_bindex_t bsrc, bend; - struct dentry *dentry, *h_dentry; - - if (cpg->bsrc < 0) { - dentry = cpg->dentry; - bend = au_dbend(dentry); - for (bsrc = cpg->bdst + 1; bsrc <= bend; bsrc++) { - h_dentry = au_h_dptr(dentry, bsrc); - if (h_dentry) { - AuDebugOn(d_is_negative(h_dentry)); - break; - } - } - AuDebugOn(bsrc > bend); - cpg->bsrc = bsrc; - } - AuDebugOn(cpg->bsrc <= cpg->bdst); - return au_do_sio_cpup_simple(cpg); -} - -int au_sio_cpdown_simple(struct au_cp_generic *cpg) -{ - AuDebugOn(cpg->bdst <= cpg->bsrc); - return au_do_sio_cpup_simple(cpg); -} - -/* ---------------------------------------------------------------------- */ - -/* - * copyup the deleted file for writing. - */ -static int au_do_cpup_wh(struct au_cp_generic *cpg, struct dentry *wh_dentry, - struct file *file) -{ - int err; - unsigned int flags_orig; - aufs_bindex_t bsrc_orig; - struct dentry *h_d_dst, *h_d_start; - struct au_dinfo *dinfo; - struct au_hdentry *hdp; - - dinfo = au_di(cpg->dentry); - AuRwMustWriteLock(&dinfo->di_rwsem); - - bsrc_orig = cpg->bsrc; - cpg->bsrc = dinfo->di_bstart; - hdp = dinfo->di_hdentry; - h_d_dst = hdp[0 + cpg->bdst].hd_dentry; - dinfo->di_bstart = cpg->bdst; - hdp[0 + cpg->bdst].hd_dentry = wh_dentry; - h_d_start = NULL; - if (file) { - h_d_start = hdp[0 + cpg->bsrc].hd_dentry; - hdp[0 + cpg->bsrc].hd_dentry = au_hf_top(file)->f_path.dentry; - } - flags_orig = cpg->flags; - cpg->flags = !AuCpup_DTIME; - err = au_cpup_single(cpg, /*h_parent*/NULL); - cpg->flags = flags_orig; - if (file) { - if (!err) - err = au_reopen_nondir(file); - hdp[0 + cpg->bsrc].hd_dentry = h_d_start; - } - hdp[0 + cpg->bdst].hd_dentry = h_d_dst; - dinfo->di_bstart = cpg->bsrc; - cpg->bsrc = bsrc_orig; - - return err; -} - -static int au_cpup_wh(struct au_cp_generic *cpg, struct file *file) -{ - int err; - aufs_bindex_t bdst; - struct au_dtime dt; - struct dentry *dentry, *parent, *h_parent, *wh_dentry; - struct au_branch *br; - struct path h_path; - - dentry = cpg->dentry; - bdst = cpg->bdst; - br = au_sbr(dentry->d_sb, bdst); - parent = dget_parent(dentry); - h_parent = au_h_dptr(parent, bdst); - wh_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); - err = PTR_ERR(wh_dentry); - if (IS_ERR(wh_dentry)) - goto out; - - h_path.dentry = h_parent; - h_path.mnt = au_br_mnt(br); - au_dtime_store(&dt, parent, &h_path); - err = au_do_cpup_wh(cpg, wh_dentry, file); - if (unlikely(err)) - goto out_wh; - - dget(wh_dentry); - h_path.dentry = wh_dentry; - if (!d_is_dir(wh_dentry)) { - /* no delegation since it is just created */ - err = vfsub_unlink(d_inode(h_parent), &h_path, - /*delegated*/NULL, /*force*/0); - } else - err = vfsub_rmdir(d_inode(h_parent), &h_path); - if (unlikely(err)) { - AuIOErr("failed remove copied-up tmp file %pd(%d)\n", - wh_dentry, err); - err = -EIO; - } - au_dtime_revert(&dt); - au_set_hi_wh(d_inode(dentry), bdst, wh_dentry); - -out_wh: - dput(wh_dentry); -out: - dput(parent); - return err; -} - -struct au_cpup_wh_args { - int *errp; - struct au_cp_generic *cpg; - struct file *file; -}; - -static void au_call_cpup_wh(void *args) -{ - struct au_cpup_wh_args *a = args; - - au_pin_hdir_acquire_nest(a->cpg->pin); - *a->errp = au_cpup_wh(a->cpg, a->file); - au_pin_hdir_release(a->cpg->pin); -} - -int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file) -{ - int err, wkq_err; - aufs_bindex_t bdst; - struct dentry *dentry, *parent, *h_orph, *h_parent; - struct inode *dir, *h_dir, *h_tmpdir; - struct au_wbr *wbr; - struct au_pin wh_pin, *pin_orig; - - dentry = cpg->dentry; - bdst = cpg->bdst; - parent = dget_parent(dentry); - dir = d_inode(parent); - h_orph = NULL; - h_parent = NULL; - h_dir = au_igrab(au_h_iptr(dir, bdst)); - h_tmpdir = h_dir; - pin_orig = NULL; - if (!h_dir->i_nlink) { - wbr = au_sbr(dentry->d_sb, bdst)->br_wbr; - h_orph = wbr->wbr_orph; - - h_parent = dget(au_h_dptr(parent, bdst)); - au_set_h_dptr(parent, bdst, dget(h_orph)); - h_tmpdir = d_inode(h_orph); - au_set_h_iptr(dir, bdst, au_igrab(h_tmpdir), /*flags*/0); - - mutex_lock_nested(&h_tmpdir->i_mutex, AuLsc_I_PARENT3); - /* todo: au_h_open_pre()? */ - - pin_orig = cpg->pin; - au_pin_init(&wh_pin, dentry, bdst, AuLsc_DI_PARENT, - AuLsc_I_PARENT3, cpg->pin->udba, AuPin_DI_LOCKED); - cpg->pin = &wh_pin; - } - - if (!au_test_h_perm_sio(h_tmpdir, MAY_EXEC | MAY_WRITE) - && !au_cpup_sio_test(cpg->pin, d_inode(dentry)->i_mode)) - err = au_cpup_wh(cpg, file); - else { - struct au_cpup_wh_args args = { - .errp = &err, - .cpg = cpg, - .file = file - }; - wkq_err = au_wkq_wait(au_call_cpup_wh, &args); - if (unlikely(wkq_err)) - err = wkq_err; - } - - if (h_orph) { - mutex_unlock(&h_tmpdir->i_mutex); - /* todo: au_h_open_post()? */ - au_set_h_iptr(dir, bdst, au_igrab(h_dir), /*flags*/0); - au_set_h_dptr(parent, bdst, h_parent); - AuDebugOn(!pin_orig); - cpg->pin = pin_orig; - } - iput(h_dir); - dput(parent); - - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* - * generic routine for both of copy-up and copy-down. - */ -/* cf. revalidate function in file.c */ -int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, - int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, - struct au_pin *pin, - struct dentry *h_parent, void *arg), - void *arg) -{ - int err; - struct au_pin pin; - struct dentry *d, *parent, *h_parent, *real_parent, *h_dentry; - - err = 0; - parent = dget_parent(dentry); - if (IS_ROOT(parent)) - goto out; - - au_pin_init(&pin, dentry, bdst, AuLsc_DI_PARENT2, AuLsc_I_PARENT2, - au_opt_udba(dentry->d_sb), AuPin_MNT_WRITE); - - /* do not use au_dpage */ - real_parent = parent; - while (1) { - dput(parent); - parent = dget_parent(dentry); - h_parent = au_h_dptr(parent, bdst); - if (h_parent) - goto out; /* success */ - - /* find top dir which is necessary to cpup */ - do { - d = parent; - dput(parent); - parent = dget_parent(d); - di_read_lock_parent3(parent, !AuLock_IR); - h_parent = au_h_dptr(parent, bdst); - di_read_unlock(parent, !AuLock_IR); - } while (!h_parent); - - if (d != real_parent) - di_write_lock_child3(d); - - /* somebody else might create while we were sleeping */ - h_dentry = au_h_dptr(d, bdst); - if (!h_dentry || d_is_negative(h_dentry)) { - if (h_dentry) - au_update_dbstart(d); - - au_pin_set_dentry(&pin, d); - err = au_do_pin(&pin); - if (!err) { - err = cp(d, bdst, &pin, h_parent, arg); - au_unpin(&pin); - } - } - - if (d != real_parent) - di_write_unlock(d); - if (unlikely(err)) - break; - } - -out: - dput(parent); - return err; -} - -static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst, - struct au_pin *pin, - struct dentry *h_parent __maybe_unused, - void *arg __maybe_unused) -{ - struct au_cp_generic cpg = { - .dentry = dentry, - .bdst = bdst, - .bsrc = -1, - .len = 0, - .pin = pin, - .flags = AuCpup_DTIME - }; - return au_sio_cpup_simple(&cpg); -} - -int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) -{ - return au_cp_dirs(dentry, bdst, au_cpup_dir, NULL); -} - -int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) -{ - int err; - struct dentry *parent; - struct inode *dir; - - parent = dget_parent(dentry); - dir = d_inode(parent); - err = 0; - if (au_h_iptr(dir, bdst)) - goto out; - - di_read_unlock(parent, AuLock_IR); - di_write_lock_parent(parent); - /* someone else might change our inode while we were sleeping */ - if (!au_h_iptr(dir, bdst)) - err = au_cpup_dirs(dentry, bdst); - di_downgrade_lock(parent, AuLock_IR); - -out: - dput(parent); - return err; -} diff --git a/fs/aufs/cpup.h b/fs/aufs/cpup.h deleted file mode 100644 index a5da7dbe9..000000000 --- a/fs/aufs/cpup.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * copy-up/down functions - */ - -#ifndef __AUFS_CPUP_H__ -#define __AUFS_CPUP_H__ - -#ifdef __KERNEL__ - -#include <linux/path.h> - -struct inode; -struct file; -struct au_pin; - -void au_cpup_attr_flags(struct inode *dst, unsigned int iflags); -void au_cpup_attr_timesizes(struct inode *inode); -void au_cpup_attr_nlink(struct inode *inode, int force); -void au_cpup_attr_changeable(struct inode *inode); -void au_cpup_igen(struct inode *inode, struct inode *h_inode); -void au_cpup_attr_all(struct inode *inode, int force); - -/* ---------------------------------------------------------------------- */ - -struct au_cp_generic { - struct dentry *dentry; - aufs_bindex_t bdst, bsrc; - loff_t len; - struct au_pin *pin; - unsigned int flags; -}; - -/* cpup flags */ -#define AuCpup_DTIME 1 /* do dtime_store/revert */ -#define AuCpup_KEEPLINO (1 << 1) /* do not clear the lower xino, - for link(2) */ -#define AuCpup_RENAME (1 << 2) /* rename after cpup */ -#define AuCpup_HOPEN (1 << 3) /* call h_open_pre/post() in - cpup */ -#define AuCpup_OVERWRITE (1 << 4) /* allow overwriting the - existing entry */ -#define AuCpup_RWDST (1 << 5) /* force write target even if - the branch is marked as RO */ - -#define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name) -#define au_fset_cpup(flags, name) \ - do { (flags) |= AuCpup_##name; } while (0) -#define au_fclr_cpup(flags, name) \ - do { (flags) &= ~AuCpup_##name; } while (0) - -int au_copy_file(struct file *dst, struct file *src, loff_t len); -int au_sio_cpup_simple(struct au_cp_generic *cpg); -int au_sio_cpdown_simple(struct au_cp_generic *cpg); -int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file); - -int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, - int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, - struct au_pin *pin, - struct dentry *h_parent, void *arg), - void *arg); -int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); -int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); - -/* ---------------------------------------------------------------------- */ - -/* keep timestamps when copyup */ -struct au_dtime { - struct dentry *dt_dentry; - struct path dt_h_path; - struct timespec dt_atime, dt_mtime; -}; -void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, - struct path *h_path); -void au_dtime_revert(struct au_dtime *dt); - -#endif /* __KERNEL__ */ -#endif /* __AUFS_CPUP_H__ */ diff --git a/fs/aufs/dbgaufs.c b/fs/aufs/dbgaufs.c deleted file mode 100644 index df4c65635..000000000 --- a/fs/aufs/dbgaufs.c +++ /dev/null @@ -1,432 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * debugfs interface - */ - -#include <linux/debugfs.h> -#include "aufs.h" - -#ifndef CONFIG_SYSFS -#error DEBUG_FS depends upon SYSFS -#endif - -static struct dentry *dbgaufs; -static const mode_t dbgaufs_mode = S_IRUSR | S_IRGRP | S_IROTH; - -/* 20 is max digits length of ulong 64 */ -struct dbgaufs_arg { - int n; - char a[20 * 4]; -}; - -/* - * common function for all XINO files - */ -static int dbgaufs_xi_release(struct inode *inode __maybe_unused, - struct file *file) -{ - kfree(file->private_data); - return 0; -} - -static int dbgaufs_xi_open(struct file *xf, struct file *file, int do_fcnt) -{ - int err; - struct kstat st; - struct dbgaufs_arg *p; - - err = -ENOMEM; - p = kmalloc(sizeof(*p), GFP_NOFS); - if (unlikely(!p)) - goto out; - - err = 0; - p->n = 0; - file->private_data = p; - if (!xf) - goto out; - - err = vfs_getattr(&xf->f_path, &st); - if (!err) { - if (do_fcnt) - p->n = snprintf - (p->a, sizeof(p->a), "%ld, %llux%lu %lld\n", - (long)file_count(xf), st.blocks, st.blksize, - (long long)st.size); - else - p->n = snprintf(p->a, sizeof(p->a), "%llux%lu %lld\n", - st.blocks, st.blksize, - (long long)st.size); - AuDebugOn(p->n >= sizeof(p->a)); - } else { - p->n = snprintf(p->a, sizeof(p->a), "err %d\n", err); - err = 0; - } - -out: - return err; - -} - -static ssize_t dbgaufs_xi_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct dbgaufs_arg *p; - - p = file->private_data; - return simple_read_from_buffer(buf, count, ppos, p->a, p->n); -} - -/* ---------------------------------------------------------------------- */ - -struct dbgaufs_plink_arg { - int n; - char a[]; -}; - -static int dbgaufs_plink_release(struct inode *inode __maybe_unused, - struct file *file) -{ - free_page((unsigned long)file->private_data); - return 0; -} - -static int dbgaufs_plink_open(struct inode *inode, struct file *file) -{ - int err, i, limit; - unsigned long n, sum; - struct dbgaufs_plink_arg *p; - struct au_sbinfo *sbinfo; - struct super_block *sb; - struct au_sphlhead *sphl; - - err = -ENOMEM; - p = (void *)get_zeroed_page(GFP_NOFS); - if (unlikely(!p)) - goto out; - - err = -EFBIG; - sbinfo = inode->i_private; - sb = sbinfo->si_sb; - si_noflush_read_lock(sb); - if (au_opt_test(au_mntflags(sb), PLINK)) { - limit = PAGE_SIZE - sizeof(p->n); - - /* the number of buckets */ - n = snprintf(p->a + p->n, limit, "%d\n", AuPlink_NHASH); - p->n += n; - limit -= n; - - sum = 0; - for (i = 0, sphl = sbinfo->si_plink; - i < AuPlink_NHASH; - i++, sphl++) { - n = au_sphl_count(sphl); - sum += n; - - n = snprintf(p->a + p->n, limit, "%lu ", n); - p->n += n; - limit -= n; - if (unlikely(limit <= 0)) - goto out_free; - } - p->a[p->n - 1] = '\n'; - - /* the sum of plinks */ - n = snprintf(p->a + p->n, limit, "%lu\n", sum); - p->n += n; - limit -= n; - if (unlikely(limit <= 0)) - goto out_free; - } else { -#define str "1\n0\n0\n" - p->n = sizeof(str) - 1; - strcpy(p->a, str); -#undef str - } - si_read_unlock(sb); - - err = 0; - file->private_data = p; - goto out; /* success */ - -out_free: - free_page((unsigned long)p); -out: - return err; -} - -static ssize_t dbgaufs_plink_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct dbgaufs_plink_arg *p; - - p = file->private_data; - return simple_read_from_buffer(buf, count, ppos, p->a, p->n); -} - -static const struct file_operations dbgaufs_plink_fop = { - .owner = THIS_MODULE, - .open = dbgaufs_plink_open, - .release = dbgaufs_plink_release, - .read = dbgaufs_plink_read -}; - -/* ---------------------------------------------------------------------- */ - -static int dbgaufs_xib_open(struct inode *inode, struct file *file) -{ - int err; - struct au_sbinfo *sbinfo; - struct super_block *sb; - - sbinfo = inode->i_private; - sb = sbinfo->si_sb; - si_noflush_read_lock(sb); - err = dbgaufs_xi_open(sbinfo->si_xib, file, /*do_fcnt*/0); - si_read_unlock(sb); - return err; -} - -static const struct file_operations dbgaufs_xib_fop = { - .owner = THIS_MODULE, - .open = dbgaufs_xib_open, - .release = dbgaufs_xi_release, - .read = dbgaufs_xi_read -}; - -/* ---------------------------------------------------------------------- */ - -#define DbgaufsXi_PREFIX "xi" - -static int dbgaufs_xino_open(struct inode *inode, struct file *file) -{ - int err; - long l; - struct au_sbinfo *sbinfo; - struct super_block *sb; - struct file *xf; - struct qstr *name; - - err = -ENOENT; - xf = NULL; - name = &file->f_path.dentry->d_name; - if (unlikely(name->len < sizeof(DbgaufsXi_PREFIX) - || memcmp(name->name, DbgaufsXi_PREFIX, - sizeof(DbgaufsXi_PREFIX) - 1))) - goto out; - err = kstrtol(name->name + sizeof(DbgaufsXi_PREFIX) - 1, 10, &l); - if (unlikely(err)) - goto out; - - sbinfo = inode->i_private; - sb = sbinfo->si_sb; - si_noflush_read_lock(sb); - if (l <= au_sbend(sb)) { - xf = au_sbr(sb, (aufs_bindex_t)l)->br_xino.xi_file; - err = dbgaufs_xi_open(xf, file, /*do_fcnt*/1); - } else - err = -ENOENT; - si_read_unlock(sb); - -out: - return err; -} - -static const struct file_operations dbgaufs_xino_fop = { - .owner = THIS_MODULE, - .open = dbgaufs_xino_open, - .release = dbgaufs_xi_release, - .read = dbgaufs_xi_read -}; - -void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) -{ - aufs_bindex_t bend; - struct au_branch *br; - struct au_xino_file *xi; - - if (!au_sbi(sb)->si_dbgaufs) - return; - - bend = au_sbend(sb); - for (; bindex <= bend; bindex++) { - br = au_sbr(sb, bindex); - xi = &br->br_xino; - debugfs_remove(xi->xi_dbgaufs); - xi->xi_dbgaufs = NULL; - } -} - -void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) -{ - struct au_sbinfo *sbinfo; - struct dentry *parent; - struct au_branch *br; - struct au_xino_file *xi; - aufs_bindex_t bend; - char name[sizeof(DbgaufsXi_PREFIX) + 5]; /* "xi" bindex NULL */ - - sbinfo = au_sbi(sb); - parent = sbinfo->si_dbgaufs; - if (!parent) - return; - - bend = au_sbend(sb); - for (; bindex <= bend; bindex++) { - snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d", bindex); - br = au_sbr(sb, bindex); - xi = &br->br_xino; - AuDebugOn(xi->xi_dbgaufs); - xi->xi_dbgaufs = debugfs_create_file(name, dbgaufs_mode, parent, - sbinfo, &dbgaufs_xino_fop); - /* ignore an error */ - if (unlikely(!xi->xi_dbgaufs)) - AuWarn1("failed %s under debugfs\n", name); - } -} - -/* ---------------------------------------------------------------------- */ - -#ifdef CONFIG_AUFS_EXPORT -static int dbgaufs_xigen_open(struct inode *inode, struct file *file) -{ - int err; - struct au_sbinfo *sbinfo; - struct super_block *sb; - - sbinfo = inode->i_private; - sb = sbinfo->si_sb; - si_noflush_read_lock(sb); - err = dbgaufs_xi_open(sbinfo->si_xigen, file, /*do_fcnt*/0); - si_read_unlock(sb); - return err; -} - -static const struct file_operations dbgaufs_xigen_fop = { - .owner = THIS_MODULE, - .open = dbgaufs_xigen_open, - .release = dbgaufs_xi_release, - .read = dbgaufs_xi_read -}; - -static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) -{ - int err; - - /* - * This function is a dynamic '__init' function actually, - * so the tiny check for si_rwsem is unnecessary. - */ - /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ - - err = -EIO; - sbinfo->si_dbgaufs_xigen = debugfs_create_file - ("xigen", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, - &dbgaufs_xigen_fop); - if (sbinfo->si_dbgaufs_xigen) - err = 0; - - return err; -} -#else -static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) -{ - return 0; -} -#endif /* CONFIG_AUFS_EXPORT */ - -/* ---------------------------------------------------------------------- */ - -void dbgaufs_si_fin(struct au_sbinfo *sbinfo) -{ - /* - * This function is a dynamic '__fin' function actually, - * so the tiny check for si_rwsem is unnecessary. - */ - /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ - - debugfs_remove_recursive(sbinfo->si_dbgaufs); - sbinfo->si_dbgaufs = NULL; - kobject_put(&sbinfo->si_kobj); -} - -int dbgaufs_si_init(struct au_sbinfo *sbinfo) -{ - int err; - char name[SysaufsSiNameLen]; - - /* - * This function is a dynamic '__init' function actually, - * so the tiny check for si_rwsem is unnecessary. - */ - /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ - - err = -ENOENT; - if (!dbgaufs) { - AuErr1("/debug/aufs is uninitialized\n"); - goto out; - } - - err = -EIO; - sysaufs_name(sbinfo, name); - sbinfo->si_dbgaufs = debugfs_create_dir(name, dbgaufs); - if (unlikely(!sbinfo->si_dbgaufs)) - goto out; - kobject_get(&sbinfo->si_kobj); - - sbinfo->si_dbgaufs_xib = debugfs_create_file - ("xib", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, - &dbgaufs_xib_fop); - if (unlikely(!sbinfo->si_dbgaufs_xib)) - goto out_dir; - - sbinfo->si_dbgaufs_plink = debugfs_create_file - ("plink", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, - &dbgaufs_plink_fop); - if (unlikely(!sbinfo->si_dbgaufs_plink)) - goto out_dir; - - err = dbgaufs_xigen_init(sbinfo); - if (!err) - goto out; /* success */ - -out_dir: - dbgaufs_si_fin(sbinfo); -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -void dbgaufs_fin(void) -{ - debugfs_remove(dbgaufs); -} - -int __init dbgaufs_init(void) -{ - int err; - - err = -EIO; - dbgaufs = debugfs_create_dir(AUFS_NAME, NULL); - if (dbgaufs) - err = 0; - return err; -} diff --git a/fs/aufs/dbgaufs.h b/fs/aufs/dbgaufs.h deleted file mode 100644 index 03d47b80b..000000000 --- a/fs/aufs/dbgaufs.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * debugfs interface - */ - -#ifndef __DBGAUFS_H__ -#define __DBGAUFS_H__ - -#ifdef __KERNEL__ - -struct super_block; -struct au_sbinfo; - -#ifdef CONFIG_DEBUG_FS -/* dbgaufs.c */ -void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); -void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); -void dbgaufs_si_fin(struct au_sbinfo *sbinfo); -int dbgaufs_si_init(struct au_sbinfo *sbinfo); -void dbgaufs_fin(void); -int __init dbgaufs_init(void); -#else -AuStubVoid(dbgaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex) -AuStubVoid(dbgaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex) -AuStubVoid(dbgaufs_si_fin, struct au_sbinfo *sbinfo) -AuStubInt0(dbgaufs_si_init, struct au_sbinfo *sbinfo) -AuStubVoid(dbgaufs_fin, void) -AuStubInt0(__init dbgaufs_init, void) -#endif /* CONFIG_DEBUG_FS */ - -#endif /* __KERNEL__ */ -#endif /* __DBGAUFS_H__ */ diff --git a/fs/aufs/dcsub.c b/fs/aufs/dcsub.c deleted file mode 100644 index 910a2e870..000000000 --- a/fs/aufs/dcsub.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * sub-routines for dentry cache - */ - -#include "aufs.h" - -static void au_dpage_free(struct au_dpage *dpage) -{ - int i; - struct dentry **p; - - p = dpage->dentries; - for (i = 0; i < dpage->ndentry; i++) - dput(*p++); - free_page((unsigned long)dpage->dentries); -} - -int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp) -{ - int err; - void *p; - - err = -ENOMEM; - dpages->dpages = kmalloc(sizeof(*dpages->dpages), gfp); - if (unlikely(!dpages->dpages)) - goto out; - - p = (void *)__get_free_page(gfp); - if (unlikely(!p)) - goto out_dpages; - - dpages->dpages[0].ndentry = 0; - dpages->dpages[0].dentries = p; - dpages->ndpage = 1; - return 0; /* success */ - -out_dpages: - kfree(dpages->dpages); -out: - return err; -} - -void au_dpages_free(struct au_dcsub_pages *dpages) -{ - int i; - struct au_dpage *p; - - p = dpages->dpages; - for (i = 0; i < dpages->ndpage; i++) - au_dpage_free(p++); - kfree(dpages->dpages); -} - -static int au_dpages_append(struct au_dcsub_pages *dpages, - struct dentry *dentry, gfp_t gfp) -{ - int err, sz; - struct au_dpage *dpage; - void *p; - - dpage = dpages->dpages + dpages->ndpage - 1; - sz = PAGE_SIZE / sizeof(dentry); - if (unlikely(dpage->ndentry >= sz)) { - AuLabel(new dpage); - err = -ENOMEM; - sz = dpages->ndpage * sizeof(*dpages->dpages); - p = au_kzrealloc(dpages->dpages, sz, - sz + sizeof(*dpages->dpages), gfp); - if (unlikely(!p)) - goto out; - - dpages->dpages = p; - dpage = dpages->dpages + dpages->ndpage; - p = (void *)__get_free_page(gfp); - if (unlikely(!p)) - goto out; - - dpage->ndentry = 0; - dpage->dentries = p; - dpages->ndpage++; - } - - AuDebugOn(au_dcount(dentry) <= 0); - dpage->dentries[dpage->ndentry++] = dget_dlock(dentry); - return 0; /* success */ - -out: - return err; -} - -/* todo: BAD approach */ -/* copied from linux/fs/dcache.c */ -enum d_walk_ret { - D_WALK_CONTINUE, - D_WALK_QUIT, - D_WALK_NORETRY, - D_WALK_SKIP, -}; - -extern void d_walk(struct dentry *parent, void *data, - enum d_walk_ret (*enter)(void *, struct dentry *), - void (*finish)(void *)); - -struct ac_dpages_arg { - int err; - struct au_dcsub_pages *dpages; - struct super_block *sb; - au_dpages_test test; - void *arg; -}; - -static enum d_walk_ret au_call_dpages_append(void *_arg, struct dentry *dentry) -{ - enum d_walk_ret ret; - struct ac_dpages_arg *arg = _arg; - - ret = D_WALK_CONTINUE; - if (dentry->d_sb == arg->sb - && !IS_ROOT(dentry) - && au_dcount(dentry) > 0 - && au_di(dentry) - && (!arg->test || arg->test(dentry, arg->arg))) { - arg->err = au_dpages_append(arg->dpages, dentry, GFP_ATOMIC); - if (unlikely(arg->err)) - ret = D_WALK_QUIT; - } - - return ret; -} - -int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, - au_dpages_test test, void *arg) -{ - struct ac_dpages_arg args = { - .err = 0, - .dpages = dpages, - .sb = root->d_sb, - .test = test, - .arg = arg - }; - - d_walk(root, &args, au_call_dpages_append, NULL); - - return args.err; -} - -int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, - int do_include, au_dpages_test test, void *arg) -{ - int err; - - err = 0; - write_seqlock(&rename_lock); - spin_lock(&dentry->d_lock); - if (do_include - && au_dcount(dentry) > 0 - && (!test || test(dentry, arg))) - err = au_dpages_append(dpages, dentry, GFP_ATOMIC); - spin_unlock(&dentry->d_lock); - if (unlikely(err)) - goto out; - - /* - * RCU for vfsmount is unnecessary since this is a traverse in a single - * mount - */ - while (!IS_ROOT(dentry)) { - dentry = dentry->d_parent; /* rename_lock is locked */ - spin_lock(&dentry->d_lock); - if (au_dcount(dentry) > 0 - && (!test || test(dentry, arg))) - err = au_dpages_append(dpages, dentry, GFP_ATOMIC); - spin_unlock(&dentry->d_lock); - if (unlikely(err)) - break; - } - -out: - write_sequnlock(&rename_lock); - return err; -} - -static inline int au_dcsub_dpages_aufs(struct dentry *dentry, void *arg) -{ - return au_di(dentry) && dentry->d_sb == arg; -} - -int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, - struct dentry *dentry, int do_include) -{ - return au_dcsub_pages_rev(dpages, dentry, do_include, - au_dcsub_dpages_aufs, dentry->d_sb); -} - -int au_test_subdir(struct dentry *d1, struct dentry *d2) -{ - struct path path[2] = { - { - .dentry = d1 - }, - { - .dentry = d2 - } - }; - - return path_is_under(path + 0, path + 1); -} diff --git a/fs/aufs/dcsub.h b/fs/aufs/dcsub.h deleted file mode 100644 index d372325b2..000000000 --- a/fs/aufs/dcsub.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * sub-routines for dentry cache - */ - -#ifndef __AUFS_DCSUB_H__ -#define __AUFS_DCSUB_H__ - -#ifdef __KERNEL__ - -#include <linux/dcache.h> -#include <linux/fs.h> - -struct au_dpage { - int ndentry; - struct dentry **dentries; -}; - -struct au_dcsub_pages { - int ndpage; - struct au_dpage *dpages; -}; - -/* ---------------------------------------------------------------------- */ - -/* dcsub.c */ -int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp); -void au_dpages_free(struct au_dcsub_pages *dpages); -typedef int (*au_dpages_test)(struct dentry *dentry, void *arg); -int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, - au_dpages_test test, void *arg); -int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, - int do_include, au_dpages_test test, void *arg); -int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, - struct dentry *dentry, int do_include); -int au_test_subdir(struct dentry *d1, struct dentry *d2); - -/* ---------------------------------------------------------------------- */ - -/* - * todo: in linux-3.13, several similar (but faster) helpers are added to - * include/linux/dcache.h. Try them (in the future). - */ - -static inline int au_d_hashed_positive(struct dentry *d) -{ - int err; - struct inode *inode = d_inode(d); - - err = 0; - if (unlikely(d_unhashed(d) - || d_is_negative(d) - || !inode->i_nlink)) - err = -ENOENT; - return err; -} - -static inline int au_d_linkable(struct dentry *d) -{ - int err; - struct inode *inode = d_inode(d); - - err = au_d_hashed_positive(d); - if (err - && d_is_positive(d) - && (inode->i_state & I_LINKABLE)) - err = 0; - return err; -} - -static inline int au_d_alive(struct dentry *d) -{ - int err; - struct inode *inode; - - err = 0; - if (!IS_ROOT(d)) - err = au_d_hashed_positive(d); - else { - inode = d_inode(d); - if (unlikely(d_unlinked(d) - || d_is_negative(d) - || !inode->i_nlink)) - err = -ENOENT; - } - return err; -} - -static inline int au_alive_dir(struct dentry *d) -{ - int err; - - err = au_d_alive(d); - if (unlikely(err || IS_DEADDIR(d_inode(d)))) - err = -ENOENT; - return err; -} - -static inline int au_qstreq(struct qstr *a, struct qstr *b) -{ - return a->len == b->len - && !memcmp(a->name, b->name, a->len); -} - -/* - * by the commit - * 360f547 2015-01-25 dcache: let the dentry count go down to zero without - * taking d_lock - * the type of d_lockref.count became int, but the inlined function d_count() - * still returns unsigned int. - * I don't know why. Maybe it is for every d_count() users? - * Anyway au_dcount() lives on. - */ -static inline int au_dcount(struct dentry *d) -{ - return (int)d_count(d); -} - -#endif /* __KERNEL__ */ -#endif /* __AUFS_DCSUB_H__ */ diff --git a/fs/aufs/debug.c b/fs/aufs/debug.c deleted file mode 100644 index a1154d6de..000000000 --- a/fs/aufs/debug.c +++ /dev/null @@ -1,440 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * debug print functions - */ - -#include "aufs.h" - -/* Returns 0, or -errno. arg is in kp->arg. */ -static int param_atomic_t_set(const char *val, const struct kernel_param *kp) -{ - int err, n; - - err = kstrtoint(val, 0, &n); - if (!err) { - if (n > 0) - au_debug_on(); - else - au_debug_off(); - } - return err; -} - -/* Returns length written or -errno. Buffer is 4k (ie. be short!) */ -static int param_atomic_t_get(char *buffer, const struct kernel_param *kp) -{ - atomic_t *a; - - a = kp->arg; - return sprintf(buffer, "%d", atomic_read(a)); -} - -static struct kernel_param_ops param_ops_atomic_t = { - .set = param_atomic_t_set, - .get = param_atomic_t_get - /* void (*free)(void *arg) */ -}; - -atomic_t aufs_debug = ATOMIC_INIT(0); -MODULE_PARM_DESC(debug, "debug print"); -module_param_named(debug, aufs_debug, atomic_t, S_IRUGO | S_IWUSR | S_IWGRP); - -DEFINE_MUTEX(au_dbg_mtx); /* just to serialize the dbg msgs */ -char *au_plevel = KERN_DEBUG; -#define dpri(fmt, ...) do { \ - if ((au_plevel \ - && strcmp(au_plevel, KERN_DEBUG)) \ - || au_debug_test()) \ - printk("%s" fmt, au_plevel, ##__VA_ARGS__); \ -} while (0) - -/* ---------------------------------------------------------------------- */ - -void au_dpri_whlist(struct au_nhash *whlist) -{ - unsigned long ul, n; - struct hlist_head *head; - struct au_vdir_wh *pos; - - n = whlist->nh_num; - head = whlist->nh_head; - for (ul = 0; ul < n; ul++) { - hlist_for_each_entry(pos, head, wh_hash) - dpri("b%d, %.*s, %d\n", - pos->wh_bindex, - pos->wh_str.len, pos->wh_str.name, - pos->wh_str.len); - head++; - } -} - -void au_dpri_vdir(struct au_vdir *vdir) -{ - unsigned long ul; - union au_vdir_deblk_p p; - unsigned char *o; - - if (!vdir || IS_ERR(vdir)) { - dpri("err %ld\n", PTR_ERR(vdir)); - return; - } - - dpri("deblk %u, nblk %lu, deblk %p, last{%lu, %p}, ver %lu\n", - vdir->vd_deblk_sz, vdir->vd_nblk, vdir->vd_deblk, - vdir->vd_last.ul, vdir->vd_last.p.deblk, vdir->vd_version); - for (ul = 0; ul < vdir->vd_nblk; ul++) { - p.deblk = vdir->vd_deblk[ul]; - o = p.deblk; - dpri("[%lu]: %p\n", ul, o); - } -} - -static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode, int hn, - struct dentry *wh) -{ - char *n = NULL; - int l = 0; - - if (!inode || IS_ERR(inode)) { - dpri("i%d: err %ld\n", bindex, PTR_ERR(inode)); - return -1; - } - - /* the type of i_blocks depends upon CONFIG_LBDAF */ - BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long) - && sizeof(inode->i_blocks) != sizeof(u64)); - if (wh) { - n = (void *)wh->d_name.name; - l = wh->d_name.len; - } - - dpri("i%d: %p, i%lu, %s, cnt %d, nl %u, 0%o, sz %llu, blk %llu," - " hn %d, ct %lld, np %lu, st 0x%lx, f 0x%x, v %llu, g %x%s%.*s\n", - bindex, inode, - inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??", - atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode, - i_size_read(inode), (unsigned long long)inode->i_blocks, - hn, (long long)timespec_to_ns(&inode->i_ctime) & 0x0ffff, - inode->i_mapping ? inode->i_mapping->nrpages : 0, - inode->i_state, inode->i_flags, inode->i_version, - inode->i_generation, - l ? ", wh " : "", l, n); - return 0; -} - -void au_dpri_inode(struct inode *inode) -{ - struct au_iinfo *iinfo; - aufs_bindex_t bindex; - int err, hn; - - err = do_pri_inode(-1, inode, -1, NULL); - if (err || !au_test_aufs(inode->i_sb)) - return; - - iinfo = au_ii(inode); - if (!iinfo) - return; - dpri("i-1: bstart %d, bend %d, gen %d\n", - iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode, NULL)); - if (iinfo->ii_bstart < 0) - return; - hn = 0; - for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++) { - hn = !!au_hn(iinfo->ii_hinode + bindex); - do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode, hn, - iinfo->ii_hinode[0 + bindex].hi_whdentry); - } -} - -void au_dpri_dalias(struct inode *inode) -{ - struct dentry *d; - - spin_lock(&inode->i_lock); - hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) - au_dpri_dentry(d); - spin_unlock(&inode->i_lock); -} - -static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry) -{ - struct dentry *wh = NULL; - int hn; - struct au_iinfo *iinfo; - - if (!dentry || IS_ERR(dentry)) { - dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry)); - return -1; - } - /* do not call dget_parent() here */ - /* note: access d_xxx without d_lock */ - dpri("d%d: %p, %pd2?, %s, cnt %d, flags 0x%x, %shashed\n", - bindex, dentry, dentry, - dentry->d_sb ? au_sbtype(dentry->d_sb) : "??", - au_dcount(dentry), dentry->d_flags, - d_unhashed(dentry) ? "un" : ""); - hn = -1; - if (bindex >= 0 - && d_is_positive(dentry) - && au_test_aufs(dentry->d_sb)) { - iinfo = au_ii(d_inode(dentry)); - if (iinfo) { - hn = !!au_hn(iinfo->ii_hinode + bindex); - wh = iinfo->ii_hinode[0 + bindex].hi_whdentry; - } - } - do_pri_inode(bindex, d_inode(dentry), hn, wh); - return 0; -} - -void au_dpri_dentry(struct dentry *dentry) -{ - struct au_dinfo *dinfo; - aufs_bindex_t bindex; - int err; - struct au_hdentry *hdp; - - err = do_pri_dentry(-1, dentry); - if (err || !au_test_aufs(dentry->d_sb)) - return; - - dinfo = au_di(dentry); - if (!dinfo) - return; - dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d, tmp %d\n", - dinfo->di_bstart, dinfo->di_bend, - dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry), - dinfo->di_tmpfile); - if (dinfo->di_bstart < 0) - return; - hdp = dinfo->di_hdentry; - for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend; bindex++) - do_pri_dentry(bindex, hdp[0 + bindex].hd_dentry); -} - -static int do_pri_file(aufs_bindex_t bindex, struct file *file) -{ - char a[32]; - - if (!file || IS_ERR(file)) { - dpri("f%d: err %ld\n", bindex, PTR_ERR(file)); - return -1; - } - a[0] = 0; - if (bindex < 0 - && !IS_ERR_OR_NULL(file->f_path.dentry) - && au_test_aufs(file->f_path.dentry->d_sb) - && au_fi(file)) - snprintf(a, sizeof(a), ", gen %d, mmapped %d", - au_figen(file), atomic_read(&au_fi(file)->fi_mmapped)); - dpri("f%d: mode 0x%x, flags 0%o, cnt %ld, v %llu, pos %llu%s\n", - bindex, file->f_mode, file->f_flags, (long)file_count(file), - file->f_version, file->f_pos, a); - if (!IS_ERR_OR_NULL(file->f_path.dentry)) - do_pri_dentry(bindex, file->f_path.dentry); - return 0; -} - -void au_dpri_file(struct file *file) -{ - struct au_finfo *finfo; - struct au_fidir *fidir; - struct au_hfile *hfile; - aufs_bindex_t bindex; - int err; - - err = do_pri_file(-1, file); - if (err - || IS_ERR_OR_NULL(file->f_path.dentry) - || !au_test_aufs(file->f_path.dentry->d_sb)) - return; - - finfo = au_fi(file); - if (!finfo) - return; - if (finfo->fi_btop < 0) - return; - fidir = finfo->fi_hdir; - if (!fidir) - do_pri_file(finfo->fi_btop, finfo->fi_htop.hf_file); - else - for (bindex = finfo->fi_btop; - bindex >= 0 && bindex <= fidir->fd_bbot; - bindex++) { - hfile = fidir->fd_hfile + bindex; - do_pri_file(bindex, hfile ? hfile->hf_file : NULL); - } -} - -static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br) -{ - struct vfsmount *mnt; - struct super_block *sb; - - if (!br || IS_ERR(br)) - goto out; - mnt = au_br_mnt(br); - if (!mnt || IS_ERR(mnt)) - goto out; - sb = mnt->mnt_sb; - if (!sb || IS_ERR(sb)) - goto out; - - dpri("s%d: {perm 0x%x, id %d, cnt %d, wbr %p}, " - "%s, dev 0x%02x%02x, flags 0x%lx, cnt %d, active %d, " - "xino %d\n", - bindex, br->br_perm, br->br_id, atomic_read(&br->br_count), - br->br_wbr, au_sbtype(sb), MAJOR(sb->s_dev), MINOR(sb->s_dev), - sb->s_flags, sb->s_count, - atomic_read(&sb->s_active), !!br->br_xino.xi_file); - return 0; - -out: - dpri("s%d: err %ld\n", bindex, PTR_ERR(br)); - return -1; -} - -void au_dpri_sb(struct super_block *sb) -{ - struct au_sbinfo *sbinfo; - aufs_bindex_t bindex; - int err; - /* to reuduce stack size */ - struct { - struct vfsmount mnt; - struct au_branch fake; - } *a; - - /* this function can be called from magic sysrq */ - a = kzalloc(sizeof(*a), GFP_ATOMIC); - if (unlikely(!a)) { - dpri("no memory\n"); - return; - } - - a->mnt.mnt_sb = sb; - a->fake.br_perm = 0; - a->fake.br_path.mnt = &a->mnt; - a->fake.br_xino.xi_file = NULL; - atomic_set(&a->fake.br_count, 0); - smp_mb(); /* atomic_set */ - err = do_pri_br(-1, &a->fake); - kfree(a); - dpri("dev 0x%x\n", sb->s_dev); - if (err || !au_test_aufs(sb)) - return; - - sbinfo = au_sbi(sb); - if (!sbinfo) - return; - dpri("nw %d, gen %u, kobj %d\n", - atomic_read(&sbinfo->si_nowait.nw_len), sbinfo->si_generation, - atomic_read(&sbinfo->si_kobj.kref.refcount)); - for (bindex = 0; bindex <= sbinfo->si_bend; bindex++) - do_pri_br(bindex, sbinfo->si_branch[0 + bindex]); -} - -/* ---------------------------------------------------------------------- */ - -void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line) -{ - struct inode *h_inode, *inode = d_inode(dentry); - struct dentry *h_dentry; - aufs_bindex_t bindex, bend, bi; - - if (!inode /* || au_di(dentry)->di_lsc == AuLsc_DI_TMP */) - return; - - bend = au_dbend(dentry); - bi = au_ibend(inode); - if (bi < bend) - bend = bi; - bindex = au_dbstart(dentry); - bi = au_ibstart(inode); - if (bi > bindex) - bindex = bi; - - for (; bindex <= bend; bindex++) { - h_dentry = au_h_dptr(dentry, bindex); - if (!h_dentry) - continue; - h_inode = au_h_iptr(inode, bindex); - if (unlikely(h_inode != d_inode(h_dentry))) { - au_debug_on(); - AuDbg("b%d, %s:%d\n", bindex, func, line); - AuDbgDentry(dentry); - AuDbgInode(inode); - au_debug_off(); - BUG(); - } - } -} - -void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen) -{ - int err, i, j; - struct au_dcsub_pages dpages; - struct au_dpage *dpage; - struct dentry **dentries; - - err = au_dpages_init(&dpages, GFP_NOFS); - AuDebugOn(err); - err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/1); - AuDebugOn(err); - for (i = dpages.ndpage - 1; !err && i >= 0; i--) { - dpage = dpages.dpages + i; - dentries = dpage->dentries; - for (j = dpage->ndentry - 1; !err && j >= 0; j--) - AuDebugOn(au_digen_test(dentries[j], sigen)); - } - au_dpages_free(&dpages); -} - -void au_dbg_verify_kthread(void) -{ - if (au_wkq_test()) { - au_dbg_blocked(); - /* - * It may be recursive, but udba=notify between two aufs mounts, - * where a single ro branch is shared, is not a problem. - */ - /* WARN_ON(1); */ - } -} - -/* ---------------------------------------------------------------------- */ - -int __init au_debug_init(void) -{ - aufs_bindex_t bindex; - struct au_vdir_destr destr; - - bindex = -1; - AuDebugOn(bindex >= 0); - - destr.len = -1; - AuDebugOn(destr.len < NAME_MAX); - -#ifdef CONFIG_4KSTACKS - pr_warn("CONFIG_4KSTACKS is defined.\n"); -#endif - - return 0; -} diff --git a/fs/aufs/debug.h b/fs/aufs/debug.h deleted file mode 100644 index de6c2a682..000000000 --- a/fs/aufs/debug.h +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * debug print functions - */ - -#ifndef __AUFS_DEBUG_H__ -#define __AUFS_DEBUG_H__ - -#ifdef __KERNEL__ - -#include <linux/atomic.h> -#include <linux/module.h> -#include <linux/kallsyms.h> -#include <linux/sysrq.h> - -#ifdef CONFIG_AUFS_DEBUG -#define AuDebugOn(a) BUG_ON(a) - -/* module parameter */ -extern atomic_t aufs_debug; -static inline void au_debug_on(void) -{ - atomic_inc(&aufs_debug); -} -static inline void au_debug_off(void) -{ - atomic_dec_if_positive(&aufs_debug); -} - -static inline int au_debug_test(void) -{ - return atomic_read(&aufs_debug) > 0; -} -#else -#define AuDebugOn(a) do {} while (0) -AuStubVoid(au_debug_on, void) -AuStubVoid(au_debug_off, void) -AuStubInt0(au_debug_test, void) -#endif /* CONFIG_AUFS_DEBUG */ - -#define param_check_atomic_t(name, p) __param_check(name, p, atomic_t) - -/* ---------------------------------------------------------------------- */ - -/* debug print */ - -#define AuDbg(fmt, ...) do { \ - if (au_debug_test()) \ - pr_debug("DEBUG: " fmt, ##__VA_ARGS__); \ -} while (0) -#define AuLabel(l) AuDbg(#l "\n") -#define AuIOErr(fmt, ...) pr_err("I/O Error, " fmt, ##__VA_ARGS__) -#define AuWarn1(fmt, ...) do { \ - static unsigned char _c; \ - if (!_c++) \ - pr_warn(fmt, ##__VA_ARGS__); \ -} while (0) - -#define AuErr1(fmt, ...) do { \ - static unsigned char _c; \ - if (!_c++) \ - pr_err(fmt, ##__VA_ARGS__); \ -} while (0) - -#define AuIOErr1(fmt, ...) do { \ - static unsigned char _c; \ - if (!_c++) \ - AuIOErr(fmt, ##__VA_ARGS__); \ -} while (0) - -#define AuUnsupportMsg "This operation is not supported." \ - " Please report this application to aufs-users ML." -#define AuUnsupport(fmt, ...) do { \ - pr_err(AuUnsupportMsg "\n" fmt, ##__VA_ARGS__); \ - dump_stack(); \ -} while (0) - -#define AuTraceErr(e) do { \ - if (unlikely((e) < 0)) \ - AuDbg("err %d\n", (int)(e)); \ -} while (0) - -#define AuTraceErrPtr(p) do { \ - if (IS_ERR(p)) \ - AuDbg("err %ld\n", PTR_ERR(p)); \ -} while (0) - -/* dirty macros for debug print, use with "%.*s" and caution */ -#define AuLNPair(qstr) (qstr)->len, (qstr)->name - -/* ---------------------------------------------------------------------- */ - -struct dentry; -#ifdef CONFIG_AUFS_DEBUG -extern struct mutex au_dbg_mtx; -extern char *au_plevel; -struct au_nhash; -void au_dpri_whlist(struct au_nhash *whlist); -struct au_vdir; -void au_dpri_vdir(struct au_vdir *vdir); -struct inode; -void au_dpri_inode(struct inode *inode); -void au_dpri_dalias(struct inode *inode); -void au_dpri_dentry(struct dentry *dentry); -struct file; -void au_dpri_file(struct file *filp); -struct super_block; -void au_dpri_sb(struct super_block *sb); - -#define au_dbg_verify_dinode(d) __au_dbg_verify_dinode(d, __func__, __LINE__) -void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line); -void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen); -void au_dbg_verify_kthread(void); - -int __init au_debug_init(void); - -#define AuDbgWhlist(w) do { \ - mutex_lock(&au_dbg_mtx); \ - AuDbg(#w "\n"); \ - au_dpri_whlist(w); \ - mutex_unlock(&au_dbg_mtx); \ -} while (0) - -#define AuDbgVdir(v) do { \ - mutex_lock(&au_dbg_mtx); \ - AuDbg(#v "\n"); \ - au_dpri_vdir(v); \ - mutex_unlock(&au_dbg_mtx); \ -} while (0) - -#define AuDbgInode(i) do { \ - mutex_lock(&au_dbg_mtx); \ - AuDbg(#i "\n"); \ - au_dpri_inode(i); \ - mutex_unlock(&au_dbg_mtx); \ -} while (0) - -#define AuDbgDAlias(i) do { \ - mutex_lock(&au_dbg_mtx); \ - AuDbg(#i "\n"); \ - au_dpri_dalias(i); \ - mutex_unlock(&au_dbg_mtx); \ -} while (0) - -#define AuDbgDentry(d) do { \ - mutex_lock(&au_dbg_mtx); \ - AuDbg(#d "\n"); \ - au_dpri_dentry(d); \ - mutex_unlock(&au_dbg_mtx); \ -} while (0) - -#define AuDbgFile(f) do { \ - mutex_lock(&au_dbg_mtx); \ - AuDbg(#f "\n"); \ - au_dpri_file(f); \ - mutex_unlock(&au_dbg_mtx); \ -} while (0) - -#define AuDbgSb(sb) do { \ - mutex_lock(&au_dbg_mtx); \ - AuDbg(#sb "\n"); \ - au_dpri_sb(sb); \ - mutex_unlock(&au_dbg_mtx); \ -} while (0) - -#define AuDbgSym(addr) do { \ - char sym[KSYM_SYMBOL_LEN]; \ - sprint_symbol(sym, (unsigned long)addr); \ - AuDbg("%s\n", sym); \ -} while (0) -#else -AuStubVoid(au_dbg_verify_dinode, struct dentry *dentry) -AuStubVoid(au_dbg_verify_gen, struct dentry *parent, unsigned int sigen) -AuStubVoid(au_dbg_verify_kthread, void) -AuStubInt0(__init au_debug_init, void) - -#define AuDbgWhlist(w) do {} while (0) -#define AuDbgVdir(v) do {} while (0) -#define AuDbgInode(i) do {} while (0) -#define AuDbgDAlias(i) do {} while (0) -#define AuDbgDentry(d) do {} while (0) -#define AuDbgFile(f) do {} while (0) -#define AuDbgSb(sb) do {} while (0) -#define AuDbgSym(addr) do {} while (0) -#endif /* CONFIG_AUFS_DEBUG */ - -/* ---------------------------------------------------------------------- */ - -#ifdef CONFIG_AUFS_MAGIC_SYSRQ -int __init au_sysrq_init(void); -void au_sysrq_fin(void); - -#ifdef CONFIG_HW_CONSOLE -#define au_dbg_blocked() do { \ - WARN_ON(1); \ - handle_sysrq('w'); \ -} while (0) -#else -AuStubVoid(au_dbg_blocked, void) -#endif - -#else -AuStubInt0(__init au_sysrq_init, void) -AuStubVoid(au_sysrq_fin, void) -AuStubVoid(au_dbg_blocked, void) -#endif /* CONFIG_AUFS_MAGIC_SYSRQ */ - -#endif /* __KERNEL__ */ -#endif /* __AUFS_DEBUG_H__ */ diff --git a/fs/aufs/dentry.c b/fs/aufs/dentry.c deleted file mode 100644 index 1191a6c93..000000000 --- a/fs/aufs/dentry.c +++ /dev/null @@ -1,1105 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * lookup and dentry operations - */ - -#include <linux/namei.h> -#include "aufs.h" - -#define AuLkup_ALLOW_NEG 1 -#define AuLkup_IGNORE_PERM (1 << 1) -#define au_ftest_lkup(flags, name) ((flags) & AuLkup_##name) -#define au_fset_lkup(flags, name) \ - do { (flags) |= AuLkup_##name; } while (0) -#define au_fclr_lkup(flags, name) \ - do { (flags) &= ~AuLkup_##name; } while (0) - -struct au_do_lookup_args { - unsigned int flags; - mode_t type; -}; - -/* - * returns positive/negative dentry, NULL or an error. - * NULL means whiteout-ed or not-found. - */ -static struct dentry* -au_do_lookup(struct dentry *h_parent, struct dentry *dentry, - aufs_bindex_t bindex, struct qstr *wh_name, - struct au_do_lookup_args *args) -{ - struct dentry *h_dentry; - struct inode *h_inode; - struct au_branch *br; - int wh_found, opq; - unsigned char wh_able; - const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG); - const unsigned char ignore_perm = !!au_ftest_lkup(args->flags, - IGNORE_PERM); - - wh_found = 0; - br = au_sbr(dentry->d_sb, bindex); - wh_able = !!au_br_whable(br->br_perm); - if (wh_able) - wh_found = au_wh_test(h_parent, wh_name, /*try_sio*/0); - h_dentry = ERR_PTR(wh_found); - if (!wh_found) - goto real_lookup; - if (unlikely(wh_found < 0)) - goto out; - - /* We found a whiteout */ - /* au_set_dbend(dentry, bindex); */ - au_set_dbwh(dentry, bindex); - if (!allow_neg) - return NULL; /* success */ - -real_lookup: - if (!ignore_perm) - h_dentry = vfsub_lkup_one(&dentry->d_name, h_parent); - else - h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent); - if (IS_ERR(h_dentry)) { - if (PTR_ERR(h_dentry) == -ENAMETOOLONG - && !allow_neg) - h_dentry = NULL; - goto out; - } - - h_inode = d_inode(h_dentry); - if (d_is_negative(h_dentry)) { - if (!allow_neg) - goto out_neg; - } else if (wh_found - || (args->type && args->type != (h_inode->i_mode & S_IFMT))) - goto out_neg; - - if (au_dbend(dentry) <= bindex) - au_set_dbend(dentry, bindex); - if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) - au_set_dbstart(dentry, bindex); - au_set_h_dptr(dentry, bindex, h_dentry); - - if (!d_is_dir(h_dentry) - || !wh_able - || (d_really_is_positive(dentry) && !d_is_dir(dentry))) - goto out; /* success */ - - mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); - opq = au_diropq_test(h_dentry); - mutex_unlock(&h_inode->i_mutex); - if (opq > 0) - au_set_dbdiropq(dentry, bindex); - else if (unlikely(opq < 0)) { - au_set_h_dptr(dentry, bindex, NULL); - h_dentry = ERR_PTR(opq); - } - goto out; - -out_neg: - dput(h_dentry); - h_dentry = NULL; -out: - return h_dentry; -} - -static int au_test_shwh(struct super_block *sb, const struct qstr *name) -{ - if (unlikely(!au_opt_test(au_mntflags(sb), SHWH) - && !strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))) - return -EPERM; - return 0; -} - -/* - * returns the number of lower positive dentries, - * otherwise an error. - * can be called at unlinking with @type is zero. - */ -int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type) -{ - int npositive, err; - aufs_bindex_t bindex, btail, bdiropq; - unsigned char isdir, dirperm1; - struct qstr whname; - struct au_do_lookup_args args = { - .flags = 0, - .type = type - }; - const struct qstr *name = &dentry->d_name; - struct dentry *parent; - struct super_block *sb; - - sb = dentry->d_sb; - err = au_test_shwh(sb, name); - if (unlikely(err)) - goto out; - - err = au_wh_name_alloc(&whname, name); - if (unlikely(err)) - goto out; - - isdir = !!d_is_dir(dentry); - if (!type) - au_fset_lkup(args.flags, ALLOW_NEG); - dirperm1 = !!au_opt_test(au_mntflags(sb), DIRPERM1); - - npositive = 0; - parent = dget_parent(dentry); - btail = au_dbtaildir(parent); - for (bindex = bstart; bindex <= btail; bindex++) { - struct dentry *h_parent, *h_dentry; - struct inode *h_inode, *h_dir; - - h_dentry = au_h_dptr(dentry, bindex); - if (h_dentry) { - if (d_is_positive(h_dentry)) - npositive++; - if (type != S_IFDIR) - break; - continue; - } - h_parent = au_h_dptr(parent, bindex); - if (!h_parent || !d_is_dir(h_parent)) - continue; - - h_dir = d_inode(h_parent); - mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); - h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname, - &args); - mutex_unlock(&h_dir->i_mutex); - err = PTR_ERR(h_dentry); - if (IS_ERR(h_dentry)) - goto out_parent; - if (h_dentry) - au_fclr_lkup(args.flags, ALLOW_NEG); - if (dirperm1) - au_fset_lkup(args.flags, IGNORE_PERM); - - if (au_dbwh(dentry) >= 0) - break; - if (!h_dentry) - continue; - if (d_is_negative(h_dentry)) - continue; - h_inode = d_inode(h_dentry); - npositive++; - if (!args.type) - args.type = h_inode->i_mode & S_IFMT; - if (args.type != S_IFDIR) - break; - else if (isdir) { - /* the type of lower may be different */ - bdiropq = au_dbdiropq(dentry); - if (bdiropq >= 0 && bdiropq <= bindex) - break; - } - } - - if (npositive) { - AuLabel(positive); - au_update_dbstart(dentry); - } - err = npositive; - if (unlikely(!au_opt_test(au_mntflags(sb), UDBA_NONE) - && au_dbstart(dentry) < 0)) { - err = -EIO; - AuIOErr("both of real entry and whiteout found, %pd, err %d\n", - dentry, err); - } - -out_parent: - dput(parent); - kfree(whname.name); -out: - return err; -} - -struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent) -{ - struct dentry *dentry; - int wkq_err; - - if (!au_test_h_perm_sio(d_inode(parent), MAY_EXEC)) - dentry = vfsub_lkup_one(name, parent); - else { - struct vfsub_lkup_one_args args = { - .errp = &dentry, - .name = name, - .parent = parent - }; - - wkq_err = au_wkq_wait(vfsub_call_lkup_one, &args); - if (unlikely(wkq_err)) - dentry = ERR_PTR(wkq_err); - } - - return dentry; -} - -/* - * lookup @dentry on @bindex which should be negative. - */ -int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh) -{ - int err; - struct dentry *parent, *h_parent, *h_dentry; - struct au_branch *br; - - parent = dget_parent(dentry); - h_parent = au_h_dptr(parent, bindex); - br = au_sbr(dentry->d_sb, bindex); - if (wh) - h_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); - else - h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent); - err = PTR_ERR(h_dentry); - if (IS_ERR(h_dentry)) - goto out; - if (unlikely(d_is_positive(h_dentry))) { - err = -EIO; - AuIOErr("%pd should be negative on b%d.\n", h_dentry, bindex); - dput(h_dentry); - goto out; - } - - err = 0; - if (bindex < au_dbstart(dentry)) - au_set_dbstart(dentry, bindex); - if (au_dbend(dentry) < bindex) - au_set_dbend(dentry, bindex); - au_set_h_dptr(dentry, bindex, h_dentry); - -out: - dput(parent); - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* subset of struct inode */ -struct au_iattr { - unsigned long i_ino; - /* unsigned int i_nlink; */ - kuid_t i_uid; - kgid_t i_gid; - u64 i_version; -/* - loff_t i_size; - blkcnt_t i_blocks; -*/ - umode_t i_mode; -}; - -static void au_iattr_save(struct au_iattr *ia, struct inode *h_inode) -{ - ia->i_ino = h_inode->i_ino; - /* ia->i_nlink = h_inode->i_nlink; */ - ia->i_uid = h_inode->i_uid; - ia->i_gid = h_inode->i_gid; - ia->i_version = h_inode->i_version; -/* - ia->i_size = h_inode->i_size; - ia->i_blocks = h_inode->i_blocks; -*/ - ia->i_mode = (h_inode->i_mode & S_IFMT); -} - -static int au_iattr_test(struct au_iattr *ia, struct inode *h_inode) -{ - return ia->i_ino != h_inode->i_ino - /* || ia->i_nlink != h_inode->i_nlink */ - || !uid_eq(ia->i_uid, h_inode->i_uid) - || !gid_eq(ia->i_gid, h_inode->i_gid) - || ia->i_version != h_inode->i_version -/* - || ia->i_size != h_inode->i_size - || ia->i_blocks != h_inode->i_blocks -*/ - || ia->i_mode != (h_inode->i_mode & S_IFMT); -} - -static int au_h_verify_dentry(struct dentry *h_dentry, struct dentry *h_parent, - struct au_branch *br) -{ - int err; - struct au_iattr ia; - struct inode *h_inode; - struct dentry *h_d; - struct super_block *h_sb; - - err = 0; - memset(&ia, -1, sizeof(ia)); - h_sb = h_dentry->d_sb; - h_inode = NULL; - if (d_is_positive(h_dentry)) { - h_inode = d_inode(h_dentry); - au_iattr_save(&ia, h_inode); - } else if (au_test_nfs(h_sb) || au_test_fuse(h_sb)) - /* nfs d_revalidate may return 0 for negative dentry */ - /* fuse d_revalidate always return 0 for negative dentry */ - goto out; - - /* main purpose is namei.c:cached_lookup() and d_revalidate */ - h_d = vfsub_lkup_one(&h_dentry->d_name, h_parent); - err = PTR_ERR(h_d); - if (IS_ERR(h_d)) - goto out; - - err = 0; - if (unlikely(h_d != h_dentry - || d_inode(h_d) != h_inode - || (h_inode && au_iattr_test(&ia, h_inode)))) - err = au_busy_or_stale(); - dput(h_d); - -out: - AuTraceErr(err); - return err; -} - -int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, - struct dentry *h_parent, struct au_branch *br) -{ - int err; - - err = 0; - if (udba == AuOpt_UDBA_REVAL - && !au_test_fs_remote(h_dentry->d_sb)) { - IMustLock(h_dir); - err = (d_inode(h_dentry->d_parent) != h_dir); - } else if (udba != AuOpt_UDBA_NONE) - err = au_h_verify_dentry(h_dentry, h_parent, br); - - return err; -} - -/* ---------------------------------------------------------------------- */ - -static int au_do_refresh_hdentry(struct dentry *dentry, struct dentry *parent) -{ - int err; - aufs_bindex_t new_bindex, bindex, bend, bwh, bdiropq; - struct au_hdentry tmp, *p, *q; - struct au_dinfo *dinfo; - struct super_block *sb; - - DiMustWriteLock(dentry); - - sb = dentry->d_sb; - dinfo = au_di(dentry); - bend = dinfo->di_bend; - bwh = dinfo->di_bwh; - bdiropq = dinfo->di_bdiropq; - p = dinfo->di_hdentry + dinfo->di_bstart; - for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) { - if (!p->hd_dentry) - continue; - - new_bindex = au_br_index(sb, p->hd_id); - if (new_bindex == bindex) - continue; - - if (dinfo->di_bwh == bindex) - bwh = new_bindex; - if (dinfo->di_bdiropq == bindex) - bdiropq = new_bindex; - if (new_bindex < 0) { - au_hdput(p); - p->hd_dentry = NULL; - continue; - } - - /* swap two lower dentries, and loop again */ - q = dinfo->di_hdentry + new_bindex; - tmp = *q; - *q = *p; - *p = tmp; - if (tmp.hd_dentry) { - bindex--; - p--; - } - } - - dinfo->di_bwh = -1; - if (bwh >= 0 && bwh <= au_sbend(sb) && au_sbr_whable(sb, bwh)) - dinfo->di_bwh = bwh; - - dinfo->di_bdiropq = -1; - if (bdiropq >= 0 - && bdiropq <= au_sbend(sb) - && au_sbr_whable(sb, bdiropq)) - dinfo->di_bdiropq = bdiropq; - - err = -EIO; - dinfo->di_bstart = -1; - dinfo->di_bend = -1; - bend = au_dbend(parent); - p = dinfo->di_hdentry; - for (bindex = 0; bindex <= bend; bindex++, p++) - if (p->hd_dentry) { - dinfo->di_bstart = bindex; - break; - } - - if (dinfo->di_bstart >= 0) { - p = dinfo->di_hdentry + bend; - for (bindex = bend; bindex >= 0; bindex--, p--) - if (p->hd_dentry) { - dinfo->di_bend = bindex; - err = 0; - break; - } - } - - return err; -} - -static void au_do_hide(struct dentry *dentry) -{ - struct inode *inode; - - if (d_really_is_positive(dentry)) { - inode = d_inode(dentry); - if (!d_is_dir(dentry)) { - if (inode->i_nlink && !d_unhashed(dentry)) - drop_nlink(inode); - } else { - clear_nlink(inode); - /* stop next lookup */ - inode->i_flags |= S_DEAD; - } - smp_mb(); /* necessary? */ - } - d_drop(dentry); -} - -static int au_hide_children(struct dentry *parent) -{ - int err, i, j, ndentry; - struct au_dcsub_pages dpages; - struct au_dpage *dpage; - struct dentry *dentry; - - err = au_dpages_init(&dpages, GFP_NOFS); - if (unlikely(err)) - goto out; - err = au_dcsub_pages(&dpages, parent, NULL, NULL); - if (unlikely(err)) - goto out_dpages; - - /* in reverse order */ - for (i = dpages.ndpage - 1; i >= 0; i--) { - dpage = dpages.dpages + i; - ndentry = dpage->ndentry; - for (j = ndentry - 1; j >= 0; j--) { - dentry = dpage->dentries[j]; - if (dentry != parent) - au_do_hide(dentry); - } - } - -out_dpages: - au_dpages_free(&dpages); -out: - return err; -} - -static void au_hide(struct dentry *dentry) -{ - int err; - - AuDbgDentry(dentry); - if (d_is_dir(dentry)) { - /* shrink_dcache_parent(dentry); */ - err = au_hide_children(dentry); - if (unlikely(err)) - AuIOErr("%pd, failed hiding children, ignored %d\n", - dentry, err); - } - au_do_hide(dentry); -} - -/* - * By adding a dirty branch, a cached dentry may be affected in various ways. - * - * a dirty branch is added - * - on the top of layers - * - in the middle of layers - * - to the bottom of layers - * - * on the added branch there exists - * - a whiteout - * - a diropq - * - a same named entry - * + exist - * * negative --> positive - * * positive --> positive - * - type is unchanged - * - type is changed - * + doesn't exist - * * negative --> negative - * * positive --> negative (rejected by au_br_del() for non-dir case) - * - none - */ -static int au_refresh_by_dinfo(struct dentry *dentry, struct au_dinfo *dinfo, - struct au_dinfo *tmp) -{ - int err; - aufs_bindex_t bindex, bend; - struct { - struct dentry *dentry; - struct inode *inode; - mode_t mode; - } orig_h, tmp_h; - struct au_hdentry *hd; - struct inode *inode, *h_inode; - struct dentry *h_dentry; - - err = 0; - AuDebugOn(dinfo->di_bstart < 0); - orig_h.mode = 0; - orig_h.dentry = dinfo->di_hdentry[dinfo->di_bstart].hd_dentry; - orig_h.inode = NULL; - if (d_is_positive(orig_h.dentry)) { - orig_h.inode = d_inode(orig_h.dentry); - orig_h.mode = orig_h.inode->i_mode & S_IFMT; - } - memset(&tmp_h, 0, sizeof(tmp_h)); - if (tmp->di_bstart >= 0) { - tmp_h.dentry = tmp->di_hdentry[tmp->di_bstart].hd_dentry; - tmp_h.inode = NULL; - if (d_is_positive(tmp_h.dentry)) { - tmp_h.inode = d_inode(tmp_h.dentry); - tmp_h.mode = tmp_h.inode->i_mode & S_IFMT; - } - } - - inode = NULL; - if (d_really_is_positive(dentry)) - inode = d_inode(dentry); - if (!orig_h.inode) { - AuDbg("nagative originally\n"); - if (inode) { - au_hide(dentry); - goto out; - } - AuDebugOn(inode); - AuDebugOn(dinfo->di_bstart != dinfo->di_bend); - AuDebugOn(dinfo->di_bdiropq != -1); - - if (!tmp_h.inode) { - AuDbg("negative --> negative\n"); - /* should have only one negative lower */ - if (tmp->di_bstart >= 0 - && tmp->di_bstart < dinfo->di_bstart) { - AuDebugOn(tmp->di_bstart != tmp->di_bend); - AuDebugOn(dinfo->di_bstart != dinfo->di_bend); - au_set_h_dptr(dentry, dinfo->di_bstart, NULL); - au_di_cp(dinfo, tmp); - hd = tmp->di_hdentry + tmp->di_bstart; - au_set_h_dptr(dentry, tmp->di_bstart, - dget(hd->hd_dentry)); - } - au_dbg_verify_dinode(dentry); - } else { - AuDbg("negative --> positive\n"); - /* - * similar to the behaviour of creating with bypassing - * aufs. - * unhash it in order to force an error in the - * succeeding create operation. - * we should not set S_DEAD here. - */ - d_drop(dentry); - /* au_di_swap(tmp, dinfo); */ - au_dbg_verify_dinode(dentry); - } - } else { - AuDbg("positive originally\n"); - /* inode may be NULL */ - AuDebugOn(inode && (inode->i_mode & S_IFMT) != orig_h.mode); - if (!tmp_h.inode) { - AuDbg("positive --> negative\n"); - /* or bypassing aufs */ - au_hide(dentry); - if (tmp->di_bwh >= 0 && tmp->di_bwh <= dinfo->di_bstart) - dinfo->di_bwh = tmp->di_bwh; - if (inode) - err = au_refresh_hinode_self(inode); - au_dbg_verify_dinode(dentry); - } else if (orig_h.mode == tmp_h.mode) { - AuDbg("positive --> positive, same type\n"); - if (!S_ISDIR(orig_h.mode) - && dinfo->di_bstart > tmp->di_bstart) { - /* - * similar to the behaviour of removing and - * creating. - */ - au_hide(dentry); - if (inode) - err = au_refresh_hinode_self(inode); - au_dbg_verify_dinode(dentry); - } else { - /* fill empty slots */ - if (dinfo->di_bstart > tmp->di_bstart) - dinfo->di_bstart = tmp->di_bstart; - if (dinfo->di_bend < tmp->di_bend) - dinfo->di_bend = tmp->di_bend; - dinfo->di_bwh = tmp->di_bwh; - dinfo->di_bdiropq = tmp->di_bdiropq; - hd = tmp->di_hdentry; - bend = dinfo->di_bend; - for (bindex = tmp->di_bstart; bindex <= bend; - bindex++) { - if (au_h_dptr(dentry, bindex)) - continue; - h_dentry = hd[bindex].hd_dentry; - if (!h_dentry) - continue; - AuDebugOn(d_is_negative(h_dentry)); - h_inode = d_inode(h_dentry); - AuDebugOn(orig_h.mode - != (h_inode->i_mode - & S_IFMT)); - au_set_h_dptr(dentry, bindex, - dget(h_dentry)); - } - err = au_refresh_hinode(inode, dentry); - au_dbg_verify_dinode(dentry); - } - } else { - AuDbg("positive --> positive, different type\n"); - /* similar to the behaviour of removing and creating */ - au_hide(dentry); - if (inode) - err = au_refresh_hinode_self(inode); - au_dbg_verify_dinode(dentry); - } - } - -out: - return err; -} - -int au_refresh_dentry(struct dentry *dentry, struct dentry *parent) -{ - int err, ebrange; - unsigned int sigen; - struct au_dinfo *dinfo, *tmp; - struct super_block *sb; - struct inode *inode; - - DiMustWriteLock(dentry); - AuDebugOn(IS_ROOT(dentry)); - AuDebugOn(d_really_is_negative(parent)); - - sb = dentry->d_sb; - sigen = au_sigen(sb); - err = au_digen_test(parent, sigen); - if (unlikely(err)) - goto out; - - dinfo = au_di(dentry); - err = au_di_realloc(dinfo, au_sbend(sb) + 1); - if (unlikely(err)) - goto out; - ebrange = au_dbrange_test(dentry); - if (!ebrange) - ebrange = au_do_refresh_hdentry(dentry, parent); - - if (d_unhashed(dentry) || ebrange /* || dinfo->di_tmpfile */) { - AuDebugOn(au_dbstart(dentry) < 0 && au_dbend(dentry) >= 0); - if (d_really_is_positive(dentry)) { - inode = d_inode(dentry); - err = au_refresh_hinode_self(inode); - } - au_dbg_verify_dinode(dentry); - if (!err) - goto out_dgen; /* success */ - goto out; - } - - /* temporary dinfo */ - AuDbgDentry(dentry); - err = -ENOMEM; - tmp = au_di_alloc(sb, AuLsc_DI_TMP); - if (unlikely(!tmp)) - goto out; - au_di_swap(tmp, dinfo); - /* returns the number of positive dentries */ - /* - * if current working dir is removed, it returns an error. - * but the dentry is legal. - */ - err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); - AuDbgDentry(dentry); - au_di_swap(tmp, dinfo); - if (err == -ENOENT) - err = 0; - if (err >= 0) { - /* compare/refresh by dinfo */ - AuDbgDentry(dentry); - err = au_refresh_by_dinfo(dentry, dinfo, tmp); - au_dbg_verify_dinode(dentry); - AuTraceErr(err); - } - au_rw_write_unlock(&tmp->di_rwsem); - au_di_free(tmp); - if (unlikely(err)) - goto out; - -out_dgen: - au_update_digen(dentry); -out: - if (unlikely(err && !(dentry->d_flags & DCACHE_NFSFS_RENAMED))) { - AuIOErr("failed refreshing %pd, %d\n", dentry, err); - AuDbgDentry(dentry); - } - AuTraceErr(err); - return err; -} - -static int au_do_h_d_reval(struct dentry *h_dentry, unsigned int flags, - struct dentry *dentry, aufs_bindex_t bindex) -{ - int err, valid; - - err = 0; - if (!(h_dentry->d_flags & DCACHE_OP_REVALIDATE)) - goto out; - - AuDbg("b%d\n", bindex); - /* - * gave up supporting LOOKUP_CREATE/OPEN for lower fs, - * due to whiteout and branch permission. - */ - flags &= ~(/*LOOKUP_PARENT |*/ LOOKUP_OPEN | LOOKUP_CREATE - | LOOKUP_FOLLOW | LOOKUP_EXCL); - /* it may return tri-state */ - valid = h_dentry->d_op->d_revalidate(h_dentry, flags); - - if (unlikely(valid < 0)) - err = valid; - else if (!valid) - err = -EINVAL; - -out: - AuTraceErr(err); - return err; -} - -/* todo: remove this */ -static int h_d_revalidate(struct dentry *dentry, struct inode *inode, - unsigned int flags, int do_udba) -{ - int err; - umode_t mode, h_mode; - aufs_bindex_t bindex, btail, bstart, ibs, ibe; - unsigned char plus, unhashed, is_root, h_plus, h_nfs, tmpfile; - struct inode *h_inode, *h_cached_inode; - struct dentry *h_dentry; - struct qstr *name, *h_name; - - err = 0; - plus = 0; - mode = 0; - ibs = -1; - ibe = -1; - unhashed = !!d_unhashed(dentry); - is_root = !!IS_ROOT(dentry); - name = &dentry->d_name; - tmpfile = au_di(dentry)->di_tmpfile; - - /* - * Theoretically, REVAL test should be unnecessary in case of - * {FS,I}NOTIFY. - * But {fs,i}notify doesn't fire some necessary events, - * IN_ATTRIB for atime/nlink/pageio - * Let's do REVAL test too. - */ - if (do_udba && inode) { - mode = (inode->i_mode & S_IFMT); - plus = (inode->i_nlink > 0); - ibs = au_ibstart(inode); - ibe = au_ibend(inode); - } - - bstart = au_dbstart(dentry); - btail = bstart; - if (inode && S_ISDIR(inode->i_mode)) - btail = au_dbtaildir(dentry); - for (bindex = bstart; bindex <= btail; bindex++) { - h_dentry = au_h_dptr(dentry, bindex); - if (!h_dentry) - continue; - - AuDbg("b%d, %pd\n", bindex, h_dentry); - h_nfs = !!au_test_nfs(h_dentry->d_sb); - spin_lock(&h_dentry->d_lock); - h_name = &h_dentry->d_name; - if (unlikely(do_udba - && !is_root - && ((!h_nfs - && (unhashed != !!d_unhashed(h_dentry) - || (!tmpfile - && !au_qstreq(name, h_name)) - )) - || (h_nfs - && !(flags & LOOKUP_OPEN) - && (h_dentry->d_flags - & DCACHE_NFSFS_RENAMED))) - )) { - int h_unhashed; - - h_unhashed = d_unhashed(h_dentry); - spin_unlock(&h_dentry->d_lock); - AuDbg("unhash 0x%x 0x%x, %pd %pd\n", - unhashed, h_unhashed, dentry, h_dentry); - goto err; - } - spin_unlock(&h_dentry->d_lock); - - err = au_do_h_d_reval(h_dentry, flags, dentry, bindex); - if (unlikely(err)) - /* do not goto err, to keep the errno */ - break; - - /* todo: plink too? */ - if (!do_udba) - continue; - - /* UDBA tests */ - if (unlikely(!!inode != d_is_positive(h_dentry))) - goto err; - - h_inode = NULL; - if (d_is_positive(h_dentry)) - h_inode = d_inode(h_dentry); - h_plus = plus; - h_mode = mode; - h_cached_inode = h_inode; - if (h_inode) { - h_mode = (h_inode->i_mode & S_IFMT); - h_plus = (h_inode->i_nlink > 0); - } - if (inode && ibs <= bindex && bindex <= ibe) - h_cached_inode = au_h_iptr(inode, bindex); - - if (!h_nfs) { - if (unlikely(plus != h_plus && !tmpfile)) - goto err; - } else { - if (unlikely(!(h_dentry->d_flags & DCACHE_NFSFS_RENAMED) - && !is_root - && !IS_ROOT(h_dentry) - && unhashed != d_unhashed(h_dentry))) - goto err; - } - if (unlikely(mode != h_mode - || h_cached_inode != h_inode)) - goto err; - continue; - -err: - err = -EINVAL; - break; - } - - AuTraceErr(err); - return err; -} - -/* todo: consolidate with do_refresh() and au_reval_for_attr() */ -static int simple_reval_dpath(struct dentry *dentry, unsigned int sigen) -{ - int err; - struct dentry *parent; - - if (!au_digen_test(dentry, sigen)) - return 0; - - parent = dget_parent(dentry); - di_read_lock_parent(parent, AuLock_IR); - AuDebugOn(au_digen_test(parent, sigen)); - au_dbg_verify_gen(parent, sigen); - err = au_refresh_dentry(dentry, parent); - di_read_unlock(parent, AuLock_IR); - dput(parent); - AuTraceErr(err); - return err; -} - -int au_reval_dpath(struct dentry *dentry, unsigned int sigen) -{ - int err; - struct dentry *d, *parent; - - if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR)) - return simple_reval_dpath(dentry, sigen); - - /* slow loop, keep it simple and stupid */ - /* cf: au_cpup_dirs() */ - err = 0; - parent = NULL; - while (au_digen_test(dentry, sigen)) { - d = dentry; - while (1) { - dput(parent); - parent = dget_parent(d); - if (!au_digen_test(parent, sigen)) - break; - d = parent; - } - - if (d != dentry) - di_write_lock_child2(d); - - /* someone might update our dentry while we were sleeping */ - if (au_digen_test(d, sigen)) { - /* - * todo: consolidate with simple_reval_dpath(), - * do_refresh() and au_reval_for_attr(). - */ - di_read_lock_parent(parent, AuLock_IR); - err = au_refresh_dentry(d, parent); - di_read_unlock(parent, AuLock_IR); - } - - if (d != dentry) - di_write_unlock(d); - dput(parent); - if (unlikely(err)) - break; - } - - return err; -} - -/* - * if valid returns 1, otherwise 0. - */ -static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags) -{ - int valid, err; - unsigned int sigen; - unsigned char do_udba; - struct super_block *sb; - struct inode *inode; - - /* todo: support rcu-walk? */ - if (flags & LOOKUP_RCU) - return -ECHILD; - - valid = 0; - if (unlikely(!au_di(dentry))) - goto out; - - valid = 1; - sb = dentry->d_sb; - /* - * todo: very ugly - * i_mutex of parent dir may be held, - * but we should not return 'invalid' due to busy. - */ - err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM); - if (unlikely(err)) { - valid = err; - AuTraceErr(err); - goto out; - } - inode = NULL; - if (d_really_is_positive(dentry)) - inode = d_inode(dentry); - if (unlikely(inode && is_bad_inode(inode))) { - err = -EINVAL; - AuTraceErr(err); - goto out_dgrade; - } - if (unlikely(au_dbrange_test(dentry))) { - err = -EINVAL; - AuTraceErr(err); - goto out_dgrade; - } - - sigen = au_sigen(sb); - if (au_digen_test(dentry, sigen)) { - AuDebugOn(IS_ROOT(dentry)); - err = au_reval_dpath(dentry, sigen); - if (unlikely(err)) { - AuTraceErr(err); - goto out_dgrade; - } - } - di_downgrade_lock(dentry, AuLock_IR); - - err = -EINVAL; - if (!(flags & (LOOKUP_OPEN | LOOKUP_EMPTY)) - && inode - && !(inode->i_state && I_LINKABLE) - && (IS_DEADDIR(inode) || !inode->i_nlink)) - goto out_inval; - - do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); - if (do_udba && inode) { - aufs_bindex_t bstart = au_ibstart(inode); - struct inode *h_inode; - - if (bstart >= 0) { - h_inode = au_h_iptr(inode, bstart); - if (h_inode && au_test_higen(inode, h_inode)) - goto out_inval; - } - } - - err = h_d_revalidate(dentry, inode, flags, do_udba); - if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) { - err = -EIO; - AuDbg("both of real entry and whiteout found, %p, err %d\n", - dentry, err); - } - goto out_inval; - -out_dgrade: - di_downgrade_lock(dentry, AuLock_IR); -out_inval: - aufs_read_unlock(dentry, AuLock_IR); - AuTraceErr(err); - valid = !err; -out: - if (!valid) { - AuDbg("%pd invalid, %d\n", dentry, valid); - d_drop(dentry); - } - return valid; -} - -static void aufs_d_release(struct dentry *dentry) -{ - if (au_di(dentry)) { - au_di_fin(dentry); - au_hn_di_reinit(dentry); - } -} - -const struct dentry_operations aufs_dop = { - .d_revalidate = aufs_d_revalidate, - .d_weak_revalidate = aufs_d_revalidate, - .d_release = aufs_d_release -}; diff --git a/fs/aufs/dentry.h b/fs/aufs/dentry.h deleted file mode 100644 index 2ce602e1a..000000000 --- a/fs/aufs/dentry.h +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * lookup and dentry operations - */ - -#ifndef __AUFS_DENTRY_H__ -#define __AUFS_DENTRY_H__ - -#ifdef __KERNEL__ - -#include <linux/dcache.h> -#include "rwsem.h" - -struct au_hdentry { - struct dentry *hd_dentry; - aufs_bindex_t hd_id; -}; - -struct au_dinfo { - atomic_t di_generation; - - struct au_rwsem di_rwsem; - aufs_bindex_t di_bstart, di_bend, di_bwh, di_bdiropq; - unsigned char di_tmpfile; /* to allow the different name */ - struct au_hdentry *di_hdentry; -} ____cacheline_aligned_in_smp; - -/* ---------------------------------------------------------------------- */ - -/* dentry.c */ -extern const struct dentry_operations aufs_dop; -struct au_branch; -struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent); -int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, - struct dentry *h_parent, struct au_branch *br); - -int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type); -int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh); -int au_refresh_dentry(struct dentry *dentry, struct dentry *parent); -int au_reval_dpath(struct dentry *dentry, unsigned int sigen); - -/* dinfo.c */ -void au_di_init_once(void *_di); -struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc); -void au_di_free(struct au_dinfo *dinfo); -void au_di_swap(struct au_dinfo *a, struct au_dinfo *b); -void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src); -int au_di_init(struct dentry *dentry); -void au_di_fin(struct dentry *dentry); -int au_di_realloc(struct au_dinfo *dinfo, int nbr); - -void di_read_lock(struct dentry *d, int flags, unsigned int lsc); -void di_read_unlock(struct dentry *d, int flags); -void di_downgrade_lock(struct dentry *d, int flags); -void di_write_lock(struct dentry *d, unsigned int lsc); -void di_write_unlock(struct dentry *d); -void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir); -void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir); -void di_write_unlock2(struct dentry *d1, struct dentry *d2); - -struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex); -struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex); -aufs_bindex_t au_dbtail(struct dentry *dentry); -aufs_bindex_t au_dbtaildir(struct dentry *dentry); - -void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, - struct dentry *h_dentry); -int au_digen_test(struct dentry *dentry, unsigned int sigen); -int au_dbrange_test(struct dentry *dentry); -void au_update_digen(struct dentry *dentry); -void au_update_dbrange(struct dentry *dentry, int do_put_zero); -void au_update_dbstart(struct dentry *dentry); -void au_update_dbend(struct dentry *dentry); -int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry); - -/* ---------------------------------------------------------------------- */ - -static inline struct au_dinfo *au_di(struct dentry *dentry) -{ - return dentry->d_fsdata; -} - -/* ---------------------------------------------------------------------- */ - -/* lock subclass for dinfo */ -enum { - AuLsc_DI_CHILD, /* child first */ - AuLsc_DI_CHILD2, /* rename(2), link(2), and cpup at hnotify */ - AuLsc_DI_CHILD3, /* copyup dirs */ - AuLsc_DI_PARENT, - AuLsc_DI_PARENT2, - AuLsc_DI_PARENT3, - AuLsc_DI_TMP /* temp for replacing dinfo */ -}; - -/* - * di_read_lock_child, di_write_lock_child, - * di_read_lock_child2, di_write_lock_child2, - * di_read_lock_child3, di_write_lock_child3, - * di_read_lock_parent, di_write_lock_parent, - * di_read_lock_parent2, di_write_lock_parent2, - * di_read_lock_parent3, di_write_lock_parent3, - */ -#define AuReadLockFunc(name, lsc) \ -static inline void di_read_lock_##name(struct dentry *d, int flags) \ -{ di_read_lock(d, flags, AuLsc_DI_##lsc); } - -#define AuWriteLockFunc(name, lsc) \ -static inline void di_write_lock_##name(struct dentry *d) \ -{ di_write_lock(d, AuLsc_DI_##lsc); } - -#define AuRWLockFuncs(name, lsc) \ - AuReadLockFunc(name, lsc) \ - AuWriteLockFunc(name, lsc) - -AuRWLockFuncs(child, CHILD); -AuRWLockFuncs(child2, CHILD2); -AuRWLockFuncs(child3, CHILD3); -AuRWLockFuncs(parent, PARENT); -AuRWLockFuncs(parent2, PARENT2); -AuRWLockFuncs(parent3, PARENT3); - -#undef AuReadLockFunc -#undef AuWriteLockFunc -#undef AuRWLockFuncs - -#define DiMustNoWaiters(d) AuRwMustNoWaiters(&au_di(d)->di_rwsem) -#define DiMustAnyLock(d) AuRwMustAnyLock(&au_di(d)->di_rwsem) -#define DiMustWriteLock(d) AuRwMustWriteLock(&au_di(d)->di_rwsem) - -/* ---------------------------------------------------------------------- */ - -/* todo: memory barrier? */ -static inline unsigned int au_digen(struct dentry *d) -{ - return atomic_read(&au_di(d)->di_generation); -} - -static inline void au_h_dentry_init(struct au_hdentry *hdentry) -{ - hdentry->hd_dentry = NULL; -} - -static inline void au_hdput(struct au_hdentry *hd) -{ - if (hd) - dput(hd->hd_dentry); -} - -static inline aufs_bindex_t au_dbstart(struct dentry *dentry) -{ - DiMustAnyLock(dentry); - return au_di(dentry)->di_bstart; -} - -static inline aufs_bindex_t au_dbend(struct dentry *dentry) -{ - DiMustAnyLock(dentry); - return au_di(dentry)->di_bend; -} - -static inline aufs_bindex_t au_dbwh(struct dentry *dentry) -{ - DiMustAnyLock(dentry); - return au_di(dentry)->di_bwh; -} - -static inline aufs_bindex_t au_dbdiropq(struct dentry *dentry) -{ - DiMustAnyLock(dentry); - return au_di(dentry)->di_bdiropq; -} - -/* todo: hard/soft set? */ -static inline void au_set_dbstart(struct dentry *dentry, aufs_bindex_t bindex) -{ - DiMustWriteLock(dentry); - au_di(dentry)->di_bstart = bindex; -} - -static inline void au_set_dbend(struct dentry *dentry, aufs_bindex_t bindex) -{ - DiMustWriteLock(dentry); - au_di(dentry)->di_bend = bindex; -} - -static inline void au_set_dbwh(struct dentry *dentry, aufs_bindex_t bindex) -{ - DiMustWriteLock(dentry); - /* dbwh can be outside of bstart - bend range */ - au_di(dentry)->di_bwh = bindex; -} - -static inline void au_set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex) -{ - DiMustWriteLock(dentry); - au_di(dentry)->di_bdiropq = bindex; -} - -/* ---------------------------------------------------------------------- */ - -#ifdef CONFIG_AUFS_HNOTIFY -static inline void au_digen_dec(struct dentry *d) -{ - atomic_dec(&au_di(d)->di_generation); -} - -static inline void au_hn_di_reinit(struct dentry *dentry) -{ - dentry->d_fsdata = NULL; -} -#else -AuStubVoid(au_hn_di_reinit, struct dentry *dentry __maybe_unused) -#endif /* CONFIG_AUFS_HNOTIFY */ - -#endif /* __KERNEL__ */ -#endif /* __AUFS_DENTRY_H__ */ diff --git a/fs/aufs/dinfo.c b/fs/aufs/dinfo.c deleted file mode 100644 index 7b9ca3b88..000000000 --- a/fs/aufs/dinfo.c +++ /dev/null @@ -1,550 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * dentry private data - */ - -#include "aufs.h" - -void au_di_init_once(void *_dinfo) -{ - struct au_dinfo *dinfo = _dinfo; - static struct lock_class_key aufs_di; - - au_rw_init(&dinfo->di_rwsem); - au_rw_class(&dinfo->di_rwsem, &aufs_di); -} - -struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc) -{ - struct au_dinfo *dinfo; - int nbr, i; - - dinfo = au_cache_alloc_dinfo(); - if (unlikely(!dinfo)) - goto out; - - nbr = au_sbend(sb) + 1; - if (nbr <= 0) - nbr = 1; - dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS); - if (dinfo->di_hdentry) { - au_rw_write_lock_nested(&dinfo->di_rwsem, lsc); - dinfo->di_bstart = -1; - dinfo->di_bend = -1; - dinfo->di_bwh = -1; - dinfo->di_bdiropq = -1; - dinfo->di_tmpfile = 0; - for (i = 0; i < nbr; i++) - dinfo->di_hdentry[i].hd_id = -1; - goto out; - } - - au_cache_free_dinfo(dinfo); - dinfo = NULL; - -out: - return dinfo; -} - -void au_di_free(struct au_dinfo *dinfo) -{ - struct au_hdentry *p; - aufs_bindex_t bend, bindex; - - /* dentry may not be revalidated */ - bindex = dinfo->di_bstart; - if (bindex >= 0) { - bend = dinfo->di_bend; - p = dinfo->di_hdentry + bindex; - while (bindex++ <= bend) - au_hdput(p++); - } - kfree(dinfo->di_hdentry); - au_cache_free_dinfo(dinfo); -} - -void au_di_swap(struct au_dinfo *a, struct au_dinfo *b) -{ - struct au_hdentry *p; - aufs_bindex_t bi; - - AuRwMustWriteLock(&a->di_rwsem); - AuRwMustWriteLock(&b->di_rwsem); - -#define DiSwap(v, name) \ - do { \ - v = a->di_##name; \ - a->di_##name = b->di_##name; \ - b->di_##name = v; \ - } while (0) - - DiSwap(p, hdentry); - DiSwap(bi, bstart); - DiSwap(bi, bend); - DiSwap(bi, bwh); - DiSwap(bi, bdiropq); - /* smp_mb(); */ - -#undef DiSwap -} - -void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src) -{ - AuRwMustWriteLock(&dst->di_rwsem); - AuRwMustWriteLock(&src->di_rwsem); - - dst->di_bstart = src->di_bstart; - dst->di_bend = src->di_bend; - dst->di_bwh = src->di_bwh; - dst->di_bdiropq = src->di_bdiropq; - /* smp_mb(); */ -} - -int au_di_init(struct dentry *dentry) -{ - int err; - struct super_block *sb; - struct au_dinfo *dinfo; - - err = 0; - sb = dentry->d_sb; - dinfo = au_di_alloc(sb, AuLsc_DI_CHILD); - if (dinfo) { - atomic_set(&dinfo->di_generation, au_sigen(sb)); - /* smp_mb(); */ /* atomic_set */ - dentry->d_fsdata = dinfo; - } else - err = -ENOMEM; - - return err; -} - -void au_di_fin(struct dentry *dentry) -{ - struct au_dinfo *dinfo; - - dinfo = au_di(dentry); - AuRwDestroy(&dinfo->di_rwsem); - au_di_free(dinfo); -} - -int au_di_realloc(struct au_dinfo *dinfo, int nbr) -{ - int err, sz; - struct au_hdentry *hdp; - - AuRwMustWriteLock(&dinfo->di_rwsem); - - err = -ENOMEM; - sz = sizeof(*hdp) * (dinfo->di_bend + 1); - if (!sz) - sz = sizeof(*hdp); - hdp = au_kzrealloc(dinfo->di_hdentry, sz, sizeof(*hdp) * nbr, GFP_NOFS); - if (hdp) { - dinfo->di_hdentry = hdp; - err = 0; - } - - return err; -} - -/* ---------------------------------------------------------------------- */ - -static void do_ii_write_lock(struct inode *inode, unsigned int lsc) -{ - switch (lsc) { - case AuLsc_DI_CHILD: - ii_write_lock_child(inode); - break; - case AuLsc_DI_CHILD2: - ii_write_lock_child2(inode); - break; - case AuLsc_DI_CHILD3: - ii_write_lock_child3(inode); - break; - case AuLsc_DI_PARENT: - ii_write_lock_parent(inode); - break; - case AuLsc_DI_PARENT2: - ii_write_lock_parent2(inode); - break; - case AuLsc_DI_PARENT3: - ii_write_lock_parent3(inode); - break; - default: - BUG(); - } -} - -static void do_ii_read_lock(struct inode *inode, unsigned int lsc) -{ - switch (lsc) { - case AuLsc_DI_CHILD: - ii_read_lock_child(inode); - break; - case AuLsc_DI_CHILD2: - ii_read_lock_child2(inode); - break; - case AuLsc_DI_CHILD3: - ii_read_lock_child3(inode); - break; - case AuLsc_DI_PARENT: - ii_read_lock_parent(inode); - break; - case AuLsc_DI_PARENT2: - ii_read_lock_parent2(inode); - break; - case AuLsc_DI_PARENT3: - ii_read_lock_parent3(inode); - break; - default: - BUG(); - } -} - -void di_read_lock(struct dentry *d, int flags, unsigned int lsc) -{ - struct inode *inode; - - au_rw_read_lock_nested(&au_di(d)->di_rwsem, lsc); - if (d_really_is_positive(d)) { - inode = d_inode(d); - if (au_ftest_lock(flags, IW)) - do_ii_write_lock(inode, lsc); - else if (au_ftest_lock(flags, IR)) - do_ii_read_lock(inode, lsc); - } -} - -void di_read_unlock(struct dentry *d, int flags) -{ - struct inode *inode; - - if (d_really_is_positive(d)) { - inode = d_inode(d); - if (au_ftest_lock(flags, IW)) { - au_dbg_verify_dinode(d); - ii_write_unlock(inode); - } else if (au_ftest_lock(flags, IR)) { - au_dbg_verify_dinode(d); - ii_read_unlock(inode); - } - } - au_rw_read_unlock(&au_di(d)->di_rwsem); -} - -void di_downgrade_lock(struct dentry *d, int flags) -{ - if (d_really_is_positive(d) && au_ftest_lock(flags, IR)) - ii_downgrade_lock(d_inode(d)); - au_rw_dgrade_lock(&au_di(d)->di_rwsem); -} - -void di_write_lock(struct dentry *d, unsigned int lsc) -{ - au_rw_write_lock_nested(&au_di(d)->di_rwsem, lsc); - if (d_really_is_positive(d)) - do_ii_write_lock(d_inode(d), lsc); -} - -void di_write_unlock(struct dentry *d) -{ - au_dbg_verify_dinode(d); - if (d_really_is_positive(d)) - ii_write_unlock(d_inode(d)); - au_rw_write_unlock(&au_di(d)->di_rwsem); -} - -void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir) -{ - AuDebugOn(d1 == d2 - || d_inode(d1) == d_inode(d2) - || d1->d_sb != d2->d_sb); - - if (isdir && au_test_subdir(d1, d2)) { - di_write_lock_child(d1); - di_write_lock_child2(d2); - } else { - /* there should be no races */ - di_write_lock_child(d2); - di_write_lock_child2(d1); - } -} - -void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir) -{ - AuDebugOn(d1 == d2 - || d_inode(d1) == d_inode(d2) - || d1->d_sb != d2->d_sb); - - if (isdir && au_test_subdir(d1, d2)) { - di_write_lock_parent(d1); - di_write_lock_parent2(d2); - } else { - /* there should be no races */ - di_write_lock_parent(d2); - di_write_lock_parent2(d1); - } -} - -void di_write_unlock2(struct dentry *d1, struct dentry *d2) -{ - di_write_unlock(d1); - if (d_inode(d1) == d_inode(d2)) - au_rw_write_unlock(&au_di(d2)->di_rwsem); - else - di_write_unlock(d2); -} - -/* ---------------------------------------------------------------------- */ - -struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex) -{ - struct dentry *d; - - DiMustAnyLock(dentry); - - if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) - return NULL; - AuDebugOn(bindex < 0); - d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry; - AuDebugOn(d && au_dcount(d) <= 0); - return d; -} - -/* - * extended version of au_h_dptr(). - * returns a hashed and positive (or linkable) h_dentry in bindex, NULL, or - * error. - */ -struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex) -{ - struct dentry *h_dentry; - struct inode *inode, *h_inode; - - AuDebugOn(d_really_is_negative(dentry)); - - h_dentry = NULL; - if (au_dbstart(dentry) <= bindex - && bindex <= au_dbend(dentry)) - h_dentry = au_h_dptr(dentry, bindex); - if (h_dentry && !au_d_linkable(h_dentry)) { - dget(h_dentry); - goto out; /* success */ - } - - inode = d_inode(dentry); - AuDebugOn(bindex < au_ibstart(inode)); - AuDebugOn(au_ibend(inode) < bindex); - h_inode = au_h_iptr(inode, bindex); - h_dentry = d_find_alias(h_inode); - if (h_dentry) { - if (!IS_ERR(h_dentry)) { - if (!au_d_linkable(h_dentry)) - goto out; /* success */ - dput(h_dentry); - } else - goto out; - } - - if (au_opt_test(au_mntflags(dentry->d_sb), PLINK)) { - h_dentry = au_plink_lkup(inode, bindex); - AuDebugOn(!h_dentry); - if (!IS_ERR(h_dentry)) { - if (!au_d_hashed_positive(h_dentry)) - goto out; /* success */ - dput(h_dentry); - h_dentry = NULL; - } - } - -out: - AuDbgDentry(h_dentry); - return h_dentry; -} - -aufs_bindex_t au_dbtail(struct dentry *dentry) -{ - aufs_bindex_t bend, bwh; - - bend = au_dbend(dentry); - if (0 <= bend) { - bwh = au_dbwh(dentry); - if (!bwh) - return bwh; - if (0 < bwh && bwh < bend) - return bwh - 1; - } - return bend; -} - -aufs_bindex_t au_dbtaildir(struct dentry *dentry) -{ - aufs_bindex_t bend, bopq; - - bend = au_dbtail(dentry); - if (0 <= bend) { - bopq = au_dbdiropq(dentry); - if (0 <= bopq && bopq < bend) - bend = bopq; - } - return bend; -} - -/* ---------------------------------------------------------------------- */ - -void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, - struct dentry *h_dentry) -{ - struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex; - struct au_branch *br; - - DiMustWriteLock(dentry); - - au_hdput(hd); - hd->hd_dentry = h_dentry; - if (h_dentry) { - br = au_sbr(dentry->d_sb, bindex); - hd->hd_id = br->br_id; - } -} - -int au_dbrange_test(struct dentry *dentry) -{ - int err; - aufs_bindex_t bstart, bend; - - err = 0; - bstart = au_dbstart(dentry); - bend = au_dbend(dentry); - if (bstart >= 0) - AuDebugOn(bend < 0 && bstart > bend); - else { - err = -EIO; - AuDebugOn(bend >= 0); - } - - return err; -} - -int au_digen_test(struct dentry *dentry, unsigned int sigen) -{ - int err; - - err = 0; - if (unlikely(au_digen(dentry) != sigen - || au_iigen_test(d_inode(dentry), sigen))) - err = -EIO; - - return err; -} - -void au_update_digen(struct dentry *dentry) -{ - atomic_set(&au_di(dentry)->di_generation, au_sigen(dentry->d_sb)); - /* smp_mb(); */ /* atomic_set */ -} - -void au_update_dbrange(struct dentry *dentry, int do_put_zero) -{ - struct au_dinfo *dinfo; - struct dentry *h_d; - struct au_hdentry *hdp; - - DiMustWriteLock(dentry); - - dinfo = au_di(dentry); - if (!dinfo || dinfo->di_bstart < 0) - return; - - hdp = dinfo->di_hdentry; - if (do_put_zero) { - aufs_bindex_t bindex, bend; - - bend = dinfo->di_bend; - for (bindex = dinfo->di_bstart; bindex <= bend; bindex++) { - h_d = hdp[0 + bindex].hd_dentry; - if (h_d && d_is_negative(h_d)) - au_set_h_dptr(dentry, bindex, NULL); - } - } - - dinfo->di_bstart = -1; - while (++dinfo->di_bstart <= dinfo->di_bend) - if (hdp[0 + dinfo->di_bstart].hd_dentry) - break; - if (dinfo->di_bstart > dinfo->di_bend) { - dinfo->di_bstart = -1; - dinfo->di_bend = -1; - return; - } - - dinfo->di_bend++; - while (0 <= --dinfo->di_bend) - if (hdp[0 + dinfo->di_bend].hd_dentry) - break; - AuDebugOn(dinfo->di_bstart > dinfo->di_bend || dinfo->di_bend < 0); -} - -void au_update_dbstart(struct dentry *dentry) -{ - aufs_bindex_t bindex, bend; - struct dentry *h_dentry; - - bend = au_dbend(dentry); - for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { - h_dentry = au_h_dptr(dentry, bindex); - if (!h_dentry) - continue; - if (d_is_positive(h_dentry)) { - au_set_dbstart(dentry, bindex); - return; - } - au_set_h_dptr(dentry, bindex, NULL); - } -} - -void au_update_dbend(struct dentry *dentry) -{ - aufs_bindex_t bindex, bstart; - struct dentry *h_dentry; - - bstart = au_dbstart(dentry); - for (bindex = au_dbend(dentry); bindex >= bstart; bindex--) { - h_dentry = au_h_dptr(dentry, bindex); - if (!h_dentry) - continue; - if (d_is_positive(h_dentry)) { - au_set_dbend(dentry, bindex); - return; - } - au_set_h_dptr(dentry, bindex, NULL); - } -} - -int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry) -{ - aufs_bindex_t bindex, bend; - - bend = au_dbend(dentry); - for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) - if (au_h_dptr(dentry, bindex) == h_dentry) - return bindex; - return -1; -} diff --git a/fs/aufs/dir.c b/fs/aufs/dir.c deleted file mode 100644 index 7705c2997..000000000 --- a/fs/aufs/dir.c +++ /dev/null @@ -1,753 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * directory operations - */ - -#include <linux/fs_stack.h> -#include "aufs.h" - -void au_add_nlink(struct inode *dir, struct inode *h_dir) -{ - unsigned int nlink; - - AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); - - nlink = dir->i_nlink; - nlink += h_dir->i_nlink - 2; - if (h_dir->i_nlink < 2) - nlink += 2; - smp_mb(); /* for i_nlink */ - /* 0 can happen in revaliding */ - set_nlink(dir, nlink); -} - -void au_sub_nlink(struct inode *dir, struct inode *h_dir) -{ - unsigned int nlink; - - AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); - - nlink = dir->i_nlink; - nlink -= h_dir->i_nlink - 2; - if (h_dir->i_nlink < 2) - nlink -= 2; - smp_mb(); /* for i_nlink */ - /* nlink == 0 means the branch-fs is broken */ - set_nlink(dir, nlink); -} - -loff_t au_dir_size(struct file *file, struct dentry *dentry) -{ - loff_t sz; - aufs_bindex_t bindex, bend; - struct file *h_file; - struct dentry *h_dentry; - - sz = 0; - if (file) { - AuDebugOn(!d_is_dir(file->f_path.dentry)); - - bend = au_fbend_dir(file); - for (bindex = au_fbstart(file); - bindex <= bend && sz < KMALLOC_MAX_SIZE; - bindex++) { - h_file = au_hf_dir(file, bindex); - if (h_file && file_inode(h_file)) - sz += vfsub_f_size_read(h_file); - } - } else { - AuDebugOn(!dentry); - AuDebugOn(!d_is_dir(dentry)); - - bend = au_dbtaildir(dentry); - for (bindex = au_dbstart(dentry); - bindex <= bend && sz < KMALLOC_MAX_SIZE; - bindex++) { - h_dentry = au_h_dptr(dentry, bindex); - if (h_dentry && d_is_positive(h_dentry)) - sz += i_size_read(d_inode(h_dentry)); - } - } - if (sz < KMALLOC_MAX_SIZE) - sz = roundup_pow_of_two(sz); - if (sz > KMALLOC_MAX_SIZE) - sz = KMALLOC_MAX_SIZE; - else if (sz < NAME_MAX) { - BUILD_BUG_ON(AUFS_RDBLK_DEF < NAME_MAX); - sz = AUFS_RDBLK_DEF; - } - return sz; -} - -struct au_dir_ts_arg { - struct dentry *dentry; - aufs_bindex_t brid; -}; - -static void au_do_dir_ts(void *arg) -{ - struct au_dir_ts_arg *a = arg; - struct au_dtime dt; - struct path h_path; - struct inode *dir, *h_dir; - struct super_block *sb; - struct au_branch *br; - struct au_hinode *hdir; - int err; - aufs_bindex_t bstart, bindex; - - sb = a->dentry->d_sb; - if (d_really_is_negative(a->dentry)) - goto out; - aufs_read_lock(a->dentry, AuLock_DW | AuLock_DIR); /* noflush */ - - /* no dir->i_mutex lock */ - dir = d_inode(a->dentry); - bstart = au_ibstart(dir); - bindex = au_br_index(sb, a->brid); - if (bindex < bstart) - goto out_unlock; - - br = au_sbr(sb, bindex); - h_path.dentry = au_h_dptr(a->dentry, bindex); - if (!h_path.dentry) - goto out_unlock; - h_path.mnt = au_br_mnt(br); - au_dtime_store(&dt, a->dentry, &h_path); - - br = au_sbr(sb, bstart); - if (!au_br_writable(br->br_perm)) - goto out_unlock; - h_path.dentry = au_h_dptr(a->dentry, bstart); - h_path.mnt = au_br_mnt(br); - err = vfsub_mnt_want_write(h_path.mnt); - if (err) - goto out_unlock; - hdir = au_hi(dir, bstart); - au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); - h_dir = au_h_iptr(dir, bstart); - if (h_dir->i_nlink - && timespec_compare(&h_dir->i_mtime, &dt.dt_mtime) < 0) { - dt.dt_h_path = h_path; - au_dtime_revert(&dt); - } - au_hn_imtx_unlock(hdir); - vfsub_mnt_drop_write(h_path.mnt); - au_cpup_attr_timesizes(dir); - -out_unlock: - aufs_read_unlock(a->dentry, AuLock_DW); -out: - dput(a->dentry); - au_nwt_done(&au_sbi(sb)->si_nowait); - kfree(arg); -} - -void au_dir_ts(struct inode *dir, aufs_bindex_t bindex) -{ - int perm, wkq_err; - aufs_bindex_t bstart; - struct au_dir_ts_arg *arg; - struct dentry *dentry; - struct super_block *sb; - - IMustLock(dir); - - dentry = d_find_any_alias(dir); - AuDebugOn(!dentry); - sb = dentry->d_sb; - bstart = au_ibstart(dir); - if (bstart == bindex) { - au_cpup_attr_timesizes(dir); - goto out; - } - - perm = au_sbr_perm(sb, bstart); - if (!au_br_writable(perm)) - goto out; - - arg = kmalloc(sizeof(*arg), GFP_NOFS); - if (!arg) - goto out; - - arg->dentry = dget(dentry); /* will be dput-ted by au_do_dir_ts() */ - arg->brid = au_sbr_id(sb, bindex); - wkq_err = au_wkq_nowait(au_do_dir_ts, arg, sb, /*flags*/0); - if (unlikely(wkq_err)) { - pr_err("wkq %d\n", wkq_err); - dput(dentry); - kfree(arg); - } - -out: - dput(dentry); -} - -/* ---------------------------------------------------------------------- */ - -static int reopen_dir(struct file *file) -{ - int err; - unsigned int flags; - aufs_bindex_t bindex, btail, bstart; - struct dentry *dentry, *h_dentry; - struct file *h_file; - - /* open all lower dirs */ - dentry = file->f_path.dentry; - bstart = au_dbstart(dentry); - for (bindex = au_fbstart(file); bindex < bstart; bindex++) - au_set_h_fptr(file, bindex, NULL); - au_set_fbstart(file, bstart); - - btail = au_dbtaildir(dentry); - for (bindex = au_fbend_dir(file); btail < bindex; bindex--) - au_set_h_fptr(file, bindex, NULL); - au_set_fbend_dir(file, btail); - - flags = vfsub_file_flags(file); - for (bindex = bstart; bindex <= btail; bindex++) { - h_dentry = au_h_dptr(dentry, bindex); - if (!h_dentry) - continue; - h_file = au_hf_dir(file, bindex); - if (h_file) - continue; - - h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); - err = PTR_ERR(h_file); - if (IS_ERR(h_file)) - goto out; /* close all? */ - au_set_h_fptr(file, bindex, h_file); - } - au_update_figen(file); - /* todo: necessary? */ - /* file->f_ra = h_file->f_ra; */ - err = 0; - -out: - return err; -} - -static int do_open_dir(struct file *file, int flags, struct file *h_file) -{ - int err; - aufs_bindex_t bindex, btail; - struct dentry *dentry, *h_dentry; - - FiMustWriteLock(file); - AuDebugOn(h_file); - - err = 0; - dentry = file->f_path.dentry; - file->f_version = d_inode(dentry)->i_version; - bindex = au_dbstart(dentry); - au_set_fbstart(file, bindex); - btail = au_dbtaildir(dentry); - au_set_fbend_dir(file, btail); - for (; !err && bindex <= btail; bindex++) { - h_dentry = au_h_dptr(dentry, bindex); - if (!h_dentry) - continue; - - h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); - if (IS_ERR(h_file)) { - err = PTR_ERR(h_file); - break; - } - au_set_h_fptr(file, bindex, h_file); - } - au_update_figen(file); - /* todo: necessary? */ - /* file->f_ra = h_file->f_ra; */ - if (!err) - return 0; /* success */ - - /* close all */ - for (bindex = au_fbstart(file); bindex <= btail; bindex++) - au_set_h_fptr(file, bindex, NULL); - au_set_fbstart(file, -1); - au_set_fbend_dir(file, -1); - - return err; -} - -static int aufs_open_dir(struct inode *inode __maybe_unused, - struct file *file) -{ - int err; - struct super_block *sb; - struct au_fidir *fidir; - - err = -ENOMEM; - sb = file->f_path.dentry->d_sb; - si_read_lock(sb, AuLock_FLUSH); - fidir = au_fidir_alloc(sb); - if (fidir) { - struct au_do_open_args args = { - .open = do_open_dir, - .fidir = fidir - }; - err = au_do_open(file, &args); - if (unlikely(err)) - kfree(fidir); - } - si_read_unlock(sb); - return err; -} - -static int aufs_release_dir(struct inode *inode __maybe_unused, - struct file *file) -{ - struct au_vdir *vdir_cache; - struct au_finfo *finfo; - struct au_fidir *fidir; - aufs_bindex_t bindex, bend; - - finfo = au_fi(file); - fidir = finfo->fi_hdir; - if (fidir) { - au_sphl_del(&finfo->fi_hlist, - &au_sbi(file->f_path.dentry->d_sb)->si_files); - vdir_cache = fidir->fd_vdir_cache; /* lock-free */ - if (vdir_cache) - au_vdir_free(vdir_cache); - - bindex = finfo->fi_btop; - if (bindex >= 0) { - /* - * calls fput() instead of filp_close(), - * since no dnotify or lock for the lower file. - */ - bend = fidir->fd_bbot; - for (; bindex <= bend; bindex++) - au_set_h_fptr(file, bindex, NULL); - } - kfree(fidir); - finfo->fi_hdir = NULL; - } - au_finfo_fin(file); - return 0; -} - -/* ---------------------------------------------------------------------- */ - -static int au_do_flush_dir(struct file *file, fl_owner_t id) -{ - int err; - aufs_bindex_t bindex, bend; - struct file *h_file; - - err = 0; - bend = au_fbend_dir(file); - for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { - h_file = au_hf_dir(file, bindex); - if (h_file) - err = vfsub_flush(h_file, id); - } - return err; -} - -static int aufs_flush_dir(struct file *file, fl_owner_t id) -{ - return au_do_flush(file, id, au_do_flush_dir); -} - -/* ---------------------------------------------------------------------- */ - -static int au_do_fsync_dir_no_file(struct dentry *dentry, int datasync) -{ - int err; - aufs_bindex_t bend, bindex; - struct inode *inode; - struct super_block *sb; - - err = 0; - sb = dentry->d_sb; - inode = d_inode(dentry); - IMustLock(inode); - bend = au_dbend(dentry); - for (bindex = au_dbstart(dentry); !err && bindex <= bend; bindex++) { - struct path h_path; - - if (au_test_ro(sb, bindex, inode)) - continue; - h_path.dentry = au_h_dptr(dentry, bindex); - if (!h_path.dentry) - continue; - - h_path.mnt = au_sbr_mnt(sb, bindex); - err = vfsub_fsync(NULL, &h_path, datasync); - } - - return err; -} - -static int au_do_fsync_dir(struct file *file, int datasync) -{ - int err; - aufs_bindex_t bend, bindex; - struct file *h_file; - struct super_block *sb; - struct inode *inode; - - err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); - if (unlikely(err)) - goto out; - - inode = file_inode(file); - sb = inode->i_sb; - bend = au_fbend_dir(file); - for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { - h_file = au_hf_dir(file, bindex); - if (!h_file || au_test_ro(sb, bindex, inode)) - continue; - - err = vfsub_fsync(h_file, &h_file->f_path, datasync); - } - -out: - return err; -} - -/* - * @file may be NULL - */ -static int aufs_fsync_dir(struct file *file, loff_t start, loff_t end, - int datasync) -{ - int err; - struct dentry *dentry; - struct inode *inode; - struct super_block *sb; - struct mutex *mtx; - - err = 0; - dentry = file->f_path.dentry; - inode = d_inode(dentry); - mtx = &inode->i_mutex; - mutex_lock(mtx); - sb = dentry->d_sb; - si_noflush_read_lock(sb); - if (file) - err = au_do_fsync_dir(file, datasync); - else { - di_write_lock_child(dentry); - err = au_do_fsync_dir_no_file(dentry, datasync); - } - au_cpup_attr_timesizes(inode); - di_write_unlock(dentry); - if (file) - fi_write_unlock(file); - - si_read_unlock(sb); - mutex_unlock(mtx); - return err; -} - -/* ---------------------------------------------------------------------- */ - -static int aufs_iterate(struct file *file, struct dir_context *ctx) -{ - int err; - struct dentry *dentry; - struct inode *inode, *h_inode; - struct super_block *sb; - - AuDbg("%pD, ctx{%pf, %llu}\n", file, ctx->actor, ctx->pos); - - dentry = file->f_path.dentry; - inode = d_inode(dentry); - IMustLock(inode); - - sb = dentry->d_sb; - si_read_lock(sb, AuLock_FLUSH); - err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); - if (unlikely(err)) - goto out; - err = au_alive_dir(dentry); - if (!err) - err = au_vdir_init(file); - di_downgrade_lock(dentry, AuLock_IR); - if (unlikely(err)) - goto out_unlock; - - h_inode = au_h_iptr(inode, au_ibstart(inode)); - if (!au_test_nfsd()) { - err = au_vdir_fill_de(file, ctx); - fsstack_copy_attr_atime(inode, h_inode); - } else { - /* - * nfsd filldir may call lookup_one_len(), vfs_getattr(), - * encode_fh() and others. - */ - atomic_inc(&h_inode->i_count); - di_read_unlock(dentry, AuLock_IR); - si_read_unlock(sb); - err = au_vdir_fill_de(file, ctx); - fsstack_copy_attr_atime(inode, h_inode); - fi_write_unlock(file); - iput(h_inode); - - AuTraceErr(err); - return err; - } - -out_unlock: - di_read_unlock(dentry, AuLock_IR); - fi_write_unlock(file); -out: - si_read_unlock(sb); - return err; -} - -/* ---------------------------------------------------------------------- */ - -#define AuTestEmpty_WHONLY 1 -#define AuTestEmpty_CALLED (1 << 1) -#define AuTestEmpty_SHWH (1 << 2) -#define au_ftest_testempty(flags, name) ((flags) & AuTestEmpty_##name) -#define au_fset_testempty(flags, name) \ - do { (flags) |= AuTestEmpty_##name; } while (0) -#define au_fclr_testempty(flags, name) \ - do { (flags) &= ~AuTestEmpty_##name; } while (0) - -#ifndef CONFIG_AUFS_SHWH -#undef AuTestEmpty_SHWH -#define AuTestEmpty_SHWH 0 -#endif - -struct test_empty_arg { - struct dir_context ctx; - struct au_nhash *whlist; - unsigned int flags; - int err; - aufs_bindex_t bindex; -}; - -static int test_empty_cb(struct dir_context *ctx, const char *__name, - int namelen, loff_t offset __maybe_unused, u64 ino, - unsigned int d_type) -{ - struct test_empty_arg *arg = container_of(ctx, struct test_empty_arg, - ctx); - char *name = (void *)__name; - - arg->err = 0; - au_fset_testempty(arg->flags, CALLED); - /* smp_mb(); */ - if (name[0] == '.' - && (namelen == 1 || (name[1] == '.' && namelen == 2))) - goto out; /* success */ - - if (namelen <= AUFS_WH_PFX_LEN - || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { - if (au_ftest_testempty(arg->flags, WHONLY) - && !au_nhash_test_known_wh(arg->whlist, name, namelen)) - arg->err = -ENOTEMPTY; - goto out; - } - - name += AUFS_WH_PFX_LEN; - namelen -= AUFS_WH_PFX_LEN; - if (!au_nhash_test_known_wh(arg->whlist, name, namelen)) - arg->err = au_nhash_append_wh - (arg->whlist, name, namelen, ino, d_type, arg->bindex, - au_ftest_testempty(arg->flags, SHWH)); - -out: - /* smp_mb(); */ - AuTraceErr(arg->err); - return arg->err; -} - -static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg) -{ - int err; - struct file *h_file; - - h_file = au_h_open(dentry, arg->bindex, - O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_LARGEFILE, - /*file*/NULL, /*force_wr*/0); - err = PTR_ERR(h_file); - if (IS_ERR(h_file)) - goto out; - - err = 0; - if (!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE) - && !file_inode(h_file)->i_nlink) - goto out_put; - - do { - arg->err = 0; - au_fclr_testempty(arg->flags, CALLED); - /* smp_mb(); */ - err = vfsub_iterate_dir(h_file, &arg->ctx); - if (err >= 0) - err = arg->err; - } while (!err && au_ftest_testempty(arg->flags, CALLED)); - -out_put: - fput(h_file); - au_sbr_put(dentry->d_sb, arg->bindex); -out: - return err; -} - -struct do_test_empty_args { - int *errp; - struct dentry *dentry; - struct test_empty_arg *arg; -}; - -static void call_do_test_empty(void *args) -{ - struct do_test_empty_args *a = args; - *a->errp = do_test_empty(a->dentry, a->arg); -} - -static int sio_test_empty(struct dentry *dentry, struct test_empty_arg *arg) -{ - int err, wkq_err; - struct dentry *h_dentry; - struct inode *h_inode; - - h_dentry = au_h_dptr(dentry, arg->bindex); - h_inode = d_inode(h_dentry); - /* todo: i_mode changes anytime? */ - mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); - err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ); - mutex_unlock(&h_inode->i_mutex); - if (!err) - err = do_test_empty(dentry, arg); - else { - struct do_test_empty_args args = { - .errp = &err, - .dentry = dentry, - .arg = arg - }; - unsigned int flags = arg->flags; - - wkq_err = au_wkq_wait(call_do_test_empty, &args); - if (unlikely(wkq_err)) - err = wkq_err; - arg->flags = flags; - } - - return err; -} - -int au_test_empty_lower(struct dentry *dentry) -{ - int err; - unsigned int rdhash; - aufs_bindex_t bindex, bstart, btail; - struct au_nhash whlist; - struct test_empty_arg arg = { - .ctx = { - .actor = test_empty_cb - } - }; - int (*test_empty)(struct dentry *dentry, struct test_empty_arg *arg); - - SiMustAnyLock(dentry->d_sb); - - rdhash = au_sbi(dentry->d_sb)->si_rdhash; - if (!rdhash) - rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, dentry)); - err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); - if (unlikely(err)) - goto out; - - arg.flags = 0; - arg.whlist = &whlist; - bstart = au_dbstart(dentry); - if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) - au_fset_testempty(arg.flags, SHWH); - test_empty = do_test_empty; - if (au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1)) - test_empty = sio_test_empty; - arg.bindex = bstart; - err = test_empty(dentry, &arg); - if (unlikely(err)) - goto out_whlist; - - au_fset_testempty(arg.flags, WHONLY); - btail = au_dbtaildir(dentry); - for (bindex = bstart + 1; !err && bindex <= btail; bindex++) { - struct dentry *h_dentry; - - h_dentry = au_h_dptr(dentry, bindex); - if (h_dentry && d_is_positive(h_dentry)) { - arg.bindex = bindex; - err = test_empty(dentry, &arg); - } - } - -out_whlist: - au_nhash_wh_free(&whlist); -out: - return err; -} - -int au_test_empty(struct dentry *dentry, struct au_nhash *whlist) -{ - int err; - struct test_empty_arg arg = { - .ctx = { - .actor = test_empty_cb - } - }; - aufs_bindex_t bindex, btail; - - err = 0; - arg.whlist = whlist; - arg.flags = AuTestEmpty_WHONLY; - if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) - au_fset_testempty(arg.flags, SHWH); - btail = au_dbtaildir(dentry); - for (bindex = au_dbstart(dentry); !err && bindex <= btail; bindex++) { - struct dentry *h_dentry; - - h_dentry = au_h_dptr(dentry, bindex); - if (h_dentry && d_is_positive(h_dentry)) { - arg.bindex = bindex; - err = sio_test_empty(dentry, &arg); - } - } - - return err; -} - -/* ---------------------------------------------------------------------- */ - -const struct file_operations aufs_dir_fop = { - .owner = THIS_MODULE, - .llseek = default_llseek, - .read = generic_read_dir, - .iterate = aufs_iterate, - .unlocked_ioctl = aufs_ioctl_dir, -#ifdef CONFIG_COMPAT - .compat_ioctl = aufs_compat_ioctl_dir, -#endif - .open = aufs_open_dir, - .release = aufs_release_dir, - .flush = aufs_flush_dir, - .fsync = aufs_fsync_dir -}; diff --git a/fs/aufs/dir.h b/fs/aufs/dir.h deleted file mode 100644 index e4acf732c..000000000 --- a/fs/aufs/dir.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * directory operations - */ - -#ifndef __AUFS_DIR_H__ -#define __AUFS_DIR_H__ - -#ifdef __KERNEL__ - -#include <linux/fs.h> - -/* ---------------------------------------------------------------------- */ - -/* need to be faster and smaller */ - -struct au_nhash { - unsigned int nh_num; - struct hlist_head *nh_head; -}; - -struct au_vdir_destr { - unsigned char len; - unsigned char name[0]; -} __packed; - -struct au_vdir_dehstr { - struct hlist_node hash; - struct au_vdir_destr *str; -} ____cacheline_aligned_in_smp; - -struct au_vdir_de { - ino_t de_ino; - unsigned char de_type; - /* caution: packed */ - struct au_vdir_destr de_str; -} __packed; - -struct au_vdir_wh { - struct hlist_node wh_hash; -#ifdef CONFIG_AUFS_SHWH - ino_t wh_ino; - aufs_bindex_t wh_bindex; - unsigned char wh_type; -#else - aufs_bindex_t wh_bindex; -#endif - /* caution: packed */ - struct au_vdir_destr wh_str; -} __packed; - -union au_vdir_deblk_p { - unsigned char *deblk; - struct au_vdir_de *de; -}; - -struct au_vdir { - unsigned char **vd_deblk; - unsigned long vd_nblk; - struct { - unsigned long ul; - union au_vdir_deblk_p p; - } vd_last; - - unsigned long vd_version; - unsigned int vd_deblk_sz; - unsigned long vd_jiffy; -} ____cacheline_aligned_in_smp; - -/* ---------------------------------------------------------------------- */ - -/* dir.c */ -extern const struct file_operations aufs_dir_fop; -void au_add_nlink(struct inode *dir, struct inode *h_dir); -void au_sub_nlink(struct inode *dir, struct inode *h_dir); -loff_t au_dir_size(struct file *file, struct dentry *dentry); -void au_dir_ts(struct inode *dir, aufs_bindex_t bsrc); -int au_test_empty_lower(struct dentry *dentry); -int au_test_empty(struct dentry *dentry, struct au_nhash *whlist); - -/* vdir.c */ -unsigned int au_rdhash_est(loff_t sz); -int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp); -void au_nhash_wh_free(struct au_nhash *whlist); -int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, - int limit); -int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen); -int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, - unsigned int d_type, aufs_bindex_t bindex, - unsigned char shwh); -void au_vdir_free(struct au_vdir *vdir); -int au_vdir_init(struct file *file); -int au_vdir_fill_de(struct file *file, struct dir_context *ctx); - -/* ioctl.c */ -long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg); - -#ifdef CONFIG_AUFS_RDU -/* rdu.c */ -long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg); -#ifdef CONFIG_COMPAT -long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg); -#endif -#else -AuStub(long, au_rdu_ioctl, return -EINVAL, struct file *file, - unsigned int cmd, unsigned long arg) -#ifdef CONFIG_COMPAT -AuStub(long, au_rdu_compat_ioctl, return -EINVAL, struct file *file, - unsigned int cmd, unsigned long arg) -#endif -#endif - -#endif /* __KERNEL__ */ -#endif /* __AUFS_DIR_H__ */ diff --git a/fs/aufs/dynop.c b/fs/aufs/dynop.c deleted file mode 100644 index a04d02f91..000000000 --- a/fs/aufs/dynop.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Copyright (C) 2010-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * dynamically customizable operations for regular files - */ - -#include "aufs.h" - -#define DyPrSym(key) AuDbgSym(key->dk_op.dy_hop) - -/* - * How large will these lists be? - * Usually just a few elements, 20-30 at most for each, I guess. - */ -static struct au_splhead dynop[AuDyLast]; - -static struct au_dykey *dy_gfind_get(struct au_splhead *spl, const void *h_op) -{ - struct au_dykey *key, *tmp; - struct list_head *head; - - key = NULL; - head = &spl->head; - rcu_read_lock(); - list_for_each_entry_rcu(tmp, head, dk_list) - if (tmp->dk_op.dy_hop == h_op) { - key = tmp; - kref_get(&key->dk_kref); - break; - } - rcu_read_unlock(); - - return key; -} - -static struct au_dykey *dy_bradd(struct au_branch *br, struct au_dykey *key) -{ - struct au_dykey **k, *found; - const void *h_op = key->dk_op.dy_hop; - int i; - - found = NULL; - k = br->br_dykey; - for (i = 0; i < AuBrDynOp; i++) - if (k[i]) { - if (k[i]->dk_op.dy_hop == h_op) { - found = k[i]; - break; - } - } else - break; - if (!found) { - spin_lock(&br->br_dykey_lock); - for (; i < AuBrDynOp; i++) - if (k[i]) { - if (k[i]->dk_op.dy_hop == h_op) { - found = k[i]; - break; - } - } else { - k[i] = key; - break; - } - spin_unlock(&br->br_dykey_lock); - BUG_ON(i == AuBrDynOp); /* expand the array */ - } - - return found; -} - -/* kref_get() if @key is already added */ -static struct au_dykey *dy_gadd(struct au_splhead *spl, struct au_dykey *key) -{ - struct au_dykey *tmp, *found; - struct list_head *head; - const void *h_op = key->dk_op.dy_hop; - - found = NULL; - head = &spl->head; - spin_lock(&spl->spin); - list_for_each_entry(tmp, head, dk_list) - if (tmp->dk_op.dy_hop == h_op) { - kref_get(&tmp->dk_kref); - found = tmp; - break; - } - if (!found) - list_add_rcu(&key->dk_list, head); - spin_unlock(&spl->spin); - - if (!found) - DyPrSym(key); - return found; -} - -static void dy_free_rcu(struct rcu_head *rcu) -{ - struct au_dykey *key; - - key = container_of(rcu, struct au_dykey, dk_rcu); - DyPrSym(key); - kfree(key); -} - -static void dy_free(struct kref *kref) -{ - struct au_dykey *key; - struct au_splhead *spl; - - key = container_of(kref, struct au_dykey, dk_kref); - spl = dynop + key->dk_op.dy_type; - au_spl_del_rcu(&key->dk_list, spl); - call_rcu(&key->dk_rcu, dy_free_rcu); -} - -void au_dy_put(struct au_dykey *key) -{ - kref_put(&key->dk_kref, dy_free); -} - -/* ---------------------------------------------------------------------- */ - -#define DyDbgSize(cnt, op) AuDebugOn(cnt != sizeof(op)/sizeof(void *)) - -#ifdef CONFIG_AUFS_DEBUG -#define DyDbgDeclare(cnt) unsigned int cnt = 0 -#define DyDbgInc(cnt) do { cnt++; } while (0) -#else -#define DyDbgDeclare(cnt) do {} while (0) -#define DyDbgInc(cnt) do {} while (0) -#endif - -#define DySet(func, dst, src, h_op, h_sb) do { \ - DyDbgInc(cnt); \ - if (h_op->func) { \ - if (src.func) \ - dst.func = src.func; \ - else \ - AuDbg("%s %s\n", au_sbtype(h_sb), #func); \ - } \ -} while (0) - -#define DySetForce(func, dst, src) do { \ - AuDebugOn(!src.func); \ - DyDbgInc(cnt); \ - dst.func = src.func; \ -} while (0) - -#define DySetAop(func) \ - DySet(func, dyaop->da_op, aufs_aop, h_aop, h_sb) -#define DySetAopForce(func) \ - DySetForce(func, dyaop->da_op, aufs_aop) - -static void dy_aop(struct au_dykey *key, const void *h_op, - struct super_block *h_sb __maybe_unused) -{ - struct au_dyaop *dyaop = (void *)key; - const struct address_space_operations *h_aop = h_op; - DyDbgDeclare(cnt); - - AuDbg("%s\n", au_sbtype(h_sb)); - - DySetAop(writepage); - DySetAopForce(readpage); /* force */ - DySetAop(writepages); - DySetAop(set_page_dirty); - DySetAop(readpages); - DySetAop(write_begin); - DySetAop(write_end); - DySetAop(bmap); - DySetAop(invalidatepage); - DySetAop(releasepage); - DySetAop(freepage); - /* this one will be changed according to an aufs mount option */ - DySetAop(direct_IO); - DySetAop(migratepage); - DySetAop(launder_page); - DySetAop(is_partially_uptodate); - DySetAop(is_dirty_writeback); - DySetAop(error_remove_page); - DySetAop(swap_activate); - DySetAop(swap_deactivate); - - DyDbgSize(cnt, *h_aop); -} - -/* ---------------------------------------------------------------------- */ - -static void dy_bug(struct kref *kref) -{ - BUG(); -} - -static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br) -{ - struct au_dykey *key, *old; - struct au_splhead *spl; - struct op { - unsigned int sz; - void (*set)(struct au_dykey *key, const void *h_op, - struct super_block *h_sb __maybe_unused); - }; - static const struct op a[] = { - [AuDy_AOP] = { - .sz = sizeof(struct au_dyaop), - .set = dy_aop - } - }; - const struct op *p; - - spl = dynop + op->dy_type; - key = dy_gfind_get(spl, op->dy_hop); - if (key) - goto out_add; /* success */ - - p = a + op->dy_type; - key = kzalloc(p->sz, GFP_NOFS); - if (unlikely(!key)) { - key = ERR_PTR(-ENOMEM); - goto out; - } - - key->dk_op.dy_hop = op->dy_hop; - kref_init(&key->dk_kref); - p->set(key, op->dy_hop, au_br_sb(br)); - old = dy_gadd(spl, key); - if (old) { - kfree(key); - key = old; - } - -out_add: - old = dy_bradd(br, key); - if (old) - /* its ref-count should never be zero here */ - kref_put(&key->dk_kref, dy_bug); -out: - return key; -} - -/* ---------------------------------------------------------------------- */ -/* - * Aufs prohibits O_DIRECT by defaut even if the branch supports it. - * This behaviour is necessary to return an error from open(O_DIRECT) instead - * of the succeeding I/O. The dio mount option enables O_DIRECT and makes - * open(O_DIRECT) always succeed, but the succeeding I/O may return an error. - * See the aufs manual in detail. - */ -static void dy_adx(struct au_dyaop *dyaop, int do_dx) -{ - if (!do_dx) - dyaop->da_op.direct_IO = NULL; - else - dyaop->da_op.direct_IO = aufs_aop.direct_IO; -} - -static struct au_dyaop *dy_aget(struct au_branch *br, - const struct address_space_operations *h_aop, - int do_dx) -{ - struct au_dyaop *dyaop; - struct au_dynop op; - - op.dy_type = AuDy_AOP; - op.dy_haop = h_aop; - dyaop = (void *)dy_get(&op, br); - if (IS_ERR(dyaop)) - goto out; - dy_adx(dyaop, do_dx); - -out: - return dyaop; -} - -int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, - struct inode *h_inode) -{ - int err, do_dx; - struct super_block *sb; - struct au_branch *br; - struct au_dyaop *dyaop; - - AuDebugOn(!S_ISREG(h_inode->i_mode)); - IiMustWriteLock(inode); - - sb = inode->i_sb; - br = au_sbr(sb, bindex); - do_dx = !!au_opt_test(au_mntflags(sb), DIO); - dyaop = dy_aget(br, h_inode->i_mapping->a_ops, do_dx); - err = PTR_ERR(dyaop); - if (IS_ERR(dyaop)) - /* unnecessary to call dy_fput() */ - goto out; - - err = 0; - inode->i_mapping->a_ops = &dyaop->da_op; - -out: - return err; -} - -/* - * Is it safe to replace a_ops during the inode/file is in operation? - * Yes, I hope so. - */ -int au_dy_irefresh(struct inode *inode) -{ - int err; - aufs_bindex_t bstart; - struct inode *h_inode; - - err = 0; - if (S_ISREG(inode->i_mode)) { - bstart = au_ibstart(inode); - h_inode = au_h_iptr(inode, bstart); - err = au_dy_iaop(inode, bstart, h_inode); - } - return err; -} - -void au_dy_arefresh(int do_dx) -{ - struct au_splhead *spl; - struct list_head *head; - struct au_dykey *key; - - spl = dynop + AuDy_AOP; - head = &spl->head; - spin_lock(&spl->spin); - list_for_each_entry(key, head, dk_list) - dy_adx((void *)key, do_dx); - spin_unlock(&spl->spin); -} - -/* ---------------------------------------------------------------------- */ - -void __init au_dy_init(void) -{ - int i; - - /* make sure that 'struct au_dykey *' can be any type */ - BUILD_BUG_ON(offsetof(struct au_dyaop, da_key)); - - for (i = 0; i < AuDyLast; i++) - au_spl_init(dynop + i); -} - -void au_dy_fin(void) -{ - int i; - - for (i = 0; i < AuDyLast; i++) - WARN_ON(!list_empty(&dynop[i].head)); -} diff --git a/fs/aufs/dynop.h b/fs/aufs/dynop.h deleted file mode 100644 index 724dddab1..000000000 --- a/fs/aufs/dynop.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2010-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * dynamically customizable operations (for regular files only) - */ - -#ifndef __AUFS_DYNOP_H__ -#define __AUFS_DYNOP_H__ - -#ifdef __KERNEL__ - -#include <linux/fs.h> -#include <linux/kref.h> - -enum {AuDy_AOP, AuDyLast}; - -struct au_dynop { - int dy_type; - union { - const void *dy_hop; - const struct address_space_operations *dy_haop; - }; -}; - -struct au_dykey { - union { - struct list_head dk_list; - struct rcu_head dk_rcu; - }; - struct au_dynop dk_op; - - /* - * during I am in the branch local array, kref is gotten. when the - * branch is removed, kref is put. - */ - struct kref dk_kref; -}; - -/* stop unioning since their sizes are very different from each other */ -struct au_dyaop { - struct au_dykey da_key; - struct address_space_operations da_op; /* not const */ -}; - -/* ---------------------------------------------------------------------- */ - -/* dynop.c */ -struct au_branch; -void au_dy_put(struct au_dykey *key); -int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, - struct inode *h_inode); -int au_dy_irefresh(struct inode *inode); -void au_dy_arefresh(int do_dio); - -void __init au_dy_init(void); -void au_dy_fin(void); - -#endif /* __KERNEL__ */ -#endif /* __AUFS_DYNOP_H__ */ diff --git a/fs/aufs/export.c b/fs/aufs/export.c deleted file mode 100644 index c85b036b8..000000000 --- a/fs/aufs/export.c +++ /dev/null @@ -1,832 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * export via nfs - */ - -#include <linux/exportfs.h> -#include <linux/fs_struct.h> -#include <linux/namei.h> -#include <linux/nsproxy.h> -#include <linux/random.h> -#include <linux/writeback.h> -#include "../fs/mount.h" -#include "aufs.h" - -union conv { -#ifdef CONFIG_AUFS_INO_T_64 - __u32 a[2]; -#else - __u32 a[1]; -#endif - ino_t ino; -}; - -static ino_t decode_ino(__u32 *a) -{ - union conv u; - - BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a)); - u.a[0] = a[0]; -#ifdef CONFIG_AUFS_INO_T_64 - u.a[1] = a[1]; -#endif - return u.ino; -} - -static void encode_ino(__u32 *a, ino_t ino) -{ - union conv u; - - u.ino = ino; - a[0] = u.a[0]; -#ifdef CONFIG_AUFS_INO_T_64 - a[1] = u.a[1]; -#endif -} - -/* NFS file handle */ -enum { - Fh_br_id, - Fh_sigen, -#ifdef CONFIG_AUFS_INO_T_64 - /* support 64bit inode number */ - Fh_ino1, - Fh_ino2, - Fh_dir_ino1, - Fh_dir_ino2, -#else - Fh_ino1, - Fh_dir_ino1, -#endif - Fh_igen, - Fh_h_type, - Fh_tail, - - Fh_ino = Fh_ino1, - Fh_dir_ino = Fh_dir_ino1 -}; - -static int au_test_anon(struct dentry *dentry) -{ - /* note: read d_flags without d_lock */ - return !!(dentry->d_flags & DCACHE_DISCONNECTED); -} - -int au_test_nfsd(void) -{ - int ret; - struct task_struct *tsk = current; - char comm[sizeof(tsk->comm)]; - - ret = 0; - if (tsk->flags & PF_KTHREAD) { - get_task_comm(comm, tsk); - ret = !strcmp(comm, "nfsd"); - } - - return ret; -} - -/* ---------------------------------------------------------------------- */ -/* inode generation external table */ - -void au_xigen_inc(struct inode *inode) -{ - loff_t pos; - ssize_t sz; - __u32 igen; - struct super_block *sb; - struct au_sbinfo *sbinfo; - - sb = inode->i_sb; - AuDebugOn(!au_opt_test(au_mntflags(sb), XINO)); - - sbinfo = au_sbi(sb); - pos = inode->i_ino; - pos *= sizeof(igen); - igen = inode->i_generation + 1; - sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xigen, &igen, - sizeof(igen), &pos); - if (sz == sizeof(igen)) - return; /* success */ - - if (unlikely(sz >= 0)) - AuIOErr("xigen error (%zd)\n", sz); -} - -int au_xigen_new(struct inode *inode) -{ - int err; - loff_t pos; - ssize_t sz; - struct super_block *sb; - struct au_sbinfo *sbinfo; - struct file *file; - - err = 0; - /* todo: dirty, at mount time */ - if (inode->i_ino == AUFS_ROOT_INO) - goto out; - sb = inode->i_sb; - SiMustAnyLock(sb); - if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) - goto out; - - err = -EFBIG; - pos = inode->i_ino; - if (unlikely(au_loff_max / sizeof(inode->i_generation) - 1 < pos)) { - AuIOErr1("too large i%lld\n", pos); - goto out; - } - pos *= sizeof(inode->i_generation); - - err = 0; - sbinfo = au_sbi(sb); - file = sbinfo->si_xigen; - BUG_ON(!file); - - if (vfsub_f_size_read(file) - < pos + sizeof(inode->i_generation)) { - inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next); - sz = xino_fwrite(sbinfo->si_xwrite, file, &inode->i_generation, - sizeof(inode->i_generation), &pos); - } else - sz = xino_fread(sbinfo->si_xread, file, &inode->i_generation, - sizeof(inode->i_generation), &pos); - if (sz == sizeof(inode->i_generation)) - goto out; /* success */ - - err = sz; - if (unlikely(sz >= 0)) { - err = -EIO; - AuIOErr("xigen error (%zd)\n", sz); - } - -out: - return err; -} - -int au_xigen_set(struct super_block *sb, struct file *base) -{ - int err; - struct au_sbinfo *sbinfo; - struct file *file; - - SiMustWriteLock(sb); - - sbinfo = au_sbi(sb); - file = au_xino_create2(base, sbinfo->si_xigen); - err = PTR_ERR(file); - if (IS_ERR(file)) - goto out; - err = 0; - if (sbinfo->si_xigen) - fput(sbinfo->si_xigen); - sbinfo->si_xigen = file; - -out: - return err; -} - -void au_xigen_clr(struct super_block *sb) -{ - struct au_sbinfo *sbinfo; - - SiMustWriteLock(sb); - - sbinfo = au_sbi(sb); - if (sbinfo->si_xigen) { - fput(sbinfo->si_xigen); - sbinfo->si_xigen = NULL; - } -} - -/* ---------------------------------------------------------------------- */ - -static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino, - ino_t dir_ino) -{ - struct dentry *dentry, *d; - struct inode *inode; - unsigned int sigen; - - dentry = NULL; - inode = ilookup(sb, ino); - if (!inode) - goto out; - - dentry = ERR_PTR(-ESTALE); - sigen = au_sigen(sb); - if (unlikely(is_bad_inode(inode) - || IS_DEADDIR(inode) - || sigen != au_iigen(inode, NULL))) - goto out_iput; - - dentry = NULL; - if (!dir_ino || S_ISDIR(inode->i_mode)) - dentry = d_find_alias(inode); - else { - spin_lock(&inode->i_lock); - hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) { - spin_lock(&d->d_lock); - if (!au_test_anon(d) - && d_inode(d->d_parent)->i_ino == dir_ino) { - dentry = dget_dlock(d); - spin_unlock(&d->d_lock); - break; - } - spin_unlock(&d->d_lock); - } - spin_unlock(&inode->i_lock); - } - if (unlikely(dentry && au_digen_test(dentry, sigen))) { - /* need to refresh */ - dput(dentry); - dentry = NULL; - } - -out_iput: - iput(inode); -out: - AuTraceErrPtr(dentry); - return dentry; -} - -/* ---------------------------------------------------------------------- */ - -/* todo: dirty? */ -/* if exportfs_decode_fh() passed vfsmount*, we could be happy */ - -struct au_compare_mnt_args { - /* input */ - struct super_block *sb; - - /* output */ - struct vfsmount *mnt; -}; - -static int au_compare_mnt(struct vfsmount *mnt, void *arg) -{ - struct au_compare_mnt_args *a = arg; - - if (mnt->mnt_sb != a->sb) - return 0; - a->mnt = mntget(mnt); - return 1; -} - -static struct vfsmount *au_mnt_get(struct super_block *sb) -{ - int err; - struct path root; - struct au_compare_mnt_args args = { - .sb = sb - }; - - get_fs_root(current->fs, &root); - rcu_read_lock(); - err = iterate_mounts(au_compare_mnt, &args, root.mnt); - rcu_read_unlock(); - path_put(&root); - AuDebugOn(!err); - AuDebugOn(!args.mnt); - return args.mnt; -} - -struct au_nfsd_si_lock { - unsigned int sigen; - aufs_bindex_t bindex, br_id; - unsigned char force_lock; -}; - -static int si_nfsd_read_lock(struct super_block *sb, - struct au_nfsd_si_lock *nsi_lock) -{ - int err; - aufs_bindex_t bindex; - - si_read_lock(sb, AuLock_FLUSH); - - /* branch id may be wrapped around */ - err = 0; - bindex = au_br_index(sb, nsi_lock->br_id); - if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb)) - goto out; /* success */ - - err = -ESTALE; - bindex = -1; - if (!nsi_lock->force_lock) - si_read_unlock(sb); - -out: - nsi_lock->bindex = bindex; - return err; -} - -struct find_name_by_ino { - struct dir_context ctx; - int called, found; - ino_t ino; - char *name; - int namelen; -}; - -static int -find_name_by_ino(struct dir_context *ctx, const char *name, int namelen, - loff_t offset, u64 ino, unsigned int d_type) -{ - struct find_name_by_ino *a = container_of(ctx, struct find_name_by_ino, - ctx); - - a->called++; - if (a->ino != ino) - return 0; - - memcpy(a->name, name, namelen); - a->namelen = namelen; - a->found = 1; - return 1; -} - -static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino, - struct au_nfsd_si_lock *nsi_lock) -{ - struct dentry *dentry, *parent; - struct file *file; - struct inode *dir; - struct find_name_by_ino arg = { - .ctx = { - .actor = find_name_by_ino - } - }; - int err; - - parent = path->dentry; - if (nsi_lock) - si_read_unlock(parent->d_sb); - file = vfsub_dentry_open(path, au_dir_roflags); - dentry = (void *)file; - if (IS_ERR(file)) - goto out; - - dentry = ERR_PTR(-ENOMEM); - arg.name = (void *)__get_free_page(GFP_NOFS); - if (unlikely(!arg.name)) - goto out_file; - arg.ino = ino; - arg.found = 0; - do { - arg.called = 0; - /* smp_mb(); */ - err = vfsub_iterate_dir(file, &arg.ctx); - } while (!err && !arg.found && arg.called); - dentry = ERR_PTR(err); - if (unlikely(err)) - goto out_name; - /* instead of ENOENT */ - dentry = ERR_PTR(-ESTALE); - if (!arg.found) - goto out_name; - - /* do not call vfsub_lkup_one() */ - dir = d_inode(parent); - mutex_lock(&dir->i_mutex); - dentry = vfsub_lookup_one_len(arg.name, parent, arg.namelen); - mutex_unlock(&dir->i_mutex); - AuTraceErrPtr(dentry); - if (IS_ERR(dentry)) - goto out_name; - AuDebugOn(au_test_anon(dentry)); - if (unlikely(d_really_is_negative(dentry))) { - dput(dentry); - dentry = ERR_PTR(-ENOENT); - } - -out_name: - free_page((unsigned long)arg.name); -out_file: - fput(file); -out: - if (unlikely(nsi_lock - && si_nfsd_read_lock(parent->d_sb, nsi_lock) < 0)) - if (!IS_ERR(dentry)) { - dput(dentry); - dentry = ERR_PTR(-ESTALE); - } - AuTraceErrPtr(dentry); - return dentry; -} - -static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino, - ino_t dir_ino, - struct au_nfsd_si_lock *nsi_lock) -{ - struct dentry *dentry; - struct path path; - - if (dir_ino != AUFS_ROOT_INO) { - path.dentry = decode_by_ino(sb, dir_ino, 0); - dentry = path.dentry; - if (!path.dentry || IS_ERR(path.dentry)) - goto out; - AuDebugOn(au_test_anon(path.dentry)); - } else - path.dentry = dget(sb->s_root); - - path.mnt = au_mnt_get(sb); - dentry = au_lkup_by_ino(&path, ino, nsi_lock); - path_put(&path); - -out: - AuTraceErrPtr(dentry); - return dentry; -} - -/* ---------------------------------------------------------------------- */ - -static int h_acceptable(void *expv, struct dentry *dentry) -{ - return 1; -} - -static char *au_build_path(struct dentry *h_parent, struct path *h_rootpath, - char *buf, int len, struct super_block *sb) -{ - char *p; - int n; - struct path path; - - p = d_path(h_rootpath, buf, len); - if (IS_ERR(p)) - goto out; - n = strlen(p); - - path.mnt = h_rootpath->mnt; - path.dentry = h_parent; - p = d_path(&path, buf, len); - if (IS_ERR(p)) - goto out; - if (n != 1) - p += n; - - path.mnt = au_mnt_get(sb); - path.dentry = sb->s_root; - p = d_path(&path, buf, len - strlen(p)); - mntput(path.mnt); - if (IS_ERR(p)) - goto out; - if (n != 1) - p[strlen(p)] = '/'; - -out: - AuTraceErrPtr(p); - return p; -} - -static -struct dentry *decode_by_path(struct super_block *sb, ino_t ino, __u32 *fh, - int fh_len, struct au_nfsd_si_lock *nsi_lock) -{ - struct dentry *dentry, *h_parent, *root; - struct super_block *h_sb; - char *pathname, *p; - struct vfsmount *h_mnt; - struct au_branch *br; - int err; - struct path path; - - br = au_sbr(sb, nsi_lock->bindex); - h_mnt = au_br_mnt(br); - h_sb = h_mnt->mnt_sb; - /* todo: call lower fh_to_dentry()? fh_to_parent()? */ - h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail), - fh_len - Fh_tail, fh[Fh_h_type], - h_acceptable, /*context*/NULL); - dentry = h_parent; - if (unlikely(!h_parent || IS_ERR(h_parent))) { - AuWarn1("%s decode_fh failed, %ld\n", - au_sbtype(h_sb), PTR_ERR(h_parent)); - goto out; - } - dentry = NULL; - if (unlikely(au_test_anon(h_parent))) { - AuWarn1("%s decode_fh returned a disconnected dentry\n", - au_sbtype(h_sb)); - goto out_h_parent; - } - - dentry = ERR_PTR(-ENOMEM); - pathname = (void *)__get_free_page(GFP_NOFS); - if (unlikely(!pathname)) - goto out_h_parent; - - root = sb->s_root; - path.mnt = h_mnt; - di_read_lock_parent(root, !AuLock_IR); - path.dentry = au_h_dptr(root, nsi_lock->bindex); - di_read_unlock(root, !AuLock_IR); - p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb); - dentry = (void *)p; - if (IS_ERR(p)) - goto out_pathname; - - si_read_unlock(sb); - err = vfsub_kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); - dentry = ERR_PTR(err); - if (unlikely(err)) - goto out_relock; - - dentry = ERR_PTR(-ENOENT); - AuDebugOn(au_test_anon(path.dentry)); - if (unlikely(d_really_is_negative(path.dentry))) - goto out_path; - - if (ino != d_inode(path.dentry)->i_ino) - dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL); - else - dentry = dget(path.dentry); - -out_path: - path_put(&path); -out_relock: - if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0)) - if (!IS_ERR(dentry)) { - dput(dentry); - dentry = ERR_PTR(-ESTALE); - } -out_pathname: - free_page((unsigned long)pathname); -out_h_parent: - dput(h_parent); -out: - AuTraceErrPtr(dentry); - return dentry; -} - -/* ---------------------------------------------------------------------- */ - -static struct dentry * -aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, - int fh_type) -{ - struct dentry *dentry; - __u32 *fh = fid->raw; - struct au_branch *br; - ino_t ino, dir_ino; - struct au_nfsd_si_lock nsi_lock = { - .force_lock = 0 - }; - - dentry = ERR_PTR(-ESTALE); - /* it should never happen, but the file handle is unreliable */ - if (unlikely(fh_len < Fh_tail)) - goto out; - nsi_lock.sigen = fh[Fh_sigen]; - nsi_lock.br_id = fh[Fh_br_id]; - - /* branch id may be wrapped around */ - br = NULL; - if (unlikely(si_nfsd_read_lock(sb, &nsi_lock))) - goto out; - nsi_lock.force_lock = 1; - - /* is this inode still cached? */ - ino = decode_ino(fh + Fh_ino); - /* it should never happen */ - if (unlikely(ino == AUFS_ROOT_INO)) - goto out; - - dir_ino = decode_ino(fh + Fh_dir_ino); - dentry = decode_by_ino(sb, ino, dir_ino); - if (IS_ERR(dentry)) - goto out_unlock; - if (dentry) - goto accept; - - /* is the parent dir cached? */ - br = au_sbr(sb, nsi_lock.bindex); - atomic_inc(&br->br_count); - dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock); - if (IS_ERR(dentry)) - goto out_unlock; - if (dentry) - goto accept; - - /* lookup path */ - dentry = decode_by_path(sb, ino, fh, fh_len, &nsi_lock); - if (IS_ERR(dentry)) - goto out_unlock; - if (unlikely(!dentry)) - /* todo?: make it ESTALE */ - goto out_unlock; - -accept: - if (!au_digen_test(dentry, au_sigen(sb)) - && d_inode(dentry)->i_generation == fh[Fh_igen]) - goto out_unlock; /* success */ - - dput(dentry); - dentry = ERR_PTR(-ESTALE); -out_unlock: - if (br) - atomic_dec(&br->br_count); - si_read_unlock(sb); -out: - AuTraceErrPtr(dentry); - return dentry; -} - -#if 0 /* reserved for future use */ -/* support subtreecheck option */ -static struct dentry *aufs_fh_to_parent(struct super_block *sb, struct fid *fid, - int fh_len, int fh_type) -{ - struct dentry *parent; - __u32 *fh = fid->raw; - ino_t dir_ino; - - dir_ino = decode_ino(fh + Fh_dir_ino); - parent = decode_by_ino(sb, dir_ino, 0); - if (IS_ERR(parent)) - goto out; - if (!parent) - parent = decode_by_path(sb, au_br_index(sb, fh[Fh_br_id]), - dir_ino, fh, fh_len); - -out: - AuTraceErrPtr(parent); - return parent; -} -#endif - -/* ---------------------------------------------------------------------- */ - -static int aufs_encode_fh(struct inode *inode, __u32 *fh, int *max_len, - struct inode *dir) -{ - int err; - aufs_bindex_t bindex; - struct super_block *sb, *h_sb; - struct dentry *dentry, *parent, *h_parent; - struct inode *h_dir; - struct au_branch *br; - - err = -ENOSPC; - if (unlikely(*max_len <= Fh_tail)) { - AuWarn1("NFSv2 client (max_len %d)?\n", *max_len); - goto out; - } - - err = FILEID_ROOT; - if (inode->i_ino == AUFS_ROOT_INO) { - AuDebugOn(inode->i_ino != AUFS_ROOT_INO); - goto out; - } - - h_parent = NULL; - sb = inode->i_sb; - err = si_read_lock(sb, AuLock_FLUSH); - if (unlikely(err)) - goto out; - -#ifdef CONFIG_AUFS_DEBUG - if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) - AuWarn1("NFS-exporting requires xino\n"); -#endif - err = -EIO; - parent = NULL; - ii_read_lock_child(inode); - bindex = au_ibstart(inode); - if (!dir) { - dentry = d_find_any_alias(inode); - if (unlikely(!dentry)) - goto out_unlock; - AuDebugOn(au_test_anon(dentry)); - parent = dget_parent(dentry); - dput(dentry); - if (unlikely(!parent)) - goto out_unlock; - if (d_really_is_positive(parent)) - dir = d_inode(parent); - } - - ii_read_lock_parent(dir); - h_dir = au_h_iptr(dir, bindex); - ii_read_unlock(dir); - if (unlikely(!h_dir)) - goto out_parent; - h_parent = d_find_any_alias(h_dir); - if (unlikely(!h_parent)) - goto out_hparent; - - err = -EPERM; - br = au_sbr(sb, bindex); - h_sb = au_br_sb(br); - if (unlikely(!h_sb->s_export_op)) { - AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); - goto out_hparent; - } - - fh[Fh_br_id] = br->br_id; - fh[Fh_sigen] = au_sigen(sb); - encode_ino(fh + Fh_ino, inode->i_ino); - encode_ino(fh + Fh_dir_ino, dir->i_ino); - fh[Fh_igen] = inode->i_generation; - - *max_len -= Fh_tail; - fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail), - max_len, - /*connectable or subtreecheck*/0); - err = fh[Fh_h_type]; - *max_len += Fh_tail; - /* todo: macros? */ - if (err != FILEID_INVALID) - err = 99; - else - AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); - -out_hparent: - dput(h_parent); -out_parent: - dput(parent); -out_unlock: - ii_read_unlock(inode); - si_read_unlock(sb); -out: - if (unlikely(err < 0)) - err = FILEID_INVALID; - return err; -} - -/* ---------------------------------------------------------------------- */ - -static int aufs_commit_metadata(struct inode *inode) -{ - int err; - aufs_bindex_t bindex; - struct super_block *sb; - struct inode *h_inode; - int (*f)(struct inode *inode); - - sb = inode->i_sb; - si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); - ii_write_lock_child(inode); - bindex = au_ibstart(inode); - AuDebugOn(bindex < 0); - h_inode = au_h_iptr(inode, bindex); - - f = h_inode->i_sb->s_export_op->commit_metadata; - if (f) - err = f(h_inode); - else { - struct writeback_control wbc = { - .sync_mode = WB_SYNC_ALL, - .nr_to_write = 0 /* metadata only */ - }; - - err = sync_inode(h_inode, &wbc); - } - - au_cpup_attr_timesizes(inode); - ii_write_unlock(inode); - si_read_unlock(sb); - return err; -} - -/* ---------------------------------------------------------------------- */ - -static struct export_operations aufs_export_op = { - .fh_to_dentry = aufs_fh_to_dentry, - /* .fh_to_parent = aufs_fh_to_parent, */ - .encode_fh = aufs_encode_fh, - .commit_metadata = aufs_commit_metadata -}; - -void au_export_init(struct super_block *sb) -{ - struct au_sbinfo *sbinfo; - __u32 u; - - sb->s_export_op = &aufs_export_op; - sbinfo = au_sbi(sb); - sbinfo->si_xigen = NULL; - get_random_bytes(&u, sizeof(u)); - BUILD_BUG_ON(sizeof(u) != sizeof(int)); - atomic_set(&sbinfo->si_xigen_next, u); -} diff --git a/fs/aufs/f_op.c b/fs/aufs/f_op.c deleted file mode 100644 index 34037c708..000000000 --- a/fs/aufs/f_op.c +++ /dev/null @@ -1,738 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * file and vm operations - */ - -#include <linux/aio.h> -#include <linux/fs_stack.h> -#include <linux/mman.h> -#include <linux/security.h> -#include "aufs.h" - -int au_do_open_nondir(struct file *file, int flags, struct file *h_file) -{ - int err; - aufs_bindex_t bindex; - struct dentry *dentry; - struct au_finfo *finfo; - struct inode *h_inode; - - FiMustWriteLock(file); - - err = 0; - dentry = file->f_path.dentry; - AuDebugOn(IS_ERR_OR_NULL(dentry)); - finfo = au_fi(file); - memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop)); - atomic_set(&finfo->fi_mmapped, 0); - bindex = au_dbstart(dentry); - if (!h_file) - h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); - else - get_file(h_file); - if (IS_ERR(h_file)) - err = PTR_ERR(h_file); - else { - if ((flags & __O_TMPFILE) - && !(flags & O_EXCL)) { - h_inode = file_inode(h_file); - spin_lock(&h_inode->i_lock); - h_inode->i_state |= I_LINKABLE; - spin_unlock(&h_inode->i_lock); - } - au_set_fbstart(file, bindex); - au_set_h_fptr(file, bindex, h_file); - au_update_figen(file); - /* todo: necessary? */ - /* file->f_ra = h_file->f_ra; */ - } - - return err; -} - -static int aufs_open_nondir(struct inode *inode __maybe_unused, - struct file *file) -{ - int err; - struct super_block *sb; - struct au_do_open_args args = { - .open = au_do_open_nondir - }; - - AuDbg("%pD, f_flags 0x%x, f_mode 0x%x\n", - file, vfsub_file_flags(file), file->f_mode); - - sb = file->f_path.dentry->d_sb; - si_read_lock(sb, AuLock_FLUSH); - err = au_do_open(file, &args); - si_read_unlock(sb); - return err; -} - -int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file) -{ - struct au_finfo *finfo; - aufs_bindex_t bindex; - - finfo = au_fi(file); - au_sphl_del(&finfo->fi_hlist, - &au_sbi(file->f_path.dentry->d_sb)->si_files); - bindex = finfo->fi_btop; - if (bindex >= 0) - au_set_h_fptr(file, bindex, NULL); - - au_finfo_fin(file); - return 0; -} - -/* ---------------------------------------------------------------------- */ - -static int au_do_flush_nondir(struct file *file, fl_owner_t id) -{ - int err; - struct file *h_file; - - err = 0; - h_file = au_hf_top(file); - if (h_file) - err = vfsub_flush(h_file, id); - return err; -} - -static int aufs_flush_nondir(struct file *file, fl_owner_t id) -{ - return au_do_flush(file, id, au_do_flush_nondir); -} - -/* ---------------------------------------------------------------------- */ -/* - * read and write functions acquire [fdi]_rwsem once, but release before - * mmap_sem. This is because to stop a race condition between mmap(2). - * Releasing these aufs-rwsem should be safe, no branch-mamagement (by keeping - * si_rwsem), no harmful copy-up should happen. Actually copy-up may happen in - * read functions after [fdi]_rwsem are released, but it should be harmless. - */ - -/* Callers should call au_read_post() or fput() in the end */ -struct file *au_read_pre(struct file *file, int keep_fi) -{ - struct file *h_file; - int err; - - err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); - if (!err) { - di_read_unlock(file->f_path.dentry, AuLock_IR); - h_file = au_hf_top(file); - get_file(h_file); - if (!keep_fi) - fi_read_unlock(file); - } else - h_file = ERR_PTR(err); - - return h_file; -} - -static void au_read_post(struct inode *inode, struct file *h_file) -{ - /* update without lock, I don't think it a problem */ - fsstack_copy_attr_atime(inode, file_inode(h_file)); - fput(h_file); -} - -struct au_write_pre { - blkcnt_t blks; - aufs_bindex_t bstart; -}; - -/* - * return with iinfo is write-locked - * callers should call au_write_post() or iinfo_write_unlock() + fput() in the - * end - */ -static struct file *au_write_pre(struct file *file, int do_ready, - struct au_write_pre *wpre) -{ - struct file *h_file; - struct dentry *dentry; - int err; - struct au_pin pin; - - err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); - h_file = ERR_PTR(err); - if (unlikely(err)) - goto out; - - dentry = file->f_path.dentry; - if (do_ready) { - err = au_ready_to_write(file, -1, &pin); - if (unlikely(err)) { - h_file = ERR_PTR(err); - di_write_unlock(dentry); - goto out_fi; - } - } - - di_downgrade_lock(dentry, /*flags*/0); - if (wpre) - wpre->bstart = au_fbstart(file); - h_file = au_hf_top(file); - get_file(h_file); - if (wpre) - wpre->blks = file_inode(h_file)->i_blocks; - if (do_ready) - au_unpin(&pin); - di_read_unlock(dentry, /*flags*/0); - -out_fi: - fi_write_unlock(file); -out: - return h_file; -} - -static void au_write_post(struct inode *inode, struct file *h_file, - struct au_write_pre *wpre, ssize_t written) -{ - struct inode *h_inode; - - au_cpup_attr_timesizes(inode); - AuDebugOn(au_ibstart(inode) != wpre->bstart); - h_inode = file_inode(h_file); - inode->i_mode = h_inode->i_mode; - ii_write_unlock(inode); - fput(h_file); - - /* AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks); */ - if (written > 0) - au_fhsm_wrote(inode->i_sb, wpre->bstart, - /*force*/h_inode->i_blocks > wpre->blks); -} - -static ssize_t aufs_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - ssize_t err; - struct inode *inode; - struct file *h_file; - struct super_block *sb; - - inode = file_inode(file); - sb = inode->i_sb; - si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); - - h_file = au_read_pre(file, /*keep_fi*/0); - err = PTR_ERR(h_file); - if (IS_ERR(h_file)) - goto out; - - /* filedata may be obsoleted by concurrent copyup, but no problem */ - err = vfsub_read_u(h_file, buf, count, ppos); - /* todo: necessary? */ - /* file->f_ra = h_file->f_ra; */ - au_read_post(inode, h_file); - -out: - si_read_unlock(sb); - return err; -} - -/* - * todo: very ugly - * it locks both of i_mutex and si_rwsem for read in safe. - * if the plink maintenance mode continues forever (that is the problem), - * may loop forever. - */ -static void au_mtx_and_read_lock(struct inode *inode) -{ - int err; - struct super_block *sb = inode->i_sb; - - while (1) { - mutex_lock(&inode->i_mutex); - err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); - if (!err) - break; - mutex_unlock(&inode->i_mutex); - si_read_lock(sb, AuLock_NOPLMW); - si_read_unlock(sb); - } -} - -static ssize_t aufs_write(struct file *file, const char __user *ubuf, - size_t count, loff_t *ppos) -{ - ssize_t err; - struct au_write_pre wpre; - struct inode *inode; - struct file *h_file; - char __user *buf = (char __user *)ubuf; - - inode = file_inode(file); - au_mtx_and_read_lock(inode); - - h_file = au_write_pre(file, /*do_ready*/1, &wpre); - err = PTR_ERR(h_file); - if (IS_ERR(h_file)) - goto out; - - err = vfsub_write_u(h_file, buf, count, ppos); - au_write_post(inode, h_file, &wpre, err); - -out: - si_read_unlock(inode->i_sb); - mutex_unlock(&inode->i_mutex); - return err; -} - -static ssize_t au_do_iter(struct file *h_file, int rw, struct kiocb *kio, - struct iov_iter *iov_iter) -{ - ssize_t err; - struct file *file; - ssize_t (*iter)(struct kiocb *, struct iov_iter *); - - err = security_file_permission(h_file, rw); - if (unlikely(err)) - goto out; - - err = -ENOSYS; - iter = NULL; - if (rw == MAY_READ) - iter = h_file->f_op->read_iter; - else if (rw == MAY_WRITE) - iter = h_file->f_op->write_iter; - - file = kio->ki_filp; - kio->ki_filp = h_file; - if (iter) { - lockdep_off(); - err = iter(kio, iov_iter); - lockdep_on(); - } else - /* currently there is no such fs */ - WARN_ON_ONCE(1); - kio->ki_filp = file; - -out: - return err; -} - -static ssize_t aufs_read_iter(struct kiocb *kio, struct iov_iter *iov_iter) -{ - ssize_t err; - struct file *file, *h_file; - struct inode *inode; - struct super_block *sb; - - file = kio->ki_filp; - inode = file_inode(file); - sb = inode->i_sb; - si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); - - h_file = au_read_pre(file, /*keep_fi*/0); - err = PTR_ERR(h_file); - if (IS_ERR(h_file)) - goto out; - - err = au_do_iter(h_file, MAY_READ, kio, iov_iter); - /* todo: necessary? */ - /* file->f_ra = h_file->f_ra; */ - au_read_post(inode, h_file); - -out: - si_read_unlock(sb); - return err; -} - -static ssize_t aufs_write_iter(struct kiocb *kio, struct iov_iter *iov_iter) -{ - ssize_t err; - struct au_write_pre wpre; - struct inode *inode; - struct file *file, *h_file; - - file = kio->ki_filp; - inode = file_inode(file); - au_mtx_and_read_lock(inode); - - h_file = au_write_pre(file, /*do_ready*/1, &wpre); - err = PTR_ERR(h_file); - if (IS_ERR(h_file)) - goto out; - - err = au_do_iter(h_file, MAY_WRITE, kio, iov_iter); - au_write_post(inode, h_file, &wpre, err); - -out: - si_read_unlock(inode->i_sb); - mutex_unlock(&inode->i_mutex); - return err; -} - -static ssize_t aufs_splice_read(struct file *file, loff_t *ppos, - struct pipe_inode_info *pipe, size_t len, - unsigned int flags) -{ - ssize_t err; - struct file *h_file; - struct inode *inode; - struct super_block *sb; - - inode = file_inode(file); - sb = inode->i_sb; - si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); - - h_file = au_read_pre(file, /*keep_fi*/1); - err = PTR_ERR(h_file); - if (IS_ERR(h_file)) - goto out; - - if (au_test_loopback_kthread()) { - au_warn_loopback(h_file->f_path.dentry->d_sb); - if (file->f_mapping != h_file->f_mapping) { - file->f_mapping = h_file->f_mapping; - smp_mb(); /* unnecessary? */ - } - } - fi_read_unlock(file); - - err = vfsub_splice_to(h_file, ppos, pipe, len, flags); - /* todo: necessasry? */ - /* file->f_ra = h_file->f_ra; */ - au_read_post(inode, h_file); - -out: - si_read_unlock(sb); - return err; -} - -static ssize_t -aufs_splice_write(struct pipe_inode_info *pipe, struct file *file, loff_t *ppos, - size_t len, unsigned int flags) -{ - ssize_t err; - struct au_write_pre wpre; - struct inode *inode; - struct file *h_file; - - inode = file_inode(file); - au_mtx_and_read_lock(inode); - - h_file = au_write_pre(file, /*do_ready*/1, &wpre); - err = PTR_ERR(h_file); - if (IS_ERR(h_file)) - goto out; - - err = vfsub_splice_from(pipe, h_file, ppos, len, flags); - au_write_post(inode, h_file, &wpre, err); - -out: - si_read_unlock(inode->i_sb); - mutex_unlock(&inode->i_mutex); - return err; -} - -static long aufs_fallocate(struct file *file, int mode, loff_t offset, - loff_t len) -{ - long err; - struct au_write_pre wpre; - struct inode *inode; - struct file *h_file; - - inode = file_inode(file); - au_mtx_and_read_lock(inode); - - h_file = au_write_pre(file, /*do_ready*/1, &wpre); - err = PTR_ERR(h_file); - if (IS_ERR(h_file)) - goto out; - - lockdep_off(); - err = vfs_fallocate(h_file, mode, offset, len); - lockdep_on(); - au_write_post(inode, h_file, &wpre, /*written*/1); - -out: - si_read_unlock(inode->i_sb); - mutex_unlock(&inode->i_mutex); - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* - * The locking order around current->mmap_sem. - * - in most and regular cases - * file I/O syscall -- aufs_read() or something - * -- si_rwsem for read -- mmap_sem - * (Note that [fdi]i_rwsem are released before mmap_sem). - * - in mmap case - * mmap(2) -- mmap_sem -- aufs_mmap() -- si_rwsem for read -- [fdi]i_rwsem - * This AB-BA order is definitly bad, but is not a problem since "si_rwsem for - * read" allows muliple processes to acquire it and [fdi]i_rwsem are not held in - * file I/O. Aufs needs to stop lockdep in aufs_mmap() though. - * It means that when aufs acquires si_rwsem for write, the process should never - * acquire mmap_sem. - * - * Actually aufs_iterate() holds [fdi]i_rwsem before mmap_sem, but this is not a - * problem either since any directory is not able to be mmap-ed. - * The similar scenario is applied to aufs_readlink() too. - */ - -#if 0 /* stop calling security_file_mmap() */ -/* cf. linux/include/linux/mman.h: calc_vm_prot_bits() */ -#define AuConv_VM_PROT(f, b) _calc_vm_trans(f, VM_##b, PROT_##b) - -static unsigned long au_arch_prot_conv(unsigned long flags) -{ - /* currently ppc64 only */ -#ifdef CONFIG_PPC64 - /* cf. linux/arch/powerpc/include/asm/mman.h */ - AuDebugOn(arch_calc_vm_prot_bits(-1) != VM_SAO); - return AuConv_VM_PROT(flags, SAO); -#else - AuDebugOn(arch_calc_vm_prot_bits(-1)); - return 0; -#endif -} - -static unsigned long au_prot_conv(unsigned long flags) -{ - return AuConv_VM_PROT(flags, READ) - | AuConv_VM_PROT(flags, WRITE) - | AuConv_VM_PROT(flags, EXEC) - | au_arch_prot_conv(flags); -} - -/* cf. linux/include/linux/mman.h: calc_vm_flag_bits() */ -#define AuConv_VM_MAP(f, b) _calc_vm_trans(f, VM_##b, MAP_##b) - -static unsigned long au_flag_conv(unsigned long flags) -{ - return AuConv_VM_MAP(flags, GROWSDOWN) - | AuConv_VM_MAP(flags, DENYWRITE) - | AuConv_VM_MAP(flags, LOCKED); -} -#endif - -static int aufs_mmap(struct file *file, struct vm_area_struct *vma) -{ - int err; - const unsigned char wlock - = (file->f_mode & FMODE_WRITE) && (vma->vm_flags & VM_SHARED); - struct super_block *sb; - struct file *h_file; - struct inode *inode; - - AuDbgVmRegion(file, vma); - - inode = file_inode(file); - sb = inode->i_sb; - lockdep_off(); - si_read_lock(sb, AuLock_NOPLMW); - - h_file = au_write_pre(file, wlock, /*wpre*/NULL); - lockdep_on(); - err = PTR_ERR(h_file); - if (IS_ERR(h_file)) - goto out; - - err = 0; - au_set_mmapped(file); - au_vm_file_reset(vma, h_file); - /* - * we cannot call security_mmap_file() here since it may acquire - * mmap_sem or i_mutex. - * - * err = security_mmap_file(h_file, au_prot_conv(vma->vm_flags), - * au_flag_conv(vma->vm_flags)); - */ - if (!err) - err = h_file->f_op->mmap(h_file, vma); - if (!err) { - au_vm_prfile_set(vma, file); - fsstack_copy_attr_atime(inode, file_inode(h_file)); - goto out_fput; /* success */ - } - au_unset_mmapped(file); - au_vm_file_reset(vma, file); - -out_fput: - lockdep_off(); - ii_write_unlock(inode); - lockdep_on(); - fput(h_file); -out: - lockdep_off(); - si_read_unlock(sb); - lockdep_on(); - AuTraceErr(err); - return err; -} - -/* ---------------------------------------------------------------------- */ - -static int aufs_fsync_nondir(struct file *file, loff_t start, loff_t end, - int datasync) -{ - int err; - struct au_write_pre wpre; - struct inode *inode; - struct file *h_file; - - err = 0; /* -EBADF; */ /* posix? */ - if (unlikely(!(file->f_mode & FMODE_WRITE))) - goto out; - - inode = file_inode(file); - au_mtx_and_read_lock(inode); - - h_file = au_write_pre(file, /*do_ready*/1, &wpre); - err = PTR_ERR(h_file); - if (IS_ERR(h_file)) - goto out_unlock; - - err = vfsub_fsync(h_file, &h_file->f_path, datasync); - au_write_post(inode, h_file, &wpre, /*written*/0); - -out_unlock: - si_read_unlock(inode->i_sb); - mutex_unlock(&inode->i_mutex); -out: - return err; -} - -/* no one supports this operation, currently */ -#if 0 -static int aufs_aio_fsync_nondir(struct kiocb *kio, int datasync) -{ - int err; - struct au_write_pre wpre; - struct inode *inode; - struct file *file, *h_file; - - err = 0; /* -EBADF; */ /* posix? */ - if (unlikely(!(file->f_mode & FMODE_WRITE))) - goto out; - - file = kio->ki_filp; - inode = file_inode(file); - au_mtx_and_read_lock(inode); - - h_file = au_write_pre(file, /*do_ready*/1, &wpre); - err = PTR_ERR(h_file); - if (IS_ERR(h_file)) - goto out_unlock; - - err = -ENOSYS; - h_file = au_hf_top(file); - if (h_file->f_op->aio_fsync) { - struct mutex *h_mtx; - - h_mtx = &file_inode(h_file)->i_mutex; - if (!is_sync_kiocb(kio)) { - get_file(h_file); - fput(file); - } - kio->ki_filp = h_file; - err = h_file->f_op->aio_fsync(kio, datasync); - mutex_lock_nested(h_mtx, AuLsc_I_CHILD); - if (!err) - vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); - /*ignore*/ - mutex_unlock(h_mtx); - } - au_write_post(inode, h_file, &wpre, /*written*/0); - -out_unlock: - si_read_unlock(inode->sb); - mutex_unlock(&inode->i_mutex); -out: - return err; -} -#endif - -static int aufs_fasync(int fd, struct file *file, int flag) -{ - int err; - struct file *h_file; - struct super_block *sb; - - sb = file->f_path.dentry->d_sb; - si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); - - h_file = au_read_pre(file, /*keep_fi*/0); - err = PTR_ERR(h_file); - if (IS_ERR(h_file)) - goto out; - - if (h_file->f_op->fasync) - err = h_file->f_op->fasync(fd, h_file, flag); - fput(h_file); /* instead of au_read_post() */ - -out: - si_read_unlock(sb); - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* no one supports this operation, currently */ -#if 0 -static ssize_t aufs_sendpage(struct file *file, struct page *page, int offset, - size_t len, loff_t *pos, int more) -{ -} -#endif - -/* ---------------------------------------------------------------------- */ - -const struct file_operations aufs_file_fop = { - .owner = THIS_MODULE, - - .llseek = default_llseek, - - .read = aufs_read, - .write = aufs_write, - .read_iter = aufs_read_iter, - .write_iter = aufs_write_iter, - -#ifdef CONFIG_AUFS_POLL - .poll = aufs_poll, -#endif - .unlocked_ioctl = aufs_ioctl_nondir, -#ifdef CONFIG_COMPAT - .compat_ioctl = aufs_compat_ioctl_nondir, -#endif - .mmap = aufs_mmap, - .open = aufs_open_nondir, - .flush = aufs_flush_nondir, - .release = aufs_release_nondir, - .fsync = aufs_fsync_nondir, - /* .aio_fsync = aufs_aio_fsync_nondir, */ - .fasync = aufs_fasync, - /* .sendpage = aufs_sendpage, */ - .splice_write = aufs_splice_write, - .splice_read = aufs_splice_read, -#if 0 - .aio_splice_write = aufs_aio_splice_write, - .aio_splice_read = aufs_aio_splice_read, -#endif - .fallocate = aufs_fallocate -}; diff --git a/fs/aufs/fhsm.c b/fs/aufs/fhsm.c deleted file mode 100644 index 7ca4d1759..000000000 --- a/fs/aufs/fhsm.c +++ /dev/null @@ -1,426 +0,0 @@ -/* - * Copyright (C) 2011-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* - * File-based Hierarchy Storage Management - */ - -#include <linux/anon_inodes.h> -#include <linux/poll.h> -#include <linux/seq_file.h> -#include <linux/statfs.h> -#include "aufs.h" - -static aufs_bindex_t au_fhsm_bottom(struct super_block *sb) -{ - struct au_sbinfo *sbinfo; - struct au_fhsm *fhsm; - - SiMustAnyLock(sb); - - sbinfo = au_sbi(sb); - fhsm = &sbinfo->si_fhsm; - AuDebugOn(!fhsm); - return fhsm->fhsm_bottom; -} - -void au_fhsm_set_bottom(struct super_block *sb, aufs_bindex_t bindex) -{ - struct au_sbinfo *sbinfo; - struct au_fhsm *fhsm; - - SiMustWriteLock(sb); - - sbinfo = au_sbi(sb); - fhsm = &sbinfo->si_fhsm; - AuDebugOn(!fhsm); - fhsm->fhsm_bottom = bindex; -} - -/* ---------------------------------------------------------------------- */ - -static int au_fhsm_test_jiffy(struct au_sbinfo *sbinfo, struct au_branch *br) -{ - struct au_br_fhsm *bf; - - bf = br->br_fhsm; - MtxMustLock(&bf->bf_lock); - - return !bf->bf_readable - || time_after(jiffies, - bf->bf_jiffy + sbinfo->si_fhsm.fhsm_expire); -} - -/* ---------------------------------------------------------------------- */ - -static void au_fhsm_notify(struct super_block *sb, int val) -{ - struct au_sbinfo *sbinfo; - struct au_fhsm *fhsm; - - SiMustAnyLock(sb); - - sbinfo = au_sbi(sb); - fhsm = &sbinfo->si_fhsm; - if (au_fhsm_pid(fhsm) - && atomic_read(&fhsm->fhsm_readable) != -1) { - atomic_set(&fhsm->fhsm_readable, val); - if (val) - wake_up(&fhsm->fhsm_wqh); - } -} - -static int au_fhsm_stfs(struct super_block *sb, aufs_bindex_t bindex, - struct aufs_stfs *rstfs, int do_lock, int do_notify) -{ - int err; - struct au_branch *br; - struct au_br_fhsm *bf; - - br = au_sbr(sb, bindex); - AuDebugOn(au_br_rdonly(br)); - bf = br->br_fhsm; - AuDebugOn(!bf); - - if (do_lock) - mutex_lock(&bf->bf_lock); - else - MtxMustLock(&bf->bf_lock); - - /* sb->s_root for NFS is unreliable */ - err = au_br_stfs(br, &bf->bf_stfs); - if (unlikely(err)) { - AuErr1("FHSM failed (%d), b%d, ignored.\n", bindex, err); - goto out; - } - - bf->bf_jiffy = jiffies; - bf->bf_readable = 1; - if (do_notify) - au_fhsm_notify(sb, /*val*/1); - if (rstfs) - *rstfs = bf->bf_stfs; - -out: - if (do_lock) - mutex_unlock(&bf->bf_lock); - au_fhsm_notify(sb, /*val*/1); - - return err; -} - -void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force) -{ - int err; - struct au_sbinfo *sbinfo; - struct au_fhsm *fhsm; - struct au_branch *br; - struct au_br_fhsm *bf; - - AuDbg("b%d, force %d\n", bindex, force); - SiMustAnyLock(sb); - - sbinfo = au_sbi(sb); - fhsm = &sbinfo->si_fhsm; - if (!au_ftest_si(sbinfo, FHSM) - || fhsm->fhsm_bottom == bindex) - return; - - br = au_sbr(sb, bindex); - bf = br->br_fhsm; - AuDebugOn(!bf); - mutex_lock(&bf->bf_lock); - if (force - || au_fhsm_pid(fhsm) - || au_fhsm_test_jiffy(sbinfo, br)) - err = au_fhsm_stfs(sb, bindex, /*rstfs*/NULL, /*do_lock*/0, - /*do_notify*/1); - mutex_unlock(&bf->bf_lock); -} - -void au_fhsm_wrote_all(struct super_block *sb, int force) -{ - aufs_bindex_t bindex, bend; - struct au_branch *br; - - /* exclude the bottom */ - bend = au_fhsm_bottom(sb); - for (bindex = 0; bindex < bend; bindex++) { - br = au_sbr(sb, bindex); - if (au_br_fhsm(br->br_perm)) - au_fhsm_wrote(sb, bindex, force); - } -} - -/* ---------------------------------------------------------------------- */ - -static unsigned int au_fhsm_poll(struct file *file, - struct poll_table_struct *wait) -{ - unsigned int mask; - struct au_sbinfo *sbinfo; - struct au_fhsm *fhsm; - - mask = 0; - sbinfo = file->private_data; - fhsm = &sbinfo->si_fhsm; - poll_wait(file, &fhsm->fhsm_wqh, wait); - if (atomic_read(&fhsm->fhsm_readable)) - mask = POLLIN /* | POLLRDNORM */; - - AuTraceErr((int)mask); - return mask; -} - -static int au_fhsm_do_read_one(struct aufs_stbr __user *stbr, - struct aufs_stfs *stfs, __s16 brid) -{ - int err; - - err = copy_to_user(&stbr->stfs, stfs, sizeof(*stfs)); - if (!err) - err = __put_user(brid, &stbr->brid); - if (unlikely(err)) - err = -EFAULT; - - return err; -} - -static ssize_t au_fhsm_do_read(struct super_block *sb, - struct aufs_stbr __user *stbr, size_t count) -{ - ssize_t err; - int nstbr; - aufs_bindex_t bindex, bend; - struct au_branch *br; - struct au_br_fhsm *bf; - - /* except the bottom branch */ - err = 0; - nstbr = 0; - bend = au_fhsm_bottom(sb); - for (bindex = 0; !err && bindex < bend; bindex++) { - br = au_sbr(sb, bindex); - if (!au_br_fhsm(br->br_perm)) - continue; - - bf = br->br_fhsm; - mutex_lock(&bf->bf_lock); - if (bf->bf_readable) { - err = -EFAULT; - if (count >= sizeof(*stbr)) - err = au_fhsm_do_read_one(stbr++, &bf->bf_stfs, - br->br_id); - if (!err) { - bf->bf_readable = 0; - count -= sizeof(*stbr); - nstbr++; - } - } - mutex_unlock(&bf->bf_lock); - } - if (!err) - err = sizeof(*stbr) * nstbr; - - return err; -} - -static ssize_t au_fhsm_read(struct file *file, char __user *buf, size_t count, - loff_t *pos) -{ - ssize_t err; - int readable; - aufs_bindex_t nfhsm, bindex, bend; - struct au_sbinfo *sbinfo; - struct au_fhsm *fhsm; - struct au_branch *br; - struct super_block *sb; - - err = 0; - sbinfo = file->private_data; - fhsm = &sbinfo->si_fhsm; -need_data: - spin_lock_irq(&fhsm->fhsm_wqh.lock); - if (!atomic_read(&fhsm->fhsm_readable)) { - if (vfsub_file_flags(file) & O_NONBLOCK) - err = -EAGAIN; - else - err = wait_event_interruptible_locked_irq - (fhsm->fhsm_wqh, - atomic_read(&fhsm->fhsm_readable)); - } - spin_unlock_irq(&fhsm->fhsm_wqh.lock); - if (unlikely(err)) - goto out; - - /* sb may already be dead */ - au_rw_read_lock(&sbinfo->si_rwsem); - readable = atomic_read(&fhsm->fhsm_readable); - if (readable > 0) { - sb = sbinfo->si_sb; - AuDebugOn(!sb); - /* exclude the bottom branch */ - nfhsm = 0; - bend = au_fhsm_bottom(sb); - for (bindex = 0; bindex < bend; bindex++) { - br = au_sbr(sb, bindex); - if (au_br_fhsm(br->br_perm)) - nfhsm++; - } - err = -EMSGSIZE; - if (nfhsm * sizeof(struct aufs_stbr) <= count) { - atomic_set(&fhsm->fhsm_readable, 0); - err = au_fhsm_do_read(sbinfo->si_sb, (void __user *)buf, - count); - } - } - au_rw_read_unlock(&sbinfo->si_rwsem); - if (!readable) - goto need_data; - -out: - return err; -} - -static int au_fhsm_release(struct inode *inode, struct file *file) -{ - struct au_sbinfo *sbinfo; - struct au_fhsm *fhsm; - - /* sb may already be dead */ - sbinfo = file->private_data; - fhsm = &sbinfo->si_fhsm; - spin_lock(&fhsm->fhsm_spin); - fhsm->fhsm_pid = 0; - spin_unlock(&fhsm->fhsm_spin); - kobject_put(&sbinfo->si_kobj); - - return 0; -} - -static const struct file_operations au_fhsm_fops = { - .owner = THIS_MODULE, - .llseek = noop_llseek, - .read = au_fhsm_read, - .poll = au_fhsm_poll, - .release = au_fhsm_release -}; - -int au_fhsm_fd(struct super_block *sb, int oflags) -{ - int err, fd; - struct au_sbinfo *sbinfo; - struct au_fhsm *fhsm; - - err = -EPERM; - if (unlikely(!capable(CAP_SYS_ADMIN))) - goto out; - - err = -EINVAL; - if (unlikely(oflags & ~(O_CLOEXEC | O_NONBLOCK))) - goto out; - - err = 0; - sbinfo = au_sbi(sb); - fhsm = &sbinfo->si_fhsm; - spin_lock(&fhsm->fhsm_spin); - if (!fhsm->fhsm_pid) - fhsm->fhsm_pid = current->pid; - else - err = -EBUSY; - spin_unlock(&fhsm->fhsm_spin); - if (unlikely(err)) - goto out; - - oflags |= O_RDONLY; - /* oflags |= FMODE_NONOTIFY; */ - fd = anon_inode_getfd("[aufs_fhsm]", &au_fhsm_fops, sbinfo, oflags); - err = fd; - if (unlikely(fd < 0)) - goto out_pid; - - /* succeed reglardless 'fhsm' status */ - kobject_get(&sbinfo->si_kobj); - si_noflush_read_lock(sb); - if (au_ftest_si(sbinfo, FHSM)) - au_fhsm_wrote_all(sb, /*force*/0); - si_read_unlock(sb); - goto out; /* success */ - -out_pid: - spin_lock(&fhsm->fhsm_spin); - fhsm->fhsm_pid = 0; - spin_unlock(&fhsm->fhsm_spin); -out: - AuTraceErr(err); - return err; -} - -/* ---------------------------------------------------------------------- */ - -int au_fhsm_br_alloc(struct au_branch *br) -{ - int err; - - err = 0; - br->br_fhsm = kmalloc(sizeof(*br->br_fhsm), GFP_NOFS); - if (br->br_fhsm) - au_br_fhsm_init(br->br_fhsm); - else - err = -ENOMEM; - - return err; -} - -/* ---------------------------------------------------------------------- */ - -void au_fhsm_fin(struct super_block *sb) -{ - au_fhsm_notify(sb, /*val*/-1); -} - -void au_fhsm_init(struct au_sbinfo *sbinfo) -{ - struct au_fhsm *fhsm; - - fhsm = &sbinfo->si_fhsm; - spin_lock_init(&fhsm->fhsm_spin); - init_waitqueue_head(&fhsm->fhsm_wqh); - atomic_set(&fhsm->fhsm_readable, 0); - fhsm->fhsm_expire - = msecs_to_jiffies(AUFS_FHSM_CACHE_DEF_SEC * MSEC_PER_SEC); - fhsm->fhsm_bottom = -1; -} - -void au_fhsm_set(struct au_sbinfo *sbinfo, unsigned int sec) -{ - sbinfo->si_fhsm.fhsm_expire - = msecs_to_jiffies(sec * MSEC_PER_SEC); -} - -void au_fhsm_show(struct seq_file *seq, struct au_sbinfo *sbinfo) -{ - unsigned int u; - - if (!au_ftest_si(sbinfo, FHSM)) - return; - - u = jiffies_to_msecs(sbinfo->si_fhsm.fhsm_expire) / MSEC_PER_SEC; - if (u != AUFS_FHSM_CACHE_DEF_SEC) - seq_printf(seq, ",fhsm_sec=%u", u); -} diff --git a/fs/aufs/file.c b/fs/aufs/file.c deleted file mode 100644 index d3a566e4f..000000000 --- a/fs/aufs/file.c +++ /dev/null @@ -1,841 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * handling file/dir, and address_space operation - */ - -#ifdef CONFIG_AUFS_DEBUG -#include <linux/migrate.h> -#endif -#include <linux/pagemap.h> -#include "aufs.h" - -/* drop flags for writing */ -unsigned int au_file_roflags(unsigned int flags) -{ - flags &= ~(O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC); - flags |= O_RDONLY | O_NOATIME; - return flags; -} - -/* common functions to regular file and dir */ -struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, - struct file *file, int force_wr) -{ - struct file *h_file; - struct dentry *h_dentry; - struct inode *h_inode; - struct super_block *sb; - struct au_branch *br; - struct path h_path; - int err; - - /* a race condition can happen between open and unlink/rmdir */ - h_file = ERR_PTR(-ENOENT); - h_dentry = au_h_dptr(dentry, bindex); - if (au_test_nfsd() && (!h_dentry || d_is_negative(h_dentry))) - goto out; - h_inode = d_inode(h_dentry); - spin_lock(&h_dentry->d_lock); - err = (!d_unhashed(dentry) && d_unlinked(h_dentry)) - /* || !d_inode(dentry)->i_nlink */ - ; - spin_unlock(&h_dentry->d_lock); - if (unlikely(err)) - goto out; - - sb = dentry->d_sb; - br = au_sbr(sb, bindex); - err = au_br_test_oflag(flags, br); - h_file = ERR_PTR(err); - if (unlikely(err)) - goto out; - - /* drop flags for writing */ - if (au_test_ro(sb, bindex, d_inode(dentry))) { - if (force_wr && !(flags & O_WRONLY)) - force_wr = 0; - flags = au_file_roflags(flags); - if (force_wr) { - h_file = ERR_PTR(-EROFS); - flags = au_file_roflags(flags); - if (unlikely(vfsub_native_ro(h_inode) - || IS_APPEND(h_inode))) - goto out; - flags &= ~O_ACCMODE; - flags |= O_WRONLY; - } - } - flags &= ~O_CREAT; - atomic_inc(&br->br_count); - h_path.dentry = h_dentry; - h_path.mnt = au_br_mnt(br); - h_file = vfsub_dentry_open(&h_path, flags); - if (IS_ERR(h_file)) - goto out_br; - - if (flags & __FMODE_EXEC) { - err = deny_write_access(h_file); - if (unlikely(err)) { - fput(h_file); - h_file = ERR_PTR(err); - goto out_br; - } - } - fsnotify_open(h_file); - goto out; /* success */ - -out_br: - atomic_dec(&br->br_count); -out: - return h_file; -} - -static int au_cmoo(struct dentry *dentry) -{ - int err, cmoo; - unsigned int udba; - struct path h_path; - struct au_pin pin; - struct au_cp_generic cpg = { - .dentry = dentry, - .bdst = -1, - .bsrc = -1, - .len = -1, - .pin = &pin, - .flags = AuCpup_DTIME | AuCpup_HOPEN - }; - struct inode *delegated; - struct super_block *sb; - struct au_sbinfo *sbinfo; - struct au_fhsm *fhsm; - pid_t pid; - struct au_branch *br; - struct dentry *parent; - struct au_hinode *hdir; - - DiMustWriteLock(dentry); - IiMustWriteLock(d_inode(dentry)); - - err = 0; - if (IS_ROOT(dentry)) - goto out; - cpg.bsrc = au_dbstart(dentry); - if (!cpg.bsrc) - goto out; - - sb = dentry->d_sb; - sbinfo = au_sbi(sb); - fhsm = &sbinfo->si_fhsm; - pid = au_fhsm_pid(fhsm); - if (pid - && (current->pid == pid - || current->real_parent->pid == pid)) - goto out; - - br = au_sbr(sb, cpg.bsrc); - cmoo = au_br_cmoo(br->br_perm); - if (!cmoo) - goto out; - if (!d_is_reg(dentry)) - cmoo &= AuBrAttr_COO_ALL; - if (!cmoo) - goto out; - - parent = dget_parent(dentry); - di_write_lock_parent(parent); - err = au_wbr_do_copyup_bu(dentry, cpg.bsrc - 1); - cpg.bdst = err; - if (unlikely(err < 0)) { - err = 0; /* there is no upper writable branch */ - goto out_dgrade; - } - AuDbg("bsrc %d, bdst %d\n", cpg.bsrc, cpg.bdst); - - /* do not respect the coo attrib for the target branch */ - err = au_cpup_dirs(dentry, cpg.bdst); - if (unlikely(err)) - goto out_dgrade; - - di_downgrade_lock(parent, AuLock_IR); - udba = au_opt_udba(sb); - err = au_pin(&pin, dentry, cpg.bdst, udba, - AuPin_DI_LOCKED | AuPin_MNT_WRITE); - if (unlikely(err)) - goto out_parent; - - err = au_sio_cpup_simple(&cpg); - au_unpin(&pin); - if (unlikely(err)) - goto out_parent; - if (!(cmoo & AuBrWAttr_MOO)) - goto out_parent; /* success */ - - err = au_pin(&pin, dentry, cpg.bsrc, udba, - AuPin_DI_LOCKED | AuPin_MNT_WRITE); - if (unlikely(err)) - goto out_parent; - - h_path.mnt = au_br_mnt(br); - h_path.dentry = au_h_dptr(dentry, cpg.bsrc); - hdir = au_hi(d_inode(parent), cpg.bsrc); - delegated = NULL; - err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, /*force*/1); - au_unpin(&pin); - /* todo: keep h_dentry or not? */ - if (unlikely(err == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal unlink\n"); - iput(delegated); - } - if (unlikely(err)) { - pr_err("unlink %pd after coo failed (%d), ignored\n", - dentry, err); - err = 0; - } - goto out_parent; /* success */ - -out_dgrade: - di_downgrade_lock(parent, AuLock_IR); -out_parent: - di_read_unlock(parent, AuLock_IR); - dput(parent); -out: - AuTraceErr(err); - return err; -} - -int au_do_open(struct file *file, struct au_do_open_args *args) -{ - int err, no_lock = args->no_lock; - struct dentry *dentry; - struct au_finfo *finfo; - - if (!no_lock) - err = au_finfo_init(file, args->fidir); - else { - lockdep_off(); - err = au_finfo_init(file, args->fidir); - lockdep_on(); - } - if (unlikely(err)) - goto out; - - dentry = file->f_path.dentry; - AuDebugOn(IS_ERR_OR_NULL(dentry)); - if (!no_lock) { - di_write_lock_child(dentry); - err = au_cmoo(dentry); - di_downgrade_lock(dentry, AuLock_IR); - if (!err) - err = args->open(file, vfsub_file_flags(file), NULL); - di_read_unlock(dentry, AuLock_IR); - } else { - err = au_cmoo(dentry); - if (!err) - err = args->open(file, vfsub_file_flags(file), - args->h_file); - if (!err && au_fbstart(file) != au_dbstart(dentry)) - /* - * cmoo happens after h_file was opened. - * need to refresh file later. - */ - atomic_dec(&au_fi(file)->fi_generation); - } - - finfo = au_fi(file); - if (!err) { - finfo->fi_file = file; - au_sphl_add(&finfo->fi_hlist, - &au_sbi(file->f_path.dentry->d_sb)->si_files); - } - if (!no_lock) - fi_write_unlock(file); - else { - lockdep_off(); - fi_write_unlock(file); - lockdep_on(); - } - if (unlikely(err)) { - finfo->fi_hdir = NULL; - au_finfo_fin(file); - } - -out: - return err; -} - -int au_reopen_nondir(struct file *file) -{ - int err; - aufs_bindex_t bstart; - struct dentry *dentry; - struct file *h_file, *h_file_tmp; - - dentry = file->f_path.dentry; - bstart = au_dbstart(dentry); - h_file_tmp = NULL; - if (au_fbstart(file) == bstart) { - h_file = au_hf_top(file); - if (file->f_mode == h_file->f_mode) - return 0; /* success */ - h_file_tmp = h_file; - get_file(h_file_tmp); - au_set_h_fptr(file, bstart, NULL); - } - AuDebugOn(au_fi(file)->fi_hdir); - /* - * it can happen - * file exists on both of rw and ro - * open --> dbstart and fbstart are both 0 - * prepend a branch as rw, "rw" become ro - * remove rw/file - * delete the top branch, "rw" becomes rw again - * --> dbstart is 1, fbstart is still 0 - * write --> fbstart is 0 but dbstart is 1 - */ - /* AuDebugOn(au_fbstart(file) < bstart); */ - - h_file = au_h_open(dentry, bstart, vfsub_file_flags(file) & ~O_TRUNC, - file, /*force_wr*/0); - err = PTR_ERR(h_file); - if (IS_ERR(h_file)) { - if (h_file_tmp) { - atomic_inc(&au_sbr(dentry->d_sb, bstart)->br_count); - au_set_h_fptr(file, bstart, h_file_tmp); - h_file_tmp = NULL; - } - goto out; /* todo: close all? */ - } - - err = 0; - au_set_fbstart(file, bstart); - au_set_h_fptr(file, bstart, h_file); - au_update_figen(file); - /* todo: necessary? */ - /* file->f_ra = h_file->f_ra; */ - -out: - if (h_file_tmp) - fput(h_file_tmp); - return err; -} - -/* ---------------------------------------------------------------------- */ - -static int au_reopen_wh(struct file *file, aufs_bindex_t btgt, - struct dentry *hi_wh) -{ - int err; - aufs_bindex_t bstart; - struct au_dinfo *dinfo; - struct dentry *h_dentry; - struct au_hdentry *hdp; - - dinfo = au_di(file->f_path.dentry); - AuRwMustWriteLock(&dinfo->di_rwsem); - - bstart = dinfo->di_bstart; - dinfo->di_bstart = btgt; - hdp = dinfo->di_hdentry; - h_dentry = hdp[0 + btgt].hd_dentry; - hdp[0 + btgt].hd_dentry = hi_wh; - err = au_reopen_nondir(file); - hdp[0 + btgt].hd_dentry = h_dentry; - dinfo->di_bstart = bstart; - - return err; -} - -static int au_ready_to_write_wh(struct file *file, loff_t len, - aufs_bindex_t bcpup, struct au_pin *pin) -{ - int err; - struct inode *inode, *h_inode; - struct dentry *h_dentry, *hi_wh; - struct au_cp_generic cpg = { - .dentry = file->f_path.dentry, - .bdst = bcpup, - .bsrc = -1, - .len = len, - .pin = pin - }; - - au_update_dbstart(cpg.dentry); - inode = d_inode(cpg.dentry); - h_inode = NULL; - if (au_dbstart(cpg.dentry) <= bcpup - && au_dbend(cpg.dentry) >= bcpup) { - h_dentry = au_h_dptr(cpg.dentry, bcpup); - if (h_dentry && d_is_positive(h_dentry)) - h_inode = d_inode(h_dentry); - } - hi_wh = au_hi_wh(inode, bcpup); - if (!hi_wh && !h_inode) - err = au_sio_cpup_wh(&cpg, file); - else - /* already copied-up after unlink */ - err = au_reopen_wh(file, bcpup, hi_wh); - - if (!err - && (inode->i_nlink > 1 - || (inode->i_state & I_LINKABLE)) - && au_opt_test(au_mntflags(cpg.dentry->d_sb), PLINK)) - au_plink_append(inode, bcpup, au_h_dptr(cpg.dentry, bcpup)); - - return err; -} - -/* - * prepare the @file for writing. - */ -int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) -{ - int err; - aufs_bindex_t dbstart; - struct dentry *parent; - struct inode *inode; - struct super_block *sb; - struct file *h_file; - struct au_cp_generic cpg = { - .dentry = file->f_path.dentry, - .bdst = -1, - .bsrc = -1, - .len = len, - .pin = pin, - .flags = AuCpup_DTIME - }; - - sb = cpg.dentry->d_sb; - inode = d_inode(cpg.dentry); - cpg.bsrc = au_fbstart(file); - err = au_test_ro(sb, cpg.bsrc, inode); - if (!err && (au_hf_top(file)->f_mode & FMODE_WRITE)) { - err = au_pin(pin, cpg.dentry, cpg.bsrc, AuOpt_UDBA_NONE, - /*flags*/0); - goto out; - } - - /* need to cpup or reopen */ - parent = dget_parent(cpg.dentry); - di_write_lock_parent(parent); - err = AuWbrCopyup(au_sbi(sb), cpg.dentry); - cpg.bdst = err; - if (unlikely(err < 0)) - goto out_dgrade; - err = 0; - - if (!d_unhashed(cpg.dentry) && !au_h_dptr(parent, cpg.bdst)) { - err = au_cpup_dirs(cpg.dentry, cpg.bdst); - if (unlikely(err)) - goto out_dgrade; - } - - err = au_pin(pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, - AuPin_DI_LOCKED | AuPin_MNT_WRITE); - if (unlikely(err)) - goto out_dgrade; - - dbstart = au_dbstart(cpg.dentry); - if (dbstart <= cpg.bdst) - cpg.bsrc = cpg.bdst; - - if (dbstart <= cpg.bdst /* just reopen */ - || !d_unhashed(cpg.dentry) /* copyup and reopen */ - ) { - h_file = au_h_open_pre(cpg.dentry, cpg.bsrc, /*force_wr*/0); - if (IS_ERR(h_file)) - err = PTR_ERR(h_file); - else { - di_downgrade_lock(parent, AuLock_IR); - if (dbstart > cpg.bdst) - err = au_sio_cpup_simple(&cpg); - if (!err) - err = au_reopen_nondir(file); - au_h_open_post(cpg.dentry, cpg.bsrc, h_file); - } - } else { /* copyup as wh and reopen */ - /* - * since writable hfsplus branch is not supported, - * h_open_pre/post() are unnecessary. - */ - err = au_ready_to_write_wh(file, len, cpg.bdst, pin); - di_downgrade_lock(parent, AuLock_IR); - } - - if (!err) { - au_pin_set_parent_lflag(pin, /*lflag*/0); - goto out_dput; /* success */ - } - au_unpin(pin); - goto out_unlock; - -out_dgrade: - di_downgrade_lock(parent, AuLock_IR); -out_unlock: - di_read_unlock(parent, AuLock_IR); -out_dput: - dput(parent); -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -int au_do_flush(struct file *file, fl_owner_t id, - int (*flush)(struct file *file, fl_owner_t id)) -{ - int err; - struct super_block *sb; - struct inode *inode; - - inode = file_inode(file); - sb = inode->i_sb; - si_noflush_read_lock(sb); - fi_read_lock(file); - ii_read_lock_child(inode); - - err = flush(file, id); - au_cpup_attr_timesizes(inode); - - ii_read_unlock(inode); - fi_read_unlock(file); - si_read_unlock(sb); - return err; -} - -/* ---------------------------------------------------------------------- */ - -static int au_file_refresh_by_inode(struct file *file, int *need_reopen) -{ - int err; - struct au_pin pin; - struct au_finfo *finfo; - struct dentry *parent, *hi_wh; - struct inode *inode; - struct super_block *sb; - struct au_cp_generic cpg = { - .dentry = file->f_path.dentry, - .bdst = -1, - .bsrc = -1, - .len = -1, - .pin = &pin, - .flags = AuCpup_DTIME - }; - - FiMustWriteLock(file); - - err = 0; - finfo = au_fi(file); - sb = cpg.dentry->d_sb; - inode = d_inode(cpg.dentry); - cpg.bdst = au_ibstart(inode); - if (cpg.bdst == finfo->fi_btop || IS_ROOT(cpg.dentry)) - goto out; - - parent = dget_parent(cpg.dentry); - if (au_test_ro(sb, cpg.bdst, inode)) { - di_read_lock_parent(parent, !AuLock_IR); - err = AuWbrCopyup(au_sbi(sb), cpg.dentry); - cpg.bdst = err; - di_read_unlock(parent, !AuLock_IR); - if (unlikely(err < 0)) - goto out_parent; - err = 0; - } - - di_read_lock_parent(parent, AuLock_IR); - hi_wh = au_hi_wh(inode, cpg.bdst); - if (!S_ISDIR(inode->i_mode) - && au_opt_test(au_mntflags(sb), PLINK) - && au_plink_test(inode) - && !d_unhashed(cpg.dentry) - && cpg.bdst < au_dbstart(cpg.dentry)) { - err = au_test_and_cpup_dirs(cpg.dentry, cpg.bdst); - if (unlikely(err)) - goto out_unlock; - - /* always superio. */ - err = au_pin(&pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, - AuPin_DI_LOCKED | AuPin_MNT_WRITE); - if (!err) { - err = au_sio_cpup_simple(&cpg); - au_unpin(&pin); - } - } else if (hi_wh) { - /* already copied-up after unlink */ - err = au_reopen_wh(file, cpg.bdst, hi_wh); - *need_reopen = 0; - } - -out_unlock: - di_read_unlock(parent, AuLock_IR); -out_parent: - dput(parent); -out: - return err; -} - -static void au_do_refresh_dir(struct file *file) -{ - aufs_bindex_t bindex, bend, new_bindex, brid; - struct au_hfile *p, tmp, *q; - struct au_finfo *finfo; - struct super_block *sb; - struct au_fidir *fidir; - - FiMustWriteLock(file); - - sb = file->f_path.dentry->d_sb; - finfo = au_fi(file); - fidir = finfo->fi_hdir; - AuDebugOn(!fidir); - p = fidir->fd_hfile + finfo->fi_btop; - brid = p->hf_br->br_id; - bend = fidir->fd_bbot; - for (bindex = finfo->fi_btop; bindex <= bend; bindex++, p++) { - if (!p->hf_file) - continue; - - new_bindex = au_br_index(sb, p->hf_br->br_id); - if (new_bindex == bindex) - continue; - if (new_bindex < 0) { - au_set_h_fptr(file, bindex, NULL); - continue; - } - - /* swap two lower inode, and loop again */ - q = fidir->fd_hfile + new_bindex; - tmp = *q; - *q = *p; - *p = tmp; - if (tmp.hf_file) { - bindex--; - p--; - } - } - - p = fidir->fd_hfile; - if (!au_test_mmapped(file) && !d_unlinked(file->f_path.dentry)) { - bend = au_sbend(sb); - for (finfo->fi_btop = 0; finfo->fi_btop <= bend; - finfo->fi_btop++, p++) - if (p->hf_file) { - if (file_inode(p->hf_file)) - break; - au_hfput(p, file); - } - } else { - bend = au_br_index(sb, brid); - for (finfo->fi_btop = 0; finfo->fi_btop < bend; - finfo->fi_btop++, p++) - if (p->hf_file) - au_hfput(p, file); - bend = au_sbend(sb); - } - - p = fidir->fd_hfile + bend; - for (fidir->fd_bbot = bend; fidir->fd_bbot >= finfo->fi_btop; - fidir->fd_bbot--, p--) - if (p->hf_file) { - if (file_inode(p->hf_file)) - break; - au_hfput(p, file); - } - AuDebugOn(fidir->fd_bbot < finfo->fi_btop); -} - -/* - * after branch manipulating, refresh the file. - */ -static int refresh_file(struct file *file, int (*reopen)(struct file *file)) -{ - int err, need_reopen; - aufs_bindex_t bend, bindex; - struct dentry *dentry; - struct au_finfo *finfo; - struct au_hfile *hfile; - - dentry = file->f_path.dentry; - finfo = au_fi(file); - if (!finfo->fi_hdir) { - hfile = &finfo->fi_htop; - AuDebugOn(!hfile->hf_file); - bindex = au_br_index(dentry->d_sb, hfile->hf_br->br_id); - AuDebugOn(bindex < 0); - if (bindex != finfo->fi_btop) - au_set_fbstart(file, bindex); - } else { - err = au_fidir_realloc(finfo, au_sbend(dentry->d_sb) + 1); - if (unlikely(err)) - goto out; - au_do_refresh_dir(file); - } - - err = 0; - need_reopen = 1; - if (!au_test_mmapped(file)) - err = au_file_refresh_by_inode(file, &need_reopen); - if (!err && need_reopen && !d_unlinked(dentry)) - err = reopen(file); - if (!err) { - au_update_figen(file); - goto out; /* success */ - } - - /* error, close all lower files */ - if (finfo->fi_hdir) { - bend = au_fbend_dir(file); - for (bindex = au_fbstart(file); bindex <= bend; bindex++) - au_set_h_fptr(file, bindex, NULL); - } - -out: - return err; -} - -/* common function to regular file and dir */ -int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), - int wlock) -{ - int err; - unsigned int sigen, figen; - aufs_bindex_t bstart; - unsigned char pseudo_link; - struct dentry *dentry; - struct inode *inode; - - err = 0; - dentry = file->f_path.dentry; - inode = d_inode(dentry); - sigen = au_sigen(dentry->d_sb); - fi_write_lock(file); - figen = au_figen(file); - di_write_lock_child(dentry); - bstart = au_dbstart(dentry); - pseudo_link = (bstart != au_ibstart(inode)); - if (sigen == figen && !pseudo_link && au_fbstart(file) == bstart) { - if (!wlock) { - di_downgrade_lock(dentry, AuLock_IR); - fi_downgrade_lock(file); - } - goto out; /* success */ - } - - AuDbg("sigen %d, figen %d\n", sigen, figen); - if (au_digen_test(dentry, sigen)) { - err = au_reval_dpath(dentry, sigen); - AuDebugOn(!err && au_digen_test(dentry, sigen)); - } - - if (!err) - err = refresh_file(file, reopen); - if (!err) { - if (!wlock) { - di_downgrade_lock(dentry, AuLock_IR); - fi_downgrade_lock(file); - } - } else { - di_write_unlock(dentry); - fi_write_unlock(file); - } - -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* cf. aufs_nopage() */ -/* for madvise(2) */ -static int aufs_readpage(struct file *file __maybe_unused, struct page *page) -{ - unlock_page(page); - return 0; -} - -/* it will never be called, but necessary to support O_DIRECT */ -static ssize_t aufs_direct_IO(struct kiocb *iocb, struct iov_iter *iter, - loff_t offset) -{ BUG(); return 0; } - -/* they will never be called. */ -#ifdef CONFIG_AUFS_DEBUG -static int aufs_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata) -{ AuUnsupport(); return 0; } -static int aufs_write_end(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned copied, - struct page *page, void *fsdata) -{ AuUnsupport(); return 0; } -static int aufs_writepage(struct page *page, struct writeback_control *wbc) -{ AuUnsupport(); return 0; } - -static int aufs_set_page_dirty(struct page *page) -{ AuUnsupport(); return 0; } -static void aufs_invalidatepage(struct page *page, unsigned int offset, - unsigned int length) -{ AuUnsupport(); } -static int aufs_releasepage(struct page *page, gfp_t gfp) -{ AuUnsupport(); return 0; } -static int aufs_migratepage(struct address_space *mapping, struct page *newpage, - struct page *page, enum migrate_mode mode) -{ AuUnsupport(); return 0; } -static int aufs_launder_page(struct page *page) -{ AuUnsupport(); return 0; } -static int aufs_is_partially_uptodate(struct page *page, - unsigned long from, - unsigned long count) -{ AuUnsupport(); return 0; } -static void aufs_is_dirty_writeback(struct page *page, bool *dirty, - bool *writeback) -{ AuUnsupport(); } -static int aufs_error_remove_page(struct address_space *mapping, - struct page *page) -{ AuUnsupport(); return 0; } -static int aufs_swap_activate(struct swap_info_struct *sis, struct file *file, - sector_t *span) -{ AuUnsupport(); return 0; } -static void aufs_swap_deactivate(struct file *file) -{ AuUnsupport(); } -#endif /* CONFIG_AUFS_DEBUG */ - -const struct address_space_operations aufs_aop = { - .readpage = aufs_readpage, - .direct_IO = aufs_direct_IO, -#ifdef CONFIG_AUFS_DEBUG - .writepage = aufs_writepage, - /* no writepages, because of writepage */ - .set_page_dirty = aufs_set_page_dirty, - /* no readpages, because of readpage */ - .write_begin = aufs_write_begin, - .write_end = aufs_write_end, - /* no bmap, no block device */ - .invalidatepage = aufs_invalidatepage, - .releasepage = aufs_releasepage, - .migratepage = aufs_migratepage, - .launder_page = aufs_launder_page, - .is_partially_uptodate = aufs_is_partially_uptodate, - .is_dirty_writeback = aufs_is_dirty_writeback, - .error_remove_page = aufs_error_remove_page, - .swap_activate = aufs_swap_activate, - .swap_deactivate = aufs_swap_deactivate -#endif /* CONFIG_AUFS_DEBUG */ -}; diff --git a/fs/aufs/file.h b/fs/aufs/file.h deleted file mode 100644 index 10ede169f..000000000 --- a/fs/aufs/file.h +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * file operations - */ - -#ifndef __AUFS_FILE_H__ -#define __AUFS_FILE_H__ - -#ifdef __KERNEL__ - -#include <linux/file.h> -#include <linux/fs.h> -#include <linux/poll.h> -#include "rwsem.h" - -struct au_branch; -struct au_hfile { - struct file *hf_file; - struct au_branch *hf_br; -}; - -struct au_vdir; -struct au_fidir { - aufs_bindex_t fd_bbot; - aufs_bindex_t fd_nent; - struct au_vdir *fd_vdir_cache; - struct au_hfile fd_hfile[]; -}; - -static inline int au_fidir_sz(int nent) -{ - AuDebugOn(nent < 0); - return sizeof(struct au_fidir) + sizeof(struct au_hfile) * nent; -} - -struct au_finfo { - atomic_t fi_generation; - - struct au_rwsem fi_rwsem; - aufs_bindex_t fi_btop; - - /* do not union them */ - struct { /* for non-dir */ - struct au_hfile fi_htop; - atomic_t fi_mmapped; - }; - struct au_fidir *fi_hdir; /* for dir only */ - - struct hlist_node fi_hlist; - struct file *fi_file; /* very ugly */ -} ____cacheline_aligned_in_smp; - -/* ---------------------------------------------------------------------- */ - -/* file.c */ -extern const struct address_space_operations aufs_aop; -unsigned int au_file_roflags(unsigned int flags); -struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, - struct file *file, int force_wr); -struct au_do_open_args { - int no_lock; - int (*open)(struct file *file, int flags, - struct file *h_file); - struct au_fidir *fidir; - struct file *h_file; -}; -int au_do_open(struct file *file, struct au_do_open_args *args); -int au_reopen_nondir(struct file *file); -struct au_pin; -int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin); -int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), - int wlock); -int au_do_flush(struct file *file, fl_owner_t id, - int (*flush)(struct file *file, fl_owner_t id)); - -/* poll.c */ -#ifdef CONFIG_AUFS_POLL -unsigned int aufs_poll(struct file *file, poll_table *wait); -#endif - -#ifdef CONFIG_AUFS_BR_HFSPLUS -/* hfsplus.c */ -struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex, - int force_wr); -void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex, - struct file *h_file); -#else -AuStub(struct file *, au_h_open_pre, return NULL, struct dentry *dentry, - aufs_bindex_t bindex, int force_wr) -AuStubVoid(au_h_open_post, struct dentry *dentry, aufs_bindex_t bindex, - struct file *h_file); -#endif - -/* f_op.c */ -extern const struct file_operations aufs_file_fop; -int au_do_open_nondir(struct file *file, int flags, struct file *h_file); -int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file); -struct file *au_read_pre(struct file *file, int keep_fi); - -/* finfo.c */ -void au_hfput(struct au_hfile *hf, struct file *file); -void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, - struct file *h_file); - -void au_update_figen(struct file *file); -struct au_fidir *au_fidir_alloc(struct super_block *sb); -int au_fidir_realloc(struct au_finfo *finfo, int nbr); - -void au_fi_init_once(void *_fi); -void au_finfo_fin(struct file *file); -int au_finfo_init(struct file *file, struct au_fidir *fidir); - -/* ioctl.c */ -long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg); -#ifdef CONFIG_COMPAT -long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd, - unsigned long arg); -long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd, - unsigned long arg); -#endif - -/* ---------------------------------------------------------------------- */ - -static inline struct au_finfo *au_fi(struct file *file) -{ - return file->private_data; -} - -/* ---------------------------------------------------------------------- */ - -/* - * fi_read_lock, fi_write_lock, - * fi_read_unlock, fi_write_unlock, fi_downgrade_lock - */ -AuSimpleRwsemFuncs(fi, struct file *f, &au_fi(f)->fi_rwsem); - -#define FiMustNoWaiters(f) AuRwMustNoWaiters(&au_fi(f)->fi_rwsem) -#define FiMustAnyLock(f) AuRwMustAnyLock(&au_fi(f)->fi_rwsem) -#define FiMustWriteLock(f) AuRwMustWriteLock(&au_fi(f)->fi_rwsem) - -/* ---------------------------------------------------------------------- */ - -/* todo: hard/soft set? */ -static inline aufs_bindex_t au_fbstart(struct file *file) -{ - FiMustAnyLock(file); - return au_fi(file)->fi_btop; -} - -static inline aufs_bindex_t au_fbend_dir(struct file *file) -{ - FiMustAnyLock(file); - AuDebugOn(!au_fi(file)->fi_hdir); - return au_fi(file)->fi_hdir->fd_bbot; -} - -static inline struct au_vdir *au_fvdir_cache(struct file *file) -{ - FiMustAnyLock(file); - AuDebugOn(!au_fi(file)->fi_hdir); - return au_fi(file)->fi_hdir->fd_vdir_cache; -} - -static inline void au_set_fbstart(struct file *file, aufs_bindex_t bindex) -{ - FiMustWriteLock(file); - au_fi(file)->fi_btop = bindex; -} - -static inline void au_set_fbend_dir(struct file *file, aufs_bindex_t bindex) -{ - FiMustWriteLock(file); - AuDebugOn(!au_fi(file)->fi_hdir); - au_fi(file)->fi_hdir->fd_bbot = bindex; -} - -static inline void au_set_fvdir_cache(struct file *file, - struct au_vdir *vdir_cache) -{ - FiMustWriteLock(file); - AuDebugOn(!au_fi(file)->fi_hdir); - au_fi(file)->fi_hdir->fd_vdir_cache = vdir_cache; -} - -static inline struct file *au_hf_top(struct file *file) -{ - FiMustAnyLock(file); - AuDebugOn(au_fi(file)->fi_hdir); - return au_fi(file)->fi_htop.hf_file; -} - -static inline struct file *au_hf_dir(struct file *file, aufs_bindex_t bindex) -{ - FiMustAnyLock(file); - AuDebugOn(!au_fi(file)->fi_hdir); - return au_fi(file)->fi_hdir->fd_hfile[0 + bindex].hf_file; -} - -/* todo: memory barrier? */ -static inline unsigned int au_figen(struct file *f) -{ - return atomic_read(&au_fi(f)->fi_generation); -} - -static inline void au_set_mmapped(struct file *f) -{ - if (atomic_inc_return(&au_fi(f)->fi_mmapped)) - return; - pr_warn("fi_mmapped wrapped around\n"); - while (!atomic_inc_return(&au_fi(f)->fi_mmapped)) - ; -} - -static inline void au_unset_mmapped(struct file *f) -{ - atomic_dec(&au_fi(f)->fi_mmapped); -} - -static inline int au_test_mmapped(struct file *f) -{ - return atomic_read(&au_fi(f)->fi_mmapped); -} - -/* customize vma->vm_file */ - -static inline void au_do_vm_file_reset(struct vm_area_struct *vma, - struct file *file) -{ - struct file *f; - - f = vma->vm_file; - get_file(file); - vma->vm_file = file; - fput(f); -} - -#ifdef CONFIG_MMU -#define AuDbgVmRegion(file, vma) do {} while (0) - -static inline void au_vm_file_reset(struct vm_area_struct *vma, - struct file *file) -{ - au_do_vm_file_reset(vma, file); -} -#else -#define AuDbgVmRegion(file, vma) \ - AuDebugOn((vma)->vm_region && (vma)->vm_region->vm_file != (file)) - -static inline void au_vm_file_reset(struct vm_area_struct *vma, - struct file *file) -{ - struct file *f; - - au_do_vm_file_reset(vma, file); - f = vma->vm_region->vm_file; - get_file(file); - vma->vm_region->vm_file = file; - fput(f); -} -#endif /* CONFIG_MMU */ - -/* handle vma->vm_prfile */ -static inline void au_vm_prfile_set(struct vm_area_struct *vma, - struct file *file) -{ - get_file(file); - vma->vm_prfile = file; -#ifndef CONFIG_MMU - get_file(file); - vma->vm_region->vm_prfile = file; -#endif -} - -#endif /* __KERNEL__ */ -#endif /* __AUFS_FILE_H__ */ diff --git a/fs/aufs/finfo.c b/fs/aufs/finfo.c deleted file mode 100644 index 7ac467cf7..000000000 --- a/fs/aufs/finfo.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * file private data - */ - -#include "aufs.h" - -void au_hfput(struct au_hfile *hf, struct file *file) -{ - /* todo: direct access f_flags */ - if (vfsub_file_flags(file) & __FMODE_EXEC) - allow_write_access(hf->hf_file); - fput(hf->hf_file); - hf->hf_file = NULL; - atomic_dec(&hf->hf_br->br_count); - hf->hf_br = NULL; -} - -void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val) -{ - struct au_finfo *finfo = au_fi(file); - struct au_hfile *hf; - struct au_fidir *fidir; - - fidir = finfo->fi_hdir; - if (!fidir) { - AuDebugOn(finfo->fi_btop != bindex); - hf = &finfo->fi_htop; - } else - hf = fidir->fd_hfile + bindex; - - if (hf && hf->hf_file) - au_hfput(hf, file); - if (val) { - FiMustWriteLock(file); - AuDebugOn(IS_ERR_OR_NULL(file->f_path.dentry)); - hf->hf_file = val; - hf->hf_br = au_sbr(file->f_path.dentry->d_sb, bindex); - } -} - -void au_update_figen(struct file *file) -{ - atomic_set(&au_fi(file)->fi_generation, au_digen(file->f_path.dentry)); - /* smp_mb(); */ /* atomic_set */ -} - -/* ---------------------------------------------------------------------- */ - -struct au_fidir *au_fidir_alloc(struct super_block *sb) -{ - struct au_fidir *fidir; - int nbr; - - nbr = au_sbend(sb) + 1; - if (nbr < 2) - nbr = 2; /* initial allocate for 2 branches */ - fidir = kzalloc(au_fidir_sz(nbr), GFP_NOFS); - if (fidir) { - fidir->fd_bbot = -1; - fidir->fd_nent = nbr; - fidir->fd_vdir_cache = NULL; - } - - return fidir; -} - -int au_fidir_realloc(struct au_finfo *finfo, int nbr) -{ - int err; - struct au_fidir *fidir, *p; - - AuRwMustWriteLock(&finfo->fi_rwsem); - fidir = finfo->fi_hdir; - AuDebugOn(!fidir); - - err = -ENOMEM; - p = au_kzrealloc(fidir, au_fidir_sz(fidir->fd_nent), au_fidir_sz(nbr), - GFP_NOFS); - if (p) { - p->fd_nent = nbr; - finfo->fi_hdir = p; - err = 0; - } - - return err; -} - -/* ---------------------------------------------------------------------- */ - -void au_finfo_fin(struct file *file) -{ - struct au_finfo *finfo; - - au_nfiles_dec(file->f_path.dentry->d_sb); - - finfo = au_fi(file); - AuDebugOn(finfo->fi_hdir); - AuRwDestroy(&finfo->fi_rwsem); - au_cache_free_finfo(finfo); -} - -void au_fi_init_once(void *_finfo) -{ - struct au_finfo *finfo = _finfo; - static struct lock_class_key aufs_fi; - - au_rw_init(&finfo->fi_rwsem); - au_rw_class(&finfo->fi_rwsem, &aufs_fi); -} - -int au_finfo_init(struct file *file, struct au_fidir *fidir) -{ - int err; - struct au_finfo *finfo; - struct dentry *dentry; - - err = -ENOMEM; - dentry = file->f_path.dentry; - finfo = au_cache_alloc_finfo(); - if (unlikely(!finfo)) - goto out; - - err = 0; - au_nfiles_inc(dentry->d_sb); - /* verbose coding for lock class name */ - if (!fidir) - au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcNonDir_FIINFO); - else - au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcDir_FIINFO); - au_rw_write_lock(&finfo->fi_rwsem); - finfo->fi_btop = -1; - finfo->fi_hdir = fidir; - atomic_set(&finfo->fi_generation, au_digen(dentry)); - /* smp_mb(); */ /* atomic_set */ - - file->private_data = finfo; - -out: - return err; -} diff --git a/fs/aufs/fstype.h b/fs/aufs/fstype.h deleted file mode 100644 index 8b6878b4e..000000000 --- a/fs/aufs/fstype.h +++ /dev/null @@ -1,400 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * judging filesystem type - */ - -#ifndef __AUFS_FSTYPE_H__ -#define __AUFS_FSTYPE_H__ - -#ifdef __KERNEL__ - -#include <linux/fs.h> -#include <linux/magic.h> -#include <linux/romfs_fs.h> -#include <linux/nfs_fs.h> - -static inline int au_test_aufs(struct super_block *sb) -{ - return sb->s_magic == AUFS_SUPER_MAGIC; -} - -static inline const char *au_sbtype(struct super_block *sb) -{ - return sb->s_type->name; -} - -static inline int au_test_iso9660(struct super_block *sb __maybe_unused) -{ -#if defined(CONFIG_ISO9660_FS) || defined(CONFIG_ISO9660_FS_MODULE) - return sb->s_magic == ISOFS_SUPER_MAGIC; -#else - return 0; -#endif -} - -static inline int au_test_romfs(struct super_block *sb __maybe_unused) -{ -#if defined(CONFIG_ROMFS_FS) || defined(CONFIG_ROMFS_FS_MODULE) - return sb->s_magic == ROMFS_MAGIC; -#else - return 0; -#endif -} - -static inline int au_test_cramfs(struct super_block *sb __maybe_unused) -{ -#if defined(CONFIG_CRAMFS) || defined(CONFIG_CRAMFS_MODULE) - return sb->s_magic == CRAMFS_MAGIC; -#endif - return 0; -} - -static inline int au_test_nfs(struct super_block *sb __maybe_unused) -{ -#if defined(CONFIG_NFS_FS) || defined(CONFIG_NFS_FS_MODULE) - return sb->s_magic == NFS_SUPER_MAGIC; -#else - return 0; -#endif -} - -static inline int au_test_fuse(struct super_block *sb __maybe_unused) -{ -#if defined(CONFIG_FUSE_FS) || defined(CONFIG_FUSE_FS_MODULE) - return sb->s_magic == FUSE_SUPER_MAGIC; -#else - return 0; -#endif -} - -static inline int au_test_xfs(struct super_block *sb __maybe_unused) -{ -#if defined(CONFIG_XFS_FS) || defined(CONFIG_XFS_FS_MODULE) - return sb->s_magic == XFS_SB_MAGIC; -#else - return 0; -#endif -} - -static inline int au_test_tmpfs(struct super_block *sb __maybe_unused) -{ -#ifdef CONFIG_TMPFS - return sb->s_magic == TMPFS_MAGIC; -#else - return 0; -#endif -} - -static inline int au_test_ecryptfs(struct super_block *sb __maybe_unused) -{ -#if defined(CONFIG_ECRYPT_FS) || defined(CONFIG_ECRYPT_FS_MODULE) - return !strcmp(au_sbtype(sb), "ecryptfs"); -#else - return 0; -#endif -} - -static inline int au_test_ramfs(struct super_block *sb) -{ - return sb->s_magic == RAMFS_MAGIC; -} - -static inline int au_test_ubifs(struct super_block *sb __maybe_unused) -{ -#if defined(CONFIG_UBIFS_FS) || defined(CONFIG_UBIFS_FS_MODULE) - return sb->s_magic == UBIFS_SUPER_MAGIC; -#else - return 0; -#endif -} - -static inline int au_test_procfs(struct super_block *sb __maybe_unused) -{ -#ifdef CONFIG_PROC_FS - return sb->s_magic == PROC_SUPER_MAGIC; -#else - return 0; -#endif -} - -static inline int au_test_sysfs(struct super_block *sb __maybe_unused) -{ -#ifdef CONFIG_SYSFS - return sb->s_magic == SYSFS_MAGIC; -#else - return 0; -#endif -} - -static inline int au_test_configfs(struct super_block *sb __maybe_unused) -{ -#if defined(CONFIG_CONFIGFS_FS) || defined(CONFIG_CONFIGFS_FS_MODULE) - return sb->s_magic == CONFIGFS_MAGIC; -#else - return 0; -#endif -} - -static inline int au_test_minix(struct super_block *sb __maybe_unused) -{ -#if defined(CONFIG_MINIX_FS) || defined(CONFIG_MINIX_FS_MODULE) - return sb->s_magic == MINIX3_SUPER_MAGIC - || sb->s_magic == MINIX2_SUPER_MAGIC - || sb->s_magic == MINIX2_SUPER_MAGIC2 - || sb->s_magic == MINIX_SUPER_MAGIC - || sb->s_magic == MINIX_SUPER_MAGIC2; -#else - return 0; -#endif -} - -static inline int au_test_fat(struct super_block *sb __maybe_unused) -{ -#if defined(CONFIG_FAT_FS) || defined(CONFIG_FAT_FS_MODULE) - return sb->s_magic == MSDOS_SUPER_MAGIC; -#else - return 0; -#endif -} - -static inline int au_test_msdos(struct super_block *sb) -{ - return au_test_fat(sb); -} - -static inline int au_test_vfat(struct super_block *sb) -{ - return au_test_fat(sb); -} - -static inline int au_test_securityfs(struct super_block *sb __maybe_unused) -{ -#ifdef CONFIG_SECURITYFS - return sb->s_magic == SECURITYFS_MAGIC; -#else - return 0; -#endif -} - -static inline int au_test_squashfs(struct super_block *sb __maybe_unused) -{ -#if defined(CONFIG_SQUASHFS) || defined(CONFIG_SQUASHFS_MODULE) - return sb->s_magic == SQUASHFS_MAGIC; -#else - return 0; -#endif -} - -static inline int au_test_btrfs(struct super_block *sb __maybe_unused) -{ -#if defined(CONFIG_BTRFS_FS) || defined(CONFIG_BTRFS_FS_MODULE) - return sb->s_magic == BTRFS_SUPER_MAGIC; -#else - return 0; -#endif -} - -static inline int au_test_xenfs(struct super_block *sb __maybe_unused) -{ -#if defined(CONFIG_XENFS) || defined(CONFIG_XENFS_MODULE) - return sb->s_magic == XENFS_SUPER_MAGIC; -#else - return 0; -#endif -} - -static inline int au_test_debugfs(struct super_block *sb __maybe_unused) -{ -#ifdef CONFIG_DEBUG_FS - return sb->s_magic == DEBUGFS_MAGIC; -#else - return 0; -#endif -} - -static inline int au_test_nilfs(struct super_block *sb __maybe_unused) -{ -#if defined(CONFIG_NILFS) || defined(CONFIG_NILFS_MODULE) - return sb->s_magic == NILFS_SUPER_MAGIC; -#else - return 0; -#endif -} - -static inline int au_test_hfsplus(struct super_block *sb __maybe_unused) -{ -#if defined(CONFIG_HFSPLUS_FS) || defined(CONFIG_HFSPLUS_FS_MODULE) - return sb->s_magic == HFSPLUS_SUPER_MAGIC; -#else - return 0; -#endif -} - -/* ---------------------------------------------------------------------- */ -/* - * they can't be an aufs branch. - */ -static inline int au_test_fs_unsuppoted(struct super_block *sb) -{ - return -#ifndef CONFIG_AUFS_BR_RAMFS - au_test_ramfs(sb) || -#endif - au_test_procfs(sb) - || au_test_sysfs(sb) - || au_test_configfs(sb) - || au_test_debugfs(sb) - || au_test_securityfs(sb) - || au_test_xenfs(sb) - || au_test_ecryptfs(sb) - /* || !strcmp(au_sbtype(sb), "unionfs") */ - || au_test_aufs(sb); /* will be supported in next version */ -} - -static inline int au_test_fs_remote(struct super_block *sb) -{ - return !au_test_tmpfs(sb) -#ifdef CONFIG_AUFS_BR_RAMFS - && !au_test_ramfs(sb) -#endif - && !(sb->s_type->fs_flags & FS_REQUIRES_DEV); -} - -/* ---------------------------------------------------------------------- */ - -/* - * Note: these functions (below) are created after reading ->getattr() in all - * filesystems under linux/fs. it means we have to do so in every update... - */ - -/* - * some filesystems require getattr to refresh the inode attributes before - * referencing. - * in most cases, we can rely on the inode attribute in NFS (or every remote fs) - * and leave the work for d_revalidate() - */ -static inline int au_test_fs_refresh_iattr(struct super_block *sb) -{ - return au_test_nfs(sb) - || au_test_fuse(sb) - /* || au_test_btrfs(sb) */ /* untested */ - ; -} - -/* - * filesystems which don't maintain i_size or i_blocks. - */ -static inline int au_test_fs_bad_iattr_size(struct super_block *sb) -{ - return au_test_xfs(sb) - || au_test_btrfs(sb) - || au_test_ubifs(sb) - || au_test_hfsplus(sb) /* maintained, but incorrect */ - /* || au_test_minix(sb) */ /* untested */ - ; -} - -/* - * filesystems which don't store the correct value in some of their inode - * attributes. - */ -static inline int au_test_fs_bad_iattr(struct super_block *sb) -{ - return au_test_fs_bad_iattr_size(sb) - || au_test_fat(sb) - || au_test_msdos(sb) - || au_test_vfat(sb); -} - -/* they don't check i_nlink in link(2) */ -static inline int au_test_fs_no_limit_nlink(struct super_block *sb) -{ - return au_test_tmpfs(sb) -#ifdef CONFIG_AUFS_BR_RAMFS - || au_test_ramfs(sb) -#endif - || au_test_ubifs(sb) - || au_test_hfsplus(sb); -} - -/* - * filesystems which sets S_NOATIME and S_NOCMTIME. - */ -static inline int au_test_fs_notime(struct super_block *sb) -{ - return au_test_nfs(sb) - || au_test_fuse(sb) - || au_test_ubifs(sb) - ; -} - -/* temporary support for i#1 in cramfs */ -static inline int au_test_fs_unique_ino(struct inode *inode) -{ - if (au_test_cramfs(inode->i_sb)) - return inode->i_ino != 1; - return 1; -} - -/* ---------------------------------------------------------------------- */ - -/* - * the filesystem where the xino files placed must support i/o after unlink and - * maintain i_size and i_blocks. - */ -static inline int au_test_fs_bad_xino(struct super_block *sb) -{ - return au_test_fs_remote(sb) - || au_test_fs_bad_iattr_size(sb) - /* don't want unnecessary work for xino */ - || au_test_aufs(sb) - || au_test_ecryptfs(sb) - || au_test_nilfs(sb); -} - -static inline int au_test_fs_trunc_xino(struct super_block *sb) -{ - return au_test_tmpfs(sb) - || au_test_ramfs(sb); -} - -/* - * test if the @sb is real-readonly. - */ -static inline int au_test_fs_rr(struct super_block *sb) -{ - return au_test_squashfs(sb) - || au_test_iso9660(sb) - || au_test_cramfs(sb) - || au_test_romfs(sb); -} - -/* - * test if the @inode is nfs with 'noacl' option - * NFS always sets MS_POSIXACL regardless its mount option 'noacl.' - */ -static inline int au_test_nfs_noacl(struct inode *inode) -{ - return au_test_nfs(inode->i_sb) - /* && IS_POSIXACL(inode) */ - && !nfs_server_capable(inode, NFS_CAP_ACLS); -} - -#endif /* __KERNEL__ */ -#endif /* __AUFS_FSTYPE_H__ */ diff --git a/fs/aufs/hfsnotify.c b/fs/aufs/hfsnotify.c deleted file mode 100644 index dc4f6c3c9..000000000 --- a/fs/aufs/hfsnotify.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * fsnotify for the lower directories - */ - -#include "aufs.h" - -/* FS_IN_IGNORED is unnecessary */ -static const __u32 AuHfsnMask = (FS_MOVED_TO | FS_MOVED_FROM | FS_DELETE - | FS_CREATE | FS_EVENT_ON_CHILD); -static DECLARE_WAIT_QUEUE_HEAD(au_hfsn_wq); -static __cacheline_aligned_in_smp atomic64_t au_hfsn_ifree = ATOMIC64_INIT(0); - -static void au_hfsn_free_mark(struct fsnotify_mark *mark) -{ - struct au_hnotify *hn = container_of(mark, struct au_hnotify, - hn_mark); - AuDbg("here\n"); - au_cache_free_hnotify(hn); - smp_mb__before_atomic(); - if (atomic64_dec_and_test(&au_hfsn_ifree)) - wake_up(&au_hfsn_wq); -} - -static int au_hfsn_alloc(struct au_hinode *hinode) -{ - int err; - struct au_hnotify *hn; - struct super_block *sb; - struct au_branch *br; - struct fsnotify_mark *mark; - aufs_bindex_t bindex; - - hn = hinode->hi_notify; - sb = hn->hn_aufs_inode->i_sb; - bindex = au_br_index(sb, hinode->hi_id); - br = au_sbr(sb, bindex); - AuDebugOn(!br->br_hfsn); - - mark = &hn->hn_mark; - fsnotify_init_mark(mark, au_hfsn_free_mark); - mark->mask = AuHfsnMask; - /* - * by udba rename or rmdir, aufs assign a new inode to the known - * h_inode, so specify 1 to allow dups. - */ - lockdep_off(); - err = fsnotify_add_mark(mark, br->br_hfsn->hfsn_group, hinode->hi_inode, - /*mnt*/NULL, /*allow_dups*/1); - /* even if err */ - fsnotify_put_mark(mark); - lockdep_on(); - - return err; -} - -static int au_hfsn_free(struct au_hinode *hinode, struct au_hnotify *hn) -{ - struct fsnotify_mark *mark; - unsigned long long ull; - struct fsnotify_group *group; - - ull = atomic64_inc_return(&au_hfsn_ifree); - BUG_ON(!ull); - - mark = &hn->hn_mark; - spin_lock(&mark->lock); - group = mark->group; - fsnotify_get_group(group); - spin_unlock(&mark->lock); - lockdep_off(); - fsnotify_destroy_mark(mark, group); - fsnotify_put_group(group); - lockdep_on(); - - /* free hn by myself */ - return 0; -} - -/* ---------------------------------------------------------------------- */ - -static void au_hfsn_ctl(struct au_hinode *hinode, int do_set) -{ - struct fsnotify_mark *mark; - - mark = &hinode->hi_notify->hn_mark; - spin_lock(&mark->lock); - if (do_set) { - AuDebugOn(mark->mask & AuHfsnMask); - mark->mask |= AuHfsnMask; - } else { - AuDebugOn(!(mark->mask & AuHfsnMask)); - mark->mask &= ~AuHfsnMask; - } - spin_unlock(&mark->lock); - /* fsnotify_recalc_inode_mask(hinode->hi_inode); */ -} - -/* ---------------------------------------------------------------------- */ - -/* #define AuDbgHnotify */ -#ifdef AuDbgHnotify -static char *au_hfsn_name(u32 mask) -{ -#ifdef CONFIG_AUFS_DEBUG -#define test_ret(flag) \ - do { \ - if (mask & flag) \ - return #flag; \ - } while (0) - test_ret(FS_ACCESS); - test_ret(FS_MODIFY); - test_ret(FS_ATTRIB); - test_ret(FS_CLOSE_WRITE); - test_ret(FS_CLOSE_NOWRITE); - test_ret(FS_OPEN); - test_ret(FS_MOVED_FROM); - test_ret(FS_MOVED_TO); - test_ret(FS_CREATE); - test_ret(FS_DELETE); - test_ret(FS_DELETE_SELF); - test_ret(FS_MOVE_SELF); - test_ret(FS_UNMOUNT); - test_ret(FS_Q_OVERFLOW); - test_ret(FS_IN_IGNORED); - test_ret(FS_ISDIR); - test_ret(FS_IN_ONESHOT); - test_ret(FS_EVENT_ON_CHILD); - return ""; -#undef test_ret -#else - return "??"; -#endif -} -#endif - -/* ---------------------------------------------------------------------- */ - -static void au_hfsn_free_group(struct fsnotify_group *group) -{ - struct au_br_hfsnotify *hfsn = group->private; - - AuDbg("here\n"); - kfree(hfsn); -} - -static int au_hfsn_handle_event(struct fsnotify_group *group, - struct inode *inode, - struct fsnotify_mark *inode_mark, - struct fsnotify_mark *vfsmount_mark, - u32 mask, void *data, int data_type, - const unsigned char *file_name, u32 cookie) -{ - int err; - struct au_hnotify *hnotify; - struct inode *h_dir, *h_inode; - struct qstr h_child_qstr = QSTR_INIT(file_name, strlen(file_name)); - - AuDebugOn(data_type != FSNOTIFY_EVENT_INODE); - - err = 0; - /* if FS_UNMOUNT happens, there must be another bug */ - AuDebugOn(mask & FS_UNMOUNT); - if (mask & (FS_IN_IGNORED | FS_UNMOUNT)) - goto out; - - h_dir = inode; - h_inode = NULL; -#ifdef AuDbgHnotify - au_debug_on(); - if (1 || h_child_qstr.len != sizeof(AUFS_XINO_FNAME) - 1 - || strncmp(h_child_qstr.name, AUFS_XINO_FNAME, h_child_qstr.len)) { - AuDbg("i%lu, mask 0x%x %s, hcname %.*s, hi%lu\n", - h_dir->i_ino, mask, au_hfsn_name(mask), - AuLNPair(&h_child_qstr), h_inode ? h_inode->i_ino : 0); - /* WARN_ON(1); */ - } - au_debug_off(); -#endif - - AuDebugOn(!inode_mark); - hnotify = container_of(inode_mark, struct au_hnotify, hn_mark); - err = au_hnotify(h_dir, hnotify, mask, &h_child_qstr, h_inode); - -out: - return err; -} - -static struct fsnotify_ops au_hfsn_ops = { - .handle_event = au_hfsn_handle_event, - .free_group_priv = au_hfsn_free_group -}; - -/* ---------------------------------------------------------------------- */ - -static void au_hfsn_fin_br(struct au_branch *br) -{ - struct au_br_hfsnotify *hfsn; - - hfsn = br->br_hfsn; - if (hfsn) { - lockdep_off(); - fsnotify_put_group(hfsn->hfsn_group); - lockdep_on(); - } -} - -static int au_hfsn_init_br(struct au_branch *br, int perm) -{ - int err; - struct fsnotify_group *group; - struct au_br_hfsnotify *hfsn; - - err = 0; - br->br_hfsn = NULL; - if (!au_br_hnotifyable(perm)) - goto out; - - err = -ENOMEM; - hfsn = kmalloc(sizeof(*hfsn), GFP_NOFS); - if (unlikely(!hfsn)) - goto out; - - err = 0; - group = fsnotify_alloc_group(&au_hfsn_ops); - if (IS_ERR(group)) { - err = PTR_ERR(group); - pr_err("fsnotify_alloc_group() failed, %d\n", err); - goto out_hfsn; - } - - group->private = hfsn; - hfsn->hfsn_group = group; - br->br_hfsn = hfsn; - goto out; /* success */ - -out_hfsn: - kfree(hfsn); -out: - return err; -} - -static int au_hfsn_reset_br(unsigned int udba, struct au_branch *br, int perm) -{ - int err; - - err = 0; - if (!br->br_hfsn) - err = au_hfsn_init_br(br, perm); - - return err; -} - -/* ---------------------------------------------------------------------- */ - -static void au_hfsn_fin(void) -{ - AuDbg("au_hfsn_ifree %lld\n", (long long)atomic64_read(&au_hfsn_ifree)); - wait_event(au_hfsn_wq, !atomic64_read(&au_hfsn_ifree)); -} - -const struct au_hnotify_op au_hnotify_op = { - .ctl = au_hfsn_ctl, - .alloc = au_hfsn_alloc, - .free = au_hfsn_free, - - .fin = au_hfsn_fin, - - .reset_br = au_hfsn_reset_br, - .fin_br = au_hfsn_fin_br, - .init_br = au_hfsn_init_br -}; diff --git a/fs/aufs/hfsplus.c b/fs/aufs/hfsplus.c deleted file mode 100644 index ced054639..000000000 --- a/fs/aufs/hfsplus.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2010-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * special support for filesystems which aqucires an inode mutex - * at final closing a file, eg, hfsplus. - * - * This trick is very simple and stupid, just to open the file before really - * neceeary open to tell hfsplus that this is not the final closing. - * The caller should call au_h_open_pre() after acquiring the inode mutex, - * and au_h_open_post() after releasing it. - */ - -#include "aufs.h" - -struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex, - int force_wr) -{ - struct file *h_file; - struct dentry *h_dentry; - - h_dentry = au_h_dptr(dentry, bindex); - AuDebugOn(!h_dentry); - AuDebugOn(d_is_negative(h_dentry)); - - h_file = NULL; - if (au_test_hfsplus(h_dentry->d_sb) - && d_is_reg(h_dentry)) - h_file = au_h_open(dentry, bindex, - O_RDONLY | O_NOATIME | O_LARGEFILE, - /*file*/NULL, force_wr); - return h_file; -} - -void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex, - struct file *h_file) -{ - if (h_file) { - fput(h_file); - au_sbr_put(dentry->d_sb, bindex); - } -} diff --git a/fs/aufs/hnotify.c b/fs/aufs/hnotify.c deleted file mode 100644 index 643bc57c5..000000000 --- a/fs/aufs/hnotify.c +++ /dev/null @@ -1,710 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * abstraction to notify the direct changes on lower directories - */ - -#include "aufs.h" - -int au_hn_alloc(struct au_hinode *hinode, struct inode *inode) -{ - int err; - struct au_hnotify *hn; - - err = -ENOMEM; - hn = au_cache_alloc_hnotify(); - if (hn) { - hn->hn_aufs_inode = inode; - hinode->hi_notify = hn; - err = au_hnotify_op.alloc(hinode); - AuTraceErr(err); - if (unlikely(err)) { - hinode->hi_notify = NULL; - au_cache_free_hnotify(hn); - /* - * The upper dir was removed by udba, but the same named - * dir left. In this case, aufs assignes a new inode - * number and set the monitor again. - * For the lower dir, the old monitnor is still left. - */ - if (err == -EEXIST) - err = 0; - } - } - - AuTraceErr(err); - return err; -} - -void au_hn_free(struct au_hinode *hinode) -{ - struct au_hnotify *hn; - - hn = hinode->hi_notify; - if (hn) { - hinode->hi_notify = NULL; - if (au_hnotify_op.free(hinode, hn)) - au_cache_free_hnotify(hn); - } -} - -/* ---------------------------------------------------------------------- */ - -void au_hn_ctl(struct au_hinode *hinode, int do_set) -{ - if (hinode->hi_notify) - au_hnotify_op.ctl(hinode, do_set); -} - -void au_hn_reset(struct inode *inode, unsigned int flags) -{ - aufs_bindex_t bindex, bend; - struct inode *hi; - struct dentry *iwhdentry; - - bend = au_ibend(inode); - for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { - hi = au_h_iptr(inode, bindex); - if (!hi) - continue; - - /* mutex_lock_nested(&hi->i_mutex, AuLsc_I_CHILD); */ - iwhdentry = au_hi_wh(inode, bindex); - if (iwhdentry) - dget(iwhdentry); - au_igrab(hi); - au_set_h_iptr(inode, bindex, NULL, 0); - au_set_h_iptr(inode, bindex, au_igrab(hi), - flags & ~AuHi_XINO); - iput(hi); - dput(iwhdentry); - /* mutex_unlock(&hi->i_mutex); */ - } -} - -/* ---------------------------------------------------------------------- */ - -static int hn_xino(struct inode *inode, struct inode *h_inode) -{ - int err; - aufs_bindex_t bindex, bend, bfound, bstart; - struct inode *h_i; - - err = 0; - if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { - pr_warn("branch root dir was changed\n"); - goto out; - } - - bfound = -1; - bend = au_ibend(inode); - bstart = au_ibstart(inode); -#if 0 /* reserved for future use */ - if (bindex == bend) { - /* keep this ino in rename case */ - goto out; - } -#endif - for (bindex = bstart; bindex <= bend; bindex++) - if (au_h_iptr(inode, bindex) == h_inode) { - bfound = bindex; - break; - } - if (bfound < 0) - goto out; - - for (bindex = bstart; bindex <= bend; bindex++) { - h_i = au_h_iptr(inode, bindex); - if (!h_i) - continue; - - err = au_xino_write(inode->i_sb, bindex, h_i->i_ino, /*ino*/0); - /* ignore this error */ - /* bad action? */ - } - - /* children inode number will be broken */ - -out: - AuTraceErr(err); - return err; -} - -static int hn_gen_tree(struct dentry *dentry) -{ - int err, i, j, ndentry; - struct au_dcsub_pages dpages; - struct au_dpage *dpage; - struct dentry **dentries; - - err = au_dpages_init(&dpages, GFP_NOFS); - if (unlikely(err)) - goto out; - err = au_dcsub_pages(&dpages, dentry, NULL, NULL); - if (unlikely(err)) - goto out_dpages; - - for (i = 0; i < dpages.ndpage; i++) { - dpage = dpages.dpages + i; - dentries = dpage->dentries; - ndentry = dpage->ndentry; - for (j = 0; j < ndentry; j++) { - struct dentry *d; - - d = dentries[j]; - if (IS_ROOT(d)) - continue; - - au_digen_dec(d); - if (d_really_is_positive(d)) - /* todo: reset children xino? - cached children only? */ - au_iigen_dec(d_inode(d)); - } - } - -out_dpages: - au_dpages_free(&dpages); - -#if 0 - /* discard children */ - dentry_unhash(dentry); - dput(dentry); -#endif -out: - return err; -} - -/* - * return 0 if processed. - */ -static int hn_gen_by_inode(char *name, unsigned int nlen, struct inode *inode, - const unsigned int isdir) -{ - int err; - struct dentry *d; - struct qstr *dname; - - err = 1; - if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { - pr_warn("branch root dir was changed\n"); - err = 0; - goto out; - } - - if (!isdir) { - AuDebugOn(!name); - au_iigen_dec(inode); - spin_lock(&inode->i_lock); - hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) { - spin_lock(&d->d_lock); - dname = &d->d_name; - if (dname->len != nlen - && memcmp(dname->name, name, nlen)) { - spin_unlock(&d->d_lock); - continue; - } - err = 0; - au_digen_dec(d); - spin_unlock(&d->d_lock); - break; - } - spin_unlock(&inode->i_lock); - } else { - au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIR); - d = d_find_any_alias(inode); - if (!d) { - au_iigen_dec(inode); - goto out; - } - - spin_lock(&d->d_lock); - dname = &d->d_name; - if (dname->len == nlen && !memcmp(dname->name, name, nlen)) { - spin_unlock(&d->d_lock); - err = hn_gen_tree(d); - spin_lock(&d->d_lock); - } - spin_unlock(&d->d_lock); - dput(d); - } - -out: - AuTraceErr(err); - return err; -} - -static int hn_gen_by_name(struct dentry *dentry, const unsigned int isdir) -{ - int err; - - if (IS_ROOT(dentry)) { - pr_warn("branch root dir was changed\n"); - return 0; - } - - err = 0; - if (!isdir) { - au_digen_dec(dentry); - if (d_really_is_positive(dentry)) - au_iigen_dec(d_inode(dentry)); - } else { - au_fset_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR); - if (d_really_is_positive(dentry)) - err = hn_gen_tree(dentry); - } - - AuTraceErr(err); - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* hnotify job flags */ -#define AuHnJob_XINO0 1 -#define AuHnJob_GEN (1 << 1) -#define AuHnJob_DIRENT (1 << 2) -#define AuHnJob_ISDIR (1 << 3) -#define AuHnJob_TRYXINO0 (1 << 4) -#define AuHnJob_MNTPNT (1 << 5) -#define au_ftest_hnjob(flags, name) ((flags) & AuHnJob_##name) -#define au_fset_hnjob(flags, name) \ - do { (flags) |= AuHnJob_##name; } while (0) -#define au_fclr_hnjob(flags, name) \ - do { (flags) &= ~AuHnJob_##name; } while (0) - -enum { - AuHn_CHILD, - AuHn_PARENT, - AuHnLast -}; - -struct au_hnotify_args { - struct inode *h_dir, *dir, *h_child_inode; - u32 mask; - unsigned int flags[AuHnLast]; - unsigned int h_child_nlen; - char h_child_name[]; -}; - -struct hn_job_args { - unsigned int flags; - struct inode *inode, *h_inode, *dir, *h_dir; - struct dentry *dentry; - char *h_name; - int h_nlen; -}; - -static int hn_job(struct hn_job_args *a) -{ - const unsigned int isdir = au_ftest_hnjob(a->flags, ISDIR); - int e; - - /* reset xino */ - if (au_ftest_hnjob(a->flags, XINO0) && a->inode) - hn_xino(a->inode, a->h_inode); /* ignore this error */ - - if (au_ftest_hnjob(a->flags, TRYXINO0) - && a->inode - && a->h_inode) { - mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); - if (!a->h_inode->i_nlink - && !(a->h_inode->i_state & I_LINKABLE)) - hn_xino(a->inode, a->h_inode); /* ignore this error */ - mutex_unlock(&a->h_inode->i_mutex); - } - - /* make the generation obsolete */ - if (au_ftest_hnjob(a->flags, GEN)) { - e = -1; - if (a->inode) - e = hn_gen_by_inode(a->h_name, a->h_nlen, a->inode, - isdir); - if (e && a->dentry) - hn_gen_by_name(a->dentry, isdir); - /* ignore this error */ - } - - /* make dir entries obsolete */ - if (au_ftest_hnjob(a->flags, DIRENT) && a->inode) { - struct au_vdir *vdir; - - vdir = au_ivdir(a->inode); - if (vdir) - vdir->vd_jiffy = 0; - /* IMustLock(a->inode); */ - /* a->inode->i_version++; */ - } - - /* can do nothing but warn */ - if (au_ftest_hnjob(a->flags, MNTPNT) - && a->dentry - && d_mountpoint(a->dentry)) - pr_warn("mount-point %pd is removed or renamed\n", a->dentry); - - return 0; -} - -/* ---------------------------------------------------------------------- */ - -static struct dentry *lookup_wlock_by_name(char *name, unsigned int nlen, - struct inode *dir) -{ - struct dentry *dentry, *d, *parent; - struct qstr *dname; - - parent = d_find_any_alias(dir); - if (!parent) - return NULL; - - dentry = NULL; - spin_lock(&parent->d_lock); - list_for_each_entry(d, &parent->d_subdirs, d_child) { - /* AuDbg("%pd\n", d); */ - spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED); - dname = &d->d_name; - if (dname->len != nlen || memcmp(dname->name, name, nlen)) - goto cont_unlock; - if (au_di(d)) - au_digen_dec(d); - else - goto cont_unlock; - if (au_dcount(d) > 0) { - dentry = dget_dlock(d); - spin_unlock(&d->d_lock); - break; - } - -cont_unlock: - spin_unlock(&d->d_lock); - } - spin_unlock(&parent->d_lock); - dput(parent); - - if (dentry) - di_write_lock_child(dentry); - - return dentry; -} - -static struct inode *lookup_wlock_by_ino(struct super_block *sb, - aufs_bindex_t bindex, ino_t h_ino) -{ - struct inode *inode; - ino_t ino; - int err; - - inode = NULL; - err = au_xino_read(sb, bindex, h_ino, &ino); - if (!err && ino) - inode = ilookup(sb, ino); - if (!inode) - goto out; - - if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { - pr_warn("wrong root branch\n"); - iput(inode); - inode = NULL; - goto out; - } - - ii_write_lock_child(inode); - -out: - return inode; -} - -static void au_hn_bh(void *_args) -{ - struct au_hnotify_args *a = _args; - struct super_block *sb; - aufs_bindex_t bindex, bend, bfound; - unsigned char xino, try_iput; - int err; - struct inode *inode; - ino_t h_ino; - struct hn_job_args args; - struct dentry *dentry; - struct au_sbinfo *sbinfo; - - AuDebugOn(!_args); - AuDebugOn(!a->h_dir); - AuDebugOn(!a->dir); - AuDebugOn(!a->mask); - AuDbg("mask 0x%x, i%lu, hi%lu, hci%lu\n", - a->mask, a->dir->i_ino, a->h_dir->i_ino, - a->h_child_inode ? a->h_child_inode->i_ino : 0); - - inode = NULL; - dentry = NULL; - /* - * do not lock a->dir->i_mutex here - * because of d_revalidate() may cause a deadlock. - */ - sb = a->dir->i_sb; - AuDebugOn(!sb); - sbinfo = au_sbi(sb); - AuDebugOn(!sbinfo); - si_write_lock(sb, AuLock_NOPLMW); - - ii_read_lock_parent(a->dir); - bfound = -1; - bend = au_ibend(a->dir); - for (bindex = au_ibstart(a->dir); bindex <= bend; bindex++) - if (au_h_iptr(a->dir, bindex) == a->h_dir) { - bfound = bindex; - break; - } - ii_read_unlock(a->dir); - if (unlikely(bfound < 0)) - goto out; - - xino = !!au_opt_test(au_mntflags(sb), XINO); - h_ino = 0; - if (a->h_child_inode) - h_ino = a->h_child_inode->i_ino; - - if (a->h_child_nlen - && (au_ftest_hnjob(a->flags[AuHn_CHILD], GEN) - || au_ftest_hnjob(a->flags[AuHn_CHILD], MNTPNT))) - dentry = lookup_wlock_by_name(a->h_child_name, a->h_child_nlen, - a->dir); - try_iput = 0; - if (dentry && d_really_is_positive(dentry)) - inode = d_inode(dentry); - if (xino && !inode && h_ino - && (au_ftest_hnjob(a->flags[AuHn_CHILD], XINO0) - || au_ftest_hnjob(a->flags[AuHn_CHILD], TRYXINO0) - || au_ftest_hnjob(a->flags[AuHn_CHILD], GEN))) { - inode = lookup_wlock_by_ino(sb, bfound, h_ino); - try_iput = 1; - } - - args.flags = a->flags[AuHn_CHILD]; - args.dentry = dentry; - args.inode = inode; - args.h_inode = a->h_child_inode; - args.dir = a->dir; - args.h_dir = a->h_dir; - args.h_name = a->h_child_name; - args.h_nlen = a->h_child_nlen; - err = hn_job(&args); - if (dentry) { - if (au_di(dentry)) - di_write_unlock(dentry); - dput(dentry); - } - if (inode && try_iput) { - ii_write_unlock(inode); - iput(inode); - } - - ii_write_lock_parent(a->dir); - args.flags = a->flags[AuHn_PARENT]; - args.dentry = NULL; - args.inode = a->dir; - args.h_inode = a->h_dir; - args.dir = NULL; - args.h_dir = NULL; - args.h_name = NULL; - args.h_nlen = 0; - err = hn_job(&args); - ii_write_unlock(a->dir); - -out: - iput(a->h_child_inode); - iput(a->h_dir); - iput(a->dir); - si_write_unlock(sb); - au_nwt_done(&sbinfo->si_nowait); - kfree(a); -} - -/* ---------------------------------------------------------------------- */ - -int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, - struct qstr *h_child_qstr, struct inode *h_child_inode) -{ - int err, len; - unsigned int flags[AuHnLast], f; - unsigned char isdir, isroot, wh; - struct inode *dir; - struct au_hnotify_args *args; - char *p, *h_child_name; - - err = 0; - AuDebugOn(!hnotify || !hnotify->hn_aufs_inode); - dir = igrab(hnotify->hn_aufs_inode); - if (!dir) - goto out; - - isroot = (dir->i_ino == AUFS_ROOT_INO); - wh = 0; - h_child_name = (void *)h_child_qstr->name; - len = h_child_qstr->len; - if (h_child_name) { - if (len > AUFS_WH_PFX_LEN - && !memcmp(h_child_name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { - h_child_name += AUFS_WH_PFX_LEN; - len -= AUFS_WH_PFX_LEN; - wh = 1; - } - } - - isdir = 0; - if (h_child_inode) - isdir = !!S_ISDIR(h_child_inode->i_mode); - flags[AuHn_PARENT] = AuHnJob_ISDIR; - flags[AuHn_CHILD] = 0; - if (isdir) - flags[AuHn_CHILD] = AuHnJob_ISDIR; - au_fset_hnjob(flags[AuHn_PARENT], DIRENT); - au_fset_hnjob(flags[AuHn_CHILD], GEN); - switch (mask & FS_EVENTS_POSS_ON_CHILD) { - case FS_MOVED_FROM: - case FS_MOVED_TO: - au_fset_hnjob(flags[AuHn_CHILD], XINO0); - au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); - /*FALLTHROUGH*/ - case FS_CREATE: - AuDebugOn(!h_child_name); - break; - - case FS_DELETE: - /* - * aufs never be able to get this child inode. - * revalidation should be in d_revalidate() - * by checking i_nlink, i_generation or d_unhashed(). - */ - AuDebugOn(!h_child_name); - au_fset_hnjob(flags[AuHn_CHILD], TRYXINO0); - au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); - break; - - default: - AuDebugOn(1); - } - - if (wh) - h_child_inode = NULL; - - err = -ENOMEM; - /* iput() and kfree() will be called in au_hnotify() */ - args = kmalloc(sizeof(*args) + len + 1, GFP_NOFS); - if (unlikely(!args)) { - AuErr1("no memory\n"); - iput(dir); - goto out; - } - args->flags[AuHn_PARENT] = flags[AuHn_PARENT]; - args->flags[AuHn_CHILD] = flags[AuHn_CHILD]; - args->mask = mask; - args->dir = dir; - args->h_dir = igrab(h_dir); - if (h_child_inode) - h_child_inode = igrab(h_child_inode); /* can be NULL */ - args->h_child_inode = h_child_inode; - args->h_child_nlen = len; - if (len) { - p = (void *)args; - p += sizeof(*args); - memcpy(p, h_child_name, len); - p[len] = 0; - } - - /* NFS fires the event for silly-renamed one from kworker */ - f = 0; - if (!dir->i_nlink - || (au_test_nfs(h_dir->i_sb) && (mask & FS_DELETE))) - f = AuWkq_NEST; - err = au_wkq_nowait(au_hn_bh, args, dir->i_sb, f); - if (unlikely(err)) { - pr_err("wkq %d\n", err); - iput(args->h_child_inode); - iput(args->h_dir); - iput(args->dir); - kfree(args); - } - -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm) -{ - int err; - - AuDebugOn(!(udba & AuOptMask_UDBA)); - - err = 0; - if (au_hnotify_op.reset_br) - err = au_hnotify_op.reset_br(udba, br, perm); - - return err; -} - -int au_hnotify_init_br(struct au_branch *br, int perm) -{ - int err; - - err = 0; - if (au_hnotify_op.init_br) - err = au_hnotify_op.init_br(br, perm); - - return err; -} - -void au_hnotify_fin_br(struct au_branch *br) -{ - if (au_hnotify_op.fin_br) - au_hnotify_op.fin_br(br); -} - -static void au_hn_destroy_cache(void) -{ - kmem_cache_destroy(au_cachep[AuCache_HNOTIFY]); - au_cachep[AuCache_HNOTIFY] = NULL; -} - -int __init au_hnotify_init(void) -{ - int err; - - err = -ENOMEM; - au_cachep[AuCache_HNOTIFY] = AuCache(au_hnotify); - if (au_cachep[AuCache_HNOTIFY]) { - err = 0; - if (au_hnotify_op.init) - err = au_hnotify_op.init(); - if (unlikely(err)) - au_hn_destroy_cache(); - } - AuTraceErr(err); - return err; -} - -void au_hnotify_fin(void) -{ - if (au_hnotify_op.fin) - au_hnotify_op.fin(); - /* cf. au_cache_fin() */ - if (au_cachep[AuCache_HNOTIFY]) - au_hn_destroy_cache(); -} diff --git a/fs/aufs/i_op.c b/fs/aufs/i_op.c deleted file mode 100644 index 17364e4f1..000000000 --- a/fs/aufs/i_op.c +++ /dev/null @@ -1,1447 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * inode operations (except add/del/rename) - */ - -#include <linux/device_cgroup.h> -#include <linux/fs_stack.h> -#include <linux/mm.h> -#include <linux/namei.h> -#include <linux/security.h> -#include "aufs.h" - -static int h_permission(struct inode *h_inode, int mask, - struct vfsmount *h_mnt, int brperm) -{ - int err; - const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); - - err = -EACCES; - if ((write_mask && IS_IMMUTABLE(h_inode)) - || ((mask & MAY_EXEC) - && S_ISREG(h_inode->i_mode) - && ((h_mnt->mnt_flags & MNT_NOEXEC) - || !(h_inode->i_mode & S_IXUGO)))) - goto out; - - /* - * - skip the lower fs test in the case of write to ro branch. - * - nfs dir permission write check is optimized, but a policy for - * link/rename requires a real check. - * - nfs always sets MS_POSIXACL regardless its mount option 'noacl.' - * in this case, generic_permission() returns -EOPNOTSUPP. - */ - if ((write_mask && !au_br_writable(brperm)) - || (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode) - && write_mask && !(mask & MAY_READ)) - || !h_inode->i_op->permission) { - /* AuLabel(generic_permission); */ - /* AuDbg("get_acl %pf\n", h_inode->i_op->get_acl); */ - err = generic_permission(h_inode, mask); - if (err == -EOPNOTSUPP && au_test_nfs_noacl(h_inode)) - err = h_inode->i_op->permission(h_inode, mask); - AuTraceErr(err); - } else { - /* AuLabel(h_inode->permission); */ - err = h_inode->i_op->permission(h_inode, mask); - AuTraceErr(err); - } - - if (!err) - err = devcgroup_inode_permission(h_inode, mask); - if (!err) - err = security_inode_permission(h_inode, mask); - -#if 0 - if (!err) { - /* todo: do we need to call ima_path_check()? */ - struct path h_path = { - .dentry = - .mnt = h_mnt - }; - err = ima_path_check(&h_path, - mask & (MAY_READ | MAY_WRITE | MAY_EXEC), - IMA_COUNT_LEAVE); - } -#endif - -out: - return err; -} - -static int aufs_permission(struct inode *inode, int mask) -{ - int err; - aufs_bindex_t bindex, bend; - const unsigned char isdir = !!S_ISDIR(inode->i_mode), - write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); - struct inode *h_inode; - struct super_block *sb; - struct au_branch *br; - - /* todo: support rcu-walk? */ - if (mask & MAY_NOT_BLOCK) - return -ECHILD; - - sb = inode->i_sb; - si_read_lock(sb, AuLock_FLUSH); - ii_read_lock_child(inode); -#if 0 - err = au_iigen_test(inode, au_sigen(sb)); - if (unlikely(err)) - goto out; -#endif - - if (!isdir - || write_mask - || au_opt_test(au_mntflags(sb), DIRPERM1)) { - err = au_busy_or_stale(); - h_inode = au_h_iptr(inode, au_ibstart(inode)); - if (unlikely(!h_inode - || (h_inode->i_mode & S_IFMT) - != (inode->i_mode & S_IFMT))) - goto out; - - err = 0; - bindex = au_ibstart(inode); - br = au_sbr(sb, bindex); - err = h_permission(h_inode, mask, au_br_mnt(br), br->br_perm); - if (write_mask - && !err - && !special_file(h_inode->i_mode)) { - /* test whether the upper writable branch exists */ - err = -EROFS; - for (; bindex >= 0; bindex--) - if (!au_br_rdonly(au_sbr(sb, bindex))) { - err = 0; - break; - } - } - goto out; - } - - /* non-write to dir */ - err = 0; - bend = au_ibend(inode); - for (bindex = au_ibstart(inode); !err && bindex <= bend; bindex++) { - h_inode = au_h_iptr(inode, bindex); - if (h_inode) { - err = au_busy_or_stale(); - if (unlikely(!S_ISDIR(h_inode->i_mode))) - break; - - br = au_sbr(sb, bindex); - err = h_permission(h_inode, mask, au_br_mnt(br), - br->br_perm); - } - } - -out: - ii_read_unlock(inode); - si_read_unlock(sb); - return err; -} - -/* ---------------------------------------------------------------------- */ - -static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, - unsigned int flags) -{ - struct dentry *ret, *parent; - struct inode *inode; - struct super_block *sb; - int err, npositive; - - IMustLock(dir); - - /* todo: support rcu-walk? */ - ret = ERR_PTR(-ECHILD); - if (flags & LOOKUP_RCU) - goto out; - - ret = ERR_PTR(-ENAMETOOLONG); - if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) - goto out; - - sb = dir->i_sb; - err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); - ret = ERR_PTR(err); - if (unlikely(err)) - goto out; - - err = au_di_init(dentry); - ret = ERR_PTR(err); - if (unlikely(err)) - goto out_si; - - inode = NULL; - npositive = 0; /* suppress a warning */ - parent = dentry->d_parent; /* dir inode is locked */ - di_read_lock_parent(parent, AuLock_IR); - err = au_alive_dir(parent); - if (!err) - err = au_digen_test(parent, au_sigen(sb)); - if (!err) { - npositive = au_lkup_dentry(dentry, au_dbstart(parent), - /*type*/0); - err = npositive; - } - di_read_unlock(parent, AuLock_IR); - ret = ERR_PTR(err); - if (unlikely(err < 0)) - goto out_unlock; - - if (npositive) { - inode = au_new_inode(dentry, /*must_new*/0); - if (IS_ERR(inode)) { - ret = (void *)inode; - inode = NULL; - goto out_unlock; - } - } - - if (inode) - atomic_inc(&inode->i_count); - ret = d_splice_alias(inode, dentry); -#if 0 - if (unlikely(d_need_lookup(dentry))) { - spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_NEED_LOOKUP; - spin_unlock(&dentry->d_lock); - } else -#endif - if (inode) { - if (!IS_ERR(ret)) { - iput(inode); - if (ret && ret != dentry) - ii_write_unlock(inode); - } else { - ii_write_unlock(inode); - iput(inode); - inode = NULL; - } - } - -out_unlock: - di_write_unlock(dentry); - if (inode) { - /* verbose coding for lock class name */ - if (unlikely(S_ISLNK(inode->i_mode))) - au_rw_class(&au_di(dentry)->di_rwsem, - au_lc_key + AuLcSymlink_DIINFO); - else if (unlikely(S_ISDIR(inode->i_mode))) - au_rw_class(&au_di(dentry)->di_rwsem, - au_lc_key + AuLcDir_DIINFO); - else /* likely */ - au_rw_class(&au_di(dentry)->di_rwsem, - au_lc_key + AuLcNonDir_DIINFO); - } -out_si: - si_read_unlock(sb); -out: - return ret; -} - -/* ---------------------------------------------------------------------- */ - -struct aopen_node { - struct hlist_node hlist; - struct file *file, *h_file; -}; - -static int au_do_aopen(struct inode *inode, struct file *file) -{ - struct au_sphlhead *aopen; - struct aopen_node *node; - struct au_do_open_args args = { - .no_lock = 1, - .open = au_do_open_nondir - }; - - aopen = &au_sbi(inode->i_sb)->si_aopen; - spin_lock(&aopen->spin); - hlist_for_each_entry(node, &aopen->head, hlist) - if (node->file == file) { - args.h_file = node->h_file; - break; - } - spin_unlock(&aopen->spin); - /* AuDebugOn(!args.h_file); */ - - return au_do_open(file, &args); -} - -static int aufs_atomic_open(struct inode *dir, struct dentry *dentry, - struct file *file, unsigned int open_flag, - umode_t create_mode, int *opened) -{ - int err, h_opened = *opened; - struct dentry *parent; - struct dentry *d; - struct au_sphlhead *aopen; - struct vfsub_aopen_args args = { - .open_flag = open_flag, - .create_mode = create_mode, - .opened = &h_opened - }; - struct aopen_node aopen_node = { - .file = file - }; - - IMustLock(dir); - AuDbg("open_flag 0x%x\n", open_flag); - AuDbgDentry(dentry); - - err = 0; - if (!au_di(dentry)) { - d = aufs_lookup(dir, dentry, /*flags*/0); - if (IS_ERR(d)) { - err = PTR_ERR(d); - goto out; - } else if (d) { - /* - * obsoleted dentry found. - * another error will be returned later. - */ - d_drop(d); - dput(d); - AuDbgDentry(d); - } - AuDbgDentry(dentry); - } - - if (d_is_positive(dentry) - || d_unhashed(dentry) - || d_unlinked(dentry) - || !(open_flag & O_CREAT)) - goto out_no_open; - - err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); - if (unlikely(err)) - goto out; - - parent = dentry->d_parent; /* dir is locked */ - di_write_lock_parent(parent); - err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); - if (unlikely(err)) - goto out_unlock; - - AuDbgDentry(dentry); - if (d_is_positive(dentry)) - goto out_unlock; - - args.file = get_empty_filp(); - err = PTR_ERR(args.file); - if (IS_ERR(args.file)) - goto out_unlock; - - args.file->f_flags = file->f_flags; - err = au_aopen_or_create(dir, dentry, &args); - AuTraceErr(err); - AuDbgFile(args.file); - if (unlikely(err < 0)) { - if (h_opened & FILE_OPENED) - fput(args.file); - else - put_filp(args.file); - goto out_unlock; - } - - /* some filesystems don't set FILE_CREATED while succeeded? */ - *opened |= FILE_CREATED; - if (h_opened & FILE_OPENED) - aopen_node.h_file = args.file; - else { - put_filp(args.file); - args.file = NULL; - } - aopen = &au_sbi(dir->i_sb)->si_aopen; - au_sphl_add(&aopen_node.hlist, aopen); - err = finish_open(file, dentry, au_do_aopen, opened); - au_sphl_del(&aopen_node.hlist, aopen); - AuTraceErr(err); - AuDbgFile(file); - if (aopen_node.h_file) - fput(aopen_node.h_file); - -out_unlock: - di_write_unlock(parent); - aufs_read_unlock(dentry, AuLock_DW); - AuDbgDentry(dentry); - if (unlikely(err)) - goto out; -out_no_open: - if (!err && !(*opened & FILE_CREATED)) { - AuLabel(out_no_open); - dget(dentry); - err = finish_no_open(file, dentry); - } -out: - AuDbg("%pd%s%s\n", dentry, - (*opened & FILE_CREATED) ? " created" : "", - (*opened & FILE_OPENED) ? " opened" : ""); - AuTraceErr(err); - return err; -} - - -/* ---------------------------------------------------------------------- */ - -static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent, - const unsigned char add_entry, aufs_bindex_t bcpup, - aufs_bindex_t bstart) -{ - int err; - struct dentry *h_parent; - struct inode *h_dir; - - if (add_entry) - IMustLock(d_inode(parent)); - else - di_write_lock_parent(parent); - - err = 0; - if (!au_h_dptr(parent, bcpup)) { - if (bstart > bcpup) - err = au_cpup_dirs(dentry, bcpup); - else if (bstart < bcpup) - err = au_cpdown_dirs(dentry, bcpup); - else - BUG(); - } - if (!err && add_entry && !au_ftest_wrdir(add_entry, TMPFILE)) { - h_parent = au_h_dptr(parent, bcpup); - h_dir = d_inode(h_parent); - mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); - err = au_lkup_neg(dentry, bcpup, /*wh*/0); - /* todo: no unlock here */ - mutex_unlock(&h_dir->i_mutex); - - AuDbg("bcpup %d\n", bcpup); - if (!err) { - if (d_really_is_negative(dentry)) - au_set_h_dptr(dentry, bstart, NULL); - au_update_dbrange(dentry, /*do_put_zero*/0); - } - } - - if (!add_entry) - di_write_unlock(parent); - if (!err) - err = bcpup; /* success */ - - AuTraceErr(err); - return err; -} - -/* - * decide the branch and the parent dir where we will create a new entry. - * returns new bindex or an error. - * copyup the parent dir if needed. - */ -int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, - struct au_wr_dir_args *args) -{ - int err; - unsigned int flags; - aufs_bindex_t bcpup, bstart, src_bstart; - const unsigned char add_entry - = au_ftest_wrdir(args->flags, ADD_ENTRY) - | au_ftest_wrdir(args->flags, TMPFILE); - struct super_block *sb; - struct dentry *parent; - struct au_sbinfo *sbinfo; - - sb = dentry->d_sb; - sbinfo = au_sbi(sb); - parent = dget_parent(dentry); - bstart = au_dbstart(dentry); - bcpup = bstart; - if (args->force_btgt < 0) { - if (src_dentry) { - src_bstart = au_dbstart(src_dentry); - if (src_bstart < bstart) - bcpup = src_bstart; - } else if (add_entry) { - flags = 0; - if (au_ftest_wrdir(args->flags, ISDIR)) - au_fset_wbr(flags, DIR); - err = AuWbrCreate(sbinfo, dentry, flags); - bcpup = err; - } - - if (bcpup < 0 || au_test_ro(sb, bcpup, d_inode(dentry))) { - if (add_entry) - err = AuWbrCopyup(sbinfo, dentry); - else { - if (!IS_ROOT(dentry)) { - di_read_lock_parent(parent, !AuLock_IR); - err = AuWbrCopyup(sbinfo, dentry); - di_read_unlock(parent, !AuLock_IR); - } else - err = AuWbrCopyup(sbinfo, dentry); - } - bcpup = err; - if (unlikely(err < 0)) - goto out; - } - } else { - bcpup = args->force_btgt; - AuDebugOn(au_test_ro(sb, bcpup, d_inode(dentry))); - } - - AuDbg("bstart %d, bcpup %d\n", bstart, bcpup); - err = bcpup; - if (bcpup == bstart) - goto out; /* success */ - - /* copyup the new parent into the branch we process */ - err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, bstart); - if (err >= 0) { - if (d_really_is_negative(dentry)) { - au_set_h_dptr(dentry, bstart, NULL); - au_set_dbstart(dentry, bcpup); - au_set_dbend(dentry, bcpup); - } - AuDebugOn(add_entry - && !au_ftest_wrdir(args->flags, TMPFILE) - && !au_h_dptr(dentry, bcpup)); - } - -out: - dput(parent); - return err; -} - -/* ---------------------------------------------------------------------- */ - -void au_pin_hdir_unlock(struct au_pin *p) -{ - if (p->hdir) - au_hn_imtx_unlock(p->hdir); -} - -int au_pin_hdir_lock(struct au_pin *p) -{ - int err; - - err = 0; - if (!p->hdir) - goto out; - - /* even if an error happens later, keep this lock */ - au_hn_imtx_lock_nested(p->hdir, p->lsc_hi); - - err = -EBUSY; - if (unlikely(p->hdir->hi_inode != d_inode(p->h_parent))) - goto out; - - err = 0; - if (p->h_dentry) - err = au_h_verify(p->h_dentry, p->udba, p->hdir->hi_inode, - p->h_parent, p->br); - -out: - return err; -} - -int au_pin_hdir_relock(struct au_pin *p) -{ - int err, i; - struct inode *h_i; - struct dentry *h_d[] = { - p->h_dentry, - p->h_parent - }; - - err = au_pin_hdir_lock(p); - if (unlikely(err)) - goto out; - - for (i = 0; !err && i < sizeof(h_d)/sizeof(*h_d); i++) { - if (!h_d[i]) - continue; - if (d_is_positive(h_d[i])) { - h_i = d_inode(h_d[i]); - err = !h_i->i_nlink; - } - } - -out: - return err; -} - -void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task) -{ -#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP) - p->hdir->hi_inode->i_mutex.owner = task; -#endif -} - -void au_pin_hdir_acquire_nest(struct au_pin *p) -{ - if (p->hdir) { - mutex_acquire_nest(&p->hdir->hi_inode->i_mutex.dep_map, - p->lsc_hi, 0, NULL, _RET_IP_); - au_pin_hdir_set_owner(p, current); - } -} - -void au_pin_hdir_release(struct au_pin *p) -{ - if (p->hdir) { - au_pin_hdir_set_owner(p, p->task); - mutex_release(&p->hdir->hi_inode->i_mutex.dep_map, 1, _RET_IP_); - } -} - -struct dentry *au_pinned_h_parent(struct au_pin *pin) -{ - if (pin && pin->parent) - return au_h_dptr(pin->parent, pin->bindex); - return NULL; -} - -void au_unpin(struct au_pin *p) -{ - if (p->hdir) - au_pin_hdir_unlock(p); - if (p->h_mnt && au_ftest_pin(p->flags, MNT_WRITE)) - vfsub_mnt_drop_write(p->h_mnt); - if (!p->hdir) - return; - - if (!au_ftest_pin(p->flags, DI_LOCKED)) - di_read_unlock(p->parent, AuLock_IR); - iput(p->hdir->hi_inode); - dput(p->parent); - p->parent = NULL; - p->hdir = NULL; - p->h_mnt = NULL; - /* do not clear p->task */ -} - -int au_do_pin(struct au_pin *p) -{ - int err; - struct super_block *sb; - struct inode *h_dir; - - err = 0; - sb = p->dentry->d_sb; - p->br = au_sbr(sb, p->bindex); - if (IS_ROOT(p->dentry)) { - if (au_ftest_pin(p->flags, MNT_WRITE)) { - p->h_mnt = au_br_mnt(p->br); - err = vfsub_mnt_want_write(p->h_mnt); - if (unlikely(err)) { - au_fclr_pin(p->flags, MNT_WRITE); - goto out_err; - } - } - goto out; - } - - p->h_dentry = NULL; - if (p->bindex <= au_dbend(p->dentry)) - p->h_dentry = au_h_dptr(p->dentry, p->bindex); - - p->parent = dget_parent(p->dentry); - if (!au_ftest_pin(p->flags, DI_LOCKED)) - di_read_lock(p->parent, AuLock_IR, p->lsc_di); - - h_dir = NULL; - p->h_parent = au_h_dptr(p->parent, p->bindex); - p->hdir = au_hi(d_inode(p->parent), p->bindex); - if (p->hdir) - h_dir = p->hdir->hi_inode; - - /* - * udba case, or - * if DI_LOCKED is not set, then p->parent may be different - * and h_parent can be NULL. - */ - if (unlikely(!p->hdir || !h_dir || !p->h_parent)) { - err = -EBUSY; - if (!au_ftest_pin(p->flags, DI_LOCKED)) - di_read_unlock(p->parent, AuLock_IR); - dput(p->parent); - p->parent = NULL; - goto out_err; - } - - if (au_ftest_pin(p->flags, MNT_WRITE)) { - p->h_mnt = au_br_mnt(p->br); - err = vfsub_mnt_want_write(p->h_mnt); - if (unlikely(err)) { - au_fclr_pin(p->flags, MNT_WRITE); - if (!au_ftest_pin(p->flags, DI_LOCKED)) - di_read_unlock(p->parent, AuLock_IR); - dput(p->parent); - p->parent = NULL; - goto out_err; - } - } - - au_igrab(h_dir); - err = au_pin_hdir_lock(p); - if (!err) - goto out; /* success */ - - au_unpin(p); - -out_err: - pr_err("err %d\n", err); - err = au_busy_or_stale(); -out: - return err; -} - -void au_pin_init(struct au_pin *p, struct dentry *dentry, - aufs_bindex_t bindex, int lsc_di, int lsc_hi, - unsigned int udba, unsigned char flags) -{ - p->dentry = dentry; - p->udba = udba; - p->lsc_di = lsc_di; - p->lsc_hi = lsc_hi; - p->flags = flags; - p->bindex = bindex; - - p->parent = NULL; - p->hdir = NULL; - p->h_mnt = NULL; - - p->h_dentry = NULL; - p->h_parent = NULL; - p->br = NULL; - p->task = current; -} - -int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, - unsigned int udba, unsigned char flags) -{ - au_pin_init(pin, dentry, bindex, AuLsc_DI_PARENT, AuLsc_I_PARENT2, - udba, flags); - return au_do_pin(pin); -} - -/* ---------------------------------------------------------------------- */ - -/* - * ->setattr() and ->getattr() are called in various cases. - * chmod, stat: dentry is revalidated. - * fchmod, fstat: file and dentry are not revalidated, additionally they may be - * unhashed. - * for ->setattr(), ia->ia_file is passed from ftruncate only. - */ -/* todo: consolidate with do_refresh() and simple_reval_dpath() */ -int au_reval_for_attr(struct dentry *dentry, unsigned int sigen) -{ - int err; - struct dentry *parent; - - err = 0; - if (au_digen_test(dentry, sigen)) { - parent = dget_parent(dentry); - di_read_lock_parent(parent, AuLock_IR); - err = au_refresh_dentry(dentry, parent); - di_read_unlock(parent, AuLock_IR); - dput(parent); - } - - AuTraceErr(err); - return err; -} - -int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, - struct au_icpup_args *a) -{ - int err; - loff_t sz; - aufs_bindex_t bstart, ibstart; - struct dentry *hi_wh, *parent; - struct inode *inode; - struct au_wr_dir_args wr_dir_args = { - .force_btgt = -1, - .flags = 0 - }; - - if (d_is_dir(dentry)) - au_fset_wrdir(wr_dir_args.flags, ISDIR); - /* plink or hi_wh() case */ - bstart = au_dbstart(dentry); - inode = d_inode(dentry); - ibstart = au_ibstart(inode); - if (bstart != ibstart && !au_test_ro(inode->i_sb, ibstart, inode)) - wr_dir_args.force_btgt = ibstart; - err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); - if (unlikely(err < 0)) - goto out; - a->btgt = err; - if (err != bstart) - au_fset_icpup(a->flags, DID_CPUP); - - err = 0; - a->pin_flags = AuPin_MNT_WRITE; - parent = NULL; - if (!IS_ROOT(dentry)) { - au_fset_pin(a->pin_flags, DI_LOCKED); - parent = dget_parent(dentry); - di_write_lock_parent(parent); - } - - err = au_pin(&a->pin, dentry, a->btgt, a->udba, a->pin_flags); - if (unlikely(err)) - goto out_parent; - - a->h_path.dentry = au_h_dptr(dentry, bstart); - sz = -1; - a->h_inode = d_inode(a->h_path.dentry); - if (ia && (ia->ia_valid & ATTR_SIZE)) { - mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); - if (ia->ia_size < i_size_read(a->h_inode)) - sz = ia->ia_size; - mutex_unlock(&a->h_inode->i_mutex); - } - - hi_wh = NULL; - if (au_ftest_icpup(a->flags, DID_CPUP) && d_unlinked(dentry)) { - hi_wh = au_hi_wh(inode, a->btgt); - if (!hi_wh) { - struct au_cp_generic cpg = { - .dentry = dentry, - .bdst = a->btgt, - .bsrc = -1, - .len = sz, - .pin = &a->pin - }; - err = au_sio_cpup_wh(&cpg, /*file*/NULL); - if (unlikely(err)) - goto out_unlock; - hi_wh = au_hi_wh(inode, a->btgt); - /* todo: revalidate hi_wh? */ - } - } - - if (parent) { - au_pin_set_parent_lflag(&a->pin, /*lflag*/0); - di_downgrade_lock(parent, AuLock_IR); - dput(parent); - parent = NULL; - } - if (!au_ftest_icpup(a->flags, DID_CPUP)) - goto out; /* success */ - - if (!d_unhashed(dentry)) { - struct au_cp_generic cpg = { - .dentry = dentry, - .bdst = a->btgt, - .bsrc = bstart, - .len = sz, - .pin = &a->pin, - .flags = AuCpup_DTIME | AuCpup_HOPEN - }; - err = au_sio_cpup_simple(&cpg); - if (!err) - a->h_path.dentry = au_h_dptr(dentry, a->btgt); - } else if (!hi_wh) - a->h_path.dentry = au_h_dptr(dentry, a->btgt); - else - a->h_path.dentry = hi_wh; /* do not dget here */ - -out_unlock: - a->h_inode = d_inode(a->h_path.dentry); - if (!err) - goto out; /* success */ - au_unpin(&a->pin); -out_parent: - if (parent) { - di_write_unlock(parent); - dput(parent); - } -out: - if (!err) - mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); - return err; -} - -static int aufs_setattr(struct dentry *dentry, struct iattr *ia) -{ - int err; - struct inode *inode, *delegated; - struct super_block *sb; - struct file *file; - struct au_icpup_args *a; - - inode = d_inode(dentry); - IMustLock(inode); - - err = -ENOMEM; - a = kzalloc(sizeof(*a), GFP_NOFS); - if (unlikely(!a)) - goto out; - - if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) - ia->ia_valid &= ~ATTR_MODE; - - file = NULL; - sb = dentry->d_sb; - err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); - if (unlikely(err)) - goto out_kfree; - - if (ia->ia_valid & ATTR_FILE) { - /* currently ftruncate(2) only */ - AuDebugOn(!d_is_reg(dentry)); - file = ia->ia_file; - err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); - if (unlikely(err)) - goto out_si; - ia->ia_file = au_hf_top(file); - a->udba = AuOpt_UDBA_NONE; - } else { - /* fchmod() doesn't pass ia_file */ - a->udba = au_opt_udba(sb); - di_write_lock_child(dentry); - /* no d_unlinked(), to set UDBA_NONE for root */ - if (d_unhashed(dentry)) - a->udba = AuOpt_UDBA_NONE; - if (a->udba != AuOpt_UDBA_NONE) { - AuDebugOn(IS_ROOT(dentry)); - err = au_reval_for_attr(dentry, au_sigen(sb)); - if (unlikely(err)) - goto out_dentry; - } - } - - err = au_pin_and_icpup(dentry, ia, a); - if (unlikely(err < 0)) - goto out_dentry; - if (au_ftest_icpup(a->flags, DID_CPUP)) { - ia->ia_file = NULL; - ia->ia_valid &= ~ATTR_FILE; - } - - a->h_path.mnt = au_sbr_mnt(sb, a->btgt); - if ((ia->ia_valid & (ATTR_MODE | ATTR_CTIME)) - == (ATTR_MODE | ATTR_CTIME)) { - err = security_path_chmod(&a->h_path, ia->ia_mode); - if (unlikely(err)) - goto out_unlock; - } else if ((ia->ia_valid & (ATTR_UID | ATTR_GID)) - && (ia->ia_valid & ATTR_CTIME)) { - err = security_path_chown(&a->h_path, ia->ia_uid, ia->ia_gid); - if (unlikely(err)) - goto out_unlock; - } - - if (ia->ia_valid & ATTR_SIZE) { - struct file *f; - - if (ia->ia_size < i_size_read(inode)) - /* unmap only */ - truncate_setsize(inode, ia->ia_size); - - f = NULL; - if (ia->ia_valid & ATTR_FILE) - f = ia->ia_file; - mutex_unlock(&a->h_inode->i_mutex); - err = vfsub_trunc(&a->h_path, ia->ia_size, ia->ia_valid, f); - mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); - } else { - delegated = NULL; - while (1) { - err = vfsub_notify_change(&a->h_path, ia, &delegated); - if (delegated) { - err = break_deleg_wait(&delegated); - if (!err) - continue; - } - break; - } - } - if (!err) - au_cpup_attr_changeable(inode); - -out_unlock: - mutex_unlock(&a->h_inode->i_mutex); - au_unpin(&a->pin); - if (unlikely(err)) - au_update_dbstart(dentry); -out_dentry: - di_write_unlock(dentry); - if (file) { - fi_write_unlock(file); - ia->ia_file = file; - ia->ia_valid |= ATTR_FILE; - } -out_si: - si_read_unlock(sb); -out_kfree: - kfree(a); -out: - AuTraceErr(err); - return err; -} - -#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL) -static int au_h_path_to_set_attr(struct dentry *dentry, - struct au_icpup_args *a, struct path *h_path) -{ - int err; - struct super_block *sb; - - sb = dentry->d_sb; - a->udba = au_opt_udba(sb); - /* no d_unlinked(), to set UDBA_NONE for root */ - if (d_unhashed(dentry)) - a->udba = AuOpt_UDBA_NONE; - if (a->udba != AuOpt_UDBA_NONE) { - AuDebugOn(IS_ROOT(dentry)); - err = au_reval_for_attr(dentry, au_sigen(sb)); - if (unlikely(err)) - goto out; - } - err = au_pin_and_icpup(dentry, /*ia*/NULL, a); - if (unlikely(err < 0)) - goto out; - - h_path->dentry = a->h_path.dentry; - h_path->mnt = au_sbr_mnt(sb, a->btgt); - -out: - return err; -} - -ssize_t au_srxattr(struct dentry *dentry, struct au_srxattr *arg) -{ - int err; - struct path h_path; - struct super_block *sb; - struct au_icpup_args *a; - struct inode *inode, *h_inode; - - inode = d_inode(dentry); - IMustLock(inode); - - err = -ENOMEM; - a = kzalloc(sizeof(*a), GFP_NOFS); - if (unlikely(!a)) - goto out; - - sb = dentry->d_sb; - err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); - if (unlikely(err)) - goto out_kfree; - - h_path.dentry = NULL; /* silence gcc */ - di_write_lock_child(dentry); - err = au_h_path_to_set_attr(dentry, a, &h_path); - if (unlikely(err)) - goto out_di; - - mutex_unlock(&a->h_inode->i_mutex); - switch (arg->type) { - case AU_XATTR_SET: - err = vfsub_setxattr(h_path.dentry, - arg->u.set.name, arg->u.set.value, - arg->u.set.size, arg->u.set.flags); - break; - case AU_XATTR_REMOVE: - err = vfsub_removexattr(h_path.dentry, arg->u.remove.name); - break; - case AU_ACL_SET: - err = -EOPNOTSUPP; - h_inode = d_inode(h_path.dentry); - if (h_inode->i_op->set_acl) - err = h_inode->i_op->set_acl(h_inode, - arg->u.acl_set.acl, - arg->u.acl_set.type); - break; - } - if (!err) - au_cpup_attr_timesizes(inode); - - au_unpin(&a->pin); - if (unlikely(err)) - au_update_dbstart(dentry); - -out_di: - di_write_unlock(dentry); - si_read_unlock(sb); -out_kfree: - kfree(a); -out: - AuTraceErr(err); - return err; -} -#endif - -static void au_refresh_iattr(struct inode *inode, struct kstat *st, - unsigned int nlink) -{ - unsigned int n; - - inode->i_mode = st->mode; - /* don't i_[ug]id_write() here */ - inode->i_uid = st->uid; - inode->i_gid = st->gid; - inode->i_atime = st->atime; - inode->i_mtime = st->mtime; - inode->i_ctime = st->ctime; - - au_cpup_attr_nlink(inode, /*force*/0); - if (S_ISDIR(inode->i_mode)) { - n = inode->i_nlink; - n -= nlink; - n += st->nlink; - smp_mb(); /* for i_nlink */ - /* 0 can happen */ - set_nlink(inode, n); - } - - spin_lock(&inode->i_lock); - inode->i_blocks = st->blocks; - i_size_write(inode, st->size); - spin_unlock(&inode->i_lock); -} - -/* - * common routine for aufs_getattr() and aufs_getxattr(). - * returns zero or negative (an error). - * @dentry will be read-locked in success. - */ -int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path) -{ - int err; - unsigned int mnt_flags, sigen; - unsigned char udba_none; - aufs_bindex_t bindex; - struct super_block *sb, *h_sb; - struct inode *inode; - - h_path->mnt = NULL; - h_path->dentry = NULL; - - err = 0; - sb = dentry->d_sb; - mnt_flags = au_mntflags(sb); - udba_none = !!au_opt_test(mnt_flags, UDBA_NONE); - - /* support fstat(2) */ - if (!d_unlinked(dentry) && !udba_none) { - sigen = au_sigen(sb); - err = au_digen_test(dentry, sigen); - if (!err) { - di_read_lock_child(dentry, AuLock_IR); - err = au_dbrange_test(dentry); - if (unlikely(err)) { - di_read_unlock(dentry, AuLock_IR); - goto out; - } - } else { - AuDebugOn(IS_ROOT(dentry)); - di_write_lock_child(dentry); - err = au_dbrange_test(dentry); - if (!err) - err = au_reval_for_attr(dentry, sigen); - if (!err) - di_downgrade_lock(dentry, AuLock_IR); - else { - di_write_unlock(dentry); - goto out; - } - } - } else - di_read_lock_child(dentry, AuLock_IR); - - inode = d_inode(dentry); - bindex = au_ibstart(inode); - h_path->mnt = au_sbr_mnt(sb, bindex); - h_sb = h_path->mnt->mnt_sb; - if (!force - && !au_test_fs_bad_iattr(h_sb) - && udba_none) - goto out; /* success */ - - if (au_dbstart(dentry) == bindex) - h_path->dentry = au_h_dptr(dentry, bindex); - else if (au_opt_test(mnt_flags, PLINK) && au_plink_test(inode)) { - h_path->dentry = au_plink_lkup(inode, bindex); - if (IS_ERR(h_path->dentry)) - /* pretending success */ - h_path->dentry = NULL; - else - dput(h_path->dentry); - } - -out: - return err; -} - -static int aufs_getattr(struct vfsmount *mnt __maybe_unused, - struct dentry *dentry, struct kstat *st) -{ - int err; - unsigned char positive; - struct path h_path; - struct inode *inode; - struct super_block *sb; - - inode = d_inode(dentry); - sb = dentry->d_sb; - err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); - if (unlikely(err)) - goto out; - err = au_h_path_getattr(dentry, /*force*/0, &h_path); - if (unlikely(err)) - goto out_si; - if (unlikely(!h_path.dentry)) - /* illegally overlapped or something */ - goto out_fill; /* pretending success */ - - positive = d_is_positive(h_path.dentry); - if (positive) - err = vfs_getattr(&h_path, st); - if (!err) { - if (positive) - au_refresh_iattr(inode, st, - d_inode(h_path.dentry)->i_nlink); - goto out_fill; /* success */ - } - AuTraceErr(err); - goto out_di; - -out_fill: - generic_fillattr(inode, st); -out_di: - di_read_unlock(dentry, AuLock_IR); -out_si: - si_read_unlock(sb); -out: - AuTraceErr(err); - return err; -} - -/* ---------------------------------------------------------------------- */ - -static int h_readlink(struct dentry *dentry, int bindex, char __user *buf, - int bufsiz) -{ - int err; - struct super_block *sb; - struct dentry *h_dentry; - struct inode *inode, *h_inode; - - err = -EINVAL; - h_dentry = au_h_dptr(dentry, bindex); - h_inode = d_inode(h_dentry); - if (unlikely(!h_inode->i_op->readlink)) - goto out; - - err = security_inode_readlink(h_dentry); - if (unlikely(err)) - goto out; - - sb = dentry->d_sb; - inode = d_inode(dentry); - if (!au_test_ro(sb, bindex, inode)) { - vfsub_touch_atime(au_sbr_mnt(sb, bindex), h_dentry); - fsstack_copy_attr_atime(inode, h_inode); - } - err = h_inode->i_op->readlink(h_dentry, buf, bufsiz); - -out: - return err; -} - -static int aufs_readlink(struct dentry *dentry, char __user *buf, int bufsiz) -{ - int err; - - err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); - if (unlikely(err)) - goto out; - err = au_d_hashed_positive(dentry); - if (!err) - err = h_readlink(dentry, au_dbstart(dentry), buf, bufsiz); - aufs_read_unlock(dentry, AuLock_IR); - -out: - return err; -} - -static void *aufs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - int err; - mm_segment_t old_fs; - union { - char *k; - char __user *u; - } buf; - - err = -ENOMEM; - buf.k = (void *)__get_free_page(GFP_NOFS); - if (unlikely(!buf.k)) - goto out; - - err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); - if (unlikely(err)) - goto out_name; - - err = au_d_hashed_positive(dentry); - if (!err) { - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = h_readlink(dentry, au_dbstart(dentry), buf.u, PATH_MAX); - set_fs(old_fs); - } - aufs_read_unlock(dentry, AuLock_IR); - - if (err >= 0) { - buf.k[err] = 0; - /* will be freed by put_link */ - nd_set_link(nd, buf.k); - return NULL; /* success */ - } - -out_name: - free_page((unsigned long)buf.k); -out: - AuTraceErr(err); - return ERR_PTR(err); -} - -static void aufs_put_link(struct dentry *dentry __maybe_unused, - struct nameidata *nd, void *cookie __maybe_unused) -{ - char *p; - - p = nd_get_link(nd); - if (!IS_ERR_OR_NULL(p)) - free_page((unsigned long)p); -} - -/* ---------------------------------------------------------------------- */ - -static int aufs_update_time(struct inode *inode, struct timespec *ts, int flags) -{ - int err; - struct super_block *sb; - struct inode *h_inode; - - sb = inode->i_sb; - /* mmap_sem might be acquired already, cf. aufs_mmap() */ - lockdep_off(); - si_read_lock(sb, AuLock_FLUSH); - ii_write_lock_child(inode); - lockdep_on(); - h_inode = au_h_iptr(inode, au_ibstart(inode)); - err = vfsub_update_time(h_inode, ts, flags); - lockdep_off(); - if (!err) - au_cpup_attr_timesizes(inode); - ii_write_unlock(inode); - si_read_unlock(sb); - lockdep_on(); - - if (!err && (flags & S_VERSION)) - inode_inc_iversion(inode); - - return err; -} - -/* ---------------------------------------------------------------------- */ - -struct inode_operations aufs_symlink_iop = { - .permission = aufs_permission, -#ifdef CONFIG_FS_POSIX_ACL - .get_acl = aufs_get_acl, - .set_acl = aufs_set_acl, /* unsupport for symlink? */ -#endif - - .setattr = aufs_setattr, - .getattr = aufs_getattr, - -#ifdef CONFIG_AUFS_XATTR - .setxattr = aufs_setxattr, - .getxattr = aufs_getxattr, - .listxattr = aufs_listxattr, - .removexattr = aufs_removexattr, -#endif - - .readlink = aufs_readlink, - .follow_link = aufs_follow_link, - .put_link = aufs_put_link, - - /* .update_time = aufs_update_time */ -}; - -struct inode_operations aufs_dir_iop = { - .create = aufs_create, - .lookup = aufs_lookup, - .link = aufs_link, - .unlink = aufs_unlink, - .symlink = aufs_symlink, - .mkdir = aufs_mkdir, - .rmdir = aufs_rmdir, - .mknod = aufs_mknod, - .rename = aufs_rename, - - .permission = aufs_permission, -#ifdef CONFIG_FS_POSIX_ACL - .get_acl = aufs_get_acl, - .set_acl = aufs_set_acl, -#endif - - .setattr = aufs_setattr, - .getattr = aufs_getattr, - -#ifdef CONFIG_AUFS_XATTR - .setxattr = aufs_setxattr, - .getxattr = aufs_getxattr, - .listxattr = aufs_listxattr, - .removexattr = aufs_removexattr, -#endif - - .update_time = aufs_update_time, - .atomic_open = aufs_atomic_open, - .tmpfile = aufs_tmpfile -}; - -struct inode_operations aufs_iop = { - .permission = aufs_permission, -#ifdef CONFIG_FS_POSIX_ACL - .get_acl = aufs_get_acl, - .set_acl = aufs_set_acl, -#endif - - .setattr = aufs_setattr, - .getattr = aufs_getattr, - -#ifdef CONFIG_AUFS_XATTR - .setxattr = aufs_setxattr, - .getxattr = aufs_getxattr, - .listxattr = aufs_listxattr, - .removexattr = aufs_removexattr, -#endif - - .update_time = aufs_update_time -}; diff --git a/fs/aufs/i_op_add.c b/fs/aufs/i_op_add.c deleted file mode 100644 index 209b26864..000000000 --- a/fs/aufs/i_op_add.c +++ /dev/null @@ -1,932 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * inode operations (add entry) - */ - -#include "aufs.h" - -/* - * final procedure of adding a new entry, except link(2). - * remove whiteout, instantiate, copyup the parent dir's times and size - * and update version. - * if it failed, re-create the removed whiteout. - */ -static int epilog(struct inode *dir, aufs_bindex_t bindex, - struct dentry *wh_dentry, struct dentry *dentry) -{ - int err, rerr; - aufs_bindex_t bwh; - struct path h_path; - struct super_block *sb; - struct inode *inode, *h_dir; - struct dentry *wh; - - bwh = -1; - sb = dir->i_sb; - if (wh_dentry) { - h_dir = d_inode(wh_dentry->d_parent); /* dir inode is locked */ - IMustLock(h_dir); - AuDebugOn(au_h_iptr(dir, bindex) != h_dir); - bwh = au_dbwh(dentry); - h_path.dentry = wh_dentry; - h_path.mnt = au_sbr_mnt(sb, bindex); - err = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, - dentry); - if (unlikely(err)) - goto out; - } - - inode = au_new_inode(dentry, /*must_new*/1); - if (!IS_ERR(inode)) { - d_instantiate(dentry, inode); - dir = d_inode(dentry->d_parent); /* dir inode is locked */ - IMustLock(dir); - au_dir_ts(dir, bindex); - dir->i_version++; - au_fhsm_wrote(sb, bindex, /*force*/0); - return 0; /* success */ - } - - err = PTR_ERR(inode); - if (!wh_dentry) - goto out; - - /* revert */ - /* dir inode is locked */ - wh = au_wh_create(dentry, bwh, wh_dentry->d_parent); - rerr = PTR_ERR(wh); - if (IS_ERR(wh)) { - AuIOErr("%pd reverting whiteout failed(%d, %d)\n", - dentry, err, rerr); - err = -EIO; - } else - dput(wh); - -out: - return err; -} - -static int au_d_may_add(struct dentry *dentry) -{ - int err; - - err = 0; - if (unlikely(d_unhashed(dentry))) - err = -ENOENT; - if (unlikely(d_really_is_positive(dentry))) - err = -EEXIST; - return err; -} - -/* - * simple tests for the adding inode operations. - * following the checks in vfs, plus the parent-child relationship. - */ -int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, - struct dentry *h_parent, int isdir) -{ - int err; - umode_t h_mode; - struct dentry *h_dentry; - struct inode *h_inode; - - err = -ENAMETOOLONG; - if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) - goto out; - - h_dentry = au_h_dptr(dentry, bindex); - if (d_really_is_negative(dentry)) { - err = -EEXIST; - if (unlikely(d_is_positive(h_dentry))) - goto out; - } else { - /* rename(2) case */ - err = -EIO; - if (unlikely(d_is_negative(h_dentry))) - goto out; - h_inode = d_inode(h_dentry); - if (unlikely(!h_inode->i_nlink)) - goto out; - - h_mode = h_inode->i_mode; - if (!isdir) { - err = -EISDIR; - if (unlikely(S_ISDIR(h_mode))) - goto out; - } else if (unlikely(!S_ISDIR(h_mode))) { - err = -ENOTDIR; - goto out; - } - } - - err = 0; - /* expected parent dir is locked */ - if (unlikely(h_parent != h_dentry->d_parent)) - err = -EIO; - -out: - AuTraceErr(err); - return err; -} - -/* - * initial procedure of adding a new entry. - * prepare writable branch and the parent dir, lock it, - * and lookup whiteout for the new entry. - */ -static struct dentry* -lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt, - struct dentry *src_dentry, struct au_pin *pin, - struct au_wr_dir_args *wr_dir_args) -{ - struct dentry *wh_dentry, *h_parent; - struct super_block *sb; - struct au_branch *br; - int err; - unsigned int udba; - aufs_bindex_t bcpup; - - AuDbg("%pd\n", dentry); - - err = au_wr_dir(dentry, src_dentry, wr_dir_args); - bcpup = err; - wh_dentry = ERR_PTR(err); - if (unlikely(err < 0)) - goto out; - - sb = dentry->d_sb; - udba = au_opt_udba(sb); - err = au_pin(pin, dentry, bcpup, udba, - AuPin_DI_LOCKED | AuPin_MNT_WRITE); - wh_dentry = ERR_PTR(err); - if (unlikely(err)) - goto out; - - h_parent = au_pinned_h_parent(pin); - if (udba != AuOpt_UDBA_NONE - && au_dbstart(dentry) == bcpup) - err = au_may_add(dentry, bcpup, h_parent, - au_ftest_wrdir(wr_dir_args->flags, ISDIR)); - else if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) - err = -ENAMETOOLONG; - wh_dentry = ERR_PTR(err); - if (unlikely(err)) - goto out_unpin; - - br = au_sbr(sb, bcpup); - if (dt) { - struct path tmp = { - .dentry = h_parent, - .mnt = au_br_mnt(br) - }; - au_dtime_store(dt, au_pinned_parent(pin), &tmp); - } - - wh_dentry = NULL; - if (bcpup != au_dbwh(dentry)) - goto out; /* success */ - - /* - * ENAMETOOLONG here means that if we allowed create such name, then it - * would not be able to removed in the future. So we don't allow such - * name here and we don't handle ENAMETOOLONG differently here. - */ - wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br); - -out_unpin: - if (IS_ERR(wh_dentry)) - au_unpin(pin); -out: - return wh_dentry; -} - -/* ---------------------------------------------------------------------- */ - -enum { Mknod, Symlink, Creat }; -struct simple_arg { - int type; - union { - struct { - umode_t mode; - bool want_excl; - bool try_aopen; - struct vfsub_aopen_args *aopen; - } c; - struct { - const char *symname; - } s; - struct { - umode_t mode; - dev_t dev; - } m; - } u; -}; - -static int add_simple(struct inode *dir, struct dentry *dentry, - struct simple_arg *arg) -{ - int err, rerr; - aufs_bindex_t bstart; - unsigned char created; - const unsigned char try_aopen - = (arg->type == Creat && arg->u.c.try_aopen); - struct dentry *wh_dentry, *parent; - struct inode *h_dir; - struct super_block *sb; - struct au_branch *br; - /* to reuduce stack size */ - struct { - struct au_dtime dt; - struct au_pin pin; - struct path h_path; - struct au_wr_dir_args wr_dir_args; - } *a; - - AuDbg("%pd\n", dentry); - IMustLock(dir); - - err = -ENOMEM; - a = kmalloc(sizeof(*a), GFP_NOFS); - if (unlikely(!a)) - goto out; - a->wr_dir_args.force_btgt = -1; - a->wr_dir_args.flags = AuWrDir_ADD_ENTRY; - - parent = dentry->d_parent; /* dir inode is locked */ - if (!try_aopen) { - err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); - if (unlikely(err)) - goto out_free; - } - err = au_d_may_add(dentry); - if (unlikely(err)) - goto out_unlock; - if (!try_aopen) - di_write_lock_parent(parent); - wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, - &a->pin, &a->wr_dir_args); - err = PTR_ERR(wh_dentry); - if (IS_ERR(wh_dentry)) - goto out_parent; - - bstart = au_dbstart(dentry); - sb = dentry->d_sb; - br = au_sbr(sb, bstart); - a->h_path.dentry = au_h_dptr(dentry, bstart); - a->h_path.mnt = au_br_mnt(br); - h_dir = au_pinned_h_dir(&a->pin); - switch (arg->type) { - case Creat: - err = 0; - if (!try_aopen || !h_dir->i_op->atomic_open) - err = vfsub_create(h_dir, &a->h_path, arg->u.c.mode, - arg->u.c.want_excl); - else - err = vfsub_atomic_open(h_dir, a->h_path.dentry, - arg->u.c.aopen, br); - break; - case Symlink: - err = vfsub_symlink(h_dir, &a->h_path, arg->u.s.symname); - break; - case Mknod: - err = vfsub_mknod(h_dir, &a->h_path, arg->u.m.mode, - arg->u.m.dev); - break; - default: - BUG(); - } - created = !err; - if (!err) - err = epilog(dir, bstart, wh_dentry, dentry); - - /* revert */ - if (unlikely(created && err && d_is_positive(a->h_path.dentry))) { - /* no delegation since it is just created */ - rerr = vfsub_unlink(h_dir, &a->h_path, /*delegated*/NULL, - /*force*/0); - if (rerr) { - AuIOErr("%pd revert failure(%d, %d)\n", - dentry, err, rerr); - err = -EIO; - } - au_dtime_revert(&a->dt); - } - - if (!err && try_aopen && !h_dir->i_op->atomic_open) - *arg->u.c.aopen->opened |= FILE_CREATED; - - au_unpin(&a->pin); - dput(wh_dentry); - -out_parent: - if (!try_aopen) - di_write_unlock(parent); -out_unlock: - if (unlikely(err)) { - au_update_dbstart(dentry); - d_drop(dentry); - } - if (!try_aopen) - aufs_read_unlock(dentry, AuLock_DW); -out_free: - kfree(a); -out: - return err; -} - -int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, - dev_t dev) -{ - struct simple_arg arg = { - .type = Mknod, - .u.m = { - .mode = mode, - .dev = dev - } - }; - return add_simple(dir, dentry, &arg); -} - -int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) -{ - struct simple_arg arg = { - .type = Symlink, - .u.s.symname = symname - }; - return add_simple(dir, dentry, &arg); -} - -int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode, - bool want_excl) -{ - struct simple_arg arg = { - .type = Creat, - .u.c = { - .mode = mode, - .want_excl = want_excl - } - }; - return add_simple(dir, dentry, &arg); -} - -int au_aopen_or_create(struct inode *dir, struct dentry *dentry, - struct vfsub_aopen_args *aopen_args) -{ - struct simple_arg arg = { - .type = Creat, - .u.c = { - .mode = aopen_args->create_mode, - .want_excl = aopen_args->open_flag & O_EXCL, - .try_aopen = true, - .aopen = aopen_args - } - }; - return add_simple(dir, dentry, &arg); -} - -int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) -{ - int err; - aufs_bindex_t bindex; - struct super_block *sb; - struct dentry *parent, *h_parent, *h_dentry; - struct inode *h_dir, *inode; - struct vfsmount *h_mnt; - struct au_wr_dir_args wr_dir_args = { - .force_btgt = -1, - .flags = AuWrDir_TMPFILE - }; - - /* copy-up may happen */ - mutex_lock(&dir->i_mutex); - - sb = dir->i_sb; - err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); - if (unlikely(err)) - goto out; - - err = au_di_init(dentry); - if (unlikely(err)) - goto out_si; - - err = -EBUSY; - parent = d_find_any_alias(dir); - AuDebugOn(!parent); - di_write_lock_parent(parent); - if (unlikely(d_inode(parent) != dir)) - goto out_parent; - - err = au_digen_test(parent, au_sigen(sb)); - if (unlikely(err)) - goto out_parent; - - bindex = au_dbstart(parent); - au_set_dbstart(dentry, bindex); - au_set_dbend(dentry, bindex); - err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); - bindex = err; - if (unlikely(err < 0)) - goto out_parent; - - err = -EOPNOTSUPP; - h_dir = au_h_iptr(dir, bindex); - if (unlikely(!h_dir->i_op->tmpfile)) - goto out_parent; - - h_mnt = au_sbr_mnt(sb, bindex); - err = vfsub_mnt_want_write(h_mnt); - if (unlikely(err)) - goto out_parent; - - h_parent = au_h_dptr(parent, bindex); - err = inode_permission(d_inode(h_parent), MAY_WRITE | MAY_EXEC); - if (unlikely(err)) - goto out_mnt; - - err = -ENOMEM; - h_dentry = d_alloc(h_parent, &dentry->d_name); - if (unlikely(!h_dentry)) - goto out_mnt; - - err = h_dir->i_op->tmpfile(h_dir, h_dentry, mode); - if (unlikely(err)) - goto out_dentry; - - au_set_dbstart(dentry, bindex); - au_set_dbend(dentry, bindex); - au_set_h_dptr(dentry, bindex, dget(h_dentry)); - inode = au_new_inode(dentry, /*must_new*/1); - if (IS_ERR(inode)) { - err = PTR_ERR(inode); - au_set_h_dptr(dentry, bindex, NULL); - au_set_dbstart(dentry, -1); - au_set_dbend(dentry, -1); - } else { - if (!inode->i_nlink) - set_nlink(inode, 1); - d_tmpfile(dentry, inode); - au_di(dentry)->di_tmpfile = 1; - - /* update without i_mutex */ - if (au_ibstart(dir) == au_dbstart(dentry)) - au_cpup_attr_timesizes(dir); - } - -out_dentry: - dput(h_dentry); -out_mnt: - vfsub_mnt_drop_write(h_mnt); -out_parent: - di_write_unlock(parent); - dput(parent); - di_write_unlock(dentry); - if (!err) -#if 0 - /* verbose coding for lock class name */ - au_rw_class(&au_di(dentry)->di_rwsem, - au_lc_key + AuLcNonDir_DIINFO); -#else - ; -#endif - else { - au_di_fin(dentry); - dentry->d_fsdata = NULL; - } -out_si: - si_read_unlock(sb); -out: - mutex_unlock(&dir->i_mutex); - return err; -} - -/* ---------------------------------------------------------------------- */ - -struct au_link_args { - aufs_bindex_t bdst, bsrc; - struct au_pin pin; - struct path h_path; - struct dentry *src_parent, *parent; -}; - -static int au_cpup_before_link(struct dentry *src_dentry, - struct au_link_args *a) -{ - int err; - struct dentry *h_src_dentry; - struct au_cp_generic cpg = { - .dentry = src_dentry, - .bdst = a->bdst, - .bsrc = a->bsrc, - .len = -1, - .pin = &a->pin, - .flags = AuCpup_DTIME | AuCpup_HOPEN /* | AuCpup_KEEPLINO */ - }; - - di_read_lock_parent(a->src_parent, AuLock_IR); - err = au_test_and_cpup_dirs(src_dentry, a->bdst); - if (unlikely(err)) - goto out; - - h_src_dentry = au_h_dptr(src_dentry, a->bsrc); - err = au_pin(&a->pin, src_dentry, a->bdst, - au_opt_udba(src_dentry->d_sb), - AuPin_DI_LOCKED | AuPin_MNT_WRITE); - if (unlikely(err)) - goto out; - - err = au_sio_cpup_simple(&cpg); - au_unpin(&a->pin); - -out: - di_read_unlock(a->src_parent, AuLock_IR); - return err; -} - -static int au_cpup_or_link(struct dentry *src_dentry, struct dentry *dentry, - struct au_link_args *a) -{ - int err; - unsigned char plink; - aufs_bindex_t bend; - struct dentry *h_src_dentry; - struct inode *h_inode, *inode, *delegated; - struct super_block *sb; - struct file *h_file; - - plink = 0; - h_inode = NULL; - sb = src_dentry->d_sb; - inode = d_inode(src_dentry); - if (au_ibstart(inode) <= a->bdst) - h_inode = au_h_iptr(inode, a->bdst); - if (!h_inode || !h_inode->i_nlink) { - /* copyup src_dentry as the name of dentry. */ - bend = au_dbend(dentry); - if (bend < a->bsrc) - au_set_dbend(dentry, a->bsrc); - au_set_h_dptr(dentry, a->bsrc, - dget(au_h_dptr(src_dentry, a->bsrc))); - dget(a->h_path.dentry); - au_set_h_dptr(dentry, a->bdst, NULL); - AuDbg("temporary d_inode...\n"); - spin_lock(&dentry->d_lock); - dentry->d_inode = d_inode(src_dentry); /* tmp */ - spin_unlock(&dentry->d_lock); - h_file = au_h_open_pre(dentry, a->bsrc, /*force_wr*/0); - if (IS_ERR(h_file)) - err = PTR_ERR(h_file); - else { - struct au_cp_generic cpg = { - .dentry = dentry, - .bdst = a->bdst, - .bsrc = -1, - .len = -1, - .pin = &a->pin, - .flags = AuCpup_KEEPLINO - }; - err = au_sio_cpup_simple(&cpg); - au_h_open_post(dentry, a->bsrc, h_file); - if (!err) { - dput(a->h_path.dentry); - a->h_path.dentry = au_h_dptr(dentry, a->bdst); - } else - au_set_h_dptr(dentry, a->bdst, - a->h_path.dentry); - } - spin_lock(&dentry->d_lock); - dentry->d_inode = NULL; /* restore */ - spin_unlock(&dentry->d_lock); - AuDbg("temporary d_inode...done\n"); - au_set_h_dptr(dentry, a->bsrc, NULL); - au_set_dbend(dentry, bend); - } else { - /* the inode of src_dentry already exists on a.bdst branch */ - h_src_dentry = d_find_alias(h_inode); - if (!h_src_dentry && au_plink_test(inode)) { - plink = 1; - h_src_dentry = au_plink_lkup(inode, a->bdst); - err = PTR_ERR(h_src_dentry); - if (IS_ERR(h_src_dentry)) - goto out; - - if (unlikely(d_is_negative(h_src_dentry))) { - dput(h_src_dentry); - h_src_dentry = NULL; - } - - } - if (h_src_dentry) { - delegated = NULL; - err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), - &a->h_path, &delegated); - if (unlikely(err == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal link\n"); - iput(delegated); - } - dput(h_src_dentry); - } else { - AuIOErr("no dentry found for hi%lu on b%d\n", - h_inode->i_ino, a->bdst); - err = -EIO; - } - } - - if (!err && !plink) - au_plink_append(inode, a->bdst, a->h_path.dentry); - -out: - AuTraceErr(err); - return err; -} - -int aufs_link(struct dentry *src_dentry, struct inode *dir, - struct dentry *dentry) -{ - int err, rerr; - struct au_dtime dt; - struct au_link_args *a; - struct dentry *wh_dentry, *h_src_dentry; - struct inode *inode, *delegated; - struct super_block *sb; - struct au_wr_dir_args wr_dir_args = { - /* .force_btgt = -1, */ - .flags = AuWrDir_ADD_ENTRY - }; - - IMustLock(dir); - inode = d_inode(src_dentry); - IMustLock(inode); - - err = -ENOMEM; - a = kzalloc(sizeof(*a), GFP_NOFS); - if (unlikely(!a)) - goto out; - - a->parent = dentry->d_parent; /* dir inode is locked */ - err = aufs_read_and_write_lock2(dentry, src_dentry, - AuLock_NOPLM | AuLock_GEN); - if (unlikely(err)) - goto out_kfree; - err = au_d_linkable(src_dentry); - if (unlikely(err)) - goto out_unlock; - err = au_d_may_add(dentry); - if (unlikely(err)) - goto out_unlock; - - a->src_parent = dget_parent(src_dentry); - wr_dir_args.force_btgt = au_ibstart(inode); - - di_write_lock_parent(a->parent); - wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt); - wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, &a->pin, - &wr_dir_args); - err = PTR_ERR(wh_dentry); - if (IS_ERR(wh_dentry)) - goto out_parent; - - err = 0; - sb = dentry->d_sb; - a->bdst = au_dbstart(dentry); - a->h_path.dentry = au_h_dptr(dentry, a->bdst); - a->h_path.mnt = au_sbr_mnt(sb, a->bdst); - a->bsrc = au_ibstart(inode); - h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); - if (!h_src_dentry && au_di(src_dentry)->di_tmpfile) - h_src_dentry = dget(au_hi_wh(inode, a->bsrc)); - if (!h_src_dentry) { - a->bsrc = au_dbstart(src_dentry); - h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); - AuDebugOn(!h_src_dentry); - } else if (IS_ERR(h_src_dentry)) { - err = PTR_ERR(h_src_dentry); - goto out_parent; - } - - if (au_opt_test(au_mntflags(sb), PLINK)) { - if (a->bdst < a->bsrc - /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) - err = au_cpup_or_link(src_dentry, dentry, a); - else { - delegated = NULL; - err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), - &a->h_path, &delegated); - if (unlikely(err == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal link\n"); - iput(delegated); - } - } - dput(h_src_dentry); - } else { - /* - * copyup src_dentry to the branch we process, - * and then link(2) to it. - */ - dput(h_src_dentry); - if (a->bdst < a->bsrc - /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) { - au_unpin(&a->pin); - di_write_unlock(a->parent); - err = au_cpup_before_link(src_dentry, a); - di_write_lock_parent(a->parent); - if (!err) - err = au_pin(&a->pin, dentry, a->bdst, - au_opt_udba(sb), - AuPin_DI_LOCKED | AuPin_MNT_WRITE); - if (unlikely(err)) - goto out_wh; - } - if (!err) { - h_src_dentry = au_h_dptr(src_dentry, a->bdst); - err = -ENOENT; - if (h_src_dentry && d_is_positive(h_src_dentry)) { - delegated = NULL; - err = vfsub_link(h_src_dentry, - au_pinned_h_dir(&a->pin), - &a->h_path, &delegated); - if (unlikely(err == -EWOULDBLOCK)) { - pr_warn("cannot retry" - " for NFSv4 delegation" - " for an internal link\n"); - iput(delegated); - } - } - } - } - if (unlikely(err)) - goto out_unpin; - - if (wh_dentry) { - a->h_path.dentry = wh_dentry; - err = au_wh_unlink_dentry(au_pinned_h_dir(&a->pin), &a->h_path, - dentry); - if (unlikely(err)) - goto out_revert; - } - - au_dir_ts(dir, a->bdst); - dir->i_version++; - inc_nlink(inode); - inode->i_ctime = dir->i_ctime; - d_instantiate(dentry, au_igrab(inode)); - if (d_unhashed(a->h_path.dentry)) - /* some filesystem calls d_drop() */ - d_drop(dentry); - /* some filesystems consume an inode even hardlink */ - au_fhsm_wrote(sb, a->bdst, /*force*/0); - goto out_unpin; /* success */ - -out_revert: - /* no delegation since it is just created */ - rerr = vfsub_unlink(au_pinned_h_dir(&a->pin), &a->h_path, - /*delegated*/NULL, /*force*/0); - if (unlikely(rerr)) { - AuIOErr("%pd reverting failed(%d, %d)\n", dentry, err, rerr); - err = -EIO; - } - au_dtime_revert(&dt); -out_unpin: - au_unpin(&a->pin); -out_wh: - dput(wh_dentry); -out_parent: - di_write_unlock(a->parent); - dput(a->src_parent); -out_unlock: - if (unlikely(err)) { - au_update_dbstart(dentry); - d_drop(dentry); - } - aufs_read_and_write_unlock2(dentry, src_dentry); -out_kfree: - kfree(a); -out: - AuTraceErr(err); - return err; -} - -int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) -{ - int err, rerr; - aufs_bindex_t bindex; - unsigned char diropq; - struct path h_path; - struct dentry *wh_dentry, *parent, *opq_dentry; - struct mutex *h_mtx; - struct super_block *sb; - struct { - struct au_pin pin; - struct au_dtime dt; - } *a; /* reduce the stack usage */ - struct au_wr_dir_args wr_dir_args = { - .force_btgt = -1, - .flags = AuWrDir_ADD_ENTRY | AuWrDir_ISDIR - }; - - IMustLock(dir); - - err = -ENOMEM; - a = kmalloc(sizeof(*a), GFP_NOFS); - if (unlikely(!a)) - goto out; - - err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); - if (unlikely(err)) - goto out_free; - err = au_d_may_add(dentry); - if (unlikely(err)) - goto out_unlock; - - parent = dentry->d_parent; /* dir inode is locked */ - di_write_lock_parent(parent); - wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, - &a->pin, &wr_dir_args); - err = PTR_ERR(wh_dentry); - if (IS_ERR(wh_dentry)) - goto out_parent; - - sb = dentry->d_sb; - bindex = au_dbstart(dentry); - h_path.dentry = au_h_dptr(dentry, bindex); - h_path.mnt = au_sbr_mnt(sb, bindex); - err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode); - if (unlikely(err)) - goto out_unpin; - - /* make the dir opaque */ - diropq = 0; - h_mtx = &d_inode(h_path.dentry)->i_mutex; - if (wh_dentry - || au_opt_test(au_mntflags(sb), ALWAYS_DIROPQ)) { - mutex_lock_nested(h_mtx, AuLsc_I_CHILD); - opq_dentry = au_diropq_create(dentry, bindex); - mutex_unlock(h_mtx); - err = PTR_ERR(opq_dentry); - if (IS_ERR(opq_dentry)) - goto out_dir; - dput(opq_dentry); - diropq = 1; - } - - err = epilog(dir, bindex, wh_dentry, dentry); - if (!err) { - inc_nlink(dir); - goto out_unpin; /* success */ - } - - /* revert */ - if (diropq) { - AuLabel(revert opq); - mutex_lock_nested(h_mtx, AuLsc_I_CHILD); - rerr = au_diropq_remove(dentry, bindex); - mutex_unlock(h_mtx); - if (rerr) { - AuIOErr("%pd reverting diropq failed(%d, %d)\n", - dentry, err, rerr); - err = -EIO; - } - } - -out_dir: - AuLabel(revert dir); - rerr = vfsub_rmdir(au_pinned_h_dir(&a->pin), &h_path); - if (rerr) { - AuIOErr("%pd reverting dir failed(%d, %d)\n", - dentry, err, rerr); - err = -EIO; - } - au_dtime_revert(&a->dt); -out_unpin: - au_unpin(&a->pin); - dput(wh_dentry); -out_parent: - di_write_unlock(parent); -out_unlock: - if (unlikely(err)) { - au_update_dbstart(dentry); - d_drop(dentry); - } - aufs_read_unlock(dentry, AuLock_DW); -out_free: - kfree(a); -out: - return err; -} diff --git a/fs/aufs/i_op_del.c b/fs/aufs/i_op_del.c deleted file mode 100644 index 78cf67a52..000000000 --- a/fs/aufs/i_op_del.c +++ /dev/null @@ -1,510 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * inode operations (del entry) - */ - -#include "aufs.h" - -/* - * decide if a new whiteout for @dentry is necessary or not. - * when it is necessary, prepare the parent dir for the upper branch whose - * branch index is @bcpup for creation. the actual creation of the whiteout will - * be done by caller. - * return value: - * 0: wh is unnecessary - * plus: wh is necessary - * minus: error - */ -int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup) -{ - int need_wh, err; - aufs_bindex_t bstart; - struct super_block *sb; - - sb = dentry->d_sb; - bstart = au_dbstart(dentry); - if (*bcpup < 0) { - *bcpup = bstart; - if (au_test_ro(sb, bstart, d_inode(dentry))) { - err = AuWbrCopyup(au_sbi(sb), dentry); - *bcpup = err; - if (unlikely(err < 0)) - goto out; - } - } else - AuDebugOn(bstart < *bcpup - || au_test_ro(sb, *bcpup, d_inode(dentry))); - AuDbg("bcpup %d, bstart %d\n", *bcpup, bstart); - - if (*bcpup != bstart) { - err = au_cpup_dirs(dentry, *bcpup); - if (unlikely(err)) - goto out; - need_wh = 1; - } else { - struct au_dinfo *dinfo, *tmp; - - need_wh = -ENOMEM; - dinfo = au_di(dentry); - tmp = au_di_alloc(sb, AuLsc_DI_TMP); - if (tmp) { - au_di_cp(tmp, dinfo); - au_di_swap(tmp, dinfo); - /* returns the number of positive dentries */ - need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0); - au_di_swap(tmp, dinfo); - au_rw_write_unlock(&tmp->di_rwsem); - au_di_free(tmp); - } - } - AuDbg("need_wh %d\n", need_wh); - err = need_wh; - -out: - return err; -} - -/* - * simple tests for the del-entry operations. - * following the checks in vfs, plus the parent-child relationship. - */ -int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, - struct dentry *h_parent, int isdir) -{ - int err; - umode_t h_mode; - struct dentry *h_dentry, *h_latest; - struct inode *h_inode; - - h_dentry = au_h_dptr(dentry, bindex); - if (d_really_is_positive(dentry)) { - err = -ENOENT; - if (unlikely(d_is_negative(h_dentry))) - goto out; - h_inode = d_inode(h_dentry); - if (unlikely(!h_inode->i_nlink)) - goto out; - - h_mode = h_inode->i_mode; - if (!isdir) { - err = -EISDIR; - if (unlikely(S_ISDIR(h_mode))) - goto out; - } else if (unlikely(!S_ISDIR(h_mode))) { - err = -ENOTDIR; - goto out; - } - } else { - /* rename(2) case */ - err = -EIO; - if (unlikely(d_is_positive(h_dentry))) - goto out; - } - - err = -ENOENT; - /* expected parent dir is locked */ - if (unlikely(h_parent != h_dentry->d_parent)) - goto out; - err = 0; - - /* - * rmdir a dir may break the consistency on some filesystem. - * let's try heavy test. - */ - err = -EACCES; - if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1) - && au_test_h_perm(d_inode(h_parent), - MAY_EXEC | MAY_WRITE))) - goto out; - - h_latest = au_sio_lkup_one(&dentry->d_name, h_parent); - err = -EIO; - if (IS_ERR(h_latest)) - goto out; - if (h_latest == h_dentry) - err = 0; - dput(h_latest); - -out: - return err; -} - -/* - * decide the branch where we operate for @dentry. the branch index will be set - * @rbcpup. after diciding it, 'pin' it and store the timestamps of the parent - * dir for reverting. - * when a new whiteout is necessary, create it. - */ -static struct dentry* -lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup, - struct au_dtime *dt, struct au_pin *pin) -{ - struct dentry *wh_dentry; - struct super_block *sb; - struct path h_path; - int err, need_wh; - unsigned int udba; - aufs_bindex_t bcpup; - - need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup); - wh_dentry = ERR_PTR(need_wh); - if (unlikely(need_wh < 0)) - goto out; - - sb = dentry->d_sb; - udba = au_opt_udba(sb); - bcpup = *rbcpup; - err = au_pin(pin, dentry, bcpup, udba, - AuPin_DI_LOCKED | AuPin_MNT_WRITE); - wh_dentry = ERR_PTR(err); - if (unlikely(err)) - goto out; - - h_path.dentry = au_pinned_h_parent(pin); - if (udba != AuOpt_UDBA_NONE - && au_dbstart(dentry) == bcpup) { - err = au_may_del(dentry, bcpup, h_path.dentry, isdir); - wh_dentry = ERR_PTR(err); - if (unlikely(err)) - goto out_unpin; - } - - h_path.mnt = au_sbr_mnt(sb, bcpup); - au_dtime_store(dt, au_pinned_parent(pin), &h_path); - wh_dentry = NULL; - if (!need_wh) - goto out; /* success, no need to create whiteout */ - - wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry); - if (IS_ERR(wh_dentry)) - goto out_unpin; - - /* returns with the parent is locked and wh_dentry is dget-ed */ - goto out; /* success */ - -out_unpin: - au_unpin(pin); -out: - return wh_dentry; -} - -/* - * when removing a dir, rename it to a unique temporary whiteout-ed name first - * in order to be revertible and save time for removing many child whiteouts - * under the dir. - * returns 1 when there are too many child whiteout and caller should remove - * them asynchronously. returns 0 when the number of children is enough small to - * remove now or the branch fs is a remote fs. - * otherwise return an error. - */ -static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex, - struct au_nhash *whlist, struct inode *dir) -{ - int rmdir_later, err, dirwh; - struct dentry *h_dentry; - struct super_block *sb; - struct inode *inode; - - sb = dentry->d_sb; - SiMustAnyLock(sb); - h_dentry = au_h_dptr(dentry, bindex); - err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex)); - if (unlikely(err)) - goto out; - - /* stop monitoring */ - inode = d_inode(dentry); - au_hn_free(au_hi(inode, bindex)); - - if (!au_test_fs_remote(h_dentry->d_sb)) { - dirwh = au_sbi(sb)->si_dirwh; - rmdir_later = (dirwh <= 1); - if (!rmdir_later) - rmdir_later = au_nhash_test_longer_wh(whlist, bindex, - dirwh); - if (rmdir_later) - return rmdir_later; - } - - err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist); - if (unlikely(err)) { - AuIOErr("rmdir %pd, b%d failed, %d. ignored\n", - h_dentry, bindex, err); - err = 0; - } - -out: - AuTraceErr(err); - return err; -} - -/* - * final procedure for deleting a entry. - * maintain dentry and iattr. - */ -static void epilog(struct inode *dir, struct dentry *dentry, - aufs_bindex_t bindex) -{ - struct inode *inode; - - inode = d_inode(dentry); - d_drop(dentry); - inode->i_ctime = dir->i_ctime; - - au_dir_ts(dir, bindex); - dir->i_version++; -} - -/* - * when an error happened, remove the created whiteout and revert everything. - */ -static int do_revert(int err, struct inode *dir, aufs_bindex_t bindex, - aufs_bindex_t bwh, struct dentry *wh_dentry, - struct dentry *dentry, struct au_dtime *dt) -{ - int rerr; - struct path h_path = { - .dentry = wh_dentry, - .mnt = au_sbr_mnt(dir->i_sb, bindex) - }; - - rerr = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry); - if (!rerr) { - au_set_dbwh(dentry, bwh); - au_dtime_revert(dt); - return 0; - } - - AuIOErr("%pd reverting whiteout failed(%d, %d)\n", dentry, err, rerr); - return -EIO; -} - -/* ---------------------------------------------------------------------- */ - -int aufs_unlink(struct inode *dir, struct dentry *dentry) -{ - int err; - aufs_bindex_t bwh, bindex, bstart; - struct inode *inode, *h_dir, *delegated; - struct dentry *parent, *wh_dentry; - /* to reuduce stack size */ - struct { - struct au_dtime dt; - struct au_pin pin; - struct path h_path; - } *a; - - IMustLock(dir); - - err = -ENOMEM; - a = kmalloc(sizeof(*a), GFP_NOFS); - if (unlikely(!a)) - goto out; - - err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); - if (unlikely(err)) - goto out_free; - err = au_d_hashed_positive(dentry); - if (unlikely(err)) - goto out_unlock; - inode = d_inode(dentry); - IMustLock(inode); - err = -EISDIR; - if (unlikely(d_is_dir(dentry))) - goto out_unlock; /* possible? */ - - bstart = au_dbstart(dentry); - bwh = au_dbwh(dentry); - bindex = -1; - parent = dentry->d_parent; /* dir inode is locked */ - di_write_lock_parent(parent); - wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &a->dt, - &a->pin); - err = PTR_ERR(wh_dentry); - if (IS_ERR(wh_dentry)) - goto out_parent; - - a->h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); - a->h_path.dentry = au_h_dptr(dentry, bstart); - dget(a->h_path.dentry); - if (bindex == bstart) { - h_dir = au_pinned_h_dir(&a->pin); - delegated = NULL; - err = vfsub_unlink(h_dir, &a->h_path, &delegated, /*force*/0); - if (unlikely(err == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal unlink\n"); - iput(delegated); - } - } else { - /* dir inode is locked */ - h_dir = d_inode(wh_dentry->d_parent); - IMustLock(h_dir); - err = 0; - } - - if (!err) { - vfsub_drop_nlink(inode); - epilog(dir, dentry, bindex); - - /* update target timestamps */ - if (bindex == bstart) { - vfsub_update_h_iattr(&a->h_path, /*did*/NULL); - /*ignore*/ - inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; - } else - /* todo: this timestamp may be reverted later */ - inode->i_ctime = h_dir->i_ctime; - goto out_unpin; /* success */ - } - - /* revert */ - if (wh_dentry) { - int rerr; - - rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, - &a->dt); - if (rerr) - err = rerr; - } - -out_unpin: - au_unpin(&a->pin); - dput(wh_dentry); - dput(a->h_path.dentry); -out_parent: - di_write_unlock(parent); -out_unlock: - aufs_read_unlock(dentry, AuLock_DW); -out_free: - kfree(a); -out: - return err; -} - -int aufs_rmdir(struct inode *dir, struct dentry *dentry) -{ - int err, rmdir_later; - aufs_bindex_t bwh, bindex, bstart; - struct inode *inode; - struct dentry *parent, *wh_dentry, *h_dentry; - struct au_whtmp_rmdir *args; - /* to reuduce stack size */ - struct { - struct au_dtime dt; - struct au_pin pin; - } *a; - - IMustLock(dir); - - err = -ENOMEM; - a = kmalloc(sizeof(*a), GFP_NOFS); - if (unlikely(!a)) - goto out; - - err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); - if (unlikely(err)) - goto out_free; - err = au_alive_dir(dentry); - if (unlikely(err)) - goto out_unlock; - inode = d_inode(dentry); - IMustLock(inode); - err = -ENOTDIR; - if (unlikely(!d_is_dir(dentry))) - goto out_unlock; /* possible? */ - - err = -ENOMEM; - args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS); - if (unlikely(!args)) - goto out_unlock; - - parent = dentry->d_parent; /* dir inode is locked */ - di_write_lock_parent(parent); - err = au_test_empty(dentry, &args->whlist); - if (unlikely(err)) - goto out_parent; - - bstart = au_dbstart(dentry); - bwh = au_dbwh(dentry); - bindex = -1; - wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &a->dt, - &a->pin); - err = PTR_ERR(wh_dentry); - if (IS_ERR(wh_dentry)) - goto out_parent; - - h_dentry = au_h_dptr(dentry, bstart); - dget(h_dentry); - rmdir_later = 0; - if (bindex == bstart) { - err = renwh_and_rmdir(dentry, bstart, &args->whlist, dir); - if (err > 0) { - rmdir_later = err; - err = 0; - } - } else { - /* stop monitoring */ - au_hn_free(au_hi(inode, bstart)); - - /* dir inode is locked */ - IMustLock(d_inode(wh_dentry->d_parent)); - err = 0; - } - - if (!err) { - vfsub_dead_dir(inode); - au_set_dbdiropq(dentry, -1); - epilog(dir, dentry, bindex); - - if (rmdir_later) { - au_whtmp_kick_rmdir(dir, bstart, h_dentry, args); - args = NULL; - } - - goto out_unpin; /* success */ - } - - /* revert */ - AuLabel(revert); - if (wh_dentry) { - int rerr; - - rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, - &a->dt); - if (rerr) - err = rerr; - } - -out_unpin: - au_unpin(&a->pin); - dput(wh_dentry); - dput(h_dentry); -out_parent: - di_write_unlock(parent); - if (args) - au_whtmp_rmdir_free(args); -out_unlock: - aufs_read_unlock(dentry, AuLock_DW); -out_free: - kfree(a); -out: - AuTraceErr(err); - return err; -} diff --git a/fs/aufs/i_op_ren.c b/fs/aufs/i_op_ren.c deleted file mode 100644 index 4db778057..000000000 --- a/fs/aufs/i_op_ren.c +++ /dev/null @@ -1,1017 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * inode operation (rename entry) - * todo: this is crazy monster - */ - -#include "aufs.h" - -enum { AuSRC, AuDST, AuSrcDst }; -enum { AuPARENT, AuCHILD, AuParentChild }; - -#define AuRen_ISDIR 1 -#define AuRen_ISSAMEDIR (1 << 1) -#define AuRen_WHSRC (1 << 2) -#define AuRen_WHDST (1 << 3) -#define AuRen_MNT_WRITE (1 << 4) -#define AuRen_DT_DSTDIR (1 << 5) -#define AuRen_DIROPQ (1 << 6) -#define au_ftest_ren(flags, name) ((flags) & AuRen_##name) -#define au_fset_ren(flags, name) \ - do { (flags) |= AuRen_##name; } while (0) -#define au_fclr_ren(flags, name) \ - do { (flags) &= ~AuRen_##name; } while (0) - -struct au_ren_args { - struct { - struct dentry *dentry, *h_dentry, *parent, *h_parent, - *wh_dentry; - struct inode *dir, *inode; - struct au_hinode *hdir; - struct au_dtime dt[AuParentChild]; - aufs_bindex_t bstart; - } sd[AuSrcDst]; - -#define src_dentry sd[AuSRC].dentry -#define src_dir sd[AuSRC].dir -#define src_inode sd[AuSRC].inode -#define src_h_dentry sd[AuSRC].h_dentry -#define src_parent sd[AuSRC].parent -#define src_h_parent sd[AuSRC].h_parent -#define src_wh_dentry sd[AuSRC].wh_dentry -#define src_hdir sd[AuSRC].hdir -#define src_h_dir sd[AuSRC].hdir->hi_inode -#define src_dt sd[AuSRC].dt -#define src_bstart sd[AuSRC].bstart - -#define dst_dentry sd[AuDST].dentry -#define dst_dir sd[AuDST].dir -#define dst_inode sd[AuDST].inode -#define dst_h_dentry sd[AuDST].h_dentry -#define dst_parent sd[AuDST].parent -#define dst_h_parent sd[AuDST].h_parent -#define dst_wh_dentry sd[AuDST].wh_dentry -#define dst_hdir sd[AuDST].hdir -#define dst_h_dir sd[AuDST].hdir->hi_inode -#define dst_dt sd[AuDST].dt -#define dst_bstart sd[AuDST].bstart - - struct dentry *h_trap; - struct au_branch *br; - struct au_hinode *src_hinode; - struct path h_path; - struct au_nhash whlist; - aufs_bindex_t btgt, src_bwh, src_bdiropq; - - unsigned int flags; - - struct au_whtmp_rmdir *thargs; - struct dentry *h_dst; -}; - -/* ---------------------------------------------------------------------- */ - -/* - * functions for reverting. - * when an error happened in a single rename systemcall, we should revert - * everything as if nothing happend. - * we don't need to revert the copied-up/down the parent dir since they are - * harmless. - */ - -#define RevertFailure(fmt, ...) do { \ - AuIOErr("revert failure: " fmt " (%d, %d)\n", \ - ##__VA_ARGS__, err, rerr); \ - err = -EIO; \ -} while (0) - -static void au_ren_rev_diropq(int err, struct au_ren_args *a) -{ - int rerr; - - au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); - rerr = au_diropq_remove(a->src_dentry, a->btgt); - au_hn_imtx_unlock(a->src_hinode); - au_set_dbdiropq(a->src_dentry, a->src_bdiropq); - if (rerr) - RevertFailure("remove diropq %pd", a->src_dentry); -} - -static void au_ren_rev_rename(int err, struct au_ren_args *a) -{ - int rerr; - struct inode *delegated; - - a->h_path.dentry = vfsub_lkup_one(&a->src_dentry->d_name, - a->src_h_parent); - rerr = PTR_ERR(a->h_path.dentry); - if (IS_ERR(a->h_path.dentry)) { - RevertFailure("lkup one %pd", a->src_dentry); - return; - } - - delegated = NULL; - rerr = vfsub_rename(a->dst_h_dir, - au_h_dptr(a->src_dentry, a->btgt), - a->src_h_dir, &a->h_path, &delegated); - if (unlikely(rerr == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal rename\n"); - iput(delegated); - } - d_drop(a->h_path.dentry); - dput(a->h_path.dentry); - /* au_set_h_dptr(a->src_dentry, a->btgt, NULL); */ - if (rerr) - RevertFailure("rename %pd", a->src_dentry); -} - -static void au_ren_rev_whtmp(int err, struct au_ren_args *a) -{ - int rerr; - struct inode *delegated; - - a->h_path.dentry = vfsub_lkup_one(&a->dst_dentry->d_name, - a->dst_h_parent); - rerr = PTR_ERR(a->h_path.dentry); - if (IS_ERR(a->h_path.dentry)) { - RevertFailure("lkup one %pd", a->dst_dentry); - return; - } - if (d_is_positive(a->h_path.dentry)) { - d_drop(a->h_path.dentry); - dput(a->h_path.dentry); - return; - } - - delegated = NULL; - rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path, - &delegated); - if (unlikely(rerr == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal rename\n"); - iput(delegated); - } - d_drop(a->h_path.dentry); - dput(a->h_path.dentry); - if (!rerr) - au_set_h_dptr(a->dst_dentry, a->btgt, dget(a->h_dst)); - else - RevertFailure("rename %pd", a->h_dst); -} - -static void au_ren_rev_whsrc(int err, struct au_ren_args *a) -{ - int rerr; - - a->h_path.dentry = a->src_wh_dentry; - rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry); - au_set_dbwh(a->src_dentry, a->src_bwh); - if (rerr) - RevertFailure("unlink %pd", a->src_wh_dentry); -} -#undef RevertFailure - -/* ---------------------------------------------------------------------- */ - -/* - * when we have to copyup the renaming entry, do it with the rename-target name - * in order to minimize the cost (the later actual rename is unnecessary). - * otherwise rename it on the target branch. - */ -static int au_ren_or_cpup(struct au_ren_args *a) -{ - int err; - struct dentry *d; - struct inode *delegated; - - d = a->src_dentry; - if (au_dbstart(d) == a->btgt) { - a->h_path.dentry = a->dst_h_dentry; - if (au_ftest_ren(a->flags, DIROPQ) - && au_dbdiropq(d) == a->btgt) - au_fclr_ren(a->flags, DIROPQ); - AuDebugOn(au_dbstart(d) != a->btgt); - delegated = NULL; - err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt), - a->dst_h_dir, &a->h_path, &delegated); - if (unlikely(err == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal rename\n"); - iput(delegated); - } - } else - BUG(); - - if (!err && a->h_dst) - /* it will be set to dinfo later */ - dget(a->h_dst); - - return err; -} - -/* cf. aufs_rmdir() */ -static int au_ren_del_whtmp(struct au_ren_args *a) -{ - int err; - struct inode *dir; - - dir = a->dst_dir; - SiMustAnyLock(dir->i_sb); - if (!au_nhash_test_longer_wh(&a->whlist, a->btgt, - au_sbi(dir->i_sb)->si_dirwh) - || au_test_fs_remote(a->h_dst->d_sb)) { - err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist); - if (unlikely(err)) - pr_warn("failed removing whtmp dir %pd (%d), " - "ignored.\n", a->h_dst, err); - } else { - au_nhash_wh_free(&a->thargs->whlist); - a->thargs->whlist = a->whlist; - a->whlist.nh_num = 0; - au_whtmp_kick_rmdir(dir, a->btgt, a->h_dst, a->thargs); - dput(a->h_dst); - a->thargs = NULL; - } - - return 0; -} - -/* make it 'opaque' dir. */ -static int au_ren_diropq(struct au_ren_args *a) -{ - int err; - struct dentry *diropq; - - err = 0; - a->src_bdiropq = au_dbdiropq(a->src_dentry); - a->src_hinode = au_hi(a->src_inode, a->btgt); - au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); - diropq = au_diropq_create(a->src_dentry, a->btgt); - au_hn_imtx_unlock(a->src_hinode); - if (IS_ERR(diropq)) - err = PTR_ERR(diropq); - else - dput(diropq); - - return err; -} - -static int do_rename(struct au_ren_args *a) -{ - int err; - struct dentry *d, *h_d; - - /* prepare workqueue args for asynchronous rmdir */ - h_d = a->dst_h_dentry; - if (au_ftest_ren(a->flags, ISDIR) && d_is_positive(h_d)) { - err = -ENOMEM; - a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, GFP_NOFS); - if (unlikely(!a->thargs)) - goto out; - a->h_dst = dget(h_d); - } - - /* create whiteout for src_dentry */ - if (au_ftest_ren(a->flags, WHSRC)) { - a->src_bwh = au_dbwh(a->src_dentry); - AuDebugOn(a->src_bwh >= 0); - a->src_wh_dentry - = au_wh_create(a->src_dentry, a->btgt, a->src_h_parent); - err = PTR_ERR(a->src_wh_dentry); - if (IS_ERR(a->src_wh_dentry)) - goto out_thargs; - } - - /* lookup whiteout for dentry */ - if (au_ftest_ren(a->flags, WHDST)) { - h_d = au_wh_lkup(a->dst_h_parent, &a->dst_dentry->d_name, - a->br); - err = PTR_ERR(h_d); - if (IS_ERR(h_d)) - goto out_whsrc; - if (d_is_negative(h_d)) - dput(h_d); - else - a->dst_wh_dentry = h_d; - } - - /* rename dentry to tmpwh */ - if (a->thargs) { - err = au_whtmp_ren(a->dst_h_dentry, a->br); - if (unlikely(err)) - goto out_whdst; - - d = a->dst_dentry; - au_set_h_dptr(d, a->btgt, NULL); - err = au_lkup_neg(d, a->btgt, /*wh*/0); - if (unlikely(err)) - goto out_whtmp; - a->dst_h_dentry = au_h_dptr(d, a->btgt); - } - - BUG_ON(d_is_positive(a->dst_h_dentry) && a->src_bstart != a->btgt); - - /* rename by vfs_rename or cpup */ - d = a->dst_dentry; - if (au_ftest_ren(a->flags, ISDIR) - && (a->dst_wh_dentry - || au_dbdiropq(d) == a->btgt - /* hide the lower to keep xino */ - || a->btgt < au_dbend(d) - || au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ))) - au_fset_ren(a->flags, DIROPQ); - err = au_ren_or_cpup(a); - if (unlikely(err)) - /* leave the copied-up one */ - goto out_whtmp; - - /* make dir opaque */ - if (au_ftest_ren(a->flags, DIROPQ)) { - err = au_ren_diropq(a); - if (unlikely(err)) - goto out_rename; - } - - /* update target timestamps */ - AuDebugOn(au_dbstart(a->src_dentry) != a->btgt); - a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt); - vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ - a->src_inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; - - /* remove whiteout for dentry */ - if (a->dst_wh_dentry) { - a->h_path.dentry = a->dst_wh_dentry; - err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path, - a->dst_dentry); - if (unlikely(err)) - goto out_diropq; - } - - /* remove whtmp */ - if (a->thargs) - au_ren_del_whtmp(a); /* ignore this error */ - - au_fhsm_wrote(a->src_dentry->d_sb, a->btgt, /*force*/0); - err = 0; - goto out_success; - -out_diropq: - if (au_ftest_ren(a->flags, DIROPQ)) - au_ren_rev_diropq(err, a); -out_rename: - au_ren_rev_rename(err, a); - dput(a->h_dst); -out_whtmp: - if (a->thargs) - au_ren_rev_whtmp(err, a); -out_whdst: - dput(a->dst_wh_dentry); - a->dst_wh_dentry = NULL; -out_whsrc: - if (a->src_wh_dentry) - au_ren_rev_whsrc(err, a); -out_success: - dput(a->src_wh_dentry); - dput(a->dst_wh_dentry); -out_thargs: - if (a->thargs) { - dput(a->h_dst); - au_whtmp_rmdir_free(a->thargs); - a->thargs = NULL; - } -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* - * test if @dentry dir can be rename destination or not. - * success means, it is a logically empty dir. - */ -static int may_rename_dstdir(struct dentry *dentry, struct au_nhash *whlist) -{ - return au_test_empty(dentry, whlist); -} - -/* - * test if @dentry dir can be rename source or not. - * if it can, return 0 and @children is filled. - * success means, - * - it is a logically empty dir. - * - or, it exists on writable branch and has no children including whiteouts - * on the lower branch. - */ -static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt) -{ - int err; - unsigned int rdhash; - aufs_bindex_t bstart; - - bstart = au_dbstart(dentry); - if (bstart != btgt) { - struct au_nhash whlist; - - SiMustAnyLock(dentry->d_sb); - rdhash = au_sbi(dentry->d_sb)->si_rdhash; - if (!rdhash) - rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, - dentry)); - err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); - if (unlikely(err)) - goto out; - err = au_test_empty(dentry, &whlist); - au_nhash_wh_free(&whlist); - goto out; - } - - if (bstart == au_dbtaildir(dentry)) - return 0; /* success */ - - err = au_test_empty_lower(dentry); - -out: - if (err == -ENOTEMPTY) { - AuWarn1("renaming dir who has child(ren) on multiple branches," - " is not supported\n"); - err = -EXDEV; - } - return err; -} - -/* side effect: sets whlist and h_dentry */ -static int au_ren_may_dir(struct au_ren_args *a) -{ - int err; - unsigned int rdhash; - struct dentry *d; - - d = a->dst_dentry; - SiMustAnyLock(d->d_sb); - - err = 0; - if (au_ftest_ren(a->flags, ISDIR) && a->dst_inode) { - rdhash = au_sbi(d->d_sb)->si_rdhash; - if (!rdhash) - rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, d)); - err = au_nhash_alloc(&a->whlist, rdhash, GFP_NOFS); - if (unlikely(err)) - goto out; - - au_set_dbstart(d, a->dst_bstart); - err = may_rename_dstdir(d, &a->whlist); - au_set_dbstart(d, a->btgt); - } - a->dst_h_dentry = au_h_dptr(d, au_dbstart(d)); - if (unlikely(err)) - goto out; - - d = a->src_dentry; - a->src_h_dentry = au_h_dptr(d, au_dbstart(d)); - if (au_ftest_ren(a->flags, ISDIR)) { - err = may_rename_srcdir(d, a->btgt); - if (unlikely(err)) { - au_nhash_wh_free(&a->whlist); - a->whlist.nh_num = 0; - } - } -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* - * simple tests for rename. - * following the checks in vfs, plus the parent-child relationship. - */ -static int au_may_ren(struct au_ren_args *a) -{ - int err, isdir; - struct inode *h_inode; - - if (a->src_bstart == a->btgt) { - err = au_may_del(a->src_dentry, a->btgt, a->src_h_parent, - au_ftest_ren(a->flags, ISDIR)); - if (unlikely(err)) - goto out; - err = -EINVAL; - if (unlikely(a->src_h_dentry == a->h_trap)) - goto out; - } - - err = 0; - if (a->dst_bstart != a->btgt) - goto out; - - err = -ENOTEMPTY; - if (unlikely(a->dst_h_dentry == a->h_trap)) - goto out; - - err = -EIO; - isdir = !!au_ftest_ren(a->flags, ISDIR); - if (d_really_is_negative(a->dst_dentry)) { - if (d_is_negative(a->dst_h_dentry)) - err = au_may_add(a->dst_dentry, a->btgt, - a->dst_h_parent, isdir); - } else { - if (unlikely(d_is_negative(a->dst_h_dentry))) - goto out; - h_inode = d_inode(a->dst_h_dentry); - if (h_inode->i_nlink) - err = au_may_del(a->dst_dentry, a->btgt, - a->dst_h_parent, isdir); - } - -out: - if (unlikely(err == -ENOENT || err == -EEXIST)) - err = -EIO; - AuTraceErr(err); - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* - * locking order - * (VFS) - * - src_dir and dir by lock_rename() - * - inode if exitsts - * (aufs) - * - lock all - * + src_dentry and dentry by aufs_read_and_write_lock2() which calls, - * + si_read_lock - * + di_write_lock2_child() - * + di_write_lock_child() - * + ii_write_lock_child() - * + di_write_lock_child2() - * + ii_write_lock_child2() - * + src_parent and parent - * + di_write_lock_parent() - * + ii_write_lock_parent() - * + di_write_lock_parent2() - * + ii_write_lock_parent2() - * + lower src_dir and dir by vfsub_lock_rename() - * + verify the every relationships between child and parent. if any - * of them failed, unlock all and return -EBUSY. - */ -static void au_ren_unlock(struct au_ren_args *a) -{ - vfsub_unlock_rename(a->src_h_parent, a->src_hdir, - a->dst_h_parent, a->dst_hdir); - if (au_ftest_ren(a->flags, MNT_WRITE)) - vfsub_mnt_drop_write(au_br_mnt(a->br)); -} - -static int au_ren_lock(struct au_ren_args *a) -{ - int err; - unsigned int udba; - - err = 0; - a->src_h_parent = au_h_dptr(a->src_parent, a->btgt); - a->src_hdir = au_hi(a->src_dir, a->btgt); - a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt); - a->dst_hdir = au_hi(a->dst_dir, a->btgt); - - err = vfsub_mnt_want_write(au_br_mnt(a->br)); - if (unlikely(err)) - goto out; - au_fset_ren(a->flags, MNT_WRITE); - a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir, - a->dst_h_parent, a->dst_hdir); - udba = au_opt_udba(a->src_dentry->d_sb); - if (unlikely(a->src_hdir->hi_inode != d_inode(a->src_h_parent) - || a->dst_hdir->hi_inode != d_inode(a->dst_h_parent))) - err = au_busy_or_stale(); - if (!err && au_dbstart(a->src_dentry) == a->btgt) - err = au_h_verify(a->src_h_dentry, udba, - d_inode(a->src_h_parent), a->src_h_parent, - a->br); - if (!err && au_dbstart(a->dst_dentry) == a->btgt) - err = au_h_verify(a->dst_h_dentry, udba, - d_inode(a->dst_h_parent), a->dst_h_parent, - a->br); - if (!err) - goto out; /* success */ - - err = au_busy_or_stale(); - au_ren_unlock(a); - -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -static void au_ren_refresh_dir(struct au_ren_args *a) -{ - struct inode *dir; - - dir = a->dst_dir; - dir->i_version++; - if (au_ftest_ren(a->flags, ISDIR)) { - /* is this updating defined in POSIX? */ - au_cpup_attr_timesizes(a->src_inode); - au_cpup_attr_nlink(dir, /*force*/1); - } - - au_dir_ts(dir, a->btgt); - - if (au_ftest_ren(a->flags, ISSAMEDIR)) - return; - - dir = a->src_dir; - dir->i_version++; - if (au_ftest_ren(a->flags, ISDIR)) - au_cpup_attr_nlink(dir, /*force*/1); - au_dir_ts(dir, a->btgt); -} - -static void au_ren_refresh(struct au_ren_args *a) -{ - aufs_bindex_t bend, bindex; - struct dentry *d, *h_d; - struct inode *i, *h_i; - struct super_block *sb; - - d = a->dst_dentry; - d_drop(d); - if (a->h_dst) - /* already dget-ed by au_ren_or_cpup() */ - au_set_h_dptr(d, a->btgt, a->h_dst); - - i = a->dst_inode; - if (i) { - if (!au_ftest_ren(a->flags, ISDIR)) - vfsub_drop_nlink(i); - else { - vfsub_dead_dir(i); - au_cpup_attr_timesizes(i); - } - au_update_dbrange(d, /*do_put_zero*/1); - } else { - bend = a->btgt; - for (bindex = au_dbstart(d); bindex < bend; bindex++) - au_set_h_dptr(d, bindex, NULL); - bend = au_dbend(d); - for (bindex = a->btgt + 1; bindex <= bend; bindex++) - au_set_h_dptr(d, bindex, NULL); - au_update_dbrange(d, /*do_put_zero*/0); - } - - d = a->src_dentry; - au_set_dbwh(d, -1); - bend = au_dbend(d); - for (bindex = a->btgt + 1; bindex <= bend; bindex++) { - h_d = au_h_dptr(d, bindex); - if (h_d) - au_set_h_dptr(d, bindex, NULL); - } - au_set_dbend(d, a->btgt); - - sb = d->d_sb; - i = a->src_inode; - if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i)) - return; /* success */ - - bend = au_ibend(i); - for (bindex = a->btgt + 1; bindex <= bend; bindex++) { - h_i = au_h_iptr(i, bindex); - if (h_i) { - au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0); - /* ignore this error */ - au_set_h_iptr(i, bindex, NULL, 0); - } - } - au_set_ibend(i, a->btgt); -} - -/* ---------------------------------------------------------------------- */ - -/* mainly for link(2) and rename(2) */ -int au_wbr(struct dentry *dentry, aufs_bindex_t btgt) -{ - aufs_bindex_t bdiropq, bwh; - struct dentry *parent; - struct au_branch *br; - - parent = dentry->d_parent; - IMustLock(d_inode(parent)); /* dir is locked */ - - bdiropq = au_dbdiropq(parent); - bwh = au_dbwh(dentry); - br = au_sbr(dentry->d_sb, btgt); - if (au_br_rdonly(br) - || (0 <= bdiropq && bdiropq < btgt) - || (0 <= bwh && bwh < btgt)) - btgt = -1; - - AuDbg("btgt %d\n", btgt); - return btgt; -} - -/* sets src_bstart, dst_bstart and btgt */ -static int au_ren_wbr(struct au_ren_args *a) -{ - int err; - struct au_wr_dir_args wr_dir_args = { - /* .force_btgt = -1, */ - .flags = AuWrDir_ADD_ENTRY - }; - - a->src_bstart = au_dbstart(a->src_dentry); - a->dst_bstart = au_dbstart(a->dst_dentry); - if (au_ftest_ren(a->flags, ISDIR)) - au_fset_wrdir(wr_dir_args.flags, ISDIR); - wr_dir_args.force_btgt = a->src_bstart; - if (a->dst_inode && a->dst_bstart < a->src_bstart) - wr_dir_args.force_btgt = a->dst_bstart; - wr_dir_args.force_btgt = au_wbr(a->dst_dentry, wr_dir_args.force_btgt); - err = au_wr_dir(a->dst_dentry, a->src_dentry, &wr_dir_args); - a->btgt = err; - - return err; -} - -static void au_ren_dt(struct au_ren_args *a) -{ - a->h_path.dentry = a->src_h_parent; - au_dtime_store(a->src_dt + AuPARENT, a->src_parent, &a->h_path); - if (!au_ftest_ren(a->flags, ISSAMEDIR)) { - a->h_path.dentry = a->dst_h_parent; - au_dtime_store(a->dst_dt + AuPARENT, a->dst_parent, &a->h_path); - } - - au_fclr_ren(a->flags, DT_DSTDIR); - if (!au_ftest_ren(a->flags, ISDIR)) - return; - - a->h_path.dentry = a->src_h_dentry; - au_dtime_store(a->src_dt + AuCHILD, a->src_dentry, &a->h_path); - if (d_is_positive(a->dst_h_dentry)) { - au_fset_ren(a->flags, DT_DSTDIR); - a->h_path.dentry = a->dst_h_dentry; - au_dtime_store(a->dst_dt + AuCHILD, a->dst_dentry, &a->h_path); - } -} - -static void au_ren_rev_dt(int err, struct au_ren_args *a) -{ - struct dentry *h_d; - struct mutex *h_mtx; - - au_dtime_revert(a->src_dt + AuPARENT); - if (!au_ftest_ren(a->flags, ISSAMEDIR)) - au_dtime_revert(a->dst_dt + AuPARENT); - - if (au_ftest_ren(a->flags, ISDIR) && err != -EIO) { - h_d = a->src_dt[AuCHILD].dt_h_path.dentry; - h_mtx = &d_inode(h_d)->i_mutex; - mutex_lock_nested(h_mtx, AuLsc_I_CHILD); - au_dtime_revert(a->src_dt + AuCHILD); - mutex_unlock(h_mtx); - - if (au_ftest_ren(a->flags, DT_DSTDIR)) { - h_d = a->dst_dt[AuCHILD].dt_h_path.dentry; - h_mtx = &d_inode(h_d)->i_mutex; - mutex_lock_nested(h_mtx, AuLsc_I_CHILD); - au_dtime_revert(a->dst_dt + AuCHILD); - mutex_unlock(h_mtx); - } - } -} - -/* ---------------------------------------------------------------------- */ - -int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, - struct inode *_dst_dir, struct dentry *_dst_dentry) -{ - int err, flags; - /* reduce stack space */ - struct au_ren_args *a; - - AuDbg("%pd, %pd\n", _src_dentry, _dst_dentry); - IMustLock(_src_dir); - IMustLock(_dst_dir); - - err = -ENOMEM; - BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE); - a = kzalloc(sizeof(*a), GFP_NOFS); - if (unlikely(!a)) - goto out; - - a->src_dir = _src_dir; - a->src_dentry = _src_dentry; - a->src_inode = NULL; - if (d_really_is_positive(a->src_dentry)) - a->src_inode = d_inode(a->src_dentry); - a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */ - a->dst_dir = _dst_dir; - a->dst_dentry = _dst_dentry; - a->dst_inode = NULL; - if (d_really_is_positive(a->dst_dentry)) - a->dst_inode = d_inode(a->dst_dentry); - a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */ - if (a->dst_inode) { - IMustLock(a->dst_inode); - au_igrab(a->dst_inode); - } - - err = -ENOTDIR; - flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN; - if (d_is_dir(a->src_dentry)) { - au_fset_ren(a->flags, ISDIR); - if (unlikely(d_really_is_positive(a->dst_dentry) - && !d_is_dir(a->dst_dentry))) - goto out_free; - err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, - AuLock_DIR | flags); - } else - err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, - flags); - if (unlikely(err)) - goto out_free; - - err = au_d_hashed_positive(a->src_dentry); - if (unlikely(err)) - goto out_unlock; - err = -ENOENT; - if (a->dst_inode) { - /* - * If it is a dir, VFS unhash dst_dentry before this - * function. It means we cannot rely upon d_unhashed(). - */ - if (unlikely(!a->dst_inode->i_nlink)) - goto out_unlock; - if (!S_ISDIR(a->dst_inode->i_mode)) { - err = au_d_hashed_positive(a->dst_dentry); - if (unlikely(err)) - goto out_unlock; - } else if (unlikely(IS_DEADDIR(a->dst_inode))) - goto out_unlock; - } else if (unlikely(d_unhashed(a->dst_dentry))) - goto out_unlock; - - /* - * is it possible? - * yes, it happend (in linux-3.3-rcN) but I don't know why. - * there may exist a problem somewhere else. - */ - err = -EINVAL; - if (unlikely(d_inode(a->dst_parent) == d_inode(a->src_dentry))) - goto out_unlock; - - au_fset_ren(a->flags, ISSAMEDIR); /* temporary */ - di_write_lock_parent(a->dst_parent); - - /* which branch we process */ - err = au_ren_wbr(a); - if (unlikely(err < 0)) - goto out_parent; - a->br = au_sbr(a->dst_dentry->d_sb, a->btgt); - a->h_path.mnt = au_br_mnt(a->br); - - /* are they available to be renamed */ - err = au_ren_may_dir(a); - if (unlikely(err)) - goto out_children; - - /* prepare the writable parent dir on the same branch */ - if (a->dst_bstart == a->btgt) { - au_fset_ren(a->flags, WHDST); - } else { - err = au_cpup_dirs(a->dst_dentry, a->btgt); - if (unlikely(err)) - goto out_children; - } - - if (a->src_dir != a->dst_dir) { - /* - * this temporary unlock is safe, - * because both dir->i_mutex are locked. - */ - di_write_unlock(a->dst_parent); - di_write_lock_parent(a->src_parent); - err = au_wr_dir_need_wh(a->src_dentry, - au_ftest_ren(a->flags, ISDIR), - &a->btgt); - di_write_unlock(a->src_parent); - di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1); - au_fclr_ren(a->flags, ISSAMEDIR); - } else - err = au_wr_dir_need_wh(a->src_dentry, - au_ftest_ren(a->flags, ISDIR), - &a->btgt); - if (unlikely(err < 0)) - goto out_children; - if (err) - au_fset_ren(a->flags, WHSRC); - - /* cpup src */ - if (a->src_bstart != a->btgt) { - struct au_pin pin; - - err = au_pin(&pin, a->src_dentry, a->btgt, - au_opt_udba(a->src_dentry->d_sb), - AuPin_DI_LOCKED | AuPin_MNT_WRITE); - if (!err) { - struct au_cp_generic cpg = { - .dentry = a->src_dentry, - .bdst = a->btgt, - .bsrc = a->src_bstart, - .len = -1, - .pin = &pin, - .flags = AuCpup_DTIME | AuCpup_HOPEN - }; - AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart); - err = au_sio_cpup_simple(&cpg); - au_unpin(&pin); - } - if (unlikely(err)) - goto out_children; - a->src_bstart = a->btgt; - a->src_h_dentry = au_h_dptr(a->src_dentry, a->btgt); - au_fset_ren(a->flags, WHSRC); - } - - /* lock them all */ - err = au_ren_lock(a); - if (unlikely(err)) - /* leave the copied-up one */ - goto out_children; - - if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) - err = au_may_ren(a); - else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN)) - err = -ENAMETOOLONG; - if (unlikely(err)) - goto out_hdir; - - /* store timestamps to be revertible */ - au_ren_dt(a); - - /* here we go */ - err = do_rename(a); - if (unlikely(err)) - goto out_dt; - - /* update dir attributes */ - au_ren_refresh_dir(a); - - /* dput/iput all lower dentries */ - au_ren_refresh(a); - - goto out_hdir; /* success */ - -out_dt: - au_ren_rev_dt(err, a); -out_hdir: - au_ren_unlock(a); -out_children: - au_nhash_wh_free(&a->whlist); - if (err && a->dst_inode && a->dst_bstart != a->btgt) { - AuDbg("bstart %d, btgt %d\n", a->dst_bstart, a->btgt); - au_set_h_dptr(a->dst_dentry, a->btgt, NULL); - au_set_dbstart(a->dst_dentry, a->dst_bstart); - } -out_parent: - if (!err) - d_move(a->src_dentry, a->dst_dentry); - else { - au_update_dbstart(a->dst_dentry); - if (!a->dst_inode) - d_drop(a->dst_dentry); - } - if (au_ftest_ren(a->flags, ISSAMEDIR)) - di_write_unlock(a->dst_parent); - else - di_write_unlock2(a->src_parent, a->dst_parent); -out_unlock: - aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry); -out_free: - iput(a->dst_inode); - if (a->thargs) - au_whtmp_rmdir_free(a->thargs); - kfree(a); -out: - AuTraceErr(err); - return err; -} diff --git a/fs/aufs/iinfo.c b/fs/aufs/iinfo.c deleted file mode 100644 index 0eaa7de7e..000000000 --- a/fs/aufs/iinfo.c +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * inode private data - */ - -#include "aufs.h" - -struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex) -{ - struct inode *h_inode; - - IiMustAnyLock(inode); - - h_inode = au_ii(inode)->ii_hinode[0 + bindex].hi_inode; - AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); - return h_inode; -} - -/* todo: hard/soft set? */ -void au_hiput(struct au_hinode *hinode) -{ - au_hn_free(hinode); - dput(hinode->hi_whdentry); - iput(hinode->hi_inode); -} - -unsigned int au_hi_flags(struct inode *inode, int isdir) -{ - unsigned int flags; - const unsigned int mnt_flags = au_mntflags(inode->i_sb); - - flags = 0; - if (au_opt_test(mnt_flags, XINO)) - au_fset_hi(flags, XINO); - if (isdir && au_opt_test(mnt_flags, UDBA_HNOTIFY)) - au_fset_hi(flags, HNOTIFY); - return flags; -} - -void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, - struct inode *h_inode, unsigned int flags) -{ - struct au_hinode *hinode; - struct inode *hi; - struct au_iinfo *iinfo = au_ii(inode); - - IiMustWriteLock(inode); - - hinode = iinfo->ii_hinode + bindex; - hi = hinode->hi_inode; - AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); - - if (hi) - au_hiput(hinode); - hinode->hi_inode = h_inode; - if (h_inode) { - int err; - struct super_block *sb = inode->i_sb; - struct au_branch *br; - - AuDebugOn(inode->i_mode - && (h_inode->i_mode & S_IFMT) - != (inode->i_mode & S_IFMT)); - if (bindex == iinfo->ii_bstart) - au_cpup_igen(inode, h_inode); - br = au_sbr(sb, bindex); - hinode->hi_id = br->br_id; - if (au_ftest_hi(flags, XINO)) { - err = au_xino_write(sb, bindex, h_inode->i_ino, - inode->i_ino); - if (unlikely(err)) - AuIOErr1("failed au_xino_write() %d\n", err); - } - - if (au_ftest_hi(flags, HNOTIFY) - && au_br_hnotifyable(br->br_perm)) { - err = au_hn_alloc(hinode, inode); - if (unlikely(err)) - AuIOErr1("au_hn_alloc() %d\n", err); - } - } -} - -void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, - struct dentry *h_wh) -{ - struct au_hinode *hinode; - - IiMustWriteLock(inode); - - hinode = au_ii(inode)->ii_hinode + bindex; - AuDebugOn(hinode->hi_whdentry); - hinode->hi_whdentry = h_wh; -} - -void au_update_iigen(struct inode *inode, int half) -{ - struct au_iinfo *iinfo; - struct au_iigen *iigen; - unsigned int sigen; - - sigen = au_sigen(inode->i_sb); - iinfo = au_ii(inode); - iigen = &iinfo->ii_generation; - spin_lock(&iinfo->ii_genspin); - iigen->ig_generation = sigen; - if (half) - au_ig_fset(iigen->ig_flags, HALF_REFRESHED); - else - au_ig_fclr(iigen->ig_flags, HALF_REFRESHED); - spin_unlock(&iinfo->ii_genspin); -} - -/* it may be called at remount time, too */ -void au_update_ibrange(struct inode *inode, int do_put_zero) -{ - struct au_iinfo *iinfo; - aufs_bindex_t bindex, bend; - - iinfo = au_ii(inode); - if (!iinfo) - return; - - IiMustWriteLock(inode); - - if (do_put_zero && iinfo->ii_bstart >= 0) { - for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; - bindex++) { - struct inode *h_i; - - h_i = iinfo->ii_hinode[0 + bindex].hi_inode; - if (h_i - && !h_i->i_nlink - && !(h_i->i_state & I_LINKABLE)) - au_set_h_iptr(inode, bindex, NULL, 0); - } - } - - iinfo->ii_bstart = -1; - iinfo->ii_bend = -1; - bend = au_sbend(inode->i_sb); - for (bindex = 0; bindex <= bend; bindex++) - if (iinfo->ii_hinode[0 + bindex].hi_inode) { - iinfo->ii_bstart = bindex; - break; - } - if (iinfo->ii_bstart >= 0) - for (bindex = bend; bindex >= iinfo->ii_bstart; bindex--) - if (iinfo->ii_hinode[0 + bindex].hi_inode) { - iinfo->ii_bend = bindex; - break; - } - AuDebugOn(iinfo->ii_bstart > iinfo->ii_bend); -} - -/* ---------------------------------------------------------------------- */ - -void au_icntnr_init_once(void *_c) -{ - struct au_icntnr *c = _c; - struct au_iinfo *iinfo = &c->iinfo; - static struct lock_class_key aufs_ii; - - spin_lock_init(&iinfo->ii_genspin); - au_rw_init(&iinfo->ii_rwsem); - au_rw_class(&iinfo->ii_rwsem, &aufs_ii); - inode_init_once(&c->vfs_inode); -} - -int au_iinfo_init(struct inode *inode) -{ - struct au_iinfo *iinfo; - struct super_block *sb; - int nbr, i; - - sb = inode->i_sb; - iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); - nbr = au_sbend(sb) + 1; - if (unlikely(nbr <= 0)) - nbr = 1; - iinfo->ii_hinode = kcalloc(nbr, sizeof(*iinfo->ii_hinode), GFP_NOFS); - if (iinfo->ii_hinode) { - au_ninodes_inc(sb); - for (i = 0; i < nbr; i++) - iinfo->ii_hinode[i].hi_id = -1; - - iinfo->ii_generation.ig_generation = au_sigen(sb); - iinfo->ii_bstart = -1; - iinfo->ii_bend = -1; - iinfo->ii_vdir = NULL; - return 0; - } - return -ENOMEM; -} - -int au_ii_realloc(struct au_iinfo *iinfo, int nbr) -{ - int err, sz; - struct au_hinode *hip; - - AuRwMustWriteLock(&iinfo->ii_rwsem); - - err = -ENOMEM; - sz = sizeof(*hip) * (iinfo->ii_bend + 1); - if (!sz) - sz = sizeof(*hip); - hip = au_kzrealloc(iinfo->ii_hinode, sz, sizeof(*hip) * nbr, GFP_NOFS); - if (hip) { - iinfo->ii_hinode = hip; - err = 0; - } - - return err; -} - -void au_iinfo_fin(struct inode *inode) -{ - struct au_iinfo *iinfo; - struct au_hinode *hi; - struct super_block *sb; - aufs_bindex_t bindex, bend; - const unsigned char unlinked = !inode->i_nlink; - - iinfo = au_ii(inode); - /* bad_inode case */ - if (!iinfo) - return; - - sb = inode->i_sb; - au_ninodes_dec(sb); - if (si_pid_test(sb)) - au_xino_delete_inode(inode, unlinked); - else { - /* - * it is safe to hide the dependency between sbinfo and - * sb->s_umount. - */ - lockdep_off(); - si_noflush_read_lock(sb); - au_xino_delete_inode(inode, unlinked); - si_read_unlock(sb); - lockdep_on(); - } - - if (iinfo->ii_vdir) - au_vdir_free(iinfo->ii_vdir); - - bindex = iinfo->ii_bstart; - if (bindex >= 0) { - hi = iinfo->ii_hinode + bindex; - bend = iinfo->ii_bend; - while (bindex++ <= bend) { - if (hi->hi_inode) - au_hiput(hi); - hi++; - } - } - kfree(iinfo->ii_hinode); - iinfo->ii_hinode = NULL; - AuRwDestroy(&iinfo->ii_rwsem); -} diff --git a/fs/aufs/inode.c b/fs/aufs/inode.c deleted file mode 100644 index 201e112a0..000000000 --- a/fs/aufs/inode.c +++ /dev/null @@ -1,500 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * inode functions - */ - -#include "aufs.h" - -struct inode *au_igrab(struct inode *inode) -{ - if (inode) { - AuDebugOn(!atomic_read(&inode->i_count)); - ihold(inode); - } - return inode; -} - -static void au_refresh_hinode_attr(struct inode *inode, int do_version) -{ - au_cpup_attr_all(inode, /*force*/0); - au_update_iigen(inode, /*half*/1); - if (do_version) - inode->i_version++; -} - -static int au_ii_refresh(struct inode *inode, int *update) -{ - int err, e; - umode_t type; - aufs_bindex_t bindex, new_bindex; - struct super_block *sb; - struct au_iinfo *iinfo; - struct au_hinode *p, *q, tmp; - - IiMustWriteLock(inode); - - *update = 0; - sb = inode->i_sb; - type = inode->i_mode & S_IFMT; - iinfo = au_ii(inode); - err = au_ii_realloc(iinfo, au_sbend(sb) + 1); - if (unlikely(err)) - goto out; - - AuDebugOn(iinfo->ii_bstart < 0); - p = iinfo->ii_hinode + iinfo->ii_bstart; - for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; - bindex++, p++) { - if (!p->hi_inode) - continue; - - AuDebugOn(type != (p->hi_inode->i_mode & S_IFMT)); - new_bindex = au_br_index(sb, p->hi_id); - if (new_bindex == bindex) - continue; - - if (new_bindex < 0) { - *update = 1; - au_hiput(p); - p->hi_inode = NULL; - continue; - } - - if (new_bindex < iinfo->ii_bstart) - iinfo->ii_bstart = new_bindex; - if (iinfo->ii_bend < new_bindex) - iinfo->ii_bend = new_bindex; - /* swap two lower inode, and loop again */ - q = iinfo->ii_hinode + new_bindex; - tmp = *q; - *q = *p; - *p = tmp; - if (tmp.hi_inode) { - bindex--; - p--; - } - } - au_update_ibrange(inode, /*do_put_zero*/0); - e = au_dy_irefresh(inode); - if (unlikely(e && !err)) - err = e; - -out: - AuTraceErr(err); - return err; -} - -int au_refresh_hinode_self(struct inode *inode) -{ - int err, update; - - err = au_ii_refresh(inode, &update); - if (!err) - au_refresh_hinode_attr(inode, update && S_ISDIR(inode->i_mode)); - - AuTraceErr(err); - return err; -} - -int au_refresh_hinode(struct inode *inode, struct dentry *dentry) -{ - int err, e, update; - unsigned int flags; - umode_t mode; - aufs_bindex_t bindex, bend; - unsigned char isdir; - struct au_hinode *p; - struct au_iinfo *iinfo; - - err = au_ii_refresh(inode, &update); - if (unlikely(err)) - goto out; - - update = 0; - iinfo = au_ii(inode); - p = iinfo->ii_hinode + iinfo->ii_bstart; - mode = (inode->i_mode & S_IFMT); - isdir = S_ISDIR(mode); - flags = au_hi_flags(inode, isdir); - bend = au_dbend(dentry); - for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { - struct inode *h_i, *h_inode; - struct dentry *h_d; - - h_d = au_h_dptr(dentry, bindex); - if (!h_d || d_is_negative(h_d)) - continue; - - h_inode = d_inode(h_d); - AuDebugOn(mode != (h_inode->i_mode & S_IFMT)); - if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) { - h_i = au_h_iptr(inode, bindex); - if (h_i) { - if (h_i == h_inode) - continue; - err = -EIO; - break; - } - } - if (bindex < iinfo->ii_bstart) - iinfo->ii_bstart = bindex; - if (iinfo->ii_bend < bindex) - iinfo->ii_bend = bindex; - au_set_h_iptr(inode, bindex, au_igrab(h_inode), flags); - update = 1; - } - au_update_ibrange(inode, /*do_put_zero*/0); - e = au_dy_irefresh(inode); - if (unlikely(e && !err)) - err = e; - if (!err) - au_refresh_hinode_attr(inode, update && isdir); - -out: - AuTraceErr(err); - return err; -} - -static int set_inode(struct inode *inode, struct dentry *dentry) -{ - int err; - unsigned int flags; - umode_t mode; - aufs_bindex_t bindex, bstart, btail; - unsigned char isdir; - struct dentry *h_dentry; - struct inode *h_inode; - struct au_iinfo *iinfo; - - IiMustWriteLock(inode); - - err = 0; - isdir = 0; - bstart = au_dbstart(dentry); - h_dentry = au_h_dptr(dentry, bstart); - h_inode = d_inode(h_dentry); - mode = h_inode->i_mode; - switch (mode & S_IFMT) { - case S_IFREG: - btail = au_dbtail(dentry); - inode->i_op = &aufs_iop; - inode->i_fop = &aufs_file_fop; - err = au_dy_iaop(inode, bstart, h_inode); - if (unlikely(err)) - goto out; - break; - case S_IFDIR: - isdir = 1; - btail = au_dbtaildir(dentry); - inode->i_op = &aufs_dir_iop; - inode->i_fop = &aufs_dir_fop; - break; - case S_IFLNK: - btail = au_dbtail(dentry); - inode->i_op = &aufs_symlink_iop; - break; - case S_IFBLK: - case S_IFCHR: - case S_IFIFO: - case S_IFSOCK: - btail = au_dbtail(dentry); - inode->i_op = &aufs_iop; - init_special_inode(inode, mode, h_inode->i_rdev); - break; - default: - AuIOErr("Unknown file type 0%o\n", mode); - err = -EIO; - goto out; - } - - /* do not set hnotify for whiteouted dirs (SHWH mode) */ - flags = au_hi_flags(inode, isdir); - if (au_opt_test(au_mntflags(dentry->d_sb), SHWH) - && au_ftest_hi(flags, HNOTIFY) - && dentry->d_name.len > AUFS_WH_PFX_LEN - && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) - au_fclr_hi(flags, HNOTIFY); - iinfo = au_ii(inode); - iinfo->ii_bstart = bstart; - iinfo->ii_bend = btail; - for (bindex = bstart; bindex <= btail; bindex++) { - h_dentry = au_h_dptr(dentry, bindex); - if (h_dentry) - au_set_h_iptr(inode, bindex, - au_igrab(d_inode(h_dentry)), flags); - } - au_cpup_attr_all(inode, /*force*/1); - /* - * to force calling aufs_get_acl() every time, - * do not call cache_no_acl() for aufs inode. - */ - -out: - return err; -} - -/* - * successful returns with iinfo write_locked - * minus: errno - * zero: success, matched - * plus: no error, but unmatched - */ -static int reval_inode(struct inode *inode, struct dentry *dentry) -{ - int err; - unsigned int gen; - struct au_iigen iigen; - aufs_bindex_t bindex, bend; - struct inode *h_inode, *h_dinode; - struct dentry *h_dentry; - - /* - * before this function, if aufs got any iinfo lock, it must be only - * one, the parent dir. - * it can happen by UDBA and the obsoleted inode number. - */ - err = -EIO; - if (unlikely(inode->i_ino == parent_ino(dentry))) - goto out; - - err = 1; - ii_write_lock_new_child(inode); - h_dentry = au_h_dptr(dentry, au_dbstart(dentry)); - h_dinode = d_inode(h_dentry); - bend = au_ibend(inode); - for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { - h_inode = au_h_iptr(inode, bindex); - if (!h_inode || h_inode != h_dinode) - continue; - - err = 0; - gen = au_iigen(inode, &iigen); - if (gen == au_digen(dentry) - && !au_ig_ftest(iigen.ig_flags, HALF_REFRESHED)) - break; - - /* fully refresh inode using dentry */ - err = au_refresh_hinode(inode, dentry); - if (!err) - au_update_iigen(inode, /*half*/0); - break; - } - - if (unlikely(err)) - ii_write_unlock(inode); -out: - return err; -} - -int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, - unsigned int d_type, ino_t *ino) -{ - int err; - struct mutex *mtx; - - /* prevent hardlinked inode number from race condition */ - mtx = NULL; - if (d_type != DT_DIR) { - mtx = &au_sbr(sb, bindex)->br_xino.xi_nondir_mtx; - mutex_lock(mtx); - } - err = au_xino_read(sb, bindex, h_ino, ino); - if (unlikely(err)) - goto out; - - if (!*ino) { - err = -EIO; - *ino = au_xino_new_ino(sb); - if (unlikely(!*ino)) - goto out; - err = au_xino_write(sb, bindex, h_ino, *ino); - if (unlikely(err)) - goto out; - } - -out: - if (mtx) - mutex_unlock(mtx); - return err; -} - -/* successful returns with iinfo write_locked */ -/* todo: return with unlocked? */ -struct inode *au_new_inode(struct dentry *dentry, int must_new) -{ - struct inode *inode, *h_inode; - struct dentry *h_dentry; - struct super_block *sb; - struct mutex *mtx; - ino_t h_ino, ino; - int err; - aufs_bindex_t bstart; - - sb = dentry->d_sb; - bstart = au_dbstart(dentry); - h_dentry = au_h_dptr(dentry, bstart); - h_inode = d_inode(h_dentry); - h_ino = h_inode->i_ino; - - /* - * stop 'race'-ing between hardlinks under different - * parents. - */ - mtx = NULL; - if (!d_is_dir(h_dentry)) - mtx = &au_sbr(sb, bstart)->br_xino.xi_nondir_mtx; - -new_ino: - if (mtx) - mutex_lock(mtx); - err = au_xino_read(sb, bstart, h_ino, &ino); - inode = ERR_PTR(err); - if (unlikely(err)) - goto out; - - if (!ino) { - ino = au_xino_new_ino(sb); - if (unlikely(!ino)) { - inode = ERR_PTR(-EIO); - goto out; - } - } - - AuDbg("i%lu\n", (unsigned long)ino); - inode = au_iget_locked(sb, ino); - err = PTR_ERR(inode); - if (IS_ERR(inode)) - goto out; - - AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW)); - if (inode->i_state & I_NEW) { - /* verbose coding for lock class name */ - if (unlikely(d_is_symlink(h_dentry))) - au_rw_class(&au_ii(inode)->ii_rwsem, - au_lc_key + AuLcSymlink_IIINFO); - else if (unlikely(d_is_dir(h_dentry))) - au_rw_class(&au_ii(inode)->ii_rwsem, - au_lc_key + AuLcDir_IIINFO); - else /* likely */ - au_rw_class(&au_ii(inode)->ii_rwsem, - au_lc_key + AuLcNonDir_IIINFO); - - ii_write_lock_new_child(inode); - err = set_inode(inode, dentry); - if (!err) { - unlock_new_inode(inode); - goto out; /* success */ - } - - /* - * iget_failed() calls iput(), but we need to call - * ii_write_unlock() after iget_failed(). so dirty hack for - * i_count. - */ - atomic_inc(&inode->i_count); - iget_failed(inode); - ii_write_unlock(inode); - au_xino_write(sb, bstart, h_ino, /*ino*/0); - /* ignore this error */ - goto out_iput; - } else if (!must_new && !IS_DEADDIR(inode) && inode->i_nlink) { - /* - * horrible race condition between lookup, readdir and copyup - * (or something). - */ - if (mtx) - mutex_unlock(mtx); - err = reval_inode(inode, dentry); - if (unlikely(err < 0)) { - mtx = NULL; - goto out_iput; - } - - if (!err) { - mtx = NULL; - goto out; /* success */ - } else if (mtx) - mutex_lock(mtx); - } - - if (unlikely(au_test_fs_unique_ino(h_inode))) - AuWarn1("Warning: Un-notified UDBA or repeatedly renamed dir," - " b%d, %s, %pd, hi%lu, i%lu.\n", - bstart, au_sbtype(h_dentry->d_sb), dentry, - (unsigned long)h_ino, (unsigned long)ino); - ino = 0; - err = au_xino_write(sb, bstart, h_ino, /*ino*/0); - if (!err) { - iput(inode); - if (mtx) - mutex_unlock(mtx); - goto new_ino; - } - -out_iput: - iput(inode); - inode = ERR_PTR(err); -out: - if (mtx) - mutex_unlock(mtx); - return inode; -} - -/* ---------------------------------------------------------------------- */ - -int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, - struct inode *inode) -{ - int err; - struct inode *hi; - - err = au_br_rdonly(au_sbr(sb, bindex)); - - /* pseudo-link after flushed may happen out of bounds */ - if (!err - && inode - && au_ibstart(inode) <= bindex - && bindex <= au_ibend(inode)) { - /* - * permission check is unnecessary since vfsub routine - * will be called later - */ - hi = au_h_iptr(inode, bindex); - if (hi) - err = IS_IMMUTABLE(hi) ? -EROFS : 0; - } - - return err; -} - -int au_test_h_perm(struct inode *h_inode, int mask) -{ - if (uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) - return 0; - return inode_permission(h_inode, mask); -} - -int au_test_h_perm_sio(struct inode *h_inode, int mask) -{ - if (au_test_nfs(h_inode->i_sb) - && (mask & MAY_WRITE) - && S_ISDIR(h_inode->i_mode)) - mask |= MAY_READ; /* force permission check */ - return au_test_h_perm(h_inode, mask); -} diff --git a/fs/aufs/inode.h b/fs/aufs/inode.h deleted file mode 100644 index 1b9be356d..000000000 --- a/fs/aufs/inode.h +++ /dev/null @@ -1,673 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * inode operations - */ - -#ifndef __AUFS_INODE_H__ -#define __AUFS_INODE_H__ - -#ifdef __KERNEL__ - -#include <linux/fsnotify.h> -#include "rwsem.h" - -struct vfsmount; - -struct au_hnotify { -#ifdef CONFIG_AUFS_HNOTIFY -#ifdef CONFIG_AUFS_HFSNOTIFY - /* never use fsnotify_add_vfsmount_mark() */ - struct fsnotify_mark hn_mark; -#endif - struct inode *hn_aufs_inode; /* no get/put */ -#endif -} ____cacheline_aligned_in_smp; - -struct au_hinode { - struct inode *hi_inode; - aufs_bindex_t hi_id; -#ifdef CONFIG_AUFS_HNOTIFY - struct au_hnotify *hi_notify; -#endif - - /* reference to the copied-up whiteout with get/put */ - struct dentry *hi_whdentry; -}; - -/* ig_flags */ -#define AuIG_HALF_REFRESHED 1 -#define au_ig_ftest(flags, name) ((flags) & AuIG_##name) -#define au_ig_fset(flags, name) \ - do { (flags) |= AuIG_##name; } while (0) -#define au_ig_fclr(flags, name) \ - do { (flags) &= ~AuIG_##name; } while (0) - -struct au_iigen { - __u32 ig_generation, ig_flags; -}; - -struct au_vdir; -struct au_iinfo { - spinlock_t ii_genspin; - struct au_iigen ii_generation; - struct super_block *ii_hsb1; /* no get/put */ - - struct au_rwsem ii_rwsem; - aufs_bindex_t ii_bstart, ii_bend; - __u32 ii_higen; - struct au_hinode *ii_hinode; - struct au_vdir *ii_vdir; -}; - -struct au_icntnr { - struct au_iinfo iinfo; - struct inode vfs_inode; -} ____cacheline_aligned_in_smp; - -/* au_pin flags */ -#define AuPin_DI_LOCKED 1 -#define AuPin_MNT_WRITE (1 << 1) -#define au_ftest_pin(flags, name) ((flags) & AuPin_##name) -#define au_fset_pin(flags, name) \ - do { (flags) |= AuPin_##name; } while (0) -#define au_fclr_pin(flags, name) \ - do { (flags) &= ~AuPin_##name; } while (0) - -struct au_pin { - /* input */ - struct dentry *dentry; - unsigned int udba; - unsigned char lsc_di, lsc_hi, flags; - aufs_bindex_t bindex; - - /* output */ - struct dentry *parent; - struct au_hinode *hdir; - struct vfsmount *h_mnt; - - /* temporary unlock/relock for copyup */ - struct dentry *h_dentry, *h_parent; - struct au_branch *br; - struct task_struct *task; -}; - -void au_pin_hdir_unlock(struct au_pin *p); -int au_pin_hdir_lock(struct au_pin *p); -int au_pin_hdir_relock(struct au_pin *p); -void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task); -void au_pin_hdir_acquire_nest(struct au_pin *p); -void au_pin_hdir_release(struct au_pin *p); - -/* ---------------------------------------------------------------------- */ - -static inline struct au_iinfo *au_ii(struct inode *inode) -{ - struct au_iinfo *iinfo; - - iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); - if (iinfo->ii_hinode) - return iinfo; - return NULL; /* debugging bad_inode case */ -} - -/* ---------------------------------------------------------------------- */ - -/* inode.c */ -struct inode *au_igrab(struct inode *inode); -int au_refresh_hinode_self(struct inode *inode); -int au_refresh_hinode(struct inode *inode, struct dentry *dentry); -int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, - unsigned int d_type, ino_t *ino); -struct inode *au_new_inode(struct dentry *dentry, int must_new); -int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, - struct inode *inode); -int au_test_h_perm(struct inode *h_inode, int mask); -int au_test_h_perm_sio(struct inode *h_inode, int mask); - -static inline int au_wh_ino(struct super_block *sb, aufs_bindex_t bindex, - ino_t h_ino, unsigned int d_type, ino_t *ino) -{ -#ifdef CONFIG_AUFS_SHWH - return au_ino(sb, bindex, h_ino, d_type, ino); -#else - return 0; -#endif -} - -/* i_op.c */ -extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop; - -/* au_wr_dir flags */ -#define AuWrDir_ADD_ENTRY 1 -#define AuWrDir_ISDIR (1 << 1) -#define AuWrDir_TMPFILE (1 << 2) -#define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name) -#define au_fset_wrdir(flags, name) \ - do { (flags) |= AuWrDir_##name; } while (0) -#define au_fclr_wrdir(flags, name) \ - do { (flags) &= ~AuWrDir_##name; } while (0) - -struct au_wr_dir_args { - aufs_bindex_t force_btgt; - unsigned char flags; -}; -int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, - struct au_wr_dir_args *args); - -struct dentry *au_pinned_h_parent(struct au_pin *pin); -void au_pin_init(struct au_pin *pin, struct dentry *dentry, - aufs_bindex_t bindex, int lsc_di, int lsc_hi, - unsigned int udba, unsigned char flags); -int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, - unsigned int udba, unsigned char flags) __must_check; -int au_do_pin(struct au_pin *pin) __must_check; -void au_unpin(struct au_pin *pin); -int au_reval_for_attr(struct dentry *dentry, unsigned int sigen); - -#define AuIcpup_DID_CPUP 1 -#define au_ftest_icpup(flags, name) ((flags) & AuIcpup_##name) -#define au_fset_icpup(flags, name) \ - do { (flags) |= AuIcpup_##name; } while (0) -#define au_fclr_icpup(flags, name) \ - do { (flags) &= ~AuIcpup_##name; } while (0) - -struct au_icpup_args { - unsigned char flags; - unsigned char pin_flags; - aufs_bindex_t btgt; - unsigned int udba; - struct au_pin pin; - struct path h_path; - struct inode *h_inode; -}; - -int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, - struct au_icpup_args *a); - -int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path); - -/* i_op_add.c */ -int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, - struct dentry *h_parent, int isdir); -int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, - dev_t dev); -int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname); -int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode, - bool want_excl); -struct vfsub_aopen_args; -int au_aopen_or_create(struct inode *dir, struct dentry *dentry, - struct vfsub_aopen_args *args); -int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode); -int aufs_link(struct dentry *src_dentry, struct inode *dir, - struct dentry *dentry); -int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode); - -/* i_op_del.c */ -int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup); -int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, - struct dentry *h_parent, int isdir); -int aufs_unlink(struct inode *dir, struct dentry *dentry); -int aufs_rmdir(struct inode *dir, struct dentry *dentry); - -/* i_op_ren.c */ -int au_wbr(struct dentry *dentry, aufs_bindex_t btgt); -int aufs_rename(struct inode *src_dir, struct dentry *src_dentry, - struct inode *dir, struct dentry *dentry); - -/* iinfo.c */ -struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex); -void au_hiput(struct au_hinode *hinode); -void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, - struct dentry *h_wh); -unsigned int au_hi_flags(struct inode *inode, int isdir); - -/* hinode flags */ -#define AuHi_XINO 1 -#define AuHi_HNOTIFY (1 << 1) -#define au_ftest_hi(flags, name) ((flags) & AuHi_##name) -#define au_fset_hi(flags, name) \ - do { (flags) |= AuHi_##name; } while (0) -#define au_fclr_hi(flags, name) \ - do { (flags) &= ~AuHi_##name; } while (0) - -#ifndef CONFIG_AUFS_HNOTIFY -#undef AuHi_HNOTIFY -#define AuHi_HNOTIFY 0 -#endif - -void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, - struct inode *h_inode, unsigned int flags); - -void au_update_iigen(struct inode *inode, int half); -void au_update_ibrange(struct inode *inode, int do_put_zero); - -void au_icntnr_init_once(void *_c); -int au_iinfo_init(struct inode *inode); -void au_iinfo_fin(struct inode *inode); -int au_ii_realloc(struct au_iinfo *iinfo, int nbr); - -#ifdef CONFIG_PROC_FS -/* plink.c */ -int au_plink_maint(struct super_block *sb, int flags); -struct au_sbinfo; -void au_plink_maint_leave(struct au_sbinfo *sbinfo); -int au_plink_maint_enter(struct super_block *sb); -#ifdef CONFIG_AUFS_DEBUG -void au_plink_list(struct super_block *sb); -#else -AuStubVoid(au_plink_list, struct super_block *sb) -#endif -int au_plink_test(struct inode *inode); -struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex); -void au_plink_append(struct inode *inode, aufs_bindex_t bindex, - struct dentry *h_dentry); -void au_plink_put(struct super_block *sb, int verbose); -void au_plink_clean(struct super_block *sb, int verbose); -void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id); -#else -AuStubInt0(au_plink_maint, struct super_block *sb, int flags); -AuStubVoid(au_plink_maint_leave, struct au_sbinfo *sbinfo); -AuStubInt0(au_plink_maint_enter, struct super_block *sb); -AuStubVoid(au_plink_list, struct super_block *sb); -AuStubInt0(au_plink_test, struct inode *inode); -AuStub(struct dentry *, au_plink_lkup, return NULL, - struct inode *inode, aufs_bindex_t bindex); -AuStubVoid(au_plink_append, struct inode *inode, aufs_bindex_t bindex, - struct dentry *h_dentry); -AuStubVoid(au_plink_put, struct super_block *sb, int verbose); -AuStubVoid(au_plink_clean, struct super_block *sb, int verbose); -AuStubVoid(au_plink_half_refresh, struct super_block *sb, aufs_bindex_t br_id); -#endif /* CONFIG_PROC_FS */ - -#ifdef CONFIG_AUFS_XATTR -/* xattr.c */ -int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags, - unsigned int verbose); -ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size); -ssize_t aufs_getxattr(struct dentry *dentry, const char *name, void *value, - size_t size); -int aufs_setxattr(struct dentry *dentry, const char *name, const void *value, - size_t size, int flags); -int aufs_removexattr(struct dentry *dentry, const char *name); - -/* void au_xattr_init(struct super_block *sb); */ -#else -AuStubInt0(au_cpup_xattr, struct dentry *h_dst, struct dentry *h_src, - int ignore_flags, unsigned int verbose); -/* AuStubVoid(au_xattr_init, struct super_block *sb); */ -#endif - -#ifdef CONFIG_FS_POSIX_ACL -struct posix_acl *aufs_get_acl(struct inode *inode, int type); -int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type); -#endif - -#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL) -enum { - AU_XATTR_SET, - AU_XATTR_REMOVE, - AU_ACL_SET -}; - -struct au_srxattr { - int type; - union { - struct { - const char *name; - const void *value; - size_t size; - int flags; - } set; - struct { - const char *name; - } remove; - struct { - struct posix_acl *acl; - int type; - } acl_set; - } u; -}; -ssize_t au_srxattr(struct dentry *dentry, struct au_srxattr *arg); -#endif - -/* ---------------------------------------------------------------------- */ - -/* lock subclass for iinfo */ -enum { - AuLsc_II_CHILD, /* child first */ - AuLsc_II_CHILD2, /* rename(2), link(2), and cpup at hnotify */ - AuLsc_II_CHILD3, /* copyup dirs */ - AuLsc_II_PARENT, /* see AuLsc_I_PARENT in vfsub.h */ - AuLsc_II_PARENT2, - AuLsc_II_PARENT3, /* copyup dirs */ - AuLsc_II_NEW_CHILD -}; - -/* - * ii_read_lock_child, ii_write_lock_child, - * ii_read_lock_child2, ii_write_lock_child2, - * ii_read_lock_child3, ii_write_lock_child3, - * ii_read_lock_parent, ii_write_lock_parent, - * ii_read_lock_parent2, ii_write_lock_parent2, - * ii_read_lock_parent3, ii_write_lock_parent3, - * ii_read_lock_new_child, ii_write_lock_new_child, - */ -#define AuReadLockFunc(name, lsc) \ -static inline void ii_read_lock_##name(struct inode *i) \ -{ \ - au_rw_read_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ -} - -#define AuWriteLockFunc(name, lsc) \ -static inline void ii_write_lock_##name(struct inode *i) \ -{ \ - au_rw_write_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ -} - -#define AuRWLockFuncs(name, lsc) \ - AuReadLockFunc(name, lsc) \ - AuWriteLockFunc(name, lsc) - -AuRWLockFuncs(child, CHILD); -AuRWLockFuncs(child2, CHILD2); -AuRWLockFuncs(child3, CHILD3); -AuRWLockFuncs(parent, PARENT); -AuRWLockFuncs(parent2, PARENT2); -AuRWLockFuncs(parent3, PARENT3); -AuRWLockFuncs(new_child, NEW_CHILD); - -#undef AuReadLockFunc -#undef AuWriteLockFunc -#undef AuRWLockFuncs - -/* - * ii_read_unlock, ii_write_unlock, ii_downgrade_lock - */ -AuSimpleUnlockRwsemFuncs(ii, struct inode *i, &au_ii(i)->ii_rwsem); - -#define IiMustNoWaiters(i) AuRwMustNoWaiters(&au_ii(i)->ii_rwsem) -#define IiMustAnyLock(i) AuRwMustAnyLock(&au_ii(i)->ii_rwsem) -#define IiMustWriteLock(i) AuRwMustWriteLock(&au_ii(i)->ii_rwsem) - -/* ---------------------------------------------------------------------- */ - -static inline void au_icntnr_init(struct au_icntnr *c) -{ -#ifdef CONFIG_AUFS_DEBUG - c->vfs_inode.i_mode = 0; -#endif -} - -static inline unsigned int au_iigen(struct inode *inode, struct au_iigen *iigen) -{ - unsigned int gen; - struct au_iinfo *iinfo; - - iinfo = au_ii(inode); - spin_lock(&iinfo->ii_genspin); - if (iigen) - *iigen = iinfo->ii_generation; - gen = iinfo->ii_generation.ig_generation; - spin_unlock(&iinfo->ii_genspin); - - return gen; -} - -/* tiny test for inode number */ -/* tmpfs generation is too rough */ -static inline int au_test_higen(struct inode *inode, struct inode *h_inode) -{ - struct au_iinfo *iinfo; - - iinfo = au_ii(inode); - AuRwMustAnyLock(&iinfo->ii_rwsem); - return !(iinfo->ii_hsb1 == h_inode->i_sb - && iinfo->ii_higen == h_inode->i_generation); -} - -static inline void au_iigen_dec(struct inode *inode) -{ - struct au_iinfo *iinfo; - - iinfo = au_ii(inode); - spin_lock(&iinfo->ii_genspin); - iinfo->ii_generation.ig_generation--; - spin_unlock(&iinfo->ii_genspin); -} - -static inline int au_iigen_test(struct inode *inode, unsigned int sigen) -{ - int err; - - err = 0; - if (unlikely(inode && au_iigen(inode, NULL) != sigen)) - err = -EIO; - - return err; -} - -/* ---------------------------------------------------------------------- */ - -static inline aufs_bindex_t au_ii_br_id(struct inode *inode, - aufs_bindex_t bindex) -{ - IiMustAnyLock(inode); - return au_ii(inode)->ii_hinode[0 + bindex].hi_id; -} - -static inline aufs_bindex_t au_ibstart(struct inode *inode) -{ - IiMustAnyLock(inode); - return au_ii(inode)->ii_bstart; -} - -static inline aufs_bindex_t au_ibend(struct inode *inode) -{ - IiMustAnyLock(inode); - return au_ii(inode)->ii_bend; -} - -static inline struct au_vdir *au_ivdir(struct inode *inode) -{ - IiMustAnyLock(inode); - return au_ii(inode)->ii_vdir; -} - -static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex) -{ - IiMustAnyLock(inode); - return au_ii(inode)->ii_hinode[0 + bindex].hi_whdentry; -} - -static inline void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex) -{ - IiMustWriteLock(inode); - au_ii(inode)->ii_bstart = bindex; -} - -static inline void au_set_ibend(struct inode *inode, aufs_bindex_t bindex) -{ - IiMustWriteLock(inode); - au_ii(inode)->ii_bend = bindex; -} - -static inline void au_set_ivdir(struct inode *inode, struct au_vdir *vdir) -{ - IiMustWriteLock(inode); - au_ii(inode)->ii_vdir = vdir; -} - -static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex) -{ - IiMustAnyLock(inode); - return au_ii(inode)->ii_hinode + bindex; -} - -/* ---------------------------------------------------------------------- */ - -static inline struct dentry *au_pinned_parent(struct au_pin *pin) -{ - if (pin) - return pin->parent; - return NULL; -} - -static inline struct inode *au_pinned_h_dir(struct au_pin *pin) -{ - if (pin && pin->hdir) - return pin->hdir->hi_inode; - return NULL; -} - -static inline struct au_hinode *au_pinned_hdir(struct au_pin *pin) -{ - if (pin) - return pin->hdir; - return NULL; -} - -static inline void au_pin_set_dentry(struct au_pin *pin, struct dentry *dentry) -{ - if (pin) - pin->dentry = dentry; -} - -static inline void au_pin_set_parent_lflag(struct au_pin *pin, - unsigned char lflag) -{ - if (pin) { - if (lflag) - au_fset_pin(pin->flags, DI_LOCKED); - else - au_fclr_pin(pin->flags, DI_LOCKED); - } -} - -#if 0 /* reserved */ -static inline void au_pin_set_parent(struct au_pin *pin, struct dentry *parent) -{ - if (pin) { - dput(pin->parent); - pin->parent = dget(parent); - } -} -#endif - -/* ---------------------------------------------------------------------- */ - -struct au_branch; -#ifdef CONFIG_AUFS_HNOTIFY -struct au_hnotify_op { - void (*ctl)(struct au_hinode *hinode, int do_set); - int (*alloc)(struct au_hinode *hinode); - - /* - * if it returns true, the the caller should free hinode->hi_notify, - * otherwise ->free() frees it. - */ - int (*free)(struct au_hinode *hinode, - struct au_hnotify *hn) __must_check; - - void (*fin)(void); - int (*init)(void); - - int (*reset_br)(unsigned int udba, struct au_branch *br, int perm); - void (*fin_br)(struct au_branch *br); - int (*init_br)(struct au_branch *br, int perm); -}; - -/* hnotify.c */ -int au_hn_alloc(struct au_hinode *hinode, struct inode *inode); -void au_hn_free(struct au_hinode *hinode); -void au_hn_ctl(struct au_hinode *hinode, int do_set); -void au_hn_reset(struct inode *inode, unsigned int flags); -int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, - struct qstr *h_child_qstr, struct inode *h_child_inode); -int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm); -int au_hnotify_init_br(struct au_branch *br, int perm); -void au_hnotify_fin_br(struct au_branch *br); -int __init au_hnotify_init(void); -void au_hnotify_fin(void); - -/* hfsnotify.c */ -extern const struct au_hnotify_op au_hnotify_op; - -static inline -void au_hn_init(struct au_hinode *hinode) -{ - hinode->hi_notify = NULL; -} - -static inline struct au_hnotify *au_hn(struct au_hinode *hinode) -{ - return hinode->hi_notify; -} - -#else -AuStub(int, au_hn_alloc, return -EOPNOTSUPP, - struct au_hinode *hinode __maybe_unused, - struct inode *inode __maybe_unused) -AuStub(struct au_hnotify *, au_hn, return NULL, struct au_hinode *hinode) -AuStubVoid(au_hn_free, struct au_hinode *hinode __maybe_unused) -AuStubVoid(au_hn_ctl, struct au_hinode *hinode __maybe_unused, - int do_set __maybe_unused) -AuStubVoid(au_hn_reset, struct inode *inode __maybe_unused, - unsigned int flags __maybe_unused) -AuStubInt0(au_hnotify_reset_br, unsigned int udba __maybe_unused, - struct au_branch *br __maybe_unused, - int perm __maybe_unused) -AuStubInt0(au_hnotify_init_br, struct au_branch *br __maybe_unused, - int perm __maybe_unused) -AuStubVoid(au_hnotify_fin_br, struct au_branch *br __maybe_unused) -AuStubInt0(__init au_hnotify_init, void) -AuStubVoid(au_hnotify_fin, void) -AuStubVoid(au_hn_init, struct au_hinode *hinode __maybe_unused) -#endif /* CONFIG_AUFS_HNOTIFY */ - -static inline void au_hn_suspend(struct au_hinode *hdir) -{ - au_hn_ctl(hdir, /*do_set*/0); -} - -static inline void au_hn_resume(struct au_hinode *hdir) -{ - au_hn_ctl(hdir, /*do_set*/1); -} - -static inline void au_hn_imtx_lock(struct au_hinode *hdir) -{ - mutex_lock(&hdir->hi_inode->i_mutex); - au_hn_suspend(hdir); -} - -static inline void au_hn_imtx_lock_nested(struct au_hinode *hdir, - unsigned int sc __maybe_unused) -{ - mutex_lock_nested(&hdir->hi_inode->i_mutex, sc); - au_hn_suspend(hdir); -} - -static inline void au_hn_imtx_unlock(struct au_hinode *hdir) -{ - au_hn_resume(hdir); - mutex_unlock(&hdir->hi_inode->i_mutex); -} - -#endif /* __KERNEL__ */ -#endif /* __AUFS_INODE_H__ */ diff --git a/fs/aufs/ioctl.c b/fs/aufs/ioctl.c deleted file mode 100644 index 18d7c6aea..000000000 --- a/fs/aufs/ioctl.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * ioctl - * plink-management and readdir in userspace. - * assist the pathconf(3) wrapper library. - * move-down - * File-based Hierarchical Storage Management. - */ - -#include <linux/compat.h> -#include <linux/file.h> -#include "aufs.h" - -static int au_wbr_fd(struct path *path, struct aufs_wbr_fd __user *arg) -{ - int err, fd; - aufs_bindex_t wbi, bindex, bend; - struct file *h_file; - struct super_block *sb; - struct dentry *root; - struct au_branch *br; - struct aufs_wbr_fd wbrfd = { - .oflags = au_dir_roflags, - .brid = -1 - }; - const int valid = O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_DIRECTORY - | O_NOATIME | O_CLOEXEC; - - AuDebugOn(wbrfd.oflags & ~valid); - - if (arg) { - err = copy_from_user(&wbrfd, arg, sizeof(wbrfd)); - if (unlikely(err)) { - err = -EFAULT; - goto out; - } - - err = -EINVAL; - AuDbg("wbrfd{0%o, %d}\n", wbrfd.oflags, wbrfd.brid); - wbrfd.oflags |= au_dir_roflags; - AuDbg("0%o\n", wbrfd.oflags); - if (unlikely(wbrfd.oflags & ~valid)) - goto out; - } - - fd = get_unused_fd_flags(0); - err = fd; - if (unlikely(fd < 0)) - goto out; - - h_file = ERR_PTR(-EINVAL); - wbi = 0; - br = NULL; - sb = path->dentry->d_sb; - root = sb->s_root; - aufs_read_lock(root, AuLock_IR); - bend = au_sbend(sb); - if (wbrfd.brid >= 0) { - wbi = au_br_index(sb, wbrfd.brid); - if (unlikely(wbi < 0 || wbi > bend)) - goto out_unlock; - } - - h_file = ERR_PTR(-ENOENT); - br = au_sbr(sb, wbi); - if (!au_br_writable(br->br_perm)) { - if (arg) - goto out_unlock; - - bindex = wbi + 1; - wbi = -1; - for (; bindex <= bend; bindex++) { - br = au_sbr(sb, bindex); - if (au_br_writable(br->br_perm)) { - wbi = bindex; - br = au_sbr(sb, wbi); - break; - } - } - } - AuDbg("wbi %d\n", wbi); - if (wbi >= 0) - h_file = au_h_open(root, wbi, wbrfd.oflags, NULL, - /*force_wr*/0); - -out_unlock: - aufs_read_unlock(root, AuLock_IR); - err = PTR_ERR(h_file); - if (IS_ERR(h_file)) - goto out_fd; - - atomic_dec(&br->br_count); /* cf. au_h_open() */ - fd_install(fd, h_file); - err = fd; - goto out; /* success */ - -out_fd: - put_unused_fd(fd); -out: - AuTraceErr(err); - return err; -} - -/* ---------------------------------------------------------------------- */ - -long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg) -{ - long err; - struct dentry *dentry; - - switch (cmd) { - case AUFS_CTL_RDU: - case AUFS_CTL_RDU_INO: - err = au_rdu_ioctl(file, cmd, arg); - break; - - case AUFS_CTL_WBR_FD: - err = au_wbr_fd(&file->f_path, (void __user *)arg); - break; - - case AUFS_CTL_IBUSY: - err = au_ibusy_ioctl(file, arg); - break; - - case AUFS_CTL_BRINFO: - err = au_brinfo_ioctl(file, arg); - break; - - case AUFS_CTL_FHSM_FD: - dentry = file->f_path.dentry; - if (IS_ROOT(dentry)) - err = au_fhsm_fd(dentry->d_sb, arg); - else - err = -ENOTTY; - break; - - default: - /* do not call the lower */ - AuDbg("0x%x\n", cmd); - err = -ENOTTY; - } - - AuTraceErr(err); - return err; -} - -long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg) -{ - long err; - - switch (cmd) { - case AUFS_CTL_MVDOWN: - err = au_mvdown(file->f_path.dentry, (void __user *)arg); - break; - - case AUFS_CTL_WBR_FD: - err = au_wbr_fd(&file->f_path, (void __user *)arg); - break; - - default: - /* do not call the lower */ - AuDbg("0x%x\n", cmd); - err = -ENOTTY; - } - - AuTraceErr(err); - return err; -} - -#ifdef CONFIG_COMPAT -long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd, - unsigned long arg) -{ - long err; - - switch (cmd) { - case AUFS_CTL_RDU: - case AUFS_CTL_RDU_INO: - err = au_rdu_compat_ioctl(file, cmd, arg); - break; - - case AUFS_CTL_IBUSY: - err = au_ibusy_compat_ioctl(file, arg); - break; - - case AUFS_CTL_BRINFO: - err = au_brinfo_compat_ioctl(file, arg); - break; - - default: - err = aufs_ioctl_dir(file, cmd, arg); - } - - AuTraceErr(err); - return err; -} - -long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd, - unsigned long arg) -{ - return aufs_ioctl_nondir(file, cmd, (unsigned long)compat_ptr(arg)); -} -#endif diff --git a/fs/aufs/loop.c b/fs/aufs/loop.c deleted file mode 100644 index 677568445..000000000 --- a/fs/aufs/loop.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * support for loopback block device as a branch - */ - -#include "aufs.h" - -/* added into drivers/block/loop.c */ -static struct file *(*backing_file_func)(struct super_block *sb); - -/* - * test if two lower dentries have overlapping branches. - */ -int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding) -{ - struct super_block *h_sb; - struct file *backing_file; - - if (unlikely(!backing_file_func)) { - /* don't load "loop" module here */ - backing_file_func = symbol_get(loop_backing_file); - if (unlikely(!backing_file_func)) - /* "loop" module is not loaded */ - return 0; - } - - h_sb = h_adding->d_sb; - backing_file = backing_file_func(h_sb); - if (!backing_file) - return 0; - - h_adding = backing_file->f_path.dentry; - /* - * h_adding can be local NFS. - * in this case aufs cannot detect the loop. - */ - if (unlikely(h_adding->d_sb == sb)) - return 1; - return !!au_test_subdir(h_adding, sb->s_root); -} - -/* true if a kernel thread named 'loop[0-9].*' accesses a file */ -int au_test_loopback_kthread(void) -{ - int ret; - struct task_struct *tsk = current; - char c, comm[sizeof(tsk->comm)]; - - ret = 0; - if (tsk->flags & PF_KTHREAD) { - get_task_comm(comm, tsk); - c = comm[4]; - ret = ('0' <= c && c <= '9' - && !strncmp(comm, "loop", 4)); - } - - return ret; -} - -/* ---------------------------------------------------------------------- */ - -#define au_warn_loopback_step 16 -static int au_warn_loopback_nelem = au_warn_loopback_step; -static unsigned long *au_warn_loopback_array; - -void au_warn_loopback(struct super_block *h_sb) -{ - int i, new_nelem; - unsigned long *a, magic; - static DEFINE_SPINLOCK(spin); - - magic = h_sb->s_magic; - spin_lock(&spin); - a = au_warn_loopback_array; - for (i = 0; i < au_warn_loopback_nelem && *a; i++) - if (a[i] == magic) { - spin_unlock(&spin); - return; - } - - /* h_sb is new to us, print it */ - if (i < au_warn_loopback_nelem) { - a[i] = magic; - goto pr; - } - - /* expand the array */ - new_nelem = au_warn_loopback_nelem + au_warn_loopback_step; - a = au_kzrealloc(au_warn_loopback_array, - au_warn_loopback_nelem * sizeof(unsigned long), - new_nelem * sizeof(unsigned long), GFP_ATOMIC); - if (a) { - au_warn_loopback_nelem = new_nelem; - au_warn_loopback_array = a; - a[i] = magic; - goto pr; - } - - spin_unlock(&spin); - AuWarn1("realloc failed, ignored\n"); - return; - -pr: - spin_unlock(&spin); - pr_warn("you may want to try another patch for loopback file " - "on %s(0x%lx) branch\n", au_sbtype(h_sb), magic); -} - -int au_loopback_init(void) -{ - int err; - struct super_block *sb __maybe_unused; - - AuDebugOn(sizeof(sb->s_magic) != sizeof(unsigned long)); - - err = 0; - au_warn_loopback_array = kcalloc(au_warn_loopback_step, - sizeof(unsigned long), GFP_NOFS); - if (unlikely(!au_warn_loopback_array)) - err = -ENOMEM; - - return err; -} - -void au_loopback_fin(void) -{ - symbol_put(loop_backing_file); - kfree(au_warn_loopback_array); -} diff --git a/fs/aufs/loop.h b/fs/aufs/loop.h deleted file mode 100644 index 0c28c2366..000000000 --- a/fs/aufs/loop.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * support for loopback mount as a branch - */ - -#ifndef __AUFS_LOOP_H__ -#define __AUFS_LOOP_H__ - -#ifdef __KERNEL__ - -struct dentry; -struct super_block; - -#ifdef CONFIG_AUFS_BDEV_LOOP -/* drivers/block/loop.c */ -struct file *loop_backing_file(struct super_block *sb); - -/* loop.c */ -int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding); -int au_test_loopback_kthread(void); -void au_warn_loopback(struct super_block *h_sb); - -int au_loopback_init(void); -void au_loopback_fin(void); -#else -AuStubInt0(au_test_loopback_overlap, struct super_block *sb, - struct dentry *h_adding) -AuStubInt0(au_test_loopback_kthread, void) -AuStubVoid(au_warn_loopback, struct super_block *h_sb) - -AuStubInt0(au_loopback_init, void) -AuStubVoid(au_loopback_fin, void) -#endif /* BLK_DEV_LOOP */ - -#endif /* __KERNEL__ */ -#endif /* __AUFS_LOOP_H__ */ diff --git a/fs/aufs/magic.mk b/fs/aufs/magic.mk deleted file mode 100644 index 4f83bdf1d..000000000 --- a/fs/aufs/magic.mk +++ /dev/null @@ -1,30 +0,0 @@ - -# defined in ${srctree}/fs/fuse/inode.c -# tristate -ifdef CONFIG_FUSE_FS -ccflags-y += -DFUSE_SUPER_MAGIC=0x65735546 -endif - -# defined in ${srctree}/fs/xfs/xfs_sb.h -# tristate -ifdef CONFIG_XFS_FS -ccflags-y += -DXFS_SB_MAGIC=0x58465342 -endif - -# defined in ${srctree}/fs/configfs/mount.c -# tristate -ifdef CONFIG_CONFIGFS_FS -ccflags-y += -DCONFIGFS_MAGIC=0x62656570 -endif - -# defined in ${srctree}/fs/ubifs/ubifs.h -# tristate -ifdef CONFIG_UBIFS_FS -ccflags-y += -DUBIFS_SUPER_MAGIC=0x24051905 -endif - -# defined in ${srctree}/fs/hfsplus/hfsplus_raw.h -# tristate -ifdef CONFIG_HFSPLUS_FS -ccflags-y += -DHFSPLUS_SUPER_MAGIC=0x482b -endif diff --git a/fs/aufs/module.c b/fs/aufs/module.c deleted file mode 100644 index 2c70dfffc..000000000 --- a/fs/aufs/module.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * module global variables and operations - */ - -#include <linux/module.h> -#include <linux/seq_file.h> -#include "aufs.h" - -void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp) -{ - if (new_sz <= nused) - return p; - - p = krealloc(p, new_sz, gfp); - if (p) - memset(p + nused, 0, new_sz - nused); - return p; -} - -/* ---------------------------------------------------------------------- */ - -/* - * aufs caches - */ -struct kmem_cache *au_cachep[AuCache_Last]; -static int __init au_cache_init(void) -{ - au_cachep[AuCache_DINFO] = AuCacheCtor(au_dinfo, au_di_init_once); - if (au_cachep[AuCache_DINFO]) - /* SLAB_DESTROY_BY_RCU */ - au_cachep[AuCache_ICNTNR] = AuCacheCtor(au_icntnr, - au_icntnr_init_once); - if (au_cachep[AuCache_ICNTNR]) - au_cachep[AuCache_FINFO] = AuCacheCtor(au_finfo, - au_fi_init_once); - if (au_cachep[AuCache_FINFO]) - au_cachep[AuCache_VDIR] = AuCache(au_vdir); - if (au_cachep[AuCache_VDIR]) - au_cachep[AuCache_DEHSTR] = AuCache(au_vdir_dehstr); - if (au_cachep[AuCache_DEHSTR]) - return 0; - - return -ENOMEM; -} - -static void au_cache_fin(void) -{ - int i; - - /* - * Make sure all delayed rcu free inodes are flushed before we - * destroy cache. - */ - rcu_barrier(); - - /* excluding AuCache_HNOTIFY */ - BUILD_BUG_ON(AuCache_HNOTIFY + 1 != AuCache_Last); - for (i = 0; i < AuCache_HNOTIFY; i++) - if (au_cachep[i]) { - kmem_cache_destroy(au_cachep[i]); - au_cachep[i] = NULL; - } -} - -/* ---------------------------------------------------------------------- */ - -int au_dir_roflags; - -#ifdef CONFIG_AUFS_SBILIST -/* - * iterate_supers_type() doesn't protect us from - * remounting (branch management) - */ -struct au_splhead au_sbilist; -#endif - -struct lock_class_key au_lc_key[AuLcKey_Last]; - -/* - * functions for module interface. - */ -MODULE_LICENSE("GPL"); -/* MODULE_LICENSE("GPL v2"); */ -MODULE_AUTHOR("Junjiro R. Okajima <aufs-users@lists.sourceforge.net>"); -MODULE_DESCRIPTION(AUFS_NAME - " -- Advanced multi layered unification filesystem"); -MODULE_VERSION(AUFS_VERSION); -MODULE_ALIAS_FS(AUFS_NAME); - -/* this module parameter has no meaning when SYSFS is disabled */ -int sysaufs_brs = 1; -MODULE_PARM_DESC(brs, "use <sysfs>/fs/aufs/si_*/brN"); -module_param_named(brs, sysaufs_brs, int, S_IRUGO); - -/* this module parameter has no meaning when USER_NS is disabled */ -static bool au_userns; -MODULE_PARM_DESC(allow_userns, "allow unprivileged to mount under userns"); -module_param_named(allow_userns, au_userns, bool, S_IRUGO); - -/* ---------------------------------------------------------------------- */ - -static char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */ - -int au_seq_path(struct seq_file *seq, struct path *path) -{ - return seq_path(seq, path, au_esc_chars); -} - -/* ---------------------------------------------------------------------- */ - -static int __init aufs_init(void) -{ - int err, i; - char *p; - - p = au_esc_chars; - for (i = 1; i <= ' '; i++) - *p++ = i; - *p++ = '\\'; - *p++ = '\x7f'; - *p = 0; - - au_dir_roflags = au_file_roflags(O_DIRECTORY | O_LARGEFILE); - - au_sbilist_init(); - sysaufs_brs_init(); - au_debug_init(); - au_dy_init(); - err = sysaufs_init(); - if (unlikely(err)) - goto out; - err = au_procfs_init(); - if (unlikely(err)) - goto out_sysaufs; - err = au_wkq_init(); - if (unlikely(err)) - goto out_procfs; - err = au_loopback_init(); - if (unlikely(err)) - goto out_wkq; - err = au_hnotify_init(); - if (unlikely(err)) - goto out_loopback; - err = au_sysrq_init(); - if (unlikely(err)) - goto out_hin; - err = au_cache_init(); - if (unlikely(err)) - goto out_sysrq; - - aufs_fs_type.fs_flags |= au_userns ? FS_USERNS_MOUNT : 0; - err = register_filesystem(&aufs_fs_type); - if (unlikely(err)) - goto out_cache; - - /* since we define pr_fmt, call printk directly */ - printk(KERN_INFO AUFS_NAME " " AUFS_VERSION "\n"); - goto out; /* success */ - -out_cache: - au_cache_fin(); -out_sysrq: - au_sysrq_fin(); -out_hin: - au_hnotify_fin(); -out_loopback: - au_loopback_fin(); -out_wkq: - au_wkq_fin(); -out_procfs: - au_procfs_fin(); -out_sysaufs: - sysaufs_fin(); - au_dy_fin(); -out: - return err; -} - -static void __exit aufs_exit(void) -{ - unregister_filesystem(&aufs_fs_type); - au_cache_fin(); - au_sysrq_fin(); - au_hnotify_fin(); - au_loopback_fin(); - au_wkq_fin(); - au_procfs_fin(); - sysaufs_fin(); - au_dy_fin(); -} - -module_init(aufs_init); -module_exit(aufs_exit); diff --git a/fs/aufs/module.h b/fs/aufs/module.h deleted file mode 100644 index c0a29d49d..000000000 --- a/fs/aufs/module.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * module initialization and module-global - */ - -#ifndef __AUFS_MODULE_H__ -#define __AUFS_MODULE_H__ - -#ifdef __KERNEL__ - -#include <linux/slab.h> - -struct path; -struct seq_file; - -/* module parameters */ -extern int sysaufs_brs; - -/* ---------------------------------------------------------------------- */ - -extern int au_dir_roflags; - -enum { - AuLcNonDir_FIINFO, - AuLcNonDir_DIINFO, - AuLcNonDir_IIINFO, - - AuLcDir_FIINFO, - AuLcDir_DIINFO, - AuLcDir_IIINFO, - - AuLcSymlink_DIINFO, - AuLcSymlink_IIINFO, - - AuLcKey_Last -}; -extern struct lock_class_key au_lc_key[AuLcKey_Last]; - -void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp); -int au_seq_path(struct seq_file *seq, struct path *path); - -#ifdef CONFIG_PROC_FS -/* procfs.c */ -int __init au_procfs_init(void); -void au_procfs_fin(void); -#else -AuStubInt0(au_procfs_init, void); -AuStubVoid(au_procfs_fin, void); -#endif - -/* ---------------------------------------------------------------------- */ - -/* kmem cache */ -enum { - AuCache_DINFO, - AuCache_ICNTNR, - AuCache_FINFO, - AuCache_VDIR, - AuCache_DEHSTR, - AuCache_HNOTIFY, /* must be last */ - AuCache_Last -}; - -#define AuCacheFlags (SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD) -#define AuCache(type) KMEM_CACHE(type, AuCacheFlags) -#define AuCacheCtor(type, ctor) \ - kmem_cache_create(#type, sizeof(struct type), \ - __alignof__(struct type), AuCacheFlags, ctor) - -extern struct kmem_cache *au_cachep[]; - -#define AuCacheFuncs(name, index) \ -static inline struct au_##name *au_cache_alloc_##name(void) \ -{ return kmem_cache_alloc(au_cachep[AuCache_##index], GFP_NOFS); } \ -static inline void au_cache_free_##name(struct au_##name *p) \ -{ kmem_cache_free(au_cachep[AuCache_##index], p); } - -AuCacheFuncs(dinfo, DINFO); -AuCacheFuncs(icntnr, ICNTNR); -AuCacheFuncs(finfo, FINFO); -AuCacheFuncs(vdir, VDIR); -AuCacheFuncs(vdir_dehstr, DEHSTR); -#ifdef CONFIG_AUFS_HNOTIFY -AuCacheFuncs(hnotify, HNOTIFY); -#endif - -#endif /* __KERNEL__ */ -#endif /* __AUFS_MODULE_H__ */ diff --git a/fs/aufs/mvdown.c b/fs/aufs/mvdown.c deleted file mode 100644 index 25e3a80b9..000000000 --- a/fs/aufs/mvdown.c +++ /dev/null @@ -1,694 +0,0 @@ -/* - * Copyright (C) 2011-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * move-down, opposite of copy-up - */ - -#include "aufs.h" - -struct au_mvd_args { - struct { - struct super_block *h_sb; - struct dentry *h_parent; - struct au_hinode *hdir; - struct inode *h_dir, *h_inode; - struct au_pin pin; - } info[AUFS_MVDOWN_NARRAY]; - - struct aufs_mvdown mvdown; - struct dentry *dentry, *parent; - struct inode *inode, *dir; - struct super_block *sb; - aufs_bindex_t bopq, bwh, bfound; - unsigned char rename_lock; -}; - -#define mvd_errno mvdown.au_errno -#define mvd_bsrc mvdown.stbr[AUFS_MVDOWN_UPPER].bindex -#define mvd_src_brid mvdown.stbr[AUFS_MVDOWN_UPPER].brid -#define mvd_bdst mvdown.stbr[AUFS_MVDOWN_LOWER].bindex -#define mvd_dst_brid mvdown.stbr[AUFS_MVDOWN_LOWER].brid - -#define mvd_h_src_sb info[AUFS_MVDOWN_UPPER].h_sb -#define mvd_h_src_parent info[AUFS_MVDOWN_UPPER].h_parent -#define mvd_hdir_src info[AUFS_MVDOWN_UPPER].hdir -#define mvd_h_src_dir info[AUFS_MVDOWN_UPPER].h_dir -#define mvd_h_src_inode info[AUFS_MVDOWN_UPPER].h_inode -#define mvd_pin_src info[AUFS_MVDOWN_UPPER].pin - -#define mvd_h_dst_sb info[AUFS_MVDOWN_LOWER].h_sb -#define mvd_h_dst_parent info[AUFS_MVDOWN_LOWER].h_parent -#define mvd_hdir_dst info[AUFS_MVDOWN_LOWER].hdir -#define mvd_h_dst_dir info[AUFS_MVDOWN_LOWER].h_dir -#define mvd_h_dst_inode info[AUFS_MVDOWN_LOWER].h_inode -#define mvd_pin_dst info[AUFS_MVDOWN_LOWER].pin - -#define AU_MVD_PR(flag, ...) do { \ - if (flag) \ - pr_err(__VA_ARGS__); \ - } while (0) - -static int find_lower_writable(struct au_mvd_args *a) -{ - struct super_block *sb; - aufs_bindex_t bindex, bend; - struct au_branch *br; - - sb = a->sb; - bindex = a->mvd_bsrc; - bend = au_sbend(sb); - if (a->mvdown.flags & AUFS_MVDOWN_FHSM_LOWER) - for (bindex++; bindex <= bend; bindex++) { - br = au_sbr(sb, bindex); - if (au_br_fhsm(br->br_perm) - && (!(au_br_sb(br)->s_flags & MS_RDONLY))) - return bindex; - } - else if (!(a->mvdown.flags & AUFS_MVDOWN_ROLOWER)) - for (bindex++; bindex <= bend; bindex++) { - br = au_sbr(sb, bindex); - if (!au_br_rdonly(br)) - return bindex; - } - else - for (bindex++; bindex <= bend; bindex++) { - br = au_sbr(sb, bindex); - if (!(au_br_sb(br)->s_flags & MS_RDONLY)) { - if (au_br_rdonly(br)) - a->mvdown.flags - |= AUFS_MVDOWN_ROLOWER_R; - return bindex; - } - } - - return -1; -} - -/* make the parent dir on bdst */ -static int au_do_mkdir(const unsigned char dmsg, struct au_mvd_args *a) -{ - int err; - - err = 0; - a->mvd_hdir_src = au_hi(a->dir, a->mvd_bsrc); - a->mvd_hdir_dst = au_hi(a->dir, a->mvd_bdst); - a->mvd_h_src_parent = au_h_dptr(a->parent, a->mvd_bsrc); - a->mvd_h_dst_parent = NULL; - if (au_dbend(a->parent) >= a->mvd_bdst) - a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); - if (!a->mvd_h_dst_parent) { - err = au_cpdown_dirs(a->dentry, a->mvd_bdst); - if (unlikely(err)) { - AU_MVD_PR(dmsg, "cpdown_dirs failed\n"); - goto out; - } - a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); - } - -out: - AuTraceErr(err); - return err; -} - -/* lock them all */ -static int au_do_lock(const unsigned char dmsg, struct au_mvd_args *a) -{ - int err; - struct dentry *h_trap; - - a->mvd_h_src_sb = au_sbr_sb(a->sb, a->mvd_bsrc); - a->mvd_h_dst_sb = au_sbr_sb(a->sb, a->mvd_bdst); - err = au_pin(&a->mvd_pin_dst, a->dentry, a->mvd_bdst, - au_opt_udba(a->sb), - AuPin_MNT_WRITE | AuPin_DI_LOCKED); - AuTraceErr(err); - if (unlikely(err)) { - AU_MVD_PR(dmsg, "pin_dst failed\n"); - goto out; - } - - if (a->mvd_h_src_sb != a->mvd_h_dst_sb) { - a->rename_lock = 0; - au_pin_init(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, - AuLsc_DI_PARENT, AuLsc_I_PARENT3, - au_opt_udba(a->sb), - AuPin_MNT_WRITE | AuPin_DI_LOCKED); - err = au_do_pin(&a->mvd_pin_src); - AuTraceErr(err); - a->mvd_h_src_dir = d_inode(a->mvd_h_src_parent); - if (unlikely(err)) { - AU_MVD_PR(dmsg, "pin_src failed\n"); - goto out_dst; - } - goto out; /* success */ - } - - a->rename_lock = 1; - au_pin_hdir_unlock(&a->mvd_pin_dst); - err = au_pin(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, - au_opt_udba(a->sb), - AuPin_MNT_WRITE | AuPin_DI_LOCKED); - AuTraceErr(err); - a->mvd_h_src_dir = d_inode(a->mvd_h_src_parent); - if (unlikely(err)) { - AU_MVD_PR(dmsg, "pin_src failed\n"); - au_pin_hdir_lock(&a->mvd_pin_dst); - goto out_dst; - } - au_pin_hdir_unlock(&a->mvd_pin_src); - h_trap = vfsub_lock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, - a->mvd_h_dst_parent, a->mvd_hdir_dst); - if (h_trap) { - err = (h_trap != a->mvd_h_src_parent); - if (err) - err = (h_trap != a->mvd_h_dst_parent); - } - BUG_ON(err); /* it should never happen */ - if (unlikely(a->mvd_h_src_dir != au_pinned_h_dir(&a->mvd_pin_src))) { - err = -EBUSY; - AuTraceErr(err); - vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, - a->mvd_h_dst_parent, a->mvd_hdir_dst); - au_pin_hdir_lock(&a->mvd_pin_src); - au_unpin(&a->mvd_pin_src); - au_pin_hdir_lock(&a->mvd_pin_dst); - goto out_dst; - } - goto out; /* success */ - -out_dst: - au_unpin(&a->mvd_pin_dst); -out: - AuTraceErr(err); - return err; -} - -static void au_do_unlock(const unsigned char dmsg, struct au_mvd_args *a) -{ - if (!a->rename_lock) - au_unpin(&a->mvd_pin_src); - else { - vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, - a->mvd_h_dst_parent, a->mvd_hdir_dst); - au_pin_hdir_lock(&a->mvd_pin_src); - au_unpin(&a->mvd_pin_src); - au_pin_hdir_lock(&a->mvd_pin_dst); - } - au_unpin(&a->mvd_pin_dst); -} - -/* copy-down the file */ -static int au_do_cpdown(const unsigned char dmsg, struct au_mvd_args *a) -{ - int err; - struct au_cp_generic cpg = { - .dentry = a->dentry, - .bdst = a->mvd_bdst, - .bsrc = a->mvd_bsrc, - .len = -1, - .pin = &a->mvd_pin_dst, - .flags = AuCpup_DTIME | AuCpup_HOPEN - }; - - AuDbg("b%d, b%d\n", cpg.bsrc, cpg.bdst); - if (a->mvdown.flags & AUFS_MVDOWN_OWLOWER) - au_fset_cpup(cpg.flags, OVERWRITE); - if (a->mvdown.flags & AUFS_MVDOWN_ROLOWER) - au_fset_cpup(cpg.flags, RWDST); - err = au_sio_cpdown_simple(&cpg); - if (unlikely(err)) - AU_MVD_PR(dmsg, "cpdown failed\n"); - - AuTraceErr(err); - return err; -} - -/* - * unlink the whiteout on bdst if exist which may be created by UDBA while we - * were sleeping - */ -static int au_do_unlink_wh(const unsigned char dmsg, struct au_mvd_args *a) -{ - int err; - struct path h_path; - struct au_branch *br; - struct inode *delegated; - - br = au_sbr(a->sb, a->mvd_bdst); - h_path.dentry = au_wh_lkup(a->mvd_h_dst_parent, &a->dentry->d_name, br); - err = PTR_ERR(h_path.dentry); - if (IS_ERR(h_path.dentry)) { - AU_MVD_PR(dmsg, "wh_lkup failed\n"); - goto out; - } - - err = 0; - if (d_is_positive(h_path.dentry)) { - h_path.mnt = au_br_mnt(br); - delegated = NULL; - err = vfsub_unlink(d_inode(a->mvd_h_dst_parent), &h_path, - &delegated, /*force*/0); - if (unlikely(err == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal unlink\n"); - iput(delegated); - } - if (unlikely(err)) - AU_MVD_PR(dmsg, "wh_unlink failed\n"); - } - dput(h_path.dentry); - -out: - AuTraceErr(err); - return err; -} - -/* - * unlink the topmost h_dentry - */ -static int au_do_unlink(const unsigned char dmsg, struct au_mvd_args *a) -{ - int err; - struct path h_path; - struct inode *delegated; - - h_path.mnt = au_sbr_mnt(a->sb, a->mvd_bsrc); - h_path.dentry = au_h_dptr(a->dentry, a->mvd_bsrc); - delegated = NULL; - err = vfsub_unlink(a->mvd_h_src_dir, &h_path, &delegated, /*force*/0); - if (unlikely(err == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal unlink\n"); - iput(delegated); - } - if (unlikely(err)) - AU_MVD_PR(dmsg, "unlink failed\n"); - - AuTraceErr(err); - return err; -} - -/* Since mvdown succeeded, we ignore an error of this function */ -static void au_do_stfs(const unsigned char dmsg, struct au_mvd_args *a) -{ - int err; - struct au_branch *br; - - a->mvdown.flags |= AUFS_MVDOWN_STFS_FAILED; - br = au_sbr(a->sb, a->mvd_bsrc); - err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_UPPER].stfs); - if (!err) { - br = au_sbr(a->sb, a->mvd_bdst); - a->mvdown.stbr[AUFS_MVDOWN_LOWER].brid = br->br_id; - err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_LOWER].stfs); - } - if (!err) - a->mvdown.flags &= ~AUFS_MVDOWN_STFS_FAILED; - else - AU_MVD_PR(dmsg, "statfs failed (%d), ignored\n", err); -} - -/* - * copy-down the file and unlink the bsrc file. - * - unlink the bdst whout if exist - * - copy-down the file (with whtmp name and rename) - * - unlink the bsrc file - */ -static int au_do_mvdown(const unsigned char dmsg, struct au_mvd_args *a) -{ - int err; - - err = au_do_mkdir(dmsg, a); - if (!err) - err = au_do_lock(dmsg, a); - if (unlikely(err)) - goto out; - - /* - * do not revert the activities we made on bdst since they should be - * harmless in aufs. - */ - - err = au_do_cpdown(dmsg, a); - if (!err) - err = au_do_unlink_wh(dmsg, a); - if (!err && !(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) - err = au_do_unlink(dmsg, a); - if (unlikely(err)) - goto out_unlock; - - AuDbg("%pd2, 0x%x, %d --> %d\n", - a->dentry, a->mvdown.flags, a->mvd_bsrc, a->mvd_bdst); - if (find_lower_writable(a) < 0) - a->mvdown.flags |= AUFS_MVDOWN_BOTTOM; - - if (a->mvdown.flags & AUFS_MVDOWN_STFS) - au_do_stfs(dmsg, a); - - /* maintain internal array */ - if (!(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) { - au_set_h_dptr(a->dentry, a->mvd_bsrc, NULL); - au_set_dbstart(a->dentry, a->mvd_bdst); - au_set_h_iptr(a->inode, a->mvd_bsrc, NULL, /*flags*/0); - au_set_ibstart(a->inode, a->mvd_bdst); - } - if (au_dbend(a->dentry) < a->mvd_bdst) - au_set_dbend(a->dentry, a->mvd_bdst); - if (au_ibend(a->inode) < a->mvd_bdst) - au_set_ibend(a->inode, a->mvd_bdst); - -out_unlock: - au_do_unlock(dmsg, a); -out: - AuTraceErr(err); - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* make sure the file is idle */ -static int au_mvd_args_busy(const unsigned char dmsg, struct au_mvd_args *a) -{ - int err, plinked; - - err = 0; - plinked = !!au_opt_test(au_mntflags(a->sb), PLINK); - if (au_dbstart(a->dentry) == a->mvd_bsrc - && au_dcount(a->dentry) == 1 - && atomic_read(&a->inode->i_count) == 1 - /* && a->mvd_h_src_inode->i_nlink == 1 */ - && (!plinked || !au_plink_test(a->inode)) - && a->inode->i_nlink == 1) - goto out; - - err = -EBUSY; - AU_MVD_PR(dmsg, - "b%d, d{b%d, c%d?}, i{c%d?, l%u}, hi{l%u}, p{%d, %d}\n", - a->mvd_bsrc, au_dbstart(a->dentry), au_dcount(a->dentry), - atomic_read(&a->inode->i_count), a->inode->i_nlink, - a->mvd_h_src_inode->i_nlink, - plinked, plinked ? au_plink_test(a->inode) : 0); - -out: - AuTraceErr(err); - return err; -} - -/* make sure the parent dir is fine */ -static int au_mvd_args_parent(const unsigned char dmsg, - struct au_mvd_args *a) -{ - int err; - aufs_bindex_t bindex; - - err = 0; - if (unlikely(au_alive_dir(a->parent))) { - err = -ENOENT; - AU_MVD_PR(dmsg, "parent dir is dead\n"); - goto out; - } - - a->bopq = au_dbdiropq(a->parent); - bindex = au_wbr_nonopq(a->dentry, a->mvd_bdst); - AuDbg("b%d\n", bindex); - if (unlikely((bindex >= 0 && bindex < a->mvd_bdst) - || (a->bopq != -1 && a->bopq < a->mvd_bdst))) { - err = -EINVAL; - a->mvd_errno = EAU_MVDOWN_OPAQUE; - AU_MVD_PR(dmsg, "ancestor is opaque b%d, b%d\n", - a->bopq, a->mvd_bdst); - } - -out: - AuTraceErr(err); - return err; -} - -static int au_mvd_args_intermediate(const unsigned char dmsg, - struct au_mvd_args *a) -{ - int err; - struct au_dinfo *dinfo, *tmp; - - /* lookup the next lower positive entry */ - err = -ENOMEM; - tmp = au_di_alloc(a->sb, AuLsc_DI_TMP); - if (unlikely(!tmp)) - goto out; - - a->bfound = -1; - a->bwh = -1; - dinfo = au_di(a->dentry); - au_di_cp(tmp, dinfo); - au_di_swap(tmp, dinfo); - - /* returns the number of positive dentries */ - err = au_lkup_dentry(a->dentry, a->mvd_bsrc + 1, /*type*/0); - if (!err) - a->bwh = au_dbwh(a->dentry); - else if (err > 0) - a->bfound = au_dbstart(a->dentry); - - au_di_swap(tmp, dinfo); - au_rw_write_unlock(&tmp->di_rwsem); - au_di_free(tmp); - if (unlikely(err < 0)) - AU_MVD_PR(dmsg, "failed look-up lower\n"); - - /* - * here, we have these cases. - * bfound == -1 - * no positive dentry under bsrc. there are more sub-cases. - * bwh < 0 - * there no whiteout, we can safely move-down. - * bwh <= bsrc - * impossible - * bsrc < bwh && bwh < bdst - * there is a whiteout on RO branch. cannot proceed. - * bwh == bdst - * there is a whiteout on the RW target branch. it should - * be removed. - * bdst < bwh - * there is a whiteout somewhere unrelated branch. - * -1 < bfound && bfound <= bsrc - * impossible. - * bfound < bdst - * found, but it is on RO branch between bsrc and bdst. cannot - * proceed. - * bfound == bdst - * found, replace it if AUFS_MVDOWN_FORCE is set. otherwise return - * error. - * bdst < bfound - * found, after we create the file on bdst, it will be hidden. - */ - - AuDebugOn(a->bfound == -1 - && a->bwh != -1 - && a->bwh <= a->mvd_bsrc); - AuDebugOn(-1 < a->bfound - && a->bfound <= a->mvd_bsrc); - - err = -EINVAL; - if (a->bfound == -1 - && a->mvd_bsrc < a->bwh - && a->bwh != -1 - && a->bwh < a->mvd_bdst) { - a->mvd_errno = EAU_MVDOWN_WHITEOUT; - AU_MVD_PR(dmsg, "bsrc %d, bdst %d, bfound %d, bwh %d\n", - a->mvd_bsrc, a->mvd_bdst, a->bfound, a->bwh); - goto out; - } else if (a->bfound != -1 && a->bfound < a->mvd_bdst) { - a->mvd_errno = EAU_MVDOWN_UPPER; - AU_MVD_PR(dmsg, "bdst %d, bfound %d\n", - a->mvd_bdst, a->bfound); - goto out; - } - - err = 0; /* success */ - -out: - AuTraceErr(err); - return err; -} - -static int au_mvd_args_exist(const unsigned char dmsg, struct au_mvd_args *a) -{ - int err; - - err = 0; - if (!(a->mvdown.flags & AUFS_MVDOWN_OWLOWER) - && a->bfound == a->mvd_bdst) - err = -EEXIST; - AuTraceErr(err); - return err; -} - -static int au_mvd_args(const unsigned char dmsg, struct au_mvd_args *a) -{ - int err; - struct au_branch *br; - - err = -EISDIR; - if (unlikely(S_ISDIR(a->inode->i_mode))) - goto out; - - err = -EINVAL; - if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_UPPER)) - a->mvd_bsrc = au_ibstart(a->inode); - else { - a->mvd_bsrc = au_br_index(a->sb, a->mvd_src_brid); - if (unlikely(a->mvd_bsrc < 0 - || (a->mvd_bsrc < au_dbstart(a->dentry) - || au_dbend(a->dentry) < a->mvd_bsrc - || !au_h_dptr(a->dentry, a->mvd_bsrc)) - || (a->mvd_bsrc < au_ibstart(a->inode) - || au_ibend(a->inode) < a->mvd_bsrc - || !au_h_iptr(a->inode, a->mvd_bsrc)))) { - a->mvd_errno = EAU_MVDOWN_NOUPPER; - AU_MVD_PR(dmsg, "no upper\n"); - goto out; - } - } - if (unlikely(a->mvd_bsrc == au_sbend(a->sb))) { - a->mvd_errno = EAU_MVDOWN_BOTTOM; - AU_MVD_PR(dmsg, "on the bottom\n"); - goto out; - } - a->mvd_h_src_inode = au_h_iptr(a->inode, a->mvd_bsrc); - br = au_sbr(a->sb, a->mvd_bsrc); - err = au_br_rdonly(br); - if (!(a->mvdown.flags & AUFS_MVDOWN_ROUPPER)) { - if (unlikely(err)) - goto out; - } else if (!(vfsub_native_ro(a->mvd_h_src_inode) - || IS_APPEND(a->mvd_h_src_inode))) { - if (err) - a->mvdown.flags |= AUFS_MVDOWN_ROUPPER_R; - /* go on */ - } else - goto out; - - err = -EINVAL; - if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_LOWER)) { - a->mvd_bdst = find_lower_writable(a); - if (unlikely(a->mvd_bdst < 0)) { - a->mvd_errno = EAU_MVDOWN_BOTTOM; - AU_MVD_PR(dmsg, "no writable lower branch\n"); - goto out; - } - } else { - a->mvd_bdst = au_br_index(a->sb, a->mvd_dst_brid); - if (unlikely(a->mvd_bdst < 0 - || au_sbend(a->sb) < a->mvd_bdst)) { - a->mvd_errno = EAU_MVDOWN_NOLOWERBR; - AU_MVD_PR(dmsg, "no lower brid\n"); - goto out; - } - } - - err = au_mvd_args_busy(dmsg, a); - if (!err) - err = au_mvd_args_parent(dmsg, a); - if (!err) - err = au_mvd_args_intermediate(dmsg, a); - if (!err) - err = au_mvd_args_exist(dmsg, a); - if (!err) - AuDbg("b%d, b%d\n", a->mvd_bsrc, a->mvd_bdst); - -out: - AuTraceErr(err); - return err; -} - -int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *uarg) -{ - int err, e; - unsigned char dmsg; - struct au_mvd_args *args; - - err = -EPERM; - if (unlikely(!capable(CAP_SYS_ADMIN))) - goto out; - - err = -ENOMEM; - args = kmalloc(sizeof(*args), GFP_NOFS); - if (unlikely(!args)) - goto out; - - err = copy_from_user(&args->mvdown, uarg, sizeof(args->mvdown)); - if (!err) - err = !access_ok(VERIFY_WRITE, uarg, sizeof(*uarg)); - if (unlikely(err)) { - err = -EFAULT; - AuTraceErr(err); - goto out_free; - } - AuDbg("flags 0x%x\n", args->mvdown.flags); - args->mvdown.flags &= ~(AUFS_MVDOWN_ROLOWER_R | AUFS_MVDOWN_ROUPPER_R); - args->mvdown.au_errno = 0; - args->dentry = dentry; - args->inode = d_inode(dentry); - args->sb = dentry->d_sb; - - err = -ENOENT; - dmsg = !!(args->mvdown.flags & AUFS_MVDOWN_DMSG); - args->parent = dget_parent(dentry); - args->dir = d_inode(args->parent); - mutex_lock_nested(&args->dir->i_mutex, I_MUTEX_PARENT); - dput(args->parent); - if (unlikely(args->parent != dentry->d_parent)) { - AU_MVD_PR(dmsg, "parent dir is moved\n"); - goto out_dir; - } - - mutex_lock_nested(&args->inode->i_mutex, I_MUTEX_CHILD); - err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH); - if (unlikely(err)) - goto out_inode; - - di_write_lock_parent(args->parent); - err = au_mvd_args(dmsg, args); - if (unlikely(err)) - goto out_parent; - - err = au_do_mvdown(dmsg, args); - if (unlikely(err)) - goto out_parent; - - au_cpup_attr_timesizes(args->dir); - au_cpup_attr_timesizes(args->inode); - au_cpup_igen(args->inode, au_h_iptr(args->inode, args->mvd_bdst)); - /* au_digen_dec(dentry); */ - -out_parent: - di_write_unlock(args->parent); - aufs_read_unlock(dentry, AuLock_DW); -out_inode: - mutex_unlock(&args->inode->i_mutex); -out_dir: - mutex_unlock(&args->dir->i_mutex); -out_free: - e = copy_to_user(uarg, &args->mvdown, sizeof(args->mvdown)); - if (unlikely(e)) - err = -EFAULT; - kfree(args); -out: - AuTraceErr(err); - return err; -} diff --git a/fs/aufs/opts.c b/fs/aufs/opts.c deleted file mode 100644 index 4e8e2e1ff..000000000 --- a/fs/aufs/opts.c +++ /dev/null @@ -1,1835 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * mount options/flags - */ - -#include <linux/namei.h> -#include <linux/types.h> /* a distribution requires */ -#include <linux/parser.h> -#include "aufs.h" - -/* ---------------------------------------------------------------------- */ - -enum { - Opt_br, - Opt_add, Opt_del, Opt_mod, Opt_append, Opt_prepend, - Opt_idel, Opt_imod, - Opt_dirwh, Opt_rdcache, Opt_rdblk, Opt_rdhash, - Opt_rdblk_def, Opt_rdhash_def, - Opt_xino, Opt_noxino, - Opt_trunc_xino, Opt_trunc_xino_v, Opt_notrunc_xino, - Opt_trunc_xino_path, Opt_itrunc_xino, - Opt_trunc_xib, Opt_notrunc_xib, - Opt_shwh, Opt_noshwh, - Opt_plink, Opt_noplink, Opt_list_plink, - Opt_udba, - Opt_dio, Opt_nodio, - Opt_diropq_a, Opt_diropq_w, - Opt_warn_perm, Opt_nowarn_perm, - Opt_wbr_copyup, Opt_wbr_create, - Opt_fhsm_sec, - Opt_verbose, Opt_noverbose, - Opt_sum, Opt_nosum, Opt_wsum, - Opt_dirperm1, Opt_nodirperm1, - Opt_acl, Opt_noacl, - Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err -}; - -static match_table_t options = { - {Opt_br, "br=%s"}, - {Opt_br, "br:%s"}, - - {Opt_add, "add=%d:%s"}, - {Opt_add, "add:%d:%s"}, - {Opt_add, "ins=%d:%s"}, - {Opt_add, "ins:%d:%s"}, - {Opt_append, "append=%s"}, - {Opt_append, "append:%s"}, - {Opt_prepend, "prepend=%s"}, - {Opt_prepend, "prepend:%s"}, - - {Opt_del, "del=%s"}, - {Opt_del, "del:%s"}, - /* {Opt_idel, "idel:%d"}, */ - {Opt_mod, "mod=%s"}, - {Opt_mod, "mod:%s"}, - /* {Opt_imod, "imod:%d:%s"}, */ - - {Opt_dirwh, "dirwh=%d"}, - - {Opt_xino, "xino=%s"}, - {Opt_noxino, "noxino"}, - {Opt_trunc_xino, "trunc_xino"}, - {Opt_trunc_xino_v, "trunc_xino_v=%d:%d"}, - {Opt_notrunc_xino, "notrunc_xino"}, - {Opt_trunc_xino_path, "trunc_xino=%s"}, - {Opt_itrunc_xino, "itrunc_xino=%d"}, - /* {Opt_zxino, "zxino=%s"}, */ - {Opt_trunc_xib, "trunc_xib"}, - {Opt_notrunc_xib, "notrunc_xib"}, - -#ifdef CONFIG_PROC_FS - {Opt_plink, "plink"}, -#else - {Opt_ignore_silent, "plink"}, -#endif - - {Opt_noplink, "noplink"}, - -#ifdef CONFIG_AUFS_DEBUG - {Opt_list_plink, "list_plink"}, -#endif - - {Opt_udba, "udba=%s"}, - - {Opt_dio, "dio"}, - {Opt_nodio, "nodio"}, - -#ifdef CONFIG_AUFS_FHSM - {Opt_fhsm_sec, "fhsm_sec=%d"}, -#else - {Opt_ignore_silent, "fhsm_sec=%d"}, -#endif - - {Opt_diropq_a, "diropq=always"}, - {Opt_diropq_a, "diropq=a"}, - {Opt_diropq_w, "diropq=whiteouted"}, - {Opt_diropq_w, "diropq=w"}, - - {Opt_warn_perm, "warn_perm"}, - {Opt_nowarn_perm, "nowarn_perm"}, - - /* keep them temporary */ - {Opt_ignore_silent, "nodlgt"}, - {Opt_ignore_silent, "clean_plink"}, - -#ifdef CONFIG_AUFS_SHWH - {Opt_shwh, "shwh"}, -#endif - {Opt_noshwh, "noshwh"}, - - {Opt_dirperm1, "dirperm1"}, - {Opt_nodirperm1, "nodirperm1"}, - - {Opt_verbose, "verbose"}, - {Opt_verbose, "v"}, - {Opt_noverbose, "noverbose"}, - {Opt_noverbose, "quiet"}, - {Opt_noverbose, "q"}, - {Opt_noverbose, "silent"}, - - {Opt_sum, "sum"}, - {Opt_nosum, "nosum"}, - {Opt_wsum, "wsum"}, - - {Opt_rdcache, "rdcache=%d"}, - {Opt_rdblk, "rdblk=%d"}, - {Opt_rdblk_def, "rdblk=def"}, - {Opt_rdhash, "rdhash=%d"}, - {Opt_rdhash_def, "rdhash=def"}, - - {Opt_wbr_create, "create=%s"}, - {Opt_wbr_create, "create_policy=%s"}, - {Opt_wbr_copyup, "cpup=%s"}, - {Opt_wbr_copyup, "copyup=%s"}, - {Opt_wbr_copyup, "copyup_policy=%s"}, - - /* generic VFS flag */ -#ifdef CONFIG_FS_POSIX_ACL - {Opt_acl, "acl"}, - {Opt_noacl, "noacl"}, -#else - {Opt_ignore_silent, "acl"}, - {Opt_ignore_silent, "noacl"}, -#endif - - /* internal use for the scripts */ - {Opt_ignore_silent, "si=%s"}, - - {Opt_br, "dirs=%s"}, - {Opt_ignore, "debug=%d"}, - {Opt_ignore, "delete=whiteout"}, - {Opt_ignore, "delete=all"}, - {Opt_ignore, "imap=%s"}, - - /* temporary workaround, due to old mount(8)? */ - {Opt_ignore_silent, "relatime"}, - - {Opt_err, NULL} -}; - -/* ---------------------------------------------------------------------- */ - -static const char *au_parser_pattern(int val, match_table_t tbl) -{ - struct match_token *p; - - p = tbl; - while (p->pattern) { - if (p->token == val) - return p->pattern; - p++; - } - BUG(); - return "??"; -} - -static const char *au_optstr(int *val, match_table_t tbl) -{ - struct match_token *p; - int v; - - v = *val; - if (!v) - goto out; - p = tbl; - while (p->pattern) { - if (p->token - && (v & p->token) == p->token) { - *val &= ~p->token; - return p->pattern; - } - p++; - } - -out: - return NULL; -} - -/* ---------------------------------------------------------------------- */ - -static match_table_t brperm = { - {AuBrPerm_RO, AUFS_BRPERM_RO}, - {AuBrPerm_RR, AUFS_BRPERM_RR}, - {AuBrPerm_RW, AUFS_BRPERM_RW}, - {0, NULL} -}; - -static match_table_t brattr = { - /* general */ - {AuBrAttr_COO_REG, AUFS_BRATTR_COO_REG}, - {AuBrAttr_COO_ALL, AUFS_BRATTR_COO_ALL}, - /* 'unpin' attrib is meaningless since linux-3.18-rc1 */ - {AuBrAttr_UNPIN, AUFS_BRATTR_UNPIN}, -#ifdef CONFIG_AUFS_FHSM - {AuBrAttr_FHSM, AUFS_BRATTR_FHSM}, -#endif -#ifdef CONFIG_AUFS_XATTR - {AuBrAttr_ICEX, AUFS_BRATTR_ICEX}, - {AuBrAttr_ICEX_SEC, AUFS_BRATTR_ICEX_SEC}, - {AuBrAttr_ICEX_SYS, AUFS_BRATTR_ICEX_SYS}, - {AuBrAttr_ICEX_TR, AUFS_BRATTR_ICEX_TR}, - {AuBrAttr_ICEX_USR, AUFS_BRATTR_ICEX_USR}, - {AuBrAttr_ICEX_OTH, AUFS_BRATTR_ICEX_OTH}, -#endif - - /* ro/rr branch */ - {AuBrRAttr_WH, AUFS_BRRATTR_WH}, - - /* rw branch */ - {AuBrWAttr_MOO, AUFS_BRWATTR_MOO}, - {AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH}, - - {0, NULL} -}; - -static int br_attr_val(char *str, match_table_t table, substring_t args[]) -{ - int attr, v; - char *p; - - attr = 0; - do { - p = strchr(str, '+'); - if (p) - *p = 0; - v = match_token(str, table, args); - if (v) { - if (v & AuBrAttr_CMOO_Mask) - attr &= ~AuBrAttr_CMOO_Mask; - attr |= v; - } else { - if (p) - *p = '+'; - pr_warn("ignored branch attribute %s\n", str); - break; - } - if (p) - str = p + 1; - } while (p); - - return attr; -} - -static int au_do_optstr_br_attr(au_br_perm_str_t *str, int perm) -{ - int sz; - const char *p; - char *q; - - q = str->a; - *q = 0; - p = au_optstr(&perm, brattr); - if (p) { - sz = strlen(p); - memcpy(q, p, sz + 1); - q += sz; - } else - goto out; - - do { - p = au_optstr(&perm, brattr); - if (p) { - *q++ = '+'; - sz = strlen(p); - memcpy(q, p, sz + 1); - q += sz; - } - } while (p); - -out: - return q - str->a; -} - -static int noinline_for_stack br_perm_val(char *perm) -{ - int val, bad, sz; - char *p; - substring_t args[MAX_OPT_ARGS]; - au_br_perm_str_t attr; - - p = strchr(perm, '+'); - if (p) - *p = 0; - val = match_token(perm, brperm, args); - if (!val) { - if (p) - *p = '+'; - pr_warn("ignored branch permission %s\n", perm); - val = AuBrPerm_RO; - goto out; - } - if (!p) - goto out; - - val |= br_attr_val(p + 1, brattr, args); - - bad = 0; - switch (val & AuBrPerm_Mask) { - case AuBrPerm_RO: - case AuBrPerm_RR: - bad = val & AuBrWAttr_Mask; - val &= ~AuBrWAttr_Mask; - break; - case AuBrPerm_RW: - bad = val & AuBrRAttr_Mask; - val &= ~AuBrRAttr_Mask; - break; - } - - /* - * 'unpin' attrib becomes meaningless since linux-3.18-rc1, but aufs - * does not treat it as an error, just warning. - * this is a tiny guard for the user operation. - */ - if (val & AuBrAttr_UNPIN) { - bad |= AuBrAttr_UNPIN; - val &= ~AuBrAttr_UNPIN; - } - - if (unlikely(bad)) { - sz = au_do_optstr_br_attr(&attr, bad); - AuDebugOn(!sz); - pr_warn("ignored branch attribute %s\n", attr.a); - } - -out: - return val; -} - -void au_optstr_br_perm(au_br_perm_str_t *str, int perm) -{ - au_br_perm_str_t attr; - const char *p; - char *q; - int sz; - - q = str->a; - p = au_optstr(&perm, brperm); - AuDebugOn(!p || !*p); - sz = strlen(p); - memcpy(q, p, sz + 1); - q += sz; - - sz = au_do_optstr_br_attr(&attr, perm); - if (sz) { - *q++ = '+'; - memcpy(q, attr.a, sz + 1); - } - - AuDebugOn(strlen(str->a) >= sizeof(str->a)); -} - -/* ---------------------------------------------------------------------- */ - -static match_table_t udbalevel = { - {AuOpt_UDBA_REVAL, "reval"}, - {AuOpt_UDBA_NONE, "none"}, -#ifdef CONFIG_AUFS_HNOTIFY - {AuOpt_UDBA_HNOTIFY, "notify"}, /* abstraction */ -#ifdef CONFIG_AUFS_HFSNOTIFY - {AuOpt_UDBA_HNOTIFY, "fsnotify"}, -#endif -#endif - {-1, NULL} -}; - -static int noinline_for_stack udba_val(char *str) -{ - substring_t args[MAX_OPT_ARGS]; - - return match_token(str, udbalevel, args); -} - -const char *au_optstr_udba(int udba) -{ - return au_parser_pattern(udba, udbalevel); -} - -/* ---------------------------------------------------------------------- */ - -static match_table_t au_wbr_create_policy = { - {AuWbrCreate_TDP, "tdp"}, - {AuWbrCreate_TDP, "top-down-parent"}, - {AuWbrCreate_RR, "rr"}, - {AuWbrCreate_RR, "round-robin"}, - {AuWbrCreate_MFS, "mfs"}, - {AuWbrCreate_MFS, "most-free-space"}, - {AuWbrCreate_MFSV, "mfs:%d"}, - {AuWbrCreate_MFSV, "most-free-space:%d"}, - - {AuWbrCreate_MFSRR, "mfsrr:%d"}, - {AuWbrCreate_MFSRRV, "mfsrr:%d:%d"}, - {AuWbrCreate_PMFS, "pmfs"}, - {AuWbrCreate_PMFSV, "pmfs:%d"}, - {AuWbrCreate_PMFSRR, "pmfsrr:%d"}, - {AuWbrCreate_PMFSRRV, "pmfsrr:%d:%d"}, - - {-1, NULL} -}; - -/* - * cf. linux/lib/parser.c and cmdline.c - * gave up calling memparse() since it uses simple_strtoull() instead of - * kstrto...(). - */ -static int noinline_for_stack -au_match_ull(substring_t *s, unsigned long long *result) -{ - int err; - unsigned int len; - char a[32]; - - err = -ERANGE; - len = s->to - s->from; - if (len + 1 <= sizeof(a)) { - memcpy(a, s->from, len); - a[len] = '\0'; - err = kstrtoull(a, 0, result); - } - return err; -} - -static int au_wbr_mfs_wmark(substring_t *arg, char *str, - struct au_opt_wbr_create *create) -{ - int err; - unsigned long long ull; - - err = 0; - if (!au_match_ull(arg, &ull)) - create->mfsrr_watermark = ull; - else { - pr_err("bad integer in %s\n", str); - err = -EINVAL; - } - - return err; -} - -static int au_wbr_mfs_sec(substring_t *arg, char *str, - struct au_opt_wbr_create *create) -{ - int n, err; - - err = 0; - if (!match_int(arg, &n) && 0 <= n && n <= AUFS_MFS_MAX_SEC) - create->mfs_second = n; - else { - pr_err("bad integer in %s\n", str); - err = -EINVAL; - } - - return err; -} - -static int noinline_for_stack -au_wbr_create_val(char *str, struct au_opt_wbr_create *create) -{ - int err, e; - substring_t args[MAX_OPT_ARGS]; - - err = match_token(str, au_wbr_create_policy, args); - create->wbr_create = err; - switch (err) { - case AuWbrCreate_MFSRRV: - case AuWbrCreate_PMFSRRV: - e = au_wbr_mfs_wmark(&args[0], str, create); - if (!e) - e = au_wbr_mfs_sec(&args[1], str, create); - if (unlikely(e)) - err = e; - break; - case AuWbrCreate_MFSRR: - case AuWbrCreate_PMFSRR: - e = au_wbr_mfs_wmark(&args[0], str, create); - if (unlikely(e)) { - err = e; - break; - } - /*FALLTHROUGH*/ - case AuWbrCreate_MFS: - case AuWbrCreate_PMFS: - create->mfs_second = AUFS_MFS_DEF_SEC; - break; - case AuWbrCreate_MFSV: - case AuWbrCreate_PMFSV: - e = au_wbr_mfs_sec(&args[0], str, create); - if (unlikely(e)) - err = e; - break; - } - - return err; -} - -const char *au_optstr_wbr_create(int wbr_create) -{ - return au_parser_pattern(wbr_create, au_wbr_create_policy); -} - -static match_table_t au_wbr_copyup_policy = { - {AuWbrCopyup_TDP, "tdp"}, - {AuWbrCopyup_TDP, "top-down-parent"}, - {AuWbrCopyup_BUP, "bup"}, - {AuWbrCopyup_BUP, "bottom-up-parent"}, - {AuWbrCopyup_BU, "bu"}, - {AuWbrCopyup_BU, "bottom-up"}, - {-1, NULL} -}; - -static int noinline_for_stack au_wbr_copyup_val(char *str) -{ - substring_t args[MAX_OPT_ARGS]; - - return match_token(str, au_wbr_copyup_policy, args); -} - -const char *au_optstr_wbr_copyup(int wbr_copyup) -{ - return au_parser_pattern(wbr_copyup, au_wbr_copyup_policy); -} - -/* ---------------------------------------------------------------------- */ - -static const int lkup_dirflags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; - -static void dump_opts(struct au_opts *opts) -{ -#ifdef CONFIG_AUFS_DEBUG - /* reduce stack space */ - union { - struct au_opt_add *add; - struct au_opt_del *del; - struct au_opt_mod *mod; - struct au_opt_xino *xino; - struct au_opt_xino_itrunc *xino_itrunc; - struct au_opt_wbr_create *create; - } u; - struct au_opt *opt; - - opt = opts->opt; - while (opt->type != Opt_tail) { - switch (opt->type) { - case Opt_add: - u.add = &opt->add; - AuDbg("add {b%d, %s, 0x%x, %p}\n", - u.add->bindex, u.add->pathname, u.add->perm, - u.add->path.dentry); - break; - case Opt_del: - case Opt_idel: - u.del = &opt->del; - AuDbg("del {%s, %p}\n", - u.del->pathname, u.del->h_path.dentry); - break; - case Opt_mod: - case Opt_imod: - u.mod = &opt->mod; - AuDbg("mod {%s, 0x%x, %p}\n", - u.mod->path, u.mod->perm, u.mod->h_root); - break; - case Opt_append: - u.add = &opt->add; - AuDbg("append {b%d, %s, 0x%x, %p}\n", - u.add->bindex, u.add->pathname, u.add->perm, - u.add->path.dentry); - break; - case Opt_prepend: - u.add = &opt->add; - AuDbg("prepend {b%d, %s, 0x%x, %p}\n", - u.add->bindex, u.add->pathname, u.add->perm, - u.add->path.dentry); - break; - case Opt_dirwh: - AuDbg("dirwh %d\n", opt->dirwh); - break; - case Opt_rdcache: - AuDbg("rdcache %d\n", opt->rdcache); - break; - case Opt_rdblk: - AuDbg("rdblk %u\n", opt->rdblk); - break; - case Opt_rdblk_def: - AuDbg("rdblk_def\n"); - break; - case Opt_rdhash: - AuDbg("rdhash %u\n", opt->rdhash); - break; - case Opt_rdhash_def: - AuDbg("rdhash_def\n"); - break; - case Opt_xino: - u.xino = &opt->xino; - AuDbg("xino {%s %pD}\n", u.xino->path, u.xino->file); - break; - case Opt_trunc_xino: - AuLabel(trunc_xino); - break; - case Opt_notrunc_xino: - AuLabel(notrunc_xino); - break; - case Opt_trunc_xino_path: - case Opt_itrunc_xino: - u.xino_itrunc = &opt->xino_itrunc; - AuDbg("trunc_xino %d\n", u.xino_itrunc->bindex); - break; - case Opt_noxino: - AuLabel(noxino); - break; - case Opt_trunc_xib: - AuLabel(trunc_xib); - break; - case Opt_notrunc_xib: - AuLabel(notrunc_xib); - break; - case Opt_shwh: - AuLabel(shwh); - break; - case Opt_noshwh: - AuLabel(noshwh); - break; - case Opt_dirperm1: - AuLabel(dirperm1); - break; - case Opt_nodirperm1: - AuLabel(nodirperm1); - break; - case Opt_plink: - AuLabel(plink); - break; - case Opt_noplink: - AuLabel(noplink); - break; - case Opt_list_plink: - AuLabel(list_plink); - break; - case Opt_udba: - AuDbg("udba %d, %s\n", - opt->udba, au_optstr_udba(opt->udba)); - break; - case Opt_dio: - AuLabel(dio); - break; - case Opt_nodio: - AuLabel(nodio); - break; - case Opt_diropq_a: - AuLabel(diropq_a); - break; - case Opt_diropq_w: - AuLabel(diropq_w); - break; - case Opt_warn_perm: - AuLabel(warn_perm); - break; - case Opt_nowarn_perm: - AuLabel(nowarn_perm); - break; - case Opt_verbose: - AuLabel(verbose); - break; - case Opt_noverbose: - AuLabel(noverbose); - break; - case Opt_sum: - AuLabel(sum); - break; - case Opt_nosum: - AuLabel(nosum); - break; - case Opt_wsum: - AuLabel(wsum); - break; - case Opt_wbr_create: - u.create = &opt->wbr_create; - AuDbg("create %d, %s\n", u.create->wbr_create, - au_optstr_wbr_create(u.create->wbr_create)); - switch (u.create->wbr_create) { - case AuWbrCreate_MFSV: - case AuWbrCreate_PMFSV: - AuDbg("%d sec\n", u.create->mfs_second); - break; - case AuWbrCreate_MFSRR: - AuDbg("%llu watermark\n", - u.create->mfsrr_watermark); - break; - case AuWbrCreate_MFSRRV: - case AuWbrCreate_PMFSRRV: - AuDbg("%llu watermark, %d sec\n", - u.create->mfsrr_watermark, - u.create->mfs_second); - break; - } - break; - case Opt_wbr_copyup: - AuDbg("copyup %d, %s\n", opt->wbr_copyup, - au_optstr_wbr_copyup(opt->wbr_copyup)); - break; - case Opt_fhsm_sec: - AuDbg("fhsm_sec %u\n", opt->fhsm_second); - break; - case Opt_acl: - AuLabel(acl); - break; - case Opt_noacl: - AuLabel(noacl); - break; - default: - BUG(); - } - opt++; - } -#endif -} - -void au_opts_free(struct au_opts *opts) -{ - struct au_opt *opt; - - opt = opts->opt; - while (opt->type != Opt_tail) { - switch (opt->type) { - case Opt_add: - case Opt_append: - case Opt_prepend: - path_put(&opt->add.path); - break; - case Opt_del: - case Opt_idel: - path_put(&opt->del.h_path); - break; - case Opt_mod: - case Opt_imod: - dput(opt->mod.h_root); - break; - case Opt_xino: - fput(opt->xino.file); - break; - } - opt++; - } -} - -static int opt_add(struct au_opt *opt, char *opt_str, unsigned long sb_flags, - aufs_bindex_t bindex) -{ - int err; - struct au_opt_add *add = &opt->add; - char *p; - - add->bindex = bindex; - add->perm = AuBrPerm_RO; - add->pathname = opt_str; - p = strchr(opt_str, '='); - if (p) { - *p++ = 0; - if (*p) - add->perm = br_perm_val(p); - } - - err = vfsub_kern_path(add->pathname, lkup_dirflags, &add->path); - if (!err) { - if (!p) { - add->perm = AuBrPerm_RO; - if (au_test_fs_rr(add->path.dentry->d_sb)) - add->perm = AuBrPerm_RR; - else if (!bindex && !(sb_flags & MS_RDONLY)) - add->perm = AuBrPerm_RW; - } - opt->type = Opt_add; - goto out; - } - pr_err("lookup failed %s (%d)\n", add->pathname, err); - err = -EINVAL; - -out: - return err; -} - -static int au_opts_parse_del(struct au_opt_del *del, substring_t args[]) -{ - int err; - - del->pathname = args[0].from; - AuDbg("del path %s\n", del->pathname); - - err = vfsub_kern_path(del->pathname, lkup_dirflags, &del->h_path); - if (unlikely(err)) - pr_err("lookup failed %s (%d)\n", del->pathname, err); - - return err; -} - -#if 0 /* reserved for future use */ -static int au_opts_parse_idel(struct super_block *sb, aufs_bindex_t bindex, - struct au_opt_del *del, substring_t args[]) -{ - int err; - struct dentry *root; - - err = -EINVAL; - root = sb->s_root; - aufs_read_lock(root, AuLock_FLUSH); - if (bindex < 0 || au_sbend(sb) < bindex) { - pr_err("out of bounds, %d\n", bindex); - goto out; - } - - err = 0; - del->h_path.dentry = dget(au_h_dptr(root, bindex)); - del->h_path.mnt = mntget(au_sbr_mnt(sb, bindex)); - -out: - aufs_read_unlock(root, !AuLock_IR); - return err; -} -#endif - -static int noinline_for_stack -au_opts_parse_mod(struct au_opt_mod *mod, substring_t args[]) -{ - int err; - struct path path; - char *p; - - err = -EINVAL; - mod->path = args[0].from; - p = strchr(mod->path, '='); - if (unlikely(!p)) { - pr_err("no permssion %s\n", args[0].from); - goto out; - } - - *p++ = 0; - err = vfsub_kern_path(mod->path, lkup_dirflags, &path); - if (unlikely(err)) { - pr_err("lookup failed %s (%d)\n", mod->path, err); - goto out; - } - - mod->perm = br_perm_val(p); - AuDbg("mod path %s, perm 0x%x, %s\n", mod->path, mod->perm, p); - mod->h_root = dget(path.dentry); - path_put(&path); - -out: - return err; -} - -#if 0 /* reserved for future use */ -static int au_opts_parse_imod(struct super_block *sb, aufs_bindex_t bindex, - struct au_opt_mod *mod, substring_t args[]) -{ - int err; - struct dentry *root; - - err = -EINVAL; - root = sb->s_root; - aufs_read_lock(root, AuLock_FLUSH); - if (bindex < 0 || au_sbend(sb) < bindex) { - pr_err("out of bounds, %d\n", bindex); - goto out; - } - - err = 0; - mod->perm = br_perm_val(args[1].from); - AuDbg("mod path %s, perm 0x%x, %s\n", - mod->path, mod->perm, args[1].from); - mod->h_root = dget(au_h_dptr(root, bindex)); - -out: - aufs_read_unlock(root, !AuLock_IR); - return err; -} -#endif - -static int au_opts_parse_xino(struct super_block *sb, struct au_opt_xino *xino, - substring_t args[]) -{ - int err; - struct file *file; - - file = au_xino_create(sb, args[0].from, /*silent*/0); - err = PTR_ERR(file); - if (IS_ERR(file)) - goto out; - - err = -EINVAL; - if (unlikely(file->f_path.dentry->d_sb == sb)) { - fput(file); - pr_err("%s must be outside\n", args[0].from); - goto out; - } - - err = 0; - xino->file = file; - xino->path = args[0].from; - -out: - return err; -} - -static int noinline_for_stack -au_opts_parse_xino_itrunc_path(struct super_block *sb, - struct au_opt_xino_itrunc *xino_itrunc, - substring_t args[]) -{ - int err; - aufs_bindex_t bend, bindex; - struct path path; - struct dentry *root; - - err = vfsub_kern_path(args[0].from, lkup_dirflags, &path); - if (unlikely(err)) { - pr_err("lookup failed %s (%d)\n", args[0].from, err); - goto out; - } - - xino_itrunc->bindex = -1; - root = sb->s_root; - aufs_read_lock(root, AuLock_FLUSH); - bend = au_sbend(sb); - for (bindex = 0; bindex <= bend; bindex++) { - if (au_h_dptr(root, bindex) == path.dentry) { - xino_itrunc->bindex = bindex; - break; - } - } - aufs_read_unlock(root, !AuLock_IR); - path_put(&path); - - if (unlikely(xino_itrunc->bindex < 0)) { - pr_err("no such branch %s\n", args[0].from); - err = -EINVAL; - } - -out: - return err; -} - -/* called without aufs lock */ -int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts) -{ - int err, n, token; - aufs_bindex_t bindex; - unsigned char skipped; - struct dentry *root; - struct au_opt *opt, *opt_tail; - char *opt_str; - /* reduce the stack space */ - union { - struct au_opt_xino_itrunc *xino_itrunc; - struct au_opt_wbr_create *create; - } u; - struct { - substring_t args[MAX_OPT_ARGS]; - } *a; - - err = -ENOMEM; - a = kmalloc(sizeof(*a), GFP_NOFS); - if (unlikely(!a)) - goto out; - - root = sb->s_root; - err = 0; - bindex = 0; - opt = opts->opt; - opt_tail = opt + opts->max_opt - 1; - opt->type = Opt_tail; - while (!err && (opt_str = strsep(&str, ",")) && *opt_str) { - err = -EINVAL; - skipped = 0; - token = match_token(opt_str, options, a->args); - switch (token) { - case Opt_br: - err = 0; - while (!err && (opt_str = strsep(&a->args[0].from, ":")) - && *opt_str) { - err = opt_add(opt, opt_str, opts->sb_flags, - bindex++); - if (unlikely(!err && ++opt > opt_tail)) { - err = -E2BIG; - break; - } - opt->type = Opt_tail; - skipped = 1; - } - break; - case Opt_add: - if (unlikely(match_int(&a->args[0], &n))) { - pr_err("bad integer in %s\n", opt_str); - break; - } - bindex = n; - err = opt_add(opt, a->args[1].from, opts->sb_flags, - bindex); - if (!err) - opt->type = token; - break; - case Opt_append: - err = opt_add(opt, a->args[0].from, opts->sb_flags, - /*dummy bindex*/1); - if (!err) - opt->type = token; - break; - case Opt_prepend: - err = opt_add(opt, a->args[0].from, opts->sb_flags, - /*bindex*/0); - if (!err) - opt->type = token; - break; - case Opt_del: - err = au_opts_parse_del(&opt->del, a->args); - if (!err) - opt->type = token; - break; -#if 0 /* reserved for future use */ - case Opt_idel: - del->pathname = "(indexed)"; - if (unlikely(match_int(&args[0], &n))) { - pr_err("bad integer in %s\n", opt_str); - break; - } - err = au_opts_parse_idel(sb, n, &opt->del, a->args); - if (!err) - opt->type = token; - break; -#endif - case Opt_mod: - err = au_opts_parse_mod(&opt->mod, a->args); - if (!err) - opt->type = token; - break; -#ifdef IMOD /* reserved for future use */ - case Opt_imod: - u.mod->path = "(indexed)"; - if (unlikely(match_int(&a->args[0], &n))) { - pr_err("bad integer in %s\n", opt_str); - break; - } - err = au_opts_parse_imod(sb, n, &opt->mod, a->args); - if (!err) - opt->type = token; - break; -#endif - case Opt_xino: - err = au_opts_parse_xino(sb, &opt->xino, a->args); - if (!err) - opt->type = token; - break; - - case Opt_trunc_xino_path: - err = au_opts_parse_xino_itrunc_path - (sb, &opt->xino_itrunc, a->args); - if (!err) - opt->type = token; - break; - - case Opt_itrunc_xino: - u.xino_itrunc = &opt->xino_itrunc; - if (unlikely(match_int(&a->args[0], &n))) { - pr_err("bad integer in %s\n", opt_str); - break; - } - u.xino_itrunc->bindex = n; - aufs_read_lock(root, AuLock_FLUSH); - if (n < 0 || au_sbend(sb) < n) { - pr_err("out of bounds, %d\n", n); - aufs_read_unlock(root, !AuLock_IR); - break; - } - aufs_read_unlock(root, !AuLock_IR); - err = 0; - opt->type = token; - break; - - case Opt_dirwh: - if (unlikely(match_int(&a->args[0], &opt->dirwh))) - break; - err = 0; - opt->type = token; - break; - - case Opt_rdcache: - if (unlikely(match_int(&a->args[0], &n))) { - pr_err("bad integer in %s\n", opt_str); - break; - } - if (unlikely(n > AUFS_RDCACHE_MAX)) { - pr_err("rdcache must be smaller than %d\n", - AUFS_RDCACHE_MAX); - break; - } - opt->rdcache = n; - err = 0; - opt->type = token; - break; - case Opt_rdblk: - if (unlikely(match_int(&a->args[0], &n) - || n < 0 - || n > KMALLOC_MAX_SIZE)) { - pr_err("bad integer in %s\n", opt_str); - break; - } - if (unlikely(n && n < NAME_MAX)) { - pr_err("rdblk must be larger than %d\n", - NAME_MAX); - break; - } - opt->rdblk = n; - err = 0; - opt->type = token; - break; - case Opt_rdhash: - if (unlikely(match_int(&a->args[0], &n) - || n < 0 - || n * sizeof(struct hlist_head) - > KMALLOC_MAX_SIZE)) { - pr_err("bad integer in %s\n", opt_str); - break; - } - opt->rdhash = n; - err = 0; - opt->type = token; - break; - - case Opt_trunc_xino: - case Opt_notrunc_xino: - case Opt_noxino: - case Opt_trunc_xib: - case Opt_notrunc_xib: - case Opt_shwh: - case Opt_noshwh: - case Opt_dirperm1: - case Opt_nodirperm1: - case Opt_plink: - case Opt_noplink: - case Opt_list_plink: - case Opt_dio: - case Opt_nodio: - case Opt_diropq_a: - case Opt_diropq_w: - case Opt_warn_perm: - case Opt_nowarn_perm: - case Opt_verbose: - case Opt_noverbose: - case Opt_sum: - case Opt_nosum: - case Opt_wsum: - case Opt_rdblk_def: - case Opt_rdhash_def: - case Opt_acl: - case Opt_noacl: - err = 0; - opt->type = token; - break; - - case Opt_udba: - opt->udba = udba_val(a->args[0].from); - if (opt->udba >= 0) { - err = 0; - opt->type = token; - } else - pr_err("wrong value, %s\n", opt_str); - break; - - case Opt_wbr_create: - u.create = &opt->wbr_create; - u.create->wbr_create - = au_wbr_create_val(a->args[0].from, u.create); - if (u.create->wbr_create >= 0) { - err = 0; - opt->type = token; - } else - pr_err("wrong value, %s\n", opt_str); - break; - case Opt_wbr_copyup: - opt->wbr_copyup = au_wbr_copyup_val(a->args[0].from); - if (opt->wbr_copyup >= 0) { - err = 0; - opt->type = token; - } else - pr_err("wrong value, %s\n", opt_str); - break; - - case Opt_fhsm_sec: - if (unlikely(match_int(&a->args[0], &n) - || n < 0)) { - pr_err("bad integer in %s\n", opt_str); - break; - } - if (sysaufs_brs) { - opt->fhsm_second = n; - opt->type = token; - } else - pr_warn("ignored %s\n", opt_str); - err = 0; - break; - - case Opt_ignore: - pr_warn("ignored %s\n", opt_str); - /*FALLTHROUGH*/ - case Opt_ignore_silent: - skipped = 1; - err = 0; - break; - case Opt_err: - pr_err("unknown option %s\n", opt_str); - break; - } - - if (!err && !skipped) { - if (unlikely(++opt > opt_tail)) { - err = -E2BIG; - opt--; - opt->type = Opt_tail; - break; - } - opt->type = Opt_tail; - } - } - - kfree(a); - dump_opts(opts); - if (unlikely(err)) - au_opts_free(opts); - -out: - return err; -} - -static int au_opt_wbr_create(struct super_block *sb, - struct au_opt_wbr_create *create) -{ - int err; - struct au_sbinfo *sbinfo; - - SiMustWriteLock(sb); - - err = 1; /* handled */ - sbinfo = au_sbi(sb); - if (sbinfo->si_wbr_create_ops->fin) { - err = sbinfo->si_wbr_create_ops->fin(sb); - if (!err) - err = 1; - } - - sbinfo->si_wbr_create = create->wbr_create; - sbinfo->si_wbr_create_ops = au_wbr_create_ops + create->wbr_create; - switch (create->wbr_create) { - case AuWbrCreate_MFSRRV: - case AuWbrCreate_MFSRR: - case AuWbrCreate_PMFSRR: - case AuWbrCreate_PMFSRRV: - sbinfo->si_wbr_mfs.mfsrr_watermark = create->mfsrr_watermark; - /*FALLTHROUGH*/ - case AuWbrCreate_MFS: - case AuWbrCreate_MFSV: - case AuWbrCreate_PMFS: - case AuWbrCreate_PMFSV: - sbinfo->si_wbr_mfs.mfs_expire - = msecs_to_jiffies(create->mfs_second * MSEC_PER_SEC); - break; - } - - if (sbinfo->si_wbr_create_ops->init) - sbinfo->si_wbr_create_ops->init(sb); /* ignore */ - - return err; -} - -/* - * returns, - * plus: processed without an error - * zero: unprocessed - */ -static int au_opt_simple(struct super_block *sb, struct au_opt *opt, - struct au_opts *opts) -{ - int err; - struct au_sbinfo *sbinfo; - - SiMustWriteLock(sb); - - err = 1; /* handled */ - sbinfo = au_sbi(sb); - switch (opt->type) { - case Opt_udba: - sbinfo->si_mntflags &= ~AuOptMask_UDBA; - sbinfo->si_mntflags |= opt->udba; - opts->given_udba |= opt->udba; - break; - - case Opt_plink: - au_opt_set(sbinfo->si_mntflags, PLINK); - break; - case Opt_noplink: - if (au_opt_test(sbinfo->si_mntflags, PLINK)) - au_plink_put(sb, /*verbose*/1); - au_opt_clr(sbinfo->si_mntflags, PLINK); - break; - case Opt_list_plink: - if (au_opt_test(sbinfo->si_mntflags, PLINK)) - au_plink_list(sb); - break; - - case Opt_dio: - au_opt_set(sbinfo->si_mntflags, DIO); - au_fset_opts(opts->flags, REFRESH_DYAOP); - break; - case Opt_nodio: - au_opt_clr(sbinfo->si_mntflags, DIO); - au_fset_opts(opts->flags, REFRESH_DYAOP); - break; - - case Opt_fhsm_sec: - au_fhsm_set(sbinfo, opt->fhsm_second); - break; - - case Opt_diropq_a: - au_opt_set(sbinfo->si_mntflags, ALWAYS_DIROPQ); - break; - case Opt_diropq_w: - au_opt_clr(sbinfo->si_mntflags, ALWAYS_DIROPQ); - break; - - case Opt_warn_perm: - au_opt_set(sbinfo->si_mntflags, WARN_PERM); - break; - case Opt_nowarn_perm: - au_opt_clr(sbinfo->si_mntflags, WARN_PERM); - break; - - case Opt_verbose: - au_opt_set(sbinfo->si_mntflags, VERBOSE); - break; - case Opt_noverbose: - au_opt_clr(sbinfo->si_mntflags, VERBOSE); - break; - - case Opt_sum: - au_opt_set(sbinfo->si_mntflags, SUM); - break; - case Opt_wsum: - au_opt_clr(sbinfo->si_mntflags, SUM); - au_opt_set(sbinfo->si_mntflags, SUM_W); - case Opt_nosum: - au_opt_clr(sbinfo->si_mntflags, SUM); - au_opt_clr(sbinfo->si_mntflags, SUM_W); - break; - - case Opt_wbr_create: - err = au_opt_wbr_create(sb, &opt->wbr_create); - break; - case Opt_wbr_copyup: - sbinfo->si_wbr_copyup = opt->wbr_copyup; - sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + opt->wbr_copyup; - break; - - case Opt_dirwh: - sbinfo->si_dirwh = opt->dirwh; - break; - - case Opt_rdcache: - sbinfo->si_rdcache - = msecs_to_jiffies(opt->rdcache * MSEC_PER_SEC); - break; - case Opt_rdblk: - sbinfo->si_rdblk = opt->rdblk; - break; - case Opt_rdblk_def: - sbinfo->si_rdblk = AUFS_RDBLK_DEF; - break; - case Opt_rdhash: - sbinfo->si_rdhash = opt->rdhash; - break; - case Opt_rdhash_def: - sbinfo->si_rdhash = AUFS_RDHASH_DEF; - break; - - case Opt_shwh: - au_opt_set(sbinfo->si_mntflags, SHWH); - break; - case Opt_noshwh: - au_opt_clr(sbinfo->si_mntflags, SHWH); - break; - - case Opt_dirperm1: - au_opt_set(sbinfo->si_mntflags, DIRPERM1); - break; - case Opt_nodirperm1: - au_opt_clr(sbinfo->si_mntflags, DIRPERM1); - break; - - case Opt_trunc_xino: - au_opt_set(sbinfo->si_mntflags, TRUNC_XINO); - break; - case Opt_notrunc_xino: - au_opt_clr(sbinfo->si_mntflags, TRUNC_XINO); - break; - - case Opt_trunc_xino_path: - case Opt_itrunc_xino: - err = au_xino_trunc(sb, opt->xino_itrunc.bindex); - if (!err) - err = 1; - break; - - case Opt_trunc_xib: - au_fset_opts(opts->flags, TRUNC_XIB); - break; - case Opt_notrunc_xib: - au_fclr_opts(opts->flags, TRUNC_XIB); - break; - - case Opt_acl: - sb->s_flags |= MS_POSIXACL; - break; - case Opt_noacl: - sb->s_flags &= ~MS_POSIXACL; - break; - - default: - err = 0; - break; - } - - return err; -} - -/* - * returns tri-state. - * plus: processed without an error - * zero: unprocessed - * minus: error - */ -static int au_opt_br(struct super_block *sb, struct au_opt *opt, - struct au_opts *opts) -{ - int err, do_refresh; - - err = 0; - switch (opt->type) { - case Opt_append: - opt->add.bindex = au_sbend(sb) + 1; - if (opt->add.bindex < 0) - opt->add.bindex = 0; - goto add; - case Opt_prepend: - opt->add.bindex = 0; - add: /* indented label */ - case Opt_add: - err = au_br_add(sb, &opt->add, - au_ftest_opts(opts->flags, REMOUNT)); - if (!err) { - err = 1; - au_fset_opts(opts->flags, REFRESH); - } - break; - - case Opt_del: - case Opt_idel: - err = au_br_del(sb, &opt->del, - au_ftest_opts(opts->flags, REMOUNT)); - if (!err) { - err = 1; - au_fset_opts(opts->flags, TRUNC_XIB); - au_fset_opts(opts->flags, REFRESH); - } - break; - - case Opt_mod: - case Opt_imod: - err = au_br_mod(sb, &opt->mod, - au_ftest_opts(opts->flags, REMOUNT), - &do_refresh); - if (!err) { - err = 1; - if (do_refresh) - au_fset_opts(opts->flags, REFRESH); - } - break; - } - - return err; -} - -static int au_opt_xino(struct super_block *sb, struct au_opt *opt, - struct au_opt_xino **opt_xino, - struct au_opts *opts) -{ - int err; - aufs_bindex_t bend, bindex; - struct dentry *root, *parent, *h_root; - - err = 0; - switch (opt->type) { - case Opt_xino: - err = au_xino_set(sb, &opt->xino, - !!au_ftest_opts(opts->flags, REMOUNT)); - if (unlikely(err)) - break; - - *opt_xino = &opt->xino; - au_xino_brid_set(sb, -1); - - /* safe d_parent access */ - parent = opt->xino.file->f_path.dentry->d_parent; - root = sb->s_root; - bend = au_sbend(sb); - for (bindex = 0; bindex <= bend; bindex++) { - h_root = au_h_dptr(root, bindex); - if (h_root == parent) { - au_xino_brid_set(sb, au_sbr_id(sb, bindex)); - break; - } - } - break; - - case Opt_noxino: - au_xino_clr(sb); - au_xino_brid_set(sb, -1); - *opt_xino = (void *)-1; - break; - } - - return err; -} - -int au_opts_verify(struct super_block *sb, unsigned long sb_flags, - unsigned int pending) -{ - int err, fhsm; - aufs_bindex_t bindex, bend; - unsigned char do_plink, skip, do_free; - struct au_branch *br; - struct au_wbr *wbr; - struct dentry *root; - struct inode *dir, *h_dir; - struct au_sbinfo *sbinfo; - struct au_hinode *hdir; - - SiMustAnyLock(sb); - - sbinfo = au_sbi(sb); - AuDebugOn(!(sbinfo->si_mntflags & AuOptMask_UDBA)); - - if (!(sb_flags & MS_RDONLY)) { - if (unlikely(!au_br_writable(au_sbr_perm(sb, 0)))) - pr_warn("first branch should be rw\n"); - if (unlikely(au_opt_test(sbinfo->si_mntflags, SHWH))) - pr_warn("shwh should be used with ro\n"); - } - - if (au_opt_test((sbinfo->si_mntflags | pending), UDBA_HNOTIFY) - && !au_opt_test(sbinfo->si_mntflags, XINO)) - pr_warn("udba=*notify requires xino\n"); - - if (au_opt_test(sbinfo->si_mntflags, DIRPERM1)) - pr_warn("dirperm1 breaks the protection" - " by the permission bits on the lower branch\n"); - - err = 0; - fhsm = 0; - root = sb->s_root; - dir = d_inode(root); - do_plink = !!au_opt_test(sbinfo->si_mntflags, PLINK); - bend = au_sbend(sb); - for (bindex = 0; !err && bindex <= bend; bindex++) { - skip = 0; - h_dir = au_h_iptr(dir, bindex); - br = au_sbr(sb, bindex); - - if ((br->br_perm & AuBrAttr_ICEX) - && !h_dir->i_op->listxattr) - br->br_perm &= ~AuBrAttr_ICEX; -#if 0 - if ((br->br_perm & AuBrAttr_ICEX_SEC) - && (au_br_sb(br)->s_flags & MS_NOSEC)) - br->br_perm &= ~AuBrAttr_ICEX_SEC; -#endif - - do_free = 0; - wbr = br->br_wbr; - if (wbr) - wbr_wh_read_lock(wbr); - - if (!au_br_writable(br->br_perm)) { - do_free = !!wbr; - skip = (!wbr - || (!wbr->wbr_whbase - && !wbr->wbr_plink - && !wbr->wbr_orph)); - } else if (!au_br_wh_linkable(br->br_perm)) { - /* skip = (!br->br_whbase && !br->br_orph); */ - skip = (!wbr || !wbr->wbr_whbase); - if (skip && wbr) { - if (do_plink) - skip = !!wbr->wbr_plink; - else - skip = !wbr->wbr_plink; - } - } else { - /* skip = (br->br_whbase && br->br_ohph); */ - skip = (wbr && wbr->wbr_whbase); - if (skip) { - if (do_plink) - skip = !!wbr->wbr_plink; - else - skip = !wbr->wbr_plink; - } - } - if (wbr) - wbr_wh_read_unlock(wbr); - - if (au_br_fhsm(br->br_perm)) { - fhsm++; - AuDebugOn(!br->br_fhsm); - } - - if (skip) - continue; - - hdir = au_hi(dir, bindex); - au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); - if (wbr) - wbr_wh_write_lock(wbr); - err = au_wh_init(br, sb); - if (wbr) - wbr_wh_write_unlock(wbr); - au_hn_imtx_unlock(hdir); - - if (!err && do_free) { - kfree(wbr); - br->br_wbr = NULL; - } - } - - if (fhsm >= 2) { - au_fset_si(sbinfo, FHSM); - for (bindex = bend; bindex >= 0; bindex--) { - br = au_sbr(sb, bindex); - if (au_br_fhsm(br->br_perm)) { - au_fhsm_set_bottom(sb, bindex); - break; - } - } - } else { - au_fclr_si(sbinfo, FHSM); - au_fhsm_set_bottom(sb, -1); - } - - return err; -} - -int au_opts_mount(struct super_block *sb, struct au_opts *opts) -{ - int err; - unsigned int tmp; - aufs_bindex_t bindex, bend; - struct au_opt *opt; - struct au_opt_xino *opt_xino, xino; - struct au_sbinfo *sbinfo; - struct au_branch *br; - struct inode *dir; - - SiMustWriteLock(sb); - - err = 0; - opt_xino = NULL; - opt = opts->opt; - while (err >= 0 && opt->type != Opt_tail) - err = au_opt_simple(sb, opt++, opts); - if (err > 0) - err = 0; - else if (unlikely(err < 0)) - goto out; - - /* disable xino and udba temporary */ - sbinfo = au_sbi(sb); - tmp = sbinfo->si_mntflags; - au_opt_clr(sbinfo->si_mntflags, XINO); - au_opt_set_udba(sbinfo->si_mntflags, UDBA_REVAL); - - opt = opts->opt; - while (err >= 0 && opt->type != Opt_tail) - err = au_opt_br(sb, opt++, opts); - if (err > 0) - err = 0; - else if (unlikely(err < 0)) - goto out; - - bend = au_sbend(sb); - if (unlikely(bend < 0)) { - err = -EINVAL; - pr_err("no branches\n"); - goto out; - } - - if (au_opt_test(tmp, XINO)) - au_opt_set(sbinfo->si_mntflags, XINO); - opt = opts->opt; - while (!err && opt->type != Opt_tail) - err = au_opt_xino(sb, opt++, &opt_xino, opts); - if (unlikely(err)) - goto out; - - err = au_opts_verify(sb, sb->s_flags, tmp); - if (unlikely(err)) - goto out; - - /* restore xino */ - if (au_opt_test(tmp, XINO) && !opt_xino) { - xino.file = au_xino_def(sb); - err = PTR_ERR(xino.file); - if (IS_ERR(xino.file)) - goto out; - - err = au_xino_set(sb, &xino, /*remount*/0); - fput(xino.file); - if (unlikely(err)) - goto out; - } - - /* restore udba */ - tmp &= AuOptMask_UDBA; - sbinfo->si_mntflags &= ~AuOptMask_UDBA; - sbinfo->si_mntflags |= tmp; - bend = au_sbend(sb); - for (bindex = 0; bindex <= bend; bindex++) { - br = au_sbr(sb, bindex); - err = au_hnotify_reset_br(tmp, br, br->br_perm); - if (unlikely(err)) - AuIOErr("hnotify failed on br %d, %d, ignored\n", - bindex, err); - /* go on even if err */ - } - if (au_opt_test(tmp, UDBA_HNOTIFY)) { - dir = d_inode(sb->s_root); - au_hn_reset(dir, au_hi_flags(dir, /*isdir*/1) & ~AuHi_XINO); - } - -out: - return err; -} - -int au_opts_remount(struct super_block *sb, struct au_opts *opts) -{ - int err, rerr; - struct inode *dir; - struct au_opt_xino *opt_xino; - struct au_opt *opt; - struct au_sbinfo *sbinfo; - - SiMustWriteLock(sb); - - dir = d_inode(sb->s_root); - sbinfo = au_sbi(sb); - err = 0; - opt_xino = NULL; - opt = opts->opt; - while (err >= 0 && opt->type != Opt_tail) { - err = au_opt_simple(sb, opt, opts); - if (!err) - err = au_opt_br(sb, opt, opts); - if (!err) - err = au_opt_xino(sb, opt, &opt_xino, opts); - opt++; - } - if (err > 0) - err = 0; - AuTraceErr(err); - /* go on even err */ - - rerr = au_opts_verify(sb, opts->sb_flags, /*pending*/0); - if (unlikely(rerr && !err)) - err = rerr; - - if (au_ftest_opts(opts->flags, TRUNC_XIB)) { - rerr = au_xib_trunc(sb); - if (unlikely(rerr && !err)) - err = rerr; - } - - /* will be handled by the caller */ - if (!au_ftest_opts(opts->flags, REFRESH) - && (opts->given_udba || au_opt_test(sbinfo->si_mntflags, XINO))) - au_fset_opts(opts->flags, REFRESH); - - AuDbg("status 0x%x\n", opts->flags); - return err; -} - -/* ---------------------------------------------------------------------- */ - -unsigned int au_opt_udba(struct super_block *sb) -{ - return au_mntflags(sb) & AuOptMask_UDBA; -} diff --git a/fs/aufs/opts.h b/fs/aufs/opts.h deleted file mode 100644 index b18a4aa00..000000000 --- a/fs/aufs/opts.h +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * mount options/flags - */ - -#ifndef __AUFS_OPTS_H__ -#define __AUFS_OPTS_H__ - -#ifdef __KERNEL__ - -#include <linux/path.h> - -struct file; -struct super_block; - -/* ---------------------------------------------------------------------- */ - -/* mount flags */ -#define AuOpt_XINO 1 /* external inode number bitmap - and translation table */ -#define AuOpt_TRUNC_XINO (1 << 1) /* truncate xino files */ -#define AuOpt_UDBA_NONE (1 << 2) /* users direct branch access */ -#define AuOpt_UDBA_REVAL (1 << 3) -#define AuOpt_UDBA_HNOTIFY (1 << 4) -#define AuOpt_SHWH (1 << 5) /* show whiteout */ -#define AuOpt_PLINK (1 << 6) /* pseudo-link */ -#define AuOpt_DIRPERM1 (1 << 7) /* ignore the lower dir's perm - bits */ -#define AuOpt_ALWAYS_DIROPQ (1 << 9) /* policy to creating diropq */ -#define AuOpt_SUM (1 << 10) /* summation for statfs(2) */ -#define AuOpt_SUM_W (1 << 11) /* unimplemented */ -#define AuOpt_WARN_PERM (1 << 12) /* warn when add-branch */ -#define AuOpt_VERBOSE (1 << 13) /* busy inode when del-branch */ -#define AuOpt_DIO (1 << 14) /* direct io */ - -#ifndef CONFIG_AUFS_HNOTIFY -#undef AuOpt_UDBA_HNOTIFY -#define AuOpt_UDBA_HNOTIFY 0 -#endif -#ifndef CONFIG_AUFS_SHWH -#undef AuOpt_SHWH -#define AuOpt_SHWH 0 -#endif - -#define AuOpt_Def (AuOpt_XINO \ - | AuOpt_UDBA_REVAL \ - | AuOpt_PLINK \ - /* | AuOpt_DIRPERM1 */ \ - | AuOpt_WARN_PERM) -#define AuOptMask_UDBA (AuOpt_UDBA_NONE \ - | AuOpt_UDBA_REVAL \ - | AuOpt_UDBA_HNOTIFY) - -#define au_opt_test(flags, name) (flags & AuOpt_##name) -#define au_opt_set(flags, name) do { \ - BUILD_BUG_ON(AuOpt_##name & AuOptMask_UDBA); \ - ((flags) |= AuOpt_##name); \ -} while (0) -#define au_opt_set_udba(flags, name) do { \ - (flags) &= ~AuOptMask_UDBA; \ - ((flags) |= AuOpt_##name); \ -} while (0) -#define au_opt_clr(flags, name) do { \ - ((flags) &= ~AuOpt_##name); \ -} while (0) - -static inline unsigned int au_opts_plink(unsigned int mntflags) -{ -#ifdef CONFIG_PROC_FS - return mntflags; -#else - return mntflags & ~AuOpt_PLINK; -#endif -} - -/* ---------------------------------------------------------------------- */ - -/* policies to select one among multiple writable branches */ -enum { - AuWbrCreate_TDP, /* top down parent */ - AuWbrCreate_RR, /* round robin */ - AuWbrCreate_MFS, /* most free space */ - AuWbrCreate_MFSV, /* mfs with seconds */ - AuWbrCreate_MFSRR, /* mfs then rr */ - AuWbrCreate_MFSRRV, /* mfs then rr with seconds */ - AuWbrCreate_PMFS, /* parent and mfs */ - AuWbrCreate_PMFSV, /* parent and mfs with seconds */ - AuWbrCreate_PMFSRR, /* parent, mfs and round-robin */ - AuWbrCreate_PMFSRRV, /* plus seconds */ - - AuWbrCreate_Def = AuWbrCreate_TDP -}; - -enum { - AuWbrCopyup_TDP, /* top down parent */ - AuWbrCopyup_BUP, /* bottom up parent */ - AuWbrCopyup_BU, /* bottom up */ - - AuWbrCopyup_Def = AuWbrCopyup_TDP -}; - -/* ---------------------------------------------------------------------- */ - -struct au_opt_add { - aufs_bindex_t bindex; - char *pathname; - int perm; - struct path path; -}; - -struct au_opt_del { - char *pathname; - struct path h_path; -}; - -struct au_opt_mod { - char *path; - int perm; - struct dentry *h_root; -}; - -struct au_opt_xino { - char *path; - struct file *file; -}; - -struct au_opt_xino_itrunc { - aufs_bindex_t bindex; -}; - -struct au_opt_wbr_create { - int wbr_create; - int mfs_second; - unsigned long long mfsrr_watermark; -}; - -struct au_opt { - int type; - union { - struct au_opt_xino xino; - struct au_opt_xino_itrunc xino_itrunc; - struct au_opt_add add; - struct au_opt_del del; - struct au_opt_mod mod; - int dirwh; - int rdcache; - unsigned int rdblk; - unsigned int rdhash; - int udba; - struct au_opt_wbr_create wbr_create; - int wbr_copyup; - unsigned int fhsm_second; - }; -}; - -/* opts flags */ -#define AuOpts_REMOUNT 1 -#define AuOpts_REFRESH (1 << 1) -#define AuOpts_TRUNC_XIB (1 << 2) -#define AuOpts_REFRESH_DYAOP (1 << 3) -#define au_ftest_opts(flags, name) ((flags) & AuOpts_##name) -#define au_fset_opts(flags, name) \ - do { (flags) |= AuOpts_##name; } while (0) -#define au_fclr_opts(flags, name) \ - do { (flags) &= ~AuOpts_##name; } while (0) - -struct au_opts { - struct au_opt *opt; - int max_opt; - - unsigned int given_udba; - unsigned int flags; - unsigned long sb_flags; -}; - -/* ---------------------------------------------------------------------- */ - -/* opts.c */ -void au_optstr_br_perm(au_br_perm_str_t *str, int perm); -const char *au_optstr_udba(int udba); -const char *au_optstr_wbr_copyup(int wbr_copyup); -const char *au_optstr_wbr_create(int wbr_create); - -void au_opts_free(struct au_opts *opts); -int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts); -int au_opts_verify(struct super_block *sb, unsigned long sb_flags, - unsigned int pending); -int au_opts_mount(struct super_block *sb, struct au_opts *opts); -int au_opts_remount(struct super_block *sb, struct au_opts *opts); - -unsigned int au_opt_udba(struct super_block *sb); - -#endif /* __KERNEL__ */ -#endif /* __AUFS_OPTS_H__ */ diff --git a/fs/aufs/plink.c b/fs/aufs/plink.c deleted file mode 100644 index 988fba940..000000000 --- a/fs/aufs/plink.c +++ /dev/null @@ -1,528 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * pseudo-link - */ - -#include "aufs.h" - -/* - * the pseudo-link maintenance mode. - * during a user process maintains the pseudo-links, - * prohibit adding a new plink and branch manipulation. - * - * Flags - * NOPLM: - * For entry functions which will handle plink, and i_mutex is already held - * in VFS. - * They cannot wait and should return an error at once. - * Callers has to check the error. - * NOPLMW: - * For entry functions which will handle plink, but i_mutex is not held - * in VFS. - * They can wait the plink maintenance mode to finish. - * - * They behave like F_SETLK and F_SETLKW. - * If the caller never handle plink, then both flags are unnecessary. - */ - -int au_plink_maint(struct super_block *sb, int flags) -{ - int err; - pid_t pid, ppid; - struct au_sbinfo *sbi; - - SiMustAnyLock(sb); - - err = 0; - if (!au_opt_test(au_mntflags(sb), PLINK)) - goto out; - - sbi = au_sbi(sb); - pid = sbi->si_plink_maint_pid; - if (!pid || pid == current->pid) - goto out; - - /* todo: it highly depends upon /sbin/mount.aufs */ - rcu_read_lock(); - ppid = task_pid_vnr(rcu_dereference(current->real_parent)); - rcu_read_unlock(); - if (pid == ppid) - goto out; - - if (au_ftest_lock(flags, NOPLMW)) { - /* if there is no i_mutex lock in VFS, we don't need to wait */ - /* AuDebugOn(!lockdep_depth(current)); */ - while (sbi->si_plink_maint_pid) { - si_read_unlock(sb); - /* gave up wake_up_bit() */ - wait_event(sbi->si_plink_wq, !sbi->si_plink_maint_pid); - - if (au_ftest_lock(flags, FLUSH)) - au_nwt_flush(&sbi->si_nowait); - si_noflush_read_lock(sb); - } - } else if (au_ftest_lock(flags, NOPLM)) { - AuDbg("ppid %d, pid %d\n", ppid, pid); - err = -EAGAIN; - } - -out: - return err; -} - -void au_plink_maint_leave(struct au_sbinfo *sbinfo) -{ - spin_lock(&sbinfo->si_plink_maint_lock); - sbinfo->si_plink_maint_pid = 0; - spin_unlock(&sbinfo->si_plink_maint_lock); - wake_up_all(&sbinfo->si_plink_wq); -} - -int au_plink_maint_enter(struct super_block *sb) -{ - int err; - struct au_sbinfo *sbinfo; - - err = 0; - sbinfo = au_sbi(sb); - /* make sure i am the only one in this fs */ - si_write_lock(sb, AuLock_FLUSH); - if (au_opt_test(au_mntflags(sb), PLINK)) { - spin_lock(&sbinfo->si_plink_maint_lock); - if (!sbinfo->si_plink_maint_pid) - sbinfo->si_plink_maint_pid = current->pid; - else - err = -EBUSY; - spin_unlock(&sbinfo->si_plink_maint_lock); - } - si_write_unlock(sb); - - return err; -} - -/* ---------------------------------------------------------------------- */ - -#ifdef CONFIG_AUFS_DEBUG -void au_plink_list(struct super_block *sb) -{ - int i; - struct au_sbinfo *sbinfo; - struct hlist_head *plink_hlist; - struct pseudo_link *plink; - - SiMustAnyLock(sb); - - sbinfo = au_sbi(sb); - AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); - AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); - - for (i = 0; i < AuPlink_NHASH; i++) { - plink_hlist = &sbinfo->si_plink[i].head; - rcu_read_lock(); - hlist_for_each_entry_rcu(plink, plink_hlist, hlist) - AuDbg("%lu\n", plink->inode->i_ino); - rcu_read_unlock(); - } -} -#endif - -/* is the inode pseudo-linked? */ -int au_plink_test(struct inode *inode) -{ - int found, i; - struct au_sbinfo *sbinfo; - struct hlist_head *plink_hlist; - struct pseudo_link *plink; - - sbinfo = au_sbi(inode->i_sb); - AuRwMustAnyLock(&sbinfo->si_rwsem); - AuDebugOn(!au_opt_test(au_mntflags(inode->i_sb), PLINK)); - AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); - - found = 0; - i = au_plink_hash(inode->i_ino); - plink_hlist = &sbinfo->si_plink[i].head; - rcu_read_lock(); - hlist_for_each_entry_rcu(plink, plink_hlist, hlist) - if (plink->inode == inode) { - found = 1; - break; - } - rcu_read_unlock(); - return found; -} - -/* ---------------------------------------------------------------------- */ - -/* - * generate a name for plink. - * the file will be stored under AUFS_WH_PLINKDIR. - */ -/* 20 is max digits length of ulong 64 */ -#define PLINK_NAME_LEN ((20 + 1) * 2) - -static int plink_name(char *name, int len, struct inode *inode, - aufs_bindex_t bindex) -{ - int rlen; - struct inode *h_inode; - - h_inode = au_h_iptr(inode, bindex); - rlen = snprintf(name, len, "%lu.%lu", inode->i_ino, h_inode->i_ino); - return rlen; -} - -struct au_do_plink_lkup_args { - struct dentry **errp; - struct qstr *tgtname; - struct dentry *h_parent; - struct au_branch *br; -}; - -static struct dentry *au_do_plink_lkup(struct qstr *tgtname, - struct dentry *h_parent, - struct au_branch *br) -{ - struct dentry *h_dentry; - struct mutex *h_mtx; - - h_mtx = &d_inode(h_parent)->i_mutex; - mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); - h_dentry = vfsub_lkup_one(tgtname, h_parent); - mutex_unlock(h_mtx); - return h_dentry; -} - -static void au_call_do_plink_lkup(void *args) -{ - struct au_do_plink_lkup_args *a = args; - *a->errp = au_do_plink_lkup(a->tgtname, a->h_parent, a->br); -} - -/* lookup the plink-ed @inode under the branch at @bindex */ -struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex) -{ - struct dentry *h_dentry, *h_parent; - struct au_branch *br; - int wkq_err; - char a[PLINK_NAME_LEN]; - struct qstr tgtname = QSTR_INIT(a, 0); - - AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); - - br = au_sbr(inode->i_sb, bindex); - h_parent = br->br_wbr->wbr_plink; - tgtname.len = plink_name(a, sizeof(a), inode, bindex); - - if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) { - struct au_do_plink_lkup_args args = { - .errp = &h_dentry, - .tgtname = &tgtname, - .h_parent = h_parent, - .br = br - }; - - wkq_err = au_wkq_wait(au_call_do_plink_lkup, &args); - if (unlikely(wkq_err)) - h_dentry = ERR_PTR(wkq_err); - } else - h_dentry = au_do_plink_lkup(&tgtname, h_parent, br); - - return h_dentry; -} - -/* create a pseudo-link */ -static int do_whplink(struct qstr *tgt, struct dentry *h_parent, - struct dentry *h_dentry, struct au_branch *br) -{ - int err; - struct path h_path = { - .mnt = au_br_mnt(br) - }; - struct inode *h_dir, *delegated; - - h_dir = d_inode(h_parent); - mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2); -again: - h_path.dentry = vfsub_lkup_one(tgt, h_parent); - err = PTR_ERR(h_path.dentry); - if (IS_ERR(h_path.dentry)) - goto out; - - err = 0; - /* wh.plink dir is not monitored */ - /* todo: is it really safe? */ - if (d_is_positive(h_path.dentry) - && d_inode(h_path.dentry) != d_inode(h_dentry)) { - delegated = NULL; - err = vfsub_unlink(h_dir, &h_path, &delegated, /*force*/0); - if (unlikely(err == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal unlink\n"); - iput(delegated); - } - dput(h_path.dentry); - h_path.dentry = NULL; - if (!err) - goto again; - } - if (!err && d_is_negative(h_path.dentry)) { - delegated = NULL; - err = vfsub_link(h_dentry, h_dir, &h_path, &delegated); - if (unlikely(err == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal link\n"); - iput(delegated); - } - } - dput(h_path.dentry); - -out: - mutex_unlock(&h_dir->i_mutex); - return err; -} - -struct do_whplink_args { - int *errp; - struct qstr *tgt; - struct dentry *h_parent; - struct dentry *h_dentry; - struct au_branch *br; -}; - -static void call_do_whplink(void *args) -{ - struct do_whplink_args *a = args; - *a->errp = do_whplink(a->tgt, a->h_parent, a->h_dentry, a->br); -} - -static int whplink(struct dentry *h_dentry, struct inode *inode, - aufs_bindex_t bindex, struct au_branch *br) -{ - int err, wkq_err; - struct au_wbr *wbr; - struct dentry *h_parent; - char a[PLINK_NAME_LEN]; - struct qstr tgtname = QSTR_INIT(a, 0); - - wbr = au_sbr(inode->i_sb, bindex)->br_wbr; - h_parent = wbr->wbr_plink; - tgtname.len = plink_name(a, sizeof(a), inode, bindex); - - /* always superio. */ - if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) { - struct do_whplink_args args = { - .errp = &err, - .tgt = &tgtname, - .h_parent = h_parent, - .h_dentry = h_dentry, - .br = br - }; - wkq_err = au_wkq_wait(call_do_whplink, &args); - if (unlikely(wkq_err)) - err = wkq_err; - } else - err = do_whplink(&tgtname, h_parent, h_dentry, br); - - return err; -} - -/* free a single plink */ -static void do_put_plink(struct pseudo_link *plink, int do_del) -{ - if (do_del) - hlist_del(&plink->hlist); - iput(plink->inode); - kfree(plink); -} - -static void do_put_plink_rcu(struct rcu_head *rcu) -{ - struct pseudo_link *plink; - - plink = container_of(rcu, struct pseudo_link, rcu); - iput(plink->inode); - kfree(plink); -} - -/* - * create a new pseudo-link for @h_dentry on @bindex. - * the linked inode is held in aufs @inode. - */ -void au_plink_append(struct inode *inode, aufs_bindex_t bindex, - struct dentry *h_dentry) -{ - struct super_block *sb; - struct au_sbinfo *sbinfo; - struct hlist_head *plink_hlist; - struct pseudo_link *plink, *tmp; - struct au_sphlhead *sphl; - int found, err, cnt, i; - - sb = inode->i_sb; - sbinfo = au_sbi(sb); - AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); - AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); - - found = au_plink_test(inode); - if (found) - return; - - i = au_plink_hash(inode->i_ino); - sphl = sbinfo->si_plink + i; - plink_hlist = &sphl->head; - tmp = kmalloc(sizeof(*plink), GFP_NOFS); - if (tmp) - tmp->inode = au_igrab(inode); - else { - err = -ENOMEM; - goto out; - } - - spin_lock(&sphl->spin); - hlist_for_each_entry(plink, plink_hlist, hlist) { - if (plink->inode == inode) { - found = 1; - break; - } - } - if (!found) - hlist_add_head_rcu(&tmp->hlist, plink_hlist); - spin_unlock(&sphl->spin); - if (!found) { - cnt = au_sphl_count(sphl); -#define msg "unexpectedly unblanced or too many pseudo-links" - if (cnt > AUFS_PLINK_WARN) - AuWarn1(msg ", %d\n", cnt); -#undef msg - err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex)); - } else { - do_put_plink(tmp, 0); - return; - } - -out: - if (unlikely(err)) { - pr_warn("err %d, damaged pseudo link.\n", err); - if (tmp) { - au_sphl_del_rcu(&tmp->hlist, sphl); - call_rcu(&tmp->rcu, do_put_plink_rcu); - } - } -} - -/* free all plinks */ -void au_plink_put(struct super_block *sb, int verbose) -{ - int i, warned; - struct au_sbinfo *sbinfo; - struct hlist_head *plink_hlist; - struct hlist_node *tmp; - struct pseudo_link *plink; - - SiMustWriteLock(sb); - - sbinfo = au_sbi(sb); - AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); - AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); - - /* no spin_lock since sbinfo is write-locked */ - warned = 0; - for (i = 0; i < AuPlink_NHASH; i++) { - plink_hlist = &sbinfo->si_plink[i].head; - if (!warned && verbose && !hlist_empty(plink_hlist)) { - pr_warn("pseudo-link is not flushed"); - warned = 1; - } - hlist_for_each_entry_safe(plink, tmp, plink_hlist, hlist) - do_put_plink(plink, 0); - INIT_HLIST_HEAD(plink_hlist); - } -} - -void au_plink_clean(struct super_block *sb, int verbose) -{ - struct dentry *root; - - root = sb->s_root; - aufs_write_lock(root); - if (au_opt_test(au_mntflags(sb), PLINK)) - au_plink_put(sb, verbose); - aufs_write_unlock(root); -} - -static int au_plink_do_half_refresh(struct inode *inode, aufs_bindex_t br_id) -{ - int do_put; - aufs_bindex_t bstart, bend, bindex; - - do_put = 0; - bstart = au_ibstart(inode); - bend = au_ibend(inode); - if (bstart >= 0) { - for (bindex = bstart; bindex <= bend; bindex++) { - if (!au_h_iptr(inode, bindex) - || au_ii_br_id(inode, bindex) != br_id) - continue; - au_set_h_iptr(inode, bindex, NULL, 0); - do_put = 1; - break; - } - if (do_put) - for (bindex = bstart; bindex <= bend; bindex++) - if (au_h_iptr(inode, bindex)) { - do_put = 0; - break; - } - } else - do_put = 1; - - return do_put; -} - -/* free the plinks on a branch specified by @br_id */ -void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id) -{ - struct au_sbinfo *sbinfo; - struct hlist_head *plink_hlist; - struct hlist_node *tmp; - struct pseudo_link *plink; - struct inode *inode; - int i, do_put; - - SiMustWriteLock(sb); - - sbinfo = au_sbi(sb); - AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); - AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); - - /* no spin_lock since sbinfo is write-locked */ - for (i = 0; i < AuPlink_NHASH; i++) { - plink_hlist = &sbinfo->si_plink[i].head; - hlist_for_each_entry_safe(plink, tmp, plink_hlist, hlist) { - inode = au_igrab(plink->inode); - ii_write_lock_child(inode); - do_put = au_plink_do_half_refresh(inode, br_id); - if (do_put) - do_put_plink(plink, 1); - ii_write_unlock(inode); - iput(inode); - } - } -} diff --git a/fs/aufs/poll.c b/fs/aufs/poll.c deleted file mode 100644 index 262a792fd..000000000 --- a/fs/aufs/poll.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * poll operation - * There is only one filesystem which implements ->poll operation, currently. - */ - -#include "aufs.h" - -unsigned int aufs_poll(struct file *file, poll_table *wait) -{ - unsigned int mask; - int err; - struct file *h_file; - struct super_block *sb; - - /* We should pretend an error happened. */ - mask = POLLERR /* | POLLIN | POLLOUT */; - sb = file->f_path.dentry->d_sb; - si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); - - h_file = au_read_pre(file, /*keep_fi*/0); - err = PTR_ERR(h_file); - if (IS_ERR(h_file)) - goto out; - - /* it is not an error if h_file has no operation */ - mask = DEFAULT_POLLMASK; - if (h_file->f_op->poll) - mask = h_file->f_op->poll(h_file, wait); - fput(h_file); /* instead of au_read_post() */ - -out: - si_read_unlock(sb); - AuTraceErr((int)mask); - return mask; -} diff --git a/fs/aufs/posix_acl.c b/fs/aufs/posix_acl.c deleted file mode 100644 index d5266ef76..000000000 --- a/fs/aufs/posix_acl.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2014-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * posix acl operations - */ - -#include <linux/fs.h> -#include <linux/posix_acl.h> -#include "aufs.h" - -struct posix_acl *aufs_get_acl(struct inode *inode, int type) -{ - struct posix_acl *acl; - int err; - aufs_bindex_t bindex; - struct inode *h_inode; - struct super_block *sb; - - acl = NULL; - sb = inode->i_sb; - si_read_lock(sb, AuLock_FLUSH); - ii_read_lock_child(inode); - if (!(sb->s_flags & MS_POSIXACL)) - goto out; - - bindex = au_ibstart(inode); - h_inode = au_h_iptr(inode, bindex); - if (unlikely(!h_inode - || ((h_inode->i_mode & S_IFMT) - != (inode->i_mode & S_IFMT)))) { - err = au_busy_or_stale(); - acl = ERR_PTR(err); - goto out; - } - - /* always topmost only */ - acl = get_acl(h_inode, type); - -out: - ii_read_unlock(inode); - si_read_unlock(sb); - - AuTraceErrPtr(acl); - return acl; -} - -int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type) -{ - int err; - ssize_t ssz; - struct dentry *dentry; - struct au_srxattr arg = { - .type = AU_ACL_SET, - .u.acl_set = { - .acl = acl, - .type = type - }, - }; - - mutex_lock(&inode->i_mutex); - if (inode->i_ino == AUFS_ROOT_INO) - dentry = dget(inode->i_sb->s_root); - else { - dentry = d_find_alias(inode); - if (!dentry) - dentry = d_find_any_alias(inode); - if (!dentry) { - pr_warn("cannot handle this inode, " - "please report to aufs-users ML\n"); - err = -ENOENT; - goto out; - } - } - - ssz = au_srxattr(dentry, &arg); - dput(dentry); - err = ssz; - if (ssz >= 0) - err = 0; - -out: - mutex_unlock(&inode->i_mutex); - return err; -} diff --git a/fs/aufs/procfs.c b/fs/aufs/procfs.c deleted file mode 100644 index 772be91a9..000000000 --- a/fs/aufs/procfs.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2010-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * procfs interfaces - */ - -#include <linux/proc_fs.h> -#include "aufs.h" - -static int au_procfs_plm_release(struct inode *inode, struct file *file) -{ - struct au_sbinfo *sbinfo; - - sbinfo = file->private_data; - if (sbinfo) { - au_plink_maint_leave(sbinfo); - kobject_put(&sbinfo->si_kobj); - } - - return 0; -} - -static void au_procfs_plm_write_clean(struct file *file) -{ - struct au_sbinfo *sbinfo; - - sbinfo = file->private_data; - if (sbinfo) - au_plink_clean(sbinfo->si_sb, /*verbose*/0); -} - -static int au_procfs_plm_write_si(struct file *file, unsigned long id) -{ - int err; - struct super_block *sb; - struct au_sbinfo *sbinfo; - - err = -EBUSY; - if (unlikely(file->private_data)) - goto out; - - sb = NULL; - /* don't use au_sbilist_lock() here */ - spin_lock(&au_sbilist.spin); - list_for_each_entry(sbinfo, &au_sbilist.head, si_list) - if (id == sysaufs_si_id(sbinfo)) { - kobject_get(&sbinfo->si_kobj); - sb = sbinfo->si_sb; - break; - } - spin_unlock(&au_sbilist.spin); - - err = -EINVAL; - if (unlikely(!sb)) - goto out; - - err = au_plink_maint_enter(sb); - if (!err) - /* keep kobject_get() */ - file->private_data = sbinfo; - else - kobject_put(&sbinfo->si_kobj); -out: - return err; -} - -/* - * Accept a valid "si=xxxx" only. - * Once it is accepted successfully, accept "clean" too. - */ -static ssize_t au_procfs_plm_write(struct file *file, const char __user *ubuf, - size_t count, loff_t *ppos) -{ - ssize_t err; - unsigned long id; - /* last newline is allowed */ - char buf[3 + sizeof(unsigned long) * 2 + 1]; - - err = -EACCES; - if (unlikely(!capable(CAP_SYS_ADMIN))) - goto out; - - err = -EINVAL; - if (unlikely(count > sizeof(buf))) - goto out; - - err = copy_from_user(buf, ubuf, count); - if (unlikely(err)) { - err = -EFAULT; - goto out; - } - buf[count] = 0; - - err = -EINVAL; - if (!strcmp("clean", buf)) { - au_procfs_plm_write_clean(file); - goto out_success; - } else if (unlikely(strncmp("si=", buf, 3))) - goto out; - - err = kstrtoul(buf + 3, 16, &id); - if (unlikely(err)) - goto out; - - err = au_procfs_plm_write_si(file, id); - if (unlikely(err)) - goto out; - -out_success: - err = count; /* success */ -out: - return err; -} - -static const struct file_operations au_procfs_plm_fop = { - .write = au_procfs_plm_write, - .release = au_procfs_plm_release, - .owner = THIS_MODULE -}; - -/* ---------------------------------------------------------------------- */ - -static struct proc_dir_entry *au_procfs_dir; - -void au_procfs_fin(void) -{ - remove_proc_entry(AUFS_PLINK_MAINT_NAME, au_procfs_dir); - remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL); -} - -int __init au_procfs_init(void) -{ - int err; - struct proc_dir_entry *entry; - - err = -ENOMEM; - au_procfs_dir = proc_mkdir(AUFS_PLINK_MAINT_DIR, NULL); - if (unlikely(!au_procfs_dir)) - goto out; - - entry = proc_create(AUFS_PLINK_MAINT_NAME, S_IFREG | S_IWUSR, - au_procfs_dir, &au_procfs_plm_fop); - if (unlikely(!entry)) - goto out_dir; - - err = 0; - goto out; /* success */ - - -out_dir: - remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL); -out: - return err; -} diff --git a/fs/aufs/rdu.c b/fs/aufs/rdu.c deleted file mode 100644 index 3beba1806..000000000 --- a/fs/aufs/rdu.c +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * readdir in userspace. - */ - -#include <linux/compat.h> -#include <linux/fs_stack.h> -#include <linux/security.h> -#include "aufs.h" - -/* bits for struct aufs_rdu.flags */ -#define AuRdu_CALLED 1 -#define AuRdu_CONT (1 << 1) -#define AuRdu_FULL (1 << 2) -#define au_ftest_rdu(flags, name) ((flags) & AuRdu_##name) -#define au_fset_rdu(flags, name) \ - do { (flags) |= AuRdu_##name; } while (0) -#define au_fclr_rdu(flags, name) \ - do { (flags) &= ~AuRdu_##name; } while (0) - -struct au_rdu_arg { - struct dir_context ctx; - struct aufs_rdu *rdu; - union au_rdu_ent_ul ent; - unsigned long end; - - struct super_block *sb; - int err; -}; - -static int au_rdu_fill(struct dir_context *ctx, const char *name, int nlen, - loff_t offset, u64 h_ino, unsigned int d_type) -{ - int err, len; - struct au_rdu_arg *arg = container_of(ctx, struct au_rdu_arg, ctx); - struct aufs_rdu *rdu = arg->rdu; - struct au_rdu_ent ent; - - err = 0; - arg->err = 0; - au_fset_rdu(rdu->cookie.flags, CALLED); - len = au_rdu_len(nlen); - if (arg->ent.ul + len < arg->end) { - ent.ino = h_ino; - ent.bindex = rdu->cookie.bindex; - ent.type = d_type; - ent.nlen = nlen; - if (unlikely(nlen > AUFS_MAX_NAMELEN)) - ent.type = DT_UNKNOWN; - - /* unnecessary to support mmap_sem since this is a dir */ - err = -EFAULT; - if (copy_to_user(arg->ent.e, &ent, sizeof(ent))) - goto out; - if (copy_to_user(arg->ent.e->name, name, nlen)) - goto out; - /* the terminating NULL */ - if (__put_user(0, arg->ent.e->name + nlen)) - goto out; - err = 0; - /* AuDbg("%p, %.*s\n", arg->ent.p, nlen, name); */ - arg->ent.ul += len; - rdu->rent++; - } else { - err = -EFAULT; - au_fset_rdu(rdu->cookie.flags, FULL); - rdu->full = 1; - rdu->tail = arg->ent; - } - -out: - /* AuTraceErr(err); */ - return err; -} - -static int au_rdu_do(struct file *h_file, struct au_rdu_arg *arg) -{ - int err; - loff_t offset; - struct au_rdu_cookie *cookie = &arg->rdu->cookie; - - /* we don't have to care (FMODE_32BITHASH | FMODE_64BITHASH) for ext4 */ - offset = vfsub_llseek(h_file, cookie->h_pos, SEEK_SET); - err = offset; - if (unlikely(offset != cookie->h_pos)) - goto out; - - err = 0; - do { - arg->err = 0; - au_fclr_rdu(cookie->flags, CALLED); - /* smp_mb(); */ - err = vfsub_iterate_dir(h_file, &arg->ctx); - if (err >= 0) - err = arg->err; - } while (!err - && au_ftest_rdu(cookie->flags, CALLED) - && !au_ftest_rdu(cookie->flags, FULL)); - cookie->h_pos = h_file->f_pos; - -out: - AuTraceErr(err); - return err; -} - -static int au_rdu(struct file *file, struct aufs_rdu *rdu) -{ - int err; - aufs_bindex_t bend; - struct au_rdu_arg arg = { - .ctx = { - .actor = au_rdu_fill - } - }; - struct dentry *dentry; - struct inode *inode; - struct file *h_file; - struct au_rdu_cookie *cookie = &rdu->cookie; - - err = !access_ok(VERIFY_WRITE, rdu->ent.e, rdu->sz); - if (unlikely(err)) { - err = -EFAULT; - AuTraceErr(err); - goto out; - } - rdu->rent = 0; - rdu->tail = rdu->ent; - rdu->full = 0; - arg.rdu = rdu; - arg.ent = rdu->ent; - arg.end = arg.ent.ul; - arg.end += rdu->sz; - - err = -ENOTDIR; - if (unlikely(!file->f_op->iterate)) - goto out; - - err = security_file_permission(file, MAY_READ); - AuTraceErr(err); - if (unlikely(err)) - goto out; - - dentry = file->f_path.dentry; - inode = d_inode(dentry); -#if 1 - mutex_lock(&inode->i_mutex); -#else - err = mutex_lock_killable(&inode->i_mutex); - AuTraceErr(err); - if (unlikely(err)) - goto out; -#endif - - arg.sb = inode->i_sb; - err = si_read_lock(arg.sb, AuLock_FLUSH | AuLock_NOPLM); - if (unlikely(err)) - goto out_mtx; - err = au_alive_dir(dentry); - if (unlikely(err)) - goto out_si; - /* todo: reval? */ - fi_read_lock(file); - - err = -EAGAIN; - if (unlikely(au_ftest_rdu(cookie->flags, CONT) - && cookie->generation != au_figen(file))) - goto out_unlock; - - err = 0; - if (!rdu->blk) { - rdu->blk = au_sbi(arg.sb)->si_rdblk; - if (!rdu->blk) - rdu->blk = au_dir_size(file, /*dentry*/NULL); - } - bend = au_fbstart(file); - if (cookie->bindex < bend) - cookie->bindex = bend; - bend = au_fbend_dir(file); - /* AuDbg("b%d, b%d\n", cookie->bindex, bend); */ - for (; !err && cookie->bindex <= bend; - cookie->bindex++, cookie->h_pos = 0) { - h_file = au_hf_dir(file, cookie->bindex); - if (!h_file) - continue; - - au_fclr_rdu(cookie->flags, FULL); - err = au_rdu_do(h_file, &arg); - AuTraceErr(err); - if (unlikely(au_ftest_rdu(cookie->flags, FULL) || err)) - break; - } - AuDbg("rent %llu\n", rdu->rent); - - if (!err && !au_ftest_rdu(cookie->flags, CONT)) { - rdu->shwh = !!au_opt_test(au_sbi(arg.sb)->si_mntflags, SHWH); - au_fset_rdu(cookie->flags, CONT); - cookie->generation = au_figen(file); - } - - ii_read_lock_child(inode); - fsstack_copy_attr_atime(inode, au_h_iptr(inode, au_ibstart(inode))); - ii_read_unlock(inode); - -out_unlock: - fi_read_unlock(file); -out_si: - si_read_unlock(arg.sb); -out_mtx: - mutex_unlock(&inode->i_mutex); -out: - AuTraceErr(err); - return err; -} - -static int au_rdu_ino(struct file *file, struct aufs_rdu *rdu) -{ - int err; - ino_t ino; - unsigned long long nent; - union au_rdu_ent_ul *u; - struct au_rdu_ent ent; - struct super_block *sb; - - err = 0; - nent = rdu->nent; - u = &rdu->ent; - sb = file->f_path.dentry->d_sb; - si_read_lock(sb, AuLock_FLUSH); - while (nent-- > 0) { - /* unnecessary to support mmap_sem since this is a dir */ - err = copy_from_user(&ent, u->e, sizeof(ent)); - if (!err) - err = !access_ok(VERIFY_WRITE, &u->e->ino, sizeof(ino)); - if (unlikely(err)) { - err = -EFAULT; - AuTraceErr(err); - break; - } - - /* AuDbg("b%d, i%llu\n", ent.bindex, ent.ino); */ - if (!ent.wh) - err = au_ino(sb, ent.bindex, ent.ino, ent.type, &ino); - else - err = au_wh_ino(sb, ent.bindex, ent.ino, ent.type, - &ino); - if (unlikely(err)) { - AuTraceErr(err); - break; - } - - err = __put_user(ino, &u->e->ino); - if (unlikely(err)) { - err = -EFAULT; - AuTraceErr(err); - break; - } - u->ul += au_rdu_len(ent.nlen); - } - si_read_unlock(sb); - - return err; -} - -/* ---------------------------------------------------------------------- */ - -static int au_rdu_verify(struct aufs_rdu *rdu) -{ - AuDbg("rdu{%llu, %p, %u | %u | %llu, %u, %u | " - "%llu, b%d, 0x%x, g%u}\n", - rdu->sz, rdu->ent.e, rdu->verify[AufsCtlRduV_SZ], - rdu->blk, - rdu->rent, rdu->shwh, rdu->full, - rdu->cookie.h_pos, rdu->cookie.bindex, rdu->cookie.flags, - rdu->cookie.generation); - - if (rdu->verify[AufsCtlRduV_SZ] == sizeof(*rdu)) - return 0; - - AuDbg("%u:%u\n", - rdu->verify[AufsCtlRduV_SZ], (unsigned int)sizeof(*rdu)); - return -EINVAL; -} - -long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - long err, e; - struct aufs_rdu rdu; - void __user *p = (void __user *)arg; - - err = copy_from_user(&rdu, p, sizeof(rdu)); - if (unlikely(err)) { - err = -EFAULT; - AuTraceErr(err); - goto out; - } - err = au_rdu_verify(&rdu); - if (unlikely(err)) - goto out; - - switch (cmd) { - case AUFS_CTL_RDU: - err = au_rdu(file, &rdu); - if (unlikely(err)) - break; - - e = copy_to_user(p, &rdu, sizeof(rdu)); - if (unlikely(e)) { - err = -EFAULT; - AuTraceErr(err); - } - break; - case AUFS_CTL_RDU_INO: - err = au_rdu_ino(file, &rdu); - break; - - default: - /* err = -ENOTTY; */ - err = -EINVAL; - } - -out: - AuTraceErr(err); - return err; -} - -#ifdef CONFIG_COMPAT -long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - long err, e; - struct aufs_rdu rdu; - void __user *p = compat_ptr(arg); - - /* todo: get_user()? */ - err = copy_from_user(&rdu, p, sizeof(rdu)); - if (unlikely(err)) { - err = -EFAULT; - AuTraceErr(err); - goto out; - } - rdu.ent.e = compat_ptr(rdu.ent.ul); - err = au_rdu_verify(&rdu); - if (unlikely(err)) - goto out; - - switch (cmd) { - case AUFS_CTL_RDU: - err = au_rdu(file, &rdu); - if (unlikely(err)) - break; - - rdu.ent.ul = ptr_to_compat(rdu.ent.e); - rdu.tail.ul = ptr_to_compat(rdu.tail.e); - e = copy_to_user(p, &rdu, sizeof(rdu)); - if (unlikely(e)) { - err = -EFAULT; - AuTraceErr(err); - } - break; - case AUFS_CTL_RDU_INO: - err = au_rdu_ino(file, &rdu); - break; - - default: - /* err = -ENOTTY; */ - err = -EINVAL; - } - -out: - AuTraceErr(err); - return err; -} -#endif diff --git a/fs/aufs/rwsem.h b/fs/aufs/rwsem.h deleted file mode 100644 index deb813f3e..000000000 --- a/fs/aufs/rwsem.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * simple read-write semaphore wrappers - */ - -#ifndef __AUFS_RWSEM_H__ -#define __AUFS_RWSEM_H__ - -#ifdef __KERNEL__ - -#include "debug.h" - -struct au_rwsem { - struct rw_semaphore rwsem; -#ifdef CONFIG_AUFS_DEBUG - /* just for debugging, not almighty counter */ - atomic_t rcnt, wcnt; -#endif -}; - -#ifdef CONFIG_AUFS_DEBUG -#define AuDbgCntInit(rw) do { \ - atomic_set(&(rw)->rcnt, 0); \ - atomic_set(&(rw)->wcnt, 0); \ - smp_mb(); /* atomic set */ \ -} while (0) - -#define AuDbgRcntInc(rw) atomic_inc(&(rw)->rcnt) -#define AuDbgRcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->rcnt) < 0) -#define AuDbgWcntInc(rw) atomic_inc(&(rw)->wcnt) -#define AuDbgWcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->wcnt) < 0) -#else -#define AuDbgCntInit(rw) do {} while (0) -#define AuDbgRcntInc(rw) do {} while (0) -#define AuDbgRcntDec(rw) do {} while (0) -#define AuDbgWcntInc(rw) do {} while (0) -#define AuDbgWcntDec(rw) do {} while (0) -#endif /* CONFIG_AUFS_DEBUG */ - -/* to debug easier, do not make them inlined functions */ -#define AuRwMustNoWaiters(rw) AuDebugOn(!list_empty(&(rw)->rwsem.wait_list)) -/* rwsem_is_locked() is unusable */ -#define AuRwMustReadLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0) -#define AuRwMustWriteLock(rw) AuDebugOn(atomic_read(&(rw)->wcnt) <= 0) -#define AuRwMustAnyLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0 \ - && atomic_read(&(rw)->wcnt) <= 0) -#define AuRwDestroy(rw) AuDebugOn(atomic_read(&(rw)->rcnt) \ - || atomic_read(&(rw)->wcnt)) - -#define au_rw_class(rw, key) lockdep_set_class(&(rw)->rwsem, key) - -static inline void au_rw_init(struct au_rwsem *rw) -{ - AuDbgCntInit(rw); - init_rwsem(&rw->rwsem); -} - -static inline void au_rw_init_wlock(struct au_rwsem *rw) -{ - au_rw_init(rw); - down_write(&rw->rwsem); - AuDbgWcntInc(rw); -} - -static inline void au_rw_init_wlock_nested(struct au_rwsem *rw, - unsigned int lsc) -{ - au_rw_init(rw); - down_write_nested(&rw->rwsem, lsc); - AuDbgWcntInc(rw); -} - -static inline void au_rw_read_lock(struct au_rwsem *rw) -{ - down_read(&rw->rwsem); - AuDbgRcntInc(rw); -} - -static inline void au_rw_read_lock_nested(struct au_rwsem *rw, unsigned int lsc) -{ - down_read_nested(&rw->rwsem, lsc); - AuDbgRcntInc(rw); -} - -static inline void au_rw_read_unlock(struct au_rwsem *rw) -{ - AuRwMustReadLock(rw); - AuDbgRcntDec(rw); - up_read(&rw->rwsem); -} - -static inline void au_rw_dgrade_lock(struct au_rwsem *rw) -{ - AuRwMustWriteLock(rw); - AuDbgRcntInc(rw); - AuDbgWcntDec(rw); - downgrade_write(&rw->rwsem); -} - -static inline void au_rw_write_lock(struct au_rwsem *rw) -{ - down_write(&rw->rwsem); - AuDbgWcntInc(rw); -} - -static inline void au_rw_write_lock_nested(struct au_rwsem *rw, - unsigned int lsc) -{ - down_write_nested(&rw->rwsem, lsc); - AuDbgWcntInc(rw); -} - -static inline void au_rw_write_unlock(struct au_rwsem *rw) -{ - AuRwMustWriteLock(rw); - AuDbgWcntDec(rw); - up_write(&rw->rwsem); -} - -/* why is not _nested version defined */ -static inline int au_rw_read_trylock(struct au_rwsem *rw) -{ - int ret; - - ret = down_read_trylock(&rw->rwsem); - if (ret) - AuDbgRcntInc(rw); - return ret; -} - -static inline int au_rw_write_trylock(struct au_rwsem *rw) -{ - int ret; - - ret = down_write_trylock(&rw->rwsem); - if (ret) - AuDbgWcntInc(rw); - return ret; -} - -#undef AuDbgCntInit -#undef AuDbgRcntInc -#undef AuDbgRcntDec -#undef AuDbgWcntInc -#undef AuDbgWcntDec - -#define AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ -static inline void prefix##_read_lock(param) \ -{ au_rw_read_lock(rwsem); } \ -static inline void prefix##_write_lock(param) \ -{ au_rw_write_lock(rwsem); } \ -static inline int prefix##_read_trylock(param) \ -{ return au_rw_read_trylock(rwsem); } \ -static inline int prefix##_write_trylock(param) \ -{ return au_rw_write_trylock(rwsem); } -/* why is not _nested version defined */ -/* static inline void prefix##_read_trylock_nested(param, lsc) -{ au_rw_read_trylock_nested(rwsem, lsc)); } -static inline void prefix##_write_trylock_nestd(param, lsc) -{ au_rw_write_trylock_nested(rwsem, lsc); } */ - -#define AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) \ -static inline void prefix##_read_unlock(param) \ -{ au_rw_read_unlock(rwsem); } \ -static inline void prefix##_write_unlock(param) \ -{ au_rw_write_unlock(rwsem); } \ -static inline void prefix##_downgrade_lock(param) \ -{ au_rw_dgrade_lock(rwsem); } - -#define AuSimpleRwsemFuncs(prefix, param, rwsem) \ - AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ - AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) - -#endif /* __KERNEL__ */ -#endif /* __AUFS_RWSEM_H__ */ diff --git a/fs/aufs/sbinfo.c b/fs/aufs/sbinfo.c deleted file mode 100644 index 4e59efc08..000000000 --- a/fs/aufs/sbinfo.c +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * superblock private data - */ - -#include "aufs.h" - -/* - * they are necessary regardless sysfs is disabled. - */ -void au_si_free(struct kobject *kobj) -{ - int i; - struct au_sbinfo *sbinfo; - char *locked __maybe_unused; /* debug only */ - - sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); - for (i = 0; i < AuPlink_NHASH; i++) - AuDebugOn(!hlist_empty(&sbinfo->si_plink[i].head)); - AuDebugOn(atomic_read(&sbinfo->si_nowait.nw_len)); - - au_rw_write_lock(&sbinfo->si_rwsem); - au_br_free(sbinfo); - au_rw_write_unlock(&sbinfo->si_rwsem); - - AuDebugOn(radix_tree_gang_lookup - (&sbinfo->au_si_pid.tree, (void **)&locked, - /*first_index*/PID_MAX_DEFAULT - 1, - /*max_items*/sizeof(locked)/sizeof(*locked))); - - kfree(sbinfo->si_branch); - kfree(sbinfo->au_si_pid.bitmap); - mutex_destroy(&sbinfo->si_xib_mtx); - AuRwDestroy(&sbinfo->si_rwsem); - - kfree(sbinfo); -} - -int au_si_alloc(struct super_block *sb) -{ - int err, i; - struct au_sbinfo *sbinfo; - static struct lock_class_key aufs_si; - - err = -ENOMEM; - sbinfo = kzalloc(sizeof(*sbinfo), GFP_NOFS); - if (unlikely(!sbinfo)) - goto out; - - BUILD_BUG_ON(sizeof(unsigned long) != - sizeof(*sbinfo->au_si_pid.bitmap)); - sbinfo->au_si_pid.bitmap = kcalloc(BITS_TO_LONGS(PID_MAX_DEFAULT), - sizeof(*sbinfo->au_si_pid.bitmap), - GFP_NOFS); - if (unlikely(!sbinfo->au_si_pid.bitmap)) - goto out_sbinfo; - - /* will be reallocated separately */ - sbinfo->si_branch = kzalloc(sizeof(*sbinfo->si_branch), GFP_NOFS); - if (unlikely(!sbinfo->si_branch)) - goto out_pidmap; - - err = sysaufs_si_init(sbinfo); - if (unlikely(err)) - goto out_br; - - au_nwt_init(&sbinfo->si_nowait); - au_rw_init_wlock(&sbinfo->si_rwsem); - au_rw_class(&sbinfo->si_rwsem, &aufs_si); - spin_lock_init(&sbinfo->au_si_pid.tree_lock); - INIT_RADIX_TREE(&sbinfo->au_si_pid.tree, GFP_ATOMIC | __GFP_NOFAIL); - - atomic_long_set(&sbinfo->si_ninodes, 0); - atomic_long_set(&sbinfo->si_nfiles, 0); - - sbinfo->si_bend = -1; - sbinfo->si_last_br_id = AUFS_BRANCH_MAX / 2; - - sbinfo->si_wbr_copyup = AuWbrCopyup_Def; - sbinfo->si_wbr_create = AuWbrCreate_Def; - sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + sbinfo->si_wbr_copyup; - sbinfo->si_wbr_create_ops = au_wbr_create_ops + sbinfo->si_wbr_create; - - au_fhsm_init(sbinfo); - - sbinfo->si_mntflags = au_opts_plink(AuOpt_Def); - - sbinfo->si_xino_jiffy = jiffies; - sbinfo->si_xino_expire - = msecs_to_jiffies(AUFS_XINO_DEF_SEC * MSEC_PER_SEC); - mutex_init(&sbinfo->si_xib_mtx); - sbinfo->si_xino_brid = -1; - /* leave si_xib_last_pindex and si_xib_next_bit */ - - au_sphl_init(&sbinfo->si_aopen); - - sbinfo->si_rdcache = msecs_to_jiffies(AUFS_RDCACHE_DEF * MSEC_PER_SEC); - sbinfo->si_rdblk = AUFS_RDBLK_DEF; - sbinfo->si_rdhash = AUFS_RDHASH_DEF; - sbinfo->si_dirwh = AUFS_DIRWH_DEF; - - for (i = 0; i < AuPlink_NHASH; i++) - au_sphl_init(sbinfo->si_plink + i); - init_waitqueue_head(&sbinfo->si_plink_wq); - spin_lock_init(&sbinfo->si_plink_maint_lock); - - au_sphl_init(&sbinfo->si_files); - - /* leave other members for sysaufs and si_mnt. */ - sbinfo->si_sb = sb; - sb->s_fs_info = sbinfo; - si_pid_set(sb); - return 0; /* success */ - -out_br: - kfree(sbinfo->si_branch); -out_pidmap: - kfree(sbinfo->au_si_pid.bitmap); -out_sbinfo: - kfree(sbinfo); -out: - return err; -} - -int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr) -{ - int err, sz; - struct au_branch **brp; - - AuRwMustWriteLock(&sbinfo->si_rwsem); - - err = -ENOMEM; - sz = sizeof(*brp) * (sbinfo->si_bend + 1); - if (unlikely(!sz)) - sz = sizeof(*brp); - brp = au_kzrealloc(sbinfo->si_branch, sz, sizeof(*brp) * nbr, GFP_NOFS); - if (brp) { - sbinfo->si_branch = brp; - err = 0; - } - - return err; -} - -/* ---------------------------------------------------------------------- */ - -unsigned int au_sigen_inc(struct super_block *sb) -{ - unsigned int gen; - struct inode *inode; - - SiMustWriteLock(sb); - - gen = ++au_sbi(sb)->si_generation; - au_update_digen(sb->s_root); - inode = d_inode(sb->s_root); - au_update_iigen(inode, /*half*/0); - inode->i_version++; - return gen; -} - -aufs_bindex_t au_new_br_id(struct super_block *sb) -{ - aufs_bindex_t br_id; - int i; - struct au_sbinfo *sbinfo; - - SiMustWriteLock(sb); - - sbinfo = au_sbi(sb); - for (i = 0; i <= AUFS_BRANCH_MAX; i++) { - br_id = ++sbinfo->si_last_br_id; - AuDebugOn(br_id < 0); - if (br_id && au_br_index(sb, br_id) < 0) - return br_id; - } - - return -1; -} - -/* ---------------------------------------------------------------------- */ - -/* it is ok that new 'nwt' tasks are appended while we are sleeping */ -int si_read_lock(struct super_block *sb, int flags) -{ - int err; - - err = 0; - if (au_ftest_lock(flags, FLUSH)) - au_nwt_flush(&au_sbi(sb)->si_nowait); - - si_noflush_read_lock(sb); - err = au_plink_maint(sb, flags); - if (unlikely(err)) - si_read_unlock(sb); - - return err; -} - -int si_write_lock(struct super_block *sb, int flags) -{ - int err; - - if (au_ftest_lock(flags, FLUSH)) - au_nwt_flush(&au_sbi(sb)->si_nowait); - - si_noflush_write_lock(sb); - err = au_plink_maint(sb, flags); - if (unlikely(err)) - si_write_unlock(sb); - - return err; -} - -/* dentry and super_block lock. call at entry point */ -int aufs_read_lock(struct dentry *dentry, int flags) -{ - int err; - struct super_block *sb; - - sb = dentry->d_sb; - err = si_read_lock(sb, flags); - if (unlikely(err)) - goto out; - - if (au_ftest_lock(flags, DW)) - di_write_lock_child(dentry); - else - di_read_lock_child(dentry, flags); - - if (au_ftest_lock(flags, GEN)) { - err = au_digen_test(dentry, au_sigen(sb)); - AuDebugOn(!err && au_dbrange_test(dentry)); - if (unlikely(err)) - aufs_read_unlock(dentry, flags); - } - -out: - return err; -} - -void aufs_read_unlock(struct dentry *dentry, int flags) -{ - if (au_ftest_lock(flags, DW)) - di_write_unlock(dentry); - else - di_read_unlock(dentry, flags); - si_read_unlock(dentry->d_sb); -} - -void aufs_write_lock(struct dentry *dentry) -{ - si_write_lock(dentry->d_sb, AuLock_FLUSH | AuLock_NOPLMW); - di_write_lock_child(dentry); -} - -void aufs_write_unlock(struct dentry *dentry) -{ - di_write_unlock(dentry); - si_write_unlock(dentry->d_sb); -} - -int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags) -{ - int err; - unsigned int sigen; - struct super_block *sb; - - sb = d1->d_sb; - err = si_read_lock(sb, flags); - if (unlikely(err)) - goto out; - - di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIR)); - - if (au_ftest_lock(flags, GEN)) { - sigen = au_sigen(sb); - err = au_digen_test(d1, sigen); - AuDebugOn(!err && au_dbrange_test(d1)); - if (!err) { - err = au_digen_test(d2, sigen); - AuDebugOn(!err && au_dbrange_test(d2)); - } - if (unlikely(err)) - aufs_read_and_write_unlock2(d1, d2); - } - -out: - return err; -} - -void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2) -{ - di_write_unlock2(d1, d2); - si_read_unlock(d1->d_sb); -} - -/* ---------------------------------------------------------------------- */ - -int si_pid_test_slow(struct super_block *sb) -{ - void *p; - - rcu_read_lock(); - p = radix_tree_lookup(&au_sbi(sb)->au_si_pid.tree, current->pid); - rcu_read_unlock(); - - return (long)!!p; -} - -void si_pid_set_slow(struct super_block *sb) -{ - int err; - struct au_sbinfo *sbinfo; - - AuDebugOn(si_pid_test_slow(sb)); - - sbinfo = au_sbi(sb); - err = radix_tree_preload(GFP_NOFS | __GFP_NOFAIL); - AuDebugOn(err); - spin_lock(&sbinfo->au_si_pid.tree_lock); - err = radix_tree_insert(&sbinfo->au_si_pid.tree, current->pid, - /*any valid ptr*/sb); - spin_unlock(&sbinfo->au_si_pid.tree_lock); - AuDebugOn(err); - radix_tree_preload_end(); -} - -void si_pid_clr_slow(struct super_block *sb) -{ - void *p; - struct au_sbinfo *sbinfo; - - AuDebugOn(!si_pid_test_slow(sb)); - - sbinfo = au_sbi(sb); - spin_lock(&sbinfo->au_si_pid.tree_lock); - p = radix_tree_delete(&sbinfo->au_si_pid.tree, current->pid); - spin_unlock(&sbinfo->au_si_pid.tree_lock); -} diff --git a/fs/aufs/spl.h b/fs/aufs/spl.h deleted file mode 100644 index 94f09f298..000000000 --- a/fs/aufs/spl.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * simple list protected by a spinlock - */ - -#ifndef __AUFS_SPL_H__ -#define __AUFS_SPL_H__ - -#ifdef __KERNEL__ - -struct au_splhead { - spinlock_t spin; - struct list_head head; -}; - -static inline void au_spl_init(struct au_splhead *spl) -{ - spin_lock_init(&spl->spin); - INIT_LIST_HEAD(&spl->head); -} - -static inline void au_spl_add(struct list_head *list, struct au_splhead *spl) -{ - spin_lock(&spl->spin); - list_add(list, &spl->head); - spin_unlock(&spl->spin); -} - -static inline void au_spl_del(struct list_head *list, struct au_splhead *spl) -{ - spin_lock(&spl->spin); - list_del(list); - spin_unlock(&spl->spin); -} - -static inline void au_spl_del_rcu(struct list_head *list, - struct au_splhead *spl) -{ - spin_lock(&spl->spin); - list_del_rcu(list); - spin_unlock(&spl->spin); -} - -/* ---------------------------------------------------------------------- */ - -struct au_sphlhead { - spinlock_t spin; - struct hlist_head head; -}; - -static inline void au_sphl_init(struct au_sphlhead *sphl) -{ - spin_lock_init(&sphl->spin); - INIT_HLIST_HEAD(&sphl->head); -} - -static inline void au_sphl_add(struct hlist_node *hlist, - struct au_sphlhead *sphl) -{ - spin_lock(&sphl->spin); - hlist_add_head(hlist, &sphl->head); - spin_unlock(&sphl->spin); -} - -static inline void au_sphl_del(struct hlist_node *hlist, - struct au_sphlhead *sphl) -{ - spin_lock(&sphl->spin); - hlist_del(hlist); - spin_unlock(&sphl->spin); -} - -static inline void au_sphl_del_rcu(struct hlist_node *hlist, - struct au_sphlhead *sphl) -{ - spin_lock(&sphl->spin); - hlist_del_rcu(hlist); - spin_unlock(&sphl->spin); -} - -static inline unsigned long au_sphl_count(struct au_sphlhead *sphl) -{ - unsigned long cnt; - struct hlist_node *pos; - - cnt = 0; - spin_lock(&sphl->spin); - hlist_for_each(pos, &sphl->head) - cnt++; - spin_unlock(&sphl->spin); - return cnt; -} - -#endif /* __KERNEL__ */ -#endif /* __AUFS_SPL_H__ */ diff --git a/fs/aufs/super.c b/fs/aufs/super.c deleted file mode 100644 index 6b7f38e48..000000000 --- a/fs/aufs/super.c +++ /dev/null @@ -1,1004 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * mount and super_block operations - */ - -#include <linux/mm.h> -#include <linux/seq_file.h> -#include <linux/statfs.h> -#include <linux/vmalloc.h> -#include "aufs.h" - -/* - * super_operations - */ -static struct inode *aufs_alloc_inode(struct super_block *sb __maybe_unused) -{ - struct au_icntnr *c; - - c = au_cache_alloc_icntnr(); - if (c) { - au_icntnr_init(c); - c->vfs_inode.i_version = 1; /* sigen(sb); */ - c->iinfo.ii_hinode = NULL; - return &c->vfs_inode; - } - return NULL; -} - -static void aufs_destroy_inode_cb(struct rcu_head *head) -{ - struct inode *inode = container_of(head, struct inode, i_rcu); - - INIT_HLIST_HEAD(&inode->i_dentry); - au_cache_free_icntnr(container_of(inode, struct au_icntnr, vfs_inode)); -} - -static void aufs_destroy_inode(struct inode *inode) -{ - au_iinfo_fin(inode); - call_rcu(&inode->i_rcu, aufs_destroy_inode_cb); -} - -struct inode *au_iget_locked(struct super_block *sb, ino_t ino) -{ - struct inode *inode; - int err; - - inode = iget_locked(sb, ino); - if (unlikely(!inode)) { - inode = ERR_PTR(-ENOMEM); - goto out; - } - if (!(inode->i_state & I_NEW)) - goto out; - - err = au_xigen_new(inode); - if (!err) - err = au_iinfo_init(inode); - if (!err) - inode->i_version++; - else { - iget_failed(inode); - inode = ERR_PTR(err); - } - -out: - /* never return NULL */ - AuDebugOn(!inode); - AuTraceErrPtr(inode); - return inode; -} - -/* lock free root dinfo */ -static int au_show_brs(struct seq_file *seq, struct super_block *sb) -{ - int err; - aufs_bindex_t bindex, bend; - struct path path; - struct au_hdentry *hdp; - struct au_branch *br; - au_br_perm_str_t perm; - - err = 0; - bend = au_sbend(sb); - hdp = au_di(sb->s_root)->di_hdentry; - for (bindex = 0; !err && bindex <= bend; bindex++) { - br = au_sbr(sb, bindex); - path.mnt = au_br_mnt(br); - path.dentry = hdp[bindex].hd_dentry; - err = au_seq_path(seq, &path); - if (err > 0) { - au_optstr_br_perm(&perm, br->br_perm); - err = seq_printf(seq, "=%s", perm.a); - if (err == -1) - err = -E2BIG; - } - if (!err && bindex != bend) - err = seq_putc(seq, ':'); - } - - return err; -} - -static void au_show_wbr_create(struct seq_file *m, int v, - struct au_sbinfo *sbinfo) -{ - const char *pat; - - AuRwMustAnyLock(&sbinfo->si_rwsem); - - seq_puts(m, ",create="); - pat = au_optstr_wbr_create(v); - switch (v) { - case AuWbrCreate_TDP: - case AuWbrCreate_RR: - case AuWbrCreate_MFS: - case AuWbrCreate_PMFS: - seq_puts(m, pat); - break; - case AuWbrCreate_MFSV: - seq_printf(m, /*pat*/"mfs:%lu", - jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) - / MSEC_PER_SEC); - break; - case AuWbrCreate_PMFSV: - seq_printf(m, /*pat*/"pmfs:%lu", - jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) - / MSEC_PER_SEC); - break; - case AuWbrCreate_MFSRR: - seq_printf(m, /*pat*/"mfsrr:%llu", - sbinfo->si_wbr_mfs.mfsrr_watermark); - break; - case AuWbrCreate_MFSRRV: - seq_printf(m, /*pat*/"mfsrr:%llu:%lu", - sbinfo->si_wbr_mfs.mfsrr_watermark, - jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) - / MSEC_PER_SEC); - break; - case AuWbrCreate_PMFSRR: - seq_printf(m, /*pat*/"pmfsrr:%llu", - sbinfo->si_wbr_mfs.mfsrr_watermark); - break; - case AuWbrCreate_PMFSRRV: - seq_printf(m, /*pat*/"pmfsrr:%llu:%lu", - sbinfo->si_wbr_mfs.mfsrr_watermark, - jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) - / MSEC_PER_SEC); - break; - } -} - -static int au_show_xino(struct seq_file *seq, struct super_block *sb) -{ -#ifdef CONFIG_SYSFS - return 0; -#else - int err; - const int len = sizeof(AUFS_XINO_FNAME) - 1; - aufs_bindex_t bindex, brid; - struct qstr *name; - struct file *f; - struct dentry *d, *h_root; - struct au_hdentry *hdp; - - AuRwMustAnyLock(&sbinfo->si_rwsem); - - err = 0; - f = au_sbi(sb)->si_xib; - if (!f) - goto out; - - /* stop printing the default xino path on the first writable branch */ - h_root = NULL; - brid = au_xino_brid(sb); - if (brid >= 0) { - bindex = au_br_index(sb, brid); - hdp = au_di(sb->s_root)->di_hdentry; - h_root = hdp[0 + bindex].hd_dentry; - } - d = f->f_path.dentry; - name = &d->d_name; - /* safe ->d_parent because the file is unlinked */ - if (d->d_parent == h_root - && name->len == len - && !memcmp(name->name, AUFS_XINO_FNAME, len)) - goto out; - - seq_puts(seq, ",xino="); - err = au_xino_path(seq, f); - -out: - return err; -#endif -} - -/* seq_file will re-call me in case of too long string */ -static int aufs_show_options(struct seq_file *m, struct dentry *dentry) -{ - int err; - unsigned int mnt_flags, v; - struct super_block *sb; - struct au_sbinfo *sbinfo; - -#define AuBool(name, str) do { \ - v = au_opt_test(mnt_flags, name); \ - if (v != au_opt_test(AuOpt_Def, name)) \ - seq_printf(m, ",%s" #str, v ? "" : "no"); \ -} while (0) - -#define AuStr(name, str) do { \ - v = mnt_flags & AuOptMask_##name; \ - if (v != (AuOpt_Def & AuOptMask_##name)) \ - seq_printf(m, "," #str "=%s", au_optstr_##str(v)); \ -} while (0) - -#define AuUInt(name, str, val) do { \ - if (val != AUFS_##name##_DEF) \ - seq_printf(m, "," #str "=%u", val); \ -} while (0) - - sb = dentry->d_sb; - if (sb->s_flags & MS_POSIXACL) - seq_puts(m, ",acl"); - - /* lock free root dinfo */ - si_noflush_read_lock(sb); - sbinfo = au_sbi(sb); - seq_printf(m, ",si=%lx", sysaufs_si_id(sbinfo)); - - mnt_flags = au_mntflags(sb); - if (au_opt_test(mnt_flags, XINO)) { - err = au_show_xino(m, sb); - if (unlikely(err)) - goto out; - } else - seq_puts(m, ",noxino"); - - AuBool(TRUNC_XINO, trunc_xino); - AuStr(UDBA, udba); - AuBool(SHWH, shwh); - AuBool(PLINK, plink); - AuBool(DIO, dio); - AuBool(DIRPERM1, dirperm1); - - v = sbinfo->si_wbr_create; - if (v != AuWbrCreate_Def) - au_show_wbr_create(m, v, sbinfo); - - v = sbinfo->si_wbr_copyup; - if (v != AuWbrCopyup_Def) - seq_printf(m, ",cpup=%s", au_optstr_wbr_copyup(v)); - - v = au_opt_test(mnt_flags, ALWAYS_DIROPQ); - if (v != au_opt_test(AuOpt_Def, ALWAYS_DIROPQ)) - seq_printf(m, ",diropq=%c", v ? 'a' : 'w'); - - AuUInt(DIRWH, dirwh, sbinfo->si_dirwh); - - v = jiffies_to_msecs(sbinfo->si_rdcache) / MSEC_PER_SEC; - AuUInt(RDCACHE, rdcache, v); - - AuUInt(RDBLK, rdblk, sbinfo->si_rdblk); - AuUInt(RDHASH, rdhash, sbinfo->si_rdhash); - - au_fhsm_show(m, sbinfo); - - AuBool(SUM, sum); - /* AuBool(SUM_W, wsum); */ - AuBool(WARN_PERM, warn_perm); - AuBool(VERBOSE, verbose); - -out: - /* be sure to print "br:" last */ - if (!sysaufs_brs) { - seq_puts(m, ",br:"); - au_show_brs(m, sb); - } - si_read_unlock(sb); - return 0; - -#undef AuBool -#undef AuStr -#undef AuUInt -} - -/* ---------------------------------------------------------------------- */ - -/* sum mode which returns the summation for statfs(2) */ - -static u64 au_add_till_max(u64 a, u64 b) -{ - u64 old; - - old = a; - a += b; - if (old <= a) - return a; - return ULLONG_MAX; -} - -static u64 au_mul_till_max(u64 a, long mul) -{ - u64 old; - - old = a; - a *= mul; - if (old <= a) - return a; - return ULLONG_MAX; -} - -static int au_statfs_sum(struct super_block *sb, struct kstatfs *buf) -{ - int err; - long bsize, factor; - u64 blocks, bfree, bavail, files, ffree; - aufs_bindex_t bend, bindex, i; - unsigned char shared; - struct path h_path; - struct super_block *h_sb; - - err = 0; - bsize = LONG_MAX; - files = 0; - ffree = 0; - blocks = 0; - bfree = 0; - bavail = 0; - bend = au_sbend(sb); - for (bindex = 0; bindex <= bend; bindex++) { - h_path.mnt = au_sbr_mnt(sb, bindex); - h_sb = h_path.mnt->mnt_sb; - shared = 0; - for (i = 0; !shared && i < bindex; i++) - shared = (au_sbr_sb(sb, i) == h_sb); - if (shared) - continue; - - /* sb->s_root for NFS is unreliable */ - h_path.dentry = h_path.mnt->mnt_root; - err = vfs_statfs(&h_path, buf); - if (unlikely(err)) - goto out; - - if (bsize > buf->f_bsize) { - /* - * we will reduce bsize, so we have to expand blocks - * etc. to match them again - */ - factor = (bsize / buf->f_bsize); - blocks = au_mul_till_max(blocks, factor); - bfree = au_mul_till_max(bfree, factor); - bavail = au_mul_till_max(bavail, factor); - bsize = buf->f_bsize; - } - - factor = (buf->f_bsize / bsize); - blocks = au_add_till_max(blocks, - au_mul_till_max(buf->f_blocks, factor)); - bfree = au_add_till_max(bfree, - au_mul_till_max(buf->f_bfree, factor)); - bavail = au_add_till_max(bavail, - au_mul_till_max(buf->f_bavail, factor)); - files = au_add_till_max(files, buf->f_files); - ffree = au_add_till_max(ffree, buf->f_ffree); - } - - buf->f_bsize = bsize; - buf->f_blocks = blocks; - buf->f_bfree = bfree; - buf->f_bavail = bavail; - buf->f_files = files; - buf->f_ffree = ffree; - buf->f_frsize = 0; - -out: - return err; -} - -static int aufs_statfs(struct dentry *dentry, struct kstatfs *buf) -{ - int err; - struct path h_path; - struct super_block *sb; - - /* lock free root dinfo */ - sb = dentry->d_sb; - si_noflush_read_lock(sb); - if (!au_opt_test(au_mntflags(sb), SUM)) { - /* sb->s_root for NFS is unreliable */ - h_path.mnt = au_sbr_mnt(sb, 0); - h_path.dentry = h_path.mnt->mnt_root; - err = vfs_statfs(&h_path, buf); - } else - err = au_statfs_sum(sb, buf); - si_read_unlock(sb); - - if (!err) { - buf->f_type = AUFS_SUPER_MAGIC; - buf->f_namelen = AUFS_MAX_NAMELEN; - memset(&buf->f_fsid, 0, sizeof(buf->f_fsid)); - } - /* buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; */ - - return err; -} - -/* ---------------------------------------------------------------------- */ - -static int aufs_sync_fs(struct super_block *sb, int wait) -{ - int err, e; - aufs_bindex_t bend, bindex; - struct au_branch *br; - struct super_block *h_sb; - - err = 0; - si_noflush_read_lock(sb); - bend = au_sbend(sb); - for (bindex = 0; bindex <= bend; bindex++) { - br = au_sbr(sb, bindex); - if (!au_br_writable(br->br_perm)) - continue; - - h_sb = au_sbr_sb(sb, bindex); - if (h_sb->s_op->sync_fs) { - e = h_sb->s_op->sync_fs(h_sb, wait); - if (unlikely(e && !err)) - err = e; - /* go on even if an error happens */ - } - } - si_read_unlock(sb); - - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* final actions when unmounting a file system */ -static void aufs_put_super(struct super_block *sb) -{ - struct au_sbinfo *sbinfo; - - sbinfo = au_sbi(sb); - if (!sbinfo) - return; - - dbgaufs_si_fin(sbinfo); - kobject_put(&sbinfo->si_kobj); -} - -/* ---------------------------------------------------------------------- */ - -void au_array_free(void *array) -{ - if (array) { - if (!is_vmalloc_addr(array)) - kfree(array); - else - vfree(array); - } -} - -void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg) -{ - void *array; - unsigned long long n, sz; - - array = NULL; - n = 0; - if (!*hint) - goto out; - - if (*hint > ULLONG_MAX / sizeof(array)) { - array = ERR_PTR(-EMFILE); - pr_err("hint %llu\n", *hint); - goto out; - } - - sz = sizeof(array) * *hint; - array = kzalloc(sz, GFP_NOFS); - if (unlikely(!array)) - array = vzalloc(sz); - if (unlikely(!array)) { - array = ERR_PTR(-ENOMEM); - goto out; - } - - n = cb(array, *hint, arg); - AuDebugOn(n > *hint); - -out: - *hint = n; - return array; -} - -static unsigned long long au_iarray_cb(void *a, - unsigned long long max __maybe_unused, - void *arg) -{ - unsigned long long n; - struct inode **p, *inode; - struct list_head *head; - - n = 0; - p = a; - head = arg; - spin_lock(&inode_sb_list_lock); - list_for_each_entry(inode, head, i_sb_list) { - if (!is_bad_inode(inode) - && au_ii(inode)->ii_bstart >= 0) { - spin_lock(&inode->i_lock); - if (atomic_read(&inode->i_count)) { - au_igrab(inode); - *p++ = inode; - n++; - AuDebugOn(n > max); - } - spin_unlock(&inode->i_lock); - } - } - spin_unlock(&inode_sb_list_lock); - - return n; -} - -struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max) -{ - *max = atomic_long_read(&au_sbi(sb)->si_ninodes); - return au_array_alloc(max, au_iarray_cb, &sb->s_inodes); -} - -void au_iarray_free(struct inode **a, unsigned long long max) -{ - unsigned long long ull; - - for (ull = 0; ull < max; ull++) - iput(a[ull]); - au_array_free(a); -} - -/* ---------------------------------------------------------------------- */ - -/* - * refresh dentry and inode at remount time. - */ -/* todo: consolidate with simple_reval_dpath() and au_reval_for_attr() */ -static int au_do_refresh(struct dentry *dentry, unsigned int dir_flags, - struct dentry *parent) -{ - int err; - - di_write_lock_child(dentry); - di_read_lock_parent(parent, AuLock_IR); - err = au_refresh_dentry(dentry, parent); - if (!err && dir_flags) - au_hn_reset(d_inode(dentry), dir_flags); - di_read_unlock(parent, AuLock_IR); - di_write_unlock(dentry); - - return err; -} - -static int au_do_refresh_d(struct dentry *dentry, unsigned int sigen, - struct au_sbinfo *sbinfo, - const unsigned int dir_flags) -{ - int err; - struct dentry *parent; - - err = 0; - parent = dget_parent(dentry); - if (!au_digen_test(parent, sigen) && au_digen_test(dentry, sigen)) { - if (d_really_is_positive(dentry)) { - if (!d_is_dir(dentry)) - err = au_do_refresh(dentry, /*dir_flags*/0, - parent); - else { - err = au_do_refresh(dentry, dir_flags, parent); - if (unlikely(err)) - au_fset_si(sbinfo, FAILED_REFRESH_DIR); - } - } else - err = au_do_refresh(dentry, /*dir_flags*/0, parent); - AuDbgDentry(dentry); - } - dput(parent); - - AuTraceErr(err); - return err; -} - -static int au_refresh_d(struct super_block *sb) -{ - int err, i, j, ndentry, e; - unsigned int sigen; - struct au_dcsub_pages dpages; - struct au_dpage *dpage; - struct dentry **dentries, *d; - struct au_sbinfo *sbinfo; - struct dentry *root = sb->s_root; - const unsigned int dir_flags = au_hi_flags(d_inode(root), /*isdir*/1); - - err = au_dpages_init(&dpages, GFP_NOFS); - if (unlikely(err)) - goto out; - err = au_dcsub_pages(&dpages, root, NULL, NULL); - if (unlikely(err)) - goto out_dpages; - - sigen = au_sigen(sb); - sbinfo = au_sbi(sb); - for (i = 0; i < dpages.ndpage; i++) { - dpage = dpages.dpages + i; - dentries = dpage->dentries; - ndentry = dpage->ndentry; - for (j = 0; j < ndentry; j++) { - d = dentries[j]; - e = au_do_refresh_d(d, sigen, sbinfo, dir_flags); - if (unlikely(e && !err)) - err = e; - /* go on even err */ - } - } - -out_dpages: - au_dpages_free(&dpages); -out: - return err; -} - -static int au_refresh_i(struct super_block *sb) -{ - int err, e; - unsigned int sigen; - unsigned long long max, ull; - struct inode *inode, **array; - - array = au_iarray_alloc(sb, &max); - err = PTR_ERR(array); - if (IS_ERR(array)) - goto out; - - err = 0; - sigen = au_sigen(sb); - for (ull = 0; ull < max; ull++) { - inode = array[ull]; - if (unlikely(!inode)) - break; - if (au_iigen(inode, NULL) != sigen) { - ii_write_lock_child(inode); - e = au_refresh_hinode_self(inode); - ii_write_unlock(inode); - if (unlikely(e)) { - pr_err("error %d, i%lu\n", e, inode->i_ino); - if (!err) - err = e; - /* go on even if err */ - } - } - } - - au_iarray_free(array, max); - -out: - return err; -} - -static void au_remount_refresh(struct super_block *sb) -{ - int err, e; - unsigned int udba; - aufs_bindex_t bindex, bend; - struct dentry *root; - struct inode *inode; - struct au_branch *br; - - au_sigen_inc(sb); - au_fclr_si(au_sbi(sb), FAILED_REFRESH_DIR); - - root = sb->s_root; - DiMustNoWaiters(root); - inode = d_inode(root); - IiMustNoWaiters(inode); - - udba = au_opt_udba(sb); - bend = au_sbend(sb); - for (bindex = 0; bindex <= bend; bindex++) { - br = au_sbr(sb, bindex); - err = au_hnotify_reset_br(udba, br, br->br_perm); - if (unlikely(err)) - AuIOErr("hnotify failed on br %d, %d, ignored\n", - bindex, err); - /* go on even if err */ - } - au_hn_reset(inode, au_hi_flags(inode, /*isdir*/1)); - - di_write_unlock(root); - err = au_refresh_d(sb); - e = au_refresh_i(sb); - if (unlikely(e && !err)) - err = e; - /* aufs_write_lock() calls ..._child() */ - di_write_lock_child(root); - - au_cpup_attr_all(inode, /*force*/1); - - if (unlikely(err)) - AuIOErr("refresh failed, ignored, %d\n", err); -} - -/* stop extra interpretation of errno in mount(8), and strange error messages */ -static int cvt_err(int err) -{ - AuTraceErr(err); - - switch (err) { - case -ENOENT: - case -ENOTDIR: - case -EEXIST: - case -EIO: - err = -EINVAL; - } - return err; -} - -static int aufs_remount_fs(struct super_block *sb, int *flags, char *data) -{ - int err, do_dx; - unsigned int mntflags; - struct au_opts opts; - struct dentry *root; - struct inode *inode; - struct au_sbinfo *sbinfo; - - err = 0; - root = sb->s_root; - if (!data || !*data) { - err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); - if (!err) { - di_write_lock_child(root); - err = au_opts_verify(sb, *flags, /*pending*/0); - aufs_write_unlock(root); - } - goto out; - } - - err = -ENOMEM; - memset(&opts, 0, sizeof(opts)); - opts.opt = (void *)__get_free_page(GFP_NOFS); - if (unlikely(!opts.opt)) - goto out; - opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); - opts.flags = AuOpts_REMOUNT; - opts.sb_flags = *flags; - - /* parse it before aufs lock */ - err = au_opts_parse(sb, data, &opts); - if (unlikely(err)) - goto out_opts; - - sbinfo = au_sbi(sb); - inode = d_inode(root); - mutex_lock(&inode->i_mutex); - err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); - if (unlikely(err)) - goto out_mtx; - di_write_lock_child(root); - - /* au_opts_remount() may return an error */ - err = au_opts_remount(sb, &opts); - au_opts_free(&opts); - - if (au_ftest_opts(opts.flags, REFRESH)) - au_remount_refresh(sb); - - if (au_ftest_opts(opts.flags, REFRESH_DYAOP)) { - mntflags = au_mntflags(sb); - do_dx = !!au_opt_test(mntflags, DIO); - au_dy_arefresh(do_dx); - } - - au_fhsm_wrote_all(sb, /*force*/1); /* ?? */ - aufs_write_unlock(root); - -out_mtx: - mutex_unlock(&inode->i_mutex); -out_opts: - free_page((unsigned long)opts.opt); -out: - err = cvt_err(err); - AuTraceErr(err); - return err; -} - -static const struct super_operations aufs_sop = { - .alloc_inode = aufs_alloc_inode, - .destroy_inode = aufs_destroy_inode, - /* always deleting, no clearing */ - .drop_inode = generic_delete_inode, - .show_options = aufs_show_options, - .statfs = aufs_statfs, - .put_super = aufs_put_super, - .sync_fs = aufs_sync_fs, - .remount_fs = aufs_remount_fs -}; - -/* ---------------------------------------------------------------------- */ - -static int alloc_root(struct super_block *sb) -{ - int err; - struct inode *inode; - struct dentry *root; - - err = -ENOMEM; - inode = au_iget_locked(sb, AUFS_ROOT_INO); - err = PTR_ERR(inode); - if (IS_ERR(inode)) - goto out; - - inode->i_op = &aufs_dir_iop; - inode->i_fop = &aufs_dir_fop; - inode->i_mode = S_IFDIR; - set_nlink(inode, 2); - unlock_new_inode(inode); - - root = d_make_root(inode); - if (unlikely(!root)) - goto out; - err = PTR_ERR(root); - if (IS_ERR(root)) - goto out; - - err = au_di_init(root); - if (!err) { - sb->s_root = root; - return 0; /* success */ - } - dput(root); - -out: - return err; -} - -static int aufs_fill_super(struct super_block *sb, void *raw_data, - int silent __maybe_unused) -{ - int err; - struct au_opts opts; - struct dentry *root; - struct inode *inode; - char *arg = raw_data; - - if (unlikely(!arg || !*arg)) { - err = -EINVAL; - pr_err("no arg\n"); - goto out; - } - - err = -ENOMEM; - memset(&opts, 0, sizeof(opts)); - opts.opt = (void *)__get_free_page(GFP_NOFS); - if (unlikely(!opts.opt)) - goto out; - opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); - opts.sb_flags = sb->s_flags; - - err = au_si_alloc(sb); - if (unlikely(err)) - goto out_opts; - - /* all timestamps always follow the ones on the branch */ - sb->s_flags |= MS_NOATIME | MS_NODIRATIME; - sb->s_op = &aufs_sop; - sb->s_d_op = &aufs_dop; - sb->s_magic = AUFS_SUPER_MAGIC; - sb->s_maxbytes = 0; - sb->s_stack_depth = 1; - au_export_init(sb); - /* au_xattr_init(sb); */ - - err = alloc_root(sb); - if (unlikely(err)) { - si_write_unlock(sb); - goto out_info; - } - root = sb->s_root; - inode = d_inode(root); - - /* - * actually we can parse options regardless aufs lock here. - * but at remount time, parsing must be done before aufs lock. - * so we follow the same rule. - */ - ii_write_lock_parent(inode); - aufs_write_unlock(root); - err = au_opts_parse(sb, arg, &opts); - if (unlikely(err)) - goto out_root; - - /* lock vfs_inode first, then aufs. */ - mutex_lock(&inode->i_mutex); - aufs_write_lock(root); - err = au_opts_mount(sb, &opts); - au_opts_free(&opts); - aufs_write_unlock(root); - mutex_unlock(&inode->i_mutex); - if (!err) - goto out_opts; /* success */ - -out_root: - dput(root); - sb->s_root = NULL; -out_info: - dbgaufs_si_fin(au_sbi(sb)); - kobject_put(&au_sbi(sb)->si_kobj); - sb->s_fs_info = NULL; -out_opts: - free_page((unsigned long)opts.opt); -out: - AuTraceErr(err); - err = cvt_err(err); - AuTraceErr(err); - return err; -} - -/* ---------------------------------------------------------------------- */ - -static struct dentry *aufs_mount(struct file_system_type *fs_type, int flags, - const char *dev_name __maybe_unused, - void *raw_data) -{ - struct dentry *root; - struct super_block *sb; - - /* all timestamps always follow the ones on the branch */ - /* mnt->mnt_flags |= MNT_NOATIME | MNT_NODIRATIME; */ - root = mount_nodev(fs_type, flags, raw_data, aufs_fill_super); - if (IS_ERR(root)) - goto out; - - sb = root->d_sb; - si_write_lock(sb, !AuLock_FLUSH); - sysaufs_brs_add(sb, 0); - si_write_unlock(sb); - au_sbilist_add(sb); - -out: - return root; -} - -static void aufs_kill_sb(struct super_block *sb) -{ - struct au_sbinfo *sbinfo; - - sbinfo = au_sbi(sb); - if (sbinfo) { - au_sbilist_del(sb); - aufs_write_lock(sb->s_root); - au_fhsm_fin(sb); - if (sbinfo->si_wbr_create_ops->fin) - sbinfo->si_wbr_create_ops->fin(sb); - if (au_opt_test(sbinfo->si_mntflags, UDBA_HNOTIFY)) { - au_opt_set_udba(sbinfo->si_mntflags, UDBA_NONE); - au_remount_refresh(sb); - } - if (au_opt_test(sbinfo->si_mntflags, PLINK)) - au_plink_put(sb, /*verbose*/1); - au_xino_clr(sb); - sbinfo->si_sb = NULL; - aufs_write_unlock(sb->s_root); - au_nwt_flush(&sbinfo->si_nowait); - } - kill_anon_super(sb); -} - -struct file_system_type aufs_fs_type = { - .name = AUFS_FSTYPE, - /* a race between rename and others */ - .fs_flags = FS_RENAME_DOES_D_MOVE, - .mount = aufs_mount, - .kill_sb = aufs_kill_sb, - /* no need to __module_get() and module_put(). */ - .owner = THIS_MODULE, -}; diff --git a/fs/aufs/super.h b/fs/aufs/super.h deleted file mode 100644 index 95ffbbb29..000000000 --- a/fs/aufs/super.h +++ /dev/null @@ -1,635 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * super_block operations - */ - -#ifndef __AUFS_SUPER_H__ -#define __AUFS_SUPER_H__ - -#ifdef __KERNEL__ - -#include <linux/fs.h> -#include <linux/kobject.h> -#include "rwsem.h" -#include "spl.h" -#include "wkq.h" - -/* policies to select one among multiple writable branches */ -struct au_wbr_copyup_operations { - int (*copyup)(struct dentry *dentry); -}; - -#define AuWbr_DIR 1 /* target is a dir */ -#define AuWbr_PARENT (1 << 1) /* always require a parent */ - -#define au_ftest_wbr(flags, name) ((flags) & AuWbr_##name) -#define au_fset_wbr(flags, name) { (flags) |= AuWbr_##name; } -#define au_fclr_wbr(flags, name) { (flags) &= ~AuWbr_##name; } - -struct au_wbr_create_operations { - int (*create)(struct dentry *dentry, unsigned int flags); - int (*init)(struct super_block *sb); - int (*fin)(struct super_block *sb); -}; - -struct au_wbr_mfs { - struct mutex mfs_lock; /* protect this structure */ - unsigned long mfs_jiffy; - unsigned long mfs_expire; - aufs_bindex_t mfs_bindex; - - unsigned long long mfsrr_bytes; - unsigned long long mfsrr_watermark; -}; - -struct pseudo_link { - union { - struct hlist_node hlist; - struct rcu_head rcu; - }; - struct inode *inode; -}; - -#define AuPlink_NHASH 100 -static inline int au_plink_hash(ino_t ino) -{ - return ino % AuPlink_NHASH; -} - -/* File-based Hierarchical Storage Management */ -struct au_fhsm { -#ifdef CONFIG_AUFS_FHSM - /* allow only one process who can receive the notification */ - spinlock_t fhsm_spin; - pid_t fhsm_pid; - wait_queue_head_t fhsm_wqh; - atomic_t fhsm_readable; - - /* these are protected by si_rwsem */ - unsigned long fhsm_expire; - aufs_bindex_t fhsm_bottom; -#endif -}; - -struct au_branch; -struct au_sbinfo { - /* nowait tasks in the system-wide workqueue */ - struct au_nowait_tasks si_nowait; - - /* - * tried sb->s_umount, but failed due to the dependecy between i_mutex. - * rwsem for au_sbinfo is necessary. - */ - struct au_rwsem si_rwsem; - - /* prevent recursive locking in deleting inode */ - struct { - unsigned long *bitmap; - spinlock_t tree_lock; - struct radix_tree_root tree; - } au_si_pid; - - /* - * dirty approach to protect sb->sb_inodes and ->s_files (gone) from - * remount. - */ - atomic_long_t si_ninodes, si_nfiles; - - /* branch management */ - unsigned int si_generation; - - /* see AuSi_ flags */ - unsigned char au_si_status; - - aufs_bindex_t si_bend; - - /* dirty trick to keep br_id plus */ - unsigned int si_last_br_id : - sizeof(aufs_bindex_t) * BITS_PER_BYTE - 1; - struct au_branch **si_branch; - - /* policy to select a writable branch */ - unsigned char si_wbr_copyup; - unsigned char si_wbr_create; - struct au_wbr_copyup_operations *si_wbr_copyup_ops; - struct au_wbr_create_operations *si_wbr_create_ops; - - /* round robin */ - atomic_t si_wbr_rr_next; - - /* most free space */ - struct au_wbr_mfs si_wbr_mfs; - - /* File-based Hierarchical Storage Management */ - struct au_fhsm si_fhsm; - - /* mount flags */ - /* include/asm-ia64/siginfo.h defines a macro named si_flags */ - unsigned int si_mntflags; - - /* external inode number (bitmap and translation table) */ - vfs_readf_t si_xread; - vfs_writef_t si_xwrite; - struct file *si_xib; - struct mutex si_xib_mtx; /* protect xib members */ - unsigned long *si_xib_buf; - unsigned long si_xib_last_pindex; - int si_xib_next_bit; - aufs_bindex_t si_xino_brid; - unsigned long si_xino_jiffy; - unsigned long si_xino_expire; - /* reserved for future use */ - /* unsigned long long si_xib_limit; */ /* Max xib file size */ - -#ifdef CONFIG_AUFS_EXPORT - /* i_generation */ - struct file *si_xigen; - atomic_t si_xigen_next; -#endif - - /* dirty trick to suppoer atomic_open */ - struct au_sphlhead si_aopen; - - /* vdir parameters */ - unsigned long si_rdcache; /* max cache time in jiffies */ - unsigned int si_rdblk; /* deblk size */ - unsigned int si_rdhash; /* hash size */ - - /* - * If the number of whiteouts are larger than si_dirwh, leave all of - * them after au_whtmp_ren to reduce the cost of rmdir(2). - * future fsck.aufs or kernel thread will remove them later. - * Otherwise, remove all whiteouts and the dir in rmdir(2). - */ - unsigned int si_dirwh; - - /* pseudo_link list */ - struct au_sphlhead si_plink[AuPlink_NHASH]; - wait_queue_head_t si_plink_wq; - spinlock_t si_plink_maint_lock; - pid_t si_plink_maint_pid; - - /* file list */ - struct au_sphlhead si_files; - - /* - * sysfs and lifetime management. - * this is not a small structure and it may be a waste of memory in case - * of sysfs is disabled, particulary when many aufs-es are mounted. - * but using sysfs is majority. - */ - struct kobject si_kobj; -#ifdef CONFIG_DEBUG_FS - struct dentry *si_dbgaufs; - struct dentry *si_dbgaufs_plink; - struct dentry *si_dbgaufs_xib; -#ifdef CONFIG_AUFS_EXPORT - struct dentry *si_dbgaufs_xigen; -#endif -#endif - -#ifdef CONFIG_AUFS_SBILIST - struct list_head si_list; -#endif - - /* dirty, necessary for unmounting, sysfs and sysrq */ - struct super_block *si_sb; -}; - -/* sbinfo status flags */ -/* - * set true when refresh_dirs() failed at remount time. - * then try refreshing dirs at access time again. - * if it is false, refreshing dirs at access time is unnecesary - */ -#define AuSi_FAILED_REFRESH_DIR 1 - -#define AuSi_FHSM (1 << 1) /* fhsm is active now */ - -#ifndef CONFIG_AUFS_FHSM -#undef AuSi_FHSM -#define AuSi_FHSM 0 -#endif - -static inline unsigned char au_do_ftest_si(struct au_sbinfo *sbi, - unsigned int flag) -{ - AuRwMustAnyLock(&sbi->si_rwsem); - return sbi->au_si_status & flag; -} -#define au_ftest_si(sbinfo, name) au_do_ftest_si(sbinfo, AuSi_##name) -#define au_fset_si(sbinfo, name) do { \ - AuRwMustWriteLock(&(sbinfo)->si_rwsem); \ - (sbinfo)->au_si_status |= AuSi_##name; \ -} while (0) -#define au_fclr_si(sbinfo, name) do { \ - AuRwMustWriteLock(&(sbinfo)->si_rwsem); \ - (sbinfo)->au_si_status &= ~AuSi_##name; \ -} while (0) - -/* ---------------------------------------------------------------------- */ - -/* policy to select one among writable branches */ -#define AuWbrCopyup(sbinfo, ...) \ - ((sbinfo)->si_wbr_copyup_ops->copyup(__VA_ARGS__)) -#define AuWbrCreate(sbinfo, ...) \ - ((sbinfo)->si_wbr_create_ops->create(__VA_ARGS__)) - -/* flags for si_read_lock()/aufs_read_lock()/di_read_lock() */ -#define AuLock_DW 1 /* write-lock dentry */ -#define AuLock_IR (1 << 1) /* read-lock inode */ -#define AuLock_IW (1 << 2) /* write-lock inode */ -#define AuLock_FLUSH (1 << 3) /* wait for 'nowait' tasks */ -#define AuLock_DIR (1 << 4) /* target is a dir */ -#define AuLock_NOPLM (1 << 5) /* return err in plm mode */ -#define AuLock_NOPLMW (1 << 6) /* wait for plm mode ends */ -#define AuLock_GEN (1 << 7) /* test digen/iigen */ -#define au_ftest_lock(flags, name) ((flags) & AuLock_##name) -#define au_fset_lock(flags, name) \ - do { (flags) |= AuLock_##name; } while (0) -#define au_fclr_lock(flags, name) \ - do { (flags) &= ~AuLock_##name; } while (0) - -/* ---------------------------------------------------------------------- */ - -/* super.c */ -extern struct file_system_type aufs_fs_type; -struct inode *au_iget_locked(struct super_block *sb, ino_t ino); -typedef unsigned long long (*au_arraycb_t)(void *array, unsigned long long max, - void *arg); -void au_array_free(void *array); -void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg); -struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max); -void au_iarray_free(struct inode **a, unsigned long long max); - -/* sbinfo.c */ -void au_si_free(struct kobject *kobj); -int au_si_alloc(struct super_block *sb); -int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr); - -unsigned int au_sigen_inc(struct super_block *sb); -aufs_bindex_t au_new_br_id(struct super_block *sb); - -int si_read_lock(struct super_block *sb, int flags); -int si_write_lock(struct super_block *sb, int flags); -int aufs_read_lock(struct dentry *dentry, int flags); -void aufs_read_unlock(struct dentry *dentry, int flags); -void aufs_write_lock(struct dentry *dentry); -void aufs_write_unlock(struct dentry *dentry); -int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags); -void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2); - -int si_pid_test_slow(struct super_block *sb); -void si_pid_set_slow(struct super_block *sb); -void si_pid_clr_slow(struct super_block *sb); - -/* wbr_policy.c */ -extern struct au_wbr_copyup_operations au_wbr_copyup_ops[]; -extern struct au_wbr_create_operations au_wbr_create_ops[]; -int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst); -int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex); -int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t bstart); - -/* mvdown.c */ -int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *arg); - -#ifdef CONFIG_AUFS_FHSM -/* fhsm.c */ - -static inline pid_t au_fhsm_pid(struct au_fhsm *fhsm) -{ - pid_t pid; - - spin_lock(&fhsm->fhsm_spin); - pid = fhsm->fhsm_pid; - spin_unlock(&fhsm->fhsm_spin); - - return pid; -} - -void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force); -void au_fhsm_wrote_all(struct super_block *sb, int force); -int au_fhsm_fd(struct super_block *sb, int oflags); -int au_fhsm_br_alloc(struct au_branch *br); -void au_fhsm_set_bottom(struct super_block *sb, aufs_bindex_t bindex); -void au_fhsm_fin(struct super_block *sb); -void au_fhsm_init(struct au_sbinfo *sbinfo); -void au_fhsm_set(struct au_sbinfo *sbinfo, unsigned int sec); -void au_fhsm_show(struct seq_file *seq, struct au_sbinfo *sbinfo); -#else -AuStubVoid(au_fhsm_wrote, struct super_block *sb, aufs_bindex_t bindex, - int force) -AuStubVoid(au_fhsm_wrote_all, struct super_block *sb, int force) -AuStub(int, au_fhsm_fd, return -EOPNOTSUPP, struct super_block *sb, int oflags) -AuStub(pid_t, au_fhsm_pid, return 0, struct au_fhsm *fhsm) -AuStubInt0(au_fhsm_br_alloc, struct au_branch *br) -AuStubVoid(au_fhsm_set_bottom, struct super_block *sb, aufs_bindex_t bindex) -AuStubVoid(au_fhsm_fin, struct super_block *sb) -AuStubVoid(au_fhsm_init, struct au_sbinfo *sbinfo) -AuStubVoid(au_fhsm_set, struct au_sbinfo *sbinfo, unsigned int sec) -AuStubVoid(au_fhsm_show, struct seq_file *seq, struct au_sbinfo *sbinfo) -#endif - -/* ---------------------------------------------------------------------- */ - -static inline struct au_sbinfo *au_sbi(struct super_block *sb) -{ - return sb->s_fs_info; -} - -/* ---------------------------------------------------------------------- */ - -#ifdef CONFIG_AUFS_EXPORT -int au_test_nfsd(void); -void au_export_init(struct super_block *sb); -void au_xigen_inc(struct inode *inode); -int au_xigen_new(struct inode *inode); -int au_xigen_set(struct super_block *sb, struct file *base); -void au_xigen_clr(struct super_block *sb); - -static inline int au_busy_or_stale(void) -{ - if (!au_test_nfsd()) - return -EBUSY; - return -ESTALE; -} -#else -AuStubInt0(au_test_nfsd, void) -AuStubVoid(au_export_init, struct super_block *sb) -AuStubVoid(au_xigen_inc, struct inode *inode) -AuStubInt0(au_xigen_new, struct inode *inode) -AuStubInt0(au_xigen_set, struct super_block *sb, struct file *base) -AuStubVoid(au_xigen_clr, struct super_block *sb) -AuStub(int, au_busy_or_stale, return -EBUSY, void) -#endif /* CONFIG_AUFS_EXPORT */ - -/* ---------------------------------------------------------------------- */ - -#ifdef CONFIG_AUFS_SBILIST -/* module.c */ -extern struct au_splhead au_sbilist; - -static inline void au_sbilist_init(void) -{ - au_spl_init(&au_sbilist); -} - -static inline void au_sbilist_add(struct super_block *sb) -{ - au_spl_add(&au_sbi(sb)->si_list, &au_sbilist); -} - -static inline void au_sbilist_del(struct super_block *sb) -{ - au_spl_del(&au_sbi(sb)->si_list, &au_sbilist); -} - -#ifdef CONFIG_AUFS_MAGIC_SYSRQ -static inline void au_sbilist_lock(void) -{ - spin_lock(&au_sbilist.spin); -} - -static inline void au_sbilist_unlock(void) -{ - spin_unlock(&au_sbilist.spin); -} -#define AuGFP_SBILIST GFP_ATOMIC -#else -AuStubVoid(au_sbilist_lock, void) -AuStubVoid(au_sbilist_unlock, void) -#define AuGFP_SBILIST GFP_NOFS -#endif /* CONFIG_AUFS_MAGIC_SYSRQ */ -#else -AuStubVoid(au_sbilist_init, void) -AuStubVoid(au_sbilist_add, struct super_block *sb) -AuStubVoid(au_sbilist_del, struct super_block *sb) -AuStubVoid(au_sbilist_lock, void) -AuStubVoid(au_sbilist_unlock, void) -#define AuGFP_SBILIST GFP_NOFS -#endif - -/* ---------------------------------------------------------------------- */ - -static inline void dbgaufs_si_null(struct au_sbinfo *sbinfo) -{ - /* - * This function is a dynamic '__init' function actually, - * so the tiny check for si_rwsem is unnecessary. - */ - /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ -#ifdef CONFIG_DEBUG_FS - sbinfo->si_dbgaufs = NULL; - sbinfo->si_dbgaufs_plink = NULL; - sbinfo->si_dbgaufs_xib = NULL; -#ifdef CONFIG_AUFS_EXPORT - sbinfo->si_dbgaufs_xigen = NULL; -#endif -#endif -} - -/* ---------------------------------------------------------------------- */ - -static inline pid_t si_pid_bit(void) -{ - /* the origin of pid is 1, but the bitmap's is 0 */ - return current->pid - 1; -} - -static inline int si_pid_test(struct super_block *sb) -{ - pid_t bit; - - bit = si_pid_bit(); - if (bit < PID_MAX_DEFAULT) - return test_bit(bit, au_sbi(sb)->au_si_pid.bitmap); - return si_pid_test_slow(sb); -} - -static inline void si_pid_set(struct super_block *sb) -{ - pid_t bit; - - bit = si_pid_bit(); - if (bit < PID_MAX_DEFAULT) { - AuDebugOn(test_bit(bit, au_sbi(sb)->au_si_pid.bitmap)); - set_bit(bit, au_sbi(sb)->au_si_pid.bitmap); - /* smp_mb(); */ - } else - si_pid_set_slow(sb); -} - -static inline void si_pid_clr(struct super_block *sb) -{ - pid_t bit; - - bit = si_pid_bit(); - if (bit < PID_MAX_DEFAULT) { - AuDebugOn(!test_bit(bit, au_sbi(sb)->au_si_pid.bitmap)); - clear_bit(bit, au_sbi(sb)->au_si_pid.bitmap); - /* smp_mb(); */ - } else - si_pid_clr_slow(sb); -} - -/* ---------------------------------------------------------------------- */ - -/* lock superblock. mainly for entry point functions */ -/* - * __si_read_lock, __si_write_lock, - * __si_read_unlock, __si_write_unlock, __si_downgrade_lock - */ -AuSimpleRwsemFuncs(__si, struct super_block *sb, &au_sbi(sb)->si_rwsem); - -#define SiMustNoWaiters(sb) AuRwMustNoWaiters(&au_sbi(sb)->si_rwsem) -#define SiMustAnyLock(sb) AuRwMustAnyLock(&au_sbi(sb)->si_rwsem) -#define SiMustWriteLock(sb) AuRwMustWriteLock(&au_sbi(sb)->si_rwsem) - -static inline void si_noflush_read_lock(struct super_block *sb) -{ - __si_read_lock(sb); - si_pid_set(sb); -} - -static inline int si_noflush_read_trylock(struct super_block *sb) -{ - int locked; - - locked = __si_read_trylock(sb); - if (locked) - si_pid_set(sb); - return locked; -} - -static inline void si_noflush_write_lock(struct super_block *sb) -{ - __si_write_lock(sb); - si_pid_set(sb); -} - -static inline int si_noflush_write_trylock(struct super_block *sb) -{ - int locked; - - locked = __si_write_trylock(sb); - if (locked) - si_pid_set(sb); - return locked; -} - -#if 0 /* reserved */ -static inline int si_read_trylock(struct super_block *sb, int flags) -{ - if (au_ftest_lock(flags, FLUSH)) - au_nwt_flush(&au_sbi(sb)->si_nowait); - return si_noflush_read_trylock(sb); -} -#endif - -static inline void si_read_unlock(struct super_block *sb) -{ - si_pid_clr(sb); - __si_read_unlock(sb); -} - -#if 0 /* reserved */ -static inline int si_write_trylock(struct super_block *sb, int flags) -{ - if (au_ftest_lock(flags, FLUSH)) - au_nwt_flush(&au_sbi(sb)->si_nowait); - return si_noflush_write_trylock(sb); -} -#endif - -static inline void si_write_unlock(struct super_block *sb) -{ - si_pid_clr(sb); - __si_write_unlock(sb); -} - -#if 0 /* reserved */ -static inline void si_downgrade_lock(struct super_block *sb) -{ - __si_downgrade_lock(sb); -} -#endif - -/* ---------------------------------------------------------------------- */ - -static inline aufs_bindex_t au_sbend(struct super_block *sb) -{ - SiMustAnyLock(sb); - return au_sbi(sb)->si_bend; -} - -static inline unsigned int au_mntflags(struct super_block *sb) -{ - SiMustAnyLock(sb); - return au_sbi(sb)->si_mntflags; -} - -static inline unsigned int au_sigen(struct super_block *sb) -{ - SiMustAnyLock(sb); - return au_sbi(sb)->si_generation; -} - -static inline void au_ninodes_inc(struct super_block *sb) -{ - atomic_long_inc(&au_sbi(sb)->si_ninodes); -} - -static inline void au_ninodes_dec(struct super_block *sb) -{ - AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_ninodes)); - atomic_long_dec(&au_sbi(sb)->si_ninodes); -} - -static inline void au_nfiles_inc(struct super_block *sb) -{ - atomic_long_inc(&au_sbi(sb)->si_nfiles); -} - -static inline void au_nfiles_dec(struct super_block *sb) -{ - AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_nfiles)); - atomic_long_dec(&au_sbi(sb)->si_nfiles); -} - -static inline struct au_branch *au_sbr(struct super_block *sb, - aufs_bindex_t bindex) -{ - SiMustAnyLock(sb); - return au_sbi(sb)->si_branch[0 + bindex]; -} - -static inline void au_xino_brid_set(struct super_block *sb, aufs_bindex_t brid) -{ - SiMustWriteLock(sb); - au_sbi(sb)->si_xino_brid = brid; -} - -static inline aufs_bindex_t au_xino_brid(struct super_block *sb) -{ - SiMustAnyLock(sb); - return au_sbi(sb)->si_xino_brid; -} - -#endif /* __KERNEL__ */ -#endif /* __AUFS_SUPER_H__ */ diff --git a/fs/aufs/sysaufs.c b/fs/aufs/sysaufs.c deleted file mode 100644 index 42dbc2825..000000000 --- a/fs/aufs/sysaufs.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * sysfs interface and lifetime management - * they are necessary regardless sysfs is disabled. - */ - -#include <linux/random.h> -#include "aufs.h" - -unsigned long sysaufs_si_mask; -struct kset *sysaufs_kset; - -#define AuSiAttr(_name) { \ - .attr = { .name = __stringify(_name), .mode = 0444 }, \ - .show = sysaufs_si_##_name, \ -} - -static struct sysaufs_si_attr sysaufs_si_attr_xi_path = AuSiAttr(xi_path); -struct attribute *sysaufs_si_attrs[] = { - &sysaufs_si_attr_xi_path.attr, - NULL, -}; - -static const struct sysfs_ops au_sbi_ops = { - .show = sysaufs_si_show -}; - -static struct kobj_type au_sbi_ktype = { - .release = au_si_free, - .sysfs_ops = &au_sbi_ops, - .default_attrs = sysaufs_si_attrs -}; - -/* ---------------------------------------------------------------------- */ - -int sysaufs_si_init(struct au_sbinfo *sbinfo) -{ - int err; - - sbinfo->si_kobj.kset = sysaufs_kset; - /* cf. sysaufs_name() */ - err = kobject_init_and_add - (&sbinfo->si_kobj, &au_sbi_ktype, /*&sysaufs_kset->kobj*/NULL, - SysaufsSiNamePrefix "%lx", sysaufs_si_id(sbinfo)); - - dbgaufs_si_null(sbinfo); - if (!err) { - err = dbgaufs_si_init(sbinfo); - if (unlikely(err)) - kobject_put(&sbinfo->si_kobj); - } - return err; -} - -void sysaufs_fin(void) -{ - dbgaufs_fin(); - sysfs_remove_group(&sysaufs_kset->kobj, sysaufs_attr_group); - kset_unregister(sysaufs_kset); -} - -int __init sysaufs_init(void) -{ - int err; - - do { - get_random_bytes(&sysaufs_si_mask, sizeof(sysaufs_si_mask)); - } while (!sysaufs_si_mask); - - err = -EINVAL; - sysaufs_kset = kset_create_and_add(AUFS_NAME, NULL, fs_kobj); - if (unlikely(!sysaufs_kset)) - goto out; - err = PTR_ERR(sysaufs_kset); - if (IS_ERR(sysaufs_kset)) - goto out; - err = sysfs_create_group(&sysaufs_kset->kobj, sysaufs_attr_group); - if (unlikely(err)) { - kset_unregister(sysaufs_kset); - goto out; - } - - err = dbgaufs_init(); - if (unlikely(err)) - sysaufs_fin(); -out: - return err; -} diff --git a/fs/aufs/sysaufs.h b/fs/aufs/sysaufs.h deleted file mode 100644 index 8e0fd02aa..000000000 --- a/fs/aufs/sysaufs.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * sysfs interface and mount lifetime management - */ - -#ifndef __SYSAUFS_H__ -#define __SYSAUFS_H__ - -#ifdef __KERNEL__ - -#include <linux/sysfs.h> -#include "module.h" - -struct super_block; -struct au_sbinfo; - -struct sysaufs_si_attr { - struct attribute attr; - int (*show)(struct seq_file *seq, struct super_block *sb); -}; - -/* ---------------------------------------------------------------------- */ - -/* sysaufs.c */ -extern unsigned long sysaufs_si_mask; -extern struct kset *sysaufs_kset; -extern struct attribute *sysaufs_si_attrs[]; -int sysaufs_si_init(struct au_sbinfo *sbinfo); -int __init sysaufs_init(void); -void sysaufs_fin(void); - -/* ---------------------------------------------------------------------- */ - -/* some people doesn't like to show a pointer in kernel */ -static inline unsigned long sysaufs_si_id(struct au_sbinfo *sbinfo) -{ - return sysaufs_si_mask ^ (unsigned long)sbinfo; -} - -#define SysaufsSiNamePrefix "si_" -#define SysaufsSiNameLen (sizeof(SysaufsSiNamePrefix) + 16) -static inline void sysaufs_name(struct au_sbinfo *sbinfo, char *name) -{ - snprintf(name, SysaufsSiNameLen, SysaufsSiNamePrefix "%lx", - sysaufs_si_id(sbinfo)); -} - -struct au_branch; -#ifdef CONFIG_SYSFS -/* sysfs.c */ -extern struct attribute_group *sysaufs_attr_group; - -int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb); -ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, - char *buf); -long au_brinfo_ioctl(struct file *file, unsigned long arg); -#ifdef CONFIG_COMPAT -long au_brinfo_compat_ioctl(struct file *file, unsigned long arg); -#endif - -void sysaufs_br_init(struct au_branch *br); -void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); -void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); - -#define sysaufs_brs_init() do {} while (0) - -#else -#define sysaufs_attr_group NULL - -AuStubInt0(sysaufs_si_xi_path, struct seq_file *seq, struct super_block *sb) -AuStub(ssize_t, sysaufs_si_show, return 0, struct kobject *kobj, - struct attribute *attr, char *buf) -AuStubVoid(sysaufs_br_init, struct au_branch *br) -AuStubVoid(sysaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex) -AuStubVoid(sysaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex) - -static inline void sysaufs_brs_init(void) -{ - sysaufs_brs = 0; -} - -#endif /* CONFIG_SYSFS */ - -#endif /* __KERNEL__ */ -#endif /* __SYSAUFS_H__ */ diff --git a/fs/aufs/sysfs.c b/fs/aufs/sysfs.c deleted file mode 100644 index 549f71d1a..000000000 --- a/fs/aufs/sysfs.c +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * sysfs interface - */ - -#include <linux/compat.h> -#include <linux/seq_file.h> -#include "aufs.h" - -#ifdef CONFIG_AUFS_FS_MODULE -/* this entry violates the "one line per file" policy of sysfs */ -static ssize_t config_show(struct kobject *kobj, struct kobj_attribute *attr, - char *buf) -{ - ssize_t err; - static char *conf = -/* this file is generated at compiling */ -#include "conf.str" - ; - - err = snprintf(buf, PAGE_SIZE, conf); - if (unlikely(err >= PAGE_SIZE)) - err = -EFBIG; - return err; -} - -static struct kobj_attribute au_config_attr = __ATTR_RO(config); -#endif - -static struct attribute *au_attr[] = { -#ifdef CONFIG_AUFS_FS_MODULE - &au_config_attr.attr, -#endif - NULL, /* need to NULL terminate the list of attributes */ -}; - -static struct attribute_group sysaufs_attr_group_body = { - .attrs = au_attr -}; - -struct attribute_group *sysaufs_attr_group = &sysaufs_attr_group_body; - -/* ---------------------------------------------------------------------- */ - -int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb) -{ - int err; - - SiMustAnyLock(sb); - - err = 0; - if (au_opt_test(au_mntflags(sb), XINO)) { - err = au_xino_path(seq, au_sbi(sb)->si_xib); - seq_putc(seq, '\n'); - } - return err; -} - -/* - * the lifetime of branch is independent from the entry under sysfs. - * sysfs handles the lifetime of the entry, and never call ->show() after it is - * unlinked. - */ -static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb, - aufs_bindex_t bindex, int idx) -{ - int err; - struct path path; - struct dentry *root; - struct au_branch *br; - au_br_perm_str_t perm; - - AuDbg("b%d\n", bindex); - - err = 0; - root = sb->s_root; - di_read_lock_parent(root, !AuLock_IR); - br = au_sbr(sb, bindex); - - switch (idx) { - case AuBrSysfs_BR: - path.mnt = au_br_mnt(br); - path.dentry = au_h_dptr(root, bindex); - au_seq_path(seq, &path); - au_optstr_br_perm(&perm, br->br_perm); - err = seq_printf(seq, "=%s\n", perm.a); - break; - case AuBrSysfs_BRID: - err = seq_printf(seq, "%d\n", br->br_id); - break; - } - di_read_unlock(root, !AuLock_IR); - if (err == -1) - err = -E2BIG; - - return err; -} - -/* ---------------------------------------------------------------------- */ - -static struct seq_file *au_seq(char *p, ssize_t len) -{ - struct seq_file *seq; - - seq = kzalloc(sizeof(*seq), GFP_NOFS); - if (seq) { - /* mutex_init(&seq.lock); */ - seq->buf = p; - seq->size = len; - return seq; /* success */ - } - - seq = ERR_PTR(-ENOMEM); - return seq; -} - -#define SysaufsBr_PREFIX "br" -#define SysaufsBrid_PREFIX "brid" - -/* todo: file size may exceed PAGE_SIZE */ -ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - ssize_t err; - int idx; - long l; - aufs_bindex_t bend; - struct au_sbinfo *sbinfo; - struct super_block *sb; - struct seq_file *seq; - char *name; - struct attribute **cattr; - - sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); - sb = sbinfo->si_sb; - - /* - * prevent a race condition between sysfs and aufs. - * for instance, sysfs_file_read() calls sysfs_get_active_two() which - * prohibits maintaining the sysfs entries. - * hew we acquire read lock after sysfs_get_active_two(). - * on the other hand, the remount process may maintain the sysfs/aufs - * entries after acquiring write lock. - * it can cause a deadlock. - * simply we gave up processing read here. - */ - err = -EBUSY; - if (unlikely(!si_noflush_read_trylock(sb))) - goto out; - - seq = au_seq(buf, PAGE_SIZE); - err = PTR_ERR(seq); - if (IS_ERR(seq)) - goto out_unlock; - - name = (void *)attr->name; - cattr = sysaufs_si_attrs; - while (*cattr) { - if (!strcmp(name, (*cattr)->name)) { - err = container_of(*cattr, struct sysaufs_si_attr, attr) - ->show(seq, sb); - goto out_seq; - } - cattr++; - } - - if (!strncmp(name, SysaufsBrid_PREFIX, - sizeof(SysaufsBrid_PREFIX) - 1)) { - idx = AuBrSysfs_BRID; - name += sizeof(SysaufsBrid_PREFIX) - 1; - } else if (!strncmp(name, SysaufsBr_PREFIX, - sizeof(SysaufsBr_PREFIX) - 1)) { - idx = AuBrSysfs_BR; - name += sizeof(SysaufsBr_PREFIX) - 1; - } else - BUG(); - - err = kstrtol(name, 10, &l); - if (!err) { - bend = au_sbend(sb); - if (l <= bend) - err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l, idx); - else - err = -ENOENT; - } - -out_seq: - if (!err) { - err = seq->count; - /* sysfs limit */ - if (unlikely(err == PAGE_SIZE)) - err = -EFBIG; - } - kfree(seq); -out_unlock: - si_read_unlock(sb); -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -static int au_brinfo(struct super_block *sb, union aufs_brinfo __user *arg) -{ - int err; - int16_t brid; - aufs_bindex_t bindex, bend; - size_t sz; - char *buf; - struct seq_file *seq; - struct au_branch *br; - - si_read_lock(sb, AuLock_FLUSH); - bend = au_sbend(sb); - err = bend + 1; - if (!arg) - goto out; - - err = -ENOMEM; - buf = (void *)__get_free_page(GFP_NOFS); - if (unlikely(!buf)) - goto out; - - seq = au_seq(buf, PAGE_SIZE); - err = PTR_ERR(seq); - if (IS_ERR(seq)) - goto out_buf; - - sz = sizeof(*arg) - offsetof(union aufs_brinfo, path); - for (bindex = 0; bindex <= bend; bindex++, arg++) { - err = !access_ok(VERIFY_WRITE, arg, sizeof(*arg)); - if (unlikely(err)) - break; - - br = au_sbr(sb, bindex); - brid = br->br_id; - BUILD_BUG_ON(sizeof(brid) != sizeof(arg->id)); - err = __put_user(brid, &arg->id); - if (unlikely(err)) - break; - - BUILD_BUG_ON(sizeof(br->br_perm) != sizeof(arg->perm)); - err = __put_user(br->br_perm, &arg->perm); - if (unlikely(err)) - break; - - au_seq_path(seq, &br->br_path); - err = seq_putc(seq, '\0'); - if (!err && seq->count <= sz) { - err = copy_to_user(arg->path, seq->buf, seq->count); - seq->count = 0; - if (unlikely(err)) - break; - } else { - err = -E2BIG; - goto out_seq; - } - } - if (unlikely(err)) - err = -EFAULT; - -out_seq: - kfree(seq); -out_buf: - free_page((unsigned long)buf); -out: - si_read_unlock(sb); - return err; -} - -long au_brinfo_ioctl(struct file *file, unsigned long arg) -{ - return au_brinfo(file->f_path.dentry->d_sb, (void __user *)arg); -} - -#ifdef CONFIG_COMPAT -long au_brinfo_compat_ioctl(struct file *file, unsigned long arg) -{ - return au_brinfo(file->f_path.dentry->d_sb, compat_ptr(arg)); -} -#endif - -/* ---------------------------------------------------------------------- */ - -void sysaufs_br_init(struct au_branch *br) -{ - int i; - struct au_brsysfs *br_sysfs; - struct attribute *attr; - - br_sysfs = br->br_sysfs; - for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { - attr = &br_sysfs->attr; - sysfs_attr_init(attr); - attr->name = br_sysfs->name; - attr->mode = S_IRUGO; - br_sysfs++; - } -} - -void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) -{ - struct au_branch *br; - struct kobject *kobj; - struct au_brsysfs *br_sysfs; - int i; - aufs_bindex_t bend; - - dbgaufs_brs_del(sb, bindex); - - if (!sysaufs_brs) - return; - - kobj = &au_sbi(sb)->si_kobj; - bend = au_sbend(sb); - for (; bindex <= bend; bindex++) { - br = au_sbr(sb, bindex); - br_sysfs = br->br_sysfs; - for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { - sysfs_remove_file(kobj, &br_sysfs->attr); - br_sysfs++; - } - } -} - -void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) -{ - int err, i; - aufs_bindex_t bend; - struct kobject *kobj; - struct au_branch *br; - struct au_brsysfs *br_sysfs; - - dbgaufs_brs_add(sb, bindex); - - if (!sysaufs_brs) - return; - - kobj = &au_sbi(sb)->si_kobj; - bend = au_sbend(sb); - for (; bindex <= bend; bindex++) { - br = au_sbr(sb, bindex); - br_sysfs = br->br_sysfs; - snprintf(br_sysfs[AuBrSysfs_BR].name, sizeof(br_sysfs->name), - SysaufsBr_PREFIX "%d", bindex); - snprintf(br_sysfs[AuBrSysfs_BRID].name, sizeof(br_sysfs->name), - SysaufsBrid_PREFIX "%d", bindex); - for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { - err = sysfs_create_file(kobj, &br_sysfs->attr); - if (unlikely(err)) - pr_warn("failed %s under sysfs(%d)\n", - br_sysfs->name, err); - br_sysfs++; - } - } -} diff --git a/fs/aufs/sysrq.c b/fs/aufs/sysrq.c deleted file mode 100644 index a3e95bce6..000000000 --- a/fs/aufs/sysrq.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * magic sysrq hanlder - */ - -/* #include <linux/sysrq.h> */ -#include <linux/writeback.h> -#include "aufs.h" - -/* ---------------------------------------------------------------------- */ - -static void sysrq_sb(struct super_block *sb) -{ - char *plevel; - struct au_sbinfo *sbinfo; - struct file *file; - struct au_sphlhead *files; - struct au_finfo *finfo; - - plevel = au_plevel; - au_plevel = KERN_WARNING; - - /* since we define pr_fmt, call printk directly */ -#define pr(str) printk(KERN_WARNING AUFS_NAME ": " str) - - sbinfo = au_sbi(sb); - printk(KERN_WARNING "si=%lx\n", sysaufs_si_id(sbinfo)); - pr("superblock\n"); - au_dpri_sb(sb); - -#if 0 - pr("root dentry\n"); - au_dpri_dentry(sb->s_root); - pr("root inode\n"); - au_dpri_inode(d_inode(sb->s_root)); -#endif - -#if 0 - do { - int err, i, j, ndentry; - struct au_dcsub_pages dpages; - struct au_dpage *dpage; - - err = au_dpages_init(&dpages, GFP_ATOMIC); - if (unlikely(err)) - break; - err = au_dcsub_pages(&dpages, sb->s_root, NULL, NULL); - if (!err) - for (i = 0; i < dpages.ndpage; i++) { - dpage = dpages.dpages + i; - ndentry = dpage->ndentry; - for (j = 0; j < ndentry; j++) - au_dpri_dentry(dpage->dentries[j]); - } - au_dpages_free(&dpages); - } while (0); -#endif - -#if 1 - { - struct inode *i; - - pr("isolated inode\n"); - spin_lock(&inode_sb_list_lock); - list_for_each_entry(i, &sb->s_inodes, i_sb_list) { - spin_lock(&i->i_lock); - if (1 || hlist_empty(&i->i_dentry)) - au_dpri_inode(i); - spin_unlock(&i->i_lock); - } - spin_unlock(&inode_sb_list_lock); - } -#endif - pr("files\n"); - files = &au_sbi(sb)->si_files; - spin_lock(&files->spin); - hlist_for_each_entry(finfo, &files->head, fi_hlist) { - umode_t mode; - - file = finfo->fi_file; - mode = file_inode(file)->i_mode; - if (!special_file(mode)) - au_dpri_file(file); - } - spin_unlock(&files->spin); - pr("done\n"); - -#undef pr - au_plevel = plevel; -} - -/* ---------------------------------------------------------------------- */ - -/* module parameter */ -static char *aufs_sysrq_key = "a"; -module_param_named(sysrq, aufs_sysrq_key, charp, S_IRUGO); -MODULE_PARM_DESC(sysrq, "MagicSysRq key for " AUFS_NAME); - -static void au_sysrq(int key __maybe_unused) -{ - struct au_sbinfo *sbinfo; - - lockdep_off(); - au_sbilist_lock(); - list_for_each_entry(sbinfo, &au_sbilist.head, si_list) - sysrq_sb(sbinfo->si_sb); - au_sbilist_unlock(); - lockdep_on(); -} - -static struct sysrq_key_op au_sysrq_op = { - .handler = au_sysrq, - .help_msg = "Aufs", - .action_msg = "Aufs", - .enable_mask = SYSRQ_ENABLE_DUMP -}; - -/* ---------------------------------------------------------------------- */ - -int __init au_sysrq_init(void) -{ - int err; - char key; - - err = -1; - key = *aufs_sysrq_key; - if ('a' <= key && key <= 'z') - err = register_sysrq_key(key, &au_sysrq_op); - if (unlikely(err)) - pr_err("err %d, sysrq=%c\n", err, key); - return err; -} - -void au_sysrq_fin(void) -{ - int err; - - err = unregister_sysrq_key(*aufs_sysrq_key, &au_sysrq_op); - if (unlikely(err)) - pr_err("err %d (ignored)\n", err); -} diff --git a/fs/aufs/vdir.c b/fs/aufs/vdir.c deleted file mode 100644 index 0929d393a..000000000 --- a/fs/aufs/vdir.c +++ /dev/null @@ -1,888 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * virtual or vertical directory - */ - -#include "aufs.h" - -static unsigned int calc_size(int nlen) -{ - return ALIGN(sizeof(struct au_vdir_de) + nlen, sizeof(ino_t)); -} - -static int set_deblk_end(union au_vdir_deblk_p *p, - union au_vdir_deblk_p *deblk_end) -{ - if (calc_size(0) <= deblk_end->deblk - p->deblk) { - p->de->de_str.len = 0; - /* smp_mb(); */ - return 0; - } - return -1; /* error */ -} - -/* returns true or false */ -static int is_deblk_end(union au_vdir_deblk_p *p, - union au_vdir_deblk_p *deblk_end) -{ - if (calc_size(0) <= deblk_end->deblk - p->deblk) - return !p->de->de_str.len; - return 1; -} - -static unsigned char *last_deblk(struct au_vdir *vdir) -{ - return vdir->vd_deblk[vdir->vd_nblk - 1]; -} - -/* ---------------------------------------------------------------------- */ - -/* estimate the apropriate size for name hash table */ -unsigned int au_rdhash_est(loff_t sz) -{ - unsigned int n; - - n = UINT_MAX; - sz >>= 10; - if (sz < n) - n = sz; - if (sz < AUFS_RDHASH_DEF) - n = AUFS_RDHASH_DEF; - /* pr_info("n %u\n", n); */ - return n; -} - -/* - * the allocated memory has to be freed by - * au_nhash_wh_free() or au_nhash_de_free(). - */ -int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp) -{ - struct hlist_head *head; - unsigned int u; - size_t sz; - - sz = sizeof(*nhash->nh_head) * num_hash; - head = kmalloc(sz, gfp); - if (head) { - nhash->nh_num = num_hash; - nhash->nh_head = head; - for (u = 0; u < num_hash; u++) - INIT_HLIST_HEAD(head++); - return 0; /* success */ - } - - return -ENOMEM; -} - -static void nhash_count(struct hlist_head *head) -{ -#if 0 - unsigned long n; - struct hlist_node *pos; - - n = 0; - hlist_for_each(pos, head) - n++; - pr_info("%lu\n", n); -#endif -} - -static void au_nhash_wh_do_free(struct hlist_head *head) -{ - struct au_vdir_wh *pos; - struct hlist_node *node; - - hlist_for_each_entry_safe(pos, node, head, wh_hash) - kfree(pos); -} - -static void au_nhash_de_do_free(struct hlist_head *head) -{ - struct au_vdir_dehstr *pos; - struct hlist_node *node; - - hlist_for_each_entry_safe(pos, node, head, hash) - au_cache_free_vdir_dehstr(pos); -} - -static void au_nhash_do_free(struct au_nhash *nhash, - void (*free)(struct hlist_head *head)) -{ - unsigned int n; - struct hlist_head *head; - - n = nhash->nh_num; - if (!n) - return; - - head = nhash->nh_head; - while (n-- > 0) { - nhash_count(head); - free(head++); - } - kfree(nhash->nh_head); -} - -void au_nhash_wh_free(struct au_nhash *whlist) -{ - au_nhash_do_free(whlist, au_nhash_wh_do_free); -} - -static void au_nhash_de_free(struct au_nhash *delist) -{ - au_nhash_do_free(delist, au_nhash_de_do_free); -} - -/* ---------------------------------------------------------------------- */ - -int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, - int limit) -{ - int num; - unsigned int u, n; - struct hlist_head *head; - struct au_vdir_wh *pos; - - num = 0; - n = whlist->nh_num; - head = whlist->nh_head; - for (u = 0; u < n; u++, head++) - hlist_for_each_entry(pos, head, wh_hash) - if (pos->wh_bindex == btgt && ++num > limit) - return 1; - return 0; -} - -static struct hlist_head *au_name_hash(struct au_nhash *nhash, - unsigned char *name, - unsigned int len) -{ - unsigned int v; - /* const unsigned int magic_bit = 12; */ - - AuDebugOn(!nhash->nh_num || !nhash->nh_head); - - v = 0; - while (len--) - v += *name++; - /* v = hash_long(v, magic_bit); */ - v %= nhash->nh_num; - return nhash->nh_head + v; -} - -static int au_nhash_test_name(struct au_vdir_destr *str, const char *name, - int nlen) -{ - return str->len == nlen && !memcmp(str->name, name, nlen); -} - -/* returns found or not */ -int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen) -{ - struct hlist_head *head; - struct au_vdir_wh *pos; - struct au_vdir_destr *str; - - head = au_name_hash(whlist, name, nlen); - hlist_for_each_entry(pos, head, wh_hash) { - str = &pos->wh_str; - AuDbg("%.*s\n", str->len, str->name); - if (au_nhash_test_name(str, name, nlen)) - return 1; - } - return 0; -} - -/* returns found(true) or not */ -static int test_known(struct au_nhash *delist, char *name, int nlen) -{ - struct hlist_head *head; - struct au_vdir_dehstr *pos; - struct au_vdir_destr *str; - - head = au_name_hash(delist, name, nlen); - hlist_for_each_entry(pos, head, hash) { - str = pos->str; - AuDbg("%.*s\n", str->len, str->name); - if (au_nhash_test_name(str, name, nlen)) - return 1; - } - return 0; -} - -static void au_shwh_init_wh(struct au_vdir_wh *wh, ino_t ino, - unsigned char d_type) -{ -#ifdef CONFIG_AUFS_SHWH - wh->wh_ino = ino; - wh->wh_type = d_type; -#endif -} - -/* ---------------------------------------------------------------------- */ - -int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, - unsigned int d_type, aufs_bindex_t bindex, - unsigned char shwh) -{ - int err; - struct au_vdir_destr *str; - struct au_vdir_wh *wh; - - AuDbg("%.*s\n", nlen, name); - AuDebugOn(!whlist->nh_num || !whlist->nh_head); - - err = -ENOMEM; - wh = kmalloc(sizeof(*wh) + nlen, GFP_NOFS); - if (unlikely(!wh)) - goto out; - - err = 0; - wh->wh_bindex = bindex; - if (shwh) - au_shwh_init_wh(wh, ino, d_type); - str = &wh->wh_str; - str->len = nlen; - memcpy(str->name, name, nlen); - hlist_add_head(&wh->wh_hash, au_name_hash(whlist, name, nlen)); - /* smp_mb(); */ - -out: - return err; -} - -static int append_deblk(struct au_vdir *vdir) -{ - int err; - unsigned long ul; - const unsigned int deblk_sz = vdir->vd_deblk_sz; - union au_vdir_deblk_p p, deblk_end; - unsigned char **o; - - err = -ENOMEM; - o = krealloc(vdir->vd_deblk, sizeof(*o) * (vdir->vd_nblk + 1), - GFP_NOFS); - if (unlikely(!o)) - goto out; - - vdir->vd_deblk = o; - p.deblk = kmalloc(deblk_sz, GFP_NOFS); - if (p.deblk) { - ul = vdir->vd_nblk++; - vdir->vd_deblk[ul] = p.deblk; - vdir->vd_last.ul = ul; - vdir->vd_last.p.deblk = p.deblk; - deblk_end.deblk = p.deblk + deblk_sz; - err = set_deblk_end(&p, &deblk_end); - } - -out: - return err; -} - -static int append_de(struct au_vdir *vdir, char *name, int nlen, ino_t ino, - unsigned int d_type, struct au_nhash *delist) -{ - int err; - unsigned int sz; - const unsigned int deblk_sz = vdir->vd_deblk_sz; - union au_vdir_deblk_p p, *room, deblk_end; - struct au_vdir_dehstr *dehstr; - - p.deblk = last_deblk(vdir); - deblk_end.deblk = p.deblk + deblk_sz; - room = &vdir->vd_last.p; - AuDebugOn(room->deblk < p.deblk || deblk_end.deblk <= room->deblk - || !is_deblk_end(room, &deblk_end)); - - sz = calc_size(nlen); - if (unlikely(sz > deblk_end.deblk - room->deblk)) { - err = append_deblk(vdir); - if (unlikely(err)) - goto out; - - p.deblk = last_deblk(vdir); - deblk_end.deblk = p.deblk + deblk_sz; - /* smp_mb(); */ - AuDebugOn(room->deblk != p.deblk); - } - - err = -ENOMEM; - dehstr = au_cache_alloc_vdir_dehstr(); - if (unlikely(!dehstr)) - goto out; - - dehstr->str = &room->de->de_str; - hlist_add_head(&dehstr->hash, au_name_hash(delist, name, nlen)); - room->de->de_ino = ino; - room->de->de_type = d_type; - room->de->de_str.len = nlen; - memcpy(room->de->de_str.name, name, nlen); - - err = 0; - room->deblk += sz; - if (unlikely(set_deblk_end(room, &deblk_end))) - err = append_deblk(vdir); - /* smp_mb(); */ - -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -void au_vdir_free(struct au_vdir *vdir) -{ - unsigned char **deblk; - - deblk = vdir->vd_deblk; - while (vdir->vd_nblk--) - kfree(*deblk++); - kfree(vdir->vd_deblk); - au_cache_free_vdir(vdir); -} - -static struct au_vdir *alloc_vdir(struct file *file) -{ - struct au_vdir *vdir; - struct super_block *sb; - int err; - - sb = file->f_path.dentry->d_sb; - SiMustAnyLock(sb); - - err = -ENOMEM; - vdir = au_cache_alloc_vdir(); - if (unlikely(!vdir)) - goto out; - - vdir->vd_deblk = kzalloc(sizeof(*vdir->vd_deblk), GFP_NOFS); - if (unlikely(!vdir->vd_deblk)) - goto out_free; - - vdir->vd_deblk_sz = au_sbi(sb)->si_rdblk; - if (!vdir->vd_deblk_sz) { - /* estimate the apropriate size for deblk */ - vdir->vd_deblk_sz = au_dir_size(file, /*dentry*/NULL); - /* pr_info("vd_deblk_sz %u\n", vdir->vd_deblk_sz); */ - } - vdir->vd_nblk = 0; - vdir->vd_version = 0; - vdir->vd_jiffy = 0; - err = append_deblk(vdir); - if (!err) - return vdir; /* success */ - - kfree(vdir->vd_deblk); - -out_free: - au_cache_free_vdir(vdir); -out: - vdir = ERR_PTR(err); - return vdir; -} - -static int reinit_vdir(struct au_vdir *vdir) -{ - int err; - union au_vdir_deblk_p p, deblk_end; - - while (vdir->vd_nblk > 1) { - kfree(vdir->vd_deblk[vdir->vd_nblk - 1]); - /* vdir->vd_deblk[vdir->vd_nblk - 1] = NULL; */ - vdir->vd_nblk--; - } - p.deblk = vdir->vd_deblk[0]; - deblk_end.deblk = p.deblk + vdir->vd_deblk_sz; - err = set_deblk_end(&p, &deblk_end); - /* keep vd_dblk_sz */ - vdir->vd_last.ul = 0; - vdir->vd_last.p.deblk = vdir->vd_deblk[0]; - vdir->vd_version = 0; - vdir->vd_jiffy = 0; - /* smp_mb(); */ - return err; -} - -/* ---------------------------------------------------------------------- */ - -#define AuFillVdir_CALLED 1 -#define AuFillVdir_WHABLE (1 << 1) -#define AuFillVdir_SHWH (1 << 2) -#define au_ftest_fillvdir(flags, name) ((flags) & AuFillVdir_##name) -#define au_fset_fillvdir(flags, name) \ - do { (flags) |= AuFillVdir_##name; } while (0) -#define au_fclr_fillvdir(flags, name) \ - do { (flags) &= ~AuFillVdir_##name; } while (0) - -#ifndef CONFIG_AUFS_SHWH -#undef AuFillVdir_SHWH -#define AuFillVdir_SHWH 0 -#endif - -struct fillvdir_arg { - struct dir_context ctx; - struct file *file; - struct au_vdir *vdir; - struct au_nhash delist; - struct au_nhash whlist; - aufs_bindex_t bindex; - unsigned int flags; - int err; -}; - -static int fillvdir(struct dir_context *ctx, const char *__name, int nlen, - loff_t offset __maybe_unused, u64 h_ino, - unsigned int d_type) -{ - struct fillvdir_arg *arg = container_of(ctx, struct fillvdir_arg, ctx); - char *name = (void *)__name; - struct super_block *sb; - ino_t ino; - const unsigned char shwh = !!au_ftest_fillvdir(arg->flags, SHWH); - - arg->err = 0; - sb = arg->file->f_path.dentry->d_sb; - au_fset_fillvdir(arg->flags, CALLED); - /* smp_mb(); */ - if (nlen <= AUFS_WH_PFX_LEN - || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { - if (test_known(&arg->delist, name, nlen) - || au_nhash_test_known_wh(&arg->whlist, name, nlen)) - goto out; /* already exists or whiteouted */ - - arg->err = au_ino(sb, arg->bindex, h_ino, d_type, &ino); - if (!arg->err) { - if (unlikely(nlen > AUFS_MAX_NAMELEN)) - d_type = DT_UNKNOWN; - arg->err = append_de(arg->vdir, name, nlen, ino, - d_type, &arg->delist); - } - } else if (au_ftest_fillvdir(arg->flags, WHABLE)) { - name += AUFS_WH_PFX_LEN; - nlen -= AUFS_WH_PFX_LEN; - if (au_nhash_test_known_wh(&arg->whlist, name, nlen)) - goto out; /* already whiteouted */ - - if (shwh) - arg->err = au_wh_ino(sb, arg->bindex, h_ino, d_type, - &ino); - if (!arg->err) { - if (nlen <= AUFS_MAX_NAMELEN + AUFS_WH_PFX_LEN) - d_type = DT_UNKNOWN; - arg->err = au_nhash_append_wh - (&arg->whlist, name, nlen, ino, d_type, - arg->bindex, shwh); - } - } - -out: - if (!arg->err) - arg->vdir->vd_jiffy = jiffies; - /* smp_mb(); */ - AuTraceErr(arg->err); - return arg->err; -} - -static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir, - struct au_nhash *whlist, struct au_nhash *delist) -{ -#ifdef CONFIG_AUFS_SHWH - int err; - unsigned int nh, u; - struct hlist_head *head; - struct au_vdir_wh *pos; - struct hlist_node *n; - char *p, *o; - struct au_vdir_destr *destr; - - AuDebugOn(!au_opt_test(au_mntflags(sb), SHWH)); - - err = -ENOMEM; - o = p = (void *)__get_free_page(GFP_NOFS); - if (unlikely(!p)) - goto out; - - err = 0; - nh = whlist->nh_num; - memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); - p += AUFS_WH_PFX_LEN; - for (u = 0; u < nh; u++) { - head = whlist->nh_head + u; - hlist_for_each_entry_safe(pos, n, head, wh_hash) { - destr = &pos->wh_str; - memcpy(p, destr->name, destr->len); - err = append_de(vdir, o, destr->len + AUFS_WH_PFX_LEN, - pos->wh_ino, pos->wh_type, delist); - if (unlikely(err)) - break; - } - } - - free_page((unsigned long)o); - -out: - AuTraceErr(err); - return err; -#else - return 0; -#endif -} - -static int au_do_read_vdir(struct fillvdir_arg *arg) -{ - int err; - unsigned int rdhash; - loff_t offset; - aufs_bindex_t bend, bindex, bstart; - unsigned char shwh; - struct file *hf, *file; - struct super_block *sb; - - file = arg->file; - sb = file->f_path.dentry->d_sb; - SiMustAnyLock(sb); - - rdhash = au_sbi(sb)->si_rdhash; - if (!rdhash) - rdhash = au_rdhash_est(au_dir_size(file, /*dentry*/NULL)); - err = au_nhash_alloc(&arg->delist, rdhash, GFP_NOFS); - if (unlikely(err)) - goto out; - err = au_nhash_alloc(&arg->whlist, rdhash, GFP_NOFS); - if (unlikely(err)) - goto out_delist; - - err = 0; - arg->flags = 0; - shwh = 0; - if (au_opt_test(au_mntflags(sb), SHWH)) { - shwh = 1; - au_fset_fillvdir(arg->flags, SHWH); - } - bstart = au_fbstart(file); - bend = au_fbend_dir(file); - for (bindex = bstart; !err && bindex <= bend; bindex++) { - hf = au_hf_dir(file, bindex); - if (!hf) - continue; - - offset = vfsub_llseek(hf, 0, SEEK_SET); - err = offset; - if (unlikely(offset)) - break; - - arg->bindex = bindex; - au_fclr_fillvdir(arg->flags, WHABLE); - if (shwh - || (bindex != bend - && au_br_whable(au_sbr_perm(sb, bindex)))) - au_fset_fillvdir(arg->flags, WHABLE); - do { - arg->err = 0; - au_fclr_fillvdir(arg->flags, CALLED); - /* smp_mb(); */ - err = vfsub_iterate_dir(hf, &arg->ctx); - if (err >= 0) - err = arg->err; - } while (!err && au_ftest_fillvdir(arg->flags, CALLED)); - - /* - * dir_relax() may be good for concurrency, but aufs should not - * use it since it will cause a lockdep problem. - */ - } - - if (!err && shwh) - err = au_handle_shwh(sb, arg->vdir, &arg->whlist, &arg->delist); - - au_nhash_wh_free(&arg->whlist); - -out_delist: - au_nhash_de_free(&arg->delist); -out: - return err; -} - -static int read_vdir(struct file *file, int may_read) -{ - int err; - unsigned long expire; - unsigned char do_read; - struct fillvdir_arg arg = { - .ctx = { - .actor = fillvdir - } - }; - struct inode *inode; - struct au_vdir *vdir, *allocated; - - err = 0; - inode = file_inode(file); - IMustLock(inode); - SiMustAnyLock(inode->i_sb); - - allocated = NULL; - do_read = 0; - expire = au_sbi(inode->i_sb)->si_rdcache; - vdir = au_ivdir(inode); - if (!vdir) { - do_read = 1; - vdir = alloc_vdir(file); - err = PTR_ERR(vdir); - if (IS_ERR(vdir)) - goto out; - err = 0; - allocated = vdir; - } else if (may_read - && (inode->i_version != vdir->vd_version - || time_after(jiffies, vdir->vd_jiffy + expire))) { - do_read = 1; - err = reinit_vdir(vdir); - if (unlikely(err)) - goto out; - } - - if (!do_read) - return 0; /* success */ - - arg.file = file; - arg.vdir = vdir; - err = au_do_read_vdir(&arg); - if (!err) { - /* file->f_pos = 0; */ /* todo: ctx->pos? */ - vdir->vd_version = inode->i_version; - vdir->vd_last.ul = 0; - vdir->vd_last.p.deblk = vdir->vd_deblk[0]; - if (allocated) - au_set_ivdir(inode, allocated); - } else if (allocated) - au_vdir_free(allocated); - -out: - return err; -} - -static int copy_vdir(struct au_vdir *tgt, struct au_vdir *src) -{ - int err, rerr; - unsigned long ul, n; - const unsigned int deblk_sz = src->vd_deblk_sz; - - AuDebugOn(tgt->vd_nblk != 1); - - err = -ENOMEM; - if (tgt->vd_nblk < src->vd_nblk) { - unsigned char **p; - - p = krealloc(tgt->vd_deblk, sizeof(*p) * src->vd_nblk, - GFP_NOFS); - if (unlikely(!p)) - goto out; - tgt->vd_deblk = p; - } - - if (tgt->vd_deblk_sz != deblk_sz) { - unsigned char *p; - - tgt->vd_deblk_sz = deblk_sz; - p = krealloc(tgt->vd_deblk[0], deblk_sz, GFP_NOFS); - if (unlikely(!p)) - goto out; - tgt->vd_deblk[0] = p; - } - memcpy(tgt->vd_deblk[0], src->vd_deblk[0], deblk_sz); - tgt->vd_version = src->vd_version; - tgt->vd_jiffy = src->vd_jiffy; - - n = src->vd_nblk; - for (ul = 1; ul < n; ul++) { - tgt->vd_deblk[ul] = kmemdup(src->vd_deblk[ul], deblk_sz, - GFP_NOFS); - if (unlikely(!tgt->vd_deblk[ul])) - goto out; - tgt->vd_nblk++; - } - tgt->vd_nblk = n; - tgt->vd_last.ul = tgt->vd_last.ul; - tgt->vd_last.p.deblk = tgt->vd_deblk[tgt->vd_last.ul]; - tgt->vd_last.p.deblk += src->vd_last.p.deblk - - src->vd_deblk[src->vd_last.ul]; - /* smp_mb(); */ - return 0; /* success */ - -out: - rerr = reinit_vdir(tgt); - BUG_ON(rerr); - return err; -} - -int au_vdir_init(struct file *file) -{ - int err; - struct inode *inode; - struct au_vdir *vdir_cache, *allocated; - - /* test file->f_pos here instead of ctx->pos */ - err = read_vdir(file, !file->f_pos); - if (unlikely(err)) - goto out; - - allocated = NULL; - vdir_cache = au_fvdir_cache(file); - if (!vdir_cache) { - vdir_cache = alloc_vdir(file); - err = PTR_ERR(vdir_cache); - if (IS_ERR(vdir_cache)) - goto out; - allocated = vdir_cache; - } else if (!file->f_pos && vdir_cache->vd_version != file->f_version) { - /* test file->f_pos here instead of ctx->pos */ - err = reinit_vdir(vdir_cache); - if (unlikely(err)) - goto out; - } else - return 0; /* success */ - - inode = file_inode(file); - err = copy_vdir(vdir_cache, au_ivdir(inode)); - if (!err) { - file->f_version = inode->i_version; - if (allocated) - au_set_fvdir_cache(file, allocated); - } else if (allocated) - au_vdir_free(allocated); - -out: - return err; -} - -static loff_t calc_offset(struct au_vdir *vdir) -{ - loff_t offset; - union au_vdir_deblk_p p; - - p.deblk = vdir->vd_deblk[vdir->vd_last.ul]; - offset = vdir->vd_last.p.deblk - p.deblk; - offset += vdir->vd_deblk_sz * vdir->vd_last.ul; - return offset; -} - -/* returns true or false */ -static int seek_vdir(struct file *file, struct dir_context *ctx) -{ - int valid; - unsigned int deblk_sz; - unsigned long ul, n; - loff_t offset; - union au_vdir_deblk_p p, deblk_end; - struct au_vdir *vdir_cache; - - valid = 1; - vdir_cache = au_fvdir_cache(file); - offset = calc_offset(vdir_cache); - AuDbg("offset %lld\n", offset); - if (ctx->pos == offset) - goto out; - - vdir_cache->vd_last.ul = 0; - vdir_cache->vd_last.p.deblk = vdir_cache->vd_deblk[0]; - if (!ctx->pos) - goto out; - - valid = 0; - deblk_sz = vdir_cache->vd_deblk_sz; - ul = div64_u64(ctx->pos, deblk_sz); - AuDbg("ul %lu\n", ul); - if (ul >= vdir_cache->vd_nblk) - goto out; - - n = vdir_cache->vd_nblk; - for (; ul < n; ul++) { - p.deblk = vdir_cache->vd_deblk[ul]; - deblk_end.deblk = p.deblk + deblk_sz; - offset = ul; - offset *= deblk_sz; - while (!is_deblk_end(&p, &deblk_end) && offset < ctx->pos) { - unsigned int l; - - l = calc_size(p.de->de_str.len); - offset += l; - p.deblk += l; - } - if (!is_deblk_end(&p, &deblk_end)) { - valid = 1; - vdir_cache->vd_last.ul = ul; - vdir_cache->vd_last.p = p; - break; - } - } - -out: - /* smp_mb(); */ - AuTraceErr(!valid); - return valid; -} - -int au_vdir_fill_de(struct file *file, struct dir_context *ctx) -{ - unsigned int l, deblk_sz; - union au_vdir_deblk_p deblk_end; - struct au_vdir *vdir_cache; - struct au_vdir_de *de; - - vdir_cache = au_fvdir_cache(file); - if (!seek_vdir(file, ctx)) - return 0; - - deblk_sz = vdir_cache->vd_deblk_sz; - while (1) { - deblk_end.deblk = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; - deblk_end.deblk += deblk_sz; - while (!is_deblk_end(&vdir_cache->vd_last.p, &deblk_end)) { - de = vdir_cache->vd_last.p.de; - AuDbg("%.*s, off%lld, i%lu, dt%d\n", - de->de_str.len, de->de_str.name, ctx->pos, - (unsigned long)de->de_ino, de->de_type); - if (unlikely(!dir_emit(ctx, de->de_str.name, - de->de_str.len, de->de_ino, - de->de_type))) { - /* todo: ignore the error caused by udba? */ - /* return err; */ - return 0; - } - - l = calc_size(de->de_str.len); - vdir_cache->vd_last.p.deblk += l; - ctx->pos += l; - } - if (vdir_cache->vd_last.ul < vdir_cache->vd_nblk - 1) { - vdir_cache->vd_last.ul++; - vdir_cache->vd_last.p.deblk - = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; - ctx->pos = deblk_sz * vdir_cache->vd_last.ul; - continue; - } - break; - } - - /* smp_mb(); */ - return 0; -} diff --git a/fs/aufs/vfsub.c b/fs/aufs/vfsub.c deleted file mode 100644 index d2af7ce8d..000000000 --- a/fs/aufs/vfsub.c +++ /dev/null @@ -1,848 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * sub-routines for VFS - */ - -#include <linux/namei.h> -#include <linux/security.h> -#include <linux/splice.h> -#include "aufs.h" - -int vfsub_update_h_iattr(struct path *h_path, int *did) -{ - int err; - struct kstat st; - struct super_block *h_sb; - - /* for remote fs, leave work for its getattr or d_revalidate */ - /* for bad i_attr fs, handle them in aufs_getattr() */ - /* still some fs may acquire i_mutex. we need to skip them */ - err = 0; - if (!did) - did = &err; - h_sb = h_path->dentry->d_sb; - *did = (!au_test_fs_remote(h_sb) && au_test_fs_refresh_iattr(h_sb)); - if (*did) - err = vfs_getattr(h_path, &st); - - return err; -} - -/* ---------------------------------------------------------------------- */ - -struct file *vfsub_dentry_open(struct path *path, int flags) -{ - struct file *file; - - file = dentry_open(path, flags /* | __FMODE_NONOTIFY */, - current_cred()); - if (!IS_ERR_OR_NULL(file) - && (file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) - i_readcount_inc(d_inode(path->dentry)); - - return file; -} - -struct file *vfsub_filp_open(const char *path, int oflags, int mode) -{ - struct file *file; - - lockdep_off(); - file = filp_open(path, - oflags /* | __FMODE_NONOTIFY */, - mode); - lockdep_on(); - if (IS_ERR(file)) - goto out; - vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ - -out: - return file; -} - -/* - * Ideally this function should call VFS:do_last() in order to keep all its - * checkings. But it is very hard for aufs to regenerate several VFS internal - * structure such as nameidata. This is a second (or third) best approach. - * cf. linux/fs/namei.c:do_last(), lookup_open() and atomic_open(). - */ -int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, - struct vfsub_aopen_args *args, struct au_branch *br) -{ - int err; - struct file *file = args->file; - /* copied from linux/fs/namei.c:atomic_open() */ - struct dentry *const DENTRY_NOT_SET = (void *)-1UL; - - IMustLock(dir); - AuDebugOn(!dir->i_op->atomic_open); - - err = au_br_test_oflag(args->open_flag, br); - if (unlikely(err)) - goto out; - - args->file->f_path.dentry = DENTRY_NOT_SET; - args->file->f_path.mnt = au_br_mnt(br); - err = dir->i_op->atomic_open(dir, dentry, file, args->open_flag, - args->create_mode, args->opened); - if (err >= 0) { - /* some filesystems don't set FILE_CREATED while succeeded? */ - if (*args->opened & FILE_CREATED) - fsnotify_create(dir, dentry); - } else - goto out; - - - if (!err) { - /* todo: call VFS:may_open() here */ - err = open_check_o_direct(file); - /* todo: ima_file_check() too? */ - if (!err && (args->open_flag & __FMODE_EXEC)) - err = deny_write_access(file); - if (unlikely(err)) - /* note that the file is created and still opened */ - goto out; - } - - atomic_inc(&br->br_count); - fsnotify_open(file); - -out: - return err; -} - -int vfsub_kern_path(const char *name, unsigned int flags, struct path *path) -{ - int err; - - err = kern_path(name, flags, path); - if (!err && d_is_positive(path->dentry)) - vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/ - return err; -} - -struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, - int len) -{ - struct path path = { - .mnt = NULL - }; - - /* VFS checks it too, but by WARN_ON_ONCE() */ - IMustLock(d_inode(parent)); - - path.dentry = lookup_one_len(name, parent, len); - if (IS_ERR(path.dentry)) - goto out; - if (d_is_positive(path.dentry)) - vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ - -out: - AuTraceErrPtr(path.dentry); - return path.dentry; -} - -void vfsub_call_lkup_one(void *args) -{ - struct vfsub_lkup_one_args *a = args; - *a->errp = vfsub_lkup_one(a->name, a->parent); -} - -/* ---------------------------------------------------------------------- */ - -struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, - struct dentry *d2, struct au_hinode *hdir2) -{ - struct dentry *d; - - lockdep_off(); - d = lock_rename(d1, d2); - lockdep_on(); - au_hn_suspend(hdir1); - if (hdir1 != hdir2) - au_hn_suspend(hdir2); - - return d; -} - -void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, - struct dentry *d2, struct au_hinode *hdir2) -{ - au_hn_resume(hdir1); - if (hdir1 != hdir2) - au_hn_resume(hdir2); - lockdep_off(); - unlock_rename(d1, d2); - lockdep_on(); -} - -/* ---------------------------------------------------------------------- */ - -int vfsub_create(struct inode *dir, struct path *path, int mode, bool want_excl) -{ - int err; - struct dentry *d; - - IMustLock(dir); - - d = path->dentry; - path->dentry = d->d_parent; - err = security_path_mknod(path, d, mode, 0); - path->dentry = d; - if (unlikely(err)) - goto out; - - lockdep_off(); - err = vfs_create(dir, path->dentry, mode, want_excl); - lockdep_on(); - if (!err) { - struct path tmp = *path; - int did; - - vfsub_update_h_iattr(&tmp, &did); - if (did) { - tmp.dentry = path->dentry->d_parent; - vfsub_update_h_iattr(&tmp, /*did*/NULL); - } - /*ignore*/ - } - -out: - return err; -} - -int vfsub_symlink(struct inode *dir, struct path *path, const char *symname) -{ - int err; - struct dentry *d; - - IMustLock(dir); - - d = path->dentry; - path->dentry = d->d_parent; - err = security_path_symlink(path, d, symname); - path->dentry = d; - if (unlikely(err)) - goto out; - - lockdep_off(); - err = vfs_symlink(dir, path->dentry, symname); - lockdep_on(); - if (!err) { - struct path tmp = *path; - int did; - - vfsub_update_h_iattr(&tmp, &did); - if (did) { - tmp.dentry = path->dentry->d_parent; - vfsub_update_h_iattr(&tmp, /*did*/NULL); - } - /*ignore*/ - } - -out: - return err; -} - -int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev) -{ - int err; - struct dentry *d; - - IMustLock(dir); - - d = path->dentry; - path->dentry = d->d_parent; - err = security_path_mknod(path, d, mode, new_encode_dev(dev)); - path->dentry = d; - if (unlikely(err)) - goto out; - - lockdep_off(); - err = vfs_mknod(dir, path->dentry, mode, dev); - lockdep_on(); - if (!err) { - struct path tmp = *path; - int did; - - vfsub_update_h_iattr(&tmp, &did); - if (did) { - tmp.dentry = path->dentry->d_parent; - vfsub_update_h_iattr(&tmp, /*did*/NULL); - } - /*ignore*/ - } - -out: - return err; -} - -static int au_test_nlink(struct inode *inode) -{ - const unsigned int link_max = UINT_MAX >> 1; /* rough margin */ - - if (!au_test_fs_no_limit_nlink(inode->i_sb) - || inode->i_nlink < link_max) - return 0; - return -EMLINK; -} - -int vfsub_link(struct dentry *src_dentry, struct inode *dir, struct path *path, - struct inode **delegated_inode) -{ - int err; - struct dentry *d; - - IMustLock(dir); - - err = au_test_nlink(d_inode(src_dentry)); - if (unlikely(err)) - return err; - - /* we don't call may_linkat() */ - d = path->dentry; - path->dentry = d->d_parent; - err = security_path_link(src_dentry, path, d); - path->dentry = d; - if (unlikely(err)) - goto out; - - lockdep_off(); - err = vfs_link(src_dentry, dir, path->dentry, delegated_inode); - lockdep_on(); - if (!err) { - struct path tmp = *path; - int did; - - /* fuse has different memory inode for the same inumber */ - vfsub_update_h_iattr(&tmp, &did); - if (did) { - tmp.dentry = path->dentry->d_parent; - vfsub_update_h_iattr(&tmp, /*did*/NULL); - tmp.dentry = src_dentry; - vfsub_update_h_iattr(&tmp, /*did*/NULL); - } - /*ignore*/ - } - -out: - return err; -} - -int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry, - struct inode *dir, struct path *path, - struct inode **delegated_inode) -{ - int err; - struct path tmp = { - .mnt = path->mnt - }; - struct dentry *d; - - IMustLock(dir); - IMustLock(src_dir); - - d = path->dentry; - path->dentry = d->d_parent; - tmp.dentry = src_dentry->d_parent; - err = security_path_rename(&tmp, src_dentry, path, d, /*flags*/0); - path->dentry = d; - if (unlikely(err)) - goto out; - - lockdep_off(); - err = vfs_rename(src_dir, src_dentry, dir, path->dentry, - delegated_inode, /*flags*/0); - lockdep_on(); - if (!err) { - int did; - - tmp.dentry = d->d_parent; - vfsub_update_h_iattr(&tmp, &did); - if (did) { - tmp.dentry = src_dentry; - vfsub_update_h_iattr(&tmp, /*did*/NULL); - tmp.dentry = src_dentry->d_parent; - vfsub_update_h_iattr(&tmp, /*did*/NULL); - } - /*ignore*/ - } - -out: - return err; -} - -int vfsub_mkdir(struct inode *dir, struct path *path, int mode) -{ - int err; - struct dentry *d; - - IMustLock(dir); - - d = path->dentry; - path->dentry = d->d_parent; - err = security_path_mkdir(path, d, mode); - path->dentry = d; - if (unlikely(err)) - goto out; - - lockdep_off(); - err = vfs_mkdir(dir, path->dentry, mode); - lockdep_on(); - if (!err) { - struct path tmp = *path; - int did; - - vfsub_update_h_iattr(&tmp, &did); - if (did) { - tmp.dentry = path->dentry->d_parent; - vfsub_update_h_iattr(&tmp, /*did*/NULL); - } - /*ignore*/ - } - -out: - return err; -} - -int vfsub_rmdir(struct inode *dir, struct path *path) -{ - int err; - struct dentry *d; - - IMustLock(dir); - - d = path->dentry; - path->dentry = d->d_parent; - err = security_path_rmdir(path, d); - path->dentry = d; - if (unlikely(err)) - goto out; - - lockdep_off(); - err = vfs_rmdir(dir, path->dentry); - lockdep_on(); - if (!err) { - struct path tmp = { - .dentry = path->dentry->d_parent, - .mnt = path->mnt - }; - - vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ - } - -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* todo: support mmap_sem? */ -ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, - loff_t *ppos) -{ - ssize_t err; - - lockdep_off(); - err = vfs_read(file, ubuf, count, ppos); - lockdep_on(); - if (err >= 0) - vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ - return err; -} - -/* todo: kernel_read()? */ -ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, - loff_t *ppos) -{ - ssize_t err; - mm_segment_t oldfs; - union { - void *k; - char __user *u; - } buf; - - buf.k = kbuf; - oldfs = get_fs(); - set_fs(KERNEL_DS); - err = vfsub_read_u(file, buf.u, count, ppos); - set_fs(oldfs); - return err; -} - -ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, - loff_t *ppos) -{ - ssize_t err; - - lockdep_off(); - err = vfs_write(file, ubuf, count, ppos); - lockdep_on(); - if (err >= 0) - vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ - return err; -} - -ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos) -{ - ssize_t err; - mm_segment_t oldfs; - union { - void *k; - const char __user *u; - } buf; - - buf.k = kbuf; - oldfs = get_fs(); - set_fs(KERNEL_DS); - err = vfsub_write_u(file, buf.u, count, ppos); - set_fs(oldfs); - return err; -} - -int vfsub_flush(struct file *file, fl_owner_t id) -{ - int err; - - err = 0; - if (file->f_op->flush) { - if (!au_test_nfs(file->f_path.dentry->d_sb)) - err = file->f_op->flush(file, id); - else { - lockdep_off(); - err = file->f_op->flush(file, id); - lockdep_on(); - } - if (!err) - vfsub_update_h_iattr(&file->f_path, /*did*/NULL); - /*ignore*/ - } - return err; -} - -int vfsub_iterate_dir(struct file *file, struct dir_context *ctx) -{ - int err; - - AuDbg("%pD, ctx{%pf, %llu}\n", file, ctx->actor, ctx->pos); - - lockdep_off(); - err = iterate_dir(file, ctx); - lockdep_on(); - if (err >= 0) - vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ - return err; -} - -long vfsub_splice_to(struct file *in, loff_t *ppos, - struct pipe_inode_info *pipe, size_t len, - unsigned int flags) -{ - long err; - - lockdep_off(); - err = do_splice_to(in, ppos, pipe, len, flags); - lockdep_on(); - file_accessed(in); - if (err >= 0) - vfsub_update_h_iattr(&in->f_path, /*did*/NULL); /*ignore*/ - return err; -} - -long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, - loff_t *ppos, size_t len, unsigned int flags) -{ - long err; - - lockdep_off(); - err = do_splice_from(pipe, out, ppos, len, flags); - lockdep_on(); - if (err >= 0) - vfsub_update_h_iattr(&out->f_path, /*did*/NULL); /*ignore*/ - return err; -} - -int vfsub_fsync(struct file *file, struct path *path, int datasync) -{ - int err; - - /* file can be NULL */ - lockdep_off(); - err = vfs_fsync(file, datasync); - lockdep_on(); - if (!err) { - if (!path) { - AuDebugOn(!file); - path = &file->f_path; - } - vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/ - } - return err; -} - -/* cf. open.c:do_sys_truncate() and do_sys_ftruncate() */ -int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, - struct file *h_file) -{ - int err; - struct inode *h_inode; - struct super_block *h_sb; - - if (!h_file) { - err = vfsub_truncate(h_path, length); - goto out; - } - - h_inode = d_inode(h_path->dentry); - h_sb = h_inode->i_sb; - lockdep_off(); - sb_start_write(h_sb); - lockdep_on(); - err = locks_verify_truncate(h_inode, h_file, length); - if (!err) - err = security_path_truncate(h_path); - if (!err) { - lockdep_off(); - err = do_truncate(h_path->dentry, length, attr, h_file); - lockdep_on(); - } - lockdep_off(); - sb_end_write(h_sb); - lockdep_on(); - -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -struct au_vfsub_mkdir_args { - int *errp; - struct inode *dir; - struct path *path; - int mode; -}; - -static void au_call_vfsub_mkdir(void *args) -{ - struct au_vfsub_mkdir_args *a = args; - *a->errp = vfsub_mkdir(a->dir, a->path, a->mode); -} - -int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode) -{ - int err, do_sio, wkq_err; - - do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); - if (!do_sio) { - lockdep_off(); - err = vfsub_mkdir(dir, path, mode); - lockdep_on(); - } else { - struct au_vfsub_mkdir_args args = { - .errp = &err, - .dir = dir, - .path = path, - .mode = mode - }; - wkq_err = au_wkq_wait(au_call_vfsub_mkdir, &args); - if (unlikely(wkq_err)) - err = wkq_err; - } - - return err; -} - -struct au_vfsub_rmdir_args { - int *errp; - struct inode *dir; - struct path *path; -}; - -static void au_call_vfsub_rmdir(void *args) -{ - struct au_vfsub_rmdir_args *a = args; - *a->errp = vfsub_rmdir(a->dir, a->path); -} - -int vfsub_sio_rmdir(struct inode *dir, struct path *path) -{ - int err, do_sio, wkq_err; - - do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); - if (!do_sio) { - lockdep_off(); - err = vfsub_rmdir(dir, path); - lockdep_on(); - } else { - struct au_vfsub_rmdir_args args = { - .errp = &err, - .dir = dir, - .path = path - }; - wkq_err = au_wkq_wait(au_call_vfsub_rmdir, &args); - if (unlikely(wkq_err)) - err = wkq_err; - } - - return err; -} - -/* ---------------------------------------------------------------------- */ - -struct notify_change_args { - int *errp; - struct path *path; - struct iattr *ia; - struct inode **delegated_inode; -}; - -static void call_notify_change(void *args) -{ - struct notify_change_args *a = args; - struct inode *h_inode; - - h_inode = d_inode(a->path->dentry); - IMustLock(h_inode); - - *a->errp = -EPERM; - if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) { - lockdep_off(); - *a->errp = notify_change(a->path->dentry, a->ia, - a->delegated_inode); - lockdep_on(); - if (!*a->errp) - vfsub_update_h_iattr(a->path, /*did*/NULL); /*ignore*/ - } - AuTraceErr(*a->errp); -} - -int vfsub_notify_change(struct path *path, struct iattr *ia, - struct inode **delegated_inode) -{ - int err; - struct notify_change_args args = { - .errp = &err, - .path = path, - .ia = ia, - .delegated_inode = delegated_inode - }; - - call_notify_change(&args); - - return err; -} - -int vfsub_sio_notify_change(struct path *path, struct iattr *ia, - struct inode **delegated_inode) -{ - int err, wkq_err; - struct notify_change_args args = { - .errp = &err, - .path = path, - .ia = ia, - .delegated_inode = delegated_inode - }; - - wkq_err = au_wkq_wait(call_notify_change, &args); - if (unlikely(wkq_err)) - err = wkq_err; - - return err; -} - -/* ---------------------------------------------------------------------- */ - -struct unlink_args { - int *errp; - struct inode *dir; - struct path *path; - struct inode **delegated_inode; -}; - -static void call_unlink(void *args) -{ - struct unlink_args *a = args; - struct dentry *d = a->path->dentry; - struct inode *h_inode; - const int stop_sillyrename = (au_test_nfs(d->d_sb) - && au_dcount(d) == 1); - - IMustLock(a->dir); - - a->path->dentry = d->d_parent; - *a->errp = security_path_unlink(a->path, d); - a->path->dentry = d; - if (unlikely(*a->errp)) - return; - - if (!stop_sillyrename) - dget(d); - h_inode = NULL; - if (d_is_positive(d)) { - h_inode = d_inode(d); - ihold(h_inode); - } - - lockdep_off(); - *a->errp = vfs_unlink(a->dir, d, a->delegated_inode); - lockdep_on(); - if (!*a->errp) { - struct path tmp = { - .dentry = d->d_parent, - .mnt = a->path->mnt - }; - vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ - } - - if (!stop_sillyrename) - dput(d); - if (h_inode) - iput(h_inode); - - AuTraceErr(*a->errp); -} - -/* - * @dir: must be locked. - * @dentry: target dentry. - */ -int vfsub_unlink(struct inode *dir, struct path *path, - struct inode **delegated_inode, int force) -{ - int err; - struct unlink_args args = { - .errp = &err, - .dir = dir, - .path = path, - .delegated_inode = delegated_inode - }; - - if (!force) - call_unlink(&args); - else { - int wkq_err; - - wkq_err = au_wkq_wait(call_unlink, &args); - if (unlikely(wkq_err)) - err = wkq_err; - } - - return err; -} diff --git a/fs/aufs/vfsub.h b/fs/aufs/vfsub.h deleted file mode 100644 index 5c89f8e56..000000000 --- a/fs/aufs/vfsub.h +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * sub-routines for VFS - */ - -#ifndef __AUFS_VFSUB_H__ -#define __AUFS_VFSUB_H__ - -#ifdef __KERNEL__ - -#include <linux/fs.h> -#include <linux/mount.h> -#include <linux/xattr.h> -#include "debug.h" - -/* copied from linux/fs/internal.h */ -/* todo: BAD approach!! */ -extern void __mnt_drop_write(struct vfsmount *); -extern spinlock_t inode_sb_list_lock; -extern int open_check_o_direct(struct file *f); - -/* ---------------------------------------------------------------------- */ - -/* lock subclass for lower inode */ -/* default MAX_LOCKDEP_SUBCLASSES(8) is not enough */ -/* reduce? gave up. */ -enum { - AuLsc_I_Begin = I_MUTEX_PARENT2, /* 5 */ - AuLsc_I_PARENT, /* lower inode, parent first */ - AuLsc_I_PARENT2, /* copyup dirs */ - AuLsc_I_PARENT3, /* copyup wh */ - AuLsc_I_CHILD, - AuLsc_I_CHILD2, - AuLsc_I_End -}; - -/* to debug easier, do not make them inlined functions */ -#define MtxMustLock(mtx) AuDebugOn(!mutex_is_locked(mtx)) -#define IMustLock(i) MtxMustLock(&(i)->i_mutex) - -/* ---------------------------------------------------------------------- */ - -static inline void vfsub_drop_nlink(struct inode *inode) -{ - AuDebugOn(!inode->i_nlink); - drop_nlink(inode); -} - -static inline void vfsub_dead_dir(struct inode *inode) -{ - AuDebugOn(!S_ISDIR(inode->i_mode)); - inode->i_flags |= S_DEAD; - clear_nlink(inode); -} - -static inline int vfsub_native_ro(struct inode *inode) -{ - return (inode->i_sb->s_flags & MS_RDONLY) - || IS_RDONLY(inode) - /* || IS_APPEND(inode) */ - || IS_IMMUTABLE(inode); -} - -/* ---------------------------------------------------------------------- */ - -int vfsub_update_h_iattr(struct path *h_path, int *did); -struct file *vfsub_dentry_open(struct path *path, int flags); -struct file *vfsub_filp_open(const char *path, int oflags, int mode); -struct vfsub_aopen_args { - struct file *file; - unsigned int open_flag; - umode_t create_mode; - int *opened; -}; -struct au_branch; -int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, - struct vfsub_aopen_args *args, struct au_branch *br); -int vfsub_kern_path(const char *name, unsigned int flags, struct path *path); - -struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, - int len); - -struct vfsub_lkup_one_args { - struct dentry **errp; - struct qstr *name; - struct dentry *parent; -}; - -static inline struct dentry *vfsub_lkup_one(struct qstr *name, - struct dentry *parent) -{ - return vfsub_lookup_one_len(name->name, parent, name->len); -} - -void vfsub_call_lkup_one(void *args); - -/* ---------------------------------------------------------------------- */ - -static inline int vfsub_mnt_want_write(struct vfsmount *mnt) -{ - int err; - - lockdep_off(); - err = mnt_want_write(mnt); - lockdep_on(); - return err; -} - -static inline void vfsub_mnt_drop_write(struct vfsmount *mnt) -{ - lockdep_off(); - mnt_drop_write(mnt); - lockdep_on(); -} - -#if 0 /* reserved */ -static inline void vfsub_mnt_drop_write_file(struct file *file) -{ - lockdep_off(); - mnt_drop_write_file(file); - lockdep_on(); -} -#endif - -/* ---------------------------------------------------------------------- */ - -struct au_hinode; -struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, - struct dentry *d2, struct au_hinode *hdir2); -void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, - struct dentry *d2, struct au_hinode *hdir2); - -int vfsub_create(struct inode *dir, struct path *path, int mode, - bool want_excl); -int vfsub_symlink(struct inode *dir, struct path *path, - const char *symname); -int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev); -int vfsub_link(struct dentry *src_dentry, struct inode *dir, - struct path *path, struct inode **delegated_inode); -int vfsub_rename(struct inode *src_hdir, struct dentry *src_dentry, - struct inode *hdir, struct path *path, - struct inode **delegated_inode); -int vfsub_mkdir(struct inode *dir, struct path *path, int mode); -int vfsub_rmdir(struct inode *dir, struct path *path); - -/* ---------------------------------------------------------------------- */ - -ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, - loff_t *ppos); -ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, - loff_t *ppos); -ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, - loff_t *ppos); -ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, - loff_t *ppos); -int vfsub_flush(struct file *file, fl_owner_t id); -int vfsub_iterate_dir(struct file *file, struct dir_context *ctx); - -static inline loff_t vfsub_f_size_read(struct file *file) -{ - return i_size_read(file_inode(file)); -} - -static inline unsigned int vfsub_file_flags(struct file *file) -{ - unsigned int flags; - - spin_lock(&file->f_lock); - flags = file->f_flags; - spin_unlock(&file->f_lock); - - return flags; -} - -#if 0 /* reserved */ -static inline void vfsub_file_accessed(struct file *h_file) -{ - file_accessed(h_file); - vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); /*ignore*/ -} -#endif - -static inline void vfsub_touch_atime(struct vfsmount *h_mnt, - struct dentry *h_dentry) -{ - struct path h_path = { - .dentry = h_dentry, - .mnt = h_mnt - }; - touch_atime(&h_path); - vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/ -} - -static inline int vfsub_update_time(struct inode *h_inode, struct timespec *ts, - int flags) -{ - return generic_update_time(h_inode, ts, flags); - /* no vfsub_update_h_iattr() since we don't have struct path */ -} - -long vfsub_splice_to(struct file *in, loff_t *ppos, - struct pipe_inode_info *pipe, size_t len, - unsigned int flags); -long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, - loff_t *ppos, size_t len, unsigned int flags); - -static inline long vfsub_truncate(struct path *path, loff_t length) -{ - long err; - - lockdep_off(); - err = vfs_truncate(path, length); - lockdep_on(); - return err; -} - -int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, - struct file *h_file); -int vfsub_fsync(struct file *file, struct path *path, int datasync); - -/* ---------------------------------------------------------------------- */ - -static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin) -{ - loff_t err; - - lockdep_off(); - err = vfs_llseek(file, offset, origin); - lockdep_on(); - return err; -} - -/* ---------------------------------------------------------------------- */ - -int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode); -int vfsub_sio_rmdir(struct inode *dir, struct path *path); -int vfsub_sio_notify_change(struct path *path, struct iattr *ia, - struct inode **delegated_inode); -int vfsub_notify_change(struct path *path, struct iattr *ia, - struct inode **delegated_inode); -int vfsub_unlink(struct inode *dir, struct path *path, - struct inode **delegated_inode, int force); - -/* ---------------------------------------------------------------------- */ - -static inline int vfsub_setxattr(struct dentry *dentry, const char *name, - const void *value, size_t size, int flags) -{ - int err; - - lockdep_off(); - err = vfs_setxattr(dentry, name, value, size, flags); - lockdep_on(); - - return err; -} - -static inline int vfsub_removexattr(struct dentry *dentry, const char *name) -{ - int err; - - lockdep_off(); - err = vfs_removexattr(dentry, name); - lockdep_on(); - - return err; -} - -#endif /* __KERNEL__ */ -#endif /* __AUFS_VFSUB_H__ */ diff --git a/fs/aufs/wbr_policy.c b/fs/aufs/wbr_policy.c deleted file mode 100644 index 3949efb50..000000000 --- a/fs/aufs/wbr_policy.c +++ /dev/null @@ -1,765 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * policies for selecting one among multiple writable branches - */ - -#include <linux/statfs.h> -#include "aufs.h" - -/* subset of cpup_attr() */ -static noinline_for_stack -int au_cpdown_attr(struct path *h_path, struct dentry *h_src) -{ - int err, sbits; - struct iattr ia; - struct inode *h_isrc; - - h_isrc = d_inode(h_src); - ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID; - ia.ia_mode = h_isrc->i_mode; - ia.ia_uid = h_isrc->i_uid; - ia.ia_gid = h_isrc->i_gid; - sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID)); - au_cpup_attr_flags(d_inode(h_path->dentry), h_isrc->i_flags); - /* no delegation since it is just created */ - err = vfsub_sio_notify_change(h_path, &ia, /*delegated*/NULL); - - /* is this nfs only? */ - if (!err && sbits && au_test_nfs(h_path->dentry->d_sb)) { - ia.ia_valid = ATTR_FORCE | ATTR_MODE; - ia.ia_mode = h_isrc->i_mode; - err = vfsub_sio_notify_change(h_path, &ia, /*delegated*/NULL); - } - - return err; -} - -#define AuCpdown_PARENT_OPQ 1 -#define AuCpdown_WHED (1 << 1) -#define AuCpdown_MADE_DIR (1 << 2) -#define AuCpdown_DIROPQ (1 << 3) -#define au_ftest_cpdown(flags, name) ((flags) & AuCpdown_##name) -#define au_fset_cpdown(flags, name) \ - do { (flags) |= AuCpdown_##name; } while (0) -#define au_fclr_cpdown(flags, name) \ - do { (flags) &= ~AuCpdown_##name; } while (0) - -static int au_cpdown_dir_opq(struct dentry *dentry, aufs_bindex_t bdst, - unsigned int *flags) -{ - int err; - struct dentry *opq_dentry; - - opq_dentry = au_diropq_create(dentry, bdst); - err = PTR_ERR(opq_dentry); - if (IS_ERR(opq_dentry)) - goto out; - dput(opq_dentry); - au_fset_cpdown(*flags, DIROPQ); - -out: - return err; -} - -static int au_cpdown_dir_wh(struct dentry *dentry, struct dentry *h_parent, - struct inode *dir, aufs_bindex_t bdst) -{ - int err; - struct path h_path; - struct au_branch *br; - - br = au_sbr(dentry->d_sb, bdst); - h_path.dentry = au_wh_lkup(h_parent, &dentry->d_name, br); - err = PTR_ERR(h_path.dentry); - if (IS_ERR(h_path.dentry)) - goto out; - - err = 0; - if (d_is_positive(h_path.dentry)) { - h_path.mnt = au_br_mnt(br); - err = au_wh_unlink_dentry(au_h_iptr(dir, bdst), &h_path, - dentry); - } - dput(h_path.dentry); - -out: - return err; -} - -static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, - struct au_pin *pin, - struct dentry *h_parent, void *arg) -{ - int err, rerr; - aufs_bindex_t bopq, bstart; - struct path h_path; - struct dentry *parent; - struct inode *h_dir, *h_inode, *inode, *dir; - unsigned int *flags = arg; - - bstart = au_dbstart(dentry); - /* dentry is di-locked */ - parent = dget_parent(dentry); - dir = d_inode(parent); - h_dir = d_inode(h_parent); - AuDebugOn(h_dir != au_h_iptr(dir, bdst)); - IMustLock(h_dir); - - err = au_lkup_neg(dentry, bdst, /*wh*/0); - if (unlikely(err < 0)) - goto out; - h_path.dentry = au_h_dptr(dentry, bdst); - h_path.mnt = au_sbr_mnt(dentry->d_sb, bdst); - err = vfsub_sio_mkdir(au_h_iptr(dir, bdst), &h_path, - S_IRWXU | S_IRUGO | S_IXUGO); - if (unlikely(err)) - goto out_put; - au_fset_cpdown(*flags, MADE_DIR); - - bopq = au_dbdiropq(dentry); - au_fclr_cpdown(*flags, WHED); - au_fclr_cpdown(*flags, DIROPQ); - if (au_dbwh(dentry) == bdst) - au_fset_cpdown(*flags, WHED); - if (!au_ftest_cpdown(*flags, PARENT_OPQ) && bopq <= bdst) - au_fset_cpdown(*flags, PARENT_OPQ); - h_inode = d_inode(h_path.dentry); - mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); - if (au_ftest_cpdown(*flags, WHED)) { - err = au_cpdown_dir_opq(dentry, bdst, flags); - if (unlikely(err)) { - mutex_unlock(&h_inode->i_mutex); - goto out_dir; - } - } - - err = au_cpdown_attr(&h_path, au_h_dptr(dentry, bstart)); - mutex_unlock(&h_inode->i_mutex); - if (unlikely(err)) - goto out_opq; - - if (au_ftest_cpdown(*flags, WHED)) { - err = au_cpdown_dir_wh(dentry, h_parent, dir, bdst); - if (unlikely(err)) - goto out_opq; - } - - inode = d_inode(dentry); - if (au_ibend(inode) < bdst) - au_set_ibend(inode, bdst); - au_set_h_iptr(inode, bdst, au_igrab(h_inode), - au_hi_flags(inode, /*isdir*/1)); - au_fhsm_wrote(dentry->d_sb, bdst, /*force*/0); - goto out; /* success */ - - /* revert */ -out_opq: - if (au_ftest_cpdown(*flags, DIROPQ)) { - mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); - rerr = au_diropq_remove(dentry, bdst); - mutex_unlock(&h_inode->i_mutex); - if (unlikely(rerr)) { - AuIOErr("failed removing diropq for %pd b%d (%d)\n", - dentry, bdst, rerr); - err = -EIO; - goto out; - } - } -out_dir: - if (au_ftest_cpdown(*flags, MADE_DIR)) { - rerr = vfsub_sio_rmdir(au_h_iptr(dir, bdst), &h_path); - if (unlikely(rerr)) { - AuIOErr("failed removing %pd b%d (%d)\n", - dentry, bdst, rerr); - err = -EIO; - } - } -out_put: - au_set_h_dptr(dentry, bdst, NULL); - if (au_dbend(dentry) == bdst) - au_update_dbend(dentry); -out: - dput(parent); - return err; -} - -int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst) -{ - int err; - unsigned int flags; - - flags = 0; - err = au_cp_dirs(dentry, bdst, au_cpdown_dir, &flags); - - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* policies for create */ - -int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex) -{ - int err, i, j, ndentry; - aufs_bindex_t bopq; - struct au_dcsub_pages dpages; - struct au_dpage *dpage; - struct dentry **dentries, *parent, *d; - - err = au_dpages_init(&dpages, GFP_NOFS); - if (unlikely(err)) - goto out; - parent = dget_parent(dentry); - err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/0); - if (unlikely(err)) - goto out_free; - - err = bindex; - for (i = 0; i < dpages.ndpage; i++) { - dpage = dpages.dpages + i; - dentries = dpage->dentries; - ndentry = dpage->ndentry; - for (j = 0; j < ndentry; j++) { - d = dentries[j]; - di_read_lock_parent2(d, !AuLock_IR); - bopq = au_dbdiropq(d); - di_read_unlock(d, !AuLock_IR); - if (bopq >= 0 && bopq < err) - err = bopq; - } - } - -out_free: - dput(parent); - au_dpages_free(&dpages); -out: - return err; -} - -static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex) -{ - for (; bindex >= 0; bindex--) - if (!au_br_rdonly(au_sbr(sb, bindex))) - return bindex; - return -EROFS; -} - -/* top down parent */ -static int au_wbr_create_tdp(struct dentry *dentry, - unsigned int flags __maybe_unused) -{ - int err; - aufs_bindex_t bstart, bindex; - struct super_block *sb; - struct dentry *parent, *h_parent; - - sb = dentry->d_sb; - bstart = au_dbstart(dentry); - err = bstart; - if (!au_br_rdonly(au_sbr(sb, bstart))) - goto out; - - err = -EROFS; - parent = dget_parent(dentry); - for (bindex = au_dbstart(parent); bindex < bstart; bindex++) { - h_parent = au_h_dptr(parent, bindex); - if (!h_parent || d_is_negative(h_parent)) - continue; - - if (!au_br_rdonly(au_sbr(sb, bindex))) { - err = bindex; - break; - } - } - dput(parent); - - /* bottom up here */ - if (unlikely(err < 0)) { - err = au_wbr_bu(sb, bstart - 1); - if (err >= 0) - err = au_wbr_nonopq(dentry, err); - } - -out: - AuDbg("b%d\n", err); - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* an exception for the policy other than tdp */ -static int au_wbr_create_exp(struct dentry *dentry) -{ - int err; - aufs_bindex_t bwh, bdiropq; - struct dentry *parent; - - err = -1; - bwh = au_dbwh(dentry); - parent = dget_parent(dentry); - bdiropq = au_dbdiropq(parent); - if (bwh >= 0) { - if (bdiropq >= 0) - err = min(bdiropq, bwh); - else - err = bwh; - AuDbg("%d\n", err); - } else if (bdiropq >= 0) { - err = bdiropq; - AuDbg("%d\n", err); - } - dput(parent); - - if (err >= 0) - err = au_wbr_nonopq(dentry, err); - - if (err >= 0 && au_br_rdonly(au_sbr(dentry->d_sb, err))) - err = -1; - - AuDbg("%d\n", err); - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* round robin */ -static int au_wbr_create_init_rr(struct super_block *sb) -{ - int err; - - err = au_wbr_bu(sb, au_sbend(sb)); - atomic_set(&au_sbi(sb)->si_wbr_rr_next, -err); /* less important */ - /* smp_mb(); */ - - AuDbg("b%d\n", err); - return err; -} - -static int au_wbr_create_rr(struct dentry *dentry, unsigned int flags) -{ - int err, nbr; - unsigned int u; - aufs_bindex_t bindex, bend; - struct super_block *sb; - atomic_t *next; - - err = au_wbr_create_exp(dentry); - if (err >= 0) - goto out; - - sb = dentry->d_sb; - next = &au_sbi(sb)->si_wbr_rr_next; - bend = au_sbend(sb); - nbr = bend + 1; - for (bindex = 0; bindex <= bend; bindex++) { - if (!au_ftest_wbr(flags, DIR)) { - err = atomic_dec_return(next) + 1; - /* modulo for 0 is meaningless */ - if (unlikely(!err)) - err = atomic_dec_return(next) + 1; - } else - err = atomic_read(next); - AuDbg("%d\n", err); - u = err; - err = u % nbr; - AuDbg("%d\n", err); - if (!au_br_rdonly(au_sbr(sb, err))) - break; - err = -EROFS; - } - - if (err >= 0) - err = au_wbr_nonopq(dentry, err); - -out: - AuDbg("%d\n", err); - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* most free space */ -static void au_mfs(struct dentry *dentry, struct dentry *parent) -{ - struct super_block *sb; - struct au_branch *br; - struct au_wbr_mfs *mfs; - struct dentry *h_parent; - aufs_bindex_t bindex, bend; - int err; - unsigned long long b, bavail; - struct path h_path; - /* reduce the stack usage */ - struct kstatfs *st; - - st = kmalloc(sizeof(*st), GFP_NOFS); - if (unlikely(!st)) { - AuWarn1("failed updating mfs(%d), ignored\n", -ENOMEM); - return; - } - - bavail = 0; - sb = dentry->d_sb; - mfs = &au_sbi(sb)->si_wbr_mfs; - MtxMustLock(&mfs->mfs_lock); - mfs->mfs_bindex = -EROFS; - mfs->mfsrr_bytes = 0; - if (!parent) { - bindex = 0; - bend = au_sbend(sb); - } else { - bindex = au_dbstart(parent); - bend = au_dbtaildir(parent); - } - - for (; bindex <= bend; bindex++) { - if (parent) { - h_parent = au_h_dptr(parent, bindex); - if (!h_parent || d_is_negative(h_parent)) - continue; - } - br = au_sbr(sb, bindex); - if (au_br_rdonly(br)) - continue; - - /* sb->s_root for NFS is unreliable */ - h_path.mnt = au_br_mnt(br); - h_path.dentry = h_path.mnt->mnt_root; - err = vfs_statfs(&h_path, st); - if (unlikely(err)) { - AuWarn1("failed statfs, b%d, %d\n", bindex, err); - continue; - } - - /* when the available size is equal, select the lower one */ - BUILD_BUG_ON(sizeof(b) < sizeof(st->f_bavail) - || sizeof(b) < sizeof(st->f_bsize)); - b = st->f_bavail * st->f_bsize; - br->br_wbr->wbr_bytes = b; - if (b >= bavail) { - bavail = b; - mfs->mfs_bindex = bindex; - mfs->mfs_jiffy = jiffies; - } - } - - mfs->mfsrr_bytes = bavail; - AuDbg("b%d\n", mfs->mfs_bindex); - kfree(st); -} - -static int au_wbr_create_mfs(struct dentry *dentry, unsigned int flags) -{ - int err; - struct dentry *parent; - struct super_block *sb; - struct au_wbr_mfs *mfs; - - err = au_wbr_create_exp(dentry); - if (err >= 0) - goto out; - - sb = dentry->d_sb; - parent = NULL; - if (au_ftest_wbr(flags, PARENT)) - parent = dget_parent(dentry); - mfs = &au_sbi(sb)->si_wbr_mfs; - mutex_lock(&mfs->mfs_lock); - if (time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire) - || mfs->mfs_bindex < 0 - || au_br_rdonly(au_sbr(sb, mfs->mfs_bindex))) - au_mfs(dentry, parent); - mutex_unlock(&mfs->mfs_lock); - err = mfs->mfs_bindex; - dput(parent); - - if (err >= 0) - err = au_wbr_nonopq(dentry, err); - -out: - AuDbg("b%d\n", err); - return err; -} - -static int au_wbr_create_init_mfs(struct super_block *sb) -{ - struct au_wbr_mfs *mfs; - - mfs = &au_sbi(sb)->si_wbr_mfs; - mutex_init(&mfs->mfs_lock); - mfs->mfs_jiffy = 0; - mfs->mfs_bindex = -EROFS; - - return 0; -} - -static int au_wbr_create_fin_mfs(struct super_block *sb __maybe_unused) -{ - mutex_destroy(&au_sbi(sb)->si_wbr_mfs.mfs_lock); - return 0; -} - -/* ---------------------------------------------------------------------- */ - -/* most free space and then round robin */ -static int au_wbr_create_mfsrr(struct dentry *dentry, unsigned int flags) -{ - int err; - struct au_wbr_mfs *mfs; - - err = au_wbr_create_mfs(dentry, flags); - if (err >= 0) { - mfs = &au_sbi(dentry->d_sb)->si_wbr_mfs; - mutex_lock(&mfs->mfs_lock); - if (mfs->mfsrr_bytes < mfs->mfsrr_watermark) - err = au_wbr_create_rr(dentry, flags); - mutex_unlock(&mfs->mfs_lock); - } - - AuDbg("b%d\n", err); - return err; -} - -static int au_wbr_create_init_mfsrr(struct super_block *sb) -{ - int err; - - au_wbr_create_init_mfs(sb); /* ignore */ - err = au_wbr_create_init_rr(sb); - - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* top down parent and most free space */ -static int au_wbr_create_pmfs(struct dentry *dentry, unsigned int flags) -{ - int err, e2; - unsigned long long b; - aufs_bindex_t bindex, bstart, bend; - struct super_block *sb; - struct dentry *parent, *h_parent; - struct au_branch *br; - - err = au_wbr_create_tdp(dentry, flags); - if (unlikely(err < 0)) - goto out; - parent = dget_parent(dentry); - bstart = au_dbstart(parent); - bend = au_dbtaildir(parent); - if (bstart == bend) - goto out_parent; /* success */ - - e2 = au_wbr_create_mfs(dentry, flags); - if (e2 < 0) - goto out_parent; /* success */ - - /* when the available size is equal, select upper one */ - sb = dentry->d_sb; - br = au_sbr(sb, err); - b = br->br_wbr->wbr_bytes; - AuDbg("b%d, %llu\n", err, b); - - for (bindex = bstart; bindex <= bend; bindex++) { - h_parent = au_h_dptr(parent, bindex); - if (!h_parent || d_is_negative(h_parent)) - continue; - - br = au_sbr(sb, bindex); - if (!au_br_rdonly(br) && br->br_wbr->wbr_bytes > b) { - b = br->br_wbr->wbr_bytes; - err = bindex; - AuDbg("b%d, %llu\n", err, b); - } - } - - if (err >= 0) - err = au_wbr_nonopq(dentry, err); - -out_parent: - dput(parent); -out: - AuDbg("b%d\n", err); - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* - * - top down parent - * - most free space with parent - * - most free space round-robin regardless parent - */ -static int au_wbr_create_pmfsrr(struct dentry *dentry, unsigned int flags) -{ - int err; - unsigned long long watermark; - struct super_block *sb; - struct au_branch *br; - struct au_wbr_mfs *mfs; - - err = au_wbr_create_pmfs(dentry, flags | AuWbr_PARENT); - if (unlikely(err < 0)) - goto out; - - sb = dentry->d_sb; - br = au_sbr(sb, err); - mfs = &au_sbi(sb)->si_wbr_mfs; - mutex_lock(&mfs->mfs_lock); - watermark = mfs->mfsrr_watermark; - mutex_unlock(&mfs->mfs_lock); - if (br->br_wbr->wbr_bytes < watermark) - /* regardless the parent dir */ - err = au_wbr_create_mfsrr(dentry, flags); - -out: - AuDbg("b%d\n", err); - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* policies for copyup */ - -/* top down parent */ -static int au_wbr_copyup_tdp(struct dentry *dentry) -{ - return au_wbr_create_tdp(dentry, /*flags, anything is ok*/0); -} - -/* bottom up parent */ -static int au_wbr_copyup_bup(struct dentry *dentry) -{ - int err; - aufs_bindex_t bindex, bstart; - struct dentry *parent, *h_parent; - struct super_block *sb; - - err = -EROFS; - sb = dentry->d_sb; - parent = dget_parent(dentry); - bstart = au_dbstart(parent); - for (bindex = au_dbstart(dentry); bindex >= bstart; bindex--) { - h_parent = au_h_dptr(parent, bindex); - if (!h_parent || d_is_negative(h_parent)) - continue; - - if (!au_br_rdonly(au_sbr(sb, bindex))) { - err = bindex; - break; - } - } - dput(parent); - - /* bottom up here */ - if (unlikely(err < 0)) - err = au_wbr_bu(sb, bstart - 1); - - AuDbg("b%d\n", err); - return err; -} - -/* bottom up */ -int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t bstart) -{ - int err; - - err = au_wbr_bu(dentry->d_sb, bstart); - AuDbg("b%d\n", err); - if (err > bstart) - err = au_wbr_nonopq(dentry, err); - - AuDbg("b%d\n", err); - return err; -} - -static int au_wbr_copyup_bu(struct dentry *dentry) -{ - int err; - aufs_bindex_t bstart; - - bstart = au_dbstart(dentry); - err = au_wbr_do_copyup_bu(dentry, bstart); - return err; -} - -/* ---------------------------------------------------------------------- */ - -struct au_wbr_copyup_operations au_wbr_copyup_ops[] = { - [AuWbrCopyup_TDP] = { - .copyup = au_wbr_copyup_tdp - }, - [AuWbrCopyup_BUP] = { - .copyup = au_wbr_copyup_bup - }, - [AuWbrCopyup_BU] = { - .copyup = au_wbr_copyup_bu - } -}; - -struct au_wbr_create_operations au_wbr_create_ops[] = { - [AuWbrCreate_TDP] = { - .create = au_wbr_create_tdp - }, - [AuWbrCreate_RR] = { - .create = au_wbr_create_rr, - .init = au_wbr_create_init_rr - }, - [AuWbrCreate_MFS] = { - .create = au_wbr_create_mfs, - .init = au_wbr_create_init_mfs, - .fin = au_wbr_create_fin_mfs - }, - [AuWbrCreate_MFSV] = { - .create = au_wbr_create_mfs, - .init = au_wbr_create_init_mfs, - .fin = au_wbr_create_fin_mfs - }, - [AuWbrCreate_MFSRR] = { - .create = au_wbr_create_mfsrr, - .init = au_wbr_create_init_mfsrr, - .fin = au_wbr_create_fin_mfs - }, - [AuWbrCreate_MFSRRV] = { - .create = au_wbr_create_mfsrr, - .init = au_wbr_create_init_mfsrr, - .fin = au_wbr_create_fin_mfs - }, - [AuWbrCreate_PMFS] = { - .create = au_wbr_create_pmfs, - .init = au_wbr_create_init_mfs, - .fin = au_wbr_create_fin_mfs - }, - [AuWbrCreate_PMFSV] = { - .create = au_wbr_create_pmfs, - .init = au_wbr_create_init_mfs, - .fin = au_wbr_create_fin_mfs - }, - [AuWbrCreate_PMFSRR] = { - .create = au_wbr_create_pmfsrr, - .init = au_wbr_create_init_mfsrr, - .fin = au_wbr_create_fin_mfs - }, - [AuWbrCreate_PMFSRRV] = { - .create = au_wbr_create_pmfsrr, - .init = au_wbr_create_init_mfsrr, - .fin = au_wbr_create_fin_mfs - } -}; diff --git a/fs/aufs/whout.c b/fs/aufs/whout.c deleted file mode 100644 index c3fc2d4bd..000000000 --- a/fs/aufs/whout.c +++ /dev/null @@ -1,1063 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * whiteout for logical deletion and opaque directory - */ - -#include "aufs.h" - -#define WH_MASK S_IRUGO - -/* - * If a directory contains this file, then it is opaque. We start with the - * .wh. flag so that it is blocked by lookup. - */ -static struct qstr diropq_name = QSTR_INIT(AUFS_WH_DIROPQ, - sizeof(AUFS_WH_DIROPQ) - 1); - -/* - * generate whiteout name, which is NOT terminated by NULL. - * @name: original d_name.name - * @len: original d_name.len - * @wh: whiteout qstr - * returns zero when succeeds, otherwise error. - * succeeded value as wh->name should be freed by kfree(). - */ -int au_wh_name_alloc(struct qstr *wh, const struct qstr *name) -{ - char *p; - - if (unlikely(name->len > PATH_MAX - AUFS_WH_PFX_LEN)) - return -ENAMETOOLONG; - - wh->len = name->len + AUFS_WH_PFX_LEN; - p = kmalloc(wh->len, GFP_NOFS); - wh->name = p; - if (p) { - memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); - memcpy(p + AUFS_WH_PFX_LEN, name->name, name->len); - /* smp_mb(); */ - return 0; - } - return -ENOMEM; -} - -/* ---------------------------------------------------------------------- */ - -/* - * test if the @wh_name exists under @h_parent. - * @try_sio specifies the necessary of super-io. - */ -int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio) -{ - int err; - struct dentry *wh_dentry; - - if (!try_sio) - wh_dentry = vfsub_lkup_one(wh_name, h_parent); - else - wh_dentry = au_sio_lkup_one(wh_name, h_parent); - err = PTR_ERR(wh_dentry); - if (IS_ERR(wh_dentry)) { - if (err == -ENAMETOOLONG) - err = 0; - goto out; - } - - err = 0; - if (d_is_negative(wh_dentry)) - goto out_wh; /* success */ - - err = 1; - if (d_is_reg(wh_dentry)) - goto out_wh; /* success */ - - err = -EIO; - AuIOErr("%pd Invalid whiteout entry type 0%o.\n", - wh_dentry, d_inode(wh_dentry)->i_mode); - -out_wh: - dput(wh_dentry); -out: - return err; -} - -/* - * test if the @h_dentry sets opaque or not. - */ -int au_diropq_test(struct dentry *h_dentry) -{ - int err; - struct inode *h_dir; - - h_dir = d_inode(h_dentry); - err = au_wh_test(h_dentry, &diropq_name, - au_test_h_perm_sio(h_dir, MAY_EXEC)); - return err; -} - -/* - * returns a negative dentry whose name is unique and temporary. - */ -struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, - struct qstr *prefix) -{ - struct dentry *dentry; - int i; - char defname[NAME_MAX - AUFS_MAX_NAMELEN + DNAME_INLINE_LEN + 1], - *name, *p; - /* strict atomic_t is unnecessary here */ - static unsigned short cnt; - struct qstr qs; - - BUILD_BUG_ON(sizeof(cnt) * 2 > AUFS_WH_TMP_LEN); - - name = defname; - qs.len = sizeof(defname) - DNAME_INLINE_LEN + prefix->len - 1; - if (unlikely(prefix->len > DNAME_INLINE_LEN)) { - dentry = ERR_PTR(-ENAMETOOLONG); - if (unlikely(qs.len > NAME_MAX)) - goto out; - dentry = ERR_PTR(-ENOMEM); - name = kmalloc(qs.len + 1, GFP_NOFS); - if (unlikely(!name)) - goto out; - } - - /* doubly whiteout-ed */ - memcpy(name, AUFS_WH_PFX AUFS_WH_PFX, AUFS_WH_PFX_LEN * 2); - p = name + AUFS_WH_PFX_LEN * 2; - memcpy(p, prefix->name, prefix->len); - p += prefix->len; - *p++ = '.'; - AuDebugOn(name + qs.len + 1 - p <= AUFS_WH_TMP_LEN); - - qs.name = name; - for (i = 0; i < 3; i++) { - sprintf(p, "%.*x", AUFS_WH_TMP_LEN, cnt++); - dentry = au_sio_lkup_one(&qs, h_parent); - if (IS_ERR(dentry) || d_is_negative(dentry)) - goto out_name; - dput(dentry); - } - /* pr_warn("could not get random name\n"); */ - dentry = ERR_PTR(-EEXIST); - AuDbg("%.*s\n", AuLNPair(&qs)); - BUG(); - -out_name: - if (name != defname) - kfree(name); -out: - AuTraceErrPtr(dentry); - return dentry; -} - -/* - * rename the @h_dentry on @br to the whiteouted temporary name. - */ -int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br) -{ - int err; - struct path h_path = { - .mnt = au_br_mnt(br) - }; - struct inode *h_dir, *delegated; - struct dentry *h_parent; - - h_parent = h_dentry->d_parent; /* dir inode is locked */ - h_dir = d_inode(h_parent); - IMustLock(h_dir); - - h_path.dentry = au_whtmp_lkup(h_parent, br, &h_dentry->d_name); - err = PTR_ERR(h_path.dentry); - if (IS_ERR(h_path.dentry)) - goto out; - - /* under the same dir, no need to lock_rename() */ - delegated = NULL; - err = vfsub_rename(h_dir, h_dentry, h_dir, &h_path, &delegated); - AuTraceErr(err); - if (unlikely(err == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal rename\n"); - iput(delegated); - } - dput(h_path.dentry); - -out: - AuTraceErr(err); - return err; -} - -/* ---------------------------------------------------------------------- */ -/* - * functions for removing a whiteout - */ - -static int do_unlink_wh(struct inode *h_dir, struct path *h_path) -{ - int err, force; - struct inode *delegated; - - /* - * forces superio when the dir has a sticky bit. - * this may be a violation of unix fs semantics. - */ - force = (h_dir->i_mode & S_ISVTX) - && !uid_eq(current_fsuid(), d_inode(h_path->dentry)->i_uid); - delegated = NULL; - err = vfsub_unlink(h_dir, h_path, &delegated, force); - if (unlikely(err == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal unlink\n"); - iput(delegated); - } - return err; -} - -int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, - struct dentry *dentry) -{ - int err; - - err = do_unlink_wh(h_dir, h_path); - if (!err && dentry) - au_set_dbwh(dentry, -1); - - return err; -} - -static int unlink_wh_name(struct dentry *h_parent, struct qstr *wh, - struct au_branch *br) -{ - int err; - struct path h_path = { - .mnt = au_br_mnt(br) - }; - - err = 0; - h_path.dentry = vfsub_lkup_one(wh, h_parent); - if (IS_ERR(h_path.dentry)) - err = PTR_ERR(h_path.dentry); - else { - if (d_is_reg(h_path.dentry)) - err = do_unlink_wh(d_inode(h_parent), &h_path); - dput(h_path.dentry); - } - - return err; -} - -/* ---------------------------------------------------------------------- */ -/* - * initialize/clean whiteout for a branch - */ - -static void au_wh_clean(struct inode *h_dir, struct path *whpath, - const int isdir) -{ - int err; - struct inode *delegated; - - if (d_is_negative(whpath->dentry)) - return; - - if (isdir) - err = vfsub_rmdir(h_dir, whpath); - else { - delegated = NULL; - err = vfsub_unlink(h_dir, whpath, &delegated, /*force*/0); - if (unlikely(err == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal unlink\n"); - iput(delegated); - } - } - if (unlikely(err)) - pr_warn("failed removing %pd (%d), ignored.\n", - whpath->dentry, err); -} - -static int test_linkable(struct dentry *h_root) -{ - struct inode *h_dir = d_inode(h_root); - - if (h_dir->i_op->link) - return 0; - - pr_err("%pd (%s) doesn't support link(2), use noplink and rw+nolwh\n", - h_root, au_sbtype(h_root->d_sb)); - return -ENOSYS; -} - -/* todo: should this mkdir be done in /sbin/mount.aufs helper? */ -static int au_whdir(struct inode *h_dir, struct path *path) -{ - int err; - - err = -EEXIST; - if (d_is_negative(path->dentry)) { - int mode = S_IRWXU; - - if (au_test_nfs(path->dentry->d_sb)) - mode |= S_IXUGO; - err = vfsub_mkdir(h_dir, path, mode); - } else if (d_is_dir(path->dentry)) - err = 0; - else - pr_err("unknown %pd exists\n", path->dentry); - - return err; -} - -struct au_wh_base { - const struct qstr *name; - struct dentry *dentry; -}; - -static void au_wh_init_ro(struct inode *h_dir, struct au_wh_base base[], - struct path *h_path) -{ - h_path->dentry = base[AuBrWh_BASE].dentry; - au_wh_clean(h_dir, h_path, /*isdir*/0); - h_path->dentry = base[AuBrWh_PLINK].dentry; - au_wh_clean(h_dir, h_path, /*isdir*/1); - h_path->dentry = base[AuBrWh_ORPH].dentry; - au_wh_clean(h_dir, h_path, /*isdir*/1); -} - -/* - * returns tri-state, - * minus: error, caller should print the message - * zero: succuess - * plus: error, caller should NOT print the message - */ -static int au_wh_init_rw_nolink(struct dentry *h_root, struct au_wbr *wbr, - int do_plink, struct au_wh_base base[], - struct path *h_path) -{ - int err; - struct inode *h_dir; - - h_dir = d_inode(h_root); - h_path->dentry = base[AuBrWh_BASE].dentry; - au_wh_clean(h_dir, h_path, /*isdir*/0); - h_path->dentry = base[AuBrWh_PLINK].dentry; - if (do_plink) { - err = test_linkable(h_root); - if (unlikely(err)) { - err = 1; - goto out; - } - - err = au_whdir(h_dir, h_path); - if (unlikely(err)) - goto out; - wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); - } else - au_wh_clean(h_dir, h_path, /*isdir*/1); - h_path->dentry = base[AuBrWh_ORPH].dentry; - err = au_whdir(h_dir, h_path); - if (unlikely(err)) - goto out; - wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); - -out: - return err; -} - -/* - * for the moment, aufs supports the branch filesystem which does not support - * link(2). testing on FAT which does not support i_op->setattr() fully either, - * copyup failed. finally, such filesystem will not be used as the writable - * branch. - * - * returns tri-state, see above. - */ -static int au_wh_init_rw(struct dentry *h_root, struct au_wbr *wbr, - int do_plink, struct au_wh_base base[], - struct path *h_path) -{ - int err; - struct inode *h_dir; - - WbrWhMustWriteLock(wbr); - - err = test_linkable(h_root); - if (unlikely(err)) { - err = 1; - goto out; - } - - /* - * todo: should this create be done in /sbin/mount.aufs helper? - */ - err = -EEXIST; - h_dir = d_inode(h_root); - if (d_is_negative(base[AuBrWh_BASE].dentry)) { - h_path->dentry = base[AuBrWh_BASE].dentry; - err = vfsub_create(h_dir, h_path, WH_MASK, /*want_excl*/true); - } else if (d_is_reg(base[AuBrWh_BASE].dentry)) - err = 0; - else - pr_err("unknown %pd2 exists\n", base[AuBrWh_BASE].dentry); - if (unlikely(err)) - goto out; - - h_path->dentry = base[AuBrWh_PLINK].dentry; - if (do_plink) { - err = au_whdir(h_dir, h_path); - if (unlikely(err)) - goto out; - wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); - } else - au_wh_clean(h_dir, h_path, /*isdir*/1); - wbr->wbr_whbase = dget(base[AuBrWh_BASE].dentry); - - h_path->dentry = base[AuBrWh_ORPH].dentry; - err = au_whdir(h_dir, h_path); - if (unlikely(err)) - goto out; - wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); - -out: - return err; -} - -/* - * initialize the whiteout base file/dir for @br. - */ -int au_wh_init(struct au_branch *br, struct super_block *sb) -{ - int err, i; - const unsigned char do_plink - = !!au_opt_test(au_mntflags(sb), PLINK); - struct inode *h_dir; - struct path path = br->br_path; - struct dentry *h_root = path.dentry; - struct au_wbr *wbr = br->br_wbr; - static const struct qstr base_name[] = { - [AuBrWh_BASE] = QSTR_INIT(AUFS_BASE_NAME, - sizeof(AUFS_BASE_NAME) - 1), - [AuBrWh_PLINK] = QSTR_INIT(AUFS_PLINKDIR_NAME, - sizeof(AUFS_PLINKDIR_NAME) - 1), - [AuBrWh_ORPH] = QSTR_INIT(AUFS_ORPHDIR_NAME, - sizeof(AUFS_ORPHDIR_NAME) - 1) - }; - struct au_wh_base base[] = { - [AuBrWh_BASE] = { - .name = base_name + AuBrWh_BASE, - .dentry = NULL - }, - [AuBrWh_PLINK] = { - .name = base_name + AuBrWh_PLINK, - .dentry = NULL - }, - [AuBrWh_ORPH] = { - .name = base_name + AuBrWh_ORPH, - .dentry = NULL - } - }; - - if (wbr) - WbrWhMustWriteLock(wbr); - - for (i = 0; i < AuBrWh_Last; i++) { - /* doubly whiteouted */ - struct dentry *d; - - d = au_wh_lkup(h_root, (void *)base[i].name, br); - err = PTR_ERR(d); - if (IS_ERR(d)) - goto out; - - base[i].dentry = d; - AuDebugOn(wbr - && wbr->wbr_wh[i] - && wbr->wbr_wh[i] != base[i].dentry); - } - - if (wbr) - for (i = 0; i < AuBrWh_Last; i++) { - dput(wbr->wbr_wh[i]); - wbr->wbr_wh[i] = NULL; - } - - err = 0; - if (!au_br_writable(br->br_perm)) { - h_dir = d_inode(h_root); - au_wh_init_ro(h_dir, base, &path); - } else if (!au_br_wh_linkable(br->br_perm)) { - err = au_wh_init_rw_nolink(h_root, wbr, do_plink, base, &path); - if (err > 0) - goto out; - else if (err) - goto out_err; - } else { - err = au_wh_init_rw(h_root, wbr, do_plink, base, &path); - if (err > 0) - goto out; - else if (err) - goto out_err; - } - goto out; /* success */ - -out_err: - pr_err("an error(%d) on the writable branch %pd(%s)\n", - err, h_root, au_sbtype(h_root->d_sb)); -out: - for (i = 0; i < AuBrWh_Last; i++) - dput(base[i].dentry); - return err; -} - -/* ---------------------------------------------------------------------- */ -/* - * whiteouts are all hard-linked usually. - * when its link count reaches a ceiling, we create a new whiteout base - * asynchronously. - */ - -struct reinit_br_wh { - struct super_block *sb; - struct au_branch *br; -}; - -static void reinit_br_wh(void *arg) -{ - int err; - aufs_bindex_t bindex; - struct path h_path; - struct reinit_br_wh *a = arg; - struct au_wbr *wbr; - struct inode *dir, *delegated; - struct dentry *h_root; - struct au_hinode *hdir; - - err = 0; - wbr = a->br->br_wbr; - /* big aufs lock */ - si_noflush_write_lock(a->sb); - if (!au_br_writable(a->br->br_perm)) - goto out; - bindex = au_br_index(a->sb, a->br->br_id); - if (unlikely(bindex < 0)) - goto out; - - di_read_lock_parent(a->sb->s_root, AuLock_IR); - dir = d_inode(a->sb->s_root); - hdir = au_hi(dir, bindex); - h_root = au_h_dptr(a->sb->s_root, bindex); - AuDebugOn(h_root != au_br_dentry(a->br)); - - au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); - wbr_wh_write_lock(wbr); - err = au_h_verify(wbr->wbr_whbase, au_opt_udba(a->sb), hdir->hi_inode, - h_root, a->br); - if (!err) { - h_path.dentry = wbr->wbr_whbase; - h_path.mnt = au_br_mnt(a->br); - delegated = NULL; - err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, - /*force*/0); - if (unlikely(err == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal unlink\n"); - iput(delegated); - } - } else { - pr_warn("%pd is moved, ignored\n", wbr->wbr_whbase); - err = 0; - } - dput(wbr->wbr_whbase); - wbr->wbr_whbase = NULL; - if (!err) - err = au_wh_init(a->br, a->sb); - wbr_wh_write_unlock(wbr); - au_hn_imtx_unlock(hdir); - di_read_unlock(a->sb->s_root, AuLock_IR); - if (!err) - au_fhsm_wrote(a->sb, bindex, /*force*/0); - -out: - if (wbr) - atomic_dec(&wbr->wbr_wh_running); - atomic_dec(&a->br->br_count); - si_write_unlock(a->sb); - au_nwt_done(&au_sbi(a->sb)->si_nowait); - kfree(arg); - if (unlikely(err)) - AuIOErr("err %d\n", err); -} - -static void kick_reinit_br_wh(struct super_block *sb, struct au_branch *br) -{ - int do_dec, wkq_err; - struct reinit_br_wh *arg; - - do_dec = 1; - if (atomic_inc_return(&br->br_wbr->wbr_wh_running) != 1) - goto out; - - /* ignore ENOMEM */ - arg = kmalloc(sizeof(*arg), GFP_NOFS); - if (arg) { - /* - * dec(wh_running), kfree(arg) and dec(br_count) - * in reinit function - */ - arg->sb = sb; - arg->br = br; - atomic_inc(&br->br_count); - wkq_err = au_wkq_nowait(reinit_br_wh, arg, sb, /*flags*/0); - if (unlikely(wkq_err)) { - atomic_dec(&br->br_wbr->wbr_wh_running); - atomic_dec(&br->br_count); - kfree(arg); - } - do_dec = 0; - } - -out: - if (do_dec) - atomic_dec(&br->br_wbr->wbr_wh_running); -} - -/* ---------------------------------------------------------------------- */ - -/* - * create the whiteout @wh. - */ -static int link_or_create_wh(struct super_block *sb, aufs_bindex_t bindex, - struct dentry *wh) -{ - int err; - struct path h_path = { - .dentry = wh - }; - struct au_branch *br; - struct au_wbr *wbr; - struct dentry *h_parent; - struct inode *h_dir, *delegated; - - h_parent = wh->d_parent; /* dir inode is locked */ - h_dir = d_inode(h_parent); - IMustLock(h_dir); - - br = au_sbr(sb, bindex); - h_path.mnt = au_br_mnt(br); - wbr = br->br_wbr; - wbr_wh_read_lock(wbr); - if (wbr->wbr_whbase) { - delegated = NULL; - err = vfsub_link(wbr->wbr_whbase, h_dir, &h_path, &delegated); - if (unlikely(err == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal link\n"); - iput(delegated); - } - if (!err || err != -EMLINK) - goto out; - - /* link count full. re-initialize br_whbase. */ - kick_reinit_br_wh(sb, br); - } - - /* return this error in this context */ - err = vfsub_create(h_dir, &h_path, WH_MASK, /*want_excl*/true); - if (!err) - au_fhsm_wrote(sb, bindex, /*force*/0); - -out: - wbr_wh_read_unlock(wbr); - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* - * create or remove the diropq. - */ -static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex, - unsigned int flags) -{ - struct dentry *opq_dentry, *h_dentry; - struct super_block *sb; - struct au_branch *br; - int err; - - sb = dentry->d_sb; - br = au_sbr(sb, bindex); - h_dentry = au_h_dptr(dentry, bindex); - opq_dentry = vfsub_lkup_one(&diropq_name, h_dentry); - if (IS_ERR(opq_dentry)) - goto out; - - if (au_ftest_diropq(flags, CREATE)) { - err = link_or_create_wh(sb, bindex, opq_dentry); - if (!err) { - au_set_dbdiropq(dentry, bindex); - goto out; /* success */ - } - } else { - struct path tmp = { - .dentry = opq_dentry, - .mnt = au_br_mnt(br) - }; - err = do_unlink_wh(au_h_iptr(d_inode(dentry), bindex), &tmp); - if (!err) - au_set_dbdiropq(dentry, -1); - } - dput(opq_dentry); - opq_dentry = ERR_PTR(err); - -out: - return opq_dentry; -} - -struct do_diropq_args { - struct dentry **errp; - struct dentry *dentry; - aufs_bindex_t bindex; - unsigned int flags; -}; - -static void call_do_diropq(void *args) -{ - struct do_diropq_args *a = args; - *a->errp = do_diropq(a->dentry, a->bindex, a->flags); -} - -struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, - unsigned int flags) -{ - struct dentry *diropq, *h_dentry; - - h_dentry = au_h_dptr(dentry, bindex); - if (!au_test_h_perm_sio(d_inode(h_dentry), MAY_EXEC | MAY_WRITE)) - diropq = do_diropq(dentry, bindex, flags); - else { - int wkq_err; - struct do_diropq_args args = { - .errp = &diropq, - .dentry = dentry, - .bindex = bindex, - .flags = flags - }; - - wkq_err = au_wkq_wait(call_do_diropq, &args); - if (unlikely(wkq_err)) - diropq = ERR_PTR(wkq_err); - } - - return diropq; -} - -/* ---------------------------------------------------------------------- */ - -/* - * lookup whiteout dentry. - * @h_parent: lower parent dentry which must exist and be locked - * @base_name: name of dentry which will be whiteouted - * returns dentry for whiteout. - */ -struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, - struct au_branch *br) -{ - int err; - struct qstr wh_name; - struct dentry *wh_dentry; - - err = au_wh_name_alloc(&wh_name, base_name); - wh_dentry = ERR_PTR(err); - if (!err) { - wh_dentry = vfsub_lkup_one(&wh_name, h_parent); - kfree(wh_name.name); - } - return wh_dentry; -} - -/* - * link/create a whiteout for @dentry on @bindex. - */ -struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, - struct dentry *h_parent) -{ - struct dentry *wh_dentry; - struct super_block *sb; - int err; - - sb = dentry->d_sb; - wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, au_sbr(sb, bindex)); - if (!IS_ERR(wh_dentry) && d_is_negative(wh_dentry)) { - err = link_or_create_wh(sb, bindex, wh_dentry); - if (!err) { - au_set_dbwh(dentry, bindex); - au_fhsm_wrote(sb, bindex, /*force*/0); - } else { - dput(wh_dentry); - wh_dentry = ERR_PTR(err); - } - } - - return wh_dentry; -} - -/* ---------------------------------------------------------------------- */ - -/* Delete all whiteouts in this directory on branch bindex. */ -static int del_wh_children(struct dentry *h_dentry, struct au_nhash *whlist, - aufs_bindex_t bindex, struct au_branch *br) -{ - int err; - unsigned long ul, n; - struct qstr wh_name; - char *p; - struct hlist_head *head; - struct au_vdir_wh *pos; - struct au_vdir_destr *str; - - err = -ENOMEM; - p = (void *)__get_free_page(GFP_NOFS); - wh_name.name = p; - if (unlikely(!wh_name.name)) - goto out; - - err = 0; - memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); - p += AUFS_WH_PFX_LEN; - n = whlist->nh_num; - head = whlist->nh_head; - for (ul = 0; !err && ul < n; ul++, head++) { - hlist_for_each_entry(pos, head, wh_hash) { - if (pos->wh_bindex != bindex) - continue; - - str = &pos->wh_str; - if (str->len + AUFS_WH_PFX_LEN <= PATH_MAX) { - memcpy(p, str->name, str->len); - wh_name.len = AUFS_WH_PFX_LEN + str->len; - err = unlink_wh_name(h_dentry, &wh_name, br); - if (!err) - continue; - break; - } - AuIOErr("whiteout name too long %.*s\n", - str->len, str->name); - err = -EIO; - break; - } - } - free_page((unsigned long)wh_name.name); - -out: - return err; -} - -struct del_wh_children_args { - int *errp; - struct dentry *h_dentry; - struct au_nhash *whlist; - aufs_bindex_t bindex; - struct au_branch *br; -}; - -static void call_del_wh_children(void *args) -{ - struct del_wh_children_args *a = args; - *a->errp = del_wh_children(a->h_dentry, a->whlist, a->bindex, a->br); -} - -/* ---------------------------------------------------------------------- */ - -struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp) -{ - struct au_whtmp_rmdir *whtmp; - int err; - unsigned int rdhash; - - SiMustAnyLock(sb); - - whtmp = kmalloc(sizeof(*whtmp), gfp); - if (unlikely(!whtmp)) { - whtmp = ERR_PTR(-ENOMEM); - goto out; - } - - whtmp->dir = NULL; - whtmp->br = NULL; - whtmp->wh_dentry = NULL; - /* no estimation for dir size */ - rdhash = au_sbi(sb)->si_rdhash; - if (!rdhash) - rdhash = AUFS_RDHASH_DEF; - err = au_nhash_alloc(&whtmp->whlist, rdhash, gfp); - if (unlikely(err)) { - kfree(whtmp); - whtmp = ERR_PTR(err); - } - -out: - return whtmp; -} - -void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp) -{ - if (whtmp->br) - atomic_dec(&whtmp->br->br_count); - dput(whtmp->wh_dentry); - iput(whtmp->dir); - au_nhash_wh_free(&whtmp->whlist); - kfree(whtmp); -} - -/* - * rmdir the whiteouted temporary named dir @h_dentry. - * @whlist: whiteouted children. - */ -int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, - struct dentry *wh_dentry, struct au_nhash *whlist) -{ - int err; - unsigned int h_nlink; - struct path h_tmp; - struct inode *wh_inode, *h_dir; - struct au_branch *br; - - h_dir = d_inode(wh_dentry->d_parent); /* dir inode is locked */ - IMustLock(h_dir); - - br = au_sbr(dir->i_sb, bindex); - wh_inode = d_inode(wh_dentry); - mutex_lock_nested(&wh_inode->i_mutex, AuLsc_I_CHILD); - - /* - * someone else might change some whiteouts while we were sleeping. - * it means this whlist may have an obsoleted entry. - */ - if (!au_test_h_perm_sio(wh_inode, MAY_EXEC | MAY_WRITE)) - err = del_wh_children(wh_dentry, whlist, bindex, br); - else { - int wkq_err; - struct del_wh_children_args args = { - .errp = &err, - .h_dentry = wh_dentry, - .whlist = whlist, - .bindex = bindex, - .br = br - }; - - wkq_err = au_wkq_wait(call_del_wh_children, &args); - if (unlikely(wkq_err)) - err = wkq_err; - } - mutex_unlock(&wh_inode->i_mutex); - - if (!err) { - h_tmp.dentry = wh_dentry; - h_tmp.mnt = au_br_mnt(br); - h_nlink = h_dir->i_nlink; - err = vfsub_rmdir(h_dir, &h_tmp); - /* some fs doesn't change the parent nlink in some cases */ - h_nlink -= h_dir->i_nlink; - } - - if (!err) { - if (au_ibstart(dir) == bindex) { - /* todo: dir->i_mutex is necessary */ - au_cpup_attr_timesizes(dir); - if (h_nlink) - vfsub_drop_nlink(dir); - } - return 0; /* success */ - } - - pr_warn("failed removing %pd(%d), ignored\n", wh_dentry, err); - return err; -} - -static void call_rmdir_whtmp(void *args) -{ - int err; - aufs_bindex_t bindex; - struct au_whtmp_rmdir *a = args; - struct super_block *sb; - struct dentry *h_parent; - struct inode *h_dir; - struct au_hinode *hdir; - - /* rmdir by nfsd may cause deadlock with this i_mutex */ - /* mutex_lock(&a->dir->i_mutex); */ - err = -EROFS; - sb = a->dir->i_sb; - si_read_lock(sb, !AuLock_FLUSH); - if (!au_br_writable(a->br->br_perm)) - goto out; - bindex = au_br_index(sb, a->br->br_id); - if (unlikely(bindex < 0)) - goto out; - - err = -EIO; - ii_write_lock_parent(a->dir); - h_parent = dget_parent(a->wh_dentry); - h_dir = d_inode(h_parent); - hdir = au_hi(a->dir, bindex); - err = vfsub_mnt_want_write(au_br_mnt(a->br)); - if (unlikely(err)) - goto out_mnt; - au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); - err = au_h_verify(a->wh_dentry, au_opt_udba(sb), h_dir, h_parent, - a->br); - if (!err) - err = au_whtmp_rmdir(a->dir, bindex, a->wh_dentry, &a->whlist); - au_hn_imtx_unlock(hdir); - vfsub_mnt_drop_write(au_br_mnt(a->br)); - -out_mnt: - dput(h_parent); - ii_write_unlock(a->dir); -out: - /* mutex_unlock(&a->dir->i_mutex); */ - au_whtmp_rmdir_free(a); - si_read_unlock(sb); - au_nwt_done(&au_sbi(sb)->si_nowait); - if (unlikely(err)) - AuIOErr("err %d\n", err); -} - -void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, - struct dentry *wh_dentry, struct au_whtmp_rmdir *args) -{ - int wkq_err; - struct super_block *sb; - - IMustLock(dir); - - /* all post-process will be done in do_rmdir_whtmp(). */ - sb = dir->i_sb; - args->dir = au_igrab(dir); - args->br = au_sbr(sb, bindex); - atomic_inc(&args->br->br_count); - args->wh_dentry = dget(wh_dentry); - wkq_err = au_wkq_nowait(call_rmdir_whtmp, args, sb, /*flags*/0); - if (unlikely(wkq_err)) { - pr_warn("rmdir error %pd (%d), ignored\n", wh_dentry, wkq_err); - au_whtmp_rmdir_free(args); - } -} diff --git a/fs/aufs/whout.h b/fs/aufs/whout.h deleted file mode 100644 index 81652ce2f..000000000 --- a/fs/aufs/whout.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * whiteout for logical deletion and opaque directory - */ - -#ifndef __AUFS_WHOUT_H__ -#define __AUFS_WHOUT_H__ - -#ifdef __KERNEL__ - -#include "dir.h" - -/* whout.c */ -int au_wh_name_alloc(struct qstr *wh, const struct qstr *name); -int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio); -int au_diropq_test(struct dentry *h_dentry); -struct au_branch; -struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, - struct qstr *prefix); -int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br); -int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, - struct dentry *dentry); -int au_wh_init(struct au_branch *br, struct super_block *sb); - -/* diropq flags */ -#define AuDiropq_CREATE 1 -#define au_ftest_diropq(flags, name) ((flags) & AuDiropq_##name) -#define au_fset_diropq(flags, name) \ - do { (flags) |= AuDiropq_##name; } while (0) -#define au_fclr_diropq(flags, name) \ - do { (flags) &= ~AuDiropq_##name; } while (0) - -struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, - unsigned int flags); -struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, - struct au_branch *br); -struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, - struct dentry *h_parent); - -/* real rmdir for the whiteout-ed dir */ -struct au_whtmp_rmdir { - struct inode *dir; - struct au_branch *br; - struct dentry *wh_dentry; - struct au_nhash whlist; -}; - -struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp); -void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp); -int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, - struct dentry *wh_dentry, struct au_nhash *whlist); -void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, - struct dentry *wh_dentry, struct au_whtmp_rmdir *args); - -/* ---------------------------------------------------------------------- */ - -static inline struct dentry *au_diropq_create(struct dentry *dentry, - aufs_bindex_t bindex) -{ - return au_diropq_sio(dentry, bindex, AuDiropq_CREATE); -} - -static inline int au_diropq_remove(struct dentry *dentry, aufs_bindex_t bindex) -{ - return PTR_ERR(au_diropq_sio(dentry, bindex, !AuDiropq_CREATE)); -} - -#endif /* __KERNEL__ */ -#endif /* __AUFS_WHOUT_H__ */ diff --git a/fs/aufs/wkq.c b/fs/aufs/wkq.c deleted file mode 100644 index 3ffedae93..000000000 --- a/fs/aufs/wkq.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * workqueue for asynchronous/super-io operations - * todo: try new dredential scheme - */ - -#include <linux/module.h> -#include "aufs.h" - -/* internal workqueue named AUFS_WKQ_NAME */ - -static struct workqueue_struct *au_wkq; - -struct au_wkinfo { - struct work_struct wk; - struct kobject *kobj; - - unsigned int flags; /* see wkq.h */ - - au_wkq_func_t func; - void *args; - - struct completion *comp; -}; - -/* ---------------------------------------------------------------------- */ - -static void wkq_func(struct work_struct *wk) -{ - struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk); - - AuDebugOn(!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)); - AuDebugOn(rlimit(RLIMIT_FSIZE) != RLIM_INFINITY); - - wkinfo->func(wkinfo->args); - if (au_ftest_wkq(wkinfo->flags, WAIT)) - complete(wkinfo->comp); - else { - kobject_put(wkinfo->kobj); - module_put(THIS_MODULE); /* todo: ?? */ - kfree(wkinfo); - } -} - -/* - * Since struct completion is large, try allocating it dynamically. - */ -#if 1 /* defined(CONFIG_4KSTACKS) || defined(AuTest4KSTACKS) */ -#define AuWkqCompDeclare(name) struct completion *comp = NULL - -static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp) -{ - *comp = kmalloc(sizeof(**comp), GFP_NOFS); - if (*comp) { - init_completion(*comp); - wkinfo->comp = *comp; - return 0; - } - return -ENOMEM; -} - -static void au_wkq_comp_free(struct completion *comp) -{ - kfree(comp); -} - -#else - -/* no braces */ -#define AuWkqCompDeclare(name) \ - DECLARE_COMPLETION_ONSTACK(_ ## name); \ - struct completion *comp = &_ ## name - -static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp) -{ - wkinfo->comp = *comp; - return 0; -} - -static void au_wkq_comp_free(struct completion *comp __maybe_unused) -{ - /* empty */ -} -#endif /* 4KSTACKS */ - -static void au_wkq_run(struct au_wkinfo *wkinfo) -{ - if (au_ftest_wkq(wkinfo->flags, NEST)) { - if (au_wkq_test()) { - AuWarn1("wkq from wkq, unless silly-rename on NFS," - " due to a dead dir by UDBA?\n"); - AuDebugOn(au_ftest_wkq(wkinfo->flags, WAIT)); - } - } else - au_dbg_verify_kthread(); - - if (au_ftest_wkq(wkinfo->flags, WAIT)) { - INIT_WORK_ONSTACK(&wkinfo->wk, wkq_func); - queue_work(au_wkq, &wkinfo->wk); - } else { - INIT_WORK(&wkinfo->wk, wkq_func); - schedule_work(&wkinfo->wk); - } -} - -/* - * Be careful. It is easy to make deadlock happen. - * processA: lock, wkq and wait - * processB: wkq and wait, lock in wkq - * --> deadlock - */ -int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args) -{ - int err; - AuWkqCompDeclare(comp); - struct au_wkinfo wkinfo = { - .flags = flags, - .func = func, - .args = args - }; - - err = au_wkq_comp_alloc(&wkinfo, &comp); - if (!err) { - au_wkq_run(&wkinfo); - /* no timeout, no interrupt */ - wait_for_completion(wkinfo.comp); - au_wkq_comp_free(comp); - destroy_work_on_stack(&wkinfo.wk); - } - - return err; - -} - -/* - * Note: dget/dput() in func for aufs dentries are not supported. It will be a - * problem in a concurrent umounting. - */ -int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb, - unsigned int flags) -{ - int err; - struct au_wkinfo *wkinfo; - - atomic_inc(&au_sbi(sb)->si_nowait.nw_len); - - /* - * wkq_func() must free this wkinfo. - * it highly depends upon the implementation of workqueue. - */ - err = 0; - wkinfo = kmalloc(sizeof(*wkinfo), GFP_NOFS); - if (wkinfo) { - wkinfo->kobj = &au_sbi(sb)->si_kobj; - wkinfo->flags = flags & ~AuWkq_WAIT; - wkinfo->func = func; - wkinfo->args = args; - wkinfo->comp = NULL; - kobject_get(wkinfo->kobj); - __module_get(THIS_MODULE); /* todo: ?? */ - - au_wkq_run(wkinfo); - } else { - err = -ENOMEM; - au_nwt_done(&au_sbi(sb)->si_nowait); - } - - return err; -} - -/* ---------------------------------------------------------------------- */ - -void au_nwt_init(struct au_nowait_tasks *nwt) -{ - atomic_set(&nwt->nw_len, 0); - /* smp_mb(); */ /* atomic_set */ - init_waitqueue_head(&nwt->nw_wq); -} - -void au_wkq_fin(void) -{ - destroy_workqueue(au_wkq); -} - -int __init au_wkq_init(void) -{ - int err; - - err = 0; - au_wkq = alloc_workqueue(AUFS_WKQ_NAME, 0, WQ_DFL_ACTIVE); - if (IS_ERR(au_wkq)) - err = PTR_ERR(au_wkq); - else if (!au_wkq) - err = -ENOMEM; - - return err; -} diff --git a/fs/aufs/wkq.h b/fs/aufs/wkq.h deleted file mode 100644 index e57bfb3d9..000000000 --- a/fs/aufs/wkq.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * workqueue for asynchronous/super-io operations - * todo: try new credentials management scheme - */ - -#ifndef __AUFS_WKQ_H__ -#define __AUFS_WKQ_H__ - -#ifdef __KERNEL__ - -struct super_block; - -/* ---------------------------------------------------------------------- */ - -/* - * in the next operation, wait for the 'nowait' tasks in system-wide workqueue - */ -struct au_nowait_tasks { - atomic_t nw_len; - wait_queue_head_t nw_wq; -}; - -/* ---------------------------------------------------------------------- */ - -typedef void (*au_wkq_func_t)(void *args); - -/* wkq flags */ -#define AuWkq_WAIT 1 -#define AuWkq_NEST (1 << 1) -#define au_ftest_wkq(flags, name) ((flags) & AuWkq_##name) -#define au_fset_wkq(flags, name) \ - do { (flags) |= AuWkq_##name; } while (0) -#define au_fclr_wkq(flags, name) \ - do { (flags) &= ~AuWkq_##name; } while (0) - -#ifndef CONFIG_AUFS_HNOTIFY -#undef AuWkq_NEST -#define AuWkq_NEST 0 -#endif - -/* wkq.c */ -int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args); -int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb, - unsigned int flags); -void au_nwt_init(struct au_nowait_tasks *nwt); -int __init au_wkq_init(void); -void au_wkq_fin(void); - -/* ---------------------------------------------------------------------- */ - -static inline int au_wkq_test(void) -{ - return current->flags & PF_WQ_WORKER; -} - -static inline int au_wkq_wait(au_wkq_func_t func, void *args) -{ - return au_wkq_do_wait(AuWkq_WAIT, func, args); -} - -static inline void au_nwt_done(struct au_nowait_tasks *nwt) -{ - if (atomic_dec_and_test(&nwt->nw_len)) - wake_up_all(&nwt->nw_wq); -} - -static inline int au_nwt_flush(struct au_nowait_tasks *nwt) -{ - wait_event(nwt->nw_wq, !atomic_read(&nwt->nw_len)); - return 0; -} - -#endif /* __KERNEL__ */ -#endif /* __AUFS_WKQ_H__ */ diff --git a/fs/aufs/xattr.c b/fs/aufs/xattr.c deleted file mode 100644 index b7e13ca3e..000000000 --- a/fs/aufs/xattr.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Copyright (C) 2014-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * handling xattr functions - */ - -#include <linux/xattr.h> -#include "aufs.h" - -static int au_xattr_ignore(int err, char *name, unsigned int ignore_flags) -{ - if (!ignore_flags) - goto out; - switch (err) { - case -ENOMEM: - case -EDQUOT: - goto out; - } - - if ((ignore_flags & AuBrAttr_ICEX) == AuBrAttr_ICEX) { - err = 0; - goto out; - } - -#define cmp(brattr, prefix) do { \ - if (!strncmp(name, XATTR_##prefix##_PREFIX, \ - XATTR_##prefix##_PREFIX_LEN)) { \ - if (ignore_flags & AuBrAttr_ICEX_##brattr) \ - err = 0; \ - goto out; \ - } \ - } while (0) - - cmp(SEC, SECURITY); - cmp(SYS, SYSTEM); - cmp(TR, TRUSTED); - cmp(USR, USER); -#undef cmp - - if (ignore_flags & AuBrAttr_ICEX_OTH) - err = 0; - -out: - return err; -} - -static const int au_xattr_out_of_list = AuBrAttr_ICEX_OTH << 1; - -static int au_do_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, - char *name, char **buf, unsigned int ignore_flags, - unsigned int verbose) -{ - int err; - ssize_t ssz; - struct inode *h_idst; - - ssz = vfs_getxattr_alloc(h_src, name, buf, 0, GFP_NOFS); - err = ssz; - if (unlikely(err <= 0)) { - if (err == -ENODATA - || (err == -EOPNOTSUPP - && ((ignore_flags & au_xattr_out_of_list) - || (au_test_nfs_noacl(d_inode(h_src)) - && (!strcmp(name, XATTR_NAME_POSIX_ACL_ACCESS) - || !strcmp(name, - XATTR_NAME_POSIX_ACL_DEFAULT)))) - )) - err = 0; - if (err && (verbose || au_debug_test())) - pr_err("%s, err %d\n", name, err); - goto out; - } - - /* unlock it temporary */ - h_idst = d_inode(h_dst); - mutex_unlock(&h_idst->i_mutex); - err = vfsub_setxattr(h_dst, name, *buf, ssz, /*flags*/0); - mutex_lock_nested(&h_idst->i_mutex, AuLsc_I_CHILD2); - if (unlikely(err)) { - if (verbose || au_debug_test()) - pr_err("%s, err %d\n", name, err); - err = au_xattr_ignore(err, name, ignore_flags); - } - -out: - return err; -} - -int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags, - unsigned int verbose) -{ - int err, unlocked, acl_access, acl_default; - ssize_t ssz; - struct inode *h_isrc, *h_idst; - char *value, *p, *o, *e; - - /* try stopping to update the source inode while we are referencing */ - /* there should not be the parent-child relationship between them */ - h_isrc = d_inode(h_src); - h_idst = d_inode(h_dst); - mutex_unlock(&h_idst->i_mutex); - mutex_lock_nested(&h_isrc->i_mutex, AuLsc_I_CHILD); - mutex_lock_nested(&h_idst->i_mutex, AuLsc_I_CHILD2); - unlocked = 0; - - /* some filesystems don't list POSIX ACL, for example tmpfs */ - ssz = vfs_listxattr(h_src, NULL, 0); - err = ssz; - if (unlikely(err < 0)) { - AuTraceErr(err); - if (err == -ENODATA - || err == -EOPNOTSUPP) - err = 0; /* ignore */ - goto out; - } - - err = 0; - p = NULL; - o = NULL; - if (ssz) { - err = -ENOMEM; - p = kmalloc(ssz, GFP_NOFS); - o = p; - if (unlikely(!p)) - goto out; - err = vfs_listxattr(h_src, p, ssz); - } - mutex_unlock(&h_isrc->i_mutex); - unlocked = 1; - AuDbg("err %d, ssz %zd\n", err, ssz); - if (unlikely(err < 0)) - goto out_free; - - err = 0; - e = p + ssz; - value = NULL; - acl_access = 0; - acl_default = 0; - while (!err && p < e) { - acl_access |= !strncmp(p, XATTR_NAME_POSIX_ACL_ACCESS, - sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1); - acl_default |= !strncmp(p, XATTR_NAME_POSIX_ACL_DEFAULT, - sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - - 1); - err = au_do_cpup_xattr(h_dst, h_src, p, &value, ignore_flags, - verbose); - p += strlen(p) + 1; - } - AuTraceErr(err); - ignore_flags |= au_xattr_out_of_list; - if (!err && !acl_access) { - err = au_do_cpup_xattr(h_dst, h_src, - XATTR_NAME_POSIX_ACL_ACCESS, &value, - ignore_flags, verbose); - AuTraceErr(err); - } - if (!err && !acl_default) { - err = au_do_cpup_xattr(h_dst, h_src, - XATTR_NAME_POSIX_ACL_DEFAULT, &value, - ignore_flags, verbose); - AuTraceErr(err); - } - - kfree(value); - -out_free: - kfree(o); -out: - if (!unlocked) - mutex_unlock(&h_isrc->i_mutex); - AuTraceErr(err); - return err; -} - -/* ---------------------------------------------------------------------- */ - -enum { - AU_XATTR_LIST, - AU_XATTR_GET -}; - -struct au_lgxattr { - int type; - union { - struct { - char *list; - size_t size; - } list; - struct { - const char *name; - void *value; - size_t size; - } get; - } u; -}; - -static ssize_t au_lgxattr(struct dentry *dentry, struct au_lgxattr *arg) -{ - ssize_t err; - struct path h_path; - struct super_block *sb; - - sb = dentry->d_sb; - err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); - if (unlikely(err)) - goto out; - err = au_h_path_getattr(dentry, /*force*/1, &h_path); - if (unlikely(err)) - goto out_si; - if (unlikely(!h_path.dentry)) - /* illegally overlapped or something */ - goto out_di; /* pretending success */ - - /* always topmost entry only */ - switch (arg->type) { - case AU_XATTR_LIST: - err = vfs_listxattr(h_path.dentry, - arg->u.list.list, arg->u.list.size); - break; - case AU_XATTR_GET: - err = vfs_getxattr(h_path.dentry, - arg->u.get.name, arg->u.get.value, - arg->u.get.size); - break; - } - -out_di: - di_read_unlock(dentry, AuLock_IR); -out_si: - si_read_unlock(sb); -out: - AuTraceErr(err); - return err; -} - -ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size) -{ - struct au_lgxattr arg = { - .type = AU_XATTR_LIST, - .u.list = { - .list = list, - .size = size - }, - }; - - return au_lgxattr(dentry, &arg); -} - -ssize_t aufs_getxattr(struct dentry *dentry, const char *name, void *value, - size_t size) -{ - struct au_lgxattr arg = { - .type = AU_XATTR_GET, - .u.get = { - .name = name, - .value = value, - .size = size - }, - }; - - return au_lgxattr(dentry, &arg); -} - -int aufs_setxattr(struct dentry *dentry, const char *name, const void *value, - size_t size, int flags) -{ - struct au_srxattr arg = { - .type = AU_XATTR_SET, - .u.set = { - .name = name, - .value = value, - .size = size, - .flags = flags - }, - }; - - return au_srxattr(dentry, &arg); -} - -int aufs_removexattr(struct dentry *dentry, const char *name) -{ - struct au_srxattr arg = { - .type = AU_XATTR_REMOVE, - .u.remove = { - .name = name - }, - }; - - return au_srxattr(dentry, &arg); -} - -/* ---------------------------------------------------------------------- */ - -#if 0 -static size_t au_xattr_list(struct dentry *dentry, char *list, size_t list_size, - const char *name, size_t name_len, int type) -{ - return aufs_listxattr(dentry, list, list_size); -} - -static int au_xattr_get(struct dentry *dentry, const char *name, void *buffer, - size_t size, int type) -{ - return aufs_getxattr(dentry, name, buffer, size); -} - -static int au_xattr_set(struct dentry *dentry, const char *name, - const void *value, size_t size, int flags, int type) -{ - return aufs_setxattr(dentry, name, value, size, flags); -} - -static const struct xattr_handler au_xattr_handler = { - /* no prefix, no flags */ - .list = au_xattr_list, - .get = au_xattr_get, - .set = au_xattr_set - /* why no remove? */ -}; - -static const struct xattr_handler *au_xattr_handlers[] = { - &au_xattr_handler -}; - -void au_xattr_init(struct super_block *sb) -{ - /* sb->s_xattr = au_xattr_handlers; */ -} -#endif diff --git a/fs/aufs/xino.c b/fs/aufs/xino.c deleted file mode 100644 index 053594410..000000000 --- a/fs/aufs/xino.c +++ /dev/null @@ -1,1297 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * external inode number translation table and bitmap - */ - -#include <linux/seq_file.h> -#include <linux/statfs.h> -#include "aufs.h" - -/* todo: unnecessary to support mmap_sem since kernel-space? */ -ssize_t xino_fread(vfs_readf_t func, struct file *file, void *kbuf, size_t size, - loff_t *pos) -{ - ssize_t err; - mm_segment_t oldfs; - union { - void *k; - char __user *u; - } buf; - - buf.k = kbuf; - oldfs = get_fs(); - set_fs(KERNEL_DS); - do { - /* todo: signal_pending? */ - err = func(file, buf.u, size, pos); - } while (err == -EAGAIN || err == -EINTR); - set_fs(oldfs); - -#if 0 /* reserved for future use */ - if (err > 0) - fsnotify_access(file->f_path.dentry); -#endif - - return err; -} - -/* ---------------------------------------------------------------------- */ - -static ssize_t do_xino_fwrite(vfs_writef_t func, struct file *file, void *kbuf, - size_t size, loff_t *pos) -{ - ssize_t err; - mm_segment_t oldfs; - union { - void *k; - const char __user *u; - } buf; - - buf.k = kbuf; - oldfs = get_fs(); - set_fs(KERNEL_DS); - do { - /* todo: signal_pending? */ - err = func(file, buf.u, size, pos); - } while (err == -EAGAIN || err == -EINTR); - set_fs(oldfs); - -#if 0 /* reserved for future use */ - if (err > 0) - fsnotify_modify(file->f_path.dentry); -#endif - - return err; -} - -struct do_xino_fwrite_args { - ssize_t *errp; - vfs_writef_t func; - struct file *file; - void *buf; - size_t size; - loff_t *pos; -}; - -static void call_do_xino_fwrite(void *args) -{ - struct do_xino_fwrite_args *a = args; - *a->errp = do_xino_fwrite(a->func, a->file, a->buf, a->size, a->pos); -} - -ssize_t xino_fwrite(vfs_writef_t func, struct file *file, void *buf, - size_t size, loff_t *pos) -{ - ssize_t err; - - /* todo: signal block and no wkq? */ - if (rlimit(RLIMIT_FSIZE) == RLIM_INFINITY) { - lockdep_off(); - err = do_xino_fwrite(func, file, buf, size, pos); - lockdep_on(); - } else { - /* - * it breaks RLIMIT_FSIZE and normal user's limit, - * users should care about quota and real 'filesystem full.' - */ - int wkq_err; - struct do_xino_fwrite_args args = { - .errp = &err, - .func = func, - .file = file, - .buf = buf, - .size = size, - .pos = pos - }; - - wkq_err = au_wkq_wait(call_do_xino_fwrite, &args); - if (unlikely(wkq_err)) - err = wkq_err; - } - - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* - * create a new xinofile at the same place/path as @base_file. - */ -struct file *au_xino_create2(struct file *base_file, struct file *copy_src) -{ - struct file *file; - struct dentry *base, *parent; - struct inode *dir, *delegated; - struct qstr *name; - struct path path; - int err; - - base = base_file->f_path.dentry; - parent = base->d_parent; /* dir inode is locked */ - dir = d_inode(parent); - IMustLock(dir); - - file = ERR_PTR(-EINVAL); - name = &base->d_name; - path.dentry = vfsub_lookup_one_len(name->name, parent, name->len); - if (IS_ERR(path.dentry)) { - file = (void *)path.dentry; - pr_err("%pd lookup err %ld\n", - base, PTR_ERR(path.dentry)); - goto out; - } - - /* no need to mnt_want_write() since we call dentry_open() later */ - err = vfs_create(dir, path.dentry, S_IRUGO | S_IWUGO, NULL); - if (unlikely(err)) { - file = ERR_PTR(err); - pr_err("%pd create err %d\n", base, err); - goto out_dput; - } - - path.mnt = base_file->f_path.mnt; - file = vfsub_dentry_open(&path, - O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE - /* | __FMODE_NONOTIFY */); - if (IS_ERR(file)) { - pr_err("%pd open err %ld\n", base, PTR_ERR(file)); - goto out_dput; - } - - delegated = NULL; - err = vfsub_unlink(dir, &file->f_path, &delegated, /*force*/0); - if (unlikely(err == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal unlink\n"); - iput(delegated); - } - if (unlikely(err)) { - pr_err("%pd unlink err %d\n", base, err); - goto out_fput; - } - - if (copy_src) { - /* no one can touch copy_src xino */ - err = au_copy_file(file, copy_src, vfsub_f_size_read(copy_src)); - if (unlikely(err)) { - pr_err("%pd copy err %d\n", base, err); - goto out_fput; - } - } - goto out_dput; /* success */ - -out_fput: - fput(file); - file = ERR_PTR(err); -out_dput: - dput(path.dentry); -out: - return file; -} - -struct au_xino_lock_dir { - struct au_hinode *hdir; - struct dentry *parent; - struct mutex *mtx; -}; - -static void au_xino_lock_dir(struct super_block *sb, struct file *xino, - struct au_xino_lock_dir *ldir) -{ - aufs_bindex_t brid, bindex; - - ldir->hdir = NULL; - bindex = -1; - brid = au_xino_brid(sb); - if (brid >= 0) - bindex = au_br_index(sb, brid); - if (bindex >= 0) { - ldir->hdir = au_hi(d_inode(sb->s_root), bindex); - au_hn_imtx_lock_nested(ldir->hdir, AuLsc_I_PARENT); - } else { - ldir->parent = dget_parent(xino->f_path.dentry); - ldir->mtx = &d_inode(ldir->parent)->i_mutex; - mutex_lock_nested(ldir->mtx, AuLsc_I_PARENT); - } -} - -static void au_xino_unlock_dir(struct au_xino_lock_dir *ldir) -{ - if (ldir->hdir) - au_hn_imtx_unlock(ldir->hdir); - else { - mutex_unlock(ldir->mtx); - dput(ldir->parent); - } -} - -/* ---------------------------------------------------------------------- */ - -/* trucate xino files asynchronously */ - -int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex) -{ - int err; - unsigned long jiffy; - blkcnt_t blocks; - aufs_bindex_t bi, bend; - struct kstatfs *st; - struct au_branch *br; - struct file *new_xino, *file; - struct super_block *h_sb; - struct au_xino_lock_dir ldir; - - err = -ENOMEM; - st = kzalloc(sizeof(*st), GFP_NOFS); - if (unlikely(!st)) - goto out; - - err = -EINVAL; - bend = au_sbend(sb); - if (unlikely(bindex < 0 || bend < bindex)) - goto out_st; - br = au_sbr(sb, bindex); - file = br->br_xino.xi_file; - if (!file) - goto out_st; - - err = vfs_statfs(&file->f_path, st); - if (unlikely(err)) - AuErr1("statfs err %d, ignored\n", err); - jiffy = jiffies; - blocks = file_inode(file)->i_blocks; - pr_info("begin truncating xino(b%d), ib%llu, %llu/%llu free blks\n", - bindex, (u64)blocks, st->f_bfree, st->f_blocks); - - au_xino_lock_dir(sb, file, &ldir); - /* mnt_want_write() is unnecessary here */ - new_xino = au_xino_create2(file, file); - au_xino_unlock_dir(&ldir); - err = PTR_ERR(new_xino); - if (IS_ERR(new_xino)) { - pr_err("err %d, ignored\n", err); - goto out_st; - } - err = 0; - fput(file); - br->br_xino.xi_file = new_xino; - - h_sb = au_br_sb(br); - for (bi = 0; bi <= bend; bi++) { - if (unlikely(bi == bindex)) - continue; - br = au_sbr(sb, bi); - if (au_br_sb(br) != h_sb) - continue; - - fput(br->br_xino.xi_file); - br->br_xino.xi_file = new_xino; - get_file(new_xino); - } - - err = vfs_statfs(&new_xino->f_path, st); - if (!err) { - pr_info("end truncating xino(b%d), ib%llu, %llu/%llu free blks\n", - bindex, (u64)file_inode(new_xino)->i_blocks, - st->f_bfree, st->f_blocks); - if (file_inode(new_xino)->i_blocks < blocks) - au_sbi(sb)->si_xino_jiffy = jiffy; - } else - AuErr1("statfs err %d, ignored\n", err); - -out_st: - kfree(st); -out: - return err; -} - -struct xino_do_trunc_args { - struct super_block *sb; - struct au_branch *br; -}; - -static void xino_do_trunc(void *_args) -{ - struct xino_do_trunc_args *args = _args; - struct super_block *sb; - struct au_branch *br; - struct inode *dir; - int err; - aufs_bindex_t bindex; - - err = 0; - sb = args->sb; - dir = d_inode(sb->s_root); - br = args->br; - - si_noflush_write_lock(sb); - ii_read_lock_parent(dir); - bindex = au_br_index(sb, br->br_id); - err = au_xino_trunc(sb, bindex); - ii_read_unlock(dir); - if (unlikely(err)) - pr_warn("err b%d, (%d)\n", bindex, err); - atomic_dec(&br->br_xino_running); - atomic_dec(&br->br_count); - si_write_unlock(sb); - au_nwt_done(&au_sbi(sb)->si_nowait); - kfree(args); -} - -static int xino_trunc_test(struct super_block *sb, struct au_branch *br) -{ - int err; - struct kstatfs st; - struct au_sbinfo *sbinfo; - - /* todo: si_xino_expire and the ratio should be customizable */ - sbinfo = au_sbi(sb); - if (time_before(jiffies, - sbinfo->si_xino_jiffy + sbinfo->si_xino_expire)) - return 0; - - /* truncation border */ - err = vfs_statfs(&br->br_xino.xi_file->f_path, &st); - if (unlikely(err)) { - AuErr1("statfs err %d, ignored\n", err); - return 0; - } - if (div64_u64(st.f_bfree * 100, st.f_blocks) >= AUFS_XINO_DEF_TRUNC) - return 0; - - return 1; -} - -static void xino_try_trunc(struct super_block *sb, struct au_branch *br) -{ - struct xino_do_trunc_args *args; - int wkq_err; - - if (!xino_trunc_test(sb, br)) - return; - - if (atomic_inc_return(&br->br_xino_running) > 1) - goto out; - - /* lock and kfree() will be called in trunc_xino() */ - args = kmalloc(sizeof(*args), GFP_NOFS); - if (unlikely(!args)) { - AuErr1("no memory\n"); - goto out_args; - } - - atomic_inc(&br->br_count); - args->sb = sb; - args->br = br; - wkq_err = au_wkq_nowait(xino_do_trunc, args, sb, /*flags*/0); - if (!wkq_err) - return; /* success */ - - pr_err("wkq %d\n", wkq_err); - atomic_dec(&br->br_count); - -out_args: - kfree(args); -out: - atomic_dec(&br->br_xino_running); -} - -/* ---------------------------------------------------------------------- */ - -static int au_xino_do_write(vfs_writef_t write, struct file *file, - ino_t h_ino, ino_t ino) -{ - loff_t pos; - ssize_t sz; - - pos = h_ino; - if (unlikely(au_loff_max / sizeof(ino) - 1 < pos)) { - AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); - return -EFBIG; - } - pos *= sizeof(ino); - sz = xino_fwrite(write, file, &ino, sizeof(ino), &pos); - if (sz == sizeof(ino)) - return 0; /* success */ - - AuIOErr("write failed (%zd)\n", sz); - return -EIO; -} - -/* - * write @ino to the xinofile for the specified branch{@sb, @bindex} - * at the position of @h_ino. - * even if @ino is zero, it is written to the xinofile and means no entry. - * if the size of the xino file on a specific filesystem exceeds the watermark, - * try truncating it. - */ -int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, - ino_t ino) -{ - int err; - unsigned int mnt_flags; - struct au_branch *br; - - BUILD_BUG_ON(sizeof(long long) != sizeof(au_loff_max) - || ((loff_t)-1) > 0); - SiMustAnyLock(sb); - - mnt_flags = au_mntflags(sb); - if (!au_opt_test(mnt_flags, XINO)) - return 0; - - br = au_sbr(sb, bindex); - err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, - h_ino, ino); - if (!err) { - if (au_opt_test(mnt_flags, TRUNC_XINO) - && au_test_fs_trunc_xino(au_br_sb(br))) - xino_try_trunc(sb, br); - return 0; /* success */ - } - - AuIOErr("write failed (%d)\n", err); - return -EIO; -} - -/* ---------------------------------------------------------------------- */ - -/* aufs inode number bitmap */ - -static const int page_bits = (int)PAGE_SIZE * BITS_PER_BYTE; -static ino_t xib_calc_ino(unsigned long pindex, int bit) -{ - ino_t ino; - - AuDebugOn(bit < 0 || page_bits <= bit); - ino = AUFS_FIRST_INO + pindex * page_bits + bit; - return ino; -} - -static void xib_calc_bit(ino_t ino, unsigned long *pindex, int *bit) -{ - AuDebugOn(ino < AUFS_FIRST_INO); - ino -= AUFS_FIRST_INO; - *pindex = ino / page_bits; - *bit = ino % page_bits; -} - -static int xib_pindex(struct super_block *sb, unsigned long pindex) -{ - int err; - loff_t pos; - ssize_t sz; - struct au_sbinfo *sbinfo; - struct file *xib; - unsigned long *p; - - sbinfo = au_sbi(sb); - MtxMustLock(&sbinfo->si_xib_mtx); - AuDebugOn(pindex > ULONG_MAX / PAGE_SIZE - || !au_opt_test(sbinfo->si_mntflags, XINO)); - - if (pindex == sbinfo->si_xib_last_pindex) - return 0; - - xib = sbinfo->si_xib; - p = sbinfo->si_xib_buf; - pos = sbinfo->si_xib_last_pindex; - pos *= PAGE_SIZE; - sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); - if (unlikely(sz != PAGE_SIZE)) - goto out; - - pos = pindex; - pos *= PAGE_SIZE; - if (vfsub_f_size_read(xib) >= pos + PAGE_SIZE) - sz = xino_fread(sbinfo->si_xread, xib, p, PAGE_SIZE, &pos); - else { - memset(p, 0, PAGE_SIZE); - sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); - } - if (sz == PAGE_SIZE) { - sbinfo->si_xib_last_pindex = pindex; - return 0; /* success */ - } - -out: - AuIOErr1("write failed (%zd)\n", sz); - err = sz; - if (sz >= 0) - err = -EIO; - return err; -} - -/* ---------------------------------------------------------------------- */ - -static void au_xib_clear_bit(struct inode *inode) -{ - int err, bit; - unsigned long pindex; - struct super_block *sb; - struct au_sbinfo *sbinfo; - - AuDebugOn(inode->i_nlink); - - sb = inode->i_sb; - xib_calc_bit(inode->i_ino, &pindex, &bit); - AuDebugOn(page_bits <= bit); - sbinfo = au_sbi(sb); - mutex_lock(&sbinfo->si_xib_mtx); - err = xib_pindex(sb, pindex); - if (!err) { - clear_bit(bit, sbinfo->si_xib_buf); - sbinfo->si_xib_next_bit = bit; - } - mutex_unlock(&sbinfo->si_xib_mtx); -} - -/* for s_op->delete_inode() */ -void au_xino_delete_inode(struct inode *inode, const int unlinked) -{ - int err; - unsigned int mnt_flags; - aufs_bindex_t bindex, bend, bi; - unsigned char try_trunc; - struct au_iinfo *iinfo; - struct super_block *sb; - struct au_hinode *hi; - struct inode *h_inode; - struct au_branch *br; - vfs_writef_t xwrite; - - sb = inode->i_sb; - mnt_flags = au_mntflags(sb); - if (!au_opt_test(mnt_flags, XINO) - || inode->i_ino == AUFS_ROOT_INO) - return; - - if (unlinked) { - au_xigen_inc(inode); - au_xib_clear_bit(inode); - } - - iinfo = au_ii(inode); - if (!iinfo) - return; - - bindex = iinfo->ii_bstart; - if (bindex < 0) - return; - - xwrite = au_sbi(sb)->si_xwrite; - try_trunc = !!au_opt_test(mnt_flags, TRUNC_XINO); - hi = iinfo->ii_hinode + bindex; - bend = iinfo->ii_bend; - for (; bindex <= bend; bindex++, hi++) { - h_inode = hi->hi_inode; - if (!h_inode - || (!unlinked && h_inode->i_nlink)) - continue; - - /* inode may not be revalidated */ - bi = au_br_index(sb, hi->hi_id); - if (bi < 0) - continue; - - br = au_sbr(sb, bi); - err = au_xino_do_write(xwrite, br->br_xino.xi_file, - h_inode->i_ino, /*ino*/0); - if (!err && try_trunc - && au_test_fs_trunc_xino(au_br_sb(br))) - xino_try_trunc(sb, br); - } -} - -/* get an unused inode number from bitmap */ -ino_t au_xino_new_ino(struct super_block *sb) -{ - ino_t ino; - unsigned long *p, pindex, ul, pend; - struct au_sbinfo *sbinfo; - struct file *file; - int free_bit, err; - - if (!au_opt_test(au_mntflags(sb), XINO)) - return iunique(sb, AUFS_FIRST_INO); - - sbinfo = au_sbi(sb); - mutex_lock(&sbinfo->si_xib_mtx); - p = sbinfo->si_xib_buf; - free_bit = sbinfo->si_xib_next_bit; - if (free_bit < page_bits && !test_bit(free_bit, p)) - goto out; /* success */ - free_bit = find_first_zero_bit(p, page_bits); - if (free_bit < page_bits) - goto out; /* success */ - - pindex = sbinfo->si_xib_last_pindex; - for (ul = pindex - 1; ul < ULONG_MAX; ul--) { - err = xib_pindex(sb, ul); - if (unlikely(err)) - goto out_err; - free_bit = find_first_zero_bit(p, page_bits); - if (free_bit < page_bits) - goto out; /* success */ - } - - file = sbinfo->si_xib; - pend = vfsub_f_size_read(file) / PAGE_SIZE; - for (ul = pindex + 1; ul <= pend; ul++) { - err = xib_pindex(sb, ul); - if (unlikely(err)) - goto out_err; - free_bit = find_first_zero_bit(p, page_bits); - if (free_bit < page_bits) - goto out; /* success */ - } - BUG(); - -out: - set_bit(free_bit, p); - sbinfo->si_xib_next_bit = free_bit + 1; - pindex = sbinfo->si_xib_last_pindex; - mutex_unlock(&sbinfo->si_xib_mtx); - ino = xib_calc_ino(pindex, free_bit); - AuDbg("i%lu\n", (unsigned long)ino); - return ino; -out_err: - mutex_unlock(&sbinfo->si_xib_mtx); - AuDbg("i0\n"); - return 0; -} - -/* - * read @ino from xinofile for the specified branch{@sb, @bindex} - * at the position of @h_ino. - * if @ino does not exist and @do_new is true, get new one. - */ -int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, - ino_t *ino) -{ - int err; - ssize_t sz; - loff_t pos; - struct file *file; - struct au_sbinfo *sbinfo; - - *ino = 0; - if (!au_opt_test(au_mntflags(sb), XINO)) - return 0; /* no xino */ - - err = 0; - sbinfo = au_sbi(sb); - pos = h_ino; - if (unlikely(au_loff_max / sizeof(*ino) - 1 < pos)) { - AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); - return -EFBIG; - } - pos *= sizeof(*ino); - - file = au_sbr(sb, bindex)->br_xino.xi_file; - if (vfsub_f_size_read(file) < pos + sizeof(*ino)) - return 0; /* no ino */ - - sz = xino_fread(sbinfo->si_xread, file, ino, sizeof(*ino), &pos); - if (sz == sizeof(*ino)) - return 0; /* success */ - - err = sz; - if (unlikely(sz >= 0)) { - err = -EIO; - AuIOErr("xino read error (%zd)\n", sz); - } - - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* create and set a new xino file */ - -struct file *au_xino_create(struct super_block *sb, char *fname, int silent) -{ - struct file *file; - struct dentry *h_parent, *d; - struct inode *h_dir, *inode; - int err; - - /* - * at mount-time, and the xino file is the default path, - * hnotify is disabled so we have no notify events to ignore. - * when a user specified the xino, we cannot get au_hdir to be ignored. - */ - file = vfsub_filp_open(fname, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE - /* | __FMODE_NONOTIFY */, - S_IRUGO | S_IWUGO); - if (IS_ERR(file)) { - if (!silent) - pr_err("open %s(%ld)\n", fname, PTR_ERR(file)); - return file; - } - - /* keep file count */ - err = 0; - inode = file_inode(file); - h_parent = dget_parent(file->f_path.dentry); - h_dir = d_inode(h_parent); - mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); - /* mnt_want_write() is unnecessary here */ - /* no delegation since it is just created */ - if (inode->i_nlink) - err = vfsub_unlink(h_dir, &file->f_path, /*delegated*/NULL, - /*force*/0); - mutex_unlock(&h_dir->i_mutex); - dput(h_parent); - if (unlikely(err)) { - if (!silent) - pr_err("unlink %s(%d)\n", fname, err); - goto out; - } - - err = -EINVAL; - d = file->f_path.dentry; - if (unlikely(sb == d->d_sb)) { - if (!silent) - pr_err("%s must be outside\n", fname); - goto out; - } - if (unlikely(au_test_fs_bad_xino(d->d_sb))) { - if (!silent) - pr_err("xino doesn't support %s(%s)\n", - fname, au_sbtype(d->d_sb)); - goto out; - } - return file; /* success */ - -out: - fput(file); - file = ERR_PTR(err); - return file; -} - -/* - * find another branch who is on the same filesystem of the specified - * branch{@btgt}. search until @bend. - */ -static int is_sb_shared(struct super_block *sb, aufs_bindex_t btgt, - aufs_bindex_t bend) -{ - aufs_bindex_t bindex; - struct super_block *tgt_sb = au_sbr_sb(sb, btgt); - - for (bindex = 0; bindex < btgt; bindex++) - if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) - return bindex; - for (bindex++; bindex <= bend; bindex++) - if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) - return bindex; - return -1; -} - -/* ---------------------------------------------------------------------- */ - -/* - * initialize the xinofile for the specified branch @br - * at the place/path where @base_file indicates. - * test whether another branch is on the same filesystem or not, - * if @do_test is true. - */ -int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t h_ino, - struct file *base_file, int do_test) -{ - int err; - ino_t ino; - aufs_bindex_t bend, bindex; - struct au_branch *shared_br, *b; - struct file *file; - struct super_block *tgt_sb; - - shared_br = NULL; - bend = au_sbend(sb); - if (do_test) { - tgt_sb = au_br_sb(br); - for (bindex = 0; bindex <= bend; bindex++) { - b = au_sbr(sb, bindex); - if (tgt_sb == au_br_sb(b)) { - shared_br = b; - break; - } - } - } - - if (!shared_br || !shared_br->br_xino.xi_file) { - struct au_xino_lock_dir ldir; - - au_xino_lock_dir(sb, base_file, &ldir); - /* mnt_want_write() is unnecessary here */ - file = au_xino_create2(base_file, NULL); - au_xino_unlock_dir(&ldir); - err = PTR_ERR(file); - if (IS_ERR(file)) - goto out; - br->br_xino.xi_file = file; - } else { - br->br_xino.xi_file = shared_br->br_xino.xi_file; - get_file(br->br_xino.xi_file); - } - - ino = AUFS_ROOT_INO; - err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, - h_ino, ino); - if (unlikely(err)) { - fput(br->br_xino.xi_file); - br->br_xino.xi_file = NULL; - } - -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* trucate a xino bitmap file */ - -/* todo: slow */ -static int do_xib_restore(struct super_block *sb, struct file *file, void *page) -{ - int err, bit; - ssize_t sz; - unsigned long pindex; - loff_t pos, pend; - struct au_sbinfo *sbinfo; - vfs_readf_t func; - ino_t *ino; - unsigned long *p; - - err = 0; - sbinfo = au_sbi(sb); - MtxMustLock(&sbinfo->si_xib_mtx); - p = sbinfo->si_xib_buf; - func = sbinfo->si_xread; - pend = vfsub_f_size_read(file); - pos = 0; - while (pos < pend) { - sz = xino_fread(func, file, page, PAGE_SIZE, &pos); - err = sz; - if (unlikely(sz <= 0)) - goto out; - - err = 0; - for (ino = page; sz > 0; ino++, sz -= sizeof(ino)) { - if (unlikely(*ino < AUFS_FIRST_INO)) - continue; - - xib_calc_bit(*ino, &pindex, &bit); - AuDebugOn(page_bits <= bit); - err = xib_pindex(sb, pindex); - if (!err) - set_bit(bit, p); - else - goto out; - } - } - -out: - return err; -} - -static int xib_restore(struct super_block *sb) -{ - int err; - aufs_bindex_t bindex, bend; - void *page; - - err = -ENOMEM; - page = (void *)__get_free_page(GFP_NOFS); - if (unlikely(!page)) - goto out; - - err = 0; - bend = au_sbend(sb); - for (bindex = 0; !err && bindex <= bend; bindex++) - if (!bindex || is_sb_shared(sb, bindex, bindex - 1) < 0) - err = do_xib_restore - (sb, au_sbr(sb, bindex)->br_xino.xi_file, page); - else - AuDbg("b%d\n", bindex); - free_page((unsigned long)page); - -out: - return err; -} - -int au_xib_trunc(struct super_block *sb) -{ - int err; - ssize_t sz; - loff_t pos; - struct au_xino_lock_dir ldir; - struct au_sbinfo *sbinfo; - unsigned long *p; - struct file *file; - - SiMustWriteLock(sb); - - err = 0; - sbinfo = au_sbi(sb); - if (!au_opt_test(sbinfo->si_mntflags, XINO)) - goto out; - - file = sbinfo->si_xib; - if (vfsub_f_size_read(file) <= PAGE_SIZE) - goto out; - - au_xino_lock_dir(sb, file, &ldir); - /* mnt_want_write() is unnecessary here */ - file = au_xino_create2(sbinfo->si_xib, NULL); - au_xino_unlock_dir(&ldir); - err = PTR_ERR(file); - if (IS_ERR(file)) - goto out; - fput(sbinfo->si_xib); - sbinfo->si_xib = file; - - p = sbinfo->si_xib_buf; - memset(p, 0, PAGE_SIZE); - pos = 0; - sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xib, p, PAGE_SIZE, &pos); - if (unlikely(sz != PAGE_SIZE)) { - err = sz; - AuIOErr("err %d\n", err); - if (sz >= 0) - err = -EIO; - goto out; - } - - mutex_lock(&sbinfo->si_xib_mtx); - /* mnt_want_write() is unnecessary here */ - err = xib_restore(sb); - mutex_unlock(&sbinfo->si_xib_mtx); - -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* - * xino mount option handlers - */ - -/* xino bitmap */ -static void xino_clear_xib(struct super_block *sb) -{ - struct au_sbinfo *sbinfo; - - SiMustWriteLock(sb); - - sbinfo = au_sbi(sb); - sbinfo->si_xread = NULL; - sbinfo->si_xwrite = NULL; - if (sbinfo->si_xib) - fput(sbinfo->si_xib); - sbinfo->si_xib = NULL; - free_page((unsigned long)sbinfo->si_xib_buf); - sbinfo->si_xib_buf = NULL; -} - -static int au_xino_set_xib(struct super_block *sb, struct file *base) -{ - int err; - loff_t pos; - struct au_sbinfo *sbinfo; - struct file *file; - - SiMustWriteLock(sb); - - sbinfo = au_sbi(sb); - file = au_xino_create2(base, sbinfo->si_xib); - err = PTR_ERR(file); - if (IS_ERR(file)) - goto out; - if (sbinfo->si_xib) - fput(sbinfo->si_xib); - sbinfo->si_xib = file; - sbinfo->si_xread = vfs_readf(file); - sbinfo->si_xwrite = vfs_writef(file); - - err = -ENOMEM; - if (!sbinfo->si_xib_buf) - sbinfo->si_xib_buf = (void *)get_zeroed_page(GFP_NOFS); - if (unlikely(!sbinfo->si_xib_buf)) - goto out_unset; - - sbinfo->si_xib_last_pindex = 0; - sbinfo->si_xib_next_bit = 0; - if (vfsub_f_size_read(file) < PAGE_SIZE) { - pos = 0; - err = xino_fwrite(sbinfo->si_xwrite, file, sbinfo->si_xib_buf, - PAGE_SIZE, &pos); - if (unlikely(err != PAGE_SIZE)) - goto out_free; - } - err = 0; - goto out; /* success */ - -out_free: - free_page((unsigned long)sbinfo->si_xib_buf); - sbinfo->si_xib_buf = NULL; - if (err >= 0) - err = -EIO; -out_unset: - fput(sbinfo->si_xib); - sbinfo->si_xib = NULL; - sbinfo->si_xread = NULL; - sbinfo->si_xwrite = NULL; -out: - return err; -} - -/* xino for each branch */ -static void xino_clear_br(struct super_block *sb) -{ - aufs_bindex_t bindex, bend; - struct au_branch *br; - - bend = au_sbend(sb); - for (bindex = 0; bindex <= bend; bindex++) { - br = au_sbr(sb, bindex); - if (!br || !br->br_xino.xi_file) - continue; - - fput(br->br_xino.xi_file); - br->br_xino.xi_file = NULL; - } -} - -static int au_xino_set_br(struct super_block *sb, struct file *base) -{ - int err; - ino_t ino; - aufs_bindex_t bindex, bend, bshared; - struct { - struct file *old, *new; - } *fpair, *p; - struct au_branch *br; - struct inode *inode; - vfs_writef_t writef; - - SiMustWriteLock(sb); - - err = -ENOMEM; - bend = au_sbend(sb); - fpair = kcalloc(bend + 1, sizeof(*fpair), GFP_NOFS); - if (unlikely(!fpair)) - goto out; - - inode = d_inode(sb->s_root); - ino = AUFS_ROOT_INO; - writef = au_sbi(sb)->si_xwrite; - for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { - br = au_sbr(sb, bindex); - bshared = is_sb_shared(sb, bindex, bindex - 1); - if (bshared >= 0) { - /* shared xino */ - *p = fpair[bshared]; - get_file(p->new); - } - - if (!p->new) { - /* new xino */ - p->old = br->br_xino.xi_file; - p->new = au_xino_create2(base, br->br_xino.xi_file); - err = PTR_ERR(p->new); - if (IS_ERR(p->new)) { - p->new = NULL; - goto out_pair; - } - } - - err = au_xino_do_write(writef, p->new, - au_h_iptr(inode, bindex)->i_ino, ino); - if (unlikely(err)) - goto out_pair; - } - - for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { - br = au_sbr(sb, bindex); - if (br->br_xino.xi_file) - fput(br->br_xino.xi_file); - get_file(p->new); - br->br_xino.xi_file = p->new; - } - -out_pair: - for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) - if (p->new) - fput(p->new); - else - break; - kfree(fpair); -out: - return err; -} - -void au_xino_clr(struct super_block *sb) -{ - struct au_sbinfo *sbinfo; - - au_xigen_clr(sb); - xino_clear_xib(sb); - xino_clear_br(sb); - sbinfo = au_sbi(sb); - /* lvalue, do not call au_mntflags() */ - au_opt_clr(sbinfo->si_mntflags, XINO); -} - -int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount) -{ - int err, skip; - struct dentry *parent, *cur_parent; - struct qstr *dname, *cur_name; - struct file *cur_xino; - struct inode *dir; - struct au_sbinfo *sbinfo; - - SiMustWriteLock(sb); - - err = 0; - sbinfo = au_sbi(sb); - parent = dget_parent(xino->file->f_path.dentry); - if (remount) { - skip = 0; - dname = &xino->file->f_path.dentry->d_name; - cur_xino = sbinfo->si_xib; - if (cur_xino) { - cur_parent = dget_parent(cur_xino->f_path.dentry); - cur_name = &cur_xino->f_path.dentry->d_name; - skip = (cur_parent == parent - && au_qstreq(dname, cur_name)); - dput(cur_parent); - } - if (skip) - goto out; - } - - au_opt_set(sbinfo->si_mntflags, XINO); - dir = d_inode(parent); - mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT); - /* mnt_want_write() is unnecessary here */ - err = au_xino_set_xib(sb, xino->file); - if (!err) - err = au_xigen_set(sb, xino->file); - if (!err) - err = au_xino_set_br(sb, xino->file); - mutex_unlock(&dir->i_mutex); - if (!err) - goto out; /* success */ - - /* reset all */ - AuIOErr("failed creating xino(%d).\n", err); - au_xigen_clr(sb); - xino_clear_xib(sb); - -out: - dput(parent); - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* - * create a xinofile at the default place/path. - */ -struct file *au_xino_def(struct super_block *sb) -{ - struct file *file; - char *page, *p; - struct au_branch *br; - struct super_block *h_sb; - struct path path; - aufs_bindex_t bend, bindex, bwr; - - br = NULL; - bend = au_sbend(sb); - bwr = -1; - for (bindex = 0; bindex <= bend; bindex++) { - br = au_sbr(sb, bindex); - if (au_br_writable(br->br_perm) - && !au_test_fs_bad_xino(au_br_sb(br))) { - bwr = bindex; - break; - } - } - - if (bwr >= 0) { - file = ERR_PTR(-ENOMEM); - page = (void *)__get_free_page(GFP_NOFS); - if (unlikely(!page)) - goto out; - path.mnt = au_br_mnt(br); - path.dentry = au_h_dptr(sb->s_root, bwr); - p = d_path(&path, page, PATH_MAX - sizeof(AUFS_XINO_FNAME)); - file = (void *)p; - if (!IS_ERR(p)) { - strcat(p, "/" AUFS_XINO_FNAME); - AuDbg("%s\n", p); - file = au_xino_create(sb, p, /*silent*/0); - if (!IS_ERR(file)) - au_xino_brid_set(sb, br->br_id); - } - free_page((unsigned long)page); - } else { - file = au_xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0); - if (IS_ERR(file)) - goto out; - h_sb = file->f_path.dentry->d_sb; - if (unlikely(au_test_fs_bad_xino(h_sb))) { - pr_err("xino doesn't support %s(%s)\n", - AUFS_XINO_DEFPATH, au_sbtype(h_sb)); - fput(file); - file = ERR_PTR(-EINVAL); - } - if (!IS_ERR(file)) - au_xino_brid_set(sb, -1); - } - -out: - return file; -} - -/* ---------------------------------------------------------------------- */ - -int au_xino_path(struct seq_file *seq, struct file *file) -{ - int err; - - err = au_seq_path(seq, &file->f_path); - if (unlikely(err < 0)) - goto out; - - err = 0; -#define Deleted "\\040(deleted)" - seq->count -= sizeof(Deleted) - 1; - AuDebugOn(memcmp(seq->buf + seq->count, Deleted, - sizeof(Deleted) - 1)); -#undef Deleted - -out: - return err; -} diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index 5b700ef1e..c37149b92 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -238,11 +238,6 @@ static inline u64 autofs4_get_ino(struct autofs_sb_info *sbi) return d_inode(sbi->sb->s_root)->i_ino; } -static inline int simple_positive(struct dentry *dentry) -{ - return d_really_is_positive(dentry) && !d_unhashed(dentry); -} - static inline void __autofs4_add_expiring(struct dentry *dentry) { struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); diff --git a/fs/autofs4/symlink.c b/fs/autofs4/symlink.c index de58cc7b8..da0c33481 100644 --- a/fs/autofs4/symlink.c +++ b/fs/autofs4/symlink.c @@ -12,14 +12,13 @@ #include "autofs_i.h" -static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *autofs4_follow_link(struct dentry *dentry, void **cookie) { struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); struct autofs_info *ino = autofs4_dentry_ino(dentry); if (ino && !autofs4_oz_mode(sbi)) ino->last_used = jiffies; - nd_set_link(nd, d_inode(dentry)->i_private); - return NULL; + return d_inode(dentry)->i_private; } const struct inode_operations autofs4_symlink_inode_operations = { diff --git a/fs/befs/befs.h b/fs/befs/befs.h index 1fead8d56..35d19e873 100644 --- a/fs/befs/befs.h +++ b/fs/befs/befs.h @@ -112,7 +112,7 @@ BEFS_SB(const struct super_block *super) static inline struct befs_inode_info * BEFS_I(const struct inode *inode) { - return list_entry(inode, struct befs_inode_info, vfs_inode); + return container_of(inode, struct befs_inode_info, vfs_inode); } static inline befs_blocknr_t diff --git a/fs/befs/btree.c b/fs/befs/btree.c index 0826e91da..22c166280 100644 --- a/fs/befs/btree.c +++ b/fs/befs/btree.c @@ -137,8 +137,8 @@ static int befs_bt_read_super(struct super_block *sb, befs_data_stream * ds, befs_btree_super * sup) { - struct buffer_head *bh = NULL; - befs_disk_btree_super *od_sup = NULL; + struct buffer_head *bh; + befs_disk_btree_super *od_sup; befs_debug(sb, "---> %s", __func__); @@ -250,7 +250,7 @@ int befs_btree_find(struct super_block *sb, befs_data_stream * ds, const char *key, befs_off_t * value) { - struct befs_btree_node *this_node = NULL; + struct befs_btree_node *this_node; befs_btree_super bt_super; befs_off_t node_off; int res; diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index 7943533c3..46aedacfa 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -42,8 +42,7 @@ static struct inode *befs_iget(struct super_block *, unsigned long); static struct inode *befs_alloc_inode(struct super_block *sb); static void befs_destroy_inode(struct inode *inode); static void befs_destroy_inodecache(void); -static void *befs_follow_link(struct dentry *, struct nameidata *); -static void *befs_fast_follow_link(struct dentry *, struct nameidata *); +static const char *befs_follow_link(struct dentry *, void **); static int befs_utf2nls(struct super_block *sb, const char *in, int in_len, char **out, int *out_len); static int befs_nls2utf(struct super_block *sb, const char *in, int in_len, @@ -80,11 +79,6 @@ static const struct address_space_operations befs_aops = { .bmap = befs_bmap, }; -static const struct inode_operations befs_fast_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = befs_fast_follow_link, -}; - static const struct inode_operations befs_symlink_inode_operations = { .readlink = generic_readlink, .follow_link = befs_follow_link, @@ -403,10 +397,12 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino) inode->i_op = &befs_dir_inode_operations; inode->i_fop = &befs_dir_operations; } else if (S_ISLNK(inode->i_mode)) { - if (befs_ino->i_flags & BEFS_LONG_SYMLINK) + if (befs_ino->i_flags & BEFS_LONG_SYMLINK) { inode->i_op = &befs_symlink_inode_operations; - else - inode->i_op = &befs_fast_symlink_inode_operations; + } else { + inode->i_link = befs_ino->i_data.symlink; + inode->i_op = &simple_symlink_inode_operations; + } } else { befs_error(sb, "Inode %lu is not a regular file, " "directory or symlink. THAT IS WRONG! BeFS has no " @@ -467,8 +463,8 @@ befs_destroy_inodecache(void) * The data stream become link name. Unless the LONG_SYMLINK * flag is set. */ -static void * -befs_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char * +befs_follow_link(struct dentry *dentry, void **cookie) { struct super_block *sb = dentry->d_sb; struct befs_inode_info *befs_ino = BEFS_I(d_inode(dentry)); @@ -478,33 +474,20 @@ befs_follow_link(struct dentry *dentry, struct nameidata *nd) if (len == 0) { befs_error(sb, "Long symlink with illegal length"); - link = ERR_PTR(-EIO); - } else { - befs_debug(sb, "Follow long symlink"); - - link = kmalloc(len, GFP_NOFS); - if (!link) { - link = ERR_PTR(-ENOMEM); - } else if (befs_read_lsymlink(sb, data, link, len) != len) { - kfree(link); - befs_error(sb, "Failed to read entire long symlink"); - link = ERR_PTR(-EIO); - } else { - link[len - 1] = '\0'; - } + return ERR_PTR(-EIO); } - nd_set_link(nd, link); - return NULL; -} - - -static void * -befs_fast_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct befs_inode_info *befs_ino = BEFS_I(d_inode(dentry)); + befs_debug(sb, "Follow long symlink"); - nd_set_link(nd, befs_ino->i_data.symlink); - return NULL; + link = kmalloc(len, GFP_NOFS); + if (!link) + return ERR_PTR(-ENOMEM); + if (befs_read_lsymlink(sb, data, link, len) != len) { + kfree(link); + befs_error(sb, "Failed to read entire long symlink"); + return ERR_PTR(-EIO); + } + link[len - 1] = '\0'; + return *cookie = link; } /* diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index cd46e4158..6b6599678 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1530,7 +1530,7 @@ static int fill_files_note(struct memelfnote *note) file = vma->vm_file; if (!file) continue; - filename = d_path(&file->f_path, name_curpos, remaining); + filename = file_path(file, name_curpos, remaining); if (IS_ERR(filename)) { if (PTR_ERR(filename) == -ENAMETOOLONG) { vfree(data); @@ -1540,7 +1540,7 @@ static int fill_files_note(struct memelfnote *note) continue; } - /* d_path() fills at the end, move name down */ + /* file_path() fills at the end, move name down */ /* n = strlen(filename) + 1: */ n = (name_curpos + remaining) - filename; remaining = filename - name_curpos; diff --git a/fs/block_dev.c b/fs/block_dev.c index c7e4163ed..198243717 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -14,6 +14,7 @@ #include <linux/device_cgroup.h> #include <linux/highmem.h> #include <linux/blkdev.h> +#include <linux/backing-dev.h> #include <linux/module.h> #include <linux/blkpg.h> #include <linux/magic.h> @@ -42,7 +43,7 @@ static inline struct bdev_inode *BDEV_I(struct inode *inode) return container_of(inode, struct bdev_inode, vfs_inode); } -inline struct block_device *I_BDEV(struct inode *inode) +struct block_device *I_BDEV(struct inode *inode) { return &BDEV_I(inode)->bdev; } @@ -151,6 +152,9 @@ blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter, loff_t offset) struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; + if (IS_DAX(inode)) + return dax_do_io(iocb, inode, iter, offset, blkdev_get_block, + NULL, DIO_SKIP_DIO_COUNT); return __blockdev_direct_IO(iocb, inode, I_BDEV(inode), iter, offset, blkdev_get_block, NULL, NULL, DIO_SKIP_DIO_COUNT); @@ -376,7 +380,7 @@ int bdev_read_page(struct block_device *bdev, sector_t sector, struct page *page) { const struct block_device_operations *ops = bdev->bd_disk->fops; - if (!ops->rw_page) + if (!ops->rw_page || bdev_get_integrity(bdev)) return -EOPNOTSUPP; return ops->rw_page(bdev, sector + get_start_sect(bdev), page, READ); } @@ -407,7 +411,7 @@ int bdev_write_page(struct block_device *bdev, sector_t sector, int result; int rw = (wbc->sync_mode == WB_SYNC_ALL) ? WRITE_SYNC : WRITE; const struct block_device_operations *ops = bdev->bd_disk->fops; - if (!ops->rw_page) + if (!ops->rw_page || bdev_get_integrity(bdev)) return -EOPNOTSUPP; set_page_writeback(page); result = ops->rw_page(bdev, sector + get_start_sect(bdev), page, rw); @@ -442,6 +446,12 @@ long bdev_direct_access(struct block_device *bdev, sector_t sector, long avail; const struct block_device_operations *ops = bdev->bd_disk->fops; + /* + * The device driver is allowed to sleep, in order to make the + * memory directly accessible. + */ + might_sleep(); + if (size < 0) return size; if (!ops->direct_access) @@ -546,7 +556,8 @@ static struct file_system_type bd_type = { .kill_sb = kill_anon_super, }; -static struct super_block *blockdev_superblock __read_mostly; +struct super_block *blockdev_superblock __read_mostly; +EXPORT_SYMBOL_GPL(blockdev_superblock); void __init bdev_cache_init(void) { @@ -687,11 +698,6 @@ static struct block_device *bd_acquire(struct inode *inode) return bdev; } -int sb_is_blkdev_sb(struct super_block *sb) -{ - return sb == blockdev_superblock; -} - /* Call when you free inode */ void bd_forget(struct inode *inode) @@ -1173,6 +1179,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) bdev->bd_disk = disk; bdev->bd_queue = disk->queue; bdev->bd_contains = bdev; + bdev->bd_inode->i_flags = disk->fops->direct_access ? S_DAX : 0; if (!partno) { ret = -ENXIO; bdev->bd_part = disk_get_part(disk, partno); diff --git a/fs/btrfs/async-thread.c b/fs/btrfs/async-thread.c index df9932b00..1ce06c849 100644 --- a/fs/btrfs/async-thread.c +++ b/fs/btrfs/async-thread.c @@ -85,6 +85,7 @@ BTRFS_WORK_HELPER(extent_refs_helper); BTRFS_WORK_HELPER(scrub_helper); BTRFS_WORK_HELPER(scrubwrc_helper); BTRFS_WORK_HELPER(scrubnc_helper); +BTRFS_WORK_HELPER(scrubparity_helper); static struct __btrfs_workqueue * __btrfs_alloc_workqueue(const char *name, unsigned int flags, int max_active, diff --git a/fs/btrfs/async-thread.h b/fs/btrfs/async-thread.h index ec2ee477f..b0b093b6a 100644 --- a/fs/btrfs/async-thread.h +++ b/fs/btrfs/async-thread.h @@ -64,6 +64,8 @@ BTRFS_WORK_HELPER_PROTO(extent_refs_helper); BTRFS_WORK_HELPER_PROTO(scrub_helper); BTRFS_WORK_HELPER_PROTO(scrubwrc_helper); BTRFS_WORK_HELPER_PROTO(scrubnc_helper); +BTRFS_WORK_HELPER_PROTO(scrubparity_helper); + struct btrfs_workqueue *btrfs_alloc_workqueue(const char *name, unsigned int flags, diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index 614aaa196..802fabb30 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -250,8 +250,12 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path, * the first item to check. But sometimes, we may enter it with * slot==nritems. In that case, go to the next leaf before we continue. */ - if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) - ret = btrfs_next_old_leaf(root, path, time_seq); + if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) { + if (time_seq == (u64)-1) + ret = btrfs_next_leaf(root, path); + else + ret = btrfs_next_old_leaf(root, path, time_seq); + } while (!ret && count < total_refs) { eb = path->nodes[0]; @@ -291,7 +295,10 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path, eie = NULL; } next: - ret = btrfs_next_old_item(root, path, time_seq); + if (time_seq == (u64)-1) + ret = btrfs_next_item(root, path); + else + ret = btrfs_next_old_item(root, path, time_seq); } if (ret > 0) @@ -334,6 +341,8 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info, if (path->search_commit_root) root_level = btrfs_header_level(root->commit_root); + else if (time_seq == (u64)-1) + root_level = btrfs_header_level(root->node); else root_level = btrfs_old_root_level(root, time_seq); @@ -343,7 +352,12 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info, } path->lowest_level = level; - ret = btrfs_search_old_slot(root, &ref->key_for_search, path, time_seq); + if (time_seq == (u64)-1) + ret = btrfs_search_slot(NULL, root, &ref->key_for_search, path, + 0, 0); + else + ret = btrfs_search_old_slot(root, &ref->key_for_search, path, + time_seq); /* root node has been locked, we can release @subvol_srcu safely here */ srcu_read_unlock(&fs_info->subvol_srcu, index); @@ -491,7 +505,9 @@ static int __add_missing_keys(struct btrfs_fs_info *fs_info, BUG_ON(!ref->wanted_disk_byte); eb = read_tree_block(fs_info->tree_root, ref->wanted_disk_byte, 0); - if (!eb || !extent_buffer_uptodate(eb)) { + if (IS_ERR(eb)) { + return PTR_ERR(eb); + } else if (!extent_buffer_uptodate(eb)) { free_extent_buffer(eb); return -EIO; } @@ -507,7 +523,7 @@ static int __add_missing_keys(struct btrfs_fs_info *fs_info, } /* - * merge two lists of backrefs and adjust counts accordingly + * merge backrefs and adjust counts accordingly * * mode = 1: merge identical keys, if key is set * FIXME: if we add more keys in __add_prelim_ref, we can merge more here. @@ -535,9 +551,9 @@ static void __merge_refs(struct list_head *head, int mode) ref2 = list_entry(pos2, struct __prelim_ref, list); + if (!ref_for_same_block(ref1, ref2)) + continue; if (mode == 1) { - if (!ref_for_same_block(ref1, ref2)) - continue; if (!ref1->parent && ref2->parent) { xchg = ref1; ref1 = ref2; @@ -572,8 +588,8 @@ static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq, struct list_head *prefs, u64 *total_refs, u64 inum) { + struct btrfs_delayed_ref_node *node; struct btrfs_delayed_extent_op *extent_op = head->extent_op; - struct rb_node *n = &head->node.rb_node; struct btrfs_key key; struct btrfs_key op_key = {0}; int sgn; @@ -583,12 +599,7 @@ static int __add_delayed_refs(struct btrfs_delayed_ref_head *head, u64 seq, btrfs_disk_key_to_cpu(&op_key, &extent_op->key); spin_lock(&head->lock); - n = rb_first(&head->ref_root); - while (n) { - struct btrfs_delayed_ref_node *node; - node = rb_entry(n, struct btrfs_delayed_ref_node, - rb_node); - n = rb_next(n); + list_for_each_entry(node, &head->ref_list, list) { if (node->seq > seq) continue; @@ -882,6 +893,11 @@ static int __add_keyed_refs(struct btrfs_fs_info *fs_info, * * NOTE: This can return values > 0 * + * If time_seq is set to (u64)-1, it will not search delayed_refs, and behave + * much like trans == NULL case, the difference only lies in it will not + * commit root. + * The special case is for qgroup to search roots in commit_transaction(). + * * FIXME some caching might speed things up */ static int find_parent_nodes(struct btrfs_trans_handle *trans, @@ -920,6 +936,9 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans, path->skip_locking = 1; } + if (time_seq == (u64)-1) + path->skip_locking = 1; + /* * grab both a lock on the path and a lock on the delayed ref head. * We need both to get a consistent picture of how the refs look @@ -934,9 +953,10 @@ again: BUG_ON(ret == 0); #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS - if (trans && likely(trans->type != __TRANS_DUMMY)) { + if (trans && likely(trans->type != __TRANS_DUMMY) && + time_seq != (u64)-1) { #else - if (trans) { + if (trans && time_seq != (u64)-1) { #endif /* * look if there are updates for this ref queued and lock the @@ -1034,7 +1054,10 @@ again: eb = read_tree_block(fs_info->extent_root, ref->parent, 0); - if (!eb || !extent_buffer_uptodate(eb)) { + if (IS_ERR(eb)) { + ret = PTR_ERR(eb); + goto out; + } else if (!extent_buffer_uptodate(eb)) { free_extent_buffer(eb); ret = -EIO; goto out; diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 0ef5cc13f..81220b220 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -44,6 +44,8 @@ #define BTRFS_INODE_IN_DELALLOC_LIST 9 #define BTRFS_INODE_READDIO_NEED_LOCK 10 #define BTRFS_INODE_HAS_PROPS 11 +/* DIO is ready to submit */ +#define BTRFS_INODE_DIO_READY 12 /* * The following 3 bits are meant only for the btree inode. * When any of them is set, it means an error happened while writing an diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 0f11ebc92..54114b488 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -1439,8 +1439,9 @@ get_old_root(struct btrfs_root *root, u64 time_seq) btrfs_tree_read_unlock(eb_root); free_extent_buffer(eb_root); old = read_tree_block(root, logical, 0); - if (WARN_ON(!old || !extent_buffer_uptodate(old))) { - free_extent_buffer(old); + if (WARN_ON(IS_ERR(old) || !extent_buffer_uptodate(old))) { + if (!IS_ERR(old)) + free_extent_buffer(old); btrfs_warn(root->fs_info, "failed to read tree block %llu from get_old_root", logical); } else { @@ -1685,7 +1686,9 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans, if (!cur || !uptodate) { if (!cur) { cur = read_tree_block(root, blocknr, gen); - if (!cur || !extent_buffer_uptodate(cur)) { + if (IS_ERR(cur)) { + return PTR_ERR(cur); + } else if (!extent_buffer_uptodate(cur)) { free_extent_buffer(cur); return -EIO; } @@ -1864,8 +1867,9 @@ static noinline struct extent_buffer *read_node_slot(struct btrfs_root *root, eb = read_tree_block(root, btrfs_node_blockptr(parent, slot), btrfs_node_ptr_generation(parent, slot)); - if (eb && !extent_buffer_uptodate(eb)) { - free_extent_buffer(eb); + if (IS_ERR(eb) || !extent_buffer_uptodate(eb)) { + if (!IS_ERR(eb)) + free_extent_buffer(eb); eb = NULL; } @@ -2494,7 +2498,7 @@ read_block_for_search(struct btrfs_trans_handle *trans, ret = -EAGAIN; tmp = read_tree_block(root, blocknr, 0); - if (tmp) { + if (!IS_ERR(tmp)) { /* * If the read above didn't mark this buffer up to date, * it will never end up being up to date. Set ret to EIO now diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 6f364e1d8..aac314e14 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -174,7 +174,7 @@ struct btrfs_ordered_sum; /* csum types */ #define BTRFS_CSUM_TYPE_CRC32 0 -static int btrfs_csum_sizes[] = { 4, 0 }; +static int btrfs_csum_sizes[] = { 4 }; /* four bytes for CRC32 */ #define BTRFS_EMPTY_DIR_SIZE 0 @@ -1619,10 +1619,7 @@ struct btrfs_fs_info { struct task_struct *cleaner_kthread; int thread_pool_size; - struct kobject super_kobj; struct kobject *space_info_kobj; - struct kobject *device_dir_kobj; - struct completion kobj_unregister; int do_barriers; int closing; int log_root_recovering; @@ -1698,6 +1695,7 @@ struct btrfs_fs_info { struct btrfs_workqueue *scrub_workers; struct btrfs_workqueue *scrub_wr_completion_workers; struct btrfs_workqueue *scrub_nocow_workers; + struct btrfs_workqueue *scrub_parity_workers; #ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY u32 check_integrity_print_mask; @@ -1735,7 +1733,7 @@ struct btrfs_fs_info { /* list of dirty qgroups to be written at next commit */ struct list_head dirty_qgroups; - /* used by btrfs_qgroup_record_ref for an efficient tree traversal */ + /* used by qgroup for an efficient tree traversal */ u64 qgroup_seq; /* qgroup rescan items */ @@ -1780,6 +1778,7 @@ struct btrfs_fs_info { spinlock_t unused_bgs_lock; struct list_head unused_bgs; struct mutex unused_bg_unpin_mutex; + struct mutex delete_unused_bgs_mutex; /* For btrfs to record security options */ struct security_mnt_opts security_opts; @@ -3458,6 +3457,7 @@ int btrfs_check_data_free_space(struct inode *inode, u64 bytes, u64 write_bytes) void btrfs_free_reserved_data_space(struct inode *inode, u64 bytes); void btrfs_trans_release_metadata(struct btrfs_trans_handle *trans, struct btrfs_root *root); +void btrfs_trans_release_chunk_metadata(struct btrfs_trans_handle *trans); int btrfs_orphan_reserve_metadata(struct btrfs_trans_handle *trans, struct inode *inode); void btrfs_orphan_release_metadata(struct inode *inode); @@ -3515,6 +3515,9 @@ int btrfs_delayed_refs_qgroup_accounting(struct btrfs_trans_handle *trans, int __get_raid_index(u64 flags); int btrfs_start_write_no_snapshoting(struct btrfs_root *root); void btrfs_end_write_no_snapshoting(struct btrfs_root *root); +void check_system_chunk(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + const u64 type); /* ctree.c */ int btrfs_bin_search(struct extent_buffer *eb, struct btrfs_key *key, int level, int *slot); @@ -4050,6 +4053,7 @@ void btrfs_printk(const struct btrfs_fs_info *fs_info, const char *fmt, ...) #ifdef CONFIG_BTRFS_ASSERT +__cold static inline void assfail(char *expr, char *file, int line) { pr_err("BTRFS: assertion failed: %s, file: %s, line: %d", @@ -4065,10 +4069,12 @@ static inline void assfail(char *expr, char *file, int line) #define btrfs_assert() __printf(5, 6) +__cold void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function, unsigned int line, int errno, const char *fmt, ...); +__cold void __btrfs_abort_transaction(struct btrfs_trans_handle *trans, struct btrfs_root *root, const char *function, unsigned int line, int errno); @@ -4111,11 +4117,17 @@ static inline int __btrfs_fs_incompat(struct btrfs_fs_info *fs_info, u64 flag) * Call btrfs_abort_transaction as early as possible when an error condition is * detected, that way the exact line number is reported. */ - #define btrfs_abort_transaction(trans, root, errno) \ do { \ - __btrfs_abort_transaction(trans, root, __func__, \ - __LINE__, errno); \ + /* Report first abort since mount */ \ + if (!test_and_set_bit(BTRFS_FS_STATE_TRANS_ABORTED, \ + &((root)->fs_info->fs_state))) { \ + WARN(1, KERN_DEBUG \ + "BTRFS: Transaction aborted (error %d)\n", \ + (errno)); \ + } \ + __btrfs_abort_transaction((trans), (root), __func__, \ + __LINE__, (errno)); \ } while (0) #define btrfs_std_error(fs_info, errno) \ @@ -4132,6 +4144,7 @@ do { \ } while (0) __printf(5, 6) +__cold void __btrfs_panic(struct btrfs_fs_info *fs_info, const char *function, unsigned int line, int errno, const char *fmt, ...); diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index 8f8ed7d20..ac3e81da6 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -22,6 +22,7 @@ #include "ctree.h" #include "delayed-ref.h" #include "transaction.h" +#include "qgroup.h" struct kmem_cache *btrfs_delayed_ref_head_cachep; struct kmem_cache *btrfs_delayed_tree_ref_cachep; @@ -84,87 +85,6 @@ static int comp_data_refs(struct btrfs_delayed_data_ref *ref2, return 0; } -/* - * entries in the rb tree are ordered by the byte number of the extent, - * type of the delayed backrefs and content of delayed backrefs. - */ -static int comp_entry(struct btrfs_delayed_ref_node *ref2, - struct btrfs_delayed_ref_node *ref1, - bool compare_seq) -{ - if (ref1->bytenr < ref2->bytenr) - return -1; - if (ref1->bytenr > ref2->bytenr) - return 1; - if (ref1->is_head && ref2->is_head) - return 0; - if (ref2->is_head) - return -1; - if (ref1->is_head) - return 1; - if (ref1->type < ref2->type) - return -1; - if (ref1->type > ref2->type) - return 1; - if (ref1->no_quota > ref2->no_quota) - return 1; - if (ref1->no_quota < ref2->no_quota) - return -1; - /* merging of sequenced refs is not allowed */ - if (compare_seq) { - if (ref1->seq < ref2->seq) - return -1; - if (ref1->seq > ref2->seq) - return 1; - } - if (ref1->type == BTRFS_TREE_BLOCK_REF_KEY || - ref1->type == BTRFS_SHARED_BLOCK_REF_KEY) { - return comp_tree_refs(btrfs_delayed_node_to_tree_ref(ref2), - btrfs_delayed_node_to_tree_ref(ref1), - ref1->type); - } else if (ref1->type == BTRFS_EXTENT_DATA_REF_KEY || - ref1->type == BTRFS_SHARED_DATA_REF_KEY) { - return comp_data_refs(btrfs_delayed_node_to_data_ref(ref2), - btrfs_delayed_node_to_data_ref(ref1)); - } - BUG(); - return 0; -} - -/* - * insert a new ref into the rbtree. This returns any existing refs - * for the same (bytenr,parent) tuple, or NULL if the new node was properly - * inserted. - */ -static struct btrfs_delayed_ref_node *tree_insert(struct rb_root *root, - struct rb_node *node) -{ - struct rb_node **p = &root->rb_node; - struct rb_node *parent_node = NULL; - struct btrfs_delayed_ref_node *entry; - struct btrfs_delayed_ref_node *ins; - int cmp; - - ins = rb_entry(node, struct btrfs_delayed_ref_node, rb_node); - while (*p) { - parent_node = *p; - entry = rb_entry(parent_node, struct btrfs_delayed_ref_node, - rb_node); - - cmp = comp_entry(entry, ins, 1); - if (cmp < 0) - p = &(*p)->rb_left; - else if (cmp > 0) - p = &(*p)->rb_right; - else - return entry; - } - - rb_link_node(node, parent_node, p); - rb_insert_color(node, root); - return NULL; -} - /* insert a new ref to head ref rbtree */ static struct btrfs_delayed_ref_head *htree_insert(struct rb_root *root, struct rb_node *node) @@ -268,7 +188,7 @@ static inline void drop_delayed_ref(struct btrfs_trans_handle *trans, rb_erase(&head->href_node, &delayed_refs->href_root); } else { assert_spin_locked(&head->lock); - rb_erase(&ref->rb_node, &head->ref_root); + list_del(&ref->list); } ref->in_tree = 0; btrfs_put_delayed_ref(ref); @@ -277,99 +197,6 @@ static inline void drop_delayed_ref(struct btrfs_trans_handle *trans, trans->delayed_ref_updates--; } -static int merge_ref(struct btrfs_trans_handle *trans, - struct btrfs_delayed_ref_root *delayed_refs, - struct btrfs_delayed_ref_head *head, - struct btrfs_delayed_ref_node *ref, u64 seq) -{ - struct rb_node *node; - int mod = 0; - int done = 0; - - node = rb_next(&ref->rb_node); - while (!done && node) { - struct btrfs_delayed_ref_node *next; - - next = rb_entry(node, struct btrfs_delayed_ref_node, rb_node); - node = rb_next(node); - if (seq && next->seq >= seq) - break; - if (comp_entry(ref, next, 0)) - continue; - - if (ref->action == next->action) { - mod = next->ref_mod; - } else { - if (ref->ref_mod < next->ref_mod) { - struct btrfs_delayed_ref_node *tmp; - - tmp = ref; - ref = next; - next = tmp; - done = 1; - } - mod = -next->ref_mod; - } - - drop_delayed_ref(trans, delayed_refs, head, next); - ref->ref_mod += mod; - if (ref->ref_mod == 0) { - drop_delayed_ref(trans, delayed_refs, head, ref); - done = 1; - } else { - /* - * You can't have multiples of the same ref on a tree - * block. - */ - WARN_ON(ref->type == BTRFS_TREE_BLOCK_REF_KEY || - ref->type == BTRFS_SHARED_BLOCK_REF_KEY); - } - } - return done; -} - -void btrfs_merge_delayed_refs(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, - struct btrfs_delayed_ref_root *delayed_refs, - struct btrfs_delayed_ref_head *head) -{ - struct rb_node *node; - u64 seq = 0; - - assert_spin_locked(&head->lock); - /* - * We don't have too much refs to merge in the case of delayed data - * refs. - */ - if (head->is_data) - return; - - spin_lock(&fs_info->tree_mod_seq_lock); - if (!list_empty(&fs_info->tree_mod_seq_list)) { - struct seq_list *elem; - - elem = list_first_entry(&fs_info->tree_mod_seq_list, - struct seq_list, list); - seq = elem->seq; - } - spin_unlock(&fs_info->tree_mod_seq_lock); - - node = rb_first(&head->ref_root); - while (node) { - struct btrfs_delayed_ref_node *ref; - - ref = rb_entry(node, struct btrfs_delayed_ref_node, - rb_node); - /* We can't merge refs that are outside of our seq count */ - if (seq && ref->seq >= seq) - break; - if (merge_ref(trans, delayed_refs, head, ref, seq)) - node = rb_first(&head->ref_root); - else - node = rb_next(&ref->rb_node); - } -} - int btrfs_check_delayed_seq(struct btrfs_fs_info *fs_info, struct btrfs_delayed_ref_root *delayed_refs, u64 seq) @@ -443,45 +270,71 @@ again: } /* - * helper function to update an extent delayed ref in the - * rbtree. existing and update must both have the same - * bytenr and parent + * Helper to insert the ref_node to the tail or merge with tail. * - * This may free existing if the update cancels out whatever - * operation it was doing. + * Return 0 for insert. + * Return >0 for merge. */ -static noinline void -update_existing_ref(struct btrfs_trans_handle *trans, - struct btrfs_delayed_ref_root *delayed_refs, - struct btrfs_delayed_ref_head *head, - struct btrfs_delayed_ref_node *existing, - struct btrfs_delayed_ref_node *update) +static int +add_delayed_ref_tail_merge(struct btrfs_trans_handle *trans, + struct btrfs_delayed_ref_root *root, + struct btrfs_delayed_ref_head *href, + struct btrfs_delayed_ref_node *ref) { - if (update->action != existing->action) { - /* - * this is effectively undoing either an add or a - * drop. We decrement the ref_mod, and if it goes - * down to zero we just delete the entry without - * every changing the extent allocation tree. - */ - existing->ref_mod--; - if (existing->ref_mod == 0) - drop_delayed_ref(trans, delayed_refs, head, existing); - else - WARN_ON(existing->type == BTRFS_TREE_BLOCK_REF_KEY || - existing->type == BTRFS_SHARED_BLOCK_REF_KEY); + struct btrfs_delayed_ref_node *exist; + int mod; + int ret = 0; + + spin_lock(&href->lock); + /* Check whether we can merge the tail node with ref */ + if (list_empty(&href->ref_list)) + goto add_tail; + exist = list_entry(href->ref_list.prev, struct btrfs_delayed_ref_node, + list); + /* No need to compare bytenr nor is_head */ + if (exist->type != ref->type || exist->no_quota != ref->no_quota || + exist->seq != ref->seq) + goto add_tail; + + if ((exist->type == BTRFS_TREE_BLOCK_REF_KEY || + exist->type == BTRFS_SHARED_BLOCK_REF_KEY) && + comp_tree_refs(btrfs_delayed_node_to_tree_ref(exist), + btrfs_delayed_node_to_tree_ref(ref), + ref->type)) + goto add_tail; + if ((exist->type == BTRFS_EXTENT_DATA_REF_KEY || + exist->type == BTRFS_SHARED_DATA_REF_KEY) && + comp_data_refs(btrfs_delayed_node_to_data_ref(exist), + btrfs_delayed_node_to_data_ref(ref))) + goto add_tail; + + /* Now we are sure we can merge */ + ret = 1; + if (exist->action == ref->action) { + mod = ref->ref_mod; } else { - WARN_ON(existing->type == BTRFS_TREE_BLOCK_REF_KEY || - existing->type == BTRFS_SHARED_BLOCK_REF_KEY); - /* - * the action on the existing ref matches - * the action on the ref we're trying to add. - * Bump the ref_mod by one so the backref that - * is eventually added/removed has the correct - * reference count - */ - existing->ref_mod += update->ref_mod; + /* Need to change action */ + if (exist->ref_mod < ref->ref_mod) { + exist->action = ref->action; + mod = -exist->ref_mod; + exist->ref_mod = ref->ref_mod; + } else + mod = -ref->ref_mod; } + exist->ref_mod += mod; + + /* remove existing tail if its ref_mod is zero */ + if (exist->ref_mod == 0) + drop_delayed_ref(trans, root, href, exist); + spin_unlock(&href->lock); + return ret; + +add_tail: + list_add_tail(&ref->list, &href->ref_list); + atomic_inc(&root->num_entries); + trans->delayed_ref_updates++; + spin_unlock(&href->lock); + return ret; } /* @@ -568,12 +421,14 @@ update_existing_head_ref(struct btrfs_delayed_ref_root *delayed_refs, static noinline struct btrfs_delayed_ref_head * add_delayed_ref_head(struct btrfs_fs_info *fs_info, struct btrfs_trans_handle *trans, - struct btrfs_delayed_ref_node *ref, u64 bytenr, - u64 num_bytes, int action, int is_data) + struct btrfs_delayed_ref_node *ref, + struct btrfs_qgroup_extent_record *qrecord, + u64 bytenr, u64 num_bytes, int action, int is_data) { struct btrfs_delayed_ref_head *existing; struct btrfs_delayed_ref_head *head_ref = NULL; struct btrfs_delayed_ref_root *delayed_refs; + struct btrfs_qgroup_extent_record *qexisting; int count_mod = 1; int must_insert_reserved = 0; @@ -618,10 +473,22 @@ add_delayed_ref_head(struct btrfs_fs_info *fs_info, head_ref = btrfs_delayed_node_to_head(ref); head_ref->must_insert_reserved = must_insert_reserved; head_ref->is_data = is_data; - head_ref->ref_root = RB_ROOT; + INIT_LIST_HEAD(&head_ref->ref_list); head_ref->processing = 0; head_ref->total_ref_mod = count_mod; + /* Record qgroup extent info if provided */ + if (qrecord) { + qrecord->bytenr = bytenr; + qrecord->num_bytes = num_bytes; + qrecord->old_roots = NULL; + + qexisting = btrfs_qgroup_insert_dirty_extent(delayed_refs, + qrecord); + if (qexisting) + kfree(qrecord); + } + spin_lock_init(&head_ref->lock); mutex_init(&head_ref->mutex); @@ -659,10 +526,10 @@ add_delayed_tree_ref(struct btrfs_fs_info *fs_info, u64 num_bytes, u64 parent, u64 ref_root, int level, int action, int no_quota) { - struct btrfs_delayed_ref_node *existing; struct btrfs_delayed_tree_ref *full_ref; struct btrfs_delayed_ref_root *delayed_refs; u64 seq = 0; + int ret; if (action == BTRFS_ADD_DELAYED_EXTENT) action = BTRFS_ADD_DELAYED_REF; @@ -693,21 +560,14 @@ add_delayed_tree_ref(struct btrfs_fs_info *fs_info, trace_add_delayed_tree_ref(ref, full_ref, action); - spin_lock(&head_ref->lock); - existing = tree_insert(&head_ref->ref_root, &ref->rb_node); - if (existing) { - update_existing_ref(trans, delayed_refs, head_ref, existing, - ref); - /* - * we've updated the existing ref, free the newly - * allocated ref - */ + ret = add_delayed_ref_tail_merge(trans, delayed_refs, head_ref, ref); + + /* + * XXX: memory should be freed at the same level allocated. + * But bad practice is anywhere... Follow it now. Need cleanup. + */ + if (ret > 0) kmem_cache_free(btrfs_delayed_tree_ref_cachep, full_ref); - } else { - atomic_inc(&delayed_refs->num_entries); - trans->delayed_ref_updates++; - } - spin_unlock(&head_ref->lock); } /* @@ -721,10 +581,10 @@ add_delayed_data_ref(struct btrfs_fs_info *fs_info, u64 num_bytes, u64 parent, u64 ref_root, u64 owner, u64 offset, int action, int no_quota) { - struct btrfs_delayed_ref_node *existing; struct btrfs_delayed_data_ref *full_ref; struct btrfs_delayed_ref_root *delayed_refs; u64 seq = 0; + int ret; if (action == BTRFS_ADD_DELAYED_EXTENT) action = BTRFS_ADD_DELAYED_REF; @@ -758,21 +618,10 @@ add_delayed_data_ref(struct btrfs_fs_info *fs_info, trace_add_delayed_data_ref(ref, full_ref, action); - spin_lock(&head_ref->lock); - existing = tree_insert(&head_ref->ref_root, &ref->rb_node); - if (existing) { - update_existing_ref(trans, delayed_refs, head_ref, existing, - ref); - /* - * we've updated the existing ref, free the newly - * allocated ref - */ + ret = add_delayed_ref_tail_merge(trans, delayed_refs, head_ref, ref); + + if (ret > 0) kmem_cache_free(btrfs_delayed_data_ref_cachep, full_ref); - } else { - atomic_inc(&delayed_refs->num_entries); - trans->delayed_ref_updates++; - } - spin_unlock(&head_ref->lock); } /* @@ -790,6 +639,7 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info, struct btrfs_delayed_tree_ref *ref; struct btrfs_delayed_ref_head *head_ref; struct btrfs_delayed_ref_root *delayed_refs; + struct btrfs_qgroup_extent_record *record = NULL; if (!is_fstree(ref_root) || !fs_info->quota_enabled) no_quota = 0; @@ -800,9 +650,13 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info, return -ENOMEM; head_ref = kmem_cache_alloc(btrfs_delayed_ref_head_cachep, GFP_NOFS); - if (!head_ref) { - kmem_cache_free(btrfs_delayed_tree_ref_cachep, ref); - return -ENOMEM; + if (!head_ref) + goto free_ref; + + if (fs_info->quota_enabled && is_fstree(ref_root)) { + record = kmalloc(sizeof(*record), GFP_NOFS); + if (!record) + goto free_head_ref; } head_ref->extent_op = extent_op; @@ -814,7 +668,7 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info, * insert both the head node and the new ref without dropping * the spin lock */ - head_ref = add_delayed_ref_head(fs_info, trans, &head_ref->node, + head_ref = add_delayed_ref_head(fs_info, trans, &head_ref->node, record, bytenr, num_bytes, action, 0); add_delayed_tree_ref(fs_info, trans, head_ref, &ref->node, bytenr, @@ -823,6 +677,13 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info, spin_unlock(&delayed_refs->lock); return 0; + +free_head_ref: + kmem_cache_free(btrfs_delayed_ref_head_cachep, head_ref); +free_ref: + kmem_cache_free(btrfs_delayed_tree_ref_cachep, ref); + + return -ENOMEM; } /* @@ -839,6 +700,7 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, struct btrfs_delayed_data_ref *ref; struct btrfs_delayed_ref_head *head_ref; struct btrfs_delayed_ref_root *delayed_refs; + struct btrfs_qgroup_extent_record *record = NULL; if (!is_fstree(ref_root) || !fs_info->quota_enabled) no_quota = 0; @@ -854,6 +716,16 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, return -ENOMEM; } + if (fs_info->quota_enabled && is_fstree(ref_root)) { + record = kmalloc(sizeof(*record), GFP_NOFS); + if (!record) { + kmem_cache_free(btrfs_delayed_data_ref_cachep, ref); + kmem_cache_free(btrfs_delayed_ref_head_cachep, + head_ref); + return -ENOMEM; + } + } + head_ref->extent_op = extent_op; delayed_refs = &trans->transaction->delayed_refs; @@ -863,7 +735,7 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, * insert both the head node and the new ref without dropping * the spin lock */ - head_ref = add_delayed_ref_head(fs_info, trans, &head_ref->node, + head_ref = add_delayed_ref_head(fs_info, trans, &head_ref->node, record, bytenr, num_bytes, action, 1); add_delayed_data_ref(fs_info, trans, head_ref, &ref->node, bytenr, @@ -891,9 +763,9 @@ int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info, delayed_refs = &trans->transaction->delayed_refs; spin_lock(&delayed_refs->lock); - add_delayed_ref_head(fs_info, trans, &head_ref->node, bytenr, - num_bytes, BTRFS_UPDATE_DELAYED_HEAD, - extent_op->is_data); + add_delayed_ref_head(fs_info, trans, &head_ref->node, NULL, bytenr, + num_bytes, BTRFS_UPDATE_DELAYED_HEAD, + extent_op->is_data); spin_unlock(&delayed_refs->lock); return 0; diff --git a/fs/btrfs/delayed-ref.h b/fs/btrfs/delayed-ref.h index 5eb089239..13fb5e609 100644 --- a/fs/btrfs/delayed-ref.h +++ b/fs/btrfs/delayed-ref.h @@ -24,9 +24,25 @@ #define BTRFS_ADD_DELAYED_EXTENT 3 /* record a full extent allocation */ #define BTRFS_UPDATE_DELAYED_HEAD 4 /* not changing ref count on head ref */ +/* + * XXX: Qu: I really hate the design that ref_head and tree/data ref shares the + * same ref_node structure. + * Ref_head is in a higher logic level than tree/data ref, and duplicated + * bytenr/num_bytes in ref_node is really a waste or memory, they should be + * referred from ref_head. + * This gets more disgusting after we use list to store tree/data ref in + * ref_head. Must clean this mess up later. + */ struct btrfs_delayed_ref_node { + /* + * ref_head use rb tree, stored in ref_root->href. + * indexed by bytenr + */ struct rb_node rb_node; + /*data/tree ref use list, stored in ref_head->ref_list. */ + struct list_head list; + /* the starting bytenr of the extent */ u64 bytenr; @@ -83,7 +99,7 @@ struct btrfs_delayed_ref_head { struct mutex mutex; spinlock_t lock; - struct rb_root ref_root; + struct list_head ref_list; struct rb_node href_node; @@ -132,6 +148,9 @@ struct btrfs_delayed_ref_root { /* head ref rbtree */ struct rb_root href_root; + /* dirty extent records */ + struct rb_root dirty_extent_root; + /* this spin lock protects the rbtree and the entries inside */ spinlock_t lock; @@ -156,6 +175,14 @@ struct btrfs_delayed_ref_root { int flushing; u64 run_delayed_start; + + /* + * To make qgroup to skip given root. + * This is for snapshot, as btrfs_qgroup_inherit() will manully + * modify counters for snapshot and its source, so we should skip + * the snapshot in new_root/old_roots or it will get calculated twice + */ + u64 qgroup_to_skip; }; extern struct kmem_cache *btrfs_delayed_ref_head_cachep; diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index 0573848c7..564a7de17 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -376,6 +376,10 @@ int btrfs_dev_replace_start(struct btrfs_root *root, WARN_ON(!tgt_device); dev_replace->tgtdev = tgt_device; + ret = btrfs_kobj_add_device(tgt_device->fs_devices, tgt_device); + if (ret) + btrfs_err(root->fs_info, "kobj add dev failed %d\n", ret); + printk_in_rcu(KERN_INFO "BTRFS: dev_replace from %s (devid %llu) to %s started\n", src_device->missing ? "<missing disk>" : @@ -583,8 +587,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, mutex_unlock(&uuid_mutex); /* replace the sysfs entry */ - btrfs_kobj_rm_device(fs_info, src_device); - btrfs_kobj_add_device(fs_info, tgt_device); + btrfs_kobj_rm_device(fs_info->fs_devices, src_device); btrfs_rm_dev_replace_free_srcdev(fs_info, src_device); /* write back the superblocks */ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 2ef9a4b72..f556c3732 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1149,12 +1149,12 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr, buf = btrfs_find_create_tree_block(root, bytenr); if (!buf) - return NULL; + return ERR_PTR(-ENOMEM); ret = btree_read_extent_buffer_pages(root, buf, 0, parent_transid); if (ret) { free_extent_buffer(buf); - return NULL; + return ERR_PTR(ret); } return buf; @@ -1509,20 +1509,19 @@ static struct btrfs_root *btrfs_read_tree_root(struct btrfs_root *tree_root, generation = btrfs_root_generation(&root->root_item); root->node = read_tree_block(root, btrfs_root_bytenr(&root->root_item), generation); - if (!root->node) { - ret = -ENOMEM; + if (IS_ERR(root->node)) { + ret = PTR_ERR(root->node); goto find_fail; } else if (!btrfs_buffer_uptodate(root->node, generation, 0)) { ret = -EIO; - goto read_fail; + free_extent_buffer(root->node); + goto find_fail; } root->commit_root = btrfs_root_node(root); out: btrfs_free_path(path); return root; -read_fail: - free_extent_buffer(root->node); find_fail: kfree(root); alloc_fail: @@ -1745,13 +1744,14 @@ static void end_workqueue_fn(struct btrfs_work *work) bio->bi_private = end_io_wq->private; bio->bi_end_io = end_io_wq->end_io; kmem_cache_free(btrfs_end_io_wq_cache, end_io_wq); - bio_endio_nodec(bio, error); + bio_endio(bio, error); } static int cleaner_kthread(void *arg) { struct btrfs_root *root = arg; int again; + struct btrfs_trans_handle *trans; do { again = 0; @@ -1773,7 +1773,6 @@ static int cleaner_kthread(void *arg) } btrfs_run_delayed_iputs(root); - btrfs_delete_unused_bgs(root->fs_info); again = btrfs_clean_one_deleted_snapshot(root); mutex_unlock(&root->fs_info->cleaner_mutex); @@ -1782,6 +1781,16 @@ static int cleaner_kthread(void *arg) * needn't do anything special here. */ btrfs_run_defrag_inodes(root->fs_info); + + /* + * Acquires fs_info->delete_unused_bgs_mutex to avoid racing + * with relocation (btrfs_relocate_chunk) and relocation + * acquires fs_info->cleaner_mutex (btrfs_relocate_block_group) + * after acquiring fs_info->delete_unused_bgs_mutex. So we + * can't hold, nor need to, fs_info->cleaner_mutex when deleting + * unused block groups. + */ + btrfs_delete_unused_bgs(root->fs_info); sleep: if (!try_to_freeze() && !again) { set_current_state(TASK_INTERRUPTIBLE); @@ -1790,6 +1799,34 @@ sleep: __set_current_state(TASK_RUNNING); } } while (!kthread_should_stop()); + + /* + * Transaction kthread is stopped before us and wakes us up. + * However we might have started a new transaction and COWed some + * tree blocks when deleting unused block groups for example. So + * make sure we commit the transaction we started to have a clean + * shutdown when evicting the btree inode - if it has dirty pages + * when we do the final iput() on it, eviction will trigger a + * writeback for it which will fail with null pointer dereferences + * since work queues and other resources were already released and + * destroyed by the time the iput/eviction/writeback is made. + */ + trans = btrfs_attach_transaction(root); + if (IS_ERR(trans)) { + if (PTR_ERR(trans) != -ENOENT) + btrfs_err(root->fs_info, + "cleaner transaction attach returned %ld", + PTR_ERR(trans)); + } else { + int ret; + + ret = btrfs_commit_transaction(trans, root); + if (ret) + btrfs_err(root->fs_info, + "cleaner open transaction commit returned %d", + ret); + } + return 0; } @@ -2320,8 +2357,12 @@ static int btrfs_replay_log(struct btrfs_fs_info *fs_info, log_tree_root->node = read_tree_block(tree_root, bytenr, fs_info->generation + 1); - if (!log_tree_root->node || - !extent_buffer_uptodate(log_tree_root->node)) { + if (IS_ERR(log_tree_root->node)) { + printk(KERN_ERR "BTRFS: failed to read log tree\n"); + ret = PTR_ERR(log_tree_root->node); + kfree(log_tree_root); + return ret; + } else if (!extent_buffer_uptodate(log_tree_root->node)) { printk(KERN_ERR "BTRFS: failed to read log tree\n"); free_extent_buffer(log_tree_root->node); kfree(log_tree_root); @@ -2489,12 +2530,12 @@ int open_ctree(struct super_block *sb, spin_lock_init(&fs_info->unused_bgs_lock); rwlock_init(&fs_info->tree_mod_log_lock); mutex_init(&fs_info->unused_bg_unpin_mutex); + mutex_init(&fs_info->delete_unused_bgs_mutex); mutex_init(&fs_info->reloc_mutex); mutex_init(&fs_info->delalloc_root_mutex); seqlock_init(&fs_info->profiles_lock); init_rwsem(&fs_info->delayed_iput_sem); - init_completion(&fs_info->kobj_unregister); INIT_LIST_HEAD(&fs_info->dirty_cowonly_roots); INIT_LIST_HEAD(&fs_info->space_info); INIT_LIST_HEAD(&fs_info->tree_mod_seq_list); @@ -2797,10 +2838,11 @@ int open_ctree(struct super_block *sb, chunk_root->node = read_tree_block(chunk_root, btrfs_super_chunk_root(disk_super), generation); - if (!chunk_root->node || - !test_bit(EXTENT_BUFFER_UPTODATE, &chunk_root->node->bflags)) { + if (IS_ERR(chunk_root->node) || + !extent_buffer_uptodate(chunk_root->node)) { printk(KERN_ERR "BTRFS: failed to read chunk root on %s\n", sb->s_id); + chunk_root->node = NULL; goto fail_tree_roots; } btrfs_set_root_node(&chunk_root->root_item, chunk_root->node); @@ -2834,11 +2876,11 @@ retry_root_backup: tree_root->node = read_tree_block(tree_root, btrfs_super_root(disk_super), generation); - if (!tree_root->node || - !test_bit(EXTENT_BUFFER_UPTODATE, &tree_root->node->bflags)) { + if (IS_ERR(tree_root->node) || + !extent_buffer_uptodate(tree_root->node)) { printk(KERN_WARNING "BTRFS: failed to read tree root on %s\n", sb->s_id); - + tree_root->node = NULL; goto recovery_tree_root; } @@ -2874,10 +2916,22 @@ retry_root_backup: btrfs_close_extra_devices(fs_devices, 1); + ret = btrfs_sysfs_add_fsid(fs_devices, NULL); + if (ret) { + pr_err("BTRFS: failed to init sysfs fsid interface: %d\n", ret); + goto fail_block_groups; + } + + ret = btrfs_sysfs_add_device(fs_devices); + if (ret) { + pr_err("BTRFS: failed to init sysfs device interface: %d\n", ret); + goto fail_fsdev_sysfs; + } + ret = btrfs_sysfs_add_one(fs_info); if (ret) { pr_err("BTRFS: failed to init sysfs interface: %d\n", ret); - goto fail_block_groups; + goto fail_fsdev_sysfs; } ret = btrfs_init_space_info(fs_info); @@ -3055,6 +3109,9 @@ fail_cleaner: fail_sysfs: btrfs_sysfs_remove_one(fs_info); +fail_fsdev_sysfs: + btrfs_sysfs_remove_fsid(fs_info->fs_devices); + fail_block_groups: btrfs_put_block_group_cache(fs_info); btrfs_free_block_groups(fs_info); @@ -3269,11 +3326,8 @@ static int write_dev_supers(struct btrfs_device *device, */ static void btrfs_end_empty_barrier(struct bio *bio, int err) { - if (err) { - if (err == -EOPNOTSUPP) - set_bit(BIO_EOPNOTSUPP, &bio->bi_flags); + if (err) clear_bit(BIO_UPTODATE, &bio->bi_flags); - } if (bio->bi_private) complete(bio->bi_private); bio_put(bio); @@ -3301,11 +3355,7 @@ static int write_dev_flush(struct btrfs_device *device, int wait) wait_for_completion(&device->flush_wait); - if (bio_flagged(bio, BIO_EOPNOTSUPP)) { - printk_in_rcu("BTRFS: disabling barriers on dev %s\n", - rcu_str_deref(device->name)); - device->nobarriers = 1; - } else if (!bio_flagged(bio, BIO_UPTODATE)) { + if (!bio_flagged(bio, BIO_UPTODATE)) { ret = -EIO; btrfs_dev_stat_inc_and_print(device, BTRFS_DEV_STAT_FLUSH_ERRS); @@ -3732,6 +3782,7 @@ void close_ctree(struct btrfs_root *root) } btrfs_sysfs_remove_one(fs_info); + btrfs_sysfs_remove_fsid(fs_info->fs_devices); btrfs_free_fs_roots(fs_info); @@ -4060,6 +4111,7 @@ static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans, while ((node = rb_first(&delayed_refs->href_root)) != NULL) { struct btrfs_delayed_ref_head *head; + struct btrfs_delayed_ref_node *tmp; bool pin_bytes = false; head = rb_entry(node, struct btrfs_delayed_ref_head, @@ -4075,11 +4127,10 @@ static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans, continue; } spin_lock(&head->lock); - while ((node = rb_first(&head->ref_root)) != NULL) { - ref = rb_entry(node, struct btrfs_delayed_ref_node, - rb_node); + list_for_each_entry_safe_reverse(ref, tmp, &head->ref_list, + list) { ref->in_tree = 0; - rb_erase(&ref->rb_node, &head->ref_root); + list_del(&ref->list); atomic_dec(&delayed_refs->num_entries); btrfs_put_delayed_ref(ref); } diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 0ec3acd14..07204bf60 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -79,11 +79,10 @@ static int update_block_group(struct btrfs_trans_handle *trans, u64 num_bytes, int alloc); static int __btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, - u64 bytenr, u64 num_bytes, u64 parent, + struct btrfs_delayed_ref_node *node, u64 parent, u64 root_objectid, u64 owner_objectid, u64 owner_offset, int refs_to_drop, - struct btrfs_delayed_extent_op *extra_op, - int no_quota); + struct btrfs_delayed_extent_op *extra_op); static void __run_delayed_extent_op(struct btrfs_delayed_extent_op *extent_op, struct extent_buffer *leaf, struct btrfs_extent_item *ei); @@ -1967,10 +1966,9 @@ int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, - u64 bytenr, u64 num_bytes, + struct btrfs_delayed_ref_node *node, u64 parent, u64 root_objectid, u64 owner, u64 offset, int refs_to_add, - int no_quota, struct btrfs_delayed_extent_op *extent_op) { struct btrfs_fs_info *fs_info = root->fs_info; @@ -1978,9 +1976,11 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, struct extent_buffer *leaf; struct btrfs_extent_item *item; struct btrfs_key key; + u64 bytenr = node->bytenr; + u64 num_bytes = node->num_bytes; u64 refs; int ret; - enum btrfs_qgroup_operation_type type = BTRFS_QGROUP_OPER_ADD_EXCL; + int no_quota = node->no_quota; path = btrfs_alloc_path(); if (!path) @@ -1996,26 +1996,8 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, bytenr, num_bytes, parent, root_objectid, owner, offset, refs_to_add, extent_op); - if ((ret < 0 && ret != -EAGAIN) || (!ret && no_quota)) + if ((ret < 0 && ret != -EAGAIN) || !ret) goto out; - /* - * Ok we were able to insert an inline extent and it appears to be a new - * reference, deal with the qgroup accounting. - */ - if (!ret && !no_quota) { - ASSERT(root->fs_info->quota_enabled); - leaf = path->nodes[0]; - btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); - item = btrfs_item_ptr(leaf, path->slots[0], - struct btrfs_extent_item); - if (btrfs_extent_refs(leaf, item) > (u64)refs_to_add) - type = BTRFS_QGROUP_OPER_ADD_SHARED; - btrfs_release_path(path); - - ret = btrfs_qgroup_record_ref(trans, fs_info, root_objectid, - bytenr, num_bytes, type, 0); - goto out; - } /* * Ok we had -EAGAIN which means we didn't have space to insert and @@ -2026,8 +2008,6 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); refs = btrfs_extent_refs(leaf, item); - if (refs) - type = BTRFS_QGROUP_OPER_ADD_SHARED; btrfs_set_extent_refs(leaf, item, refs + refs_to_add); if (extent_op) __run_delayed_extent_op(extent_op, leaf, item); @@ -2035,13 +2015,6 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, btrfs_mark_buffer_dirty(leaf); btrfs_release_path(path); - if (!no_quota) { - ret = btrfs_qgroup_record_ref(trans, fs_info, root_objectid, - bytenr, num_bytes, type, 0); - if (ret) - goto out; - } - path->reada = 1; path->leave_spinning = 1; /* now insert the actual backref */ @@ -2087,17 +2060,15 @@ static int run_delayed_data_ref(struct btrfs_trans_handle *trans, ref->objectid, ref->offset, &ins, node->ref_mod); } else if (node->action == BTRFS_ADD_DELAYED_REF) { - ret = __btrfs_inc_extent_ref(trans, root, node->bytenr, - node->num_bytes, parent, + ret = __btrfs_inc_extent_ref(trans, root, node, parent, ref_root, ref->objectid, ref->offset, node->ref_mod, - node->no_quota, extent_op); + extent_op); } else if (node->action == BTRFS_DROP_DELAYED_REF) { - ret = __btrfs_free_extent(trans, root, node->bytenr, - node->num_bytes, parent, + ret = __btrfs_free_extent(trans, root, node, parent, ref_root, ref->objectid, ref->offset, node->ref_mod, - extent_op, node->no_quota); + extent_op); } else { BUG(); } @@ -2255,15 +2226,14 @@ static int run_delayed_tree_ref(struct btrfs_trans_handle *trans, ref->level, &ins, node->no_quota); } else if (node->action == BTRFS_ADD_DELAYED_REF) { - ret = __btrfs_inc_extent_ref(trans, root, node->bytenr, - node->num_bytes, parent, ref_root, - ref->level, 0, 1, node->no_quota, + ret = __btrfs_inc_extent_ref(trans, root, node, + parent, ref_root, + ref->level, 0, 1, extent_op); } else if (node->action == BTRFS_DROP_DELAYED_REF) { - ret = __btrfs_free_extent(trans, root, node->bytenr, - node->num_bytes, parent, ref_root, - ref->level, 0, 1, extent_op, - node->no_quota); + ret = __btrfs_free_extent(trans, root, node, + parent, ref_root, + ref->level, 0, 1, extent_op); } else { BUG(); } @@ -2323,28 +2293,27 @@ static int run_one_delayed_ref(struct btrfs_trans_handle *trans, return ret; } -static noinline struct btrfs_delayed_ref_node * +static inline struct btrfs_delayed_ref_node * select_delayed_ref(struct btrfs_delayed_ref_head *head) { - struct rb_node *node; - struct btrfs_delayed_ref_node *ref, *last = NULL;; + struct btrfs_delayed_ref_node *ref; + + if (list_empty(&head->ref_list)) + return NULL; /* - * select delayed ref of type BTRFS_ADD_DELAYED_REF first. - * this prevents ref count from going down to zero when - * there still are pending delayed ref. + * Select a delayed ref of type BTRFS_ADD_DELAYED_REF first. + * This is to prevent a ref count from going down to zero, which deletes + * the extent item from the extent tree, when there still are references + * to add, which would fail because they would not find the extent item. */ - node = rb_first(&head->ref_root); - while (node) { - ref = rb_entry(node, struct btrfs_delayed_ref_node, - rb_node); + list_for_each_entry(ref, &head->ref_list, list) { if (ref->action == BTRFS_ADD_DELAYED_REF) return ref; - else if (last == NULL) - last = ref; - node = rb_next(node); } - return last; + + return list_entry(head->ref_list.next, struct btrfs_delayed_ref_node, + list); } /* @@ -2396,16 +2365,7 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, } } - /* - * We need to try and merge add/drops of the same ref since we - * can run into issues with relocate dropping the implicit ref - * and then it being added back again before the drop can - * finish. If we merged anything we need to re-loop so we can - * get a good ref. - */ spin_lock(&locked_ref->lock); - btrfs_merge_delayed_refs(trans, fs_info, delayed_refs, - locked_ref); /* * locked_ref is the head node, so we have to go one @@ -2482,7 +2442,7 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, spin_unlock(&locked_ref->lock); spin_lock(&delayed_refs->lock); spin_lock(&locked_ref->lock); - if (rb_first(&locked_ref->ref_root) || + if (!list_empty(&locked_ref->ref_list) || locked_ref->extent_op) { spin_unlock(&locked_ref->lock); spin_unlock(&delayed_refs->lock); @@ -2496,7 +2456,7 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, } else { actual_count++; ref->in_tree = 0; - rb_erase(&ref->rb_node, &locked_ref->ref_root); + list_del(&ref->list); } atomic_dec(&delayed_refs->num_entries); @@ -2864,9 +2824,6 @@ again: goto again; } out: - ret = btrfs_delayed_qgroup_accounting(trans, root->fs_info); - if (ret) - return ret; assert_qgroups_uptodate(trans); return 0; } @@ -2905,7 +2862,6 @@ static noinline int check_delayed_ref(struct btrfs_trans_handle *trans, struct btrfs_delayed_ref_node *ref; struct btrfs_delayed_data_ref *data_ref; struct btrfs_delayed_ref_root *delayed_refs; - struct rb_node *node; int ret = 0; delayed_refs = &trans->transaction->delayed_refs; @@ -2934,11 +2890,7 @@ static noinline int check_delayed_ref(struct btrfs_trans_handle *trans, spin_unlock(&delayed_refs->lock); spin_lock(&head->lock); - node = rb_first(&head->ref_root); - while (node) { - ref = rb_entry(node, struct btrfs_delayed_ref_node, rb_node); - node = rb_next(node); - + list_for_each_entry(ref, &head->ref_list, list) { /* If it's a shared ref we know a cross reference exists */ if (ref->type != BTRFS_EXTENT_DATA_REF_KEY) { ret = 1; @@ -3693,7 +3645,8 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags, found->disk_total += total_bytes * factor; found->bytes_used += bytes_used; found->disk_used += bytes_used * factor; - found->full = 0; + if (total_bytes > 0) + found->full = 0; spin_unlock(&found->lock); *space_info = found; return 0; @@ -3721,7 +3674,10 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags, found->bytes_reserved = 0; found->bytes_readonly = 0; found->bytes_may_use = 0; - found->full = 0; + if (total_bytes > 0) + found->full = 0; + else + found->full = 1; found->force_alloc = CHUNK_ALLOC_NO_FORCE; found->chunk_alloc = 0; found->flush = 0; @@ -3975,6 +3931,9 @@ commit_trans: !atomic_read(&root->fs_info->open_ioctl_trans)) { need_commit--; + if (need_commit > 0) + btrfs_wait_ordered_roots(fs_info, -1); + trans = btrfs_join_transaction(root); if (IS_ERR(trans)) return PTR_ERR(trans); @@ -4088,7 +4047,7 @@ static int should_alloc_chunk(struct btrfs_root *root, return 1; } -static u64 get_system_chunk_thresh(struct btrfs_root *root, u64 type) +static u64 get_profile_num_devs(struct btrfs_root *root, u64 type) { u64 num_dev; @@ -4102,24 +4061,43 @@ static u64 get_system_chunk_thresh(struct btrfs_root *root, u64 type) else num_dev = 1; /* DUP or single */ - /* metadata for updaing devices and chunk tree */ - return btrfs_calc_trans_metadata_size(root, num_dev + 1); + return num_dev; } -static void check_system_chunk(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 type) +/* + * If @is_allocation is true, reserve space in the system space info necessary + * for allocating a chunk, otherwise if it's false, reserve space necessary for + * removing a chunk. + */ +void check_system_chunk(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 type) { struct btrfs_space_info *info; u64 left; u64 thresh; + int ret = 0; + u64 num_devs; + + /* + * Needed because we can end up allocating a system chunk and for an + * atomic and race free space reservation in the chunk block reserve. + */ + ASSERT(mutex_is_locked(&root->fs_info->chunk_mutex)); info = __find_space_info(root->fs_info, BTRFS_BLOCK_GROUP_SYSTEM); spin_lock(&info->lock); left = info->total_bytes - info->bytes_used - info->bytes_pinned - - info->bytes_reserved - info->bytes_readonly; + info->bytes_reserved - info->bytes_readonly - + info->bytes_may_use; spin_unlock(&info->lock); - thresh = get_system_chunk_thresh(root, type); + num_devs = get_profile_num_devs(root, type); + + /* num_devs device items to update and 1 chunk item to add or remove */ + thresh = btrfs_calc_trunc_metadata_size(root, num_devs) + + btrfs_calc_trans_metadata_size(root, 1); + if (left < thresh && btrfs_test_opt(root, ENOSPC_DEBUG)) { btrfs_info(root->fs_info, "left=%llu, need=%llu, flags=%llu", left, thresh, type); @@ -4130,7 +4108,21 @@ static void check_system_chunk(struct btrfs_trans_handle *trans, u64 flags; flags = btrfs_get_alloc_profile(root->fs_info->chunk_root, 0); - btrfs_alloc_chunk(trans, root, flags); + /* + * Ignore failure to create system chunk. We might end up not + * needing it, as we might not need to COW all nodes/leafs from + * the paths we visit in the chunk tree (they were already COWed + * or created in the current transaction for example). + */ + ret = btrfs_alloc_chunk(trans, root, flags); + } + + if (!ret) { + ret = btrfs_block_rsv_add(root->fs_info->chunk_root, + &root->fs_info->chunk_block_rsv, + thresh, BTRFS_RESERVE_NO_FLUSH); + if (!ret) + trans->chunk_bytes_reserved += thresh; } } @@ -4235,6 +4227,24 @@ out: space_info->chunk_alloc = 0; spin_unlock(&space_info->lock); mutex_unlock(&fs_info->chunk_mutex); + /* + * When we allocate a new chunk we reserve space in the chunk block + * reserve to make sure we can COW nodes/leafs in the chunk tree or + * add new nodes/leafs to it if we end up needing to do it when + * inserting the chunk item and updating device items as part of the + * second phase of chunk allocation, performed by + * btrfs_finish_chunk_alloc(). So make sure we don't accumulate a + * large number of new block groups to create in our transaction + * handle's new_bgs list to avoid exhausting the chunk block reserve + * in extreme cases - like having a single transaction create many new + * block groups when starting to write out the free space caches of all + * the block groups that were made dirty during the lifetime of the + * transaction. + */ + if (trans->chunk_bytes_reserved >= (2 * 1024 * 1024ull)) { + btrfs_create_pending_block_groups(trans, trans->root); + btrfs_trans_release_chunk_metadata(trans); + } return ret; } @@ -5188,6 +5198,24 @@ void btrfs_trans_release_metadata(struct btrfs_trans_handle *trans, trans->bytes_reserved = 0; } +/* + * To be called after all the new block groups attached to the transaction + * handle have been created (btrfs_create_pending_block_groups()). + */ +void btrfs_trans_release_chunk_metadata(struct btrfs_trans_handle *trans) +{ + struct btrfs_fs_info *fs_info = trans->root->fs_info; + + if (!trans->chunk_bytes_reserved) + return; + + WARN_ON_ONCE(!list_empty(&trans->new_bgs)); + + block_rsv_release_bytes(fs_info, &fs_info->chunk_block_rsv, NULL, + trans->chunk_bytes_reserved); + trans->chunk_bytes_reserved = 0; +} + /* Can only return 0 or -ENOSPC */ int btrfs_orphan_reserve_metadata(struct btrfs_trans_handle *trans, struct inode *inode) @@ -6092,11 +6120,10 @@ static void add_pinned_bytes(struct btrfs_fs_info *fs_info, u64 num_bytes, static int __btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, - u64 bytenr, u64 num_bytes, u64 parent, + struct btrfs_delayed_ref_node *node, u64 parent, u64 root_objectid, u64 owner_objectid, u64 owner_offset, int refs_to_drop, - struct btrfs_delayed_extent_op *extent_op, - int no_quota) + struct btrfs_delayed_extent_op *extent_op) { struct btrfs_key key; struct btrfs_path *path; @@ -6110,10 +6137,12 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, int extent_slot = 0; int found_extent = 0; int num_to_del = 1; + int no_quota = node->no_quota; u32 item_size; u64 refs; + u64 bytenr = node->bytenr; + u64 num_bytes = node->num_bytes; int last_ref = 0; - enum btrfs_qgroup_operation_type type = BTRFS_QGROUP_OPER_SUB_EXCL; bool skinny_metadata = btrfs_fs_incompat(root->fs_info, SKINNY_METADATA); @@ -6294,7 +6323,6 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, refs -= refs_to_drop; if (refs > 0) { - type = BTRFS_QGROUP_OPER_SUB_SHARED; if (extent_op) __run_delayed_extent_op(extent_op, leaf, ei); /* @@ -6356,18 +6384,6 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, } btrfs_release_path(path); - /* Deal with the quota accounting */ - if (!ret && last_ref && !no_quota) { - int mod_seq = 0; - - if (owner_objectid >= BTRFS_FIRST_FREE_OBJECTID && - type == BTRFS_QGROUP_OPER_SUB_SHARED) - mod_seq = 1; - - ret = btrfs_qgroup_record_ref(trans, info, root_objectid, - bytenr, num_bytes, type, - mod_seq); - } out: btrfs_free_path(path); return ret; @@ -6393,7 +6409,7 @@ static noinline int check_ref_cleanup(struct btrfs_trans_handle *trans, goto out_delayed_unlock; spin_lock(&head->lock); - if (rb_first(&head->ref_root)) + if (!list_empty(&head->ref_list)) goto out; if (head->extent_op) { @@ -7303,13 +7319,6 @@ static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans, btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_free_path(path); - /* Always set parent to 0 here since its exclusive anyway. */ - ret = btrfs_qgroup_record_ref(trans, fs_info, root_objectid, - ins->objectid, ins->offset, - BTRFS_QGROUP_OPER_ADD_EXCL, 0); - if (ret) - return ret; - ret = update_block_group(trans, root, ins->objectid, ins->offset, 1); if (ret) { /* -ENOENT, logic error */ btrfs_err(fs_info, "update block group failed for %llu %llu", @@ -7391,14 +7400,6 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, btrfs_mark_buffer_dirty(leaf); btrfs_free_path(path); - if (!no_quota) { - ret = btrfs_qgroup_record_ref(trans, fs_info, root_objectid, - ins->objectid, num_bytes, - BTRFS_QGROUP_OPER_ADD_EXCL, 0); - if (ret) - return ret; - } - ret = update_block_group(trans, root, ins->objectid, root->nodesize, 1); if (ret) { /* -ENOENT, logic error */ @@ -7755,12 +7756,18 @@ reada: wc->reada_slot = slot; } +/* + * TODO: Modify related function to add related node/leaf to dirty_extent_root, + * for later qgroup accounting. + * + * Current, this function does nothing. + */ static int account_leaf_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct extent_buffer *eb) { int nr = btrfs_header_nritems(eb); - int i, extent_type, ret; + int i, extent_type; struct btrfs_key key; struct btrfs_file_extent_item *fi; u64 bytenr, num_bytes; @@ -7783,13 +7790,6 @@ static int account_leaf_items(struct btrfs_trans_handle *trans, continue; num_bytes = btrfs_file_extent_disk_num_bytes(eb, fi); - - ret = btrfs_qgroup_record_ref(trans, root->fs_info, - root->objectid, - bytenr, num_bytes, - BTRFS_QGROUP_OPER_SUB_SUBTREE, 0); - if (ret) - return ret; } return 0; } @@ -7858,6 +7858,8 @@ static int adjust_slots_upwards(struct btrfs_root *root, /* * root_eb is the subtree root and is locked before this function is called. + * TODO: Modify this function to mark all (including complete shared node) + * to dirty_extent_root to allow it get accounted in qgroup. */ static int account_shared_subtree(struct btrfs_trans_handle *trans, struct btrfs_root *root, @@ -7920,7 +7922,11 @@ walk_down: child_gen = btrfs_node_ptr_generation(eb, parent_slot); eb = read_tree_block(root, child_bytenr, child_gen); - if (!eb || !extent_buffer_uptodate(eb)) { + if (IS_ERR(eb)) { + ret = PTR_ERR(eb); + goto out; + } else if (!extent_buffer_uptodate(eb)) { + free_extent_buffer(eb); ret = -EIO; goto out; } @@ -7931,16 +7937,6 @@ walk_down: btrfs_tree_read_lock(eb); btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK); path->locks[level] = BTRFS_READ_LOCK_BLOCKING; - - ret = btrfs_qgroup_record_ref(trans, root->fs_info, - root->objectid, - child_bytenr, - root->nodesize, - BTRFS_QGROUP_OPER_SUB_SUBTREE, - 0); - if (ret) - goto out; - } if (level == 0) { @@ -8151,7 +8147,9 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, if (reada && level == 1) reada_walk_down(trans, root, wc, path); next = read_tree_block(root, bytenr, generation); - if (!next || !extent_buffer_uptodate(next)) { + if (IS_ERR(next)) { + return PTR_ERR(next); + } else if (!extent_buffer_uptodate(next)) { free_extent_buffer(next); return -EIO; } @@ -8533,24 +8531,6 @@ int btrfs_drop_snapshot(struct btrfs_root *root, goto out_end_trans; } - /* - * Qgroup update accounting is run from - * delayed ref handling. This usually works - * out because delayed refs are normally the - * only way qgroup updates are added. However, - * we may have added updates during our tree - * walk so run qgroups here to make sure we - * don't lose any updates. - */ - ret = btrfs_delayed_qgroup_accounting(trans, - root->fs_info); - if (ret) - printk_ratelimited(KERN_ERR "BTRFS: Failure %d " - "running qgroup updates " - "during snapshot delete. " - "Quota is out of sync, " - "rescan required.\n", ret); - btrfs_end_transaction_throttle(trans, tree_root); if (!for_reloc && btrfs_need_cleaner_sleep(root)) { pr_debug("BTRFS: drop snapshot early exit\n"); @@ -8604,14 +8584,6 @@ int btrfs_drop_snapshot(struct btrfs_root *root, } root_dropped = true; out_end_trans: - ret = btrfs_delayed_qgroup_accounting(trans, tree_root->fs_info); - if (ret) - printk_ratelimited(KERN_ERR "BTRFS: Failure %d " - "running qgroup updates " - "during snapshot delete. " - "Quota is out of sync, " - "rescan required.\n", ret); - btrfs_end_transaction_throttle(trans, tree_root); out_free: kfree(wc); @@ -9562,6 +9534,19 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans, free_excluded_extents(root, cache); + /* + * Call to ensure the corresponding space_info object is created and + * assigned to our block group, but don't update its counters just yet. + * We want our bg to be added to the rbtree with its ->space_info set. + */ + ret = update_space_info(root->fs_info, cache->flags, 0, 0, + &cache->space_info); + if (ret) { + btrfs_remove_free_space_cache(cache); + btrfs_put_block_group(cache); + return ret; + } + ret = btrfs_add_block_group_cache(root->fs_info, cache); if (ret) { btrfs_remove_free_space_cache(cache); @@ -9569,6 +9554,10 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans, return ret; } + /* + * Now that our block group has its ->space_info set and is inserted in + * the rbtree, update the space info's counters. + */ ret = update_space_info(root->fs_info, cache->flags, size, bytes_used, &cache->space_info); if (ret) { @@ -9931,6 +9920,8 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info) } spin_unlock(&fs_info->unused_bgs_lock); + mutex_lock(&root->fs_info->delete_unused_bgs_mutex); + /* Don't want to race with allocators so take the groups_sem */ down_write(&space_info->groups_sem); spin_lock(&block_group->lock); @@ -10025,6 +10016,7 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info) end_trans: btrfs_end_transaction(trans, root); next: + mutex_unlock(&root->fs_info->delete_unused_bgs_mutex); btrfs_put_block_group(block_group); spin_lock(&fs_info->unused_bgs_lock); } diff --git a/fs/btrfs/extent-tree.h b/fs/btrfs/extent-tree.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/fs/btrfs/extent-tree.h diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index c32d226bf..02d05817c 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -1277,7 +1277,12 @@ int set_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, int clear_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, unsigned bits, gfp_t mask) { - return clear_extent_bit(tree, start, end, bits, 0, 0, NULL, mask); + int wake = 0; + + if (bits & EXTENT_LOCKED) + wake = 1; + + return clear_extent_bit(tree, start, end, bits, wake, 0, NULL, mask); } int set_extent_delalloc(struct extent_io_tree *tree, u64 start, u64 end, @@ -2767,8 +2772,6 @@ static int __must_check submit_one_bio(int rw, struct bio *bio, else btrfsic_submit_bio(rw, bio); - if (bio_flagged(bio, BIO_EOPNOTSUPP)) - ret = -EOPNOTSUPP; bio_put(bio); return ret; } @@ -4492,6 +4495,8 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, } if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)) flags |= FIEMAP_EXTENT_ENCODED; + if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) + flags |= FIEMAP_EXTENT_UNWRITTEN; free_extent_map(em); em = NULL; diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index b072e1747..b823fac91 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1748,7 +1748,7 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb, } current->backing_dev_info = inode_to_bdi(inode); - err = file_remove_suid(file); + err = file_remove_privs(file); if (err) { mutex_unlock(&inode->i_mutex); goto out; @@ -1868,6 +1868,7 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) struct btrfs_log_ctx ctx; int ret = 0; bool full_sync = 0; + const u64 len = end - start + 1; trace_btrfs_sync_file(file, datasync); @@ -1896,7 +1897,7 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) * all extents are persisted and the respective file extent * items are in the fs/subvol btree. */ - ret = btrfs_wait_ordered_range(inode, start, end - start + 1); + ret = btrfs_wait_ordered_range(inode, start, len); } else { /* * Start any new ordered operations before starting to log the @@ -1968,8 +1969,10 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) */ smp_mb(); if (btrfs_inode_in_log(inode, root->fs_info->generation) || - (full_sync && BTRFS_I(inode)->last_trans <= - root->fs_info->last_trans_committed)) { + (BTRFS_I(inode)->last_trans <= + root->fs_info->last_trans_committed && + (full_sync || + !btrfs_have_ordered_extents_in_range(inode, start, len)))) { /* * We'v had everything committed since the last time we were * modified so clear this flag in case it was set for whatever diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 9dbe5b548..fb5a6b1c6 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -231,6 +231,7 @@ int btrfs_truncate_free_space_cache(struct btrfs_root *root, { int ret = 0; struct btrfs_path *path = btrfs_alloc_path(); + bool locked = false; if (!path) { ret = -ENOMEM; @@ -238,6 +239,7 @@ int btrfs_truncate_free_space_cache(struct btrfs_root *root, } if (block_group) { + locked = true; mutex_lock(&trans->transaction->cache_write_mutex); if (!list_empty(&block_group->io_list)) { list_del_init(&block_group->io_list); @@ -269,18 +271,14 @@ int btrfs_truncate_free_space_cache(struct btrfs_root *root, */ ret = btrfs_truncate_inode_items(trans, root, inode, 0, BTRFS_EXTENT_DATA_KEY); - if (ret) { - mutex_unlock(&trans->transaction->cache_write_mutex); - btrfs_abort_transaction(trans, root, ret); - return ret; - } + if (ret) + goto fail; ret = btrfs_update_inode(trans, root, inode); - if (block_group) - mutex_unlock(&trans->transaction->cache_write_mutex); - fail: + if (locked) + mutex_unlock(&trans->transaction->cache_write_mutex); if (ret) btrfs_abort_transaction(trans, root, ret); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 8bb013672..e33dff356 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4209,7 +4209,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, u64 extent_num_bytes = 0; u64 extent_offset = 0; u64 item_end = 0; - u64 last_size = (u64)-1; + u64 last_size = new_size; u32 found_type = (u8)-1; int found_extent; int del_item; @@ -4493,8 +4493,7 @@ out: btrfs_abort_transaction(trans, root, ret); } error: - if (last_size != (u64)-1 && - root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID) + if (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID) btrfs_ordered_update_i_size(inode, last_size, NULL); btrfs_free_path(path); @@ -4986,24 +4985,41 @@ static void evict_inode_truncate_pages(struct inode *inode) } write_unlock(&map_tree->lock); + /* + * Keep looping until we have no more ranges in the io tree. + * We can have ongoing bios started by readpages (called from readahead) + * that have their endio callback (extent_io.c:end_bio_extent_readpage) + * still in progress (unlocked the pages in the bio but did not yet + * unlocked the ranges in the io tree). Therefore this means some + * ranges can still be locked and eviction started because before + * submitting those bios, which are executed by a separate task (work + * queue kthread), inode references (inode->i_count) were not taken + * (which would be dropped in the end io callback of each bio). + * Therefore here we effectively end up waiting for those bios and + * anyone else holding locked ranges without having bumped the inode's + * reference count - if we don't do it, when they access the inode's + * io_tree to unlock a range it may be too late, leading to an + * use-after-free issue. + */ spin_lock(&io_tree->lock); while (!RB_EMPTY_ROOT(&io_tree->state)) { struct extent_state *state; struct extent_state *cached_state = NULL; + u64 start; + u64 end; node = rb_first(&io_tree->state); state = rb_entry(node, struct extent_state, rb_node); - atomic_inc(&state->refs); + start = state->start; + end = state->end; spin_unlock(&io_tree->lock); - lock_extent_bits(io_tree, state->start, state->end, - 0, &cached_state); - clear_extent_bit(io_tree, state->start, state->end, + lock_extent_bits(io_tree, start, end, 0, &cached_state); + clear_extent_bit(io_tree, start, end, EXTENT_LOCKED | EXTENT_DIRTY | EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, 1, 1, &cached_state, GFP_NOFS); - free_extent_state(state); cond_resched(); spin_lock(&io_tree->lock); @@ -7530,6 +7546,7 @@ unlock: current->journal_info = outstanding_extents; btrfs_free_reserved_data_space(inode, len); + set_bit(BTRFS_INODE_DIO_READY, &BTRFS_I(inode)->runtime_flags); } /* @@ -7855,8 +7872,6 @@ static void btrfs_endio_direct_write(struct bio *bio, int err) struct bio *dio_bio; int ret; - if (err) - goto out_done; again: ret = btrfs_dec_test_first_ordered_pending(inode, &ordered, &ordered_offset, @@ -7879,7 +7894,6 @@ out_test: ordered = NULL; goto again; } -out_done: dio_bio = dip->dio_bio; kfree(dip); @@ -8147,9 +8161,8 @@ out_err: static void btrfs_submit_direct(int rw, struct bio *dio_bio, struct inode *inode, loff_t file_offset) { - struct btrfs_root *root = BTRFS_I(inode)->root; - struct btrfs_dio_private *dip; - struct bio *io_bio; + struct btrfs_dio_private *dip = NULL; + struct bio *io_bio = NULL; struct btrfs_io_bio *btrfs_bio; int skip_sum; int write = rw & REQ_WRITE; @@ -8166,7 +8179,7 @@ static void btrfs_submit_direct(int rw, struct bio *dio_bio, dip = kzalloc(sizeof(*dip), GFP_NOFS); if (!dip) { ret = -ENOMEM; - goto free_io_bio; + goto free_ordered; } dip->private = dio_bio->bi_private; @@ -8194,25 +8207,55 @@ static void btrfs_submit_direct(int rw, struct bio *dio_bio, if (btrfs_bio->end_io) btrfs_bio->end_io(btrfs_bio, ret); -free_io_bio: - bio_put(io_bio); free_ordered: /* - * If this is a write, we need to clean up the reserved space and kill - * the ordered extent. + * If we arrived here it means either we failed to submit the dip + * or we either failed to clone the dio_bio or failed to allocate the + * dip. If we cloned the dio_bio and allocated the dip, we can just + * call bio_endio against our io_bio so that we get proper resource + * cleanup if we fail to submit the dip, otherwise, we must do the + * same as btrfs_endio_direct_[write|read] because we can't call these + * callbacks - they require an allocated dip and a clone of dio_bio. */ - if (write) { - struct btrfs_ordered_extent *ordered; - ordered = btrfs_lookup_ordered_extent(inode, file_offset); - if (!test_bit(BTRFS_ORDERED_PREALLOC, &ordered->flags) && - !test_bit(BTRFS_ORDERED_NOCOW, &ordered->flags)) - btrfs_free_reserved_extent(root, ordered->start, - ordered->disk_len, 1); - btrfs_put_ordered_extent(ordered); - btrfs_put_ordered_extent(ordered); + if (io_bio && dip) { + bio_endio(io_bio, ret); + /* + * The end io callbacks free our dip, do the final put on io_bio + * and all the cleanup and final put for dio_bio (through + * dio_end_io()). + */ + dip = NULL; + io_bio = NULL; + } else { + if (write) { + struct btrfs_ordered_extent *ordered; + + ordered = btrfs_lookup_ordered_extent(inode, + file_offset); + set_bit(BTRFS_ORDERED_IOERR, &ordered->flags); + /* + * Decrements our ref on the ordered extent and removes + * the ordered extent from the inode's ordered tree, + * doing all the proper resource cleanup such as for the + * reserved space and waking up any waiters for this + * ordered extent (through btrfs_remove_ordered_extent). + */ + btrfs_finish_ordered_io(ordered); + } else { + unlock_extent(&BTRFS_I(inode)->io_tree, file_offset, + file_offset + dio_bio->bi_iter.bi_size - 1); + } + clear_bit(BIO_UPTODATE, &dio_bio->bi_flags); + /* + * Releases and cleans up our dio_bio, no need to bio_put() + * nor bio_endio()/bio_io_error() against dio_bio. + */ + dio_end_io(dio_bio, ret); } - bio_endio(dio_bio, ret); + if (io_bio) + bio_put(io_bio); + kfree(dip); } static ssize_t check_direct_IO(struct btrfs_root *root, struct kiocb *iocb, @@ -8314,9 +8357,18 @@ static ssize_t btrfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter, btrfs_submit_direct, flags); if (iov_iter_rw(iter) == WRITE) { current->journal_info = NULL; - if (ret < 0 && ret != -EIOCBQUEUED) - btrfs_delalloc_release_space(inode, count); - else if (ret >= 0 && (size_t)ret < count) + if (ret < 0 && ret != -EIOCBQUEUED) { + /* + * If the error comes from submitting stage, + * btrfs_get_blocsk_direct() has free'd data space, + * and metadata space will be handled by + * finish_ordered_fn, don't do that again to make + * sure bytes_may_use is correct. + */ + if (!test_and_clear_bit(BTRFS_INODE_DIO_READY, + &BTRFS_I(inode)->runtime_flags)) + btrfs_delalloc_release_space(inode, count); + } else if (ret >= 0 && (size_t)ret < count) btrfs_delalloc_release_space(inode, count - (size_t)ret); } diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 37d456a9a..0770c9158 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -87,7 +87,8 @@ struct btrfs_ioctl_received_subvol_args_32 { static int btrfs_clone(struct inode *src, struct inode *inode, - u64 off, u64 olen, u64 olen_aligned, u64 destoff); + u64 off, u64 olen, u64 olen_aligned, u64 destoff, + int no_time_update); /* Mask out flags that are inappropriate for the given type of inode. */ static inline __u32 btrfs_mask_flags(umode_t mode, __u32 flags) @@ -553,8 +554,8 @@ static noinline int create_subvol(struct inode *dir, key.offset = (u64)-1; new_root = btrfs_read_fs_root_no_name(root->fs_info, &key); if (IS_ERR(new_root)) { - btrfs_abort_transaction(trans, root, PTR_ERR(new_root)); ret = PTR_ERR(new_root); + btrfs_abort_transaction(trans, root, ret); goto fail; } @@ -1318,7 +1319,7 @@ int btrfs_defrag_file(struct inode *inode, struct file *file, i = range->start >> PAGE_CACHE_SHIFT; } if (!max_to_defrag) - max_to_defrag = last_index + 1; + max_to_defrag = last_index - i + 1; /* * make writeback starts from i, so the defrag range can be @@ -1368,7 +1369,7 @@ int btrfs_defrag_file(struct inode *inode, struct file *file, ra_index = max(i, ra_index); btrfs_force_ra(inode->i_mapping, ra, file, ra_index, cluster); - ra_index += max_cluster; + ra_index += cluster; } mutex_lock(&inode->i_mutex); @@ -2271,10 +2272,7 @@ static noinline int btrfs_ioctl_ino_lookup(struct file *file, { struct btrfs_ioctl_ino_lookup_args *args; struct inode *inode; - int ret; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; + int ret = 0; args = memdup_user(argp, sizeof(*args)); if (IS_ERR(args)) @@ -2282,13 +2280,28 @@ static noinline int btrfs_ioctl_ino_lookup(struct file *file, inode = file_inode(file); + /* + * Unprivileged query to obtain the containing subvolume root id. The + * path is reset so it's consistent with btrfs_search_path_in_tree. + */ if (args->treeid == 0) args->treeid = BTRFS_I(inode)->root->root_key.objectid; + if (args->objectid == BTRFS_FIRST_FREE_OBJECTID) { + args->name[0] = 0; + goto out; + } + + if (!capable(CAP_SYS_ADMIN)) { + ret = -EPERM; + goto out; + } + ret = btrfs_search_path_in_tree(BTRFS_I(inode)->root->fs_info, args->treeid, args->objectid, args->name); +out: if (ret == 0 && copy_to_user(argp, args, sizeof(*args))) ret = -EFAULT; @@ -2753,14 +2766,11 @@ out: return ret; } -static struct page *extent_same_get_page(struct inode *inode, u64 off) +static struct page *extent_same_get_page(struct inode *inode, pgoff_t index) { struct page *page; - pgoff_t index; struct extent_io_tree *tree = &BTRFS_I(inode)->io_tree; - index = off >> PAGE_CACHE_SHIFT; - page = grab_cache_page(inode->i_mapping, index); if (!page) return NULL; @@ -2781,6 +2791,20 @@ static struct page *extent_same_get_page(struct inode *inode, u64 off) return page; } +static int gather_extent_pages(struct inode *inode, struct page **pages, + int num_pages, u64 off) +{ + int i; + pgoff_t index = off >> PAGE_CACHE_SHIFT; + + for (i = 0; i < num_pages; i++) { + pages[i] = extent_same_get_page(inode, index + i); + if (!pages[i]) + return -ENOMEM; + } + return 0; +} + static inline void lock_extent_range(struct inode *inode, u64 off, u64 len) { /* do any pending delalloc/csum calc on src, one way or @@ -2806,52 +2830,120 @@ static inline void lock_extent_range(struct inode *inode, u64 off, u64 len) } } -static void btrfs_double_unlock(struct inode *inode1, u64 loff1, - struct inode *inode2, u64 loff2, u64 len) +static void btrfs_double_inode_unlock(struct inode *inode1, struct inode *inode2) { - unlock_extent(&BTRFS_I(inode1)->io_tree, loff1, loff1 + len - 1); - unlock_extent(&BTRFS_I(inode2)->io_tree, loff2, loff2 + len - 1); - mutex_unlock(&inode1->i_mutex); mutex_unlock(&inode2->i_mutex); } -static void btrfs_double_lock(struct inode *inode1, u64 loff1, - struct inode *inode2, u64 loff2, u64 len) +static void btrfs_double_inode_lock(struct inode *inode1, struct inode *inode2) +{ + if (inode1 < inode2) + swap(inode1, inode2); + + mutex_lock_nested(&inode1->i_mutex, I_MUTEX_PARENT); + if (inode1 != inode2) + mutex_lock_nested(&inode2->i_mutex, I_MUTEX_CHILD); +} + +static void btrfs_double_extent_unlock(struct inode *inode1, u64 loff1, + struct inode *inode2, u64 loff2, u64 len) +{ + unlock_extent(&BTRFS_I(inode1)->io_tree, loff1, loff1 + len - 1); + unlock_extent(&BTRFS_I(inode2)->io_tree, loff2, loff2 + len - 1); +} + +static void btrfs_double_extent_lock(struct inode *inode1, u64 loff1, + struct inode *inode2, u64 loff2, u64 len) { if (inode1 < inode2) { swap(inode1, inode2); swap(loff1, loff2); } - - mutex_lock_nested(&inode1->i_mutex, I_MUTEX_PARENT); lock_extent_range(inode1, loff1, len); - if (inode1 != inode2) { - mutex_lock_nested(&inode2->i_mutex, I_MUTEX_CHILD); + if (inode1 != inode2) lock_extent_range(inode2, loff2, len); +} + +struct cmp_pages { + int num_pages; + struct page **src_pages; + struct page **dst_pages; +}; + +static void btrfs_cmp_data_free(struct cmp_pages *cmp) +{ + int i; + struct page *pg; + + for (i = 0; i < cmp->num_pages; i++) { + pg = cmp->src_pages[i]; + if (pg) + page_cache_release(pg); + pg = cmp->dst_pages[i]; + if (pg) + page_cache_release(pg); + } + kfree(cmp->src_pages); + kfree(cmp->dst_pages); +} + +static int btrfs_cmp_data_prepare(struct inode *src, u64 loff, + struct inode *dst, u64 dst_loff, + u64 len, struct cmp_pages *cmp) +{ + int ret; + int num_pages = PAGE_CACHE_ALIGN(len) >> PAGE_CACHE_SHIFT; + struct page **src_pgarr, **dst_pgarr; + + /* + * We must gather up all the pages before we initiate our + * extent locking. We use an array for the page pointers. Size + * of the array is bounded by len, which is in turn bounded by + * BTRFS_MAX_DEDUPE_LEN. + */ + src_pgarr = kzalloc(num_pages * sizeof(struct page *), GFP_NOFS); + dst_pgarr = kzalloc(num_pages * sizeof(struct page *), GFP_NOFS); + if (!src_pgarr || !dst_pgarr) { + kfree(src_pgarr); + kfree(dst_pgarr); + return -ENOMEM; } + cmp->num_pages = num_pages; + cmp->src_pages = src_pgarr; + cmp->dst_pages = dst_pgarr; + + ret = gather_extent_pages(src, cmp->src_pages, cmp->num_pages, loff); + if (ret) + goto out; + + ret = gather_extent_pages(dst, cmp->dst_pages, cmp->num_pages, dst_loff); + +out: + if (ret) + btrfs_cmp_data_free(cmp); + return 0; } static int btrfs_cmp_data(struct inode *src, u64 loff, struct inode *dst, - u64 dst_loff, u64 len) + u64 dst_loff, u64 len, struct cmp_pages *cmp) { int ret = 0; + int i; struct page *src_page, *dst_page; unsigned int cmp_len = PAGE_CACHE_SIZE; void *addr, *dst_addr; + i = 0; while (len) { if (len < PAGE_CACHE_SIZE) cmp_len = len; - src_page = extent_same_get_page(src, loff); - if (!src_page) - return -EINVAL; - dst_page = extent_same_get_page(dst, dst_loff); - if (!dst_page) { - page_cache_release(src_page); - return -EINVAL; - } + BUG_ON(i >= cmp->num_pages); + + src_page = cmp->src_pages[i]; + dst_page = cmp->dst_pages[i]; + addr = kmap_atomic(src_page); dst_addr = kmap_atomic(dst_page); @@ -2863,26 +2955,30 @@ static int btrfs_cmp_data(struct inode *src, u64 loff, struct inode *dst, kunmap_atomic(addr); kunmap_atomic(dst_addr); - page_cache_release(src_page); - page_cache_release(dst_page); if (ret) break; - loff += cmp_len; - dst_loff += cmp_len; len -= cmp_len; + i++; } return ret; } -static int extent_same_check_offsets(struct inode *inode, u64 off, u64 len) +static int extent_same_check_offsets(struct inode *inode, u64 off, u64 *plen, + u64 olen) { + u64 len = *plen; u64 bs = BTRFS_I(inode)->root->fs_info->sb->s_blocksize; - if (off + len > inode->i_size || off + len < off) + if (off + olen > inode->i_size || off + olen < off) return -EINVAL; + + /* if we extend to eof, continue to block boundary */ + if (off + len == inode->i_size) + *plen = len = ALIGN(inode->i_size, bs) - off; + /* Check that we are block aligned - btrfs_clone() requires this */ if (!IS_ALIGNED(off, bs) || !IS_ALIGNED(off + len, bs)) return -EINVAL; @@ -2890,31 +2986,67 @@ static int extent_same_check_offsets(struct inode *inode, u64 off, u64 len) return 0; } -static int btrfs_extent_same(struct inode *src, u64 loff, u64 len, +static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen, struct inode *dst, u64 dst_loff) { int ret; + u64 len = olen; + struct cmp_pages cmp; + int same_inode = 0; + u64 same_lock_start = 0; + u64 same_lock_len = 0; - /* - * btrfs_clone() can't handle extents in the same file - * yet. Once that works, we can drop this check and replace it - * with a check for the same inode, but overlapping extents. - */ if (src == dst) - return -EINVAL; + same_inode = 1; if (len == 0) return 0; - btrfs_double_lock(src, loff, dst, dst_loff, len); + if (same_inode) { + mutex_lock(&src->i_mutex); - ret = extent_same_check_offsets(src, loff, len); - if (ret) - goto out_unlock; + ret = extent_same_check_offsets(src, loff, &len, olen); + if (ret) + goto out_unlock; - ret = extent_same_check_offsets(dst, dst_loff, len); - if (ret) - goto out_unlock; + /* + * Single inode case wants the same checks, except we + * don't want our length pushed out past i_size as + * comparing that data range makes no sense. + * + * extent_same_check_offsets() will do this for an + * unaligned length at i_size, so catch it here and + * reject the request. + * + * This effectively means we require aligned extents + * for the single-inode case, whereas the other cases + * allow an unaligned length so long as it ends at + * i_size. + */ + if (len != olen) { + ret = -EINVAL; + goto out_unlock; + } + + /* Check for overlapping ranges */ + if (dst_loff + len > loff && dst_loff < loff + len) { + ret = -EINVAL; + goto out_unlock; + } + + same_lock_start = min_t(u64, loff, dst_loff); + same_lock_len = max_t(u64, loff, dst_loff) + len - same_lock_start; + } else { + btrfs_double_inode_lock(src, dst); + + ret = extent_same_check_offsets(src, loff, &len, olen); + if (ret) + goto out_unlock; + + ret = extent_same_check_offsets(dst, dst_loff, &len, olen); + if (ret) + goto out_unlock; + } /* don't make the dst file partly checksummed */ if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) != @@ -2923,12 +3055,32 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 len, goto out_unlock; } - ret = btrfs_cmp_data(src, loff, dst, dst_loff, len); + ret = btrfs_cmp_data_prepare(src, loff, dst, dst_loff, olen, &cmp); + if (ret) + goto out_unlock; + + if (same_inode) + lock_extent_range(src, same_lock_start, same_lock_len); + else + btrfs_double_extent_lock(src, loff, dst, dst_loff, len); + + /* pass original length for comparison so we stay within i_size */ + ret = btrfs_cmp_data(src, loff, dst, dst_loff, olen, &cmp); if (ret == 0) - ret = btrfs_clone(src, dst, loff, len, len, dst_loff); + ret = btrfs_clone(src, dst, loff, olen, len, dst_loff, 1); + if (same_inode) + unlock_extent(&BTRFS_I(src)->io_tree, same_lock_start, + same_lock_start + same_lock_len - 1); + else + btrfs_double_extent_unlock(src, loff, dst, dst_loff, len); + + btrfs_cmp_data_free(&cmp); out_unlock: - btrfs_double_unlock(src, loff, dst, dst_loff, len); + if (same_inode) + mutex_unlock(&src->i_mutex); + else + btrfs_double_inode_unlock(src, dst); return ret; } @@ -3082,13 +3234,15 @@ static int clone_finish_inode_update(struct btrfs_trans_handle *trans, struct inode *inode, u64 endoff, const u64 destoff, - const u64 olen) + const u64 olen, + int no_time_update) { struct btrfs_root *root = BTRFS_I(inode)->root; int ret; inode_inc_iversion(inode); - inode->i_mtime = inode->i_ctime = CURRENT_TIME; + if (!no_time_update) + inode->i_mtime = inode->i_ctime = CURRENT_TIME; /* * We round up to the block size at eof when determining which * extents to clone above, but shouldn't round up the file size. @@ -3173,13 +3327,13 @@ static void clone_update_extent_map(struct inode *inode, * @inode: Inode to clone to * @off: Offset within source to start clone from * @olen: Original length, passed by user, of range to clone - * @olen_aligned: Block-aligned value of olen, extent_same uses - * identical values here + * @olen_aligned: Block-aligned value of olen * @destoff: Offset within @inode to start clone + * @no_time_update: Whether to update mtime/ctime on the target inode */ static int btrfs_clone(struct inode *src, struct inode *inode, const u64 off, const u64 olen, const u64 olen_aligned, - const u64 destoff) + const u64 destoff, int no_time_update) { struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_path *path = NULL; @@ -3517,7 +3671,8 @@ process_slot: root->sectorsize); ret = clone_finish_inode_update(trans, inode, last_dest_end, - destoff, olen); + destoff, olen, + no_time_update); if (ret) goto out; if (new_key.offset + datal >= destoff + len) @@ -3555,7 +3710,7 @@ process_slot: clone_update_extent_map(inode, trans, NULL, last_dest_end, destoff + len - last_dest_end); ret = clone_finish_inode_update(trans, inode, destoff + len, - destoff, olen); + destoff, olen, no_time_update); } out: @@ -3692,7 +3847,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd, lock_extent_range(inode, destoff, len); } - ret = btrfs_clone(src, inode, off, olen, len, destoff); + ret = btrfs_clone(src, inode, off, olen, len, destoff, 0); if (same_inode) { u64 lock_start = min_t(u64, off, destoff); diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 760c4a5e0..52170cf17 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -198,9 +198,6 @@ static int __btrfs_add_ordered_extent(struct inode *inode, u64 file_offset, entry->file_offset = file_offset; entry->start = start; entry->len = len; - if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM) && - !(type == BTRFS_ORDERED_NOCOW)) - entry->csum_bytes_left = disk_len; entry->disk_len = disk_len; entry->bytes_left = len; entry->inode = igrab(inode); @@ -286,10 +283,6 @@ void btrfs_add_ordered_sum(struct inode *inode, tree = &BTRFS_I(inode)->ordered_tree; spin_lock_irq(&tree->lock); list_add_tail(&sum->list, &entry->list); - WARN_ON(entry->csum_bytes_left < sum->len); - entry->csum_bytes_left -= sum->len; - if (entry->csum_bytes_left == 0) - wake_up(&entry->wait); spin_unlock_irq(&tree->lock); } @@ -509,7 +502,21 @@ void btrfs_wait_logged_extents(struct btrfs_trans_handle *trans, wait_event(ordered->wait, test_bit(BTRFS_ORDERED_IO_DONE, &ordered->flags)); - list_add_tail(&ordered->trans_list, &trans->ordered); + /* + * If our ordered extent completed it means it updated the + * fs/subvol and csum trees already, so no need to make the + * current transaction's commit wait for it, as we end up + * holding memory unnecessarily and delaying the inode's iput + * until the transaction commit (we schedule an iput for the + * inode when the ordered extent's refcount drops to 0), which + * prevents it from being evictable until the transaction + * commits. + */ + if (test_bit(BTRFS_ORDERED_COMPLETE, &ordered->flags)) + btrfs_put_ordered_extent(ordered); + else + list_add_tail(&ordered->trans_list, &trans->ordered); + spin_lock_irq(&log->log_extents_lock[index]); } spin_unlock_irq(&log->log_extents_lock[index]); @@ -545,6 +552,10 @@ void btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry) trace_btrfs_ordered_extent_put(entry->inode, entry); if (atomic_dec_and_test(&entry->refs)) { + ASSERT(list_empty(&entry->log_list)); + ASSERT(list_empty(&entry->trans_list)); + ASSERT(list_empty(&entry->root_extent_list)); + ASSERT(RB_EMPTY_NODE(&entry->rb_node)); if (entry->inode) btrfs_add_delayed_iput(entry->inode); while (!list_empty(&entry->list)) { @@ -572,6 +583,7 @@ void btrfs_remove_ordered_extent(struct inode *inode, spin_lock_irq(&tree->lock); node = &entry->rb_node; rb_erase(node, &tree->tree); + RB_CLEAR_NODE(node); if (tree->last == node) tree->last = NULL; set_bit(BTRFS_ORDERED_COMPLETE, &entry->flags); @@ -844,6 +856,20 @@ out: return entry; } +bool btrfs_have_ordered_extents_in_range(struct inode *inode, + u64 file_offset, + u64 len) +{ + struct btrfs_ordered_extent *oe; + + oe = btrfs_lookup_ordered_range(inode, file_offset, len); + if (oe) { + btrfs_put_ordered_extent(oe); + return true; + } + return false; +} + /* * lookup and return any extent before 'file_offset'. NULL is returned * if none is found diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h index e96cd4ccd..7176cc0fe 100644 --- a/fs/btrfs/ordered-data.h +++ b/fs/btrfs/ordered-data.h @@ -89,9 +89,6 @@ struct btrfs_ordered_extent { /* number of bytes that still need writing */ u64 bytes_left; - /* number of bytes that still need csumming */ - u64 csum_bytes_left; - /* * the end of the ordered extent which is behind it but * didn't update disk_i_size. Please see the comment of @@ -191,6 +188,9 @@ btrfs_lookup_first_ordered_extent(struct inode * inode, u64 file_offset); struct btrfs_ordered_extent *btrfs_lookup_ordered_range(struct inode *inode, u64 file_offset, u64 len); +bool btrfs_have_ordered_extents_in_range(struct inode *inode, + u64 file_offset, + u64 len); int btrfs_ordered_update_i_size(struct inode *inode, u64 offset, struct btrfs_ordered_extent *ordered); int btrfs_find_ordered_sum(struct inode *inode, u64 offset, u64 disk_bytenr, diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 3d6546581..8a8202956 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -34,6 +34,7 @@ #include "extent_io.h" #include "qgroup.h" + /* TODO XXX FIXME * - subvol delete -> delete when ref goes to 0? delete limits also? * - reorganize keys @@ -84,11 +85,42 @@ struct btrfs_qgroup { /* * temp variables for accounting operations + * Refer to qgroup_shared_accouting() for details. */ u64 old_refcnt; u64 new_refcnt; }; +static void btrfs_qgroup_update_old_refcnt(struct btrfs_qgroup *qg, u64 seq, + int mod) +{ + if (qg->old_refcnt < seq) + qg->old_refcnt = seq; + qg->old_refcnt += mod; +} + +static void btrfs_qgroup_update_new_refcnt(struct btrfs_qgroup *qg, u64 seq, + int mod) +{ + if (qg->new_refcnt < seq) + qg->new_refcnt = seq; + qg->new_refcnt += mod; +} + +static inline u64 btrfs_qgroup_get_old_refcnt(struct btrfs_qgroup *qg, u64 seq) +{ + if (qg->old_refcnt < seq) + return 0; + return qg->old_refcnt - seq; +} + +static inline u64 btrfs_qgroup_get_new_refcnt(struct btrfs_qgroup *qg, u64 seq) +{ + if (qg->new_refcnt < seq) + return 0; + return qg->new_refcnt - seq; +} + /* * glue structure to represent the relations between qgroups. */ @@ -1115,14 +1147,14 @@ int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, struct ulist *tmp; int ret = 0; - tmp = ulist_alloc(GFP_NOFS); - if (!tmp) - return -ENOMEM; - /* Check the level of src and dst first */ if (btrfs_qgroup_level(src) >= btrfs_qgroup_level(dst)) return -EINVAL; + tmp = ulist_alloc(GFP_NOFS); + if (!tmp) + return -ENOMEM; + mutex_lock(&fs_info->qgroup_ioctl_lock); quota_root = fs_info->quota_root; if (!quota_root) { @@ -1317,6 +1349,11 @@ int btrfs_limit_qgroup(struct btrfs_trans_handle *trans, struct btrfs_root *quota_root; struct btrfs_qgroup *qgroup; int ret = 0; + /* Sometimes we would want to clear the limit on this qgroup. + * To meet this requirement, we treat the -1 as a special value + * which tell kernel to clear the limit on this qgroup. + */ + const u64 CLEAR_VALUE = -1; mutex_lock(&fs_info->qgroup_ioctl_lock); quota_root = fs_info->quota_root; @@ -1332,14 +1369,42 @@ int btrfs_limit_qgroup(struct btrfs_trans_handle *trans, } spin_lock(&fs_info->qgroup_lock); - if (limit->flags & BTRFS_QGROUP_LIMIT_MAX_RFER) - qgroup->max_rfer = limit->max_rfer; - if (limit->flags & BTRFS_QGROUP_LIMIT_MAX_EXCL) - qgroup->max_excl = limit->max_excl; - if (limit->flags & BTRFS_QGROUP_LIMIT_RSV_RFER) - qgroup->rsv_rfer = limit->rsv_rfer; - if (limit->flags & BTRFS_QGROUP_LIMIT_RSV_EXCL) - qgroup->rsv_excl = limit->rsv_excl; + if (limit->flags & BTRFS_QGROUP_LIMIT_MAX_RFER) { + if (limit->max_rfer == CLEAR_VALUE) { + qgroup->lim_flags &= ~BTRFS_QGROUP_LIMIT_MAX_RFER; + limit->flags &= ~BTRFS_QGROUP_LIMIT_MAX_RFER; + qgroup->max_rfer = 0; + } else { + qgroup->max_rfer = limit->max_rfer; + } + } + if (limit->flags & BTRFS_QGROUP_LIMIT_MAX_EXCL) { + if (limit->max_excl == CLEAR_VALUE) { + qgroup->lim_flags &= ~BTRFS_QGROUP_LIMIT_MAX_EXCL; + limit->flags &= ~BTRFS_QGROUP_LIMIT_MAX_EXCL; + qgroup->max_excl = 0; + } else { + qgroup->max_excl = limit->max_excl; + } + } + if (limit->flags & BTRFS_QGROUP_LIMIT_RSV_RFER) { + if (limit->rsv_rfer == CLEAR_VALUE) { + qgroup->lim_flags &= ~BTRFS_QGROUP_LIMIT_RSV_RFER; + limit->flags &= ~BTRFS_QGROUP_LIMIT_RSV_RFER; + qgroup->rsv_rfer = 0; + } else { + qgroup->rsv_rfer = limit->rsv_rfer; + } + } + if (limit->flags & BTRFS_QGROUP_LIMIT_RSV_EXCL) { + if (limit->rsv_excl == CLEAR_VALUE) { + qgroup->lim_flags &= ~BTRFS_QGROUP_LIMIT_RSV_EXCL; + limit->flags &= ~BTRFS_QGROUP_LIMIT_RSV_EXCL; + qgroup->rsv_excl = 0; + } else { + qgroup->rsv_excl = limit->rsv_excl; + } + } qgroup->lim_flags |= limit->flags; spin_unlock(&fs_info->qgroup_lock); @@ -1356,239 +1421,86 @@ out: return ret; } -static int comp_oper_exist(struct btrfs_qgroup_operation *oper1, - struct btrfs_qgroup_operation *oper2) +int btrfs_qgroup_prepare_account_extents(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info) { - /* - * Ignore seq and type here, we're looking for any operation - * at all related to this extent on that root. - */ - if (oper1->bytenr < oper2->bytenr) - return -1; - if (oper1->bytenr > oper2->bytenr) - return 1; - if (oper1->ref_root < oper2->ref_root) - return -1; - if (oper1->ref_root > oper2->ref_root) - return 1; - return 0; -} + struct btrfs_qgroup_extent_record *record; + struct btrfs_delayed_ref_root *delayed_refs; + struct rb_node *node; + u64 qgroup_to_skip; + int ret = 0; -static int qgroup_oper_exists(struct btrfs_fs_info *fs_info, - struct btrfs_qgroup_operation *oper) -{ - struct rb_node *n; - struct btrfs_qgroup_operation *cur; - int cmp; + delayed_refs = &trans->transaction->delayed_refs; + qgroup_to_skip = delayed_refs->qgroup_to_skip; - spin_lock(&fs_info->qgroup_op_lock); - n = fs_info->qgroup_op_tree.rb_node; - while (n) { - cur = rb_entry(n, struct btrfs_qgroup_operation, n); - cmp = comp_oper_exist(cur, oper); - if (cmp < 0) { - n = n->rb_right; - } else if (cmp) { - n = n->rb_left; - } else { - spin_unlock(&fs_info->qgroup_op_lock); - return -EEXIST; - } + /* + * No need to do lock, since this function will only be called in + * btrfs_commmit_transaction(). + */ + node = rb_first(&delayed_refs->dirty_extent_root); + while (node) { + record = rb_entry(node, struct btrfs_qgroup_extent_record, + node); + ret = btrfs_find_all_roots(NULL, fs_info, record->bytenr, 0, + &record->old_roots); + if (ret < 0) + break; + if (qgroup_to_skip) + ulist_del(record->old_roots, qgroup_to_skip, 0); + node = rb_next(node); } - spin_unlock(&fs_info->qgroup_op_lock); - return 0; -} - -static int comp_oper(struct btrfs_qgroup_operation *oper1, - struct btrfs_qgroup_operation *oper2) -{ - if (oper1->bytenr < oper2->bytenr) - return -1; - if (oper1->bytenr > oper2->bytenr) - return 1; - if (oper1->ref_root < oper2->ref_root) - return -1; - if (oper1->ref_root > oper2->ref_root) - return 1; - if (oper1->seq < oper2->seq) - return -1; - if (oper1->seq > oper2->seq) - return 1; - if (oper1->type < oper2->type) - return -1; - if (oper1->type > oper2->type) - return 1; - return 0; + return ret; } -static int insert_qgroup_oper(struct btrfs_fs_info *fs_info, - struct btrfs_qgroup_operation *oper) +struct btrfs_qgroup_extent_record +*btrfs_qgroup_insert_dirty_extent(struct btrfs_delayed_ref_root *delayed_refs, + struct btrfs_qgroup_extent_record *record) { - struct rb_node **p; - struct rb_node *parent = NULL; - struct btrfs_qgroup_operation *cur; - int cmp; + struct rb_node **p = &delayed_refs->dirty_extent_root.rb_node; + struct rb_node *parent_node = NULL; + struct btrfs_qgroup_extent_record *entry; + u64 bytenr = record->bytenr; - spin_lock(&fs_info->qgroup_op_lock); - p = &fs_info->qgroup_op_tree.rb_node; while (*p) { - parent = *p; - cur = rb_entry(parent, struct btrfs_qgroup_operation, n); - cmp = comp_oper(cur, oper); - if (cmp < 0) { - p = &(*p)->rb_right; - } else if (cmp) { + parent_node = *p; + entry = rb_entry(parent_node, struct btrfs_qgroup_extent_record, + node); + if (bytenr < entry->bytenr) p = &(*p)->rb_left; - } else { - spin_unlock(&fs_info->qgroup_op_lock); - return -EEXIST; - } - } - rb_link_node(&oper->n, parent, p); - rb_insert_color(&oper->n, &fs_info->qgroup_op_tree); - spin_unlock(&fs_info->qgroup_op_lock); - return 0; -} - -/* - * Record a quota operation for processing later on. - * @trans: the transaction we are adding the delayed op to. - * @fs_info: the fs_info for this fs. - * @ref_root: the root of the reference we are acting on, - * @bytenr: the bytenr we are acting on. - * @num_bytes: the number of bytes in the reference. - * @type: the type of operation this is. - * @mod_seq: do we need to get a sequence number for looking up roots. - * - * We just add it to our trans qgroup_ref_list and carry on and process these - * operations in order at some later point. If the reference root isn't a fs - * root then we don't bother with doing anything. - * - * MUST BE HOLDING THE REF LOCK. - */ -int btrfs_qgroup_record_ref(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, u64 ref_root, - u64 bytenr, u64 num_bytes, - enum btrfs_qgroup_operation_type type, int mod_seq) -{ - struct btrfs_qgroup_operation *oper; - int ret; - - if (!is_fstree(ref_root) || !fs_info->quota_enabled) - return 0; - - oper = kmalloc(sizeof(*oper), GFP_NOFS); - if (!oper) - return -ENOMEM; - - oper->ref_root = ref_root; - oper->bytenr = bytenr; - oper->num_bytes = num_bytes; - oper->type = type; - oper->seq = atomic_inc_return(&fs_info->qgroup_op_seq); - INIT_LIST_HEAD(&oper->elem.list); - oper->elem.seq = 0; - - trace_btrfs_qgroup_record_ref(oper); - - if (type == BTRFS_QGROUP_OPER_SUB_SUBTREE) { - /* - * If any operation for this bytenr/ref_root combo - * exists, then we know it's not exclusively owned and - * shouldn't be queued up. - * - * This also catches the case where we have a cloned - * extent that gets queued up multiple times during - * drop snapshot. - */ - if (qgroup_oper_exists(fs_info, oper)) { - kfree(oper); - return 0; - } - } - - ret = insert_qgroup_oper(fs_info, oper); - if (ret) { - /* Shouldn't happen so have an assert for developers */ - ASSERT(0); - kfree(oper); - return ret; + else if (bytenr > entry->bytenr) + p = &(*p)->rb_right; + else + return entry; } - list_add_tail(&oper->list, &trans->qgroup_ref_list); - - if (mod_seq) - btrfs_get_tree_mod_seq(fs_info, &oper->elem); - - return 0; -} - -static int qgroup_excl_accounting(struct btrfs_fs_info *fs_info, - struct btrfs_qgroup_operation *oper) -{ - struct ulist *tmp; - int sign = 0; - int ret = 0; - tmp = ulist_alloc(GFP_NOFS); - if (!tmp) - return -ENOMEM; - - spin_lock(&fs_info->qgroup_lock); - if (!fs_info->quota_root) - goto out; - - switch (oper->type) { - case BTRFS_QGROUP_OPER_ADD_EXCL: - sign = 1; - break; - case BTRFS_QGROUP_OPER_SUB_EXCL: - sign = -1; - break; - default: - ASSERT(0); - } - ret = __qgroup_excl_accounting(fs_info, tmp, oper->ref_root, - oper->num_bytes, sign); -out: - spin_unlock(&fs_info->qgroup_lock); - ulist_free(tmp); - return ret; + rb_link_node(&record->node, parent_node, p); + rb_insert_color(&record->node, &delayed_refs->dirty_extent_root); + return NULL; } +#define UPDATE_NEW 0 +#define UPDATE_OLD 1 /* - * Walk all of the roots that pointed to our bytenr and adjust their refcnts as - * properly. + * Walk all of the roots that points to the bytenr and adjust their refcnts. */ -static int qgroup_calc_old_refcnt(struct btrfs_fs_info *fs_info, - u64 root_to_skip, struct ulist *tmp, - struct ulist *roots, struct ulist *qgroups, - u64 seq, int *old_roots, int rescan) +static int qgroup_update_refcnt(struct btrfs_fs_info *fs_info, + struct ulist *roots, struct ulist *tmp, + struct ulist *qgroups, u64 seq, int update_old) { struct ulist_node *unode; struct ulist_iterator uiter; struct ulist_node *tmp_unode; struct ulist_iterator tmp_uiter; struct btrfs_qgroup *qg; - int ret; + int ret = 0; + if (!roots) + return 0; ULIST_ITER_INIT(&uiter); while ((unode = ulist_next(roots, &uiter))) { - /* We don't count our current root here */ - if (unode->val == root_to_skip) - continue; qg = find_qgroup_rb(fs_info, unode->val); if (!qg) continue; - /* - * We could have a pending removal of this same ref so we may - * not have actually found our ref root when doing - * btrfs_find_all_roots, so we need to keep track of how many - * old roots we find in case we removed ours and added a - * different one at the same time. I don't think this could - * happen in practice but that sort of thinking leads to pain - * and suffering and to the dark side. - */ - (*old_roots)++; ulist_reinit(tmp); ret = ulist_add(qgroups, qg->qgroupid, ptr_to_u64(qg), @@ -1603,29 +1515,10 @@ static int qgroup_calc_old_refcnt(struct btrfs_fs_info *fs_info, struct btrfs_qgroup_list *glist; qg = u64_to_ptr(tmp_unode->aux); - /* - * We use this sequence number to keep from having to - * run the whole list and 0 out the refcnt every time. - * We basically use sequnce as the known 0 count and - * then add 1 everytime we see a qgroup. This is how we - * get how many of the roots actually point up to the - * upper level qgroups in order to determine exclusive - * counts. - * - * For rescan we want to set old_refcnt to seq so our - * exclusive calculations end up correct. - */ - if (rescan) - qg->old_refcnt = seq; - else if (qg->old_refcnt < seq) - qg->old_refcnt = seq + 1; + if (update_old) + btrfs_qgroup_update_old_refcnt(qg, seq, 1); else - qg->old_refcnt++; - - if (qg->new_refcnt < seq) - qg->new_refcnt = seq + 1; - else - qg->new_refcnt++; + btrfs_qgroup_update_new_refcnt(qg, seq, 1); list_for_each_entry(glist, &qg->groups, next_group) { ret = ulist_add(qgroups, glist->group->qgroupid, ptr_to_u64(glist->group), @@ -1644,161 +1537,46 @@ static int qgroup_calc_old_refcnt(struct btrfs_fs_info *fs_info, } /* - * We need to walk forward in our operation tree and account for any roots that - * were deleted after we made this operation. - */ -static int qgroup_account_deleted_refs(struct btrfs_fs_info *fs_info, - struct btrfs_qgroup_operation *oper, - struct ulist *tmp, - struct ulist *qgroups, u64 seq, - int *old_roots) -{ - struct ulist_node *unode; - struct ulist_iterator uiter; - struct btrfs_qgroup *qg; - struct btrfs_qgroup_operation *tmp_oper; - struct rb_node *n; - int ret; - - ulist_reinit(tmp); - - /* - * We only walk forward in the tree since we're only interested in - * removals that happened _after_ our operation. - */ - spin_lock(&fs_info->qgroup_op_lock); - n = rb_next(&oper->n); - spin_unlock(&fs_info->qgroup_op_lock); - if (!n) - return 0; - tmp_oper = rb_entry(n, struct btrfs_qgroup_operation, n); - while (tmp_oper->bytenr == oper->bytenr) { - /* - * If it's not a removal we don't care, additions work out - * properly with our refcnt tracking. - */ - if (tmp_oper->type != BTRFS_QGROUP_OPER_SUB_SHARED && - tmp_oper->type != BTRFS_QGROUP_OPER_SUB_EXCL) - goto next; - qg = find_qgroup_rb(fs_info, tmp_oper->ref_root); - if (!qg) - goto next; - ret = ulist_add(qgroups, qg->qgroupid, ptr_to_u64(qg), - GFP_ATOMIC); - if (ret) { - if (ret < 0) - return ret; - /* - * We only want to increase old_roots if this qgroup is - * not already in the list of qgroups. If it is already - * there then that means it must have been re-added or - * the delete will be discarded because we had an - * existing ref that we haven't looked up yet. In this - * case we don't want to increase old_roots. So if ret - * == 1 then we know that this is the first time we've - * seen this qgroup and we can bump the old_roots. - */ - (*old_roots)++; - ret = ulist_add(tmp, qg->qgroupid, ptr_to_u64(qg), - GFP_ATOMIC); - if (ret < 0) - return ret; - } -next: - spin_lock(&fs_info->qgroup_op_lock); - n = rb_next(&tmp_oper->n); - spin_unlock(&fs_info->qgroup_op_lock); - if (!n) - break; - tmp_oper = rb_entry(n, struct btrfs_qgroup_operation, n); - } - - /* Ok now process the qgroups we found */ - ULIST_ITER_INIT(&uiter); - while ((unode = ulist_next(tmp, &uiter))) { - struct btrfs_qgroup_list *glist; - - qg = u64_to_ptr(unode->aux); - if (qg->old_refcnt < seq) - qg->old_refcnt = seq + 1; - else - qg->old_refcnt++; - if (qg->new_refcnt < seq) - qg->new_refcnt = seq + 1; - else - qg->new_refcnt++; - list_for_each_entry(glist, &qg->groups, next_group) { - ret = ulist_add(qgroups, glist->group->qgroupid, - ptr_to_u64(glist->group), GFP_ATOMIC); - if (ret < 0) - return ret; - ret = ulist_add(tmp, glist->group->qgroupid, - ptr_to_u64(glist->group), GFP_ATOMIC); - if (ret < 0) - return ret; - } - } - return 0; -} - -/* Add refcnt for the newly added reference. */ -static int qgroup_calc_new_refcnt(struct btrfs_fs_info *fs_info, - struct btrfs_qgroup_operation *oper, - struct btrfs_qgroup *qgroup, - struct ulist *tmp, struct ulist *qgroups, - u64 seq) -{ - struct ulist_node *unode; - struct ulist_iterator uiter; - struct btrfs_qgroup *qg; - int ret; - - ulist_reinit(tmp); - ret = ulist_add(qgroups, qgroup->qgroupid, ptr_to_u64(qgroup), - GFP_ATOMIC); - if (ret < 0) - return ret; - ret = ulist_add(tmp, qgroup->qgroupid, ptr_to_u64(qgroup), - GFP_ATOMIC); - if (ret < 0) - return ret; - ULIST_ITER_INIT(&uiter); - while ((unode = ulist_next(tmp, &uiter))) { - struct btrfs_qgroup_list *glist; - - qg = u64_to_ptr(unode->aux); - if (oper->type == BTRFS_QGROUP_OPER_ADD_SHARED) { - if (qg->new_refcnt < seq) - qg->new_refcnt = seq + 1; - else - qg->new_refcnt++; - } else { - if (qg->old_refcnt < seq) - qg->old_refcnt = seq + 1; - else - qg->old_refcnt++; - } - list_for_each_entry(glist, &qg->groups, next_group) { - ret = ulist_add(tmp, glist->group->qgroupid, - ptr_to_u64(glist->group), GFP_ATOMIC); - if (ret < 0) - return ret; - ret = ulist_add(qgroups, glist->group->qgroupid, - ptr_to_u64(glist->group), GFP_ATOMIC); - if (ret < 0) - return ret; - } - } - return 0; -} - -/* - * This adjusts the counters for all referenced qgroups if need be. + * Update qgroup rfer/excl counters. + * Rfer update is easy, codes can explain themselves. + * + * Excl update is tricky, the update is split into 2 part. + * Part 1: Possible exclusive <-> sharing detect: + * | A | !A | + * ------------------------------------- + * B | * | - | + * ------------------------------------- + * !B | + | ** | + * ------------------------------------- + * + * Conditions: + * A: cur_old_roots < nr_old_roots (not exclusive before) + * !A: cur_old_roots == nr_old_roots (possible exclusive before) + * B: cur_new_roots < nr_new_roots (not exclusive now) + * !B: cur_new_roots == nr_new_roots (possible exclsuive now) + * + * Results: + * +: Possible sharing -> exclusive -: Possible exclusive -> sharing + * *: Definitely not changed. **: Possible unchanged. + * + * For !A and !B condition, the exception is cur_old/new_roots == 0 case. + * + * To make the logic clear, we first use condition A and B to split + * combination into 4 results. + * + * Then, for result "+" and "-", check old/new_roots == 0 case, as in them + * only on variant maybe 0. + * + * Lastly, check result **, since there are 2 variants maybe 0, split them + * again(2x2). + * But this time we don't need to consider other things, the codes and logic + * is easy to understand now. */ -static int qgroup_adjust_counters(struct btrfs_fs_info *fs_info, - u64 root_to_skip, u64 num_bytes, - struct ulist *qgroups, u64 seq, - int old_roots, int new_roots, int rescan) +static int qgroup_update_counters(struct btrfs_fs_info *fs_info, + struct ulist *qgroups, + u64 nr_old_roots, + u64 nr_new_roots, + u64 num_bytes, u64 seq) { struct ulist_node *unode; struct ulist_iterator uiter; @@ -1810,423 +1588,196 @@ static int qgroup_adjust_counters(struct btrfs_fs_info *fs_info, bool dirty = false; qg = u64_to_ptr(unode->aux); - /* - * Wasn't referenced before but is now, add to the reference - * counters. - */ - if (qg->old_refcnt <= seq && qg->new_refcnt > seq) { + cur_old_count = btrfs_qgroup_get_old_refcnt(qg, seq); + cur_new_count = btrfs_qgroup_get_new_refcnt(qg, seq); + + /* Rfer update part */ + if (cur_old_count == 0 && cur_new_count > 0) { qg->rfer += num_bytes; qg->rfer_cmpr += num_bytes; dirty = true; } - - /* - * Was referenced before but isn't now, subtract from the - * reference counters. - */ - if (qg->old_refcnt > seq && qg->new_refcnt <= seq) { + if (cur_old_count > 0 && cur_new_count == 0) { qg->rfer -= num_bytes; qg->rfer_cmpr -= num_bytes; dirty = true; } - if (qg->old_refcnt < seq) - cur_old_count = 0; - else - cur_old_count = qg->old_refcnt - seq; - if (qg->new_refcnt < seq) - cur_new_count = 0; - else - cur_new_count = qg->new_refcnt - seq; + /* Excl update part */ + /* Exclusive/none -> shared case */ + if (cur_old_count == nr_old_roots && + cur_new_count < nr_new_roots) { + /* Exclusive -> shared */ + if (cur_old_count != 0) { + qg->excl -= num_bytes; + qg->excl_cmpr -= num_bytes; + dirty = true; + } + } - /* - * If our refcount was the same as the roots previously but our - * new count isn't the same as the number of roots now then we - * went from having a exclusive reference on this range to not. - */ - if (old_roots && cur_old_count == old_roots && - (cur_new_count != new_roots || new_roots == 0)) { - WARN_ON(cur_new_count != new_roots && new_roots == 0); - qg->excl -= num_bytes; - qg->excl_cmpr -= num_bytes; - dirty = true; + /* Shared -> exclusive/none case */ + if (cur_old_count < nr_old_roots && + cur_new_count == nr_new_roots) { + /* Shared->exclusive */ + if (cur_new_count != 0) { + qg->excl += num_bytes; + qg->excl_cmpr += num_bytes; + dirty = true; + } } - /* - * If we didn't reference all the roots before but now we do we - * have an exclusive reference to this range. - */ - if ((!old_roots || (old_roots && cur_old_count != old_roots)) - && cur_new_count == new_roots) { - qg->excl += num_bytes; - qg->excl_cmpr += num_bytes; - dirty = true; + /* Exclusive/none -> exclusive/none case */ + if (cur_old_count == nr_old_roots && + cur_new_count == nr_new_roots) { + if (cur_old_count == 0) { + /* None -> exclusive/none */ + + if (cur_new_count != 0) { + /* None -> exclusive */ + qg->excl += num_bytes; + qg->excl_cmpr += num_bytes; + dirty = true; + } + /* None -> none, nothing changed */ + } else { + /* Exclusive -> exclusive/none */ + + if (cur_new_count == 0) { + /* Exclusive -> none */ + qg->excl -= num_bytes; + qg->excl_cmpr -= num_bytes; + dirty = true; + } + /* Exclusive -> exclusive, nothing changed */ + } } + /* For exclusive extent, free its reserved bytes too */ + if (nr_old_roots == 0 && nr_new_roots == 1 && + cur_new_count == nr_new_roots) + qg->reserved -= num_bytes; if (dirty) qgroup_dirty(fs_info, qg); } return 0; } -/* - * If we removed a data extent and there were other references for that bytenr - * then we need to lookup all referenced roots to make sure we still don't - * reference this bytenr. If we do then we can just discard this operation. - */ -static int check_existing_refs(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, - struct btrfs_qgroup_operation *oper) -{ - struct ulist *roots = NULL; - struct ulist_node *unode; - struct ulist_iterator uiter; - int ret = 0; - - ret = btrfs_find_all_roots(trans, fs_info, oper->bytenr, - oper->elem.seq, &roots); - if (ret < 0) - return ret; - ret = 0; - - ULIST_ITER_INIT(&uiter); - while ((unode = ulist_next(roots, &uiter))) { - if (unode->val == oper->ref_root) { - ret = 1; - break; - } - } - ulist_free(roots); - btrfs_put_tree_mod_seq(fs_info, &oper->elem); - - return ret; -} - -/* - * If we share a reference across multiple roots then we may need to adjust - * various qgroups referenced and exclusive counters. The basic premise is this - * - * 1) We have seq to represent a 0 count. Instead of looping through all of the - * qgroups and resetting their refcount to 0 we just constantly bump this - * sequence number to act as the base reference count. This means that if - * anybody is equal to or below this sequence they were never referenced. We - * jack this sequence up by the number of roots we found each time in order to - * make sure we don't have any overlap. - * - * 2) We first search all the roots that reference the area _except_ the root - * we're acting on currently. This makes up the old_refcnt of all the qgroups - * before. - * - * 3) We walk all of the qgroups referenced by the root we are currently acting - * on, and will either adjust old_refcnt in the case of a removal or the - * new_refcnt in the case of an addition. - * - * 4) Finally we walk all the qgroups that are referenced by this range - * including the root we are acting on currently. We will adjust the counters - * based on the number of roots we had and will have after this operation. - * - * Take this example as an illustration - * - * [qgroup 1/0] - * / | \ - * [qg 0/0] [qg 0/1] [qg 0/2] - * \ | / - * [ extent ] - * - * Say we are adding a reference that is covered by qg 0/0. The first step - * would give a refcnt of 1 to qg 0/1 and 0/2 and a refcnt of 2 to qg 1/0 with - * old_roots being 2. Because it is adding new_roots will be 1. We then go - * through qg 0/0 which will get the new_refcnt set to 1 and add 1 to qg 1/0's - * new_refcnt, bringing it to 3. We then walk through all of the qgroups, we - * notice that the old refcnt for qg 0/0 < the new refcnt, so we added a - * reference and thus must add the size to the referenced bytes. Everything - * else is the same so nothing else changes. - */ -static int qgroup_shared_accounting(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, - struct btrfs_qgroup_operation *oper) +int +btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info, + u64 bytenr, u64 num_bytes, + struct ulist *old_roots, struct ulist *new_roots) { - struct ulist *roots = NULL; - struct ulist *qgroups, *tmp; - struct btrfs_qgroup *qgroup; - struct seq_list elem = SEQ_LIST_INIT(elem); + struct ulist *qgroups = NULL; + struct ulist *tmp = NULL; u64 seq; - int old_roots = 0; - int new_roots = 0; + u64 nr_new_roots = 0; + u64 nr_old_roots = 0; int ret = 0; - if (oper->elem.seq) { - ret = check_existing_refs(trans, fs_info, oper); - if (ret < 0) - return ret; - if (ret) - return 0; - } + if (new_roots) + nr_new_roots = new_roots->nnodes; + if (old_roots) + nr_old_roots = old_roots->nnodes; - qgroups = ulist_alloc(GFP_NOFS); - if (!qgroups) - return -ENOMEM; + if (!fs_info->quota_enabled) + goto out_free; + BUG_ON(!fs_info->quota_root); + qgroups = ulist_alloc(GFP_NOFS); + if (!qgroups) { + ret = -ENOMEM; + goto out_free; + } tmp = ulist_alloc(GFP_NOFS); if (!tmp) { - ulist_free(qgroups); - return -ENOMEM; + ret = -ENOMEM; + goto out_free; } - btrfs_get_tree_mod_seq(fs_info, &elem); - ret = btrfs_find_all_roots(trans, fs_info, oper->bytenr, elem.seq, - &roots); - btrfs_put_tree_mod_seq(fs_info, &elem); - if (ret < 0) { - ulist_free(qgroups); - ulist_free(tmp); - return ret; + mutex_lock(&fs_info->qgroup_rescan_lock); + if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) { + if (fs_info->qgroup_rescan_progress.objectid <= bytenr) { + mutex_unlock(&fs_info->qgroup_rescan_lock); + ret = 0; + goto out_free; + } } + mutex_unlock(&fs_info->qgroup_rescan_lock); + spin_lock(&fs_info->qgroup_lock); - qgroup = find_qgroup_rb(fs_info, oper->ref_root); - if (!qgroup) - goto out; seq = fs_info->qgroup_seq; - /* - * So roots is the list of all the roots currently pointing at the - * bytenr, including the ref we are adding if we are adding, or not if - * we are removing a ref. So we pass in the ref_root to skip that root - * in our calculations. We set old_refnct and new_refcnt cause who the - * hell knows what everything looked like before, and it doesn't matter - * except... - */ - ret = qgroup_calc_old_refcnt(fs_info, oper->ref_root, tmp, roots, qgroups, - seq, &old_roots, 0); - if (ret < 0) - goto out; - - /* - * Now adjust the refcounts of the qgroups that care about this - * reference, either the old_count in the case of removal or new_count - * in the case of an addition. - */ - ret = qgroup_calc_new_refcnt(fs_info, oper, qgroup, tmp, qgroups, - seq); + /* Update old refcnts using old_roots */ + ret = qgroup_update_refcnt(fs_info, old_roots, tmp, qgroups, seq, + UPDATE_OLD); if (ret < 0) goto out; - /* - * ...in the case of removals. If we had a removal before we got around - * to processing this operation then we need to find that guy and count - * his references as if they really existed so we don't end up screwing - * up the exclusive counts. Then whenever we go to process the delete - * everything will be grand and we can account for whatever exclusive - * changes need to be made there. We also have to pass in old_roots so - * we have an accurate count of the roots as it pertains to this - * operations view of the world. - */ - ret = qgroup_account_deleted_refs(fs_info, oper, tmp, qgroups, seq, - &old_roots); + /* Update new refcnts using new_roots */ + ret = qgroup_update_refcnt(fs_info, new_roots, tmp, qgroups, seq, + UPDATE_NEW); if (ret < 0) goto out; - /* - * We are adding our root, need to adjust up the number of roots, - * otherwise old_roots is the number of roots we want. - */ - if (oper->type == BTRFS_QGROUP_OPER_ADD_SHARED) { - new_roots = old_roots + 1; - } else { - new_roots = old_roots; - old_roots++; - } - fs_info->qgroup_seq += old_roots + 1; - + qgroup_update_counters(fs_info, qgroups, nr_old_roots, nr_new_roots, + num_bytes, seq); /* - * And now the magic happens, bless Arne for having a pretty elegant - * solution for this. + * Bump qgroup_seq to avoid seq overlap */ - qgroup_adjust_counters(fs_info, oper->ref_root, oper->num_bytes, - qgroups, seq, old_roots, new_roots, 0); + fs_info->qgroup_seq += max(nr_old_roots, nr_new_roots) + 1; out: spin_unlock(&fs_info->qgroup_lock); - ulist_free(qgroups); - ulist_free(roots); +out_free: ulist_free(tmp); + ulist_free(qgroups); + ulist_free(old_roots); + ulist_free(new_roots); return ret; } -/* - * Process a reference to a shared subtree. This type of operation is - * queued during snapshot removal when we encounter extents which are - * shared between more than one root. - */ -static int qgroup_subtree_accounting(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, - struct btrfs_qgroup_operation *oper) -{ - struct ulist *roots = NULL; - struct ulist_node *unode; - struct ulist_iterator uiter; - struct btrfs_qgroup_list *glist; - struct ulist *parents; - int ret = 0; - int err; - struct btrfs_qgroup *qg; - u64 root_obj = 0; - struct seq_list elem = SEQ_LIST_INIT(elem); - - parents = ulist_alloc(GFP_NOFS); - if (!parents) - return -ENOMEM; - - btrfs_get_tree_mod_seq(fs_info, &elem); - ret = btrfs_find_all_roots(trans, fs_info, oper->bytenr, - elem.seq, &roots); - btrfs_put_tree_mod_seq(fs_info, &elem); - if (ret < 0) - goto out; - - if (roots->nnodes != 1) - goto out; - - ULIST_ITER_INIT(&uiter); - unode = ulist_next(roots, &uiter); /* Only want 1 so no need to loop */ - /* - * If we find our ref root then that means all refs - * this extent has to the root have not yet been - * deleted. In that case, we do nothing and let the - * last ref for this bytenr drive our update. - * - * This can happen for example if an extent is - * referenced multiple times in a snapshot (clone, - * etc). If we are in the middle of snapshot removal, - * queued updates for such an extent will find the - * root if we have not yet finished removing the - * snapshot. - */ - if (unode->val == oper->ref_root) - goto out; - - root_obj = unode->val; - BUG_ON(!root_obj); - - spin_lock(&fs_info->qgroup_lock); - qg = find_qgroup_rb(fs_info, root_obj); - if (!qg) - goto out_unlock; - - qg->excl += oper->num_bytes; - qg->excl_cmpr += oper->num_bytes; - qgroup_dirty(fs_info, qg); - - /* - * Adjust counts for parent groups. First we find all - * parents, then in the 2nd loop we do the adjustment - * while adding parents of the parents to our ulist. - */ - list_for_each_entry(glist, &qg->groups, next_group) { - err = ulist_add(parents, glist->group->qgroupid, - ptr_to_u64(glist->group), GFP_ATOMIC); - if (err < 0) { - ret = err; - goto out_unlock; - } - } - - ULIST_ITER_INIT(&uiter); - while ((unode = ulist_next(parents, &uiter))) { - qg = u64_to_ptr(unode->aux); - qg->excl += oper->num_bytes; - qg->excl_cmpr += oper->num_bytes; - qgroup_dirty(fs_info, qg); - - /* Add any parents of the parents */ - list_for_each_entry(glist, &qg->groups, next_group) { - err = ulist_add(parents, glist->group->qgroupid, - ptr_to_u64(glist->group), GFP_ATOMIC); - if (err < 0) { - ret = err; - goto out_unlock; - } - } - } - -out_unlock: - spin_unlock(&fs_info->qgroup_lock); - -out: - ulist_free(roots); - ulist_free(parents); - return ret; -} - -/* - * btrfs_qgroup_account_ref is called for every ref that is added to or deleted - * from the fs. First, all roots referencing the extent are searched, and - * then the space is accounted accordingly to the different roots. The - * accounting algorithm works in 3 steps documented inline. - */ -static int btrfs_qgroup_account(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, - struct btrfs_qgroup_operation *oper) +int btrfs_qgroup_account_extents(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info) { + struct btrfs_qgroup_extent_record *record; + struct btrfs_delayed_ref_root *delayed_refs; + struct ulist *new_roots = NULL; + struct rb_node *node; + u64 qgroup_to_skip; int ret = 0; - if (!fs_info->quota_enabled) - return 0; - - BUG_ON(!fs_info->quota_root); + delayed_refs = &trans->transaction->delayed_refs; + qgroup_to_skip = delayed_refs->qgroup_to_skip; + while ((node = rb_first(&delayed_refs->dirty_extent_root))) { + record = rb_entry(node, struct btrfs_qgroup_extent_record, + node); - mutex_lock(&fs_info->qgroup_rescan_lock); - if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) { - if (fs_info->qgroup_rescan_progress.objectid <= oper->bytenr) { - mutex_unlock(&fs_info->qgroup_rescan_lock); - return 0; + if (!ret) { + /* + * Use (u64)-1 as time_seq to do special search, which + * doesn't lock tree or delayed_refs and search current + * root. It's safe inside commit_transaction(). + */ + ret = btrfs_find_all_roots(trans, fs_info, + record->bytenr, (u64)-1, &new_roots); + if (ret < 0) + goto cleanup; + if (qgroup_to_skip) + ulist_del(new_roots, qgroup_to_skip, 0); + ret = btrfs_qgroup_account_extent(trans, fs_info, + record->bytenr, record->num_bytes, + record->old_roots, new_roots); + record->old_roots = NULL; + new_roots = NULL; } - } - mutex_unlock(&fs_info->qgroup_rescan_lock); - - ASSERT(is_fstree(oper->ref_root)); - - trace_btrfs_qgroup_account(oper); - - switch (oper->type) { - case BTRFS_QGROUP_OPER_ADD_EXCL: - case BTRFS_QGROUP_OPER_SUB_EXCL: - ret = qgroup_excl_accounting(fs_info, oper); - break; - case BTRFS_QGROUP_OPER_ADD_SHARED: - case BTRFS_QGROUP_OPER_SUB_SHARED: - ret = qgroup_shared_accounting(trans, fs_info, oper); - break; - case BTRFS_QGROUP_OPER_SUB_SUBTREE: - ret = qgroup_subtree_accounting(trans, fs_info, oper); - break; - default: - ASSERT(0); - } - return ret; -} - -/* - * Needs to be called everytime we run delayed refs, even if there is an error - * in order to cleanup outstanding operations. - */ -int btrfs_delayed_qgroup_accounting(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info) -{ - struct btrfs_qgroup_operation *oper; - int ret = 0; +cleanup: + ulist_free(record->old_roots); + ulist_free(new_roots); + new_roots = NULL; + rb_erase(node, &delayed_refs->dirty_extent_root); + kfree(record); - while (!list_empty(&trans->qgroup_ref_list)) { - oper = list_first_entry(&trans->qgroup_ref_list, - struct btrfs_qgroup_operation, list); - list_del_init(&oper->list); - if (!ret || !trans->aborted) - ret = btrfs_qgroup_account(trans, fs_info, oper); - spin_lock(&fs_info->qgroup_op_lock); - rb_erase(&oper->n, &fs_info->qgroup_op_tree); - spin_unlock(&fs_info->qgroup_op_lock); - btrfs_put_tree_mod_seq(fs_info, &oper->elem); - kfree(oper); } return ret; } @@ -2637,15 +2188,13 @@ void assert_qgroups_uptodate(struct btrfs_trans_handle *trans) */ static int qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path, - struct btrfs_trans_handle *trans, struct ulist *qgroups, - struct ulist *tmp, struct extent_buffer *scratch_leaf) + struct btrfs_trans_handle *trans, + struct extent_buffer *scratch_leaf) { struct btrfs_key found; struct ulist *roots = NULL; struct seq_list tree_mod_seq_elem = SEQ_LIST_INIT(tree_mod_seq_elem); u64 num_bytes; - u64 seq; - int new_roots; int slot; int ret; @@ -2695,33 +2244,15 @@ qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path, else num_bytes = found.offset; - ulist_reinit(qgroups); ret = btrfs_find_all_roots(NULL, fs_info, found.objectid, 0, &roots); if (ret < 0) goto out; - spin_lock(&fs_info->qgroup_lock); - seq = fs_info->qgroup_seq; - fs_info->qgroup_seq += roots->nnodes + 1; /* max refcnt */ - - new_roots = 0; - ret = qgroup_calc_old_refcnt(fs_info, 0, tmp, roots, qgroups, - seq, &new_roots, 1); - if (ret < 0) { - spin_unlock(&fs_info->qgroup_lock); - ulist_free(roots); - goto out; - } - - ret = qgroup_adjust_counters(fs_info, 0, num_bytes, qgroups, - seq, 0, new_roots, 1); - if (ret < 0) { - spin_unlock(&fs_info->qgroup_lock); - ulist_free(roots); + /* For rescan, just pass old_roots as NULL */ + ret = btrfs_qgroup_account_extent(trans, fs_info, + found.objectid, num_bytes, NULL, roots); + if (ret < 0) goto out; - } - spin_unlock(&fs_info->qgroup_lock); - ulist_free(roots); } out: btrfs_put_tree_mod_seq(fs_info, &tree_mod_seq_elem); @@ -2735,7 +2266,6 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work) qgroup_rescan_work); struct btrfs_path *path; struct btrfs_trans_handle *trans = NULL; - struct ulist *tmp = NULL, *qgroups = NULL; struct extent_buffer *scratch_leaf = NULL; int err = -ENOMEM; int ret = 0; @@ -2743,12 +2273,6 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work) path = btrfs_alloc_path(); if (!path) goto out; - qgroups = ulist_alloc(GFP_NOFS); - if (!qgroups) - goto out; - tmp = ulist_alloc(GFP_NOFS); - if (!tmp) - goto out; scratch_leaf = kmalloc(sizeof(*scratch_leaf), GFP_NOFS); if (!scratch_leaf) goto out; @@ -2764,7 +2288,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work) err = -EINTR; } else { err = qgroup_rescan_leaf(fs_info, path, trans, - qgroups, tmp, scratch_leaf); + scratch_leaf); } if (err > 0) btrfs_commit_transaction(trans, fs_info->fs_root); @@ -2774,8 +2298,6 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work) out: kfree(scratch_leaf); - ulist_free(qgroups); - ulist_free(tmp); btrfs_free_path(path); mutex_lock(&fs_info->qgroup_rescan_lock); diff --git a/fs/btrfs/qgroup.h b/fs/btrfs/qgroup.h index c5242aa9a..6387dcfa3 100644 --- a/fs/btrfs/qgroup.h +++ b/fs/btrfs/qgroup.h @@ -19,43 +19,18 @@ #ifndef __BTRFS_QGROUP__ #define __BTRFS_QGROUP__ +#include "ulist.h" +#include "delayed-ref.h" + /* - * A description of the operations, all of these operations only happen when we - * are adding the 1st reference for that subvolume in the case of adding space - * or on the last reference delete in the case of subtraction. The only - * exception is the last one, which is added for confusion. - * - * BTRFS_QGROUP_OPER_ADD_EXCL: adding bytes where this subvolume is the only - * one pointing at the bytes we are adding. This is called on the first - * allocation. - * - * BTRFS_QGROUP_OPER_ADD_SHARED: adding bytes where this bytenr is going to be - * shared between subvols. This is called on the creation of a ref that already - * has refs from a different subvolume, so basically reflink. - * - * BTRFS_QGROUP_OPER_SUB_EXCL: removing bytes where this subvolume is the only - * one referencing the range. - * - * BTRFS_QGROUP_OPER_SUB_SHARED: removing bytes where this subvolume shares with - * refs with other subvolumes. + * Record a dirty extent, and info qgroup to update quota on it + * TODO: Use kmem cache to alloc it. */ -enum btrfs_qgroup_operation_type { - BTRFS_QGROUP_OPER_ADD_EXCL, - BTRFS_QGROUP_OPER_ADD_SHARED, - BTRFS_QGROUP_OPER_SUB_EXCL, - BTRFS_QGROUP_OPER_SUB_SHARED, - BTRFS_QGROUP_OPER_SUB_SUBTREE, -}; - -struct btrfs_qgroup_operation { - u64 ref_root; +struct btrfs_qgroup_extent_record { + struct rb_node node; u64 bytenr; u64 num_bytes; - u64 seq; - enum btrfs_qgroup_operation_type type; - struct seq_list elem; - struct rb_node n; - struct list_head list; + struct ulist *old_roots; }; int btrfs_quota_enable(struct btrfs_trans_handle *trans, @@ -79,16 +54,18 @@ int btrfs_limit_qgroup(struct btrfs_trans_handle *trans, int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info); void btrfs_free_qgroup_config(struct btrfs_fs_info *fs_info); struct btrfs_delayed_extent_op; -int btrfs_qgroup_record_ref(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, u64 ref_root, +int btrfs_qgroup_prepare_account_extents(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info); +struct btrfs_qgroup_extent_record +*btrfs_qgroup_insert_dirty_extent(struct btrfs_delayed_ref_root *delayed_refs, + struct btrfs_qgroup_extent_record *record); +int +btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info, u64 bytenr, u64 num_bytes, - enum btrfs_qgroup_operation_type type, - int mod_seq); -int btrfs_delayed_qgroup_accounting(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info); -void btrfs_remove_qgroup_operation(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, - struct btrfs_qgroup_operation *oper); + struct ulist *old_roots, struct ulist *new_roots); +int btrfs_qgroup_account_extents(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info); int btrfs_run_qgroups(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info); int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 74b24b01d..88cbb5995 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -1847,8 +1847,10 @@ again: } eb = read_tree_block(dest, old_bytenr, old_ptr_gen); - if (!eb || !extent_buffer_uptodate(eb)) { - ret = (!eb) ? -ENOMEM : -EIO; + if (IS_ERR(eb)) { + ret = PTR_ERR(eb); + } else if (!extent_buffer_uptodate(eb)) { + ret = -EIO; free_extent_buffer(eb); break; } @@ -2002,7 +2004,9 @@ int walk_down_reloc_tree(struct btrfs_root *root, struct btrfs_path *path, bytenr = btrfs_node_blockptr(eb, path->slots[i]); eb = read_tree_block(root, bytenr, ptr_gen); - if (!eb || !extent_buffer_uptodate(eb)) { + if (IS_ERR(eb)) { + return PTR_ERR(eb); + } else if (!extent_buffer_uptodate(eb)) { free_extent_buffer(eb); return -EIO; } @@ -2710,7 +2714,10 @@ static int do_relocation(struct btrfs_trans_handle *trans, blocksize = root->nodesize; generation = btrfs_node_ptr_generation(upper->eb, slot); eb = read_tree_block(root, bytenr, generation); - if (!eb || !extent_buffer_uptodate(eb)) { + if (IS_ERR(eb)) { + err = PTR_ERR(eb); + goto next; + } else if (!extent_buffer_uptodate(eb)) { free_extent_buffer(eb); err = -EIO; goto next; @@ -2873,7 +2880,9 @@ static int get_tree_block_key(struct reloc_control *rc, BUG_ON(block->key_ready); eb = read_tree_block(rc->extent_root, block->bytenr, block->key.offset); - if (!eb || !extent_buffer_uptodate(eb)) { + if (IS_ERR(eb)) { + return PTR_ERR(eb); + } else if (!extent_buffer_uptodate(eb)) { free_extent_buffer(eb); return -EIO; } @@ -4040,7 +4049,7 @@ restart: if (trans && progress && err == -ENOSPC) { ret = btrfs_force_chunk_alloc(trans, rc->extent_root, rc->block_group->flags); - if (ret == 0) { + if (ret == 1) { err = 0; progress = 0; goto restart; diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index ab5811545..94db0fa52 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -2662,18 +2662,30 @@ static void scrub_free_parity(struct scrub_parity *sparity) kfree(sparity); } +static void scrub_parity_bio_endio_worker(struct btrfs_work *work) +{ + struct scrub_parity *sparity = container_of(work, struct scrub_parity, + work); + struct scrub_ctx *sctx = sparity->sctx; + + scrub_free_parity(sparity); + scrub_pending_bio_dec(sctx); +} + static void scrub_parity_bio_endio(struct bio *bio, int error) { struct scrub_parity *sparity = (struct scrub_parity *)bio->bi_private; - struct scrub_ctx *sctx = sparity->sctx; if (error) bitmap_or(sparity->ebitmap, sparity->ebitmap, sparity->dbitmap, sparity->nsectors); - scrub_free_parity(sparity); - scrub_pending_bio_dec(sctx); bio_put(bio); + + btrfs_init_work(&sparity->work, btrfs_scrubparity_helper, + scrub_parity_bio_endio_worker, NULL, NULL); + btrfs_queue_work(sparity->sctx->dev_root->fs_info->scrub_parity_workers, + &sparity->work); } static void scrub_parity_check_and_repair(struct scrub_parity *sparity) @@ -3559,7 +3571,6 @@ static noinline_for_stack int scrub_supers(struct scrub_ctx *sctx, static noinline_for_stack int scrub_workers_get(struct btrfs_fs_info *fs_info, int is_dev_replace) { - int ret = 0; unsigned int flags = WQ_FREEZABLE | WQ_UNBOUND; int max_active = fs_info->thread_pool_size; @@ -3572,27 +3583,36 @@ static noinline_for_stack int scrub_workers_get(struct btrfs_fs_info *fs_info, fs_info->scrub_workers = btrfs_alloc_workqueue("btrfs-scrub", flags, max_active, 4); - if (!fs_info->scrub_workers) { - ret = -ENOMEM; - goto out; - } + if (!fs_info->scrub_workers) + goto fail_scrub_workers; + fs_info->scrub_wr_completion_workers = btrfs_alloc_workqueue("btrfs-scrubwrc", flags, max_active, 2); - if (!fs_info->scrub_wr_completion_workers) { - ret = -ENOMEM; - goto out; - } + if (!fs_info->scrub_wr_completion_workers) + goto fail_scrub_wr_completion_workers; + fs_info->scrub_nocow_workers = btrfs_alloc_workqueue("btrfs-scrubnc", flags, 1, 0); - if (!fs_info->scrub_nocow_workers) { - ret = -ENOMEM; - goto out; - } + if (!fs_info->scrub_nocow_workers) + goto fail_scrub_nocow_workers; + fs_info->scrub_parity_workers = + btrfs_alloc_workqueue("btrfs-scrubparity", flags, + max_active, 2); + if (!fs_info->scrub_parity_workers) + goto fail_scrub_parity_workers; } ++fs_info->scrub_workers_refcnt; -out: - return ret; + return 0; + +fail_scrub_parity_workers: + btrfs_destroy_workqueue(fs_info->scrub_nocow_workers); +fail_scrub_nocow_workers: + btrfs_destroy_workqueue(fs_info->scrub_wr_completion_workers); +fail_scrub_wr_completion_workers: + btrfs_destroy_workqueue(fs_info->scrub_workers); +fail_scrub_workers: + return -ENOMEM; } static noinline_for_stack void scrub_workers_put(struct btrfs_fs_info *fs_info) @@ -3601,6 +3621,7 @@ static noinline_for_stack void scrub_workers_put(struct btrfs_fs_info *fs_info) btrfs_destroy_workqueue(fs_info->scrub_workers); btrfs_destroy_workqueue(fs_info->scrub_wr_completion_workers); btrfs_destroy_workqueue(fs_info->scrub_nocow_workers); + btrfs_destroy_workqueue(fs_info->scrub_parity_workers); } WARN_ON(fs_info->scrub_workers_refcnt < 0); } diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 5cf7838fb..aa72bfd28 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -243,6 +243,7 @@ struct waiting_dir_move { * after this directory is moved, we can try to rmdir the ino rmdir_ino. */ u64 rmdir_ino; + bool orphanized; }; struct orphan_dir_info { @@ -1916,8 +1917,13 @@ static int did_overwrite_ref(struct send_ctx *sctx, goto out; } - /* we know that it is or will be overwritten. check this now */ - if (ow_inode < sctx->send_progress) + /* + * We know that it is or will be overwritten. Check this now. + * The current inode being processed might have been the one that caused + * inode 'ino' to be orphanized, therefore ow_inode can actually be the + * same as sctx->send_progress. + */ + if (ow_inode <= sctx->send_progress) ret = 1; else ret = 0; @@ -2239,6 +2245,8 @@ static int get_cur_path(struct send_ctx *sctx, u64 ino, u64 gen, fs_path_reset(dest); while (!stop && ino != BTRFS_FIRST_FREE_OBJECTID) { + struct waiting_dir_move *wdm; + fs_path_reset(name); if (is_waiting_for_rm(sctx, ino)) { @@ -2249,7 +2257,11 @@ static int get_cur_path(struct send_ctx *sctx, u64 ino, u64 gen, break; } - if (is_waiting_for_move(sctx, ino)) { + wdm = get_waiting_dir_move(sctx, ino); + if (wdm && wdm->orphanized) { + ret = gen_unique_name(sctx, ino, gen, name); + stop = 1; + } else if (wdm) { ret = get_first_ref(sctx->parent_root, ino, &parent_inode, &parent_gen, name); } else { @@ -2344,8 +2356,12 @@ static int send_subvol_begin(struct send_ctx *sctx) TLV_PUT_U64(sctx, BTRFS_SEND_A_CTRANSID, le64_to_cpu(sctx->send_root->root_item.ctransid)); if (parent_root) { - TLV_PUT_UUID(sctx, BTRFS_SEND_A_CLONE_UUID, - sctx->parent_root->root_item.uuid); + if (!btrfs_is_empty_uuid(parent_root->root_item.received_uuid)) + TLV_PUT_UUID(sctx, BTRFS_SEND_A_CLONE_UUID, + parent_root->root_item.received_uuid); + else + TLV_PUT_UUID(sctx, BTRFS_SEND_A_CLONE_UUID, + parent_root->root_item.uuid); TLV_PUT_U64(sctx, BTRFS_SEND_A_CLONE_CTRANSID, le64_to_cpu(sctx->parent_root->root_item.ctransid)); } @@ -2939,7 +2955,7 @@ static int is_waiting_for_move(struct send_ctx *sctx, u64 ino) return entry != NULL; } -static int add_waiting_dir_move(struct send_ctx *sctx, u64 ino) +static int add_waiting_dir_move(struct send_ctx *sctx, u64 ino, bool orphanized) { struct rb_node **p = &sctx->waiting_dir_moves.rb_node; struct rb_node *parent = NULL; @@ -2950,6 +2966,7 @@ static int add_waiting_dir_move(struct send_ctx *sctx, u64 ino) return -ENOMEM; dm->ino = ino; dm->rmdir_ino = 0; + dm->orphanized = orphanized; while (*p) { parent = *p; @@ -3046,7 +3063,7 @@ static int add_pending_dir_move(struct send_ctx *sctx, goto out; } - ret = add_waiting_dir_move(sctx, pm->ino); + ret = add_waiting_dir_move(sctx, pm->ino, is_orphan); if (ret) goto out; @@ -3369,8 +3386,40 @@ out: return ret; } +/* + * Check if ino ino1 is an ancestor of inode ino2 in the given root. + * Return 1 if true, 0 if false and < 0 on error. + */ +static int is_ancestor(struct btrfs_root *root, + const u64 ino1, + const u64 ino1_gen, + const u64 ino2, + struct fs_path *fs_path) +{ + u64 ino = ino2; + + while (ino > BTRFS_FIRST_FREE_OBJECTID) { + int ret; + u64 parent; + u64 parent_gen; + + fs_path_reset(fs_path); + ret = get_first_ref(root, ino, &parent, &parent_gen, fs_path); + if (ret < 0) { + if (ret == -ENOENT && ino == ino2) + ret = 0; + return ret; + } + if (parent == ino1) + return parent_gen == ino1_gen ? 1 : 0; + ino = parent; + } + return 0; +} + static int wait_for_parent_move(struct send_ctx *sctx, - struct recorded_ref *parent_ref) + struct recorded_ref *parent_ref, + const bool is_orphan) { int ret = 0; u64 ino = parent_ref->dir; @@ -3390,11 +3439,24 @@ static int wait_for_parent_move(struct send_ctx *sctx, * Our current directory inode may not yet be renamed/moved because some * ancestor (immediate or not) has to be renamed/moved first. So find if * such ancestor exists and make sure our own rename/move happens after - * that ancestor is processed. + * that ancestor is processed to avoid path build infinite loops (done + * at get_cur_path()). */ while (ino > BTRFS_FIRST_FREE_OBJECTID) { if (is_waiting_for_move(sctx, ino)) { - ret = 1; + /* + * If the current inode is an ancestor of ino in the + * parent root, we need to delay the rename of the + * current inode, otherwise don't delayed the rename + * because we can end up with a circular dependency + * of renames, resulting in some directories never + * getting the respective rename operations issued in + * the send stream or getting into infinite path build + * loops. + */ + ret = is_ancestor(sctx->parent_root, + sctx->cur_ino, sctx->cur_inode_gen, + ino, path_before); break; } @@ -3436,7 +3498,7 @@ out: ino, &sctx->new_refs, &sctx->deleted_refs, - false); + is_orphan); if (!ret) ret = 1; } @@ -3605,6 +3667,17 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino); } } + if (S_ISDIR(sctx->cur_inode_mode) && sctx->parent_root && + can_rename) { + ret = wait_for_parent_move(sctx, cur, is_orphan); + if (ret < 0) + goto out; + if (ret == 1) { + can_rename = false; + *pending_move = 1; + } + } + /* * link/move the ref to the new place. If we have an orphan * inode, move it and update valid_path. If not, link or move @@ -3625,18 +3698,11 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino); * dirs, we always have one new and one deleted * ref. The deleted ref is ignored later. */ - ret = wait_for_parent_move(sctx, cur); - if (ret < 0) - goto out; - if (ret) { - *pending_move = 1; - } else { - ret = send_rename(sctx, valid_path, - cur->full_path); - if (!ret) - ret = fs_path_copy(valid_path, - cur->full_path); - } + ret = send_rename(sctx, valid_path, + cur->full_path); + if (!ret) + ret = fs_path_copy(valid_path, + cur->full_path); if (ret < 0) goto out; } else { @@ -4524,8 +4590,21 @@ verbose_printk("btrfs: send_clone offset=%llu, len=%d, clone_root=%llu, " if (ret < 0) goto out; - TLV_PUT_UUID(sctx, BTRFS_SEND_A_CLONE_UUID, - clone_root->root->root_item.uuid); + /* + * If the parent we're using has a received_uuid set then use that as + * our clone source as that is what we will look for when doing a + * receive. + * + * This covers the case that we create a snapshot off of a received + * subvolume and then use that as the parent and try to receive on a + * different host. + */ + if (!btrfs_is_empty_uuid(clone_root->root->root_item.received_uuid)) + TLV_PUT_UUID(sctx, BTRFS_SEND_A_CLONE_UUID, + clone_root->root->root_item.received_uuid); + else + TLV_PUT_UUID(sctx, BTRFS_SEND_A_CLONE_UUID, + clone_root->root->root_item.uuid); TLV_PUT_U64(sctx, BTRFS_SEND_A_CLONE_CTRANSID, le64_to_cpu(clone_root->root->root_item.ctransid)); TLV_PUT_PATH(sctx, BTRFS_SEND_A_CLONE_PATH, p); diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 9e66f5e72..cd7ef34d2 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -135,6 +135,7 @@ static void btrfs_handle_error(struct btrfs_fs_info *fs_info) * __btrfs_std_error decodes expected errors from the caller and * invokes the approciate error response. */ +__cold void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function, unsigned int line, int errno, const char *fmt, ...) { @@ -247,18 +248,11 @@ void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function, * We'll complete the cleanup in btrfs_end_transaction and * btrfs_commit_transaction. */ +__cold void __btrfs_abort_transaction(struct btrfs_trans_handle *trans, struct btrfs_root *root, const char *function, unsigned int line, int errno) { - /* - * Report first abort since mount - */ - if (!test_and_set_bit(BTRFS_FS_STATE_TRANS_ABORTED, - &root->fs_info->fs_state)) { - WARN(1, KERN_DEBUG "BTRFS: Transaction aborted (error %d)\n", - errno); - } trans->aborted = errno; /* Nothing used. The other threads that have joined this * transaction may be able to continue. */ @@ -281,6 +275,7 @@ void __btrfs_abort_transaction(struct btrfs_trans_handle *trans, * __btrfs_panic decodes unexpected, fatal errors from the caller, * issues an alert, and either panics or BUGs, depending on mount options. */ +__cold void __btrfs_panic(struct btrfs_fs_info *fs_info, const char *function, unsigned int line, int errno, const char *fmt, ...) { @@ -841,33 +836,153 @@ out: return error; } -static struct dentry *get_default_root(struct super_block *sb, - u64 subvol_objectid) +static char *get_subvol_name_from_objectid(struct btrfs_fs_info *fs_info, + u64 subvol_objectid) { - struct btrfs_fs_info *fs_info = btrfs_sb(sb); struct btrfs_root *root = fs_info->tree_root; - struct btrfs_root *new_root; - struct btrfs_dir_item *di; - struct btrfs_path *path; - struct btrfs_key location; - struct inode *inode; - u64 dir_id; - int new = 0; + struct btrfs_root *fs_root; + struct btrfs_root_ref *root_ref; + struct btrfs_inode_ref *inode_ref; + struct btrfs_key key; + struct btrfs_path *path = NULL; + char *name = NULL, *ptr; + u64 dirid; + int len; + int ret; + + path = btrfs_alloc_path(); + if (!path) { + ret = -ENOMEM; + goto err; + } + path->leave_spinning = 1; + + name = kmalloc(PATH_MAX, GFP_NOFS); + if (!name) { + ret = -ENOMEM; + goto err; + } + ptr = name + PATH_MAX - 1; + ptr[0] = '\0'; /* - * We have a specific subvol we want to mount, just setup location and - * go look up the root. + * Walk up the subvolume trees in the tree of tree roots by root + * backrefs until we hit the top-level subvolume. */ - if (subvol_objectid) { - location.objectid = subvol_objectid; - location.type = BTRFS_ROOT_ITEM_KEY; - location.offset = (u64)-1; - goto find_root; + while (subvol_objectid != BTRFS_FS_TREE_OBJECTID) { + key.objectid = subvol_objectid; + key.type = BTRFS_ROOT_BACKREF_KEY; + key.offset = (u64)-1; + + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + if (ret < 0) { + goto err; + } else if (ret > 0) { + ret = btrfs_previous_item(root, path, subvol_objectid, + BTRFS_ROOT_BACKREF_KEY); + if (ret < 0) { + goto err; + } else if (ret > 0) { + ret = -ENOENT; + goto err; + } + } + + btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + subvol_objectid = key.offset; + + root_ref = btrfs_item_ptr(path->nodes[0], path->slots[0], + struct btrfs_root_ref); + len = btrfs_root_ref_name_len(path->nodes[0], root_ref); + ptr -= len + 1; + if (ptr < name) { + ret = -ENAMETOOLONG; + goto err; + } + read_extent_buffer(path->nodes[0], ptr + 1, + (unsigned long)(root_ref + 1), len); + ptr[0] = '/'; + dirid = btrfs_root_ref_dirid(path->nodes[0], root_ref); + btrfs_release_path(path); + + key.objectid = subvol_objectid; + key.type = BTRFS_ROOT_ITEM_KEY; + key.offset = (u64)-1; + fs_root = btrfs_read_fs_root_no_name(fs_info, &key); + if (IS_ERR(fs_root)) { + ret = PTR_ERR(fs_root); + goto err; + } + + /* + * Walk up the filesystem tree by inode refs until we hit the + * root directory. + */ + while (dirid != BTRFS_FIRST_FREE_OBJECTID) { + key.objectid = dirid; + key.type = BTRFS_INODE_REF_KEY; + key.offset = (u64)-1; + + ret = btrfs_search_slot(NULL, fs_root, &key, path, 0, 0); + if (ret < 0) { + goto err; + } else if (ret > 0) { + ret = btrfs_previous_item(fs_root, path, dirid, + BTRFS_INODE_REF_KEY); + if (ret < 0) { + goto err; + } else if (ret > 0) { + ret = -ENOENT; + goto err; + } + } + + btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + dirid = key.offset; + + inode_ref = btrfs_item_ptr(path->nodes[0], + path->slots[0], + struct btrfs_inode_ref); + len = btrfs_inode_ref_name_len(path->nodes[0], + inode_ref); + ptr -= len + 1; + if (ptr < name) { + ret = -ENAMETOOLONG; + goto err; + } + read_extent_buffer(path->nodes[0], ptr + 1, + (unsigned long)(inode_ref + 1), len); + ptr[0] = '/'; + btrfs_release_path(path); + } } + btrfs_free_path(path); + if (ptr == name + PATH_MAX - 1) { + name[0] = '/'; + name[1] = '\0'; + } else { + memmove(name, ptr, name + PATH_MAX - ptr); + } + return name; + +err: + btrfs_free_path(path); + kfree(name); + return ERR_PTR(ret); +} + +static int get_default_subvol_objectid(struct btrfs_fs_info *fs_info, u64 *objectid) +{ + struct btrfs_root *root = fs_info->tree_root; + struct btrfs_dir_item *di; + struct btrfs_path *path; + struct btrfs_key location; + u64 dir_id; + path = btrfs_alloc_path(); if (!path) - return ERR_PTR(-ENOMEM); + return -ENOMEM; path->leave_spinning = 1; /* @@ -879,58 +994,23 @@ static struct dentry *get_default_root(struct super_block *sb, di = btrfs_lookup_dir_item(NULL, root, path, dir_id, "default", 7, 0); if (IS_ERR(di)) { btrfs_free_path(path); - return ERR_CAST(di); + return PTR_ERR(di); } if (!di) { /* * Ok the default dir item isn't there. This is weird since * it's always been there, but don't freak out, just try and - * mount to root most subvolume. + * mount the top-level subvolume. */ btrfs_free_path(path); - dir_id = BTRFS_FIRST_FREE_OBJECTID; - new_root = fs_info->fs_root; - goto setup_root; + *objectid = BTRFS_FS_TREE_OBJECTID; + return 0; } btrfs_dir_item_key_to_cpu(path->nodes[0], di, &location); btrfs_free_path(path); - -find_root: - new_root = btrfs_read_fs_root_no_name(fs_info, &location); - if (IS_ERR(new_root)) - return ERR_CAST(new_root); - - if (!(sb->s_flags & MS_RDONLY)) { - int ret; - down_read(&fs_info->cleanup_work_sem); - ret = btrfs_orphan_cleanup(new_root); - up_read(&fs_info->cleanup_work_sem); - if (ret) - return ERR_PTR(ret); - } - - dir_id = btrfs_root_dirid(&new_root->root_item); -setup_root: - location.objectid = dir_id; - location.type = BTRFS_INODE_ITEM_KEY; - location.offset = 0; - - inode = btrfs_iget(sb, &location, new_root, &new); - if (IS_ERR(inode)) - return ERR_CAST(inode); - - /* - * If we're just mounting the root most subvol put the inode and return - * a reference to the dentry. We will have already gotten a reference - * to the inode in btrfs_fill_super so we're good to go. - */ - if (!new && d_inode(sb->s_root) == inode) { - iput(inode); - return dget(sb->s_root); - } - - return d_obtain_root(inode); + *objectid = location.objectid; + return 0; } static int btrfs_fill_super(struct super_block *sb, @@ -1108,6 +1188,10 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry) seq_puts(seq, ",fatal_errors=panic"); if (info->commit_interval != BTRFS_DEFAULT_COMMIT_INTERVAL) seq_printf(seq, ",commit=%d", info->commit_interval); + seq_printf(seq, ",subvolid=%llu", + BTRFS_I(d_inode(dentry))->root->root_key.objectid); + seq_puts(seq, ",subvol="); + seq_dentry(seq, dentry, " \t\n\\"); return 0; } @@ -1138,107 +1222,139 @@ static inline int is_subvolume_inode(struct inode *inode) } /* - * This will strip out the subvol=%s argument for an argument string and add - * subvolid=0 to make sure we get the actual tree root for path walking to the - * subvol we want. + * This will add subvolid=0 to the argument string while removing any subvol= + * and subvolid= arguments to make sure we get the top-level root for path + * walking to the subvol we want. */ static char *setup_root_args(char *args) { - unsigned len = strlen(args) + 2 + 1; - char *src, *dst, *buf; + char *buf, *dst, *sep; - /* - * We need the same args as before, but with this substitution: - * s!subvol=[^,]+!subvolid=0! - * - * Since the replacement string is up to 2 bytes longer than the - * original, allocate strlen(args) + 2 + 1 bytes. - */ + if (!args) + return kstrdup("subvolid=0", GFP_NOFS); - src = strstr(args, "subvol="); - /* This shouldn't happen, but just in case.. */ - if (!src) - return NULL; - - buf = dst = kmalloc(len, GFP_NOFS); + /* The worst case is that we add ",subvolid=0" to the end. */ + buf = dst = kmalloc(strlen(args) + strlen(",subvolid=0") + 1, GFP_NOFS); if (!buf) return NULL; - /* - * If the subvol= arg is not at the start of the string, - * copy whatever precedes it into buf. - */ - if (src != args) { - *src++ = '\0'; - strcpy(buf, args); - dst += strlen(args); + while (1) { + sep = strchrnul(args, ','); + if (!strstarts(args, "subvol=") && + !strstarts(args, "subvolid=")) { + memcpy(dst, args, sep - args); + dst += sep - args; + *dst++ = ','; + } + if (*sep) + args = sep + 1; + else + break; } - strcpy(dst, "subvolid=0"); - dst += strlen("subvolid=0"); - - /* - * If there is a "," after the original subvol=... string, - * copy that suffix into our buffer. Otherwise, we're done. - */ - src = strchr(src, ','); - if (src) - strcpy(dst, src); return buf; } -static struct dentry *mount_subvol(const char *subvol_name, int flags, - const char *device_name, char *data) +static struct dentry *mount_subvol(const char *subvol_name, u64 subvol_objectid, + int flags, const char *device_name, + char *data) { struct dentry *root; - struct vfsmount *mnt; + struct vfsmount *mnt = NULL; char *newargs; + int ret; newargs = setup_root_args(data); - if (!newargs) - return ERR_PTR(-ENOMEM); - mnt = vfs_kern_mount(&btrfs_fs_type, flags, device_name, - newargs); + if (!newargs) { + root = ERR_PTR(-ENOMEM); + goto out; + } - if (PTR_RET(mnt) == -EBUSY) { + mnt = vfs_kern_mount(&btrfs_fs_type, flags, device_name, newargs); + if (PTR_ERR_OR_ZERO(mnt) == -EBUSY) { if (flags & MS_RDONLY) { - mnt = vfs_kern_mount(&btrfs_fs_type, flags & ~MS_RDONLY, device_name, - newargs); + mnt = vfs_kern_mount(&btrfs_fs_type, flags & ~MS_RDONLY, + device_name, newargs); } else { - int r; - mnt = vfs_kern_mount(&btrfs_fs_type, flags | MS_RDONLY, device_name, - newargs); + mnt = vfs_kern_mount(&btrfs_fs_type, flags | MS_RDONLY, + device_name, newargs); if (IS_ERR(mnt)) { - kfree(newargs); - return ERR_CAST(mnt); + root = ERR_CAST(mnt); + mnt = NULL; + goto out; } - r = btrfs_remount(mnt->mnt_sb, &flags, NULL); - if (r < 0) { - /* FIXME: release vfsmount mnt ??*/ - kfree(newargs); - return ERR_PTR(r); + down_write(&mnt->mnt_sb->s_umount); + ret = btrfs_remount(mnt->mnt_sb, &flags, NULL); + up_write(&mnt->mnt_sb->s_umount); + if (ret < 0) { + root = ERR_PTR(ret); + goto out; } } } + if (IS_ERR(mnt)) { + root = ERR_CAST(mnt); + mnt = NULL; + goto out; + } - kfree(newargs); + if (!subvol_name) { + if (!subvol_objectid) { + ret = get_default_subvol_objectid(btrfs_sb(mnt->mnt_sb), + &subvol_objectid); + if (ret) { + root = ERR_PTR(ret); + goto out; + } + } + subvol_name = get_subvol_name_from_objectid(btrfs_sb(mnt->mnt_sb), + subvol_objectid); + if (IS_ERR(subvol_name)) { + root = ERR_CAST(subvol_name); + subvol_name = NULL; + goto out; + } - if (IS_ERR(mnt)) - return ERR_CAST(mnt); + } root = mount_subtree(mnt, subvol_name); + /* mount_subtree() drops our reference on the vfsmount. */ + mnt = NULL; - if (!IS_ERR(root) && !is_subvolume_inode(d_inode(root))) { + if (!IS_ERR(root)) { struct super_block *s = root->d_sb; - dput(root); - root = ERR_PTR(-EINVAL); - deactivate_locked_super(s); - printk(KERN_ERR "BTRFS: '%s' is not a valid subvolume\n", - subvol_name); + struct inode *root_inode = d_inode(root); + u64 root_objectid = BTRFS_I(root_inode)->root->root_key.objectid; + + ret = 0; + if (!is_subvolume_inode(root_inode)) { + pr_err("BTRFS: '%s' is not a valid subvolume\n", + subvol_name); + ret = -EINVAL; + } + if (subvol_objectid && root_objectid != subvol_objectid) { + /* + * This will also catch a race condition where a + * subvolume which was passed by ID is renamed and + * another subvolume is renamed over the old location. + */ + pr_err("BTRFS: subvol '%s' does not match subvolid %llu\n", + subvol_name, subvol_objectid); + ret = -EINVAL; + } + if (ret) { + dput(root); + root = ERR_PTR(ret); + deactivate_locked_super(s); + } } +out: + mntput(mnt); + kfree(newargs); + kfree(subvol_name); return root; } @@ -1303,7 +1419,6 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, { struct block_device *bdev = NULL; struct super_block *s; - struct dentry *root; struct btrfs_fs_devices *fs_devices = NULL; struct btrfs_fs_info *fs_info = NULL; struct security_mnt_opts new_sec_opts; @@ -1323,10 +1438,10 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, return ERR_PTR(error); } - if (subvol_name) { - root = mount_subvol(subvol_name, flags, device_name, data); - kfree(subvol_name); - return root; + if (subvol_name || subvol_objectid != BTRFS_FS_TREE_OBJECTID) { + /* mount_subvol() will free subvol_name. */ + return mount_subvol(subvol_name, subvol_objectid, flags, + device_name, data); } security_init_mnt_opts(&new_sec_opts); @@ -1392,23 +1507,19 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags, error = btrfs_fill_super(s, fs_devices, data, flags & MS_SILENT ? 1 : 0); } - - root = !error ? get_default_root(s, subvol_objectid) : ERR_PTR(error); - if (IS_ERR(root)) { + if (error) { deactivate_locked_super(s); - error = PTR_ERR(root); goto error_sec_opts; } fs_info = btrfs_sb(s); error = setup_security_options(fs_info, s, &new_sec_opts); if (error) { - dput(root); deactivate_locked_super(s); goto error_sec_opts; } - return root; + return dget(s->s_root); error_close_devices: btrfs_close_devices(fs_devices); diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index e8a4c86d2..603b0cc2b 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -33,6 +33,7 @@ #include "volumes.h" static inline struct btrfs_fs_info *to_fs_info(struct kobject *kobj); +static inline struct btrfs_fs_devices *to_fs_devs(struct kobject *kobj); static u64 get_features(struct btrfs_fs_info *fs_info, enum btrfs_feature_set set) @@ -428,7 +429,7 @@ static ssize_t btrfs_clone_alignment_show(struct kobject *kobj, BTRFS_ATTR(clone_alignment, btrfs_clone_alignment_show); -static struct attribute *btrfs_attrs[] = { +static const struct attribute *btrfs_attrs[] = { BTRFS_ATTR_PTR(label), BTRFS_ATTR_PTR(nodesize), BTRFS_ATTR_PTR(sectorsize), @@ -438,21 +439,29 @@ static struct attribute *btrfs_attrs[] = { static void btrfs_release_super_kobj(struct kobject *kobj) { - struct btrfs_fs_info *fs_info = to_fs_info(kobj); - complete(&fs_info->kobj_unregister); + struct btrfs_fs_devices *fs_devs = to_fs_devs(kobj); + + memset(&fs_devs->super_kobj, 0, sizeof(struct kobject)); + complete(&fs_devs->kobj_unregister); } static struct kobj_type btrfs_ktype = { .sysfs_ops = &kobj_sysfs_ops, .release = btrfs_release_super_kobj, - .default_attrs = btrfs_attrs, }; +static inline struct btrfs_fs_devices *to_fs_devs(struct kobject *kobj) +{ + if (kobj->ktype != &btrfs_ktype) + return NULL; + return container_of(kobj, struct btrfs_fs_devices, super_kobj); +} + static inline struct btrfs_fs_info *to_fs_info(struct kobject *kobj) { if (kobj->ktype != &btrfs_ktype) return NULL; - return container_of(kobj, struct btrfs_fs_info, super_kobj); + return to_fs_devs(kobj)->fs_info; } #define NUM_FEATURE_BITS 64 @@ -493,12 +502,12 @@ static int addrm_unknown_feature_attrs(struct btrfs_fs_info *fs_info, bool add) attrs[0] = &fa->kobj_attr.attr; if (add) { int ret; - ret = sysfs_merge_group(&fs_info->super_kobj, + ret = sysfs_merge_group(&fs_info->fs_devices->super_kobj, &agroup); if (ret) return ret; } else - sysfs_unmerge_group(&fs_info->super_kobj, + sysfs_unmerge_group(&fs_info->fs_devices->super_kobj, &agroup); } @@ -506,25 +515,49 @@ static int addrm_unknown_feature_attrs(struct btrfs_fs_info *fs_info, bool add) return 0; } -static void __btrfs_sysfs_remove_one(struct btrfs_fs_info *fs_info) +static void __btrfs_sysfs_remove_fsid(struct btrfs_fs_devices *fs_devs) +{ + if (fs_devs->device_dir_kobj) { + kobject_del(fs_devs->device_dir_kobj); + kobject_put(fs_devs->device_dir_kobj); + fs_devs->device_dir_kobj = NULL; + } + + if (fs_devs->super_kobj.state_initialized) { + kobject_del(&fs_devs->super_kobj); + kobject_put(&fs_devs->super_kobj); + wait_for_completion(&fs_devs->kobj_unregister); + } +} + +/* when fs_devs is NULL it will remove all fsid kobject */ +void btrfs_sysfs_remove_fsid(struct btrfs_fs_devices *fs_devs) { - kobject_del(&fs_info->super_kobj); - kobject_put(&fs_info->super_kobj); - wait_for_completion(&fs_info->kobj_unregister); + struct list_head *fs_uuids = btrfs_get_fs_uuids(); + + if (fs_devs) { + __btrfs_sysfs_remove_fsid(fs_devs); + return; + } + + list_for_each_entry(fs_devs, fs_uuids, list) { + __btrfs_sysfs_remove_fsid(fs_devs); + } } void btrfs_sysfs_remove_one(struct btrfs_fs_info *fs_info) { + btrfs_reset_fs_info_ptr(fs_info); + if (fs_info->space_info_kobj) { sysfs_remove_files(fs_info->space_info_kobj, allocation_attrs); kobject_del(fs_info->space_info_kobj); kobject_put(fs_info->space_info_kobj); } - kobject_del(fs_info->device_dir_kobj); - kobject_put(fs_info->device_dir_kobj); addrm_unknown_feature_attrs(fs_info, false); - sysfs_remove_group(&fs_info->super_kobj, &btrfs_feature_attr_group); - __btrfs_sysfs_remove_one(fs_info); + sysfs_remove_group(&fs_info->fs_devices->super_kobj, &btrfs_feature_attr_group); + sysfs_remove_files(&fs_info->fs_devices->super_kobj, btrfs_attrs); + btrfs_kobj_rm_device(fs_info->fs_devices, NULL); } const char * const btrfs_feature_set_names[3] = { @@ -602,40 +635,60 @@ static void init_feature_attrs(void) } } -int btrfs_kobj_rm_device(struct btrfs_fs_info *fs_info, +/* when one_device is NULL, it removes all device links */ + +int btrfs_kobj_rm_device(struct btrfs_fs_devices *fs_devices, struct btrfs_device *one_device) { struct hd_struct *disk; struct kobject *disk_kobj; - if (!fs_info->device_dir_kobj) + if (!fs_devices->device_dir_kobj) return -EINVAL; if (one_device && one_device->bdev) { disk = one_device->bdev->bd_part; disk_kobj = &part_to_dev(disk)->kobj; - sysfs_remove_link(fs_info->device_dir_kobj, + sysfs_remove_link(fs_devices->device_dir_kobj, + disk_kobj->name); + } + + if (one_device) + return 0; + + list_for_each_entry(one_device, + &fs_devices->devices, dev_list) { + if (!one_device->bdev) + continue; + disk = one_device->bdev->bd_part; + disk_kobj = &part_to_dev(disk)->kobj; + + sysfs_remove_link(fs_devices->device_dir_kobj, disk_kobj->name); } return 0; } -int btrfs_kobj_add_device(struct btrfs_fs_info *fs_info, - struct btrfs_device *one_device) +int btrfs_sysfs_add_device(struct btrfs_fs_devices *fs_devs) { - int error = 0; - struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; - struct btrfs_device *dev; - - if (!fs_info->device_dir_kobj) - fs_info->device_dir_kobj = kobject_create_and_add("devices", - &fs_info->super_kobj); + if (!fs_devs->device_dir_kobj) + fs_devs->device_dir_kobj = kobject_create_and_add("devices", + &fs_devs->super_kobj); - if (!fs_info->device_dir_kobj) + if (!fs_devs->device_dir_kobj) return -ENOMEM; + return 0; +} + +int btrfs_kobj_add_device(struct btrfs_fs_devices *fs_devices, + struct btrfs_device *one_device) +{ + int error = 0; + struct btrfs_device *dev; + list_for_each_entry(dev, &fs_devices->devices, dev_list) { struct hd_struct *disk; struct kobject *disk_kobj; @@ -649,7 +702,7 @@ int btrfs_kobj_add_device(struct btrfs_fs_info *fs_info, disk = dev->bdev->bd_part; disk_kobj = &part_to_dev(disk)->kobj; - error = sysfs_create_link(fs_info->device_dir_kobj, + error = sysfs_create_link(fs_devices->device_dir_kobj, disk_kobj, disk_kobj->name); if (error) break; @@ -667,34 +720,51 @@ static struct dentry *btrfs_debugfs_root_dentry; /* Debugging tunables and exported data */ u64 btrfs_debugfs_test; +/* + * Can be called by the device discovery thread. + * And parent can be specified for seed device + */ +int btrfs_sysfs_add_fsid(struct btrfs_fs_devices *fs_devs, + struct kobject *parent) +{ + int error; + + init_completion(&fs_devs->kobj_unregister); + fs_devs->super_kobj.kset = btrfs_kset; + error = kobject_init_and_add(&fs_devs->super_kobj, + &btrfs_ktype, parent, "%pU", fs_devs->fsid); + return error; +} + int btrfs_sysfs_add_one(struct btrfs_fs_info *fs_info) { int error; + struct btrfs_fs_devices *fs_devs = fs_info->fs_devices; + struct kobject *super_kobj = &fs_devs->super_kobj; + + btrfs_set_fs_info_ptr(fs_info); - init_completion(&fs_info->kobj_unregister); - fs_info->super_kobj.kset = btrfs_kset; - error = kobject_init_and_add(&fs_info->super_kobj, &btrfs_ktype, NULL, - "%pU", fs_info->fsid); + error = btrfs_kobj_add_device(fs_devs, NULL); if (error) return error; - error = sysfs_create_group(&fs_info->super_kobj, - &btrfs_feature_attr_group); + error = sysfs_create_files(super_kobj, btrfs_attrs); if (error) { - __btrfs_sysfs_remove_one(fs_info); + btrfs_kobj_rm_device(fs_devs, NULL); return error; } - error = addrm_unknown_feature_attrs(fs_info, true); + error = sysfs_create_group(super_kobj, + &btrfs_feature_attr_group); if (error) goto failure; - error = btrfs_kobj_add_device(fs_info, NULL); + error = addrm_unknown_feature_attrs(fs_info, true); if (error) goto failure; fs_info->space_info_kobj = kobject_create_and_add("allocation", - &fs_info->super_kobj); + super_kobj); if (!fs_info->space_info_kobj) { error = -ENOMEM; goto failure; diff --git a/fs/btrfs/sysfs.h b/fs/btrfs/sysfs.h index 3a4bbed72..6392527bc 100644 --- a/fs/btrfs/sysfs.h +++ b/fs/btrfs/sysfs.h @@ -82,8 +82,12 @@ char *btrfs_printable_features(enum btrfs_feature_set set, u64 flags); extern const char * const btrfs_feature_set_names[3]; extern struct kobj_type space_info_ktype; extern struct kobj_type btrfs_raid_ktype; -int btrfs_kobj_add_device(struct btrfs_fs_info *fs_info, +int btrfs_kobj_add_device(struct btrfs_fs_devices *fs_devices, struct btrfs_device *one_device); -int btrfs_kobj_rm_device(struct btrfs_fs_info *fs_info, +int btrfs_kobj_rm_device(struct btrfs_fs_devices *fs_devices, struct btrfs_device *one_device); +int btrfs_sysfs_add_fsid(struct btrfs_fs_devices *fs_devs, + struct kobject *parent); +int btrfs_sysfs_add_device(struct btrfs_fs_devices *fs_devs); +void btrfs_sysfs_remove_fsid(struct btrfs_fs_devices *fs_devs); #endif /* _BTRFS_SYSFS_H_ */ diff --git a/fs/btrfs/tests/qgroup-tests.c b/fs/btrfs/tests/qgroup-tests.c index c32a7ba76..846d277b1 100644 --- a/fs/btrfs/tests/qgroup-tests.c +++ b/fs/btrfs/tests/qgroup-tests.c @@ -21,6 +21,7 @@ #include "../transaction.h" #include "../disk-io.h" #include "../qgroup.h" +#include "../backref.h" static void init_dummy_trans(struct btrfs_trans_handle *trans) { @@ -227,6 +228,8 @@ static int test_no_shared_qgroup(struct btrfs_root *root) { struct btrfs_trans_handle trans; struct btrfs_fs_info *fs_info = root->fs_info; + struct ulist *old_roots = NULL; + struct ulist *new_roots = NULL; int ret; init_dummy_trans(&trans); @@ -238,10 +241,15 @@ static int test_no_shared_qgroup(struct btrfs_root *root) return ret; } - ret = btrfs_qgroup_record_ref(&trans, fs_info, 5, 4096, 4096, - BTRFS_QGROUP_OPER_ADD_EXCL, 0); + /* + * Since the test trans doesn't havee the complicated delayed refs, + * we can only call btrfs_qgroup_account_extent() directly to test + * quota. + */ + ret = btrfs_find_all_roots(&trans, fs_info, 4096, 0, &old_roots); if (ret) { - test_msg("Couldn't add space to a qgroup %d\n", ret); + ulist_free(old_roots); + test_msg("Couldn't find old roots: %d\n", ret); return ret; } @@ -249,9 +257,18 @@ static int test_no_shared_qgroup(struct btrfs_root *root) if (ret) return ret; - ret = btrfs_delayed_qgroup_accounting(&trans, fs_info); + ret = btrfs_find_all_roots(&trans, fs_info, 4096, 0, &new_roots); + if (ret) { + ulist_free(old_roots); + ulist_free(new_roots); + test_msg("Couldn't find old roots: %d\n", ret); + return ret; + } + + ret = btrfs_qgroup_account_extent(&trans, fs_info, 4096, 4096, + old_roots, new_roots); if (ret) { - test_msg("Delayed qgroup accounting failed %d\n", ret); + test_msg("Couldn't account space for a qgroup %d\n", ret); return ret; } @@ -259,21 +276,32 @@ static int test_no_shared_qgroup(struct btrfs_root *root) test_msg("Qgroup counts didn't match expected values\n"); return -EINVAL; } + old_roots = NULL; + new_roots = NULL; + + ret = btrfs_find_all_roots(&trans, fs_info, 4096, 0, &old_roots); + if (ret) { + ulist_free(old_roots); + test_msg("Couldn't find old roots: %d\n", ret); + return ret; + } ret = remove_extent_item(root, 4096, 4096); if (ret) return -EINVAL; - ret = btrfs_qgroup_record_ref(&trans, fs_info, 5, 4096, 4096, - BTRFS_QGROUP_OPER_SUB_EXCL, 0); + ret = btrfs_find_all_roots(&trans, fs_info, 4096, 0, &new_roots); if (ret) { - test_msg("Couldn't remove space from the qgroup %d\n", ret); - return -EINVAL; + ulist_free(old_roots); + ulist_free(new_roots); + test_msg("Couldn't find old roots: %d\n", ret); + return ret; } - ret = btrfs_delayed_qgroup_accounting(&trans, fs_info); + ret = btrfs_qgroup_account_extent(&trans, fs_info, 4096, 4096, + old_roots, new_roots); if (ret) { - test_msg("Qgroup accounting failed %d\n", ret); + test_msg("Couldn't account space for a qgroup %d\n", ret); return -EINVAL; } @@ -294,6 +322,8 @@ static int test_multiple_refs(struct btrfs_root *root) { struct btrfs_trans_handle trans; struct btrfs_fs_info *fs_info = root->fs_info; + struct ulist *old_roots = NULL; + struct ulist *new_roots = NULL; int ret; init_dummy_trans(&trans); @@ -307,20 +337,29 @@ static int test_multiple_refs(struct btrfs_root *root) return ret; } + ret = btrfs_find_all_roots(&trans, fs_info, 4096, 0, &old_roots); + if (ret) { + ulist_free(old_roots); + test_msg("Couldn't find old roots: %d\n", ret); + return ret; + } + ret = insert_normal_tree_ref(root, 4096, 4096, 0, 5); if (ret) return ret; - ret = btrfs_qgroup_record_ref(&trans, fs_info, 5, 4096, 4096, - BTRFS_QGROUP_OPER_ADD_EXCL, 0); + ret = btrfs_find_all_roots(&trans, fs_info, 4096, 0, &new_roots); if (ret) { - test_msg("Couldn't add space to a qgroup %d\n", ret); + ulist_free(old_roots); + ulist_free(new_roots); + test_msg("Couldn't find old roots: %d\n", ret); return ret; } - ret = btrfs_delayed_qgroup_accounting(&trans, fs_info); + ret = btrfs_qgroup_account_extent(&trans, fs_info, 4096, 4096, + old_roots, new_roots); if (ret) { - test_msg("Delayed qgroup accounting failed %d\n", ret); + test_msg("Couldn't account space for a qgroup %d\n", ret); return ret; } @@ -329,20 +368,29 @@ static int test_multiple_refs(struct btrfs_root *root) return -EINVAL; } + ret = btrfs_find_all_roots(&trans, fs_info, 4096, 0, &old_roots); + if (ret) { + ulist_free(old_roots); + test_msg("Couldn't find old roots: %d\n", ret); + return ret; + } + ret = add_tree_ref(root, 4096, 4096, 0, 256); if (ret) return ret; - ret = btrfs_qgroup_record_ref(&trans, fs_info, 256, 4096, 4096, - BTRFS_QGROUP_OPER_ADD_SHARED, 0); + ret = btrfs_find_all_roots(&trans, fs_info, 4096, 0, &new_roots); if (ret) { - test_msg("Qgroup record ref failed %d\n", ret); + ulist_free(old_roots); + ulist_free(new_roots); + test_msg("Couldn't find old roots: %d\n", ret); return ret; } - ret = btrfs_delayed_qgroup_accounting(&trans, fs_info); + ret = btrfs_qgroup_account_extent(&trans, fs_info, 4096, 4096, + old_roots, new_roots); if (ret) { - test_msg("Qgroup accounting failed %d\n", ret); + test_msg("Couldn't account space for a qgroup %d\n", ret); return ret; } @@ -356,20 +404,29 @@ static int test_multiple_refs(struct btrfs_root *root) return -EINVAL; } + ret = btrfs_find_all_roots(&trans, fs_info, 4096, 0, &old_roots); + if (ret) { + ulist_free(old_roots); + test_msg("Couldn't find old roots: %d\n", ret); + return ret; + } + ret = remove_extent_ref(root, 4096, 4096, 0, 256); if (ret) return ret; - ret = btrfs_qgroup_record_ref(&trans, fs_info, 256, 4096, 4096, - BTRFS_QGROUP_OPER_SUB_SHARED, 0); + ret = btrfs_find_all_roots(&trans, fs_info, 4096, 0, &new_roots); if (ret) { - test_msg("Qgroup record ref failed %d\n", ret); + ulist_free(old_roots); + ulist_free(new_roots); + test_msg("Couldn't find old roots: %d\n", ret); return ret; } - ret = btrfs_delayed_qgroup_accounting(&trans, fs_info); + ret = btrfs_qgroup_account_extent(&trans, fs_info, 4096, 4096, + old_roots, new_roots); if (ret) { - test_msg("Qgroup accounting failed %d\n", ret); + test_msg("Couldn't account space for a qgroup %d\n", ret); return ret; } diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 94e909c5a..f5021fcb1 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -225,12 +225,14 @@ loop: cur_trans->dirty_bg_run = 0; cur_trans->delayed_refs.href_root = RB_ROOT; + cur_trans->delayed_refs.dirty_extent_root = RB_ROOT; atomic_set(&cur_trans->delayed_refs.num_entries, 0); cur_trans->delayed_refs.num_heads_ready = 0; cur_trans->delayed_refs.pending_csums = 0; cur_trans->delayed_refs.num_heads = 0; cur_trans->delayed_refs.flushing = 0; cur_trans->delayed_refs.run_delayed_start = 0; + cur_trans->delayed_refs.qgroup_to_skip = 0; /* * although the tree mod log is per file system and not per transaction, @@ -509,6 +511,7 @@ again: h->transaction = cur_trans; h->blocks_used = 0; h->bytes_reserved = 0; + h->chunk_bytes_reserved = 0; h->root = root; h->delayed_ref_updates = 0; h->use_count = 1; @@ -792,6 +795,8 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans, if (!list_empty(&trans->new_bgs)) btrfs_create_pending_block_groups(trans, root); + btrfs_trans_release_chunk_metadata(trans); + if (lock && !atomic_read(&root->fs_info->open_ioctl_trans) && should_end_transaction(trans, root) && ACCESS_ONCE(cur_trans->state) == TRANS_STATE_RUNNING) { @@ -1290,6 +1295,12 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, if (pending->error) goto no_free_objectid; + /* + * Make qgroup to skip current new snapshot's qgroupid, as it is + * accounted by later btrfs_qgroup_inherit(). + */ + btrfs_set_skip_qgroup(trans, objectid); + btrfs_reloc_pre_snapshot(trans, pending, &to_reserve); if (to_reserve > 0) { @@ -1298,7 +1309,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, to_reserve, BTRFS_RESERVE_NO_FLUSH); if (pending->error) - goto no_free_objectid; + goto clear_skip_qgroup; } key.objectid = objectid; @@ -1396,25 +1407,6 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, btrfs_abort_transaction(trans, root, ret); goto fail; } - - /* - * We need to flush delayed refs in order to make sure all of our quota - * operations have been done before we call btrfs_qgroup_inherit. - */ - ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1); - if (ret) { - btrfs_abort_transaction(trans, root, ret); - goto fail; - } - - ret = btrfs_qgroup_inherit(trans, fs_info, - root->root_key.objectid, - objectid, pending->inherit); - if (ret) { - btrfs_abort_transaction(trans, root, ret); - goto fail; - } - /* see comments in should_cow_block() */ set_bit(BTRFS_ROOT_FORCE_COW, &root->state); smp_wmb(); @@ -1497,11 +1489,37 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, goto fail; } } + + ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1); + if (ret) { + btrfs_abort_transaction(trans, root, ret); + goto fail; + } + + /* + * account qgroup counters before qgroup_inherit() + */ + ret = btrfs_qgroup_prepare_account_extents(trans, fs_info); + if (ret) + goto fail; + ret = btrfs_qgroup_account_extents(trans, fs_info); + if (ret) + goto fail; + ret = btrfs_qgroup_inherit(trans, fs_info, + root->root_key.objectid, + objectid, pending->inherit); + if (ret) { + btrfs_abort_transaction(trans, root, ret); + goto fail; + } + fail: pending->error = ret; dir_item_existed: trans->block_rsv = rsv; trans->bytes_reserved = 0; +clear_skip_qgroup: + btrfs_clear_skip_qgroup(trans); no_free_objectid: kfree(new_root_item); root_item_alloc_fail: @@ -1963,6 +1981,13 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, goto scrub_continue; } + /* Reocrd old roots for later qgroup accounting */ + ret = btrfs_qgroup_prepare_account_extents(trans, root->fs_info); + if (ret) { + mutex_unlock(&root->fs_info->reloc_mutex); + goto scrub_continue; + } + /* * make sure none of the code above managed to slip in a * delayed item @@ -2004,6 +2029,17 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, */ btrfs_free_log_root_tree(trans, root->fs_info); + /* + * Since fs roots are all committed, we can get a quite accurate + * new_roots. So let's do quota accounting. + */ + ret = btrfs_qgroup_account_extents(trans, root->fs_info); + if (ret < 0) { + mutex_unlock(&root->fs_info->tree_log_mutex); + mutex_unlock(&root->fs_info->reloc_mutex); + goto scrub_continue; + } + ret = commit_cowonly_roots(trans, root); if (ret) { mutex_unlock(&root->fs_info->tree_log_mutex); @@ -2054,6 +2090,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, clear_bit(BTRFS_INODE_BTREE_LOG1_ERR, &btree_ino->runtime_flags); clear_bit(BTRFS_INODE_BTREE_LOG2_ERR, &btree_ino->runtime_flags); + btrfs_trans_release_chunk_metadata(trans); + spin_lock(&root->fs_info->trans_lock); cur_trans->state = TRANS_STATE_UNBLOCKED; root->fs_info->running_transaction = NULL; @@ -2114,7 +2152,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, kmem_cache_free(btrfs_trans_handle_cachep, trans); - if (current != root->fs_info->transaction_kthread) + if (current != root->fs_info->transaction_kthread && + current != root->fs_info->cleaner_kthread) btrfs_run_delayed_iputs(root); return ret; @@ -2123,6 +2162,7 @@ scrub_continue: btrfs_scrub_continue(root); cleanup_transaction: btrfs_trans_release_metadata(trans, root); + btrfs_trans_release_chunk_metadata(trans); trans->block_rsv = NULL; if (trans->qgroup_reserved) { btrfs_qgroup_free(root, trans->qgroup_reserved); diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h index 0b2475559..eb09c2067 100644 --- a/fs/btrfs/transaction.h +++ b/fs/btrfs/transaction.h @@ -102,6 +102,7 @@ struct btrfs_transaction { struct btrfs_trans_handle { u64 transid; u64 bytes_reserved; + u64 chunk_bytes_reserved; u64 qgroup_reserved; unsigned long use_count; unsigned long blocks_reserved; @@ -153,6 +154,29 @@ static inline void btrfs_set_inode_last_trans(struct btrfs_trans_handle *trans, spin_unlock(&BTRFS_I(inode)->lock); } +/* + * Make qgroup codes to skip given qgroupid, means the old/new_roots for + * qgroup won't contain the qgroupid in it. + */ +static inline void btrfs_set_skip_qgroup(struct btrfs_trans_handle *trans, + u64 qgroupid) +{ + struct btrfs_delayed_ref_root *delayed_refs; + + delayed_refs = &trans->transaction->delayed_refs; + WARN_ON(delayed_refs->qgroup_to_skip); + delayed_refs->qgroup_to_skip = qgroupid; +} + +static inline void btrfs_clear_skip_qgroup(struct btrfs_trans_handle *trans) +{ + struct btrfs_delayed_ref_root *delayed_refs; + + delayed_refs = &trans->transaction->delayed_refs; + WARN_ON(!delayed_refs->qgroup_to_skip); + delayed_refs->qgroup_to_skip = 0; +} + int btrfs_end_transaction(struct btrfs_trans_handle *trans, struct btrfs_root *root); struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root, diff --git a/fs/btrfs/tree-defrag.c b/fs/btrfs/tree-defrag.c index a63719cc9..a4b9c8b2d 100644 --- a/fs/btrfs/tree-defrag.c +++ b/fs/btrfs/tree-defrag.c @@ -52,9 +52,6 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans, if (!test_bit(BTRFS_ROOT_REF_COWS, &root->state)) goto out; - if (btrfs_test_opt(root, SSD)) - goto out; - path = btrfs_alloc_path(); if (!path) return -ENOMEM; diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 4920fceff..9c45431e6 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -3881,12 +3881,6 @@ static int wait_ordered_extents(struct btrfs_trans_handle *trans, &ordered->flags)) continue; - if (ordered->csum_bytes_left) { - btrfs_start_ordered_extent(inode, ordered, 0); - wait_event(ordered->wait, - ordered->csum_bytes_left == 0); - } - list_for_each_entry(sum, &ordered->list, list) { ret = btrfs_csum_file_blocks(trans, log, sum); if (ret) @@ -4123,6 +4117,187 @@ static int logged_inode_size(struct btrfs_root *log, struct inode *inode, return 0; } +/* + * At the moment we always log all xattrs. This is to figure out at log replay + * time which xattrs must have their deletion replayed. If a xattr is missing + * in the log tree and exists in the fs/subvol tree, we delete it. This is + * because if a xattr is deleted, the inode is fsynced and a power failure + * happens, causing the log to be replayed the next time the fs is mounted, + * we want the xattr to not exist anymore (same behaviour as other filesystems + * with a journal, ext3/4, xfs, f2fs, etc). + */ +static int btrfs_log_all_xattrs(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct inode *inode, + struct btrfs_path *path, + struct btrfs_path *dst_path) +{ + int ret; + struct btrfs_key key; + const u64 ino = btrfs_ino(inode); + int ins_nr = 0; + int start_slot = 0; + + key.objectid = ino; + key.type = BTRFS_XATTR_ITEM_KEY; + key.offset = 0; + + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + if (ret < 0) + return ret; + + while (true) { + int slot = path->slots[0]; + struct extent_buffer *leaf = path->nodes[0]; + int nritems = btrfs_header_nritems(leaf); + + if (slot >= nritems) { + if (ins_nr > 0) { + u64 last_extent = 0; + + ret = copy_items(trans, inode, dst_path, path, + &last_extent, start_slot, + ins_nr, 1, 0); + /* can't be 1, extent items aren't processed */ + ASSERT(ret <= 0); + if (ret < 0) + return ret; + ins_nr = 0; + } + ret = btrfs_next_leaf(root, path); + if (ret < 0) + return ret; + else if (ret > 0) + break; + continue; + } + + btrfs_item_key_to_cpu(leaf, &key, slot); + if (key.objectid != ino || key.type != BTRFS_XATTR_ITEM_KEY) + break; + + if (ins_nr == 0) + start_slot = slot; + ins_nr++; + path->slots[0]++; + cond_resched(); + } + if (ins_nr > 0) { + u64 last_extent = 0; + + ret = copy_items(trans, inode, dst_path, path, + &last_extent, start_slot, + ins_nr, 1, 0); + /* can't be 1, extent items aren't processed */ + ASSERT(ret <= 0); + if (ret < 0) + return ret; + } + + return 0; +} + +/* + * If the no holes feature is enabled we need to make sure any hole between the + * last extent and the i_size of our inode is explicitly marked in the log. This + * is to make sure that doing something like: + * + * 1) create file with 128Kb of data + * 2) truncate file to 64Kb + * 3) truncate file to 256Kb + * 4) fsync file + * 5) <crash/power failure> + * 6) mount fs and trigger log replay + * + * Will give us a file with a size of 256Kb, the first 64Kb of data match what + * the file had in its first 64Kb of data at step 1 and the last 192Kb of the + * file correspond to a hole. The presence of explicit holes in a log tree is + * what guarantees that log replay will remove/adjust file extent items in the + * fs/subvol tree. + * + * Here we do not need to care about holes between extents, that is already done + * by copy_items(). We also only need to do this in the full sync path, where we + * lookup for extents from the fs/subvol tree only. In the fast path case, we + * lookup the list of modified extent maps and if any represents a hole, we + * insert a corresponding extent representing a hole in the log tree. + */ +static int btrfs_log_trailing_hole(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct inode *inode, + struct btrfs_path *path) +{ + int ret; + struct btrfs_key key; + u64 hole_start; + u64 hole_size; + struct extent_buffer *leaf; + struct btrfs_root *log = root->log_root; + const u64 ino = btrfs_ino(inode); + const u64 i_size = i_size_read(inode); + + if (!btrfs_fs_incompat(root->fs_info, NO_HOLES)) + return 0; + + key.objectid = ino; + key.type = BTRFS_EXTENT_DATA_KEY; + key.offset = (u64)-1; + + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + ASSERT(ret != 0); + if (ret < 0) + return ret; + + ASSERT(path->slots[0] > 0); + path->slots[0]--; + leaf = path->nodes[0]; + btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); + + if (key.objectid != ino || key.type != BTRFS_EXTENT_DATA_KEY) { + /* inode does not have any extents */ + hole_start = 0; + hole_size = i_size; + } else { + struct btrfs_file_extent_item *extent; + u64 len; + + /* + * If there's an extent beyond i_size, an explicit hole was + * already inserted by copy_items(). + */ + if (key.offset >= i_size) + return 0; + + extent = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_file_extent_item); + + if (btrfs_file_extent_type(leaf, extent) == + BTRFS_FILE_EXTENT_INLINE) { + len = btrfs_file_extent_inline_len(leaf, + path->slots[0], + extent); + ASSERT(len == i_size); + return 0; + } + + len = btrfs_file_extent_num_bytes(leaf, extent); + /* Last extent goes beyond i_size, no need to log a hole. */ + if (key.offset + len > i_size) + return 0; + hole_start = key.offset + len; + hole_size = i_size - hole_start; + } + btrfs_release_path(path); + + /* Last extent ends at i_size. */ + if (hole_size == 0) + return 0; + + hole_size = ALIGN(hole_size, root->sectorsize); + ret = btrfs_insert_file_extent(trans, log, ino, hole_start, 0, 0, + hole_size, 0, hole_size, 0, 0, 0); + return ret; +} + /* log a single inode in the tree log. * At least one parent directory for this inode must exist in the tree * or be logged already. @@ -4295,6 +4470,25 @@ again: if (min_key.type == BTRFS_INODE_ITEM_KEY) need_log_inode_item = false; + /* Skip xattrs, we log them later with btrfs_log_all_xattrs() */ + if (min_key.type == BTRFS_XATTR_ITEM_KEY) { + if (ins_nr == 0) + goto next_slot; + ret = copy_items(trans, inode, dst_path, path, + &last_extent, ins_start_slot, + ins_nr, inode_only, logged_isize); + if (ret < 0) { + err = ret; + goto out_unlock; + } + ins_nr = 0; + if (ret) { + btrfs_release_path(path); + continue; + } + goto next_slot; + } + src = path->nodes[0]; if (ins_nr && ins_start_slot + ins_nr == path->slots[0]) { ins_nr++; @@ -4362,6 +4556,18 @@ next_slot: ins_nr = 0; } + btrfs_release_path(path); + btrfs_release_path(dst_path); + err = btrfs_log_all_xattrs(trans, root, inode, path, dst_path); + if (err) + goto out_unlock; + if (max_key.type >= BTRFS_EXTENT_DATA_KEY && !fast_search) { + btrfs_release_path(path); + btrfs_release_path(dst_path); + err = btrfs_log_trailing_hole(trans, root, inode, path); + if (err) + goto out_unlock; + } log_extents: btrfs_release_path(path); btrfs_release_path(dst_path); diff --git a/fs/btrfs/ulist.c b/fs/btrfs/ulist.c index 840a38b27..91feb2bde 100644 --- a/fs/btrfs/ulist.c +++ b/fs/btrfs/ulist.c @@ -132,6 +132,15 @@ static struct ulist_node *ulist_rbtree_search(struct ulist *ulist, u64 val) return NULL; } +static void ulist_rbtree_erase(struct ulist *ulist, struct ulist_node *node) +{ + rb_erase(&node->rb_node, &ulist->root); + list_del(&node->list); + kfree(node); + BUG_ON(ulist->nnodes == 0); + ulist->nnodes--; +} + static int ulist_rbtree_insert(struct ulist *ulist, struct ulist_node *ins) { struct rb_node **p = &ulist->root.rb_node; @@ -197,9 +206,6 @@ int ulist_add_merge(struct ulist *ulist, u64 val, u64 aux, node->val = val; node->aux = aux; -#ifdef CONFIG_BTRFS_DEBUG - node->seqnum = ulist->nnodes; -#endif ret = ulist_rbtree_insert(ulist, node); ASSERT(!ret); @@ -209,6 +215,33 @@ int ulist_add_merge(struct ulist *ulist, u64 val, u64 aux, return 1; } +/* + * ulist_del - delete one node from ulist + * @ulist: ulist to remove node from + * @val: value to delete + * @aux: aux to delete + * + * The deletion will only be done when *BOTH* val and aux matches. + * Return 0 for successful delete. + * Return > 0 for not found. + */ +int ulist_del(struct ulist *ulist, u64 val, u64 aux) +{ + struct ulist_node *node; + + node = ulist_rbtree_search(ulist, val); + /* Not found */ + if (!node) + return 1; + + if (node->aux != aux) + return 1; + + /* Found and delete */ + ulist_rbtree_erase(ulist, node); + return 0; +} + /** * ulist_next - iterate ulist * @ulist: ulist to iterate @@ -237,15 +270,7 @@ struct ulist_node *ulist_next(struct ulist *ulist, struct ulist_iterator *uiter) uiter->cur_list = uiter->cur_list->next; } else { uiter->cur_list = ulist->nodes.next; -#ifdef CONFIG_BTRFS_DEBUG - uiter->i = 0; -#endif } node = list_entry(uiter->cur_list, struct ulist_node, list); -#ifdef CONFIG_BTRFS_DEBUG - ASSERT(node->seqnum == uiter->i); - ASSERT(uiter->i >= 0 && uiter->i < ulist->nnodes); - uiter->i++; -#endif return node; } diff --git a/fs/btrfs/ulist.h b/fs/btrfs/ulist.h index 4c29db604..a01a2c458 100644 --- a/fs/btrfs/ulist.h +++ b/fs/btrfs/ulist.h @@ -57,6 +57,7 @@ void ulist_free(struct ulist *ulist); int ulist_add(struct ulist *ulist, u64 val, u64 aux, gfp_t gfp_mask); int ulist_add_merge(struct ulist *ulist, u64 val, u64 aux, u64 *old_aux, gfp_t gfp_mask); +int ulist_del(struct ulist *ulist, u64 val, u64 aux); /* just like ulist_add_merge() but take a pointer for the aux data */ static inline int ulist_add_merge_ptr(struct ulist *ulist, u64 val, void *aux, diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 174f5e1e0..fbe7c1045 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -52,6 +52,10 @@ static void btrfs_dev_stat_print_on_load(struct btrfs_device *device); DEFINE_MUTEX(uuid_mutex); static LIST_HEAD(fs_uuids); +struct list_head *btrfs_get_fs_uuids(void) +{ + return &fs_uuids; +} static struct btrfs_fs_devices *__alloc_fs_devices(void) { @@ -345,7 +349,7 @@ loop_lock: waitqueue_active(&fs_info->async_submit_wait)) wake_up(&fs_info->async_submit_wait); - BUG_ON(atomic_read(&cur->bi_cnt) == 0); + BUG_ON(atomic_read(&cur->__bi_cnt) == 0); /* * if we're doing the sync list, record that our @@ -441,6 +445,61 @@ static void pending_bios_fn(struct btrfs_work *work) run_scheduled_bios(device); } + +void btrfs_free_stale_device(struct btrfs_device *cur_dev) +{ + struct btrfs_fs_devices *fs_devs; + struct btrfs_device *dev; + + if (!cur_dev->name) + return; + + list_for_each_entry(fs_devs, &fs_uuids, list) { + int del = 1; + + if (fs_devs->opened) + continue; + if (fs_devs->seeding) + continue; + + list_for_each_entry(dev, &fs_devs->devices, dev_list) { + + if (dev == cur_dev) + continue; + if (!dev->name) + continue; + + /* + * Todo: This won't be enough. What if the same device + * comes back (with new uuid and) with its mapper path? + * But for now, this does help as mostly an admin will + * either use mapper or non mapper path throughout. + */ + rcu_read_lock(); + del = strcmp(rcu_str_deref(dev->name), + rcu_str_deref(cur_dev->name)); + rcu_read_unlock(); + if (!del) + break; + } + + if (!del) { + /* delete the stale device */ + if (fs_devs->num_devices == 1) { + btrfs_sysfs_remove_fsid(fs_devs); + list_del(&fs_devs->list); + free_fs_devices(fs_devs); + } else { + fs_devs->num_devices--; + list_del(&dev->dev_list); + rcu_string_free(dev->name); + kfree(dev); + } + break; + } + } +} + /* * Add new device to list of registered devices * @@ -556,6 +615,12 @@ static noinline int device_list_add(const char *path, if (!fs_devices->opened) device->generation = found_transid; + /* + * if there is new btrfs on an already registered device, + * then remove the stale device entry. + */ + btrfs_free_stale_device(device); + *fs_devices_ret = fs_devices; return ret; @@ -693,13 +758,13 @@ static void free_device(struct rcu_head *head) static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices) { - struct btrfs_device *device; + struct btrfs_device *device, *tmp; if (--fs_devices->opened > 0) return 0; mutex_lock(&fs_devices->device_list_mutex); - list_for_each_entry(device, &fs_devices->devices, dev_list) { + list_for_each_entry_safe(device, tmp, &fs_devices->devices, dev_list) { struct btrfs_device *new_device; struct rcu_string *name; @@ -1067,15 +1132,31 @@ again: map = (struct map_lookup *)em->bdev; for (i = 0; i < map->num_stripes; i++) { + u64 end; + if (map->stripes[i].dev != device) continue; if (map->stripes[i].physical >= physical_start + len || map->stripes[i].physical + em->orig_block_len <= physical_start) continue; - *start = map->stripes[i].physical + - em->orig_block_len; - ret = 1; + /* + * Make sure that while processing the pinned list we do + * not override our *start with a lower value, because + * we can have pinned chunks that fall within this + * device hole and that have lower physical addresses + * than the pending chunks we processed before. If we + * do not take this special care we can end up getting + * 2 pending chunks that start at the same physical + * device offsets because the end offset of a pinned + * chunk can be equal to the start offset of some + * pending chunk. + */ + end = map->stripes[i].physical + em->orig_block_len; + if (end > *start) { + *start = end; + ret = 1; + } } } if (search_list == &trans->transaction->pending_chunks) { @@ -1706,7 +1787,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) if (device->bdev) { device->fs_devices->open_devices--; /* remove sysfs entry */ - btrfs_kobj_rm_device(root->fs_info, device); + btrfs_kobj_rm_device(root->fs_info->fs_devices, device); } call_rcu(&device->rcu, free_device); @@ -1875,6 +1956,9 @@ void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, mutex_lock(&uuid_mutex); WARN_ON(!tgtdev); mutex_lock(&fs_info->fs_devices->device_list_mutex); + + btrfs_kobj_rm_device(fs_info->fs_devices, tgtdev); + if (tgtdev->bdev) { btrfs_scratch_superblock(tgtdev); fs_info->fs_devices->open_devices--; @@ -2211,7 +2295,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) tmp + 1); /* add sysfs device entry */ - btrfs_kobj_add_device(root->fs_info, device); + btrfs_kobj_add_device(root->fs_info->fs_devices, device); /* * we've got more storage, clear any full flags on the space @@ -2252,8 +2336,9 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) */ snprintf(fsid_buf, BTRFS_UUID_UNPARSED_SIZE, "%pU", root->fs_info->fsid); - if (kobject_rename(&root->fs_info->super_kobj, fsid_buf)) - goto error_trans; + if (kobject_rename(&root->fs_info->fs_devices->super_kobj, + fsid_buf)) + pr_warn("BTRFS: sysfs: failed to create fsid for sprout\n"); } root->fs_info->num_tolerated_disk_barrier_failures = @@ -2289,7 +2374,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) error_trans: btrfs_end_transaction(trans, root); rcu_string_free(device->name); - btrfs_kobj_rm_device(root->fs_info, device); + btrfs_kobj_rm_device(root->fs_info->fs_devices, device); kfree(device); error: blkdev_put(bdev, FMODE_EXCL); @@ -2609,6 +2694,9 @@ int btrfs_remove_chunk(struct btrfs_trans_handle *trans, return -EINVAL; } map = (struct map_lookup *)em->bdev; + lock_chunks(root->fs_info->chunk_root); + check_system_chunk(trans, extent_root, map->type); + unlock_chunks(root->fs_info->chunk_root); for (i = 0; i < map->num_stripes; i++) { struct btrfs_device *device = map->stripes[i].dev; @@ -2678,6 +2766,20 @@ static int btrfs_relocate_chunk(struct btrfs_root *root, root = root->fs_info->chunk_root; extent_root = root->fs_info->extent_root; + /* + * Prevent races with automatic removal of unused block groups. + * After we relocate and before we remove the chunk with offset + * chunk_offset, automatic removal of the block group can kick in, + * resulting in a failure when calling btrfs_remove_chunk() below. + * + * Make sure to acquire this mutex before doing a tree search (dev + * or chunk trees) to find chunks. Otherwise the cleaner kthread might + * call btrfs_remove_chunk() (through btrfs_delete_unused_bgs()) after + * we release the path used to search the chunk/dev tree and before + * the current task acquires this mutex and calls us. + */ + ASSERT(mutex_is_locked(&root->fs_info->delete_unused_bgs_mutex)); + ret = btrfs_can_relocate(extent_root, chunk_offset); if (ret) return -ENOSPC; @@ -2726,13 +2828,18 @@ again: key.type = BTRFS_CHUNK_ITEM_KEY; while (1) { + mutex_lock(&root->fs_info->delete_unused_bgs_mutex); ret = btrfs_search_slot(NULL, chunk_root, &key, path, 0, 0); - if (ret < 0) + if (ret < 0) { + mutex_unlock(&root->fs_info->delete_unused_bgs_mutex); goto error; + } BUG_ON(ret == 0); /* Corruption */ ret = btrfs_previous_item(chunk_root, path, key.objectid, key.type); + if (ret) + mutex_unlock(&root->fs_info->delete_unused_bgs_mutex); if (ret < 0) goto error; if (ret > 0) @@ -2755,6 +2862,7 @@ again: else BUG_ON(ret); } + mutex_unlock(&root->fs_info->delete_unused_bgs_mutex); if (found_key.offset == 0) break; @@ -3211,9 +3319,12 @@ again: goto error; } + mutex_lock(&fs_info->delete_unused_bgs_mutex); ret = btrfs_search_slot(NULL, chunk_root, &key, path, 0, 0); - if (ret < 0) + if (ret < 0) { + mutex_unlock(&fs_info->delete_unused_bgs_mutex); goto error; + } /* * this shouldn't happen, it means the last relocate @@ -3225,6 +3336,7 @@ again: ret = btrfs_previous_item(chunk_root, path, 0, BTRFS_CHUNK_ITEM_KEY); if (ret) { + mutex_unlock(&fs_info->delete_unused_bgs_mutex); ret = 0; break; } @@ -3233,8 +3345,10 @@ again: slot = path->slots[0]; btrfs_item_key_to_cpu(leaf, &found_key, slot); - if (found_key.objectid != key.objectid) + if (found_key.objectid != key.objectid) { + mutex_unlock(&fs_info->delete_unused_bgs_mutex); break; + } chunk = btrfs_item_ptr(leaf, slot, struct btrfs_chunk); @@ -3247,10 +3361,13 @@ again: ret = should_balance_chunk(chunk_root, leaf, chunk, found_key.offset); btrfs_release_path(path); - if (!ret) + if (!ret) { + mutex_unlock(&fs_info->delete_unused_bgs_mutex); goto loop; + } if (counting) { + mutex_unlock(&fs_info->delete_unused_bgs_mutex); spin_lock(&fs_info->balance_lock); bctl->stat.expected++; spin_unlock(&fs_info->balance_lock); @@ -3260,6 +3377,7 @@ again: ret = btrfs_relocate_chunk(chunk_root, found_key.objectid, found_key.offset); + mutex_unlock(&fs_info->delete_unused_bgs_mutex); if (ret && ret != -ENOSPC) goto error; if (ret == -ENOSPC) { @@ -3908,9 +4026,9 @@ int btrfs_create_uuid_tree(struct btrfs_fs_info *fs_info) uuid_root = btrfs_create_tree(trans, fs_info, BTRFS_UUID_TREE_OBJECTID); if (IS_ERR(uuid_root)) { - btrfs_abort_transaction(trans, tree_root, - PTR_ERR(uuid_root)); - return PTR_ERR(uuid_root); + ret = PTR_ERR(uuid_root); + btrfs_abort_transaction(trans, tree_root, ret); + return ret; } fs_info->uuid_root = uuid_root; @@ -3965,6 +4083,7 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size) int slot; int failed = 0; bool retried = false; + bool checked_pending_chunks = false; struct extent_buffer *l; struct btrfs_key key; struct btrfs_super_block *super_copy = root->fs_info->super_copy; @@ -3998,11 +4117,16 @@ again: key.type = BTRFS_DEV_EXTENT_KEY; do { + mutex_lock(&root->fs_info->delete_unused_bgs_mutex); ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); - if (ret < 0) + if (ret < 0) { + mutex_unlock(&root->fs_info->delete_unused_bgs_mutex); goto done; + } ret = btrfs_previous_item(root, path, 0, key.type); + if (ret) + mutex_unlock(&root->fs_info->delete_unused_bgs_mutex); if (ret < 0) goto done; if (ret) { @@ -4016,6 +4140,7 @@ again: btrfs_item_key_to_cpu(l, &key, path->slots[0]); if (key.objectid != device->devid) { + mutex_unlock(&root->fs_info->delete_unused_bgs_mutex); btrfs_release_path(path); break; } @@ -4024,6 +4149,7 @@ again: length = btrfs_dev_extent_length(l, dev_extent); if (key.offset + length <= new_size) { + mutex_unlock(&root->fs_info->delete_unused_bgs_mutex); btrfs_release_path(path); break; } @@ -4033,6 +4159,7 @@ again: btrfs_release_path(path); ret = btrfs_relocate_chunk(root, chunk_objectid, chunk_offset); + mutex_unlock(&root->fs_info->delete_unused_bgs_mutex); if (ret && ret != -ENOSPC) goto done; if (ret == -ENOSPC) @@ -4045,15 +4172,6 @@ again: goto again; } else if (failed && retried) { ret = -ENOSPC; - lock_chunks(root); - - btrfs_device_set_total_bytes(device, old_size); - if (device->writeable) - device->fs_devices->total_rw_bytes += diff; - spin_lock(&root->fs_info->free_chunk_lock); - root->fs_info->free_chunk_space += diff; - spin_unlock(&root->fs_info->free_chunk_lock); - unlock_chunks(root); goto done; } @@ -4065,6 +4183,35 @@ again: } lock_chunks(root); + + /* + * We checked in the above loop all device extents that were already in + * the device tree. However before we have updated the device's + * total_bytes to the new size, we might have had chunk allocations that + * have not complete yet (new block groups attached to transaction + * handles), and therefore their device extents were not yet in the + * device tree and we missed them in the loop above. So if we have any + * pending chunk using a device extent that overlaps the device range + * that we can not use anymore, commit the current transaction and + * repeat the search on the device tree - this way we guarantee we will + * not have chunks using device extents that end beyond 'new_size'. + */ + if (!checked_pending_chunks) { + u64 start = new_size; + u64 len = old_size - new_size; + + if (contains_pending_extent(trans, device, &start, len)) { + unlock_chunks(root); + checked_pending_chunks = true; + failed = 0; + retried = false; + ret = btrfs_commit_transaction(trans, root); + if (ret) + goto done; + goto again; + } + } + btrfs_device_set_disk_total_bytes(device, new_size); if (list_empty(&device->resized_list)) list_add_tail(&device->resized_list, @@ -4079,6 +4226,16 @@ again: btrfs_end_transaction(trans, root); done: btrfs_free_path(path); + if (ret) { + lock_chunks(root); + btrfs_device_set_total_bytes(device, old_size); + if (device->writeable) + device->fs_devices->total_rw_bytes += diff; + spin_lock(&root->fs_info->free_chunk_lock); + root->fs_info->free_chunk_space += diff; + spin_unlock(&root->fs_info->free_chunk_lock); + unlock_chunks(root); + } return ret; } @@ -5586,17 +5743,16 @@ int btrfs_rmap_block(struct btrfs_mapping_tree *map_tree, static inline void btrfs_end_bbio(struct btrfs_bio *bbio, struct bio *bio, int err) { - if (likely(bbio->flags & BTRFS_BIO_ORIG_BIO_SUBMITTED)) - bio_endio_nodec(bio, err); - else - bio_endio(bio, err); + bio->bi_private = bbio->private; + bio->bi_end_io = bbio->end_io; + bio_endio(bio, err); + btrfs_put_bbio(bbio); } static void btrfs_end_bio(struct bio *bio, int err) { struct btrfs_bio *bbio = bio->bi_private; - struct btrfs_device *dev = bbio->stripes[0].dev; int is_orig_bio = 0; if (err) { @@ -5604,6 +5760,7 @@ static void btrfs_end_bio(struct bio *bio, int err) if (err == -EIO || err == -EREMOTEIO) { unsigned int stripe_index = btrfs_io_bio(bio)->stripe_index; + struct btrfs_device *dev; BUG_ON(stripe_index >= bbio->num_stripes); dev = bbio->stripes[stripe_index].dev; @@ -5633,8 +5790,6 @@ static void btrfs_end_bio(struct bio *bio, int err) bio = bbio->orig_bio; } - bio->bi_private = bbio->private; - bio->bi_end_io = bbio->end_io; btrfs_io_bio(bio)->mirror_num = bbio->mirror_num; /* only send an error to the higher layers if it is * beyond the tolerance of the btrfs bio @@ -5816,8 +5971,6 @@ static void bbio_error(struct btrfs_bio *bbio, struct bio *bio, u64 logical) /* Shoud be the original bio. */ WARN_ON(bio != bbio->orig_bio); - bio->bi_private = bbio->private; - bio->bi_end_io = bbio->end_io; btrfs_io_bio(bio)->mirror_num = bbio->mirror_num; bio->bi_iter.bi_sector = logical >> 9; @@ -5898,10 +6051,8 @@ int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio, if (dev_nr < total_devs - 1) { bio = btrfs_bio_clone(first_bio, GFP_NOFS); BUG_ON(!bio); /* -ENOMEM */ - } else { + } else bio = first_bio; - bbio->flags |= BTRFS_BIO_ORIG_BIO_SUBMITTED; - } submit_stripe_bio(root, bbio, bio, bbio->stripes[dev_nr].physical, dev_nr, rw, @@ -6078,6 +6229,8 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key, free_extent_map(em); return -EIO; } + btrfs_warn(root->fs_info, "devid %llu uuid %pU is missing", + devid, uuid); } map->stripes[i].dev->in_fs_metadata = 1; } @@ -6197,10 +6350,11 @@ static int read_one_dev(struct btrfs_root *root, if (!btrfs_test_opt(root, DEGRADED)) return -EIO; - btrfs_warn(root->fs_info, "devid %llu missing", devid); device = add_missing_dev(root, fs_devices, devid, dev_uuid); if (!device) return -ENOMEM; + btrfs_warn(root->fs_info, "devid %llu uuid %pU missing", + devid, dev_uuid); } else { if (!device->bdev && !btrfs_test_opt(root, DEGRADED)) return -EIO; @@ -6728,3 +6882,21 @@ void btrfs_update_commit_device_bytes_used(struct btrfs_root *root, } unlock_chunks(root); } + +void btrfs_set_fs_info_ptr(struct btrfs_fs_info *fs_info) +{ + struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; + while (fs_devices) { + fs_devices->fs_info = fs_info; + fs_devices = fs_devices->seed; + } +} + +void btrfs_reset_fs_info_ptr(struct btrfs_fs_info *fs_info) +{ + struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; + while (fs_devices) { + fs_devices->fs_info = NULL; + fs_devices = fs_devices->seed; + } +} diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index ebc31331a..95842a909 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -253,6 +253,12 @@ struct btrfs_fs_devices { * nonrot flag set */ int rotating; + + struct btrfs_fs_info *fs_info; + /* sysfs kobjects */ + struct kobject super_kobj; + struct kobject *device_dir_kobj; + struct completion kobj_unregister; }; #define BTRFS_BIO_INLINE_CSUM_SIZE 64 @@ -292,8 +298,6 @@ struct btrfs_bio_stripe { struct btrfs_bio; typedef void (btrfs_bio_end_io_t) (struct btrfs_bio *bio, int err); -#define BTRFS_BIO_ORIG_BIO_SUBMITTED (1 << 0) - struct btrfs_bio { atomic_t refs; atomic_t stripes_pending; @@ -537,5 +541,8 @@ static inline void unlock_chunks(struct btrfs_root *root) mutex_unlock(&root->fs_info->chunk_mutex); } +struct list_head *btrfs_get_fs_uuids(void); +void btrfs_set_fs_info_ptr(struct btrfs_fs_info *fs_info); +void btrfs_reset_fs_info_ptr(struct btrfs_fs_info *fs_info); #endif diff --git a/fs/buffer.c b/fs/buffer.c index 8c50a2250..1cf7a53a0 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -30,6 +30,7 @@ #include <linux/quotaops.h> #include <linux/highmem.h> #include <linux/export.h> +#include <linux/backing-dev.h> #include <linux/writeback.h> #include <linux/hash.h> #include <linux/suspend.h> @@ -44,6 +45,9 @@ #include <trace/events/block.h> static int fsync_buffers_list(spinlock_t *lock, struct list_head *list); +static int submit_bh_wbc(int rw, struct buffer_head *bh, + unsigned long bio_flags, + struct writeback_control *wbc); #define BH_ENTRY(list) list_entry((list), struct buffer_head, b_assoc_buffers) @@ -623,21 +627,22 @@ EXPORT_SYMBOL(mark_buffer_dirty_inode); * * If warn is true, then emit a warning if the page is not uptodate and has * not been truncated. + * + * The caller must hold mem_cgroup_begin_page_stat() lock. */ -static void __set_page_dirty(struct page *page, - struct address_space *mapping, int warn) +static void __set_page_dirty(struct page *page, struct address_space *mapping, + struct mem_cgroup *memcg, int warn) { unsigned long flags; spin_lock_irqsave(&mapping->tree_lock, flags); if (page->mapping) { /* Race with truncate? */ WARN_ON_ONCE(warn && !PageUptodate(page)); - account_page_dirtied(page, mapping); + account_page_dirtied(page, mapping, memcg); radix_tree_tag_set(&mapping->page_tree, page_index(page), PAGECACHE_TAG_DIRTY); } spin_unlock_irqrestore(&mapping->tree_lock, flags); - __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); } /* @@ -668,6 +673,7 @@ static void __set_page_dirty(struct page *page, int __set_page_dirty_buffers(struct page *page) { int newly_dirty; + struct mem_cgroup *memcg; struct address_space *mapping = page_mapping(page); if (unlikely(!mapping)) @@ -683,11 +689,22 @@ int __set_page_dirty_buffers(struct page *page) bh = bh->b_this_page; } while (bh != head); } + /* + * Use mem_group_begin_page_stat() to keep PageDirty synchronized with + * per-memcg dirty page counters. + */ + memcg = mem_cgroup_begin_page_stat(page); newly_dirty = !TestSetPageDirty(page); spin_unlock(&mapping->private_lock); if (newly_dirty) - __set_page_dirty(page, mapping, 1); + __set_page_dirty(page, mapping, memcg, 1); + + mem_cgroup_end_page_stat(memcg); + + if (newly_dirty) + __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); + return newly_dirty; } EXPORT_SYMBOL(__set_page_dirty_buffers); @@ -1158,11 +1175,18 @@ void mark_buffer_dirty(struct buffer_head *bh) if (!test_set_buffer_dirty(bh)) { struct page *page = bh->b_page; + struct address_space *mapping = NULL; + struct mem_cgroup *memcg; + + memcg = mem_cgroup_begin_page_stat(page); if (!TestSetPageDirty(page)) { - struct address_space *mapping = page_mapping(page); + mapping = page_mapping(page); if (mapping) - __set_page_dirty(page, mapping, 0); + __set_page_dirty(page, mapping, memcg, 0); } + mem_cgroup_end_page_stat(memcg); + if (mapping) + __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); } } EXPORT_SYMBOL(mark_buffer_dirty); @@ -1684,8 +1708,7 @@ static int __block_write_full_page(struct inode *inode, struct page *page, struct buffer_head *bh, *head; unsigned int blocksize, bbits; int nr_underway = 0; - int write_op = (wbc->sync_mode == WB_SYNC_ALL ? - WRITE_SYNC : WRITE); + int write_op = (wbc->sync_mode == WB_SYNC_ALL ? WRITE_SYNC : WRITE); head = create_page_buffers(page, inode, (1 << BH_Dirty)|(1 << BH_Uptodate)); @@ -1774,7 +1797,7 @@ static int __block_write_full_page(struct inode *inode, struct page *page, do { struct buffer_head *next = bh->b_this_page; if (buffer_async_write(bh)) { - submit_bh(write_op, bh); + submit_bh_wbc(write_op, bh, 0, wbc); nr_underway++; } bh = next; @@ -1828,7 +1851,7 @@ recover: struct buffer_head *next = bh->b_this_page; if (buffer_async_write(bh)) { clear_buffer_dirty(bh); - submit_bh(write_op, bh); + submit_bh_wbc(write_op, bh, 0, wbc); nr_underway++; } bh = next; @@ -2450,7 +2473,7 @@ int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf, * Update file times before taking page lock. We may end up failing the * fault so this update may be superfluous but who really cares... */ - vma_file_update_time(vma); + file_update_time(vma->vm_file); ret = __block_page_mkwrite(vma, vmf, get_block); sb_end_pagefault(sb); @@ -2938,10 +2961,6 @@ static void end_bio_bh_io_sync(struct bio *bio, int err) { struct buffer_head *bh = bio->bi_private; - if (err == -EOPNOTSUPP) { - set_bit(BIO_EOPNOTSUPP, &bio->bi_flags); - } - if (unlikely (test_bit(BIO_QUIET,&bio->bi_flags))) set_bit(BH_Quiet, &bh->b_state); @@ -2997,10 +3016,10 @@ void guard_bio_eod(int rw, struct bio *bio) } } -int _submit_bh(int rw, struct buffer_head *bh, unsigned long bio_flags) +static int submit_bh_wbc(int rw, struct buffer_head *bh, + unsigned long bio_flags, struct writeback_control *wbc) { struct bio *bio; - int ret = 0; BUG_ON(!buffer_locked(bh)); BUG_ON(!buffer_mapped(bh)); @@ -3020,6 +3039,11 @@ int _submit_bh(int rw, struct buffer_head *bh, unsigned long bio_flags) */ bio = bio_alloc(GFP_NOIO, 1); + if (wbc) { + wbc_init_bio(wbc, bio); + wbc_account_io(wbc, bh->b_page, bh->b_size); + } + bio->bi_iter.bi_sector = bh->b_blocknr * (bh->b_size >> 9); bio->bi_bdev = bh->b_bdev; bio->bi_io_vec[0].bv_page = bh->b_page; @@ -3041,20 +3065,19 @@ int _submit_bh(int rw, struct buffer_head *bh, unsigned long bio_flags) if (buffer_prio(bh)) rw |= REQ_PRIO; - bio_get(bio); submit_bio(rw, bio); + return 0; +} - if (bio_flagged(bio, BIO_EOPNOTSUPP)) - ret = -EOPNOTSUPP; - - bio_put(bio); - return ret; +int _submit_bh(int rw, struct buffer_head *bh, unsigned long bio_flags) +{ + return submit_bh_wbc(rw, bh, bio_flags, NULL); } EXPORT_SYMBOL_GPL(_submit_bh); int submit_bh(int rw, struct buffer_head *bh) { - return _submit_bh(rw, bh, 0); + return submit_bh_wbc(rw, bh, 0, NULL); } EXPORT_SYMBOL(submit_bh); @@ -3243,8 +3266,8 @@ int try_to_free_buffers(struct page *page) * to synchronise against __set_page_dirty_buffers and prevent the * dirty bit from being lost. */ - if (ret && TestClearPageDirty(page)) - account_page_cleaned(page, mapping); + if (ret) + cancel_dirty_page(page); spin_unlock(&mapping->private_lock); out: if (buffers_to_free) { diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index 8c52472d2..aecd0859e 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -43,7 +43,6 @@ struct cachefiles_object { loff_t i_size; /* object size */ unsigned long flags; #define CACHEFILES_OBJECT_ACTIVE 0 /* T if marked active */ -#define CACHEFILES_OBJECT_BURIED 1 /* T if preemptively buried */ atomic_t usage; /* object usage count */ uint8_t type; /* object type */ uint8_t new; /* T if object new */ diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index ab857ab9f..fc1056f5c 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -97,7 +97,8 @@ static noinline void cachefiles_printk_object(struct cachefiles_object *object, * call vfs_unlink(), vfs_rmdir() or vfs_rename() */ static void cachefiles_mark_object_buried(struct cachefiles_cache *cache, - struct dentry *dentry) + struct dentry *dentry, + enum fscache_why_object_killed why) { struct cachefiles_object *object; struct rb_node *p; @@ -132,8 +133,9 @@ found_dentry: pr_err("\n"); pr_err("Error: Can't preemptively bury live object\n"); cachefiles_printk_object(object, NULL); - } else if (test_and_set_bit(CACHEFILES_OBJECT_BURIED, &object->flags)) { - pr_err("Error: Object already preemptively buried\n"); + } else { + if (why != FSCACHE_OBJECT_IS_STALE) + fscache_object_mark_killed(&object->fscache, why); } write_unlock(&cache->active_lock); @@ -265,7 +267,8 @@ requeue: static int cachefiles_bury_object(struct cachefiles_cache *cache, struct dentry *dir, struct dentry *rep, - bool preemptive) + bool preemptive, + enum fscache_why_object_killed why) { struct dentry *grave, *trap; struct path path, path_to_graveyard; @@ -289,7 +292,7 @@ static int cachefiles_bury_object(struct cachefiles_cache *cache, ret = vfs_unlink(d_inode(dir), rep, NULL); if (preemptive) - cachefiles_mark_object_buried(cache, rep); + cachefiles_mark_object_buried(cache, rep, why); } mutex_unlock(&d_inode(dir)->i_mutex); @@ -394,7 +397,7 @@ try_again: "Rename failed with error %d", ret); if (preemptive) - cachefiles_mark_object_buried(cache, rep); + cachefiles_mark_object_buried(cache, rep, why); } unlock_rename(cache->graveyard, dir); @@ -422,7 +425,7 @@ int cachefiles_delete_object(struct cachefiles_cache *cache, mutex_lock_nested(&d_inode(dir)->i_mutex, I_MUTEX_PARENT); - if (test_bit(CACHEFILES_OBJECT_BURIED, &object->flags)) { + if (test_bit(FSCACHE_OBJECT_KILLED_BY_CACHE, &object->fscache.flags)) { /* object allocation for the same key preemptively deleted this * object's file so that it could create its own file */ _debug("object preemptively buried"); @@ -433,7 +436,8 @@ int cachefiles_delete_object(struct cachefiles_cache *cache, * may have been renamed */ if (dir == object->dentry->d_parent) { ret = cachefiles_bury_object(cache, dir, - object->dentry, false); + object->dentry, false, + FSCACHE_OBJECT_WAS_RETIRED); } else { /* it got moved, presumably by cachefilesd culling it, * so it's no longer in the key path and we can ignore @@ -522,7 +526,7 @@ lookup_again: if (d_is_negative(next)) { ret = cachefiles_has_space(cache, 1, 0); if (ret < 0) - goto create_error; + goto no_space_error; path.dentry = dir; ret = security_path_mkdir(&path, next, 0); @@ -551,7 +555,7 @@ lookup_again: if (d_is_negative(next)) { ret = cachefiles_has_space(cache, 1, 0); if (ret < 0) - goto create_error; + goto no_space_error; path.dentry = dir; ret = security_path_mknod(&path, next, S_IFREG, 0); @@ -602,7 +606,8 @@ lookup_again: * mutex) */ object->dentry = NULL; - ret = cachefiles_bury_object(cache, dir, next, true); + ret = cachefiles_bury_object(cache, dir, next, true, + FSCACHE_OBJECT_IS_STALE); dput(next); next = NULL; @@ -610,6 +615,7 @@ lookup_again: goto delete_error; _debug("redo lookup"); + fscache_object_retrying_stale(&object->fscache); goto lookup_again; } } @@ -662,6 +668,8 @@ lookup_again: _leave(" = 0 [%lu]", d_backing_inode(object->dentry)->i_ino); return 0; +no_space_error: + fscache_object_mark_killed(&object->fscache, FSCACHE_OBJECT_NO_SPACE); create_error: _debug("create error %d", ret); if (ret == -EIO) @@ -927,7 +935,8 @@ int cachefiles_cull(struct cachefiles_cache *cache, struct dentry *dir, /* actually remove the victim (drops the dir mutex) */ _debug("bury"); - ret = cachefiles_bury_object(cache, dir, victim, false); + ret = cachefiles_bury_object(cache, dir, victim, false, + FSCACHE_OBJECT_WAS_CULLED); if (ret < 0) goto error; diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c index 64fa24834..8f84646f1 100644 --- a/fs/ceph/acl.c +++ b/fs/ceph/acl.c @@ -187,10 +187,10 @@ int ceph_pre_init_acls(struct inode *dir, umode_t *mode, val_size2 = posix_acl_xattr_size(default_acl->a_count); err = -ENOMEM; - tmp_buf = kmalloc(max(val_size1, val_size2), GFP_NOFS); + tmp_buf = kmalloc(max(val_size1, val_size2), GFP_KERNEL); if (!tmp_buf) goto out_err; - pagelist = kmalloc(sizeof(struct ceph_pagelist), GFP_NOFS); + pagelist = kmalloc(sizeof(struct ceph_pagelist), GFP_KERNEL); if (!pagelist) goto out_err; ceph_pagelist_init(pagelist); diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index e162bcd10..890c50971 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -87,17 +87,21 @@ static int ceph_set_page_dirty(struct page *page) inode = mapping->host; ci = ceph_inode(inode); - /* - * Note that we're grabbing a snapc ref here without holding - * any locks! - */ - snapc = ceph_get_snap_context(ci->i_snap_realm->cached_context); - /* dirty the head */ spin_lock(&ci->i_ceph_lock); - if (ci->i_head_snapc == NULL) - ci->i_head_snapc = ceph_get_snap_context(snapc); - ++ci->i_wrbuffer_ref_head; + BUG_ON(ci->i_wr_ref == 0); // caller should hold Fw reference + if (__ceph_have_pending_cap_snap(ci)) { + struct ceph_cap_snap *capsnap = + list_last_entry(&ci->i_cap_snaps, + struct ceph_cap_snap, + ci_item); + snapc = ceph_get_snap_context(capsnap->context); + capsnap->dirty_pages++; + } else { + BUG_ON(!ci->i_head_snapc); + snapc = ceph_get_snap_context(ci->i_head_snapc); + ++ci->i_wrbuffer_ref_head; + } if (ci->i_wrbuffer_ref == 0) ihold(inode); ++ci->i_wrbuffer_ref; @@ -346,7 +350,7 @@ static int start_read(struct inode *inode, struct list_head *page_list, int max) /* build page vector */ nr_pages = calc_pages_for(0, len); - pages = kmalloc(sizeof(*pages) * nr_pages, GFP_NOFS); + pages = kmalloc(sizeof(*pages) * nr_pages, GFP_KERNEL); ret = -ENOMEM; if (!pages) goto out; @@ -358,7 +362,7 @@ static int start_read(struct inode *inode, struct list_head *page_list, int max) dout("start_read %p adding %p idx %lu\n", inode, page, page->index); if (add_to_page_cache_lru(page, &inode->i_data, page->index, - GFP_NOFS)) { + GFP_KERNEL)) { ceph_fscache_uncache_page(inode, page); page_cache_release(page); dout("start_read %p add_to_page_cache failed %p\n", @@ -436,7 +440,7 @@ out: * only snap context we are allowed to write back. */ static struct ceph_snap_context *get_oldest_context(struct inode *inode, - u64 *snap_size) + loff_t *snap_size) { struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_snap_context *snapc = NULL; @@ -476,8 +480,9 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) struct ceph_osd_client *osdc; struct ceph_snap_context *snapc, *oldest; loff_t page_off = page_offset(page); + loff_t snap_size = -1; long writeback_stat; - u64 truncate_size, snap_size = 0; + u64 truncate_size; u32 truncate_seq; int err = 0, len = PAGE_CACHE_SIZE; @@ -512,7 +517,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) spin_lock(&ci->i_ceph_lock); truncate_seq = ci->i_truncate_seq; truncate_size = ci->i_truncate_size; - if (!snap_size) + if (snap_size == -1) snap_size = i_size_read(inode); spin_unlock(&ci->i_ceph_lock); @@ -695,7 +700,8 @@ static int ceph_writepages_start(struct address_space *mapping, unsigned wsize = 1 << inode->i_blkbits; struct ceph_osd_request *req = NULL; int do_sync = 0; - u64 truncate_size, snap_size; + loff_t snap_size, i_size; + u64 truncate_size; u32 truncate_seq; /* @@ -741,7 +747,7 @@ static int ceph_writepages_start(struct address_space *mapping, retry: /* find oldest snap context with dirty data */ ceph_put_snap_context(snapc); - snap_size = 0; + snap_size = -1; snapc = get_oldest_context(inode, &snap_size); if (!snapc) { /* hmm, why does writepages get called when there @@ -749,16 +755,13 @@ retry: dout(" no snap context with dirty data?\n"); goto out; } - if (snap_size == 0) - snap_size = i_size_read(inode); dout(" oldest snapc is %p seq %lld (%d snaps)\n", snapc, snapc->seq, snapc->num_snaps); spin_lock(&ci->i_ceph_lock); truncate_seq = ci->i_truncate_seq; truncate_size = ci->i_truncate_size; - if (!snap_size) - snap_size = i_size_read(inode); + i_size = i_size_read(inode); spin_unlock(&ci->i_ceph_lock); if (last_snapc && snapc != last_snapc) { @@ -828,8 +831,10 @@ get_more_pages: dout("waiting on writeback %p\n", page); wait_on_page_writeback(page); } - if (page_offset(page) >= snap_size) { - dout("%p page eof %llu\n", page, snap_size); + if (page_offset(page) >= + (snap_size == -1 ? i_size : snap_size)) { + dout("%p page eof %llu\n", page, + (snap_size == -1 ? i_size : snap_size)); done = 1; unlock_page(page); break; @@ -884,7 +889,8 @@ get_more_pages: } if (do_sync) - osd_req_op_init(req, 1, CEPH_OSD_OP_STARTSYNC); + osd_req_op_init(req, 1, + CEPH_OSD_OP_STARTSYNC, 0); req->r_callback = writepages_finish; req->r_inode = inode; @@ -944,10 +950,18 @@ get_more_pages: } /* Format the osd request message and submit the write */ - offset = page_offset(pages[0]); - len = min(snap_size - offset, - (u64)locked_pages << PAGE_CACHE_SHIFT); + len = (u64)locked_pages << PAGE_CACHE_SHIFT; + if (snap_size == -1) { + len = min(len, (u64)i_size_read(inode) - offset); + /* writepages_finish() clears writeback pages + * according to the data length, so make sure + * data length covers all locked pages */ + len = max(len, 1 + + ((u64)(locked_pages - 1) << PAGE_CACHE_SHIFT)); + } else { + len = min(len, snap_size - offset); + } dout("writepages got %d pages at %llu~%llu\n", locked_pages, offset, len); @@ -1032,7 +1046,6 @@ static int ceph_update_writeable_page(struct file *file, { struct inode *inode = file_inode(file); struct ceph_inode_info *ci = ceph_inode(inode); - struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; loff_t page_off = pos & PAGE_CACHE_MASK; int pos_in_page = pos & ~PAGE_CACHE_MASK; int end_in_page = pos_in_page + len; @@ -1044,10 +1057,6 @@ retry_locked: /* writepages currently holds page lock, but if we change that later, */ wait_on_page_writeback(page); - /* check snap context */ - BUG_ON(!ci->i_snap_realm); - down_read(&mdsc->snap_rwsem); - BUG_ON(!ci->i_snap_realm->cached_context); snapc = page_snap_context(page); if (snapc && snapc != ci->i_head_snapc) { /* @@ -1055,7 +1064,6 @@ retry_locked: * context! is it writeable now? */ oldest = get_oldest_context(inode, NULL); - up_read(&mdsc->snap_rwsem); if (snapc->seq > oldest->seq) { ceph_put_snap_context(oldest); @@ -1112,7 +1120,6 @@ retry_locked: } /* we need to read it. */ - up_read(&mdsc->snap_rwsem); r = readpage_nounlock(file, page); if (r < 0) goto fail_nosnap; @@ -1157,16 +1164,13 @@ static int ceph_write_begin(struct file *file, struct address_space *mapping, /* * we don't do anything in here that simple_write_end doesn't do - * except adjust dirty page accounting and drop read lock on - * mdsc->snap_rwsem. + * except adjust dirty page accounting */ static int ceph_write_end(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, struct page *page, void *fsdata) { struct inode *inode = file_inode(file); - struct ceph_fs_client *fsc = ceph_inode_to_client(inode); - struct ceph_mds_client *mdsc = fsc->mdsc; unsigned from = pos & (PAGE_CACHE_SIZE - 1); int check_cap = 0; @@ -1188,7 +1192,6 @@ static int ceph_write_end(struct file *file, struct address_space *mapping, set_page_dirty(page); unlock_page(page); - up_read(&mdsc->snap_rwsem); page_cache_release(page); if (check_cap) @@ -1314,13 +1317,17 @@ static int ceph_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) struct inode *inode = file_inode(vma->vm_file); struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_file_info *fi = vma->vm_file->private_data; - struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; + struct ceph_cap_flush *prealloc_cf; struct page *page = vmf->page; loff_t off = page_offset(page); loff_t size = i_size_read(inode); size_t len; int want, got, ret; + prealloc_cf = ceph_alloc_cap_flush(); + if (!prealloc_cf) + return VM_FAULT_SIGBUS; + if (ci->i_inline_version != CEPH_INLINE_NONE) { struct page *locked_page = NULL; if (off == 0) { @@ -1330,8 +1337,10 @@ static int ceph_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) ret = ceph_uninline_data(vma->vm_file, locked_page); if (locked_page) unlock_page(locked_page); - if (ret < 0) - return VM_FAULT_SIGBUS; + if (ret < 0) { + ret = VM_FAULT_SIGBUS; + goto out_free; + } } if (off + PAGE_CACHE_SIZE <= size) @@ -1353,7 +1362,8 @@ static int ceph_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) break; if (ret != -ERESTARTSYS) { WARN_ON(1); - return VM_FAULT_SIGBUS; + ret = VM_FAULT_SIGBUS; + goto out_free; } } dout("page_mkwrite %p %llu~%zd got cap refs on %s\n", @@ -1373,7 +1383,6 @@ static int ceph_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) if (ret == 0) { /* success. we'll keep the page locked. */ set_page_dirty(page); - up_read(&mdsc->snap_rwsem); ret = VM_FAULT_LOCKED; } else { if (ret == -ENOMEM) @@ -1389,7 +1398,8 @@ out: int dirty; spin_lock(&ci->i_ceph_lock); ci->i_inline_version = CEPH_INLINE_NONE; - dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR); + dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR, + &prealloc_cf); spin_unlock(&ci->i_ceph_lock); if (dirty) __mark_inode_dirty(inode, dirty); @@ -1398,6 +1408,8 @@ out: dout("page_mkwrite %p %llu~%zd dropping cap refs on %s ret %d\n", inode, off, len, ceph_cap_string(got), ret); ceph_put_cap_refs(ci, got); +out_free: + ceph_free_cap_flush(prealloc_cf); return ret; } @@ -1509,8 +1521,7 @@ int ceph_uninline_data(struct file *filp, struct page *locked_page) ceph_vino(inode), 0, &len, 0, 1, CEPH_OSD_OP_CREATE, CEPH_OSD_FLAG_ONDISK | CEPH_OSD_FLAG_WRITE, - ci->i_snap_realm->cached_context, - 0, 0, false); + ceph_empty_snapc, 0, 0, false); if (IS_ERR(req)) { err = PTR_ERR(req); goto out; @@ -1528,7 +1539,7 @@ int ceph_uninline_data(struct file *filp, struct page *locked_page) ceph_vino(inode), 0, &len, 1, 3, CEPH_OSD_OP_WRITE, CEPH_OSD_FLAG_ONDISK | CEPH_OSD_FLAG_WRITE, - ci->i_snap_realm->cached_context, + ceph_empty_snapc, ci->i_truncate_seq, ci->i_truncate_size, false); if (IS_ERR(req)) { @@ -1597,3 +1608,206 @@ int ceph_mmap(struct file *file, struct vm_area_struct *vma) vma->vm_ops = &ceph_vmops; return 0; } + +enum { + POOL_READ = 1, + POOL_WRITE = 2, +}; + +static int __ceph_pool_perm_get(struct ceph_inode_info *ci, u32 pool) +{ + struct ceph_fs_client *fsc = ceph_inode_to_client(&ci->vfs_inode); + struct ceph_mds_client *mdsc = fsc->mdsc; + struct ceph_osd_request *rd_req = NULL, *wr_req = NULL; + struct rb_node **p, *parent; + struct ceph_pool_perm *perm; + struct page **pages; + int err = 0, err2 = 0, have = 0; + + down_read(&mdsc->pool_perm_rwsem); + p = &mdsc->pool_perm_tree.rb_node; + while (*p) { + perm = rb_entry(*p, struct ceph_pool_perm, node); + if (pool < perm->pool) + p = &(*p)->rb_left; + else if (pool > perm->pool) + p = &(*p)->rb_right; + else { + have = perm->perm; + break; + } + } + up_read(&mdsc->pool_perm_rwsem); + if (*p) + goto out; + + dout("__ceph_pool_perm_get pool %u no perm cached\n", pool); + + down_write(&mdsc->pool_perm_rwsem); + parent = NULL; + while (*p) { + parent = *p; + perm = rb_entry(parent, struct ceph_pool_perm, node); + if (pool < perm->pool) + p = &(*p)->rb_left; + else if (pool > perm->pool) + p = &(*p)->rb_right; + else { + have = perm->perm; + break; + } + } + if (*p) { + up_write(&mdsc->pool_perm_rwsem); + goto out; + } + + rd_req = ceph_osdc_alloc_request(&fsc->client->osdc, + ceph_empty_snapc, + 1, false, GFP_NOFS); + if (!rd_req) { + err = -ENOMEM; + goto out_unlock; + } + + rd_req->r_flags = CEPH_OSD_FLAG_READ; + osd_req_op_init(rd_req, 0, CEPH_OSD_OP_STAT, 0); + rd_req->r_base_oloc.pool = pool; + snprintf(rd_req->r_base_oid.name, sizeof(rd_req->r_base_oid.name), + "%llx.00000000", ci->i_vino.ino); + rd_req->r_base_oid.name_len = strlen(rd_req->r_base_oid.name); + + wr_req = ceph_osdc_alloc_request(&fsc->client->osdc, + ceph_empty_snapc, + 1, false, GFP_NOFS); + if (!wr_req) { + err = -ENOMEM; + goto out_unlock; + } + + wr_req->r_flags = CEPH_OSD_FLAG_WRITE | + CEPH_OSD_FLAG_ACK | CEPH_OSD_FLAG_ONDISK; + osd_req_op_init(wr_req, 0, CEPH_OSD_OP_CREATE, CEPH_OSD_OP_FLAG_EXCL); + wr_req->r_base_oloc.pool = pool; + wr_req->r_base_oid = rd_req->r_base_oid; + + /* one page should be large enough for STAT data */ + pages = ceph_alloc_page_vector(1, GFP_KERNEL); + if (IS_ERR(pages)) { + err = PTR_ERR(pages); + goto out_unlock; + } + + osd_req_op_raw_data_in_pages(rd_req, 0, pages, PAGE_SIZE, + 0, false, true); + ceph_osdc_build_request(rd_req, 0, NULL, CEPH_NOSNAP, + &ci->vfs_inode.i_mtime); + err = ceph_osdc_start_request(&fsc->client->osdc, rd_req, false); + + ceph_osdc_build_request(wr_req, 0, NULL, CEPH_NOSNAP, + &ci->vfs_inode.i_mtime); + err2 = ceph_osdc_start_request(&fsc->client->osdc, wr_req, false); + + if (!err) + err = ceph_osdc_wait_request(&fsc->client->osdc, rd_req); + if (!err2) + err2 = ceph_osdc_wait_request(&fsc->client->osdc, wr_req); + + if (err >= 0 || err == -ENOENT) + have |= POOL_READ; + else if (err != -EPERM) + goto out_unlock; + + if (err2 == 0 || err2 == -EEXIST) + have |= POOL_WRITE; + else if (err2 != -EPERM) { + err = err2; + goto out_unlock; + } + + perm = kmalloc(sizeof(*perm), GFP_NOFS); + if (!perm) { + err = -ENOMEM; + goto out_unlock; + } + + perm->pool = pool; + perm->perm = have; + rb_link_node(&perm->node, parent, p); + rb_insert_color(&perm->node, &mdsc->pool_perm_tree); + err = 0; +out_unlock: + up_write(&mdsc->pool_perm_rwsem); + + if (rd_req) + ceph_osdc_put_request(rd_req); + if (wr_req) + ceph_osdc_put_request(wr_req); +out: + if (!err) + err = have; + dout("__ceph_pool_perm_get pool %u result = %d\n", pool, err); + return err; +} + +int ceph_pool_perm_check(struct ceph_inode_info *ci, int need) +{ + u32 pool; + int ret, flags; + + if (ceph_test_mount_opt(ceph_inode_to_client(&ci->vfs_inode), + NOPOOLPERM)) + return 0; + + spin_lock(&ci->i_ceph_lock); + flags = ci->i_ceph_flags; + pool = ceph_file_layout_pg_pool(ci->i_layout); + spin_unlock(&ci->i_ceph_lock); +check: + if (flags & CEPH_I_POOL_PERM) { + if ((need & CEPH_CAP_FILE_RD) && !(flags & CEPH_I_POOL_RD)) { + dout("ceph_pool_perm_check pool %u no read perm\n", + pool); + return -EPERM; + } + if ((need & CEPH_CAP_FILE_WR) && !(flags & CEPH_I_POOL_WR)) { + dout("ceph_pool_perm_check pool %u no write perm\n", + pool); + return -EPERM; + } + return 0; + } + + ret = __ceph_pool_perm_get(ci, pool); + if (ret < 0) + return ret; + + flags = CEPH_I_POOL_PERM; + if (ret & POOL_READ) + flags |= CEPH_I_POOL_RD; + if (ret & POOL_WRITE) + flags |= CEPH_I_POOL_WR; + + spin_lock(&ci->i_ceph_lock); + if (pool == ceph_file_layout_pg_pool(ci->i_layout)) { + ci->i_ceph_flags = flags; + } else { + pool = ceph_file_layout_pg_pool(ci->i_layout); + flags = ci->i_ceph_flags; + } + spin_unlock(&ci->i_ceph_lock); + goto check; +} + +void ceph_pool_perm_destroy(struct ceph_mds_client *mdsc) +{ + struct ceph_pool_perm *perm; + struct rb_node *n; + + while (!RB_EMPTY_ROOT(&mdsc->pool_perm_tree)) { + n = rb_first(&mdsc->pool_perm_tree); + perm = rb_entry(n, struct ceph_pool_perm, node); + rb_erase(n, &mdsc->pool_perm_tree); + kfree(perm); + } +} diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index be5ea6af8..ddd5e9471 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -833,7 +833,9 @@ int __ceph_caps_used(struct ceph_inode_info *ci) used |= CEPH_CAP_PIN; if (ci->i_rd_ref) used |= CEPH_CAP_FILE_RD; - if (ci->i_rdcache_ref || ci->vfs_inode.i_data.nrpages) + if (ci->i_rdcache_ref || + (!S_ISDIR(ci->vfs_inode.i_mode) && /* ignore readdir cache */ + ci->vfs_inode.i_data.nrpages)) used |= CEPH_CAP_FILE_CACHE; if (ci->i_wr_ref) used |= CEPH_CAP_FILE_WR; @@ -926,16 +928,6 @@ void __ceph_remove_cap(struct ceph_cap *cap, bool queue_release) /* remove from session list */ spin_lock(&session->s_cap_lock); - /* - * s_cap_reconnect is protected by s_cap_lock. no one changes - * s_cap_gen while session is in the reconnect state. - */ - if (queue_release && - (!session->s_cap_reconnect || - cap->cap_gen == session->s_cap_gen)) - __queue_cap_release(session, ci->i_vino.ino, cap->cap_id, - cap->mseq, cap->issue_seq); - if (session->s_cap_iterator == cap) { /* not yet, we are iterating over this very cap */ dout("__ceph_remove_cap delaying %p removal from session %p\n", @@ -948,6 +940,25 @@ void __ceph_remove_cap(struct ceph_cap *cap, bool queue_release) } /* protect backpointer with s_cap_lock: see iterate_session_caps */ cap->ci = NULL; + + /* + * s_cap_reconnect is protected by s_cap_lock. no one changes + * s_cap_gen while session is in the reconnect state. + */ + if (queue_release && + (!session->s_cap_reconnect || cap->cap_gen == session->s_cap_gen)) { + cap->queue_release = 1; + if (removed) { + list_add_tail(&cap->session_caps, + &session->s_cap_releases); + session->s_num_cap_releases++; + removed = 0; + } + } else { + cap->queue_release = 0; + } + cap->cap_ino = ci->i_vino.ino; + spin_unlock(&session->s_cap_lock); /* remove from inode list */ @@ -977,8 +988,8 @@ void __ceph_remove_cap(struct ceph_cap *cap, bool queue_release) static int send_cap_msg(struct ceph_mds_session *session, u64 ino, u64 cid, int op, int caps, int wanted, int dirty, - u32 seq, u64 flush_tid, u32 issue_seq, u32 mseq, - u64 size, u64 max_size, + u32 seq, u64 flush_tid, u64 oldest_flush_tid, + u32 issue_seq, u32 mseq, u64 size, u64 max_size, struct timespec *mtime, struct timespec *atime, u64 time_warp_seq, kuid_t uid, kgid_t gid, umode_t mode, @@ -992,20 +1003,23 @@ static int send_cap_msg(struct ceph_mds_session *session, size_t extra_len; dout("send_cap_msg %s %llx %llx caps %s wanted %s dirty %s" - " seq %u/%u mseq %u follows %lld size %llu/%llu" + " seq %u/%u tid %llu/%llu mseq %u follows %lld size %llu/%llu" " xattr_ver %llu xattr_len %d\n", ceph_cap_op_name(op), cid, ino, ceph_cap_string(caps), ceph_cap_string(wanted), ceph_cap_string(dirty), - seq, issue_seq, mseq, follows, size, max_size, + seq, issue_seq, flush_tid, oldest_flush_tid, + mseq, follows, size, max_size, xattr_version, xattrs_buf ? (int)xattrs_buf->vec.iov_len : 0); - /* flock buffer size + inline version + inline data size */ - extra_len = 4 + 8 + 4; + /* flock buffer size + inline version + inline data size + + * osd_epoch_barrier + oldest_flush_tid */ + extra_len = 4 + 8 + 4 + 4 + 8; msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPS, sizeof(*fc) + extra_len, GFP_NOFS, false); if (!msg) return -ENOMEM; + msg->hdr.version = cpu_to_le16(6); msg->hdr.tid = cpu_to_le64(flush_tid); fc = msg->front.iov_base; @@ -1041,6 +1055,10 @@ static int send_cap_msg(struct ceph_mds_session *session, ceph_encode_64(&p, inline_data ? 0 : CEPH_INLINE_NONE); /* inline data size */ ceph_encode_32(&p, 0); + /* osd_epoch_barrier */ + ceph_encode_32(&p, 0); + /* oldest_flush_tid */ + ceph_encode_64(&p, oldest_flush_tid); fc->xattr_version = cpu_to_le64(xattr_version); if (xattrs_buf) { @@ -1053,44 +1071,6 @@ static int send_cap_msg(struct ceph_mds_session *session, return 0; } -void __queue_cap_release(struct ceph_mds_session *session, - u64 ino, u64 cap_id, u32 migrate_seq, - u32 issue_seq) -{ - struct ceph_msg *msg; - struct ceph_mds_cap_release *head; - struct ceph_mds_cap_item *item; - - BUG_ON(!session->s_num_cap_releases); - msg = list_first_entry(&session->s_cap_releases, - struct ceph_msg, list_head); - - dout(" adding %llx release to mds%d msg %p (%d left)\n", - ino, session->s_mds, msg, session->s_num_cap_releases); - - BUG_ON(msg->front.iov_len + sizeof(*item) > PAGE_CACHE_SIZE); - head = msg->front.iov_base; - le32_add_cpu(&head->num, 1); - item = msg->front.iov_base + msg->front.iov_len; - item->ino = cpu_to_le64(ino); - item->cap_id = cpu_to_le64(cap_id); - item->migrate_seq = cpu_to_le32(migrate_seq); - item->seq = cpu_to_le32(issue_seq); - - session->s_num_cap_releases--; - - msg->front.iov_len += sizeof(*item); - if (le32_to_cpu(head->num) == CEPH_CAPS_PER_RELEASE) { - dout(" release msg %p full\n", msg); - list_move_tail(&msg->list_head, &session->s_cap_releases_done); - } else { - dout(" release msg %p at %d/%d (%d)\n", msg, - (int)le32_to_cpu(head->num), - (int)CEPH_CAPS_PER_RELEASE, - (int)msg->front.iov_len); - } -} - /* * Queue cap releases when an inode is dropped from our cache. Since * inode is about to be destroyed, there is no need for i_ceph_lock. @@ -1127,7 +1107,7 @@ void ceph_queue_caps_release(struct inode *inode) */ static int __send_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap, int op, int used, int want, int retain, int flushing, - unsigned *pflush_tid) + u64 flush_tid, u64 oldest_flush_tid) __releases(cap->ci->i_ceph_lock) { struct ceph_inode_info *ci = cap->ci; @@ -1145,8 +1125,6 @@ static int __send_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap, u64 xattr_version = 0; struct ceph_buffer *xattr_blob = NULL; int delayed = 0; - u64 flush_tid = 0; - int i; int ret; bool inline_data; @@ -1190,26 +1168,7 @@ static int __send_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap, cap->implemented &= cap->issued | used; cap->mds_wanted = want; - if (flushing) { - /* - * assign a tid for flush operations so we can avoid - * flush1 -> dirty1 -> flush2 -> flushack1 -> mark - * clean type races. track latest tid for every bit - * so we can handle flush AxFw, flush Fw, and have the - * first ack clean Ax. - */ - flush_tid = ++ci->i_cap_flush_last_tid; - if (pflush_tid) - *pflush_tid = flush_tid; - dout(" cap_flush_tid %d\n", (int)flush_tid); - for (i = 0; i < CEPH_CAP_BITS; i++) - if (flushing & (1 << i)) - ci->i_cap_flush_tid[i] = flush_tid; - - follows = ci->i_head_snapc->seq; - } else { - follows = 0; - } + follows = flushing ? ci->i_head_snapc->seq : 0; keep = cap->implemented; seq = cap->seq; @@ -1237,7 +1196,8 @@ static int __send_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap, spin_unlock(&ci->i_ceph_lock); ret = send_cap_msg(session, ceph_vino(inode).ino, cap_id, - op, keep, want, flushing, seq, flush_tid, issue_seq, mseq, + op, keep, want, flushing, seq, + flush_tid, oldest_flush_tid, issue_seq, mseq, size, max_size, &mtime, &atime, time_warp_seq, uid, gid, mode, xattr_version, xattr_blob, follows, inline_data); @@ -1259,14 +1219,14 @@ static int __send_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap, * asynchronously back to the MDS once sync writes complete and dirty * data is written out. * - * Unless @again is true, skip cap_snaps that were already sent to + * Unless @kick is true, skip cap_snaps that were already sent to * the MDS (i.e., during this session). * * Called under i_ceph_lock. Takes s_mutex as needed. */ void __ceph_flush_snaps(struct ceph_inode_info *ci, struct ceph_mds_session **psession, - int again) + int kick) __releases(ci->i_ceph_lock) __acquires(ci->i_ceph_lock) { @@ -1297,11 +1257,8 @@ retry: if (capsnap->dirty_pages || capsnap->writing) break; - /* - * if cap writeback already occurred, we should have dropped - * the capsnap in ceph_put_wrbuffer_cap_refs. - */ - BUG_ON(capsnap->dirty == 0); + /* should be removed by ceph_try_drop_cap_snap() */ + BUG_ON(!capsnap->need_flush); /* pick mds, take s_mutex */ if (ci->i_auth_cap == NULL) { @@ -1310,7 +1267,7 @@ retry: } /* only flush each capsnap once */ - if (!again && !list_empty(&capsnap->flushing_item)) { + if (!kick && !list_empty(&capsnap->flushing_item)) { dout("already flushed %p, skipping\n", capsnap); continue; } @@ -1320,6 +1277,9 @@ retry: if (session && session->s_mds != mds) { dout("oops, wrong session %p mutex\n", session); + if (kick) + goto out; + mutex_unlock(&session->s_mutex); ceph_put_mds_session(session); session = NULL; @@ -1343,20 +1303,22 @@ retry: goto retry; } - capsnap->flush_tid = ++ci->i_cap_flush_last_tid; + spin_lock(&mdsc->cap_dirty_lock); + capsnap->flush_tid = ++mdsc->last_cap_flush_tid; + spin_unlock(&mdsc->cap_dirty_lock); + atomic_inc(&capsnap->nref); - if (!list_empty(&capsnap->flushing_item)) - list_del_init(&capsnap->flushing_item); - list_add_tail(&capsnap->flushing_item, - &session->s_cap_snaps_flushing); + if (list_empty(&capsnap->flushing_item)) + list_add_tail(&capsnap->flushing_item, + &session->s_cap_snaps_flushing); spin_unlock(&ci->i_ceph_lock); dout("flush_snaps %p cap_snap %p follows %lld tid %llu\n", inode, capsnap, capsnap->follows, capsnap->flush_tid); send_cap_msg(session, ceph_vino(inode).ino, 0, CEPH_CAP_OP_FLUSHSNAP, capsnap->issued, 0, - capsnap->dirty, 0, capsnap->flush_tid, 0, mseq, - capsnap->size, 0, + capsnap->dirty, 0, capsnap->flush_tid, 0, + 0, mseq, capsnap->size, 0, &capsnap->mtime, &capsnap->atime, capsnap->time_warp_seq, capsnap->uid, capsnap->gid, capsnap->mode, @@ -1396,7 +1358,8 @@ static void ceph_flush_snaps(struct ceph_inode_info *ci) * Caller is then responsible for calling __mark_inode_dirty with the * returned flags value. */ -int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask) +int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask, + struct ceph_cap_flush **pcf) { struct ceph_mds_client *mdsc = ceph_sb_to_client(ci->vfs_inode.i_sb)->mdsc; @@ -1416,9 +1379,14 @@ int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask) ceph_cap_string(was | mask)); ci->i_dirty_caps |= mask; if (was == 0) { - if (!ci->i_head_snapc) + WARN_ON_ONCE(ci->i_prealloc_cap_flush); + swap(ci->i_prealloc_cap_flush, *pcf); + + if (!ci->i_head_snapc) { + WARN_ON_ONCE(!rwsem_is_locked(&mdsc->snap_rwsem)); ci->i_head_snapc = ceph_get_snap_context( ci->i_snap_realm->cached_context); + } dout(" inode %p now dirty snapc %p auth cap %p\n", &ci->vfs_inode, ci->i_head_snapc, ci->i_auth_cap); BUG_ON(!list_empty(&ci->i_dirty_item)); @@ -1429,6 +1397,8 @@ int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask) ihold(inode); dirty |= I_DIRTY_SYNC; } + } else { + WARN_ON_ONCE(!ci->i_prealloc_cap_flush); } BUG_ON(list_empty(&ci->i_dirty_item)); if (((was | ci->i_flushing_caps) & CEPH_CAP_FILE_BUFFER) && @@ -1438,6 +1408,74 @@ int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask) return dirty; } +static void __add_cap_flushing_to_inode(struct ceph_inode_info *ci, + struct ceph_cap_flush *cf) +{ + struct rb_node **p = &ci->i_cap_flush_tree.rb_node; + struct rb_node *parent = NULL; + struct ceph_cap_flush *other = NULL; + + while (*p) { + parent = *p; + other = rb_entry(parent, struct ceph_cap_flush, i_node); + + if (cf->tid < other->tid) + p = &(*p)->rb_left; + else if (cf->tid > other->tid) + p = &(*p)->rb_right; + else + BUG(); + } + + rb_link_node(&cf->i_node, parent, p); + rb_insert_color(&cf->i_node, &ci->i_cap_flush_tree); +} + +static void __add_cap_flushing_to_mdsc(struct ceph_mds_client *mdsc, + struct ceph_cap_flush *cf) +{ + struct rb_node **p = &mdsc->cap_flush_tree.rb_node; + struct rb_node *parent = NULL; + struct ceph_cap_flush *other = NULL; + + while (*p) { + parent = *p; + other = rb_entry(parent, struct ceph_cap_flush, g_node); + + if (cf->tid < other->tid) + p = &(*p)->rb_left; + else if (cf->tid > other->tid) + p = &(*p)->rb_right; + else + BUG(); + } + + rb_link_node(&cf->g_node, parent, p); + rb_insert_color(&cf->g_node, &mdsc->cap_flush_tree); +} + +struct ceph_cap_flush *ceph_alloc_cap_flush(void) +{ + return kmem_cache_alloc(ceph_cap_flush_cachep, GFP_KERNEL); +} + +void ceph_free_cap_flush(struct ceph_cap_flush *cf) +{ + if (cf) + kmem_cache_free(ceph_cap_flush_cachep, cf); +} + +static u64 __get_oldest_flush_tid(struct ceph_mds_client *mdsc) +{ + struct rb_node *n = rb_first(&mdsc->cap_flush_tree); + if (n) { + struct ceph_cap_flush *cf = + rb_entry(n, struct ceph_cap_flush, g_node); + return cf->tid; + } + return 0; +} + /* * Add dirty inode to the flushing list. Assigned a seq number so we * can wait for caps to flush without starving. @@ -1445,14 +1483,17 @@ int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask) * Called under i_ceph_lock. */ static int __mark_caps_flushing(struct inode *inode, - struct ceph_mds_session *session) + struct ceph_mds_session *session, + u64 *flush_tid, u64 *oldest_flush_tid) { struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_cap_flush *cf = NULL; int flushing; BUG_ON(ci->i_dirty_caps == 0); BUG_ON(list_empty(&ci->i_dirty_item)); + BUG_ON(!ci->i_prealloc_cap_flush); flushing = ci->i_dirty_caps; dout("__mark_caps_flushing flushing %s, flushing_caps %s -> %s\n", @@ -1463,22 +1504,30 @@ static int __mark_caps_flushing(struct inode *inode, ci->i_dirty_caps = 0; dout(" inode %p now !dirty\n", inode); + swap(cf, ci->i_prealloc_cap_flush); + cf->caps = flushing; + spin_lock(&mdsc->cap_dirty_lock); list_del_init(&ci->i_dirty_item); + cf->tid = ++mdsc->last_cap_flush_tid; + __add_cap_flushing_to_mdsc(mdsc, cf); + *oldest_flush_tid = __get_oldest_flush_tid(mdsc); + if (list_empty(&ci->i_flushing_item)) { - ci->i_cap_flush_seq = ++mdsc->cap_flush_seq; list_add_tail(&ci->i_flushing_item, &session->s_cap_flushing); mdsc->num_cap_flushing++; - dout(" inode %p now flushing seq %lld\n", inode, - ci->i_cap_flush_seq); + dout(" inode %p now flushing tid %llu\n", inode, cf->tid); } else { list_move_tail(&ci->i_flushing_item, &session->s_cap_flushing); - dout(" inode %p now flushing (more) seq %lld\n", inode, - ci->i_cap_flush_seq); + dout(" inode %p now flushing (more) tid %llu\n", + inode, cf->tid); } spin_unlock(&mdsc->cap_dirty_lock); + __add_cap_flushing_to_inode(ci, cf); + + *flush_tid = cf->tid; return flushing; } @@ -1524,6 +1573,7 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags, struct ceph_mds_client *mdsc = fsc->mdsc; struct inode *inode = &ci->vfs_inode; struct ceph_cap *cap; + u64 flush_tid, oldest_flush_tid; int file_wanted, used, cap_used; int took_snap_rwsem = 0; /* true if mdsc->snap_rwsem held */ int issued, implemented, want, retain, revoking, flushing = 0; @@ -1553,13 +1603,13 @@ retry: retry_locked: file_wanted = __ceph_caps_file_wanted(ci); used = __ceph_caps_used(ci); - want = file_wanted | used; issued = __ceph_caps_issued(ci, &implemented); revoking = implemented & ~issued; - retain = want | CEPH_CAP_PIN; + want = file_wanted; + retain = file_wanted | used | CEPH_CAP_PIN; if (!mdsc->stopping && inode->i_nlink > 0) { - if (want) { + if (file_wanted) { retain |= CEPH_CAP_ANY; /* be greedy */ } else if (S_ISDIR(inode->i_mode) && (issued & CEPH_CAP_FILE_SHARED) && @@ -1602,9 +1652,10 @@ retry_locked: * If we fail, it's because pages are locked.... try again later. */ if ((!is_delayed || mdsc->stopping) && - ci->i_wrbuffer_ref == 0 && /* no dirty pages... */ - inode->i_data.nrpages && /* have cached pages */ - (file_wanted == 0 || /* no open files */ + !S_ISDIR(inode->i_mode) && /* ignore readdir cache */ + ci->i_wrbuffer_ref == 0 && /* no dirty pages... */ + inode->i_data.nrpages && /* have cached pages */ + (file_wanted == 0 || /* no open files */ (revoking & (CEPH_CAP_FILE_CACHE| CEPH_CAP_FILE_LAZYIO))) && /* or revoking cache */ !tried_invalidate) { @@ -1742,17 +1793,25 @@ ack: took_snap_rwsem = 1; } - if (cap == ci->i_auth_cap && ci->i_dirty_caps) - flushing = __mark_caps_flushing(inode, session); - else + if (cap == ci->i_auth_cap && ci->i_dirty_caps) { + flushing = __mark_caps_flushing(inode, session, + &flush_tid, + &oldest_flush_tid); + } else { flushing = 0; + flush_tid = 0; + spin_lock(&mdsc->cap_dirty_lock); + oldest_flush_tid = __get_oldest_flush_tid(mdsc); + spin_unlock(&mdsc->cap_dirty_lock); + } mds = cap->mds; /* remember mds, so we don't repeat */ sent++; /* __send_cap drops i_ceph_lock */ delayed += __send_cap(mdsc, cap, CEPH_CAP_OP_UPDATE, cap_used, - want, retain, flushing, NULL); + want, retain, flushing, + flush_tid, oldest_flush_tid); goto retry; /* retake i_ceph_lock and restart our cap scan. */ } @@ -1781,12 +1840,13 @@ ack: /* * Try to flush dirty caps back to the auth mds. */ -static int try_flush_caps(struct inode *inode, unsigned *flush_tid) +static int try_flush_caps(struct inode *inode, u64 *ptid) { struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; struct ceph_inode_info *ci = ceph_inode(inode); - int flushing = 0; struct ceph_mds_session *session = NULL; + int flushing = 0; + u64 flush_tid = 0, oldest_flush_tid = 0; retry: spin_lock(&ci->i_ceph_lock); @@ -1811,42 +1871,54 @@ retry: if (cap->session->s_state < CEPH_MDS_SESSION_OPEN) goto out; - flushing = __mark_caps_flushing(inode, session); + flushing = __mark_caps_flushing(inode, session, &flush_tid, + &oldest_flush_tid); /* __send_cap drops i_ceph_lock */ delayed = __send_cap(mdsc, cap, CEPH_CAP_OP_FLUSH, used, want, - cap->issued | cap->implemented, flushing, - flush_tid); - if (!delayed) - goto out_unlocked; + (cap->issued | cap->implemented), + flushing, flush_tid, oldest_flush_tid); - spin_lock(&ci->i_ceph_lock); - __cap_delay_requeue(mdsc, ci); + if (delayed) { + spin_lock(&ci->i_ceph_lock); + __cap_delay_requeue(mdsc, ci); + spin_unlock(&ci->i_ceph_lock); + } + } else { + struct rb_node *n = rb_last(&ci->i_cap_flush_tree); + if (n) { + struct ceph_cap_flush *cf = + rb_entry(n, struct ceph_cap_flush, i_node); + flush_tid = cf->tid; + } + flushing = ci->i_flushing_caps; + spin_unlock(&ci->i_ceph_lock); } out: - spin_unlock(&ci->i_ceph_lock); -out_unlocked: if (session) mutex_unlock(&session->s_mutex); + + *ptid = flush_tid; return flushing; } /* * Return true if we've flushed caps through the given flush_tid. */ -static int caps_are_flushed(struct inode *inode, unsigned tid) +static int caps_are_flushed(struct inode *inode, u64 flush_tid) { struct ceph_inode_info *ci = ceph_inode(inode); - int i, ret = 1; + struct ceph_cap_flush *cf; + struct rb_node *n; + int ret = 1; spin_lock(&ci->i_ceph_lock); - for (i = 0; i < CEPH_CAP_BITS; i++) - if ((ci->i_flushing_caps & (1 << i)) && - ci->i_cap_flush_tid[i] <= tid) { - /* still flushing this bit */ + n = rb_first(&ci->i_cap_flush_tree); + if (n) { + cf = rb_entry(n, struct ceph_cap_flush, i_node); + if (cf->tid <= flush_tid) ret = 0; - break; - } + } spin_unlock(&ci->i_ceph_lock); return ret; } @@ -1864,13 +1936,16 @@ static void sync_write_wait(struct inode *inode) struct ceph_osd_request *req; u64 last_tid; + if (!S_ISREG(inode->i_mode)) + return; + spin_lock(&ci->i_unsafe_lock); if (list_empty(head)) goto out; /* set upper bound as _last_ entry in chain */ - req = list_entry(head->prev, struct ceph_osd_request, - r_unsafe_item); + req = list_last_entry(head, struct ceph_osd_request, + r_unsafe_item); last_tid = req->r_tid; do { @@ -1888,18 +1963,64 @@ static void sync_write_wait(struct inode *inode) */ if (list_empty(head)) break; - req = list_entry(head->next, struct ceph_osd_request, - r_unsafe_item); + req = list_first_entry(head, struct ceph_osd_request, + r_unsafe_item); } while (req->r_tid < last_tid); out: spin_unlock(&ci->i_unsafe_lock); } +/* + * wait for any uncommitted directory operations to commit. + */ +static int unsafe_dirop_wait(struct inode *inode) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + struct list_head *head = &ci->i_unsafe_dirops; + struct ceph_mds_request *req; + u64 last_tid; + int ret = 0; + + if (!S_ISDIR(inode->i_mode)) + return 0; + + spin_lock(&ci->i_unsafe_lock); + if (list_empty(head)) + goto out; + + req = list_last_entry(head, struct ceph_mds_request, + r_unsafe_dir_item); + last_tid = req->r_tid; + + do { + ceph_mdsc_get_request(req); + spin_unlock(&ci->i_unsafe_lock); + + dout("unsafe_dirop_wait %p wait on tid %llu (until %llu)\n", + inode, req->r_tid, last_tid); + ret = !wait_for_completion_timeout(&req->r_safe_completion, + ceph_timeout_jiffies(req->r_timeout)); + if (ret) + ret = -EIO; /* timed out */ + + ceph_mdsc_put_request(req); + + spin_lock(&ci->i_unsafe_lock); + if (ret || list_empty(head)) + break; + req = list_first_entry(head, struct ceph_mds_request, + r_unsafe_dir_item); + } while (req->r_tid < last_tid); +out: + spin_unlock(&ci->i_unsafe_lock); + return ret; +} + int ceph_fsync(struct file *file, loff_t start, loff_t end, int datasync) { struct inode *inode = file->f_mapping->host; struct ceph_inode_info *ci = ceph_inode(inode); - unsigned flush_tid; + u64 flush_tid; int ret; int dirty; @@ -1908,25 +2029,30 @@ int ceph_fsync(struct file *file, loff_t start, loff_t end, int datasync) ret = filemap_write_and_wait_range(inode->i_mapping, start, end); if (ret < 0) - return ret; + goto out; + + if (datasync) + goto out; + mutex_lock(&inode->i_mutex); dirty = try_flush_caps(inode, &flush_tid); dout("fsync dirty caps are %s\n", ceph_cap_string(dirty)); + ret = unsafe_dirop_wait(inode); + /* * only wait on non-file metadata writeback (the mds * can recover size and mtime, so we don't need to * wait for that) */ - if (!datasync && (dirty & ~CEPH_CAP_ANY_FILE_WR)) { - dout("fsync waiting for flush_tid %u\n", flush_tid); + if (!ret && (dirty & ~CEPH_CAP_ANY_FILE_WR)) { ret = wait_event_interruptible(ci->i_cap_wq, - caps_are_flushed(inode, flush_tid)); + caps_are_flushed(inode, flush_tid)); } - - dout("fsync %p%s done\n", inode, datasync ? " datasync" : ""); mutex_unlock(&inode->i_mutex); +out: + dout("fsync %p%s result=%d\n", inode, datasync ? " datasync" : "", ret); return ret; } @@ -1939,7 +2065,7 @@ int ceph_fsync(struct file *file, loff_t start, loff_t end, int datasync) int ceph_write_inode(struct inode *inode, struct writeback_control *wbc) { struct ceph_inode_info *ci = ceph_inode(inode); - unsigned flush_tid; + u64 flush_tid; int err = 0; int dirty; int wait = wbc->sync_mode == WB_SYNC_ALL; @@ -1994,6 +2120,93 @@ static void kick_flushing_capsnaps(struct ceph_mds_client *mdsc, } } +static int __kick_flushing_caps(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session, + struct ceph_inode_info *ci) +{ + struct inode *inode = &ci->vfs_inode; + struct ceph_cap *cap; + struct ceph_cap_flush *cf; + struct rb_node *n; + int delayed = 0; + u64 first_tid = 0; + u64 oldest_flush_tid; + + spin_lock(&mdsc->cap_dirty_lock); + oldest_flush_tid = __get_oldest_flush_tid(mdsc); + spin_unlock(&mdsc->cap_dirty_lock); + + while (true) { + spin_lock(&ci->i_ceph_lock); + cap = ci->i_auth_cap; + if (!(cap && cap->session == session)) { + pr_err("%p auth cap %p not mds%d ???\n", inode, + cap, session->s_mds); + spin_unlock(&ci->i_ceph_lock); + break; + } + + for (n = rb_first(&ci->i_cap_flush_tree); n; n = rb_next(n)) { + cf = rb_entry(n, struct ceph_cap_flush, i_node); + if (cf->tid >= first_tid) + break; + } + if (!n) { + spin_unlock(&ci->i_ceph_lock); + break; + } + + cf = rb_entry(n, struct ceph_cap_flush, i_node); + + first_tid = cf->tid + 1; + + dout("kick_flushing_caps %p cap %p tid %llu %s\n", inode, + cap, cf->tid, ceph_cap_string(cf->caps)); + delayed |= __send_cap(mdsc, cap, CEPH_CAP_OP_FLUSH, + __ceph_caps_used(ci), + __ceph_caps_wanted(ci), + cap->issued | cap->implemented, + cf->caps, cf->tid, oldest_flush_tid); + } + return delayed; +} + +void ceph_early_kick_flushing_caps(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session) +{ + struct ceph_inode_info *ci; + struct ceph_cap *cap; + + dout("early_kick_flushing_caps mds%d\n", session->s_mds); + list_for_each_entry(ci, &session->s_cap_flushing, i_flushing_item) { + spin_lock(&ci->i_ceph_lock); + cap = ci->i_auth_cap; + if (!(cap && cap->session == session)) { + pr_err("%p auth cap %p not mds%d ???\n", + &ci->vfs_inode, cap, session->s_mds); + spin_unlock(&ci->i_ceph_lock); + continue; + } + + + /* + * if flushing caps were revoked, we re-send the cap flush + * in client reconnect stage. This guarantees MDS * processes + * the cap flush message before issuing the flushing caps to + * other client. + */ + if ((cap->issued & ci->i_flushing_caps) != + ci->i_flushing_caps) { + spin_unlock(&ci->i_ceph_lock); + if (!__kick_flushing_caps(mdsc, session, ci)) + continue; + spin_lock(&ci->i_ceph_lock); + } + + spin_unlock(&ci->i_ceph_lock); + } +} + void ceph_kick_flushing_caps(struct ceph_mds_client *mdsc, struct ceph_mds_session *session) { @@ -2003,28 +2216,10 @@ void ceph_kick_flushing_caps(struct ceph_mds_client *mdsc, dout("kick_flushing_caps mds%d\n", session->s_mds); list_for_each_entry(ci, &session->s_cap_flushing, i_flushing_item) { - struct inode *inode = &ci->vfs_inode; - struct ceph_cap *cap; - int delayed = 0; - - spin_lock(&ci->i_ceph_lock); - cap = ci->i_auth_cap; - if (cap && cap->session == session) { - dout("kick_flushing_caps %p cap %p %s\n", inode, - cap, ceph_cap_string(ci->i_flushing_caps)); - delayed = __send_cap(mdsc, cap, CEPH_CAP_OP_FLUSH, - __ceph_caps_used(ci), - __ceph_caps_wanted(ci), - cap->issued | cap->implemented, - ci->i_flushing_caps, NULL); - if (delayed) { - spin_lock(&ci->i_ceph_lock); - __cap_delay_requeue(mdsc, ci); - spin_unlock(&ci->i_ceph_lock); - } - } else { - pr_err("%p auth cap %p not mds%d ???\n", inode, - cap, session->s_mds); + int delayed = __kick_flushing_caps(mdsc, session, ci); + if (delayed) { + spin_lock(&ci->i_ceph_lock); + __cap_delay_requeue(mdsc, ci); spin_unlock(&ci->i_ceph_lock); } } @@ -2036,26 +2231,25 @@ static void kick_flushing_inode_caps(struct ceph_mds_client *mdsc, { struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_cap *cap; - int delayed = 0; spin_lock(&ci->i_ceph_lock); cap = ci->i_auth_cap; - dout("kick_flushing_inode_caps %p flushing %s flush_seq %lld\n", inode, - ceph_cap_string(ci->i_flushing_caps), ci->i_cap_flush_seq); + dout("kick_flushing_inode_caps %p flushing %s\n", inode, + ceph_cap_string(ci->i_flushing_caps)); __ceph_flush_snaps(ci, &session, 1); if (ci->i_flushing_caps) { + int delayed; + spin_lock(&mdsc->cap_dirty_lock); list_move_tail(&ci->i_flushing_item, &cap->session->s_cap_flushing); spin_unlock(&mdsc->cap_dirty_lock); - delayed = __send_cap(mdsc, cap, CEPH_CAP_OP_FLUSH, - __ceph_caps_used(ci), - __ceph_caps_wanted(ci), - cap->issued | cap->implemented, - ci->i_flushing_caps, NULL); + spin_unlock(&ci->i_ceph_lock); + + delayed = __kick_flushing_caps(mdsc, session, ci); if (delayed) { spin_lock(&ci->i_ceph_lock); __cap_delay_requeue(mdsc, ci); @@ -2073,7 +2267,8 @@ static void kick_flushing_inode_caps(struct ceph_mds_client *mdsc, * * Protected by i_ceph_lock. */ -static void __take_cap_refs(struct ceph_inode_info *ci, int got) +static void __take_cap_refs(struct ceph_inode_info *ci, int got, + bool snap_rwsem_locked) { if (got & CEPH_CAP_PIN) ci->i_pin_ref++; @@ -2081,8 +2276,14 @@ static void __take_cap_refs(struct ceph_inode_info *ci, int got) ci->i_rd_ref++; if (got & CEPH_CAP_FILE_CACHE) ci->i_rdcache_ref++; - if (got & CEPH_CAP_FILE_WR) + if (got & CEPH_CAP_FILE_WR) { + if (ci->i_wr_ref == 0 && !ci->i_head_snapc) { + BUG_ON(!snap_rwsem_locked); + ci->i_head_snapc = ceph_get_snap_context( + ci->i_snap_realm->cached_context); + } ci->i_wr_ref++; + } if (got & CEPH_CAP_FILE_BUFFER) { if (ci->i_wb_ref == 0) ihold(&ci->vfs_inode); @@ -2100,16 +2301,19 @@ static void __take_cap_refs(struct ceph_inode_info *ci, int got) * requested from the MDS. */ static int try_get_cap_refs(struct ceph_inode_info *ci, int need, int want, - loff_t endoff, int *got, int *check_max, int *err) + loff_t endoff, bool nonblock, int *got, int *err) { struct inode *inode = &ci->vfs_inode; + struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; int ret = 0; int have, implemented; int file_wanted; + bool snap_rwsem_locked = false; dout("get_cap_refs %p need %s want %s\n", inode, ceph_cap_string(need), ceph_cap_string(want)); +again: spin_lock(&ci->i_ceph_lock); /* make sure file is actually open */ @@ -2125,6 +2329,10 @@ static int try_get_cap_refs(struct ceph_inode_info *ci, int need, int want, /* finish pending truncate */ while (ci->i_truncate_pending) { spin_unlock(&ci->i_ceph_lock); + if (snap_rwsem_locked) { + up_read(&mdsc->snap_rwsem); + snap_rwsem_locked = false; + } __ceph_do_pending_vmtruncate(inode); spin_lock(&ci->i_ceph_lock); } @@ -2136,7 +2344,7 @@ static int try_get_cap_refs(struct ceph_inode_info *ci, int need, int want, dout("get_cap_refs %p endoff %llu > maxsize %llu\n", inode, endoff, ci->i_max_size); if (endoff > ci->i_requested_max_size) { - *check_max = 1; + *err = -EAGAIN; ret = 1; } goto out_unlock; @@ -2164,8 +2372,29 @@ static int try_get_cap_refs(struct ceph_inode_info *ci, int need, int want, inode, ceph_cap_string(have), ceph_cap_string(not), ceph_cap_string(revoking)); if ((revoking & not) == 0) { + if (!snap_rwsem_locked && + !ci->i_head_snapc && + (need & CEPH_CAP_FILE_WR)) { + if (!down_read_trylock(&mdsc->snap_rwsem)) { + /* + * we can not call down_read() when + * task isn't in TASK_RUNNING state + */ + if (nonblock) { + *err = -EAGAIN; + ret = 1; + goto out_unlock; + } + + spin_unlock(&ci->i_ceph_lock); + down_read(&mdsc->snap_rwsem); + snap_rwsem_locked = true; + goto again; + } + snap_rwsem_locked = true; + } *got = need | (have & want); - __take_cap_refs(ci, *got); + __take_cap_refs(ci, *got, true); ret = 1; } } else { @@ -2189,6 +2418,8 @@ static int try_get_cap_refs(struct ceph_inode_info *ci, int need, int want, } out_unlock: spin_unlock(&ci->i_ceph_lock); + if (snap_rwsem_locked) + up_read(&mdsc->snap_rwsem); dout("get_cap_refs %p ret %d got %s\n", inode, ret, ceph_cap_string(*got)); @@ -2231,50 +2462,70 @@ static void check_max_size(struct inode *inode, loff_t endoff) int ceph_get_caps(struct ceph_inode_info *ci, int need, int want, loff_t endoff, int *got, struct page **pinned_page) { - int _got, check_max, ret, err = 0; + int _got, ret, err = 0; -retry: - if (endoff > 0) - check_max_size(&ci->vfs_inode, endoff); - _got = 0; - check_max = 0; - ret = wait_event_interruptible(ci->i_cap_wq, - try_get_cap_refs(ci, need, want, endoff, - &_got, &check_max, &err)); - if (err) - ret = err; + ret = ceph_pool_perm_check(ci, need); if (ret < 0) return ret; - if (check_max) - goto retry; + while (true) { + if (endoff > 0) + check_max_size(&ci->vfs_inode, endoff); - if (ci->i_inline_version != CEPH_INLINE_NONE && - (_got & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) && - i_size_read(&ci->vfs_inode) > 0) { - struct page *page = find_get_page(ci->vfs_inode.i_mapping, 0); - if (page) { - if (PageUptodate(page)) { - *pinned_page = page; - goto out; - } - page_cache_release(page); - } - /* - * drop cap refs first because getattr while holding - * caps refs can cause deadlock. - */ - ceph_put_cap_refs(ci, _got); + err = 0; _got = 0; + ret = try_get_cap_refs(ci, need, want, endoff, + false, &_got, &err); + if (ret) { + if (err == -EAGAIN) + continue; + if (err < 0) + return err; + } else { + ret = wait_event_interruptible(ci->i_cap_wq, + try_get_cap_refs(ci, need, want, endoff, + true, &_got, &err)); + if (err == -EAGAIN) + continue; + if (err < 0) + ret = err; + if (ret < 0) + return ret; + } - /* getattr request will bring inline data into page cache */ - ret = __ceph_do_getattr(&ci->vfs_inode, NULL, - CEPH_STAT_CAP_INLINE_DATA, true); - if (ret < 0) - return ret; - goto retry; + if (ci->i_inline_version != CEPH_INLINE_NONE && + (_got & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) && + i_size_read(&ci->vfs_inode) > 0) { + struct page *page = + find_get_page(ci->vfs_inode.i_mapping, 0); + if (page) { + if (PageUptodate(page)) { + *pinned_page = page; + break; + } + page_cache_release(page); + } + /* + * drop cap refs first because getattr while + * holding * caps refs can cause deadlock. + */ + ceph_put_cap_refs(ci, _got); + _got = 0; + + /* + * getattr request will bring inline data into + * page cache + */ + ret = __ceph_do_getattr(&ci->vfs_inode, NULL, + CEPH_STAT_CAP_INLINE_DATA, + true); + if (ret < 0) + return ret; + continue; + } + break; } -out: + *got = _got; return 0; } @@ -2286,10 +2537,31 @@ out: void ceph_get_cap_refs(struct ceph_inode_info *ci, int caps) { spin_lock(&ci->i_ceph_lock); - __take_cap_refs(ci, caps); + __take_cap_refs(ci, caps, false); spin_unlock(&ci->i_ceph_lock); } + +/* + * drop cap_snap that is not associated with any snapshot. + * we don't need to send FLUSHSNAP message for it. + */ +static int ceph_try_drop_cap_snap(struct ceph_cap_snap *capsnap) +{ + if (!capsnap->need_flush && + !capsnap->writing && !capsnap->dirty_pages) { + + dout("dropping cap_snap %p follows %llu\n", + capsnap, capsnap->follows); + ceph_put_snap_context(capsnap->context); + list_del(&capsnap->ci_item); + list_del(&capsnap->flushing_item); + ceph_put_cap_snap(capsnap); + return 1; + } + return 0; +} + /* * Release cap refs. * @@ -2303,7 +2575,6 @@ void ceph_put_cap_refs(struct ceph_inode_info *ci, int had) { struct inode *inode = &ci->vfs_inode; int last = 0, put = 0, flushsnaps = 0, wake = 0; - struct ceph_cap_snap *capsnap; spin_lock(&ci->i_ceph_lock); if (had & CEPH_CAP_PIN) @@ -2325,17 +2596,24 @@ void ceph_put_cap_refs(struct ceph_inode_info *ci, int had) if (had & CEPH_CAP_FILE_WR) if (--ci->i_wr_ref == 0) { last++; - if (!list_empty(&ci->i_cap_snaps)) { - capsnap = list_first_entry(&ci->i_cap_snaps, - struct ceph_cap_snap, - ci_item); - if (capsnap->writing) { - capsnap->writing = 0; - flushsnaps = - __ceph_finish_cap_snap(ci, - capsnap); - wake = 1; - } + if (__ceph_have_pending_cap_snap(ci)) { + struct ceph_cap_snap *capsnap = + list_last_entry(&ci->i_cap_snaps, + struct ceph_cap_snap, + ci_item); + capsnap->writing = 0; + if (ceph_try_drop_cap_snap(capsnap)) + put++; + else if (__ceph_finish_cap_snap(ci, capsnap)) + flushsnaps = 1; + wake = 1; + } + if (ci->i_wrbuffer_ref_head == 0 && + ci->i_dirty_caps == 0 && + ci->i_flushing_caps == 0) { + BUG_ON(!ci->i_head_snapc); + ceph_put_snap_context(ci->i_head_snapc); + ci->i_head_snapc = NULL; } /* see comment in __ceph_remove_cap() */ if (!__ceph_is_any_caps(ci) && ci->i_snap_realm) @@ -2352,7 +2630,7 @@ void ceph_put_cap_refs(struct ceph_inode_info *ci, int had) ceph_flush_snaps(ci); if (wake) wake_up_all(&ci->i_cap_wq); - if (put) + while (put-- > 0) iput(inode); } @@ -2380,7 +2658,9 @@ void ceph_put_wrbuffer_cap_refs(struct ceph_inode_info *ci, int nr, if (ci->i_head_snapc == snapc) { ci->i_wrbuffer_ref_head -= nr; if (ci->i_wrbuffer_ref_head == 0 && - ci->i_dirty_caps == 0 && ci->i_flushing_caps == 0) { + ci->i_wr_ref == 0 && + ci->i_dirty_caps == 0 && + ci->i_flushing_caps == 0) { BUG_ON(!ci->i_head_snapc); ceph_put_snap_context(ci->i_head_snapc); ci->i_head_snapc = NULL; @@ -2401,25 +2681,15 @@ void ceph_put_wrbuffer_cap_refs(struct ceph_inode_info *ci, int nr, capsnap->dirty_pages -= nr; if (capsnap->dirty_pages == 0) { complete_capsnap = 1; - if (capsnap->dirty == 0) - /* cap writeback completed before we created - * the cap_snap; no FLUSHSNAP is needed */ - drop_capsnap = 1; + drop_capsnap = ceph_try_drop_cap_snap(capsnap); } dout("put_wrbuffer_cap_refs on %p cap_snap %p " - " snap %lld %d/%d -> %d/%d %s%s%s\n", + " snap %lld %d/%d -> %d/%d %s%s\n", inode, capsnap, capsnap->context->seq, ci->i_wrbuffer_ref+nr, capsnap->dirty_pages + nr, ci->i_wrbuffer_ref, capsnap->dirty_pages, last ? " (wrbuffer last)" : "", - complete_capsnap ? " (complete capsnap)" : "", - drop_capsnap ? " (drop capsnap)" : ""); - if (drop_capsnap) { - ceph_put_snap_context(capsnap->context); - list_del(&capsnap->ci_item); - list_del(&capsnap->flushing_item); - ceph_put_cap_snap(capsnap); - } + complete_capsnap ? " (complete capsnap)" : ""); } spin_unlock(&ci->i_ceph_lock); @@ -2526,7 +2796,8 @@ static void handle_cap_grant(struct ceph_mds_client *mdsc, * try to invalidate (once). (If there are dirty buffers, we * will invalidate _after_ writeback.) */ - if (((cap->issued & ~newcaps) & CEPH_CAP_FILE_CACHE) && + if (!S_ISDIR(inode->i_mode) && /* don't invalidate readdir cache */ + ((cap->issued & ~newcaps) & CEPH_CAP_FILE_CACHE) && (newcaps & CEPH_CAP_FILE_LAZYIO) == 0 && !ci->i_wrbuffer_ref) { if (try_nonblocking_invalidate(inode)) { @@ -2732,16 +3003,29 @@ static void handle_cap_flush_ack(struct inode *inode, u64 flush_tid, { struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; + struct ceph_cap_flush *cf; + struct rb_node *n; + LIST_HEAD(to_remove); unsigned seq = le32_to_cpu(m->seq); int dirty = le32_to_cpu(m->dirty); int cleaned = 0; int drop = 0; - int i; - for (i = 0; i < CEPH_CAP_BITS; i++) - if ((dirty & (1 << i)) && - (u16)flush_tid == ci->i_cap_flush_tid[i]) - cleaned |= 1 << i; + n = rb_first(&ci->i_cap_flush_tree); + while (n) { + cf = rb_entry(n, struct ceph_cap_flush, i_node); + n = rb_next(&cf->i_node); + if (cf->tid == flush_tid) + cleaned = cf->caps; + if (cf->tid <= flush_tid) { + rb_erase(&cf->i_node, &ci->i_cap_flush_tree); + list_add_tail(&cf->list, &to_remove); + } else { + cleaned &= ~cf->caps; + if (!cleaned) + break; + } + } dout("handle_cap_flush_ack inode %p mds%d seq %d on %s cleaned %s," " flushing %s -> %s\n", @@ -2749,12 +3033,23 @@ static void handle_cap_flush_ack(struct inode *inode, u64 flush_tid, ceph_cap_string(cleaned), ceph_cap_string(ci->i_flushing_caps), ceph_cap_string(ci->i_flushing_caps & ~cleaned)); - if (ci->i_flushing_caps == (ci->i_flushing_caps & ~cleaned)) + if (list_empty(&to_remove) && !cleaned) goto out; ci->i_flushing_caps &= ~cleaned; spin_lock(&mdsc->cap_dirty_lock); + + if (!list_empty(&to_remove)) { + list_for_each_entry(cf, &to_remove, list) + rb_erase(&cf->g_node, &mdsc->cap_flush_tree); + + n = rb_first(&mdsc->cap_flush_tree); + cf = n ? rb_entry(n, struct ceph_cap_flush, g_node) : NULL; + if (!cf || cf->tid > flush_tid) + wake_up_all(&mdsc->cap_flushing_wq); + } + if (ci->i_flushing_caps == 0) { list_del_init(&ci->i_flushing_item); if (!list_empty(&session->s_cap_flushing)) @@ -2764,14 +3059,14 @@ static void handle_cap_flush_ack(struct inode *inode, u64 flush_tid, struct ceph_inode_info, i_flushing_item)->vfs_inode); mdsc->num_cap_flushing--; - wake_up_all(&mdsc->cap_flushing_wq); dout(" inode %p now !flushing\n", inode); if (ci->i_dirty_caps == 0) { dout(" inode %p now clean\n", inode); BUG_ON(!list_empty(&ci->i_dirty_item)); drop = 1; - if (ci->i_wrbuffer_ref_head == 0) { + if (ci->i_wr_ref == 0 && + ci->i_wrbuffer_ref_head == 0) { BUG_ON(!ci->i_head_snapc); ceph_put_snap_context(ci->i_head_snapc); ci->i_head_snapc = NULL; @@ -2785,6 +3080,13 @@ static void handle_cap_flush_ack(struct inode *inode, u64 flush_tid, out: spin_unlock(&ci->i_ceph_lock); + + while (!list_empty(&to_remove)) { + cf = list_first_entry(&to_remove, + struct ceph_cap_flush, list); + list_del(&cf->list); + ceph_free_cap_flush(cf); + } if (drop) iput(inode); } @@ -2800,6 +3102,7 @@ static void handle_cap_flushsnap_ack(struct inode *inode, u64 flush_tid, struct ceph_mds_session *session) { struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; u64 follows = le64_to_cpu(m->snap_follows); struct ceph_cap_snap *capsnap; int drop = 0; @@ -2823,6 +3126,7 @@ static void handle_cap_flushsnap_ack(struct inode *inode, u64 flush_tid, list_del(&capsnap->ci_item); list_del(&capsnap->flushing_item); ceph_put_cap_snap(capsnap); + wake_up_all(&mdsc->cap_flushing_wq); drop = 1; break; } else { @@ -2971,7 +3275,6 @@ retry: mutex_lock_nested(&session->s_mutex, SINGLE_DEPTH_NESTING); } - ceph_add_cap_releases(mdsc, tsession); new_cap = ceph_get_cap(mdsc, NULL); } else { WARN_ON(1); @@ -3167,16 +3470,20 @@ void ceph_handle_caps(struct ceph_mds_session *session, dout(" mds%d seq %lld cap seq %u\n", session->s_mds, session->s_seq, (unsigned)seq); - if (op == CEPH_CAP_OP_IMPORT) - ceph_add_cap_releases(mdsc, session); - if (!inode) { dout(" i don't have ino %llx\n", vino.ino); if (op == CEPH_CAP_OP_IMPORT) { + cap = ceph_get_cap(mdsc, NULL); + cap->cap_ino = vino.ino; + cap->queue_release = 1; + cap->cap_id = cap_id; + cap->mseq = mseq; + cap->seq = seq; spin_lock(&session->s_cap_lock); - __queue_cap_release(session, vino.ino, cap_id, - mseq, seq); + list_add_tail(&cap->session_caps, + &session->s_cap_releases); + session->s_num_cap_releases++; spin_unlock(&session->s_cap_lock); } goto flush_cap_releases; @@ -3252,11 +3559,10 @@ void ceph_handle_caps(struct ceph_mds_session *session, flush_cap_releases: /* - * send any full release message to try to move things + * send any cap release message to try to move things * along for the mds (who clearly thinks we still have this * cap). */ - ceph_add_cap_releases(mdsc, session); ceph_send_cap_releases(mdsc, session); done: diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 4248307fe..9314b4ea2 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -38,7 +38,7 @@ int ceph_init_dentry(struct dentry *dentry) if (dentry->d_fsdata) return 0; - di = kmem_cache_alloc(ceph_dentry_cachep, GFP_NOFS | __GFP_ZERO); + di = kmem_cache_alloc(ceph_dentry_cachep, GFP_KERNEL | __GFP_ZERO); if (!di) return -ENOMEM; /* oh well */ @@ -107,6 +107,27 @@ static int fpos_cmp(loff_t l, loff_t r) } /* + * make note of the last dentry we read, so we can + * continue at the same lexicographical point, + * regardless of what dir changes take place on the + * server. + */ +static int note_last_dentry(struct ceph_file_info *fi, const char *name, + int len, unsigned next_offset) +{ + char *buf = kmalloc(len+1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + kfree(fi->last_name); + fi->last_name = buf; + memcpy(fi->last_name, name, len); + fi->last_name[len] = 0; + fi->next_offset = next_offset; + dout("note_last_dentry '%s'\n", fi->last_name); + return 0; +} + +/* * When possible, we try to satisfy a readdir by peeking at the * dcache. We make this work by carefully ordering dentries on * d_child when we initially get results back from the MDS, and @@ -123,123 +144,113 @@ static int __dcache_readdir(struct file *file, struct dir_context *ctx, struct ceph_file_info *fi = file->private_data; struct dentry *parent = file->f_path.dentry; struct inode *dir = d_inode(parent); - struct list_head *p; - struct dentry *dentry, *last; + struct dentry *dentry, *last = NULL; struct ceph_dentry_info *di; + unsigned nsize = PAGE_CACHE_SIZE / sizeof(struct dentry *); int err = 0; + loff_t ptr_pos = 0; + struct ceph_readdir_cache_control cache_ctl = {}; - /* claim ref on last dentry we returned */ - last = fi->dentry; - fi->dentry = NULL; - - dout("__dcache_readdir %p v%u at %llu (last %p)\n", - dir, shared_gen, ctx->pos, last); + dout("__dcache_readdir %p v%u at %llu\n", dir, shared_gen, ctx->pos); - spin_lock(&parent->d_lock); - - /* start at beginning? */ - if (ctx->pos == 2 || last == NULL || - fpos_cmp(ctx->pos, ceph_dentry(last)->offset) < 0) { - if (list_empty(&parent->d_subdirs)) - goto out_unlock; - p = parent->d_subdirs.prev; - dout(" initial p %p/%p\n", p->prev, p->next); - } else { - p = last->d_child.prev; + /* we can calculate cache index for the first dirfrag */ + if (ceph_frag_is_leftmost(fpos_frag(ctx->pos))) { + cache_ctl.index = fpos_off(ctx->pos) - 2; + BUG_ON(cache_ctl.index < 0); + ptr_pos = cache_ctl.index * sizeof(struct dentry *); } -more: - dentry = list_entry(p, struct dentry, d_child); - di = ceph_dentry(dentry); - while (1) { - dout(" p %p/%p %s d_subdirs %p/%p\n", p->prev, p->next, - d_unhashed(dentry) ? "!hashed" : "hashed", - parent->d_subdirs.prev, parent->d_subdirs.next); - if (p == &parent->d_subdirs) { + while (true) { + pgoff_t pgoff; + bool emit_dentry; + + if (ptr_pos >= i_size_read(dir)) { fi->flags |= CEPH_F_ATEND; - goto out_unlock; + err = 0; + break; + } + + err = -EAGAIN; + pgoff = ptr_pos >> PAGE_CACHE_SHIFT; + if (!cache_ctl.page || pgoff != page_index(cache_ctl.page)) { + ceph_readdir_cache_release(&cache_ctl); + cache_ctl.page = find_lock_page(&dir->i_data, pgoff); + if (!cache_ctl.page) { + dout(" page %lu not found\n", pgoff); + break; + } + /* reading/filling the cache are serialized by + * i_mutex, no need to use page lock */ + unlock_page(cache_ctl.page); + cache_ctl.dentries = kmap(cache_ctl.page); } - spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); + + rcu_read_lock(); + spin_lock(&parent->d_lock); + /* check i_size again here, because empty directory can be + * marked as complete while not holding the i_mutex. */ + if (ceph_dir_is_complete_ordered(dir) && + ptr_pos < i_size_read(dir)) + dentry = cache_ctl.dentries[cache_ctl.index % nsize]; + else + dentry = NULL; + spin_unlock(&parent->d_lock); + if (dentry && !lockref_get_not_dead(&dentry->d_lockref)) + dentry = NULL; + rcu_read_unlock(); + if (!dentry) + break; + + emit_dentry = false; + di = ceph_dentry(dentry); + spin_lock(&dentry->d_lock); if (di->lease_shared_gen == shared_gen && - !d_unhashed(dentry) && d_really_is_positive(dentry) && + d_really_is_positive(dentry) && ceph_snap(d_inode(dentry)) != CEPH_SNAPDIR && ceph_ino(d_inode(dentry)) != CEPH_INO_CEPH && - fpos_cmp(ctx->pos, di->offset) <= 0) - break; - dout(" skipping %p %pd at %llu (%llu)%s%s\n", dentry, - dentry, di->offset, - ctx->pos, d_unhashed(dentry) ? " unhashed" : "", - !d_inode(dentry) ? " null" : ""); + fpos_cmp(ctx->pos, di->offset) <= 0) { + emit_dentry = true; + } spin_unlock(&dentry->d_lock); - p = p->prev; - dentry = list_entry(p, struct dentry, d_child); - di = ceph_dentry(dentry); - } - - dget_dlock(dentry); - spin_unlock(&dentry->d_lock); - spin_unlock(&parent->d_lock); - /* make sure a dentry wasn't dropped while we didn't have parent lock */ - if (!ceph_dir_is_complete_ordered(dir)) { - dout(" lost dir complete on %p; falling back to mds\n", dir); - dput(dentry); - err = -EAGAIN; - goto out; - } + if (emit_dentry) { + dout(" %llu (%llu) dentry %p %pd %p\n", di->offset, ctx->pos, + dentry, dentry, d_inode(dentry)); + ctx->pos = di->offset; + if (!dir_emit(ctx, dentry->d_name.name, + dentry->d_name.len, + ceph_translate_ino(dentry->d_sb, + d_inode(dentry)->i_ino), + d_inode(dentry)->i_mode >> 12)) { + dput(dentry); + err = 0; + break; + } + ctx->pos++; - dout(" %llu (%llu) dentry %p %pd %p\n", di->offset, ctx->pos, - dentry, dentry, d_inode(dentry)); - if (!dir_emit(ctx, dentry->d_name.name, - dentry->d_name.len, - ceph_translate_ino(dentry->d_sb, d_inode(dentry)->i_ino), - d_inode(dentry)->i_mode >> 12)) { - if (last) { - /* remember our position */ - fi->dentry = last; - fi->next_offset = fpos_off(di->offset); + if (last) + dput(last); + last = dentry; + } else { + dput(dentry); } - dput(dentry); - return 0; - } - - ctx->pos = di->offset + 1; - if (last) - dput(last); - last = dentry; - - spin_lock(&parent->d_lock); - p = p->prev; /* advance to next dentry */ - goto more; - -out_unlock: - spin_unlock(&parent->d_lock); -out: - if (last) + cache_ctl.index++; + ptr_pos += sizeof(struct dentry *); + } + ceph_readdir_cache_release(&cache_ctl); + if (last) { + int ret; + di = ceph_dentry(last); + ret = note_last_dentry(fi, last->d_name.name, last->d_name.len, + fpos_off(di->offset) + 1); + if (ret < 0) + err = ret; dput(last); + } return err; } -/* - * make note of the last dentry we read, so we can - * continue at the same lexicographical point, - * regardless of what dir changes take place on the - * server. - */ -static int note_last_dentry(struct ceph_file_info *fi, const char *name, - int len) -{ - kfree(fi->last_name); - fi->last_name = kmalloc(len+1, GFP_NOFS); - if (!fi->last_name) - return -ENOMEM; - memcpy(fi->last_name, name, len); - fi->last_name[len] = 0; - dout("note_last_dentry '%s'\n", fi->last_name); - return 0; -} - static int ceph_readdir(struct file *file, struct dir_context *ctx) { struct ceph_file_info *fi = file->private_data; @@ -280,8 +291,7 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx) /* can we use the dcache? */ spin_lock(&ci->i_ceph_lock); - if ((ctx->pos == 2 || fi->dentry) && - ceph_test_mount_opt(fsc, DCACHE) && + if (ceph_test_mount_opt(fsc, DCACHE) && !ceph_test_mount_opt(fsc, NOASYNCREADDIR) && ceph_snap(inode) != CEPH_SNAPDIR && __ceph_dir_is_complete_ordered(ci) && @@ -296,24 +306,8 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx) } else { spin_unlock(&ci->i_ceph_lock); } - if (fi->dentry) { - err = note_last_dentry(fi, fi->dentry->d_name.name, - fi->dentry->d_name.len); - if (err) - return err; - dput(fi->dentry); - fi->dentry = NULL; - } /* proceed with a normal readdir */ - - if (ctx->pos == 2) { - /* note dir version at start of readdir so we can tell - * if any dentries get dropped */ - fi->dir_release_count = atomic_read(&ci->i_release_count); - fi->dir_ordered_count = ci->i_ordered_count; - } - more: /* do we have the correct frag content buffered? */ if (fi->frag != frag || fi->last_readdir == NULL) { @@ -342,12 +336,15 @@ more: req->r_direct_hash = ceph_frag_value(frag); req->r_direct_is_hash = true; if (fi->last_name) { - req->r_path2 = kstrdup(fi->last_name, GFP_NOFS); + req->r_path2 = kstrdup(fi->last_name, GFP_KERNEL); if (!req->r_path2) { ceph_mdsc_put_request(req); return -ENOMEM; } } + req->r_dir_release_cnt = fi->dir_release_count; + req->r_dir_ordered_cnt = fi->dir_ordered_count; + req->r_readdir_cache_idx = fi->readdir_cache_idx; req->r_readdir_offset = fi->next_offset; req->r_args.readdir.frag = cpu_to_le32(frag); @@ -364,26 +361,38 @@ more: (int)req->r_reply_info.dir_end, (int)req->r_reply_info.dir_complete); - if (!req->r_did_prepopulate) { - dout("readdir !did_prepopulate"); - /* preclude from marking dir complete */ - fi->dir_release_count--; - } /* note next offset and last dentry name */ rinfo = &req->r_reply_info; if (le32_to_cpu(rinfo->dir_dir->frag) != frag) { frag = le32_to_cpu(rinfo->dir_dir->frag); - if (ceph_frag_is_leftmost(frag)) - fi->next_offset = 2; - else - fi->next_offset = 0; - off = fi->next_offset; + off = req->r_readdir_offset; + fi->next_offset = off; } + fi->frag = frag; fi->offset = fi->next_offset; fi->last_readdir = req; + if (req->r_did_prepopulate) { + fi->readdir_cache_idx = req->r_readdir_cache_idx; + if (fi->readdir_cache_idx < 0) { + /* preclude from marking dir ordered */ + fi->dir_ordered_count = 0; + } else if (ceph_frag_is_leftmost(frag) && off == 2) { + /* note dir version at start of readdir so + * we can tell if any dentries get dropped */ + fi->dir_release_count = req->r_dir_release_cnt; + fi->dir_ordered_count = req->r_dir_ordered_cnt; + } + } else { + dout("readdir !did_prepopulate"); + /* disable readdir cache */ + fi->readdir_cache_idx = -1; + /* preclude from marking dir complete */ + fi->dir_release_count = 0; + } + if (req->r_reply_info.dir_end) { kfree(fi->last_name); fi->last_name = NULL; @@ -394,10 +403,10 @@ more: } else { err = note_last_dentry(fi, rinfo->dir_dname[rinfo->dir_nr-1], - rinfo->dir_dname_len[rinfo->dir_nr-1]); + rinfo->dir_dname_len[rinfo->dir_nr-1], + fi->next_offset + rinfo->dir_nr); if (err) return err; - fi->next_offset += rinfo->dir_nr; } } @@ -453,16 +462,22 @@ more: * were released during the whole readdir, and we should have * the complete dir contents in our cache. */ - spin_lock(&ci->i_ceph_lock); - if (atomic_read(&ci->i_release_count) == fi->dir_release_count) { - if (ci->i_ordered_count == fi->dir_ordered_count) + if (atomic64_read(&ci->i_release_count) == fi->dir_release_count) { + spin_lock(&ci->i_ceph_lock); + if (fi->dir_ordered_count == atomic64_read(&ci->i_ordered_count)) { dout(" marking %p complete and ordered\n", inode); - else + /* use i_size to track number of entries in + * readdir cache */ + BUG_ON(fi->readdir_cache_idx < 0); + i_size_write(inode, fi->readdir_cache_idx * + sizeof(struct dentry*)); + } else { dout(" marking %p complete\n", inode); + } __ceph_dir_set_complete(ci, fi->dir_release_count, fi->dir_ordered_count); + spin_unlock(&ci->i_ceph_lock); } - spin_unlock(&ci->i_ceph_lock); dout("readdir %p file %p done.\n", inode, file); return 0; @@ -476,14 +491,12 @@ static void reset_readdir(struct ceph_file_info *fi, unsigned frag) } kfree(fi->last_name); fi->last_name = NULL; + fi->dir_release_count = 0; + fi->readdir_cache_idx = -1; if (ceph_frag_is_leftmost(frag)) fi->next_offset = 2; /* compensate for . and .. */ else fi->next_offset = 0; - if (fi->dentry) { - dput(fi->dentry); - fi->dentry = NULL; - } fi->flags &= ~CEPH_F_ATEND; } @@ -497,13 +510,12 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int whence) mutex_lock(&inode->i_mutex); retval = -EINVAL; switch (whence) { - case SEEK_END: - offset += inode->i_size + 2; /* FIXME */ - break; case SEEK_CUR: offset += file->f_pos; case SEEK_SET: break; + case SEEK_END: + retval = -EOPNOTSUPP; default: goto out; } @@ -516,20 +528,18 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int whence) } retval = offset; - /* - * discard buffered readdir content on seekdir(0), or - * seek to new frag, or seek prior to current chunk. - */ if (offset == 0 || fpos_frag(offset) != fi->frag || fpos_off(offset) < fi->offset) { + /* discard buffered readdir content on seekdir(0), or + * seek to new frag, or seek prior to current chunk */ dout("dir_llseek dropping %p content\n", file); reset_readdir(fi, fpos_frag(offset)); + } else if (fpos_cmp(offset, old_offset) > 0) { + /* reset dir_release_count if we did a forward seek */ + fi->dir_release_count = 0; + fi->readdir_cache_idx = -1; } - - /* bump dir_release_count if we did a forward seek */ - if (fpos_cmp(offset, old_offset) > 0) - fi->dir_release_count--; } out: mutex_unlock(&inode->i_mutex); @@ -764,7 +774,7 @@ static int ceph_symlink(struct inode *dir, struct dentry *dentry, err = PTR_ERR(req); goto out; } - req->r_path2 = kstrdup(dest, GFP_NOFS); + req->r_path2 = kstrdup(dest, GFP_KERNEL); if (!req->r_path2) { err = -ENOMEM; ceph_mdsc_put_request(req); @@ -985,16 +995,15 @@ static int ceph_rename(struct inode *old_dir, struct dentry *old_dentry, * to do it here. */ + /* d_move screws up sibling dentries' offsets */ + ceph_dir_clear_complete(old_dir); + ceph_dir_clear_complete(new_dir); + d_move(old_dentry, new_dentry); /* ensure target dentry is invalidated, despite rehashing bug in vfs_rename_dir */ ceph_invalidate_dentry_lease(new_dentry); - - /* d_move screws up sibling dentries' offsets */ - ceph_dir_clear_complete(old_dir); - ceph_dir_clear_complete(new_dir); - } ceph_mdsc_put_request(req); return err; @@ -1189,7 +1198,7 @@ static ssize_t ceph_read_dir(struct file *file, char __user *buf, size_t size, return -EISDIR; if (!cf->dir_info) { - cf->dir_info = kmalloc(bufsize, GFP_NOFS); + cf->dir_info = kmalloc(bufsize, GFP_KERNEL); if (!cf->dir_info) return -ENOMEM; cf->dir_info_len = @@ -1224,66 +1233,6 @@ static ssize_t ceph_read_dir(struct file *file, char __user *buf, size_t size, } /* - * an fsync() on a dir will wait for any uncommitted directory - * operations to commit. - */ -static int ceph_dir_fsync(struct file *file, loff_t start, loff_t end, - int datasync) -{ - struct inode *inode = file_inode(file); - struct ceph_inode_info *ci = ceph_inode(inode); - struct list_head *head = &ci->i_unsafe_dirops; - struct ceph_mds_request *req; - u64 last_tid; - int ret = 0; - - dout("dir_fsync %p\n", inode); - ret = filemap_write_and_wait_range(inode->i_mapping, start, end); - if (ret) - return ret; - mutex_lock(&inode->i_mutex); - - spin_lock(&ci->i_unsafe_lock); - if (list_empty(head)) - goto out; - - req = list_entry(head->prev, - struct ceph_mds_request, r_unsafe_dir_item); - last_tid = req->r_tid; - - do { - ceph_mdsc_get_request(req); - spin_unlock(&ci->i_unsafe_lock); - - dout("dir_fsync %p wait on tid %llu (until %llu)\n", - inode, req->r_tid, last_tid); - if (req->r_timeout) { - unsigned long time_left = wait_for_completion_timeout( - &req->r_safe_completion, - req->r_timeout); - if (time_left > 0) - ret = 0; - else - ret = -EIO; /* timed out */ - } else { - wait_for_completion(&req->r_safe_completion); - } - ceph_mdsc_put_request(req); - - spin_lock(&ci->i_unsafe_lock); - if (ret || list_empty(head)) - break; - req = list_entry(head->next, - struct ceph_mds_request, r_unsafe_dir_item); - } while (req->r_tid < last_tid); -out: - spin_unlock(&ci->i_unsafe_lock); - mutex_unlock(&inode->i_mutex); - - return ret; -} - -/* * We maintain a private dentry LRU. * * FIXME: this needs to be changed to a per-mds lru to be useful. @@ -1353,7 +1302,7 @@ const struct file_operations ceph_dir_fops = { .open = ceph_open, .release = ceph_release, .unlocked_ioctl = ceph_ioctl, - .fsync = ceph_dir_fsync, + .fsync = ceph_fsync, }; const struct file_operations ceph_snapdir_fops = { diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 3b6b522b4..8b79d87ea 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -89,13 +89,14 @@ static int ceph_init_file(struct inode *inode, struct file *file, int fmode) case S_IFDIR: dout("init_file %p %p 0%o (regular)\n", inode, file, inode->i_mode); - cf = kmem_cache_alloc(ceph_file_cachep, GFP_NOFS | __GFP_ZERO); + cf = kmem_cache_alloc(ceph_file_cachep, GFP_KERNEL | __GFP_ZERO); if (cf == NULL) { ceph_put_fmode(ceph_inode(inode), fmode); /* clean up */ return -ENOMEM; } cf->fmode = fmode; cf->next_offset = 2; + cf->readdir_cache_idx = -1; file->private_data = cf; BUG_ON(inode->i_fop->release != ceph_release); break; @@ -324,7 +325,6 @@ int ceph_release(struct inode *inode, struct file *file) ceph_mdsc_put_request(cf->last_readdir); kfree(cf->last_name); kfree(cf->dir_info); - dput(cf->dentry); kmem_cache_free(ceph_file_cachep, cf); /* wake up anyone waiting for caps on this inode */ @@ -483,7 +483,7 @@ static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *i, } } else { num_pages = calc_pages_for(off, len); - pages = ceph_alloc_page_vector(num_pages, GFP_NOFS); + pages = ceph_alloc_page_vector(num_pages, GFP_KERNEL); if (IS_ERR(pages)) return PTR_ERR(pages); ret = striped_read(inode, off, len, pages, @@ -557,13 +557,13 @@ static void ceph_sync_write_unsafe(struct ceph_osd_request *req, bool unsafe) * objects, rollback on failure, etc.) */ static ssize_t -ceph_sync_direct_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos) +ceph_sync_direct_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos, + struct ceph_snap_context *snapc) { struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_fs_client *fsc = ceph_inode_to_client(inode); - struct ceph_snap_context *snapc; struct ceph_vino vino; struct ceph_osd_request *req; struct page **pages; @@ -600,7 +600,6 @@ ceph_sync_direct_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos) size_t start; ssize_t n; - snapc = ci->i_snap_realm->cached_context; vino = ceph_vino(inode); req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout, vino, pos, &len, 0, @@ -614,7 +613,7 @@ ceph_sync_direct_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos) break; } - osd_req_op_init(req, 1, CEPH_OSD_OP_STARTSYNC); + osd_req_op_init(req, 1, CEPH_OSD_OP_STARTSYNC, 0); n = iov_iter_get_pages_alloc(from, &pages, len, &start); if (unlikely(n < 0)) { @@ -674,13 +673,13 @@ ceph_sync_direct_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos) * objects, rollback on failure, etc.) */ static ssize_t -ceph_sync_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos) +ceph_sync_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos, + struct ceph_snap_context *snapc) { struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_fs_client *fsc = ceph_inode_to_client(inode); - struct ceph_snap_context *snapc; struct ceph_vino vino; struct ceph_osd_request *req; struct page **pages; @@ -717,7 +716,6 @@ ceph_sync_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos) size_t left; int n; - snapc = ci->i_snap_realm->cached_context; vino = ceph_vino(inode); req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout, vino, pos, &len, 0, 1, @@ -736,7 +734,7 @@ ceph_sync_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos) */ num_pages = (len + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - pages = ceph_alloc_page_vector(num_pages, GFP_NOFS); + pages = ceph_alloc_page_vector(num_pages, GFP_KERNEL); if (IS_ERR(pages)) { ret = PTR_ERR(pages); goto out; @@ -860,7 +858,7 @@ again: struct page *page = NULL; loff_t i_size; if (retry_op == READ_INLINE) { - page = __page_cache_alloc(GFP_NOFS); + page = __page_cache_alloc(GFP_KERNEL); if (!page) return -ENOMEM; } @@ -941,6 +939,7 @@ static ssize_t ceph_write_iter(struct kiocb *iocb, struct iov_iter *from) struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_osd_client *osdc = &ceph_sb_to_client(inode->i_sb)->client->osdc; + struct ceph_cap_flush *prealloc_cf; ssize_t count, written = 0; int err, want, got; loff_t pos; @@ -948,6 +947,10 @@ static ssize_t ceph_write_iter(struct kiocb *iocb, struct iov_iter *from) if (ceph_snap(inode) != CEPH_NOSNAP) return -EROFS; + prealloc_cf = ceph_alloc_cap_flush(); + if (!prealloc_cf) + return -ENOMEM; + mutex_lock(&inode->i_mutex); /* We can write back this queue in page reclaim */ @@ -959,7 +962,7 @@ static ssize_t ceph_write_iter(struct kiocb *iocb, struct iov_iter *from) pos = iocb->ki_pos; count = iov_iter_count(from); - err = file_remove_suid(file); + err = file_remove_privs(file); if (err) goto out; @@ -996,14 +999,30 @@ retry_snap: if ((got & (CEPH_CAP_FILE_BUFFER|CEPH_CAP_FILE_LAZYIO)) == 0 || (iocb->ki_flags & IOCB_DIRECT) || (fi->flags & CEPH_F_SYNC)) { + struct ceph_snap_context *snapc; struct iov_iter data; mutex_unlock(&inode->i_mutex); + + spin_lock(&ci->i_ceph_lock); + if (__ceph_have_pending_cap_snap(ci)) { + struct ceph_cap_snap *capsnap = + list_last_entry(&ci->i_cap_snaps, + struct ceph_cap_snap, + ci_item); + snapc = ceph_get_snap_context(capsnap->context); + } else { + BUG_ON(!ci->i_head_snapc); + snapc = ceph_get_snap_context(ci->i_head_snapc); + } + spin_unlock(&ci->i_ceph_lock); + /* we might need to revert back to that point */ data = *from; if (iocb->ki_flags & IOCB_DIRECT) - written = ceph_sync_direct_write(iocb, &data, pos); + written = ceph_sync_direct_write(iocb, &data, pos, + snapc); else - written = ceph_sync_write(iocb, &data, pos); + written = ceph_sync_write(iocb, &data, pos, snapc); if (written == -EOLDSNAPC) { dout("aio_write %p %llx.%llx %llu~%u" "got EOLDSNAPC, retrying\n", @@ -1014,6 +1033,7 @@ retry_snap: } if (written > 0) iov_iter_advance(from, written); + ceph_put_snap_context(snapc); } else { loff_t old_size = inode->i_size; /* @@ -1035,7 +1055,8 @@ retry_snap: int dirty; spin_lock(&ci->i_ceph_lock); ci->i_inline_version = CEPH_INLINE_NONE; - dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR); + dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR, + &prealloc_cf); spin_unlock(&ci->i_ceph_lock); if (dirty) __mark_inode_dirty(inode, dirty); @@ -1059,6 +1080,7 @@ retry_snap: out: mutex_unlock(&inode->i_mutex); out_unlocked: + ceph_free_cap_flush(prealloc_cf); current->backing_dev_info = NULL; return written ? written : err; } @@ -1255,6 +1277,7 @@ static long ceph_fallocate(struct file *file, int mode, struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_osd_client *osdc = &ceph_inode_to_client(inode)->client->osdc; + struct ceph_cap_flush *prealloc_cf; int want, got = 0; int dirty; int ret = 0; @@ -1267,6 +1290,10 @@ static long ceph_fallocate(struct file *file, int mode, if (!S_ISREG(inode->i_mode)) return -EOPNOTSUPP; + prealloc_cf = ceph_alloc_cap_flush(); + if (!prealloc_cf) + return -ENOMEM; + mutex_lock(&inode->i_mutex); if (ceph_snap(inode) != CEPH_NOSNAP) { @@ -1313,7 +1340,8 @@ static long ceph_fallocate(struct file *file, int mode, if (!ret) { spin_lock(&ci->i_ceph_lock); ci->i_inline_version = CEPH_INLINE_NONE; - dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR); + dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR, + &prealloc_cf); spin_unlock(&ci->i_ceph_lock); if (dirty) __mark_inode_dirty(inode, dirty); @@ -1322,6 +1350,7 @@ static long ceph_fallocate(struct file *file, int mode, ceph_put_cap_refs(ci, got); unlock: mutex_unlock(&inode->i_mutex); + ceph_free_cap_flush(prealloc_cf); return ret; } diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index e876e1944..96d2bd829 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -6,7 +6,6 @@ #include <linux/string.h> #include <linux/uaccess.h> #include <linux/kernel.h> -#include <linux/namei.h> #include <linux/writeback.h> #include <linux/vmalloc.h> #include <linux/posix_acl.h> @@ -390,9 +389,10 @@ struct inode *ceph_alloc_inode(struct super_block *sb) ci->i_inline_version = 0; ci->i_time_warp_seq = 0; ci->i_ceph_flags = 0; - ci->i_ordered_count = 0; - atomic_set(&ci->i_release_count, 1); - atomic_set(&ci->i_complete_count, 0); + atomic64_set(&ci->i_ordered_count, 1); + atomic64_set(&ci->i_release_count, 1); + atomic64_set(&ci->i_complete_seq[0], 0); + atomic64_set(&ci->i_complete_seq[1], 0); ci->i_symlink = NULL; memset(&ci->i_dir_layout, 0, sizeof(ci->i_dir_layout)); @@ -416,9 +416,8 @@ struct inode *ceph_alloc_inode(struct super_block *sb) ci->i_flushing_caps = 0; INIT_LIST_HEAD(&ci->i_dirty_item); INIT_LIST_HEAD(&ci->i_flushing_item); - ci->i_cap_flush_seq = 0; - ci->i_cap_flush_last_tid = 0; - memset(&ci->i_cap_flush_tid, 0, sizeof(ci->i_cap_flush_tid)); + ci->i_prealloc_cap_flush = NULL; + ci->i_cap_flush_tree = RB_ROOT; init_waitqueue_head(&ci->i_cap_wq); ci->i_hold_caps_min = 0; ci->i_hold_caps_max = 0; @@ -753,7 +752,10 @@ static int fill_inode(struct inode *inode, struct page *locked_page, if (new_version || (new_issued & (CEPH_CAP_ANY_FILE_RD | CEPH_CAP_ANY_FILE_WR))) { + if (ci->i_layout.fl_pg_pool != info->layout.fl_pg_pool) + ci->i_ceph_flags &= ~CEPH_I_POOL_PERM; ci->i_layout = info->layout; + queue_trunc = ceph_fill_file_size(inode, issued, le32_to_cpu(info->truncate_seq), le64_to_cpu(info->truncate_size), @@ -819,6 +821,7 @@ static int fill_inode(struct inode *inode, struct page *locked_page, else kfree(sym); /* lost a race */ } + inode->i_link = ci->i_symlink; break; case S_IFDIR: inode->i_op = &ceph_dir_iops; @@ -858,9 +861,10 @@ static int fill_inode(struct inode *inode, struct page *locked_page, (issued & CEPH_CAP_FILE_EXCL) == 0 && !__ceph_dir_is_complete(ci)) { dout(" marking %p complete (empty)\n", inode); + i_size_write(inode, 0); __ceph_dir_set_complete(ci, - atomic_read(&ci->i_release_count), - ci->i_ordered_count); + atomic64_read(&ci->i_release_count), + atomic64_read(&ci->i_ordered_count)); } wake = true; @@ -1212,6 +1216,10 @@ retry_lookup: dout("fill_trace doing d_move %p -> %p\n", req->r_old_dentry, dn); + /* d_move screws up sibling dentries' offsets */ + ceph_dir_clear_ordered(dir); + ceph_dir_clear_ordered(olddir); + d_move(req->r_old_dentry, dn); dout(" src %p '%pd' dst %p '%pd'\n", req->r_old_dentry, @@ -1222,10 +1230,6 @@ retry_lookup: rehashing bug in vfs_rename_dir */ ceph_invalidate_dentry_lease(dn); - /* d_move screws up sibling dentries' offsets */ - ceph_dir_clear_ordered(dir); - ceph_dir_clear_ordered(olddir); - dout("dn %p gets new offset %lld\n", req->r_old_dentry, ceph_dentry(req->r_old_dentry)->offset); @@ -1333,6 +1337,49 @@ static int readdir_prepopulate_inodes_only(struct ceph_mds_request *req, return err; } +void ceph_readdir_cache_release(struct ceph_readdir_cache_control *ctl) +{ + if (ctl->page) { + kunmap(ctl->page); + page_cache_release(ctl->page); + ctl->page = NULL; + } +} + +static int fill_readdir_cache(struct inode *dir, struct dentry *dn, + struct ceph_readdir_cache_control *ctl, + struct ceph_mds_request *req) +{ + struct ceph_inode_info *ci = ceph_inode(dir); + unsigned nsize = PAGE_CACHE_SIZE / sizeof(struct dentry*); + unsigned idx = ctl->index % nsize; + pgoff_t pgoff = ctl->index / nsize; + + if (!ctl->page || pgoff != page_index(ctl->page)) { + ceph_readdir_cache_release(ctl); + ctl->page = grab_cache_page(&dir->i_data, pgoff); + if (!ctl->page) { + ctl->index = -1; + return -ENOMEM; + } + /* reading/filling the cache are serialized by + * i_mutex, no need to use page lock */ + unlock_page(ctl->page); + ctl->dentries = kmap(ctl->page); + } + + if (req->r_dir_release_cnt == atomic64_read(&ci->i_release_count) && + req->r_dir_ordered_cnt == atomic64_read(&ci->i_ordered_count)) { + dout("readdir cache dn %p idx %d\n", dn, ctl->index); + ctl->dentries[idx] = dn; + ctl->index++; + } else { + dout("disable readdir cache\n"); + ctl->index = -1; + } + return 0; +} + int ceph_readdir_prepopulate(struct ceph_mds_request *req, struct ceph_mds_session *session) { @@ -1345,8 +1392,11 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req, struct inode *snapdir = NULL; struct ceph_mds_request_head *rhead = req->r_request->front.iov_base; struct ceph_dentry_info *di; - u64 r_readdir_offset = req->r_readdir_offset; u32 frag = le32_to_cpu(rhead->args.readdir.frag); + struct ceph_readdir_cache_control cache_ctl = {}; + + if (req->r_aborted) + return readdir_prepopulate_inodes_only(req, session); if (rinfo->dir_dir && le32_to_cpu(rinfo->dir_dir->frag) != frag) { @@ -1354,14 +1404,11 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req, frag, le32_to_cpu(rinfo->dir_dir->frag)); frag = le32_to_cpu(rinfo->dir_dir->frag); if (ceph_frag_is_leftmost(frag)) - r_readdir_offset = 2; + req->r_readdir_offset = 2; else - r_readdir_offset = 0; + req->r_readdir_offset = 0; } - if (req->r_aborted) - return readdir_prepopulate_inodes_only(req, session); - if (le32_to_cpu(rinfo->head->op) == CEPH_MDS_OP_LSSNAP) { snapdir = ceph_get_snapdir(d_inode(parent)); parent = d_find_alias(snapdir); @@ -1374,6 +1421,17 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req, ceph_fill_dirfrag(d_inode(parent), rinfo->dir_dir); } + if (ceph_frag_is_leftmost(frag) && req->r_readdir_offset == 2) { + /* note dir version at start of readdir so we can tell + * if any dentries get dropped */ + struct ceph_inode_info *ci = ceph_inode(d_inode(parent)); + req->r_dir_release_cnt = atomic64_read(&ci->i_release_count); + req->r_dir_ordered_cnt = atomic64_read(&ci->i_ordered_count); + req->r_readdir_cache_idx = 0; + } + + cache_ctl.index = req->r_readdir_cache_idx; + /* FIXME: release caps/leases if error occurs */ for (i = 0; i < rinfo->dir_nr; i++) { struct ceph_vino vino; @@ -1413,13 +1471,6 @@ retry_lookup: d_delete(dn); dput(dn); goto retry_lookup; - } else { - /* reorder parent's d_subdirs */ - spin_lock(&parent->d_lock); - spin_lock_nested(&dn->d_lock, DENTRY_D_LOCK_NESTED); - list_move(&dn->d_child, &parent->d_subdirs); - spin_unlock(&dn->d_lock); - spin_unlock(&parent->d_lock); } /* inode */ @@ -1436,13 +1487,15 @@ retry_lookup: } } - if (fill_inode(in, NULL, &rinfo->dir_in[i], NULL, session, - req->r_request_started, -1, - &req->r_caps_reservation) < 0) { + ret = fill_inode(in, NULL, &rinfo->dir_in[i], NULL, session, + req->r_request_started, -1, + &req->r_caps_reservation); + if (ret < 0) { pr_err("fill_inode badness on %p\n", in); if (d_really_is_negative(dn)) iput(in); d_drop(dn); + err = ret; goto next_item; } @@ -1458,19 +1511,28 @@ retry_lookup: } di = dn->d_fsdata; - di->offset = ceph_make_fpos(frag, i + r_readdir_offset); + di->offset = ceph_make_fpos(frag, i + req->r_readdir_offset); update_dentry_lease(dn, rinfo->dir_dlease[i], req->r_session, req->r_request_started); + + if (err == 0 && cache_ctl.index >= 0) { + ret = fill_readdir_cache(d_inode(parent), dn, + &cache_ctl, req); + if (ret < 0) + err = ret; + } next_item: if (dn) dput(dn); } - if (err == 0) - req->r_did_prepopulate = true; - out: + if (err == 0) { + req->r_did_prepopulate = true; + req->r_readdir_cache_idx = cache_ctl.index; + } + ceph_readdir_cache_release(&cache_ctl); if (snapdir) { iput(snapdir); dput(parent); @@ -1691,16 +1753,9 @@ retry: /* * symlinks */ -static void *ceph_sym_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct ceph_inode_info *ci = ceph_inode(d_inode(dentry)); - nd_set_link(nd, ci->i_symlink); - return NULL; -} - static const struct inode_operations ceph_symlink_iops = { .readlink = generic_readlink, - .follow_link = ceph_sym_follow_link, + .follow_link = simple_follow_link, .setattr = ceph_setattr, .getattr = ceph_getattr, .setxattr = ceph_setxattr, @@ -1719,11 +1774,13 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) const unsigned int ia_valid = attr->ia_valid; struct ceph_mds_request *req; struct ceph_mds_client *mdsc = ceph_sb_to_client(dentry->d_sb)->mdsc; + struct ceph_cap_flush *prealloc_cf; int issued; int release = 0, dirtied = 0; int mask = 0; int err = 0; int inode_dirty_flags = 0; + bool lock_snap_rwsem = false; if (ceph_snap(inode) != CEPH_NOSNAP) return -EROFS; @@ -1732,13 +1789,31 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) if (err != 0) return err; + prealloc_cf = ceph_alloc_cap_flush(); + if (!prealloc_cf) + return -ENOMEM; + req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SETATTR, USE_AUTH_MDS); - if (IS_ERR(req)) + if (IS_ERR(req)) { + ceph_free_cap_flush(prealloc_cf); return PTR_ERR(req); + } spin_lock(&ci->i_ceph_lock); issued = __ceph_caps_issued(ci, NULL); + + if (!ci->i_head_snapc && + (issued & (CEPH_CAP_ANY_EXCL | CEPH_CAP_FILE_WR))) { + lock_snap_rwsem = true; + if (!down_read_trylock(&mdsc->snap_rwsem)) { + spin_unlock(&ci->i_ceph_lock); + down_read(&mdsc->snap_rwsem); + spin_lock(&ci->i_ceph_lock); + issued = __ceph_caps_issued(ci, NULL); + } + } + dout("setattr %p issued %s\n", inode, ceph_cap_string(issued)); if (ia_valid & ATTR_UID) { @@ -1881,12 +1956,15 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) dout("setattr %p ATTR_FILE ... hrm!\n", inode); if (dirtied) { - inode_dirty_flags = __ceph_mark_dirty_caps(ci, dirtied); + inode_dirty_flags = __ceph_mark_dirty_caps(ci, dirtied, + &prealloc_cf); inode->i_ctime = CURRENT_TIME; } release &= issued; spin_unlock(&ci->i_ceph_lock); + if (lock_snap_rwsem) + up_read(&mdsc->snap_rwsem); if (inode_dirty_flags) __mark_inode_dirty(inode, inode_dirty_flags); @@ -1911,9 +1989,11 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) ceph_mdsc_put_request(req); if (mask & CEPH_SETATTR_SIZE) __ceph_do_pending_vmtruncate(inode); + ceph_free_cap_flush(prealloc_cf); return err; out_put: ceph_mdsc_put_request(req); + ceph_free_cap_flush(prealloc_cf); return err; } diff --git a/fs/ceph/locks.c b/fs/ceph/locks.c index 4347039ec..6706bde9a 100644 --- a/fs/ceph/locks.c +++ b/fs/ceph/locks.c @@ -287,7 +287,7 @@ int ceph_encode_locks_to_buffer(struct inode *inode, return 0; spin_lock(&ctx->flc_lock); - list_for_each_entry(lock, &ctx->flc_flock, fl_list) { + list_for_each_entry(lock, &ctx->flc_posix, fl_list) { ++seen_fcntl; if (seen_fcntl > num_fcntl_locks) { err = -ENOSPC; diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 84f37f34f..6aa07af67 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -8,6 +8,7 @@ #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/utsname.h> +#include <linux/ratelimit.h> #include "super.h" #include "mds_client.h" @@ -458,7 +459,6 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, s->s_cap_reconnect = 0; s->s_cap_iterator = NULL; INIT_LIST_HEAD(&s->s_cap_releases); - INIT_LIST_HEAD(&s->s_cap_releases_done); INIT_LIST_HEAD(&s->s_cap_flushing); INIT_LIST_HEAD(&s->s_cap_snaps_flushing); @@ -629,6 +629,9 @@ static void __register_request(struct ceph_mds_client *mdsc, req->r_uid = current_fsuid(); req->r_gid = current_fsgid(); + if (mdsc->oldest_tid == 0 && req->r_op != CEPH_MDS_OP_SETFILELOCK) + mdsc->oldest_tid = req->r_tid; + if (dir) { struct ceph_inode_info *ci = ceph_inode(dir); @@ -644,6 +647,21 @@ static void __unregister_request(struct ceph_mds_client *mdsc, struct ceph_mds_request *req) { dout("__unregister_request %p tid %lld\n", req, req->r_tid); + + if (req->r_tid == mdsc->oldest_tid) { + struct rb_node *p = rb_next(&req->r_node); + mdsc->oldest_tid = 0; + while (p) { + struct ceph_mds_request *next_req = + rb_entry(p, struct ceph_mds_request, r_node); + if (next_req->r_op != CEPH_MDS_OP_SETFILELOCK) { + mdsc->oldest_tid = next_req->r_tid; + break; + } + p = rb_next(p); + } + } + rb_erase(&req->r_node, &mdsc->request_tree); RB_CLEAR_NODE(&req->r_node); @@ -998,27 +1016,25 @@ void ceph_mdsc_open_export_target_sessions(struct ceph_mds_client *mdsc, * session caps */ -/* - * Free preallocated cap messages assigned to this session - */ -static void cleanup_cap_releases(struct ceph_mds_session *session) +/* caller holds s_cap_lock, we drop it */ +static void cleanup_cap_releases(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session) + __releases(session->s_cap_lock) { - struct ceph_msg *msg; + LIST_HEAD(tmp_list); + list_splice_init(&session->s_cap_releases, &tmp_list); + session->s_num_cap_releases = 0; + spin_unlock(&session->s_cap_lock); - spin_lock(&session->s_cap_lock); - while (!list_empty(&session->s_cap_releases)) { - msg = list_first_entry(&session->s_cap_releases, - struct ceph_msg, list_head); - list_del_init(&msg->list_head); - ceph_msg_put(msg); - } - while (!list_empty(&session->s_cap_releases_done)) { - msg = list_first_entry(&session->s_cap_releases_done, - struct ceph_msg, list_head); - list_del_init(&msg->list_head); - ceph_msg_put(msg); + dout("cleanup_cap_releases mds%d\n", session->s_mds); + while (!list_empty(&tmp_list)) { + struct ceph_cap *cap; + /* zero out the in-progress message */ + cap = list_first_entry(&tmp_list, + struct ceph_cap, session_caps); + list_del(&cap->session_caps); + ceph_put_cap(mdsc, cap); } - spin_unlock(&session->s_cap_lock); } static void cleanup_session_requests(struct ceph_mds_client *mdsc, @@ -1033,7 +1049,8 @@ static void cleanup_session_requests(struct ceph_mds_client *mdsc, req = list_first_entry(&session->s_unsafe, struct ceph_mds_request, r_unsafe_item); list_del_init(&req->r_unsafe_item); - pr_info(" dropping unsafe request %llu\n", req->r_tid); + pr_warn_ratelimited(" dropping unsafe request %llu\n", + req->r_tid); __unregister_request(mdsc, req); } /* zero r_attempts, so kick_requests() will re-send requests */ @@ -1095,10 +1112,16 @@ static int iterate_session_caps(struct ceph_mds_session *session, dout("iterate_session_caps finishing cap %p removal\n", cap); BUG_ON(cap->session != session); + cap->session = NULL; list_del_init(&cap->session_caps); session->s_nr_caps--; - cap->session = NULL; - old_cap = cap; /* put_cap it w/o locks held */ + if (cap->queue_release) { + list_add_tail(&cap->session_caps, + &session->s_cap_releases); + session->s_num_cap_releases++; + } else { + old_cap = cap; /* put_cap it w/o locks held */ + } } if (ret < 0) goto out; @@ -1119,6 +1142,7 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap, void *arg) { struct ceph_inode_info *ci = ceph_inode(inode); + LIST_HEAD(to_remove); int drop = 0; dout("removing cap %p, ci is %p, inode is %p\n", @@ -1126,12 +1150,27 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap, spin_lock(&ci->i_ceph_lock); __ceph_remove_cap(cap, false); if (!ci->i_auth_cap) { + struct ceph_cap_flush *cf; struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; + while (true) { + struct rb_node *n = rb_first(&ci->i_cap_flush_tree); + if (!n) + break; + cf = rb_entry(n, struct ceph_cap_flush, i_node); + rb_erase(&cf->i_node, &ci->i_cap_flush_tree); + list_add(&cf->list, &to_remove); + } + spin_lock(&mdsc->cap_dirty_lock); + + list_for_each_entry(cf, &to_remove, list) + rb_erase(&cf->g_node, &mdsc->cap_flush_tree); + if (!list_empty(&ci->i_dirty_item)) { - pr_info(" dropping dirty %s state for %p %lld\n", + pr_warn_ratelimited( + " dropping dirty %s state for %p %lld\n", ceph_cap_string(ci->i_dirty_caps), inode, ceph_ino(inode)); ci->i_dirty_caps = 0; @@ -1139,7 +1178,8 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap, drop = 1; } if (!list_empty(&ci->i_flushing_item)) { - pr_info(" dropping dirty+flushing %s state for %p %lld\n", + pr_warn_ratelimited( + " dropping dirty+flushing %s state for %p %lld\n", ceph_cap_string(ci->i_flushing_caps), inode, ceph_ino(inode)); ci->i_flushing_caps = 0; @@ -1148,8 +1188,20 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap, drop = 1; } spin_unlock(&mdsc->cap_dirty_lock); + + if (!ci->i_dirty_caps && ci->i_prealloc_cap_flush) { + list_add(&ci->i_prealloc_cap_flush->list, &to_remove); + ci->i_prealloc_cap_flush = NULL; + } } spin_unlock(&ci->i_ceph_lock); + while (!list_empty(&to_remove)) { + struct ceph_cap_flush *cf; + cf = list_first_entry(&to_remove, + struct ceph_cap_flush, list); + list_del(&cf->list); + ceph_free_cap_flush(cf); + } while (drop--) iput(inode); return 0; @@ -1191,11 +1243,12 @@ static void remove_session_caps(struct ceph_mds_session *session) spin_lock(&session->s_cap_lock); } } - spin_unlock(&session->s_cap_lock); + + // drop cap expires and unlock s_cap_lock + cleanup_cap_releases(session->s_mdsc, session); BUG_ON(session->s_nr_caps > 0); BUG_ON(!list_empty(&session->s_cap_flushing)); - cleanup_cap_releases(session); } /* @@ -1371,7 +1424,8 @@ static int trim_caps_cb(struct inode *inode, struct ceph_cap *cap, void *arg) inode, cap, ceph_cap_string(mine), ceph_cap_string(oissued), ceph_cap_string(used), ceph_cap_string(wanted)); if (cap == ci->i_auth_cap) { - if (ci->i_dirty_caps | ci->i_flushing_caps) + if (ci->i_dirty_caps || ci->i_flushing_caps || + !list_empty(&ci->i_cap_snaps)) goto out; if ((used | wanted) & CEPH_CAP_ANY_WR) goto out; @@ -1417,121 +1471,80 @@ static int trim_caps(struct ceph_mds_client *mdsc, session->s_trim_caps = 0; } - ceph_add_cap_releases(mdsc, session); ceph_send_cap_releases(mdsc, session); return 0; } -/* - * Allocate cap_release messages. If there is a partially full message - * in the queue, try to allocate enough to cover it's remainder, so that - * we can send it immediately. - * - * Called under s_mutex. - */ -int ceph_add_cap_releases(struct ceph_mds_client *mdsc, - struct ceph_mds_session *session) +static int check_capsnap_flush(struct ceph_inode_info *ci, + u64 want_snap_seq) { - struct ceph_msg *msg, *partial = NULL; - struct ceph_mds_cap_release *head; - int err = -ENOMEM; - int extra = mdsc->fsc->mount_options->cap_release_safety; - int num; - - dout("add_cap_releases %p mds%d extra %d\n", session, session->s_mds, - extra); - - spin_lock(&session->s_cap_lock); - - if (!list_empty(&session->s_cap_releases)) { - msg = list_first_entry(&session->s_cap_releases, - struct ceph_msg, - list_head); - head = msg->front.iov_base; - num = le32_to_cpu(head->num); - if (num) { - dout(" partial %p with (%d/%d)\n", msg, num, - (int)CEPH_CAPS_PER_RELEASE); - extra += CEPH_CAPS_PER_RELEASE - num; - partial = msg; - } - } - while (session->s_num_cap_releases < session->s_nr_caps + extra) { - spin_unlock(&session->s_cap_lock); - msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPRELEASE, PAGE_CACHE_SIZE, - GFP_NOFS, false); - if (!msg) - goto out_unlocked; - dout("add_cap_releases %p msg %p now %d\n", session, msg, - (int)msg->front.iov_len); - head = msg->front.iov_base; - head->num = cpu_to_le32(0); - msg->front.iov_len = sizeof(*head); - spin_lock(&session->s_cap_lock); - list_add(&msg->list_head, &session->s_cap_releases); - session->s_num_cap_releases += CEPH_CAPS_PER_RELEASE; - } - - if (partial) { - head = partial->front.iov_base; - num = le32_to_cpu(head->num); - dout(" queueing partial %p with %d/%d\n", partial, num, - (int)CEPH_CAPS_PER_RELEASE); - list_move_tail(&partial->list_head, - &session->s_cap_releases_done); - session->s_num_cap_releases -= CEPH_CAPS_PER_RELEASE - num; + int ret = 1; + spin_lock(&ci->i_ceph_lock); + if (want_snap_seq > 0 && !list_empty(&ci->i_cap_snaps)) { + struct ceph_cap_snap *capsnap = + list_first_entry(&ci->i_cap_snaps, + struct ceph_cap_snap, ci_item); + ret = capsnap->follows >= want_snap_seq; } - err = 0; - spin_unlock(&session->s_cap_lock); -out_unlocked: - return err; + spin_unlock(&ci->i_ceph_lock); + return ret; } -static int check_cap_flush(struct inode *inode, u64 want_flush_seq) +static int check_caps_flush(struct ceph_mds_client *mdsc, + u64 want_flush_tid) { - struct ceph_inode_info *ci = ceph_inode(inode); - int ret; - spin_lock(&ci->i_ceph_lock); - if (ci->i_flushing_caps) - ret = ci->i_cap_flush_seq >= want_flush_seq; - else - ret = 1; - spin_unlock(&ci->i_ceph_lock); + struct rb_node *n; + struct ceph_cap_flush *cf; + int ret = 1; + + spin_lock(&mdsc->cap_dirty_lock); + n = rb_first(&mdsc->cap_flush_tree); + cf = n ? rb_entry(n, struct ceph_cap_flush, g_node) : NULL; + if (cf && cf->tid <= want_flush_tid) { + dout("check_caps_flush still flushing tid %llu <= %llu\n", + cf->tid, want_flush_tid); + ret = 0; + } + spin_unlock(&mdsc->cap_dirty_lock); return ret; } /* * flush all dirty inode data to disk. * - * returns true if we've flushed through want_flush_seq + * returns true if we've flushed through want_flush_tid */ -static void wait_caps_flush(struct ceph_mds_client *mdsc, u64 want_flush_seq) +static void wait_caps_flush(struct ceph_mds_client *mdsc, + u64 want_flush_tid, u64 want_snap_seq) { int mds; - dout("check_cap_flush want %lld\n", want_flush_seq); + dout("check_caps_flush want %llu snap want %llu\n", + want_flush_tid, want_snap_seq); mutex_lock(&mdsc->mutex); - for (mds = 0; mds < mdsc->max_sessions; mds++) { + for (mds = 0; mds < mdsc->max_sessions; ) { struct ceph_mds_session *session = mdsc->sessions[mds]; struct inode *inode = NULL; - if (!session) + if (!session) { + mds++; continue; + } get_session(session); mutex_unlock(&mdsc->mutex); mutex_lock(&session->s_mutex); - if (!list_empty(&session->s_cap_flushing)) { - struct ceph_inode_info *ci = - list_entry(session->s_cap_flushing.next, - struct ceph_inode_info, - i_flushing_item); - - if (!check_cap_flush(&ci->vfs_inode, want_flush_seq)) { - dout("check_cap_flush still flushing %p " - "seq %lld <= %lld to mds%d\n", - &ci->vfs_inode, ci->i_cap_flush_seq, - want_flush_seq, session->s_mds); + if (!list_empty(&session->s_cap_snaps_flushing)) { + struct ceph_cap_snap *capsnap = + list_first_entry(&session->s_cap_snaps_flushing, + struct ceph_cap_snap, + flushing_item); + struct ceph_inode_info *ci = capsnap->ci; + if (!check_capsnap_flush(ci, want_snap_seq)) { + dout("check_cap_flush still flushing snap %p " + "follows %lld <= %lld to mds%d\n", + &ci->vfs_inode, capsnap->follows, + want_snap_seq, mds); inode = igrab(&ci->vfs_inode); } } @@ -1540,15 +1553,21 @@ static void wait_caps_flush(struct ceph_mds_client *mdsc, u64 want_flush_seq) if (inode) { wait_event(mdsc->cap_flushing_wq, - check_cap_flush(inode, want_flush_seq)); + check_capsnap_flush(ceph_inode(inode), + want_snap_seq)); iput(inode); + } else { + mds++; } mutex_lock(&mdsc->mutex); } - mutex_unlock(&mdsc->mutex); - dout("check_cap_flush ok, flushed thru %lld\n", want_flush_seq); + + wait_event(mdsc->cap_flushing_wq, + check_caps_flush(mdsc, want_flush_tid)); + + dout("check_caps_flush ok, flushed thru %llu\n", want_flush_tid); } /* @@ -1557,60 +1576,74 @@ static void wait_caps_flush(struct ceph_mds_client *mdsc, u64 want_flush_seq) void ceph_send_cap_releases(struct ceph_mds_client *mdsc, struct ceph_mds_session *session) { - struct ceph_msg *msg; + struct ceph_msg *msg = NULL; + struct ceph_mds_cap_release *head; + struct ceph_mds_cap_item *item; + struct ceph_cap *cap; + LIST_HEAD(tmp_list); + int num_cap_releases; - dout("send_cap_releases mds%d\n", session->s_mds); spin_lock(&session->s_cap_lock); - while (!list_empty(&session->s_cap_releases_done)) { - msg = list_first_entry(&session->s_cap_releases_done, - struct ceph_msg, list_head); - list_del_init(&msg->list_head); - spin_unlock(&session->s_cap_lock); - msg->hdr.front_len = cpu_to_le32(msg->front.iov_len); - dout("send_cap_releases mds%d %p\n", session->s_mds, msg); - ceph_con_send(&session->s_con, msg); - spin_lock(&session->s_cap_lock); - } +again: + list_splice_init(&session->s_cap_releases, &tmp_list); + num_cap_releases = session->s_num_cap_releases; + session->s_num_cap_releases = 0; spin_unlock(&session->s_cap_lock); -} -static void discard_cap_releases(struct ceph_mds_client *mdsc, - struct ceph_mds_session *session) -{ - struct ceph_msg *msg; - struct ceph_mds_cap_release *head; - unsigned num; - - dout("discard_cap_releases mds%d\n", session->s_mds); + while (!list_empty(&tmp_list)) { + if (!msg) { + msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPRELEASE, + PAGE_CACHE_SIZE, GFP_NOFS, false); + if (!msg) + goto out_err; + head = msg->front.iov_base; + head->num = cpu_to_le32(0); + msg->front.iov_len = sizeof(*head); + } + cap = list_first_entry(&tmp_list, struct ceph_cap, + session_caps); + list_del(&cap->session_caps); + num_cap_releases--; - if (!list_empty(&session->s_cap_releases)) { - /* zero out the in-progress message */ - msg = list_first_entry(&session->s_cap_releases, - struct ceph_msg, list_head); head = msg->front.iov_base; - num = le32_to_cpu(head->num); - dout("discard_cap_releases mds%d %p %u\n", - session->s_mds, msg, num); - head->num = cpu_to_le32(0); - msg->front.iov_len = sizeof(*head); - session->s_num_cap_releases += num; + le32_add_cpu(&head->num, 1); + item = msg->front.iov_base + msg->front.iov_len; + item->ino = cpu_to_le64(cap->cap_ino); + item->cap_id = cpu_to_le64(cap->cap_id); + item->migrate_seq = cpu_to_le32(cap->mseq); + item->seq = cpu_to_le32(cap->issue_seq); + msg->front.iov_len += sizeof(*item); + + ceph_put_cap(mdsc, cap); + + if (le32_to_cpu(head->num) == CEPH_CAPS_PER_RELEASE) { + msg->hdr.front_len = cpu_to_le32(msg->front.iov_len); + dout("send_cap_releases mds%d %p\n", session->s_mds, msg); + ceph_con_send(&session->s_con, msg); + msg = NULL; + } } - /* requeue completed messages */ - while (!list_empty(&session->s_cap_releases_done)) { - msg = list_first_entry(&session->s_cap_releases_done, - struct ceph_msg, list_head); - list_del_init(&msg->list_head); + BUG_ON(num_cap_releases != 0); - head = msg->front.iov_base; - num = le32_to_cpu(head->num); - dout("discard_cap_releases mds%d %p %u\n", session->s_mds, msg, - num); - session->s_num_cap_releases += num; - head->num = cpu_to_le32(0); - msg->front.iov_len = sizeof(*head); - list_add(&msg->list_head, &session->s_cap_releases); + spin_lock(&session->s_cap_lock); + if (!list_empty(&session->s_cap_releases)) + goto again; + spin_unlock(&session->s_cap_lock); + + if (msg) { + msg->hdr.front_len = cpu_to_le32(msg->front.iov_len); + dout("send_cap_releases mds%d %p\n", session->s_mds, msg); + ceph_con_send(&session->s_con, msg); } + return; +out_err: + pr_err("send_cap_releases mds%d, failed to allocate message\n", + session->s_mds); + spin_lock(&session->s_cap_lock); + list_splice(&tmp_list, &session->s_cap_releases); + session->s_num_cap_releases += num_cap_releases; + spin_unlock(&session->s_cap_lock); } /* @@ -1635,7 +1668,8 @@ int ceph_alloc_readdir_reply_buffer(struct ceph_mds_request *req, order = get_order(size * num_entries); while (order >= 0) { - rinfo->dir_in = (void*)__get_free_pages(GFP_NOFS | __GFP_NOWARN, + rinfo->dir_in = (void*)__get_free_pages(GFP_KERNEL | + __GFP_NOWARN, order); if (rinfo->dir_in) break; @@ -1697,13 +1731,9 @@ static struct ceph_mds_request *__get_oldest_req(struct ceph_mds_client *mdsc) struct ceph_mds_request, r_node); } -static u64 __get_oldest_tid(struct ceph_mds_client *mdsc) +static inline u64 __get_oldest_tid(struct ceph_mds_client *mdsc) { - struct ceph_mds_request *req = __get_oldest_req(mdsc); - - if (req) - return req->r_tid; - return 0; + return mdsc->oldest_tid; } /* @@ -2267,15 +2297,18 @@ int ceph_mdsc_do_request(struct ceph_mds_client *mdsc, /* wait */ mutex_unlock(&mdsc->mutex); dout("do_request waiting\n"); - if (req->r_timeout) { - err = (long)wait_for_completion_killable_timeout( - &req->r_completion, req->r_timeout); - if (err == 0) - err = -EIO; - } else if (req->r_wait_for_completion) { + if (!req->r_timeout && req->r_wait_for_completion) { err = req->r_wait_for_completion(mdsc, req); } else { - err = wait_for_completion_killable(&req->r_completion); + long timeleft = wait_for_completion_killable_timeout( + &req->r_completion, + ceph_timeout_jiffies(req->r_timeout)); + if (timeleft > 0) + err = 0; + else if (!timeleft) + err = -EIO; /* timed out */ + else + err = timeleft; /* killed */ } dout("do_request waited, got %d\n", err); mutex_lock(&mdsc->mutex); @@ -2496,7 +2529,6 @@ out_err: } mutex_unlock(&mdsc->mutex); - ceph_add_cap_releases(mdsc, req->r_session); mutex_unlock(&session->s_mutex); /* kick calling process */ @@ -2888,8 +2920,7 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, */ session->s_cap_reconnect = 1; /* drop old cap expires; we're about to reestablish that state */ - discard_cap_releases(mdsc, session); - spin_unlock(&session->s_cap_lock); + cleanup_cap_releases(mdsc, session); /* trim unused caps to reduce MDS's cache rejoin time */ if (mdsc->fsc->sb->s_root) @@ -2956,6 +2987,9 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, reply->hdr.data_len = cpu_to_le32(pagelist->length); ceph_msg_data_add_pagelist(reply, pagelist); + + ceph_early_kick_flushing_caps(mdsc, session); + ceph_con_send(&session->s_con, reply); mutex_unlock(&session->s_mutex); @@ -3352,7 +3386,6 @@ static void delayed_work(struct work_struct *work) send_renew_caps(mdsc, s); else ceph_con_keepalive(&s->s_con); - ceph_add_cap_releases(mdsc, s); if (s->s_state == CEPH_MDS_SESSION_OPEN || s->s_state == CEPH_MDS_SESSION_HUNG) ceph_send_cap_releases(mdsc, s); @@ -3390,11 +3423,13 @@ int ceph_mdsc_init(struct ceph_fs_client *fsc) atomic_set(&mdsc->num_sessions, 0); mdsc->max_sessions = 0; mdsc->stopping = 0; + mdsc->last_snap_seq = 0; init_rwsem(&mdsc->snap_rwsem); mdsc->snap_realms = RB_ROOT; INIT_LIST_HEAD(&mdsc->snap_empty); spin_lock_init(&mdsc->snap_empty_lock); mdsc->last_tid = 0; + mdsc->oldest_tid = 0; mdsc->request_tree = RB_ROOT; INIT_DELAYED_WORK(&mdsc->delayed_work, delayed_work); mdsc->last_renew_caps = jiffies; @@ -3402,7 +3437,8 @@ int ceph_mdsc_init(struct ceph_fs_client *fsc) spin_lock_init(&mdsc->cap_delay_lock); INIT_LIST_HEAD(&mdsc->snap_flush_list); spin_lock_init(&mdsc->snap_flush_lock); - mdsc->cap_flush_seq = 0; + mdsc->last_cap_flush_tid = 1; + mdsc->cap_flush_tree = RB_ROOT; INIT_LIST_HEAD(&mdsc->cap_dirty); INIT_LIST_HEAD(&mdsc->cap_dirty_migrating); mdsc->num_cap_flushing = 0; @@ -3414,6 +3450,9 @@ int ceph_mdsc_init(struct ceph_fs_client *fsc) ceph_caps_init(mdsc); ceph_adjust_min_caps(mdsc, fsc->min_caps); + init_rwsem(&mdsc->pool_perm_rwsem); + mdsc->pool_perm_tree = RB_ROOT; + return 0; } @@ -3423,8 +3462,8 @@ int ceph_mdsc_init(struct ceph_fs_client *fsc) */ static void wait_requests(struct ceph_mds_client *mdsc) { + struct ceph_options *opts = mdsc->fsc->client->options; struct ceph_mds_request *req; - struct ceph_fs_client *fsc = mdsc->fsc; mutex_lock(&mdsc->mutex); if (__get_oldest_req(mdsc)) { @@ -3432,7 +3471,7 @@ static void wait_requests(struct ceph_mds_client *mdsc) dout("wait_requests waiting for requests\n"); wait_for_completion_timeout(&mdsc->safe_umount_waiters, - fsc->client->options->mount_timeout * HZ); + ceph_timeout_jiffies(opts->mount_timeout)); /* tear down remaining requests */ mutex_lock(&mdsc->mutex); @@ -3485,7 +3524,8 @@ restart: nextreq = rb_entry(n, struct ceph_mds_request, r_node); else nextreq = NULL; - if ((req->r_op & CEPH_MDS_OP_WRITE)) { + if (req->r_op != CEPH_MDS_OP_SETFILELOCK && + (req->r_op & CEPH_MDS_OP_WRITE)) { /* write op */ ceph_mdsc_get_request(req); if (nextreq) @@ -3513,7 +3553,7 @@ restart: void ceph_mdsc_sync(struct ceph_mds_client *mdsc) { - u64 want_tid, want_flush; + u64 want_tid, want_flush, want_snap; if (mdsc->fsc->mount_state == CEPH_MOUNT_SHUTDOWN) return; @@ -3525,13 +3565,18 @@ void ceph_mdsc_sync(struct ceph_mds_client *mdsc) ceph_flush_dirty_caps(mdsc); spin_lock(&mdsc->cap_dirty_lock); - want_flush = mdsc->cap_flush_seq; + want_flush = mdsc->last_cap_flush_tid; spin_unlock(&mdsc->cap_dirty_lock); - dout("sync want tid %lld flush_seq %lld\n", want_tid, want_flush); + down_read(&mdsc->snap_rwsem); + want_snap = mdsc->last_snap_seq; + up_read(&mdsc->snap_rwsem); + + dout("sync want tid %lld flush_seq %lld snap_seq %lld\n", + want_tid, want_flush, want_snap); wait_unsafe_requests(mdsc, want_tid); - wait_caps_flush(mdsc, want_flush); + wait_caps_flush(mdsc, want_flush, want_snap); } /* @@ -3549,10 +3594,9 @@ static bool done_closing_sessions(struct ceph_mds_client *mdsc) */ void ceph_mdsc_close_sessions(struct ceph_mds_client *mdsc) { + struct ceph_options *opts = mdsc->fsc->client->options; struct ceph_mds_session *session; int i; - struct ceph_fs_client *fsc = mdsc->fsc; - unsigned long timeout = fsc->client->options->mount_timeout * HZ; dout("close_sessions\n"); @@ -3573,7 +3617,7 @@ void ceph_mdsc_close_sessions(struct ceph_mds_client *mdsc) dout("waiting for sessions to close\n"); wait_event_timeout(mdsc->session_close_wq, done_closing_sessions(mdsc), - timeout); + ceph_timeout_jiffies(opts->mount_timeout)); /* tear down remaining sessions */ mutex_lock(&mdsc->mutex); @@ -3607,6 +3651,7 @@ static void ceph_mdsc_stop(struct ceph_mds_client *mdsc) ceph_mdsmap_destroy(mdsc->mdsmap); kfree(mdsc->sessions); ceph_caps_finalize(mdsc); + ceph_pool_perm_destroy(mdsc); } void ceph_mdsc_destroy(struct ceph_fs_client *fsc) diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 1875b5d98..762757e6c 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -139,7 +139,6 @@ struct ceph_mds_session { int s_cap_reconnect; int s_readonly; struct list_head s_cap_releases; /* waiting cap_release messages */ - struct list_head s_cap_releases_done; /* ready to send */ struct ceph_cap *s_cap_iterator; /* protected by mutex */ @@ -228,7 +227,7 @@ struct ceph_mds_request { int r_err; bool r_aborted; - unsigned long r_timeout; /* optional. jiffies */ + unsigned long r_timeout; /* optional. jiffies, 0 is "wait forever" */ unsigned long r_started; /* start time to measure timeout against */ unsigned long r_request_started; /* start time for mds request only, used to measure lease durations */ @@ -254,12 +253,21 @@ struct ceph_mds_request { bool r_got_unsafe, r_got_safe, r_got_result; bool r_did_prepopulate; + long long r_dir_release_cnt; + long long r_dir_ordered_cnt; + int r_readdir_cache_idx; u32 r_readdir_offset; struct ceph_cap_reservation r_caps_reservation; int r_num_caps; }; +struct ceph_pool_perm { + struct rb_node node; + u32 pool; + int perm; +}; + /* * mds client state */ @@ -284,12 +292,15 @@ struct ceph_mds_client { * references (implying they contain no inodes with caps) that * should be destroyed. */ + u64 last_snap_seq; struct rw_semaphore snap_rwsem; struct rb_root snap_realms; struct list_head snap_empty; spinlock_t snap_empty_lock; /* protect snap_empty */ u64 last_tid; /* most recent mds request */ + u64 oldest_tid; /* oldest incomplete mds request, + excluding setfilelock requests */ struct rb_root request_tree; /* pending mds requests */ struct delayed_work delayed_work; /* delayed work */ unsigned long last_renew_caps; /* last time we renewed our caps */ @@ -298,7 +309,8 @@ struct ceph_mds_client { struct list_head snap_flush_list; /* cap_snaps ready to flush */ spinlock_t snap_flush_lock; - u64 cap_flush_seq; + u64 last_cap_flush_tid; + struct rb_root cap_flush_tree; struct list_head cap_dirty; /* inodes with dirty caps */ struct list_head cap_dirty_migrating; /* ...that are migration... */ int num_cap_flushing; /* # caps we are flushing */ @@ -328,6 +340,9 @@ struct ceph_mds_client { spinlock_t dentry_lru_lock; struct list_head dentry_lru; int num_dentry; + + struct rw_semaphore pool_perm_rwsem; + struct rb_root pool_perm_tree; }; extern const char *ceph_mds_op_name(int op); @@ -379,8 +394,6 @@ static inline void ceph_mdsc_put_request(struct ceph_mds_request *req) kref_put(&req->r_kref, ceph_mdsc_release_request); } -extern int ceph_add_cap_releases(struct ceph_mds_client *mdsc, - struct ceph_mds_session *session); extern void ceph_send_cap_releases(struct ceph_mds_client *mdsc, struct ceph_mds_session *session); diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c index a97e39f09..233d906ae 100644 --- a/fs/ceph/snap.c +++ b/fs/ceph/snap.c @@ -296,7 +296,7 @@ static int cmpu64_rev(const void *a, const void *b) } -static struct ceph_snap_context *empty_snapc; +struct ceph_snap_context *ceph_empty_snapc; /* * build the snap context for a given realm. @@ -338,9 +338,9 @@ static int build_snap_context(struct ceph_snap_realm *realm) return 0; } - if (num == 0 && realm->seq == empty_snapc->seq) { - ceph_get_snap_context(empty_snapc); - snapc = empty_snapc; + if (num == 0 && realm->seq == ceph_empty_snapc->seq) { + ceph_get_snap_context(ceph_empty_snapc); + snapc = ceph_empty_snapc; goto done; } @@ -436,6 +436,14 @@ static int dup_array(u64 **dst, __le64 *src, u32 num) return 0; } +static bool has_new_snaps(struct ceph_snap_context *o, + struct ceph_snap_context *n) +{ + if (n->num_snaps == 0) + return false; + /* snaps are in descending order */ + return n->snaps[0] > o->seq; +} /* * When a snapshot is applied, the size/mtime inode metadata is queued @@ -455,6 +463,7 @@ void ceph_queue_cap_snap(struct ceph_inode_info *ci) { struct inode *inode = &ci->vfs_inode; struct ceph_cap_snap *capsnap; + struct ceph_snap_context *old_snapc, *new_snapc; int used, dirty; capsnap = kzalloc(sizeof(*capsnap), GFP_NOFS); @@ -467,6 +476,9 @@ void ceph_queue_cap_snap(struct ceph_inode_info *ci) used = __ceph_caps_used(ci); dirty = __ceph_caps_dirty(ci); + old_snapc = ci->i_head_snapc; + new_snapc = ci->i_snap_realm->cached_context; + /* * If there is a write in progress, treat that as a dirty Fw, * even though it hasn't completed yet; by the time we finish @@ -481,76 +493,95 @@ void ceph_queue_cap_snap(struct ceph_inode_info *ci) writes in progress now were started before the previous cap_snap. lucky us. */ dout("queue_cap_snap %p already pending\n", inode); - kfree(capsnap); - } else if (ci->i_snap_realm->cached_context == empty_snapc) { - dout("queue_cap_snap %p empty snapc\n", inode); - kfree(capsnap); - } else if (dirty & (CEPH_CAP_AUTH_EXCL|CEPH_CAP_XATTR_EXCL| - CEPH_CAP_FILE_EXCL|CEPH_CAP_FILE_WR)) { - struct ceph_snap_context *snapc = ci->i_head_snapc; - - /* - * if we are a sync write, we may need to go to the snaprealm - * to get the current snapc. - */ - if (!snapc) - snapc = ci->i_snap_realm->cached_context; + goto update_snapc; + } + if (ci->i_wrbuffer_ref_head == 0 && + !(dirty & (CEPH_CAP_ANY_EXCL|CEPH_CAP_FILE_WR))) { + dout("queue_cap_snap %p nothing dirty|writing\n", inode); + goto update_snapc; + } - dout("queue_cap_snap %p cap_snap %p queuing under %p %s\n", - inode, capsnap, snapc, ceph_cap_string(dirty)); - ihold(inode); + BUG_ON(!old_snapc); - atomic_set(&capsnap->nref, 1); - capsnap->ci = ci; - INIT_LIST_HEAD(&capsnap->ci_item); - INIT_LIST_HEAD(&capsnap->flushing_item); - - capsnap->follows = snapc->seq; - capsnap->issued = __ceph_caps_issued(ci, NULL); - capsnap->dirty = dirty; - - capsnap->mode = inode->i_mode; - capsnap->uid = inode->i_uid; - capsnap->gid = inode->i_gid; - - if (dirty & CEPH_CAP_XATTR_EXCL) { - __ceph_build_xattrs_blob(ci); - capsnap->xattr_blob = - ceph_buffer_get(ci->i_xattrs.blob); - capsnap->xattr_version = ci->i_xattrs.version; - } else { - capsnap->xattr_blob = NULL; - capsnap->xattr_version = 0; + /* + * There is no need to send FLUSHSNAP message to MDS if there is + * no new snapshot. But when there is dirty pages or on-going + * writes, we still need to create cap_snap. cap_snap is needed + * by the write path and page writeback path. + * + * also see ceph_try_drop_cap_snap() + */ + if (has_new_snaps(old_snapc, new_snapc)) { + if (dirty & (CEPH_CAP_ANY_EXCL|CEPH_CAP_FILE_WR)) + capsnap->need_flush = true; + } else { + if (!(used & CEPH_CAP_FILE_WR) && + ci->i_wrbuffer_ref_head == 0) { + dout("queue_cap_snap %p " + "no new_snap|dirty_page|writing\n", inode); + goto update_snapc; } + } - capsnap->inline_data = ci->i_inline_version != CEPH_INLINE_NONE; - - /* dirty page count moved from _head to this cap_snap; - all subsequent writes page dirties occur _after_ this - snapshot. */ - capsnap->dirty_pages = ci->i_wrbuffer_ref_head; - ci->i_wrbuffer_ref_head = 0; - capsnap->context = snapc; - ci->i_head_snapc = - ceph_get_snap_context(ci->i_snap_realm->cached_context); - dout(" new snapc is %p\n", ci->i_head_snapc); - list_add_tail(&capsnap->ci_item, &ci->i_cap_snaps); - - if (used & CEPH_CAP_FILE_WR) { - dout("queue_cap_snap %p cap_snap %p snapc %p" - " seq %llu used WR, now pending\n", inode, - capsnap, snapc, snapc->seq); - capsnap->writing = 1; - } else { - /* note mtime, size NOW. */ - __ceph_finish_cap_snap(ci, capsnap); - } + dout("queue_cap_snap %p cap_snap %p queuing under %p %s %s\n", + inode, capsnap, old_snapc, ceph_cap_string(dirty), + capsnap->need_flush ? "" : "no_flush"); + ihold(inode); + + atomic_set(&capsnap->nref, 1); + capsnap->ci = ci; + INIT_LIST_HEAD(&capsnap->ci_item); + INIT_LIST_HEAD(&capsnap->flushing_item); + + capsnap->follows = old_snapc->seq; + capsnap->issued = __ceph_caps_issued(ci, NULL); + capsnap->dirty = dirty; + + capsnap->mode = inode->i_mode; + capsnap->uid = inode->i_uid; + capsnap->gid = inode->i_gid; + + if (dirty & CEPH_CAP_XATTR_EXCL) { + __ceph_build_xattrs_blob(ci); + capsnap->xattr_blob = + ceph_buffer_get(ci->i_xattrs.blob); + capsnap->xattr_version = ci->i_xattrs.version; } else { - dout("queue_cap_snap %p nothing dirty|writing\n", inode); - kfree(capsnap); + capsnap->xattr_blob = NULL; + capsnap->xattr_version = 0; } + capsnap->inline_data = ci->i_inline_version != CEPH_INLINE_NONE; + + /* dirty page count moved from _head to this cap_snap; + all subsequent writes page dirties occur _after_ this + snapshot. */ + capsnap->dirty_pages = ci->i_wrbuffer_ref_head; + ci->i_wrbuffer_ref_head = 0; + capsnap->context = old_snapc; + list_add_tail(&capsnap->ci_item, &ci->i_cap_snaps); + old_snapc = NULL; + + if (used & CEPH_CAP_FILE_WR) { + dout("queue_cap_snap %p cap_snap %p snapc %p" + " seq %llu used WR, now pending\n", inode, + capsnap, old_snapc, old_snapc->seq); + capsnap->writing = 1; + } else { + /* note mtime, size NOW. */ + __ceph_finish_cap_snap(ci, capsnap); + } + capsnap = NULL; + +update_snapc: + if (ci->i_head_snapc) { + ci->i_head_snapc = ceph_get_snap_context(new_snapc); + dout(" new snapc is %p\n", new_snapc); + } spin_unlock(&ci->i_ceph_lock); + + kfree(capsnap); + ceph_put_snap_context(old_snapc); } /* @@ -699,6 +730,8 @@ more: /* queue realm for cap_snap creation */ list_add(&realm->dirty_item, &dirty_realms); + if (realm->seq > mdsc->last_snap_seq) + mdsc->last_snap_seq = realm->seq; invalidate = 1; } else if (!realm->cached_context) { @@ -964,14 +997,14 @@ out: int __init ceph_snap_init(void) { - empty_snapc = ceph_create_snap_context(0, GFP_NOFS); - if (!empty_snapc) + ceph_empty_snapc = ceph_create_snap_context(0, GFP_NOFS); + if (!ceph_empty_snapc) return -ENOMEM; - empty_snapc->seq = 1; + ceph_empty_snapc->seq = 1; return 0; } void ceph_snap_exit(void) { - ceph_put_snap_context(empty_snapc); + ceph_put_snap_context(ceph_empty_snapc); } diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 4e9905374..d1c833c32 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -134,10 +134,12 @@ enum { Opt_noino32, Opt_fscache, Opt_nofscache, + Opt_poolperm, + Opt_nopoolperm, #ifdef CONFIG_CEPH_FS_POSIX_ACL Opt_acl, #endif - Opt_noacl + Opt_noacl, }; static match_table_t fsopt_tokens = { @@ -165,6 +167,8 @@ static match_table_t fsopt_tokens = { {Opt_noino32, "noino32"}, {Opt_fscache, "fsc"}, {Opt_nofscache, "nofsc"}, + {Opt_poolperm, "poolperm"}, + {Opt_nopoolperm, "nopoolperm"}, #ifdef CONFIG_CEPH_FS_POSIX_ACL {Opt_acl, "acl"}, #endif @@ -268,6 +272,13 @@ static int parse_fsopt_token(char *c, void *private) case Opt_nofscache: fsopt->flags &= ~CEPH_MOUNT_OPT_FSCACHE; break; + case Opt_poolperm: + fsopt->flags &= ~CEPH_MOUNT_OPT_NOPOOLPERM; + printk ("pool perm"); + break; + case Opt_nopoolperm: + fsopt->flags |= CEPH_MOUNT_OPT_NOPOOLPERM; + break; #ifdef CONFIG_CEPH_FS_POSIX_ACL case Opt_acl: fsopt->sb_flags |= MS_POSIXACL; @@ -436,6 +447,8 @@ static int ceph_show_options(struct seq_file *m, struct dentry *root) seq_puts(m, ",nodcache"); if (fsopt->flags & CEPH_MOUNT_OPT_FSCACHE) seq_puts(m, ",fsc"); + if (fsopt->flags & CEPH_MOUNT_OPT_NOPOOLPERM) + seq_puts(m, ",nopoolperm"); #ifdef CONFIG_CEPH_FS_POSIX_ACL if (fsopt->sb_flags & MS_POSIXACL) @@ -609,6 +622,7 @@ static void destroy_fs_client(struct ceph_fs_client *fsc) */ struct kmem_cache *ceph_inode_cachep; struct kmem_cache *ceph_cap_cachep; +struct kmem_cache *ceph_cap_flush_cachep; struct kmem_cache *ceph_dentry_cachep; struct kmem_cache *ceph_file_cachep; @@ -634,6 +648,10 @@ static int __init init_caches(void) SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD); if (ceph_cap_cachep == NULL) goto bad_cap; + ceph_cap_flush_cachep = KMEM_CACHE(ceph_cap_flush, + SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD); + if (ceph_cap_flush_cachep == NULL) + goto bad_cap_flush; ceph_dentry_cachep = KMEM_CACHE(ceph_dentry_info, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD); @@ -652,6 +670,8 @@ static int __init init_caches(void) bad_file: kmem_cache_destroy(ceph_dentry_cachep); bad_dentry: + kmem_cache_destroy(ceph_cap_flush_cachep); +bad_cap_flush: kmem_cache_destroy(ceph_cap_cachep); bad_cap: kmem_cache_destroy(ceph_inode_cachep); @@ -668,6 +688,7 @@ static void destroy_caches(void) kmem_cache_destroy(ceph_inode_cachep); kmem_cache_destroy(ceph_cap_cachep); + kmem_cache_destroy(ceph_cap_flush_cachep); kmem_cache_destroy(ceph_dentry_cachep); kmem_cache_destroy(ceph_file_cachep); @@ -729,7 +750,7 @@ static struct dentry *open_root_dentry(struct ceph_fs_client *fsc, req->r_ino1.ino = CEPH_INO_ROOT; req->r_ino1.snap = CEPH_NOSNAP; req->r_started = started; - req->r_timeout = fsc->client->options->mount_timeout * HZ; + req->r_timeout = fsc->client->options->mount_timeout; req->r_args.getattr.mask = cpu_to_le32(CEPH_STAT_CAP_INODE); req->r_num_caps = 2; err = ceph_mdsc_do_request(mdsc, NULL, req); diff --git a/fs/ceph/super.h b/fs/ceph/super.h index fa20e1318..2f2460d23 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -35,6 +35,7 @@ #define CEPH_MOUNT_OPT_INO32 (1<<8) /* 32 bit inos */ #define CEPH_MOUNT_OPT_DCACHE (1<<9) /* use dcache for readdir etc */ #define CEPH_MOUNT_OPT_FSCACHE (1<<10) /* use fscache */ +#define CEPH_MOUNT_OPT_NOPOOLPERM (1<<11) /* no pool permission check */ #define CEPH_MOUNT_OPT_DEFAULT (CEPH_MOUNT_OPT_RBYTES | \ CEPH_MOUNT_OPT_DCACHE) @@ -121,11 +122,21 @@ struct ceph_cap { struct rb_node ci_node; /* per-ci cap tree */ struct ceph_mds_session *session; struct list_head session_caps; /* per-session caplist */ - int mds; u64 cap_id; /* unique cap id (mds provided) */ - int issued; /* latest, from the mds */ - int implemented; /* implemented superset of issued (for revocation) */ - int mds_wanted; + union { + /* in-use caps */ + struct { + int issued; /* latest, from the mds */ + int implemented; /* implemented superset of + issued (for revocation) */ + int mds, mds_wanted; + }; + /* caps to release */ + struct { + u64 cap_ino; + int queue_release; + }; + }; u32 seq, issue_seq, mseq; u32 cap_gen; /* active/stale cycle */ unsigned long last_used; @@ -163,6 +174,7 @@ struct ceph_cap_snap { int writing; /* a sync write is still in progress */ int dirty_pages; /* dirty pages awaiting writeback */ bool inline_data; + bool need_flush; }; static inline void ceph_put_cap_snap(struct ceph_cap_snap *capsnap) @@ -174,6 +186,16 @@ static inline void ceph_put_cap_snap(struct ceph_cap_snap *capsnap) } } +struct ceph_cap_flush { + u64 tid; + int caps; + struct rb_node g_node; // global + union { + struct rb_node i_node; // inode + struct list_head list; + }; +}; + /* * The frag tree describes how a directory is fragmented, potentially across * multiple metadata servers. It is also used to indicate points where @@ -259,9 +281,9 @@ struct ceph_inode_info { u32 i_time_warp_seq; unsigned i_ceph_flags; - int i_ordered_count; - atomic_t i_release_count; - atomic_t i_complete_count; + atomic64_t i_release_count; + atomic64_t i_ordered_count; + atomic64_t i_complete_seq[2]; struct ceph_dir_layout i_dir_layout; struct ceph_file_layout i_layout; @@ -283,11 +305,11 @@ struct ceph_inode_info { struct ceph_cap *i_auth_cap; /* authoritative cap, if any */ unsigned i_dirty_caps, i_flushing_caps; /* mask of dirtied fields */ struct list_head i_dirty_item, i_flushing_item; - u64 i_cap_flush_seq; /* we need to track cap writeback on a per-cap-bit basis, to allow * overlapping, pipelined cap flushes to the mds. we can probably * reduce the tid to 8 bits if we're concerned about inode size. */ - u16 i_cap_flush_last_tid, i_cap_flush_tid[CEPH_CAP_BITS]; + struct ceph_cap_flush *i_prealloc_cap_flush; + struct rb_root i_cap_flush_tree; wait_queue_head_t i_cap_wq; /* threads waiting on a capability */ unsigned long i_hold_caps_min; /* jiffies */ unsigned long i_hold_caps_max; /* jiffies */ @@ -438,36 +460,46 @@ static inline struct inode *ceph_find_inode(struct super_block *sb, /* * Ceph inode. */ -#define CEPH_I_DIR_ORDERED 1 /* dentries in dir are ordered */ -#define CEPH_I_NODELAY 4 /* do not delay cap release */ -#define CEPH_I_FLUSH 8 /* do not delay flush of dirty metadata */ -#define CEPH_I_NOFLUSH 16 /* do not flush dirty caps */ +#define CEPH_I_DIR_ORDERED (1 << 0) /* dentries in dir are ordered */ +#define CEPH_I_NODELAY (1 << 1) /* do not delay cap release */ +#define CEPH_I_FLUSH (1 << 2) /* do not delay flush of dirty metadata */ +#define CEPH_I_NOFLUSH (1 << 3) /* do not flush dirty caps */ +#define CEPH_I_POOL_PERM (1 << 4) /* pool rd/wr bits are valid */ +#define CEPH_I_POOL_RD (1 << 5) /* can read from pool */ +#define CEPH_I_POOL_WR (1 << 6) /* can write to pool */ + static inline void __ceph_dir_set_complete(struct ceph_inode_info *ci, - int release_count, int ordered_count) + long long release_count, + long long ordered_count) { - atomic_set(&ci->i_complete_count, release_count); - if (ci->i_ordered_count == ordered_count) - ci->i_ceph_flags |= CEPH_I_DIR_ORDERED; - else - ci->i_ceph_flags &= ~CEPH_I_DIR_ORDERED; + smp_mb__before_atomic(); + atomic64_set(&ci->i_complete_seq[0], release_count); + atomic64_set(&ci->i_complete_seq[1], ordered_count); } static inline void __ceph_dir_clear_complete(struct ceph_inode_info *ci) { - atomic_inc(&ci->i_release_count); + atomic64_inc(&ci->i_release_count); +} + +static inline void __ceph_dir_clear_ordered(struct ceph_inode_info *ci) +{ + atomic64_inc(&ci->i_ordered_count); } static inline bool __ceph_dir_is_complete(struct ceph_inode_info *ci) { - return atomic_read(&ci->i_complete_count) == - atomic_read(&ci->i_release_count); + return atomic64_read(&ci->i_complete_seq[0]) == + atomic64_read(&ci->i_release_count); } static inline bool __ceph_dir_is_complete_ordered(struct ceph_inode_info *ci) { - return __ceph_dir_is_complete(ci) && - (ci->i_ceph_flags & CEPH_I_DIR_ORDERED); + return atomic64_read(&ci->i_complete_seq[0]) == + atomic64_read(&ci->i_release_count) && + atomic64_read(&ci->i_complete_seq[1]) == + atomic64_read(&ci->i_ordered_count); } static inline void ceph_dir_clear_complete(struct inode *inode) @@ -477,20 +509,13 @@ static inline void ceph_dir_clear_complete(struct inode *inode) static inline void ceph_dir_clear_ordered(struct inode *inode) { - struct ceph_inode_info *ci = ceph_inode(inode); - spin_lock(&ci->i_ceph_lock); - ci->i_ordered_count++; - ci->i_ceph_flags &= ~CEPH_I_DIR_ORDERED; - spin_unlock(&ci->i_ceph_lock); + __ceph_dir_clear_ordered(ceph_inode(inode)); } static inline bool ceph_dir_is_complete_ordered(struct inode *inode) { - struct ceph_inode_info *ci = ceph_inode(inode); - bool ret; - spin_lock(&ci->i_ceph_lock); - ret = __ceph_dir_is_complete_ordered(ci); - spin_unlock(&ci->i_ceph_lock); + bool ret = __ceph_dir_is_complete_ordered(ceph_inode(inode)); + smp_rmb(); return ret; } @@ -552,7 +577,10 @@ static inline int __ceph_caps_dirty(struct ceph_inode_info *ci) { return ci->i_dirty_caps | ci->i_flushing_caps; } -extern int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask); +extern struct ceph_cap_flush *ceph_alloc_cap_flush(void); +extern void ceph_free_cap_flush(struct ceph_cap_flush *cf); +extern int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask, + struct ceph_cap_flush **pcf); extern int __ceph_caps_revoking_other(struct ceph_inode_info *ci, struct ceph_cap *ocap, int mask); @@ -606,16 +634,20 @@ struct ceph_file_info { unsigned offset; /* offset of last chunk, adjusted for . and .. */ unsigned next_offset; /* offset of next chunk (last_name's + 1) */ char *last_name; /* last entry in previous chunk */ - struct dentry *dentry; /* next dentry (for dcache readdir) */ - int dir_release_count; - int dir_ordered_count; + long long dir_release_count; + long long dir_ordered_count; + int readdir_cache_idx; /* used for -o dirstat read() on directory thing */ char *dir_info; int dir_info_len; }; - +struct ceph_readdir_cache_control { + struct page *page; + struct dentry **dentries; + int index; +}; /* * A "snap realm" describes a subset of the file hierarchy sharing @@ -687,6 +719,7 @@ static inline int default_congestion_kb(void) /* snap.c */ +extern struct ceph_snap_context *ceph_empty_snapc; struct ceph_snap_realm *ceph_lookup_snap_realm(struct ceph_mds_client *mdsc, u64 ino); extern void ceph_get_snap_realm(struct ceph_mds_client *mdsc, @@ -713,8 +746,8 @@ extern void ceph_snap_exit(void); static inline bool __ceph_have_pending_cap_snap(struct ceph_inode_info *ci) { return !list_empty(&ci->i_cap_snaps) && - list_entry(ci->i_cap_snaps.prev, struct ceph_cap_snap, - ci_item)->writing; + list_last_entry(&ci->i_cap_snaps, struct ceph_cap_snap, + ci_item)->writing; } /* inode.c */ @@ -838,12 +871,12 @@ extern void ceph_put_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap); extern int ceph_is_any_caps(struct inode *inode); -extern void __queue_cap_release(struct ceph_mds_session *session, u64 ino, - u64 cap_id, u32 migrate_seq, u32 issue_seq); extern void ceph_queue_caps_release(struct inode *inode); extern int ceph_write_inode(struct inode *inode, struct writeback_control *wbc); extern int ceph_fsync(struct file *file, loff_t start, loff_t end, int datasync); +extern void ceph_early_kick_flushing_caps(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session); extern void ceph_kick_flushing_caps(struct ceph_mds_client *mdsc, struct ceph_mds_session *session); extern struct ceph_cap *ceph_get_cap_for_mds(struct ceph_inode_info *ci, @@ -879,6 +912,9 @@ extern void ceph_put_fmode(struct ceph_inode_info *ci, int mode); /* addr.c */ extern const struct address_space_operations ceph_aops; extern int ceph_mmap(struct file *file, struct vm_area_struct *vma); +extern int ceph_uninline_data(struct file *filp, struct page *locked_page); +extern int ceph_pool_perm_check(struct ceph_inode_info *ci, int need); +extern void ceph_pool_perm_destroy(struct ceph_mds_client* mdsc); /* file.c */ extern const struct file_operations ceph_file_fops; @@ -890,7 +926,6 @@ extern int ceph_atomic_open(struct inode *dir, struct dentry *dentry, extern int ceph_release(struct inode *inode, struct file *filp); extern void ceph_fill_inline_data(struct inode *inode, struct page *locked_page, char *data, size_t len); -int ceph_uninline_data(struct file *filp, struct page *locked_page); /* dir.c */ extern const struct file_operations ceph_dir_fops; extern const struct file_operations ceph_snapdir_fops; @@ -911,6 +946,7 @@ extern void ceph_dentry_lru_del(struct dentry *dn); extern void ceph_invalidate_dentry_lease(struct dentry *dentry); extern unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn); extern struct inode *ceph_get_dentry_parent_inode(struct dentry *dentry); +extern void ceph_readdir_cache_release(struct ceph_readdir_cache_control *ctl); /* * our d_ops vary depending on whether the inode is live, diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index cd7ffad40..819163d83 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -911,6 +911,8 @@ int __ceph_setxattr(struct dentry *dentry, const char *name, struct inode *inode = d_inode(dentry); struct ceph_vxattr *vxattr; struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_mds_client *mdsc = ceph_sb_to_client(dentry->d_sb)->mdsc; + struct ceph_cap_flush *prealloc_cf = NULL; int issued; int err; int dirty = 0; @@ -920,6 +922,7 @@ int __ceph_setxattr(struct dentry *dentry, const char *name, char *newval = NULL; struct ceph_inode_xattr *xattr = NULL; int required_blob_size; + bool lock_snap_rwsem = false; if (!ceph_is_valid_xattr(name)) return -EOPNOTSUPP; @@ -948,12 +951,27 @@ int __ceph_setxattr(struct dentry *dentry, const char *name, if (!xattr) goto out; + prealloc_cf = ceph_alloc_cap_flush(); + if (!prealloc_cf) + goto out; + spin_lock(&ci->i_ceph_lock); retry: issued = __ceph_caps_issued(ci, NULL); - dout("setxattr %p issued %s\n", inode, ceph_cap_string(issued)); if (ci->i_xattrs.version == 0 || !(issued & CEPH_CAP_XATTR_EXCL)) goto do_sync; + + if (!lock_snap_rwsem && !ci->i_head_snapc) { + lock_snap_rwsem = true; + if (!down_read_trylock(&mdsc->snap_rwsem)) { + spin_unlock(&ci->i_ceph_lock); + down_read(&mdsc->snap_rwsem); + spin_lock(&ci->i_ceph_lock); + goto retry; + } + } + + dout("setxattr %p issued %s\n", inode, ceph_cap_string(issued)); __build_xattrs(inode); required_blob_size = __get_required_blob_size(ci, name_len, val_len); @@ -966,7 +984,7 @@ retry: dout(" preaallocating new blob size=%d\n", required_blob_size); blob = ceph_buffer_new(required_blob_size, GFP_NOFS); if (!blob) - goto out; + goto do_sync_unlocked; spin_lock(&ci->i_ceph_lock); if (ci->i_xattrs.prealloc_blob) ceph_buffer_put(ci->i_xattrs.prealloc_blob); @@ -978,21 +996,28 @@ retry: flags, value ? 1 : -1, &xattr); if (!err) { - dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_XATTR_EXCL); + dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_XATTR_EXCL, + &prealloc_cf); ci->i_xattrs.dirty = true; inode->i_ctime = CURRENT_TIME; } spin_unlock(&ci->i_ceph_lock); + if (lock_snap_rwsem) + up_read(&mdsc->snap_rwsem); if (dirty) __mark_inode_dirty(inode, dirty); + ceph_free_cap_flush(prealloc_cf); return err; do_sync: spin_unlock(&ci->i_ceph_lock); do_sync_unlocked: + if (lock_snap_rwsem) + up_read(&mdsc->snap_rwsem); err = ceph_sync_setxattr(dentry, name, value, size, flags); out: + ceph_free_cap_flush(prealloc_cf); kfree(newname); kfree(newval); kfree(xattr); @@ -1044,10 +1069,13 @@ int __ceph_removexattr(struct dentry *dentry, const char *name) struct inode *inode = d_inode(dentry); struct ceph_vxattr *vxattr; struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_mds_client *mdsc = ceph_sb_to_client(dentry->d_sb)->mdsc; + struct ceph_cap_flush *prealloc_cf = NULL; int issued; int err; int required_blob_size; int dirty; + bool lock_snap_rwsem = false; if (!ceph_is_valid_xattr(name)) return -EOPNOTSUPP; @@ -1060,14 +1088,29 @@ int __ceph_removexattr(struct dentry *dentry, const char *name) if (!strncmp(name, XATTR_CEPH_PREFIX, XATTR_CEPH_PREFIX_LEN)) goto do_sync_unlocked; + prealloc_cf = ceph_alloc_cap_flush(); + if (!prealloc_cf) + return -ENOMEM; + err = -ENOMEM; spin_lock(&ci->i_ceph_lock); retry: issued = __ceph_caps_issued(ci, NULL); - dout("removexattr %p issued %s\n", inode, ceph_cap_string(issued)); - if (ci->i_xattrs.version == 0 || !(issued & CEPH_CAP_XATTR_EXCL)) goto do_sync; + + if (!lock_snap_rwsem && !ci->i_head_snapc) { + lock_snap_rwsem = true; + if (!down_read_trylock(&mdsc->snap_rwsem)) { + spin_unlock(&ci->i_ceph_lock); + down_read(&mdsc->snap_rwsem); + spin_lock(&ci->i_ceph_lock); + goto retry; + } + } + + dout("removexattr %p issued %s\n", inode, ceph_cap_string(issued)); + __build_xattrs(inode); required_blob_size = __get_required_blob_size(ci, 0, 0); @@ -1080,7 +1123,7 @@ retry: dout(" preaallocating new blob size=%d\n", required_blob_size); blob = ceph_buffer_new(required_blob_size, GFP_NOFS); if (!blob) - goto out; + goto do_sync_unlocked; spin_lock(&ci->i_ceph_lock); if (ci->i_xattrs.prealloc_blob) ceph_buffer_put(ci->i_xattrs.prealloc_blob); @@ -1090,18 +1133,24 @@ retry: err = __remove_xattr_by_name(ceph_inode(inode), name); - dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_XATTR_EXCL); + dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_XATTR_EXCL, + &prealloc_cf); ci->i_xattrs.dirty = true; inode->i_ctime = CURRENT_TIME; spin_unlock(&ci->i_ceph_lock); + if (lock_snap_rwsem) + up_read(&mdsc->snap_rwsem); if (dirty) __mark_inode_dirty(inode, dirty); + ceph_free_cap_flush(prealloc_cf); return err; do_sync: spin_unlock(&ci->i_ceph_lock); do_sync_unlocked: + if (lock_snap_rwsem) + up_read(&mdsc->snap_rwsem); + ceph_free_cap_flush(prealloc_cf); err = ceph_send_removexattr(dentry, name); -out: return err; } diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index a2172f3f6..e7b478b49 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -192,6 +192,15 @@ config CIFS_SMB2 options are also slightly simpler (compared to CIFS) due to protocol improvements. +config CIFS_SMB311 + bool "SMB3.1.1 network file system support (Experimental)" + depends on CIFS_SMB2 && INET + + help + This enables experimental support for the newest, SMB3.1.1, dialect. + This dialect includes improved security negotiation features. + If unsure, say N + config CIFS_FSCACHE bool "Provide CIFS client caching support" depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 252f5c158..a782b2290 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -120,7 +120,7 @@ extern struct vfsmount *cifs_dfs_d_automount(struct path *path); #endif /* Functions related to symlinks */ -extern void *cifs_follow_link(struct dentry *direntry, struct nameidata *nd); +extern const char *cifs_follow_link(struct dentry *direntry, void **cookie); extern int cifs_readlink(struct dentry *direntry, char __user *buffer, int buflen); extern int cifs_symlink(struct inode *inode, struct dentry *direntry, diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 22b289a3b..b406a32de 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -171,6 +171,10 @@ enum smb_version { Smb_21, Smb_30, Smb_302, +#ifdef CONFIG_CIFS_SMB311 + Smb_311, +#endif /* SMB311 */ + Smb_version_err }; struct mid_q_entry; @@ -368,6 +372,8 @@ struct smb_version_operations { void (*new_lease_key)(struct cifs_fid *); int (*generate_signingkey)(struct cifs_ses *); int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *); + int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon, + struct cifsFileInfo *src_file); int (*query_mf_symlink)(unsigned int, struct cifs_tcon *, struct cifs_sb_info *, const unsigned char *, char *, unsigned int *); @@ -386,6 +392,9 @@ struct smb_version_operations { int (*clone_range)(const unsigned int, struct cifsFileInfo *src_file, struct cifsFileInfo *target_file, u64 src_off, u64 len, u64 dest_off); + int (*duplicate_extents)(const unsigned int, struct cifsFileInfo *src, + struct cifsFileInfo *target_file, u64 src_off, u64 len, + u64 dest_off); int (*validate_negotiate)(const unsigned int, struct cifs_tcon *); ssize_t (*query_all_EAs)(const unsigned int, struct cifs_tcon *, const unsigned char *, const unsigned char *, char *, @@ -1617,4 +1626,8 @@ extern struct smb_version_values smb30_values; #define SMB302_VERSION_STRING "3.02" /*extern struct smb_version_operations smb302_operations;*/ /* not needed yet */ extern struct smb_version_values smb302_values; +#define SMB311_VERSION_STRING "3.1.1" +#define ALT_SMB311_VERSION_STRING "3.11" +extern struct smb_version_operations smb311_operations; +extern struct smb_version_values smb311_values; #endif /* _CIFS_GLOB_H */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 5f9822ac0..47b030da0 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -2255,6 +2255,8 @@ typedef struct { /* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */ +#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */ +#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */ #define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000 #define FILE_SUPPORTS_USN_JOURNAL 0x02000000 #define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000 @@ -2310,6 +2312,16 @@ typedef struct { /* data block encoding of response to level 263 QPathInfo */ char FileName[1]; } __attribute__((packed)) FILE_ALL_INFO; /* level 0x107 QPathInfo */ +typedef struct { + __le64 AllocationSize; + __le64 EndOfFile; /* size ie offset to first free byte in file */ + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __u16 Pad; +} __attribute__((packed)) FILE_STANDARD_INFO; /* level 0x102 QPathInfo */ + + /* defines for enumerating possible values of the Unix type field below */ #define UNIX_FILE 0 #define UNIX_DIR 1 diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index f26ffbfc6..672ef35c9 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -625,9 +625,8 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) server->negflavor = CIFS_NEGFLAVOR_UNENCAP; memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey, CIFS_CRYPTO_KEY_SIZE); - } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC || - server->capabilities & CAP_EXTENDED_SECURITY) && - (pSMBr->EncryptionKeyLength == 0)) { + } else if (pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC || + server->capabilities & CAP_EXTENDED_SECURITY) { server->negflavor = CIFS_NEGFLAVOR_EXTENDED; rc = decode_ext_sec_blob(ses, pSMBr); } else if (server->sec_mode & SECMODE_PW_ENCRYPT) { diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 8383d5ea4..773f4dc77 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -280,6 +280,11 @@ static const match_table_t cifs_smb_version_tokens = { { Smb_21, SMB21_VERSION_STRING }, { Smb_30, SMB30_VERSION_STRING }, { Smb_302, SMB302_VERSION_STRING }, +#ifdef CONFIG_CIFS_SMB311 + { Smb_311, SMB311_VERSION_STRING }, + { Smb_311, ALT_SMB311_VERSION_STRING }, +#endif /* SMB311 */ + { Smb_version_err, NULL } }; static int ip_connect(struct TCP_Server_Info *server); @@ -1133,6 +1138,12 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol) vol->ops = &smb30_operations; /* currently identical with 3.0 */ vol->vals = &smb302_values; break; +#ifdef CONFIG_CIFS_SMB311 + case Smb_311: + vol->ops = &smb311_operations; + vol->vals = &smb311_values; + break; +#endif /* SMB311 */ #endif default: cifs_dbg(VFS, "Unknown vers= option specified: %s\n", value); @@ -3461,6 +3472,8 @@ try_mount_again: else if (ses) cifs_put_smb_ses(ses); + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS; + free_xid(xid); } #endif diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c index 8b7898b76..49b8b6e41 100644 --- a/fs/cifs/ioctl.c +++ b/fs/cifs/ioctl.c @@ -31,12 +31,15 @@ #include "cifsproto.h" #include "cifs_debug.h" #include "cifsfs.h" +#include <linux/btrfs.h> #define CIFS_IOCTL_MAGIC 0xCF #define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int) +#define CIFS_IOC_SET_INTEGRITY _IO(CIFS_IOCTL_MAGIC, 4) static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file, - unsigned long srcfd, u64 off, u64 len, u64 destoff) + unsigned long srcfd, u64 off, u64 len, u64 destoff, + bool dup_extents) { int rc; struct cifsFileInfo *smb_file_target = dst_file->private_data; @@ -109,9 +112,14 @@ static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file, truncate_inode_pages_range(&target_inode->i_data, destoff, PAGE_CACHE_ALIGN(destoff + len)-1); - if (target_tcon->ses->server->ops->clone_range) + if (dup_extents && target_tcon->ses->server->ops->duplicate_extents) + rc = target_tcon->ses->server->ops->duplicate_extents(xid, + smb_file_src, smb_file_target, off, len, destoff); + else if (!dup_extents && target_tcon->ses->server->ops->clone_range) rc = target_tcon->ses->server->ops->clone_range(xid, smb_file_src, smb_file_target, off, len, destoff); + else + rc = -EOPNOTSUPP; /* force revalidate of size and timestamps of target file now that target is updated on the server */ @@ -205,7 +213,20 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) } break; case CIFS_IOC_COPYCHUNK_FILE: - rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0); + rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0, false); + break; + case BTRFS_IOC_CLONE: + rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0, true); + break; + case CIFS_IOC_SET_INTEGRITY: + if (pSMBFile == NULL) + break; + tcon = tlink_tcon(pSMBFile->tlink); + if (tcon->ses->server->ops->set_integrity) + rc = tcon->ses->server->ops->set_integrity(xid, + tcon, pSMBFile); + else + rc = -EOPNOTSUPP; break; default: cifs_dbg(FYI, "unsupported ioctl\n"); diff --git a/fs/cifs/link.c b/fs/cifs/link.c index e6c707cc6..e3548f73b 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -626,8 +626,8 @@ cifs_hl_exit: return rc; } -void * -cifs_follow_link(struct dentry *direntry, struct nameidata *nd) +const char * +cifs_follow_link(struct dentry *direntry, void **cookie) { struct inode *inode = d_inode(direntry); int rc = -ENOMEM; @@ -643,16 +643,18 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - tlink = NULL; - goto out; + free_xid(xid); + return ERR_CAST(tlink); } tcon = tlink_tcon(tlink); server = tcon->ses->server; full_path = build_path_from_dentry(direntry); - if (!full_path) - goto out; + if (!full_path) { + free_xid(xid); + cifs_put_tlink(tlink); + return ERR_PTR(-ENOMEM); + } cifs_dbg(FYI, "Full path: %s inode = 0x%p\n", full_path, inode); @@ -670,17 +672,13 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) &target_path, cifs_sb); kfree(full_path); -out: + free_xid(xid); + cifs_put_tlink(tlink); if (rc != 0) { kfree(target_path); - target_path = ERR_PTR(rc); + return ERR_PTR(rc); } - - free_xid(xid); - if (tlink) - cifs_put_tlink(tlink); - nd_set_link(nd, target_path); - return NULL; + return *cookie = target_path; } int diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 54daee5ad..df91bcf56 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -806,6 +806,53 @@ smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon, cfile->fid.volatile_fid, cfile->pid, &eof, false); } +#ifdef CONFIG_CIFS_SMB311 +static int +smb2_duplicate_extents(const unsigned int xid, + struct cifsFileInfo *srcfile, + struct cifsFileInfo *trgtfile, u64 src_off, + u64 len, u64 dest_off) +{ + int rc; + unsigned int ret_data_len; + char *retbuf = NULL; + struct duplicate_extents_to_file dup_ext_buf; + struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink); + + /* server fileays advertise duplicate extent support with this flag */ + if ((le32_to_cpu(tcon->fsAttrInfo.Attributes) & + FILE_SUPPORTS_BLOCK_REFCOUNTING) == 0) + return -EOPNOTSUPP; + + dup_ext_buf.VolatileFileHandle = srcfile->fid.volatile_fid; + dup_ext_buf.PersistentFileHandle = srcfile->fid.persistent_fid; + dup_ext_buf.SourceFileOffset = cpu_to_le64(src_off); + dup_ext_buf.TargetFileOffset = cpu_to_le64(dest_off); + dup_ext_buf.ByteCount = cpu_to_le64(len); + cifs_dbg(FYI, "duplicate extents: src off %lld dst off %lld len %lld", + src_off, dest_off, len); + + rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false); + if (rc) + goto duplicate_extents_out; + + rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, + trgtfile->fid.volatile_fid, + FSCTL_DUPLICATE_EXTENTS_TO_FILE, + true /* is_fsctl */, (char *)&dup_ext_buf, + sizeof(struct duplicate_extents_to_file), + (char **)&retbuf, + &ret_data_len); + + if (ret_data_len > 0) + cifs_dbg(FYI, "non-zero response length in duplicate extents"); + +duplicate_extents_out: + return rc; +} +#endif /* CONFIG_CIFS_SMB311 */ + + static int smb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, struct cifsFileInfo *cfile) @@ -815,6 +862,28 @@ smb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, } static int +smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile) +{ + struct fsctl_set_integrity_information_req integr_info; + char *retbuf = NULL; + unsigned int ret_data_len; + + integr_info.ChecksumAlgorithm = cpu_to_le16(CHECKSUM_TYPE_UNCHANGED); + integr_info.Flags = 0; + integr_info.Reserved = 0; + + return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + FSCTL_SET_INTEGRITY_INFORMATION, + true /* is_fsctl */, (char *)&integr_info, + sizeof(struct fsctl_set_integrity_information_req), + (char **)&retbuf, + &ret_data_len); + +} + +static int smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, const char *path, struct cifs_sb_info *cifs_sb, struct cifs_fid *fid, __u16 search_flags, @@ -1624,6 +1693,7 @@ struct smb_version_operations smb30_operations = { .new_lease_key = smb2_new_lease_key, .generate_signingkey = generate_smb3signingkey, .calc_signature = smb3_calc_signature, + .set_integrity = smb3_set_integrity, .is_read_op = smb21_is_read_op, .set_oplock_level = smb3_set_oplock_level, .create_lease_buf = smb3_create_lease_buf, @@ -1635,6 +1705,94 @@ struct smb_version_operations smb30_operations = { .fallocate = smb3_fallocate, }; +#ifdef CONFIG_CIFS_SMB311 +struct smb_version_operations smb311_operations = { + .compare_fids = smb2_compare_fids, + .setup_request = smb2_setup_request, + .setup_async_request = smb2_setup_async_request, + .check_receive = smb2_check_receive, + .add_credits = smb2_add_credits, + .set_credits = smb2_set_credits, + .get_credits_field = smb2_get_credits_field, + .get_credits = smb2_get_credits, + .wait_mtu_credits = smb2_wait_mtu_credits, + .get_next_mid = smb2_get_next_mid, + .read_data_offset = smb2_read_data_offset, + .read_data_length = smb2_read_data_length, + .map_error = map_smb2_to_linux_error, + .find_mid = smb2_find_mid, + .check_message = smb2_check_message, + .dump_detail = smb2_dump_detail, + .clear_stats = smb2_clear_stats, + .print_stats = smb2_print_stats, + .dump_share_caps = smb2_dump_share_caps, + .is_oplock_break = smb2_is_valid_oplock_break, + .downgrade_oplock = smb2_downgrade_oplock, + .need_neg = smb2_need_neg, + .negotiate = smb2_negotiate, + .negotiate_wsize = smb2_negotiate_wsize, + .negotiate_rsize = smb2_negotiate_rsize, + .sess_setup = SMB2_sess_setup, + .logoff = SMB2_logoff, + .tree_connect = SMB2_tcon, + .tree_disconnect = SMB2_tdis, + .qfs_tcon = smb3_qfs_tcon, + .is_path_accessible = smb2_is_path_accessible, + .can_echo = smb2_can_echo, + .echo = SMB2_echo, + .query_path_info = smb2_query_path_info, + .get_srv_inum = smb2_get_srv_inum, + .query_file_info = smb2_query_file_info, + .set_path_size = smb2_set_path_size, + .set_file_size = smb2_set_file_size, + .set_file_info = smb2_set_file_info, + .set_compression = smb2_set_compression, + .mkdir = smb2_mkdir, + .mkdir_setinfo = smb2_mkdir_setinfo, + .rmdir = smb2_rmdir, + .unlink = smb2_unlink, + .rename = smb2_rename_path, + .create_hardlink = smb2_create_hardlink, + .query_symlink = smb2_query_symlink, + .query_mf_symlink = smb3_query_mf_symlink, + .create_mf_symlink = smb3_create_mf_symlink, + .open = smb2_open_file, + .set_fid = smb2_set_fid, + .close = smb2_close_file, + .flush = smb2_flush_file, + .async_readv = smb2_async_readv, + .async_writev = smb2_async_writev, + .sync_read = smb2_sync_read, + .sync_write = smb2_sync_write, + .query_dir_first = smb2_query_dir_first, + .query_dir_next = smb2_query_dir_next, + .close_dir = smb2_close_dir, + .calc_smb_size = smb2_calc_size, + .is_status_pending = smb2_is_status_pending, + .oplock_response = smb2_oplock_response, + .queryfs = smb2_queryfs, + .mand_lock = smb2_mand_lock, + .mand_unlock_range = smb2_unlock_range, + .push_mand_locks = smb2_push_mandatory_locks, + .get_lease_key = smb2_get_lease_key, + .set_lease_key = smb2_set_lease_key, + .new_lease_key = smb2_new_lease_key, + .generate_signingkey = generate_smb3signingkey, + .calc_signature = smb3_calc_signature, + .set_integrity = smb3_set_integrity, + .is_read_op = smb21_is_read_op, + .set_oplock_level = smb3_set_oplock_level, + .create_lease_buf = smb3_create_lease_buf, + .parse_lease_buf = smb3_parse_lease_buf, + .clone_range = smb2_clone_range, + .duplicate_extents = smb2_duplicate_extents, +/* .validate_negotiate = smb3_validate_negotiate, */ /* not used in 3.11 */ + .wp_retry_size = smb2_wp_retry_size, + .dir_needs_close = smb2_dir_needs_close, + .fallocate = smb3_fallocate, +}; +#endif /* CIFS_SMB311 */ + struct smb_version_values smb20_values = { .version_string = SMB20_VERSION_STRING, .protocol_id = SMB20_PROT_ID, @@ -1714,3 +1872,25 @@ struct smb_version_values smb302_values = { .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, .create_lease_size = sizeof(struct create_lease_v2), }; + +#ifdef CONFIG_CIFS_SMB311 +struct smb_version_values smb311_values = { + .version_string = SMB311_VERSION_STRING, + .protocol_id = SMB311_PROT_ID, + .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, + .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, + .create_lease_size = sizeof(struct create_lease_v2), +}; +#endif /* SMB311 */ diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 54cbe19d9..b8b4f08ee 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -304,6 +304,59 @@ small_smb2_init(__le16 smb2_command, struct cifs_tcon *tcon, return rc; } +#ifdef CONFIG_CIFS_SMB311 +/* offset is sizeof smb2_negotiate_req - 4 but rounded up to 8 bytes */ +#define OFFSET_OF_NEG_CONTEXT 0x68 /* sizeof(struct smb2_negotiate_req) - 4 */ + + +#define SMB2_PREAUTH_INTEGRITY_CAPABILITIES cpu_to_le16(1) +#define SMB2_ENCRYPTION_CAPABILITIES cpu_to_le16(2) + +static void +build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt) +{ + pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES; + pneg_ctxt->DataLength = cpu_to_le16(38); + pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1); + pneg_ctxt->SaltLength = cpu_to_le16(SMB311_SALT_SIZE); + get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE); + pneg_ctxt->HashAlgorithms = SMB2_PREAUTH_INTEGRITY_SHA512; +} + +static void +build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt) +{ + pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES; + pneg_ctxt->DataLength = cpu_to_le16(6); + pneg_ctxt->CipherCount = cpu_to_le16(2); + pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM; + pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES128_CCM; +} + +static void +assemble_neg_contexts(struct smb2_negotiate_req *req) +{ + + /* +4 is to account for the RFC1001 len field */ + char *pneg_ctxt = (char *)req + OFFSET_OF_NEG_CONTEXT + 4; + + build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt); + /* Add 2 to size to round to 8 byte boundary */ + pneg_ctxt += 2 + sizeof(struct smb2_preauth_neg_context); + build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt); + req->NegotiateContextOffset = cpu_to_le32(OFFSET_OF_NEG_CONTEXT); + req->NegotiateContextCount = cpu_to_le16(2); + inc_rfc1001_len(req, 4 + sizeof(struct smb2_preauth_neg_context) + 2 + + sizeof(struct smb2_encryption_neg_context)); /* calculate hash */ +} +#else +static void assemble_neg_contexts(struct smb2_negotiate_req *req) +{ + return; +} +#endif /* SMB311 */ + + /* * * SMB2 Worker functions follow: @@ -363,10 +416,12 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) /* ClientGUID must be zero for SMB2.02 dialect */ if (ses->server->vals->protocol_id == SMB20_PROT_ID) memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE); - else + else { memcpy(req->ClientGUID, server->client_guid, SMB2_CLIENT_GUID_SIZE); - + if (ses->server->vals->protocol_id == SMB311_PROT_ID) + assemble_neg_contexts(req); + } iov[0].iov_base = (char *)req; /* 4 for rfc1002 length field */ iov[0].iov_len = get_rfc1002_length(req) + 4; @@ -393,8 +448,12 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) cifs_dbg(FYI, "negotiated smb3.0 dialect\n"); else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID)) cifs_dbg(FYI, "negotiated smb3.02 dialect\n"); +#ifdef CONFIG_CIFS_SMB311 + else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) + cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n"); +#endif /* SMB311 */ else { - cifs_dbg(VFS, "Illegal dialect returned by server %d\n", + cifs_dbg(VFS, "Illegal dialect returned by server 0x%x\n", le16_to_cpu(rsp->DialectRevision)); rc = -EIO; goto neg_exit; @@ -572,7 +631,7 @@ ssetup_ntlmssp_authenticate: return rc; req->hdr.SessionId = 0; /* First session, not a reauthenticate */ - req->VcNumber = 0; /* MBZ */ + req->Flags = 0; /* MBZ */ /* to enable echos and oplocks */ req->hdr.CreditRequest = cpu_to_le16(3); diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 70867d54f..451108284 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -136,9 +136,6 @@ struct smb2_transform_hdr { __u64 SessionId; } __packed; -/* Encryption Algorithms */ -#define SMB2_ENCRYPTION_AES128_CCM cpu_to_le16(0x0001) - /* * SMB2 flag definitions */ @@ -191,7 +188,10 @@ struct smb2_negotiate_req { __le16 Reserved; /* MBZ */ __le32 Capabilities; __u8 ClientGUID[SMB2_CLIENT_GUID_SIZE]; - __le64 ClientStartTime; /* MBZ */ + /* In SMB3.02 and earlier next three were MBZ le64 ClientStartTime */ + __le32 NegotiateContextOffset; /* SMB3.1.1 only. MBZ earlier */ + __le16 NegotiateContextCount; /* SMB3.1.1 only. MBZ earlier */ + __le16 Reserved2; __le16 Dialects[1]; /* One dialect (vers=) at a time for now */ } __packed; @@ -200,6 +200,7 @@ struct smb2_negotiate_req { #define SMB21_PROT_ID 0x0210 #define SMB30_PROT_ID 0x0300 #define SMB302_PROT_ID 0x0302 +#define SMB311_PROT_ID 0x0311 #define BAD_PROT_ID 0xFFFF /* SecurityMode flags */ @@ -217,12 +218,38 @@ struct smb2_negotiate_req { #define SMB2_NT_FIND 0x00100000 #define SMB2_LARGE_FILES 0x00200000 +#define SMB311_SALT_SIZE 32 +/* Hash Algorithm Types */ +#define SMB2_PREAUTH_INTEGRITY_SHA512 cpu_to_le16(0x0001) + +struct smb2_preauth_neg_context { + __le16 ContextType; /* 1 */ + __le16 DataLength; + __le32 Reserved; + __le16 HashAlgorithmCount; /* 1 */ + __le16 SaltLength; + __le16 HashAlgorithms; /* HashAlgorithms[0] since only one defined */ + __u8 Salt[SMB311_SALT_SIZE]; +} __packed; + +/* Encryption Algorithms Ciphers */ +#define SMB2_ENCRYPTION_AES128_CCM cpu_to_le16(0x0001) +#define SMB2_ENCRYPTION_AES128_GCM cpu_to_le16(0x0002) + +struct smb2_encryption_neg_context { + __le16 ContextType; /* 2 */ + __le16 DataLength; + __le32 Reserved; + __le16 CipherCount; /* AES-128-GCM and AES-128-CCM */ + __le16 Ciphers[2]; /* Ciphers[0] since only one used now */ +} __packed; + struct smb2_negotiate_rsp { struct smb2_hdr hdr; __le16 StructureSize; /* Must be 65 */ __le16 SecurityMode; __le16 DialectRevision; - __le16 Reserved; /* MBZ */ + __le16 NegotiateContextCount; /* Prior to SMB3.1.1 was Reserved & MBZ */ __u8 ServerGUID[16]; __le32 Capabilities; __le32 MaxTransactSize; @@ -232,14 +259,18 @@ struct smb2_negotiate_rsp { __le64 ServerStartTime; __le16 SecurityBufferOffset; __le16 SecurityBufferLength; - __le32 Reserved2; /* may be any value, ignore */ + __le32 NegotiateContextOffset; /* Pre:SMB3.1.1 was reserved/ignored */ __u8 Buffer[1]; /* variable length GSS security buffer */ } __packed; +/* Flags */ +#define SMB2_SESSION_REQ_FLAG_BINDING 0x01 +#define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04 + struct smb2_sess_setup_req { struct smb2_hdr hdr; __le16 StructureSize; /* Must be 25 */ - __u8 VcNumber; + __u8 Flags; __u8 SecurityMode; __le32 Capabilities; __le32 Channel; @@ -274,10 +305,13 @@ struct smb2_logoff_rsp { __le16 Reserved; } __packed; +/* Flags/Reserved for SMB3.1.1 */ +#define SMB2_SHAREFLAG_CLUSTER_RECONNECT 0x0001 + struct smb2_tree_connect_req { struct smb2_hdr hdr; __le16 StructureSize; /* Must be 9 */ - __le16 Reserved; + __le16 Reserved; /* Flags in SMB3.1.1 */ __le16 PathOffset; __le16 PathLength; __u8 Buffer[1]; /* variable length */ @@ -587,6 +621,29 @@ struct copychunk_ioctl_rsp { __le32 TotalBytesWritten; } __packed; +struct fsctl_set_integrity_information_req { + __le16 ChecksumAlgorithm; + __le16 Reserved; + __le32 Flags; +} __packed; + +struct fsctl_get_integrity_information_rsp { + __le16 ChecksumAlgorithm; + __le16 Reserved; + __le32 Flags; + __le32 ChecksumChunkSizeInBytes; + __le32 ClusterSizeInBytes; +} __packed; + +/* Integrity ChecksumAlgorithm choices for above */ +#define CHECKSUM_TYPE_NONE 0x0000 +#define CHECKSUM_TYPE_CRC64 0x0002 +#define CHECKSUM_TYPE_UNCHANGED 0xFFFF /* set only */ + +/* Integrity flags for above */ +#define FSCTL_INTEGRITY_FLAG_CHECKSUM_ENFORCEMENT_OFF 0x00000001 + + struct validate_negotiate_info_req { __le32 Capabilities; __u8 Guid[SMB2_CLIENT_GUID_SIZE]; @@ -620,6 +677,14 @@ struct compress_ioctl { __le16 CompressionState; /* See cifspdu.h for possible flag values */ } __packed; +struct duplicate_extents_to_file { + __u64 PersistentFileHandle; /* source file handle, opaque endianness */ + __u64 VolatileFileHandle; + __le64 SourceFileOffset; + __le64 TargetFileOffset; + __le64 ByteCount; /* Bytes to be copied */ +} __packed; + struct smb2_ioctl_req { struct smb2_hdr hdr; __le16 StructureSize; /* Must be 57 */ diff --git a/fs/cifs/smbfsctl.h b/fs/cifs/smbfsctl.h index 83efa5953..a639d0dab 100644 --- a/fs/cifs/smbfsctl.h +++ b/fs/cifs/smbfsctl.h @@ -75,10 +75,13 @@ #define FSCTL_QUERY_SPARING_INFO 0x00090138 /* BB add struct */ #define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */ #define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */ +#define FSCTL_GET_INTEGRITY_INFORMATION 0x0009027C #define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */ #define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */ #define FSCTL_FILE_LEVEL_TRIM 0x00098208 /* BB add struct */ +#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344 #define FSCTL_SIS_LINK_FILES 0x0009C104 +#define FSCTL_SET_INTEGRITY_INFORMATION 0x0009C280 #define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */ #define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */ /* strange that the number for this op is not sequential with previous op */ diff --git a/fs/coda/coda_linux.h b/fs/coda/coda_linux.h index d6f7a76a1..f829fe963 100644 --- a/fs/coda/coda_linux.h +++ b/fs/coda/coda_linux.h @@ -79,7 +79,7 @@ void coda_sysctl_clean(void); static inline struct coda_inode_info *ITOC(struct inode *inode) { - return list_entry(inode, struct coda_inode_info, vfs_inode); + return container_of(inode, struct coda_inode_info, vfs_inode); } static __inline__ struct CodaFid *coda_i2f(struct inode *inode) diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 6b8e2f091..48851f6ea 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -896,6 +896,7 @@ COMPATIBLE_IOCTL(FIGETBSZ) /* 'X' - originally XFS but some now in the VFS */ COMPATIBLE_IOCTL(FIFREEZE) COMPATIBLE_IOCTL(FITHAW) +COMPATIBLE_IOCTL(FITRIM) COMPATIBLE_IOCTL(KDGETKEYCODE) COMPATIBLE_IOCTL(KDSETKEYCODE) COMPATIBLE_IOCTL(KDGKBTYPE) diff --git a/fs/configfs/inode.c b/fs/configfs/inode.c index 8d89f5fd0..eae87575e 100644 --- a/fs/configfs/inode.c +++ b/fs/configfs/inode.c @@ -236,7 +236,7 @@ void configfs_drop_dentry(struct configfs_dirent * sd, struct dentry * parent) if (dentry) { spin_lock(&dentry->d_lock); - if (!d_unhashed(dentry) && d_really_is_positive(dentry)) { + if (simple_positive(dentry)) { dget_dlock(dentry); __d_drop(dentry); spin_unlock(&dentry->d_lock); diff --git a/fs/configfs/item.c b/fs/configfs/item.c index e65f9ffbb..b863a09cd 100644 --- a/fs/configfs/item.c +++ b/fs/configfs/item.c @@ -47,12 +47,11 @@ static void config_item_release(struct kref *kref); * config_item_init - initialize item. * @item: item in question. */ -void config_item_init(struct config_item *item) +static void config_item_init(struct config_item *item) { kref_init(&item->ci_kref); INIT_LIST_HEAD(&item->ci_entry); } -EXPORT_SYMBOL(config_item_init); /** * config_item_set_name - Set the name of an item @@ -116,7 +115,7 @@ void config_item_init_type_name(struct config_item *item, const char *name, struct config_item_type *type) { - config_item_set_name(item, name); + config_item_set_name(item, "%s", name); item->ci_type = type; config_item_init(item); } @@ -125,7 +124,7 @@ EXPORT_SYMBOL(config_item_init_type_name); void config_group_init_type_name(struct config_group *group, const char *name, struct config_item_type *type) { - config_item_set_name(&group->cg_item, name); + config_item_set_name(&group->cg_item, "%s", name); group->cg_item.ci_type = type; config_group_init(group); } diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c index cc9f2546e..ec5c8325b 100644 --- a/fs/configfs/symlink.c +++ b/fs/configfs/symlink.c @@ -279,36 +279,27 @@ static int configfs_getlink(struct dentry *dentry, char * path) } -static void *configfs_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *configfs_follow_link(struct dentry *dentry, void **cookie) { - int error = -ENOMEM; unsigned long page = get_zeroed_page(GFP_KERNEL); + int error; - if (page) { - error = configfs_getlink(dentry, (char *)page); - if (!error) { - nd_set_link(nd, (char *)page); - return (void *)page; - } - } - - nd_set_link(nd, ERR_PTR(error)); - return NULL; -} + if (!page) + return ERR_PTR(-ENOMEM); -static void configfs_put_link(struct dentry *dentry, struct nameidata *nd, - void *cookie) -{ - if (cookie) { - unsigned long page = (unsigned long)cookie; - free_page(page); + error = configfs_getlink(dentry, (char *)page); + if (!error) { + return *cookie = (void *)page; } + + free_page(page); + return ERR_PTR(error); } const struct inode_operations configfs_symlink_inode_operations = { .follow_link = configfs_follow_link, .readlink = generic_readlink, - .put_link = configfs_put_link, + .put_link = free_page_put_link, .setattr = configfs_setattr, }; diff --git a/fs/coredump.c b/fs/coredump.c index bbbe139ab..c5ecde6f3 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -70,7 +70,8 @@ static int expand_corename(struct core_name *cn, int size) return 0; } -static int cn_vprintf(struct core_name *cn, const char *fmt, va_list arg) +static __printf(2, 0) int cn_vprintf(struct core_name *cn, const char *fmt, + va_list arg) { int free, need; va_list arg_copy; @@ -93,7 +94,7 @@ again: return -ENOMEM; } -static int cn_printf(struct core_name *cn, const char *fmt, ...) +static __printf(2, 3) int cn_printf(struct core_name *cn, const char *fmt, ...) { va_list arg; int ret; @@ -105,7 +106,8 @@ static int cn_printf(struct core_name *cn, const char *fmt, ...) return ret; } -static int cn_esc_printf(struct core_name *cn, const char *fmt, ...) +static __printf(2, 3) +int cn_esc_printf(struct core_name *cn, const char *fmt, ...) { int cur = cn->used; va_list arg; @@ -138,7 +140,7 @@ static int cn_print_exe_file(struct core_name *cn) goto put_exe_file; } - path = d_path(&exe_file->f_path, pathbuf, PATH_MAX); + path = file_path(exe_file, pathbuf, PATH_MAX); if (IS_ERR(path)) { ret = PTR_ERR(path); goto free_buf; @@ -209,11 +211,15 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm) break; /* uid */ case 'u': - err = cn_printf(cn, "%d", cred->uid); + err = cn_printf(cn, "%u", + from_kuid(&init_user_ns, + cred->uid)); break; /* gid */ case 'g': - err = cn_printf(cn, "%d", cred->gid); + err = cn_printf(cn, "%u", + from_kgid(&init_user_ns, + cred->gid)); break; case 'd': err = cn_printf(cn, "%d", @@ -221,7 +227,8 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm) break; /* signal that caused the coredump */ case 's': - err = cn_printf(cn, "%ld", cprm->siginfo->si_signo); + err = cn_printf(cn, "%d", + cprm->siginfo->si_signo); break; /* UNIX time of coredump */ case 't': { @@ -155,7 +155,7 @@ static ssize_t dax_io(struct inode *inode, struct iov_iter *iter, } if (iov_iter_rw(iter) == WRITE) - len = copy_from_iter(addr, max - pos, iter); + len = copy_from_iter_nocache(addr, max - pos, iter); else if (!hole) len = copy_to_iter(addr, max - pos, iter); else @@ -209,7 +209,8 @@ ssize_t dax_do_io(struct kiocb *iocb, struct inode *inode, } /* Protects against truncate */ - inode_dio_begin(inode); + if (!(flags & DIO_SKIP_DIO_COUNT)) + inode_dio_begin(inode); retval = dax_io(inode, iter, pos, end, get_block, &bh); @@ -219,7 +220,8 @@ ssize_t dax_do_io(struct kiocb *iocb, struct inode *inode, if ((retval > 0) && end_io) end_io(iocb, pos, retval, bh.b_private); - inode_dio_end(inode); + if (!(flags & DIO_SKIP_DIO_COUNT)) + inode_dio_end(inode); out: return retval; } @@ -309,14 +311,27 @@ static int dax_insert_mapping(struct inode *inode, struct buffer_head *bh, out: i_mmap_unlock_read(mapping); - if (bh->b_end_io) - bh->b_end_io(bh, 1); - return error; } -static int do_dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf, - get_block_t get_block) +/** + * __dax_fault - handle a page fault on a DAX file + * @vma: The virtual memory area where the fault occurred + * @vmf: The description of the fault + * @get_block: The filesystem method used to translate file offsets to blocks + * @complete_unwritten: The filesystem method used to convert unwritten blocks + * to written so the data written to them is exposed. This is required for + * required by write faults for filesystems that will return unwritten + * extent mappings from @get_block, but it is optional for reads as + * dax_insert_mapping() will always zero unwritten blocks. If the fs does + * not support unwritten extents, the it should pass NULL. + * + * When a page fault occurs, filesystems may call this helper in their + * fault handler for DAX files. __dax_fault() assumes the caller has done all + * the necessary locking for the page fault to proceed successfully. + */ +int __dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf, + get_block_t get_block, dax_iodone_t complete_unwritten) { struct file *file = vma->vm_file; struct address_space *mapping = file->f_mapping; @@ -417,7 +432,23 @@ static int do_dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf, page_cache_release(page); } + /* + * If we successfully insert the new mapping over an unwritten extent, + * we need to ensure we convert the unwritten extent. If there is an + * error inserting the mapping, the filesystem needs to leave it as + * unwritten to prevent exposure of the stale underlying data to + * userspace, but we still need to call the completion function so + * the private resources on the mapping buffer can be released. We + * indicate what the callback should do via the uptodate variable, same + * as for normal BH based IO completions. + */ error = dax_insert_mapping(inode, &bh, vma, vmf); + if (buffer_unwritten(&bh)) { + if (complete_unwritten) + complete_unwritten(&bh, !error); + else + WARN_ON_ONCE(!(vmf->flags & FAULT_FLAG_WRITE)); + } out: if (error == -ENOMEM) @@ -434,6 +465,7 @@ static int do_dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf, } goto out; } +EXPORT_SYMBOL(__dax_fault); /** * dax_fault - handle a page fault on a DAX file @@ -445,7 +477,7 @@ static int do_dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf, * fault handler for DAX files. */ int dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf, - get_block_t get_block) + get_block_t get_block, dax_iodone_t complete_unwritten) { int result; struct super_block *sb = file_inode(vma->vm_file)->i_sb; @@ -454,7 +486,7 @@ int dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf, sb_start_pagefault(sb); file_update_time(vma->vm_file); } - result = do_dax_fault(vma, vmf, get_block); + result = __dax_fault(vma, vmf, get_block, complete_unwritten); if (vmf->flags & FAULT_FLAG_WRITE) sb_end_pagefault(sb); diff --git a/fs/dcache.c b/fs/dcache.c index 7c39f19bd..9b5fe503f 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -322,17 +322,17 @@ static void dentry_free(struct dentry *dentry) } /** - * dentry_rcuwalk_barrier - invalidate in-progress rcu-walk lookups + * dentry_rcuwalk_invalidate - invalidate in-progress rcu-walk lookups * @dentry: the target dentry * After this call, in-progress rcu-walk path lookup will fail. This * should be called after unhashing, and after changing d_inode (if * the dentry has not already been unhashed). */ -static inline void dentry_rcuwalk_barrier(struct dentry *dentry) +static inline void dentry_rcuwalk_invalidate(struct dentry *dentry) { - assert_spin_locked(&dentry->d_lock); - /* Go through a barrier */ - write_seqcount_barrier(&dentry->d_seq); + lockdep_assert_held(&dentry->d_lock); + /* Go through am invalidation barrier */ + write_seqcount_invalidate(&dentry->d_seq); } /* @@ -372,7 +372,7 @@ static void dentry_unlink_inode(struct dentry * dentry) struct inode *inode = dentry->d_inode; __d_clear_type_and_inode(dentry); hlist_del_init(&dentry->d_u.d_alias); - dentry_rcuwalk_barrier(dentry); + dentry_rcuwalk_invalidate(dentry); spin_unlock(&dentry->d_lock); spin_unlock(&inode->i_lock); if (!inode->i_nlink) @@ -494,7 +494,7 @@ void __d_drop(struct dentry *dentry) __hlist_bl_del(&dentry->d_hash); dentry->d_hash.pprev = NULL; hlist_bl_unlock(b); - dentry_rcuwalk_barrier(dentry); + dentry_rcuwalk_invalidate(dentry); } } EXPORT_SYMBOL(__d_drop); @@ -1167,7 +1167,7 @@ enum d_walk_ret { * * The @enter() and @finish() callbacks are called with d_lock held. */ -void d_walk(struct dentry *parent, void *data, +static void d_walk(struct dentry *parent, void *data, enum d_walk_ret (*enter)(void *, struct dentry *), void (*finish)(void *)) { @@ -1272,7 +1272,6 @@ rename_retry: seq = 1; goto again; } -EXPORT_SYMBOL(d_walk); /* * Search for at least 1 mount point in the dentry's subdirs. @@ -1677,7 +1676,8 @@ void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op) DCACHE_OP_COMPARE | DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE | - DCACHE_OP_DELETE )); + DCACHE_OP_DELETE | + DCACHE_OP_SELECT_INODE)); dentry->d_op = op; if (!op) return; @@ -1693,6 +1693,8 @@ void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op) dentry->d_flags |= DCACHE_OP_DELETE; if (op->d_prune) dentry->d_flags |= DCACHE_OP_PRUNE; + if (op->d_select_inode) + dentry->d_flags |= DCACHE_OP_SELECT_INODE; } EXPORT_SYMBOL(d_set_d_op); @@ -1756,7 +1758,7 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode) if (inode) hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry); __d_set_inode_and_type(dentry, inode, add_flags); - dentry_rcuwalk_barrier(dentry); + dentry_rcuwalk_invalidate(dentry); spin_unlock(&dentry->d_lock); fsnotify_d_instantiate(dentry, inode); } @@ -3440,22 +3442,15 @@ void __init vfs_caches_init_early(void) inode_init_early(); } -void __init vfs_caches_init(unsigned long mempages) +void __init vfs_caches_init(void) { - unsigned long reserve; - - /* Base hash sizes on available memory, with a reserve equal to - 150% of current kernel size */ - - reserve = min((mempages - nr_free_pages()) * 3/2, mempages - 1); - mempages -= reserve; - names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); dcache_init(); inode_init(); - files_init(mempages); + files_init(); + files_maxfiles_init(); mnt_init(); bdev_cache_init(); chrdev_init(); diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 830a7e76f..284f9aa00 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -17,7 +17,6 @@ #include <linux/fs.h> #include <linux/seq_file.h> #include <linux/pagemap.h> -#include <linux/namei.h> #include <linux/debugfs.h> #include <linux/io.h> #include <linux/slab.h> @@ -43,17 +42,6 @@ const struct file_operations debugfs_file_operations = { .llseek = noop_llseek, }; -static void *debugfs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - nd_set_link(nd, d_inode(dentry)->i_private); - return NULL; -} - -const struct inode_operations debugfs_link_operations = { - .readlink = generic_readlink, - .follow_link = debugfs_follow_link, -}; - static int debugfs_u8_set(void *data, u64 val) { *(u8 *)data = val; diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 12756040c..c711be8d6 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -44,11 +44,6 @@ static struct inode *debugfs_get_inode(struct super_block *sb) return inode; } -static inline int debugfs_positive(struct dentry *dentry) -{ - return d_really_is_positive(dentry) && !d_unhashed(dentry); -} - struct debugfs_mount_opts { kuid_t uid; kgid_t gid; @@ -174,7 +169,7 @@ static void debugfs_evict_inode(struct inode *inode) truncate_inode_pages_final(&inode->i_data); clear_inode(inode); if (S_ISLNK(inode->i_mode)) - kfree(inode->i_private); + kfree(inode->i_link); } static const struct super_operations debugfs_super_operations = { @@ -511,8 +506,8 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent, return failed_creating(dentry); } inode->i_mode = S_IFLNK | S_IRWXUGO; - inode->i_op = &debugfs_link_operations; - inode->i_private = link; + inode->i_op = &simple_symlink_inode_operations; + inode->i_link = link; d_instantiate(dentry, inode); return end_creating(dentry); } @@ -522,7 +517,7 @@ static int __debugfs_remove(struct dentry *dentry, struct dentry *parent) { int ret = 0; - if (debugfs_positive(dentry)) { + if (simple_positive(dentry)) { dget(dentry); if (d_is_dir(dentry)) ret = simple_rmdir(d_inode(parent), dentry); @@ -602,7 +597,7 @@ void debugfs_remove_recursive(struct dentry *dentry) */ spin_lock(&parent->d_lock); list_for_each_entry(child, &parent->d_subdirs, d_child) { - if (!debugfs_positive(child)) + if (!simple_positive(child)) continue; /* perhaps simple_empty(child) makes more sense */ @@ -623,7 +618,7 @@ void debugfs_remove_recursive(struct dentry *dentry) * from d_subdirs. When releasing the parent->d_lock we can * no longer trust that the next pointer is valid. * Restart the loop. We'll skip this one with the - * debugfs_positive() check. + * simple_positive() check. */ goto loop; } diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index add566303..c35ffdc12 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -142,6 +142,8 @@ static inline struct super_block *pts_sb_from_inode(struct inode *inode) if (inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) return inode->i_sb; #endif + if (!devpts_mnt) + return NULL; return devpts_mnt->mnt_sb; } @@ -525,10 +527,14 @@ static struct file_system_type devpts_fs_type = { int devpts_new_index(struct inode *ptmx_inode) { struct super_block *sb = pts_sb_from_inode(ptmx_inode); - struct pts_fs_info *fsi = DEVPTS_SB(sb); + struct pts_fs_info *fsi; int index; int ida_ret; + if (!sb) + return -ENODEV; + + fsi = DEVPTS_SB(sb); retry: if (!ida_pre_get(&fsi->allocated_ptys, GFP_KERNEL)) return -ENOMEM; @@ -584,11 +590,18 @@ struct inode *devpts_pty_new(struct inode *ptmx_inode, dev_t device, int index, struct dentry *dentry; struct super_block *sb = pts_sb_from_inode(ptmx_inode); struct inode *inode; - struct dentry *root = sb->s_root; - struct pts_fs_info *fsi = DEVPTS_SB(sb); - struct pts_mount_opts *opts = &fsi->mount_opts; + struct dentry *root; + struct pts_fs_info *fsi; + struct pts_mount_opts *opts; char s[12]; + if (!sb) + return ERR_PTR(-ENODEV); + + root = sb->s_root; + fsi = DEVPTS_SB(sb); + opts = &fsi->mount_opts; + inode = new_inode(sb); if (!inode) return ERR_PTR(-ENOMEM); @@ -676,12 +689,16 @@ static int __init init_devpts_fs(void) struct ctl_table_header *table; if (!err) { + struct vfsmount *mnt; + table = register_sysctl_table(pty_root_table); - devpts_mnt = kern_mount(&devpts_fs_type); - if (IS_ERR(devpts_mnt)) { - err = PTR_ERR(devpts_mnt); + mnt = kern_mount(&devpts_fs_type); + if (IS_ERR(mnt)) { + err = PTR_ERR(mnt); unregister_filesystem(&devpts_fs_type); unregister_sysctl_table(table); + } else { + devpts_mnt = mnt; } } return err; diff --git a/fs/dlm/lowcomms.c b/fs/dlm/lowcomms.c index d08e079ea..754fd6c0b 100644 --- a/fs/dlm/lowcomms.c +++ b/fs/dlm/lowcomms.c @@ -921,8 +921,8 @@ static int tcp_accept_from_sock(struct connection *con) mutex_unlock(&connections_lock); memset(&peeraddr, 0, sizeof(peeraddr)); - result = sock_create_kern(dlm_local_addr[0]->ss_family, SOCK_STREAM, - IPPROTO_TCP, &newsock); + result = sock_create_kern(&init_net, dlm_local_addr[0]->ss_family, + SOCK_STREAM, IPPROTO_TCP, &newsock); if (result < 0) return -ENOMEM; @@ -1173,8 +1173,8 @@ static void tcp_connect_to_sock(struct connection *con) goto out; /* Create a socket to communicate with */ - result = sock_create_kern(dlm_local_addr[0]->ss_family, SOCK_STREAM, - IPPROTO_TCP, &sock); + result = sock_create_kern(&init_net, dlm_local_addr[0]->ss_family, + SOCK_STREAM, IPPROTO_TCP, &sock); if (result < 0) goto out_err; @@ -1258,8 +1258,8 @@ static struct socket *tcp_create_listen_sock(struct connection *con, addr_len = sizeof(struct sockaddr_in6); /* Create a socket to communicate with */ - result = sock_create_kern(dlm_local_addr[0]->ss_family, SOCK_STREAM, - IPPROTO_TCP, &sock); + result = sock_create_kern(&init_net, dlm_local_addr[0]->ss_family, + SOCK_STREAM, IPPROTO_TCP, &sock); if (result < 0) { log_print("Can't create listening comms socket"); goto create_out; @@ -1365,8 +1365,8 @@ static int sctp_listen_for_all(void) log_print("Using SCTP for communications"); - result = sock_create_kern(dlm_local_addr[0]->ss_family, SOCK_SEQPACKET, - IPPROTO_SCTP, &sock); + result = sock_create_kern(&init_net, dlm_local_addr[0]->ss_family, + SOCK_SEQPACKET, IPPROTO_SCTP, &sock); if (result < 0) { log_print("Can't create comms socket, check SCTP is loaded"); goto out; diff --git a/fs/drop_caches.c b/fs/drop_caches.c index 8289cb195..5718cb9f7 100644 --- a/fs/drop_caches.c +++ b/fs/drop_caches.c @@ -8,7 +8,6 @@ #include <linux/writeback.h> #include <linux/sysctl.h> #include <linux/gfp.h> -#include <linux/export.h> #include "internal.h" /* A global variable is a bit ugly, but it keeps the code simple */ @@ -38,12 +37,6 @@ static void drop_pagecache_sb(struct super_block *sb, void *unused) iput(toput_inode); } -/* For TuxOnIce */ -void drop_pagecache(void) -{ - iterate_supers(drop_pagecache_sb, NULL); -} - int drop_caches_sysctl_handler(struct ctl_table *table, int write, void __user *buffer, size_t *length, loff_t *ppos) { diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index 72afcc629..feef8a9c4 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -325,7 +325,6 @@ ecryptfs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return rc; switch (cmd) { - case FITRIM: case FS_IOC32_GETFLAGS: case FS_IOC32_SETFLAGS: case FS_IOC32_GETVERSION: diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index fc850b55d..3c4db1172 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -170,7 +170,6 @@ out_unlock: * @directory_inode: inode of the new file's dentry's parent in ecryptfs * @ecryptfs_dentry: New file's dentry in ecryptfs * @mode: The mode of the new file - * @nd: nameidata of ecryptfs' parent's dentry & vfsmount * * Creates the underlying file and the eCryptfs inode which will link to * it. It will also update the eCryptfs directory inode to mimic the @@ -384,7 +383,7 @@ static int ecryptfs_lookup_interpose(struct dentry *dentry, * ecryptfs_lookup * @ecryptfs_dir_inode: The eCryptfs directory inode * @ecryptfs_dentry: The eCryptfs dentry that we are looking up - * @ecryptfs_nd: nameidata; may be NULL + * @flags: lookup flags * * Find a file on disk. If the file does not exist, then we'll add it to the * dentry cache and continue on to read it from the disk. @@ -675,18 +674,16 @@ out: return rc ? ERR_PTR(rc) : buf; } -static void *ecryptfs_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *ecryptfs_follow_link(struct dentry *dentry, void **cookie) { size_t len; char *buf = ecryptfs_readlink_lower(dentry, &len); if (IS_ERR(buf)) - goto out; + return buf; fsstack_copy_attr_atime(d_inode(dentry), d_inode(ecryptfs_dentry_to_lower(dentry))); buf[len] = '\0'; -out: - nd_set_link(nd, buf); - return NULL; + return *cookie = buf; } /** diff --git a/fs/efs/super.c b/fs/efs/super.c index 7fca462ea..c8411a30f 100644 --- a/fs/efs/super.c +++ b/fs/efs/super.c @@ -67,7 +67,7 @@ static struct kmem_cache * efs_inode_cachep; static struct inode *efs_alloc_inode(struct super_block *sb) { struct efs_inode_info *ei; - ei = (struct efs_inode_info *)kmem_cache_alloc(efs_inode_cachep, GFP_KERNEL); + ei = kmem_cache_alloc(efs_inode_cachep, GFP_KERNEL); if (!ei) return NULL; return &ei->vfs_inode; diff --git a/fs/exofs/Kbuild b/fs/exofs/Kbuild index b47c7b8dc..a364fd096 100644 --- a/fs/exofs/Kbuild +++ b/fs/exofs/Kbuild @@ -16,5 +16,5 @@ libore-y := ore.o ore_raid.o obj-$(CONFIG_ORE) += libore.o -exofs-y := inode.o file.o symlink.o namei.o dir.o super.o sys.o +exofs-y := inode.o file.o namei.o dir.o super.o sys.o obj-$(CONFIG_EXOFS_FS) += exofs.o diff --git a/fs/exofs/dir.c b/fs/exofs/dir.c index 4deb0b05b..e5bb2abf7 100644 --- a/fs/exofs/dir.c +++ b/fs/exofs/dir.c @@ -44,12 +44,6 @@ static inline void exofs_put_page(struct page *page) page_cache_release(page); } -/* Accesses dir's inode->i_size must be called under inode lock */ -static inline unsigned long dir_pages(struct inode *inode) -{ - return (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; -} - static unsigned exofs_last_byte(struct inode *inode, unsigned long page_nr) { loff_t last_byte = inode->i_size; diff --git a/fs/exofs/exofs.h b/fs/exofs/exofs.h index ad9cac670..2e86086bc 100644 --- a/fs/exofs/exofs.h +++ b/fs/exofs/exofs.h @@ -207,10 +207,6 @@ extern const struct address_space_operations exofs_aops; extern const struct inode_operations exofs_dir_inode_operations; extern const struct inode_operations exofs_special_inode_operations; -/* symlink.c */ -extern const struct inode_operations exofs_symlink_inode_operations; -extern const struct inode_operations exofs_fast_symlink_inode_operations; - /* exofs_init_comps will initialize an ore_components device array * pointing to a single ore_comp struct, and a round-robin view * of the device table. diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c index 786e4cc8c..73c64daa0 100644 --- a/fs/exofs/inode.c +++ b/fs/exofs/inode.c @@ -1222,10 +1222,11 @@ struct inode *exofs_iget(struct super_block *sb, unsigned long ino) inode->i_fop = &exofs_dir_operations; inode->i_mapping->a_ops = &exofs_aops; } else if (S_ISLNK(inode->i_mode)) { - if (exofs_inode_is_fast_symlink(inode)) - inode->i_op = &exofs_fast_symlink_inode_operations; - else { - inode->i_op = &exofs_symlink_inode_operations; + if (exofs_inode_is_fast_symlink(inode)) { + inode->i_op = &simple_symlink_inode_operations; + inode->i_link = (char *)oi->i_data; + } else { + inode->i_op = &page_symlink_inode_operations; inode->i_mapping->a_ops = &exofs_aops; } } else { diff --git a/fs/exofs/namei.c b/fs/exofs/namei.c index 5ae25e431..09a6bb1ad 100644 --- a/fs/exofs/namei.c +++ b/fs/exofs/namei.c @@ -113,7 +113,7 @@ static int exofs_symlink(struct inode *dir, struct dentry *dentry, oi = exofs_i(inode); if (l > sizeof(oi->i_data)) { /* slow symlink */ - inode->i_op = &exofs_symlink_inode_operations; + inode->i_op = &page_symlink_inode_operations; inode->i_mapping->a_ops = &exofs_aops; memset(oi->i_data, 0, sizeof(oi->i_data)); @@ -122,7 +122,8 @@ static int exofs_symlink(struct inode *dir, struct dentry *dentry, goto out_fail; } else { /* fast symlink */ - inode->i_op = &exofs_fast_symlink_inode_operations; + inode->i_op = &simple_symlink_inode_operations; + inode->i_link = (char *)oi->i_data; memcpy(oi->i_data, symname, l); inode->i_size = l-1; } diff --git a/fs/exofs/symlink.c b/fs/exofs/symlink.c deleted file mode 100644 index 6f6f3a4c1..000000000 --- a/fs/exofs/symlink.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2005, 2006 - * Avishay Traeger (avishay@gmail.com) - * Copyright (C) 2008, 2009 - * Boaz Harrosh <ooo@electrozaur.com> - * - * Copyrights for code taken from ext2: - * Copyright (C) 1992, 1993, 1994, 1995 - * Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) - * from - * linux/fs/minix/inode.c - * Copyright (C) 1991, 1992 Linus Torvalds - * - * This file is part of exofs. - * - * exofs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation. Since it is based on ext2, and the only - * valid version of GPL for the Linux kernel is version 2, the only valid - * version of GPL for exofs is version 2. - * - * exofs is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with exofs; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include <linux/namei.h> - -#include "exofs.h" - -static void *exofs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct exofs_i_info *oi = exofs_i(d_inode(dentry)); - - nd_set_link(nd, (char *)oi->i_data); - return NULL; -} - -const struct inode_operations exofs_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, -}; - -const struct inode_operations exofs_fast_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = exofs_follow_link, -}; diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index 796b491e6..0c6638b40 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -70,11 +70,6 @@ static inline void ext2_put_page(struct page *page) page_cache_release(page); } -static inline unsigned long dir_pages(struct inode *inode) -{ - return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT; -} - /* * Return the offset into page `page_nr' of the last valid * byte in that page, plus one. diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 3a0a6c640..3b57c9f83 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -28,12 +28,12 @@ #ifdef CONFIG_FS_DAX static int ext2_dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { - return dax_fault(vma, vmf, ext2_get_block); + return dax_fault(vma, vmf, ext2_get_block, NULL); } static int ext2_dax_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { - return dax_mkwrite(vma, vmf, ext2_get_block); + return dax_mkwrite(vma, vmf, ext2_get_block, NULL); } static const struct vm_operations_struct ext2_dax_vm_ops = { diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index f460ae36d..5c09776d3 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1403,6 +1403,7 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino) inode->i_mapping->a_ops = &ext2_aops; } else if (S_ISLNK(inode->i_mode)) { if (ext2_inode_is_fast_symlink(inode)) { + inode->i_link = (char *)ei->i_data; inode->i_op = &ext2_fast_symlink_inode_operations; nd_terminate_link(ei->i_data, inode->i_size, sizeof(ei->i_data) - 1); diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 3e074a9cc..13ec54a99 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -189,7 +189,8 @@ static int ext2_symlink (struct inode * dir, struct dentry * dentry, } else { /* fast symlink */ inode->i_op = &ext2_fast_symlink_inode_operations; - memcpy((char*)(EXT2_I(inode)->i_data),symname,l); + inode->i_link = (char*)EXT2_I(inode)->i_data; + memcpy(inode->i_link, symname, l); inode->i_size = l-1; } mark_inode_dirty(inode); diff --git a/fs/ext2/super.c b/fs/ext2/super.c index d0e746e96..900e19cf9 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -882,6 +882,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | ((EXT2_SB(sb)->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0); + sb->s_iflags |= SB_I_CGROUPWB; if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV && (EXT2_HAS_COMPAT_FEATURE(sb, ~0U) || diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c index 20608f17c..ae17179f3 100644 --- a/fs/ext2/symlink.c +++ b/fs/ext2/symlink.c @@ -19,14 +19,6 @@ #include "ext2.h" #include "xattr.h" -#include <linux/namei.h> - -static void *ext2_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct ext2_inode_info *ei = EXT2_I(d_inode(dentry)); - nd_set_link(nd, (char *)ei->i_data); - return NULL; -} const struct inode_operations ext2_symlink_inode_operations = { .readlink = generic_readlink, @@ -43,7 +35,7 @@ const struct inode_operations ext2_symlink_inode_operations = { const struct inode_operations ext2_fast_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = ext2_follow_link, + .follow_link = simple_follow_link, .setattr = ext2_setattr, #ifdef CONFIG_EXT2_FS_XATTR .setxattr = generic_setxattr, diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 2ee2dc435..6c7e5468a 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -2999,6 +2999,7 @@ struct inode *ext3_iget(struct super_block *sb, unsigned long ino) inode->i_op = &ext3_fast_symlink_inode_operations; nd_terminate_link(ei->i_data, inode->i_size, sizeof(ei->i_data) - 1); + inode->i_link = (char *)ei->i_data; } else { inode->i_op = &ext3_symlink_inode_operations; ext3_set_aops(inode); diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index 4264b9bd0..c9e767cd4 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -2308,7 +2308,8 @@ retry: } } else { inode->i_op = &ext3_fast_symlink_inode_operations; - memcpy((char*)&EXT3_I(inode)->i_data,symname,l); + inode->i_link = (char*)&EXT3_I(inode)->i_data; + memcpy(inode->i_link, symname, l); inode->i_size = l-1; } EXT3_I(inode)->i_disksize = inode->i_size; diff --git a/fs/ext3/super.c b/fs/ext3/super.c index a9312f0a5..5ed0044fb 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -1908,7 +1908,7 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) sbi->s_mount_state = le16_to_cpu(es->s_state); sbi->s_addr_per_block_bits = ilog2(EXT3_ADDR_PER_BLOCK(sb)); sbi->s_desc_per_block_bits = ilog2(EXT3_DESC_PER_BLOCK(sb)); - for (i=0; i < 4; i++) + for (i = 0; i < 4; i++) sbi->s_hash_seed[i] = le32_to_cpu(es->s_hash_seed[i]); sbi->s_def_hash_version = es->s_def_hash_version; i = le32_to_cpu(es->s_flags); diff --git a/fs/ext3/symlink.c b/fs/ext3/symlink.c index ea96df3c5..c08c59094 100644 --- a/fs/ext3/symlink.c +++ b/fs/ext3/symlink.c @@ -17,17 +17,9 @@ * ext3 symlink handling code */ -#include <linux/namei.h> #include "ext3.h" #include "xattr.h" -static void * ext3_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct ext3_inode_info *ei = EXT3_I(d_inode(dentry)); - nd_set_link(nd, (char*)ei->i_data); - return NULL; -} - const struct inode_operations ext3_symlink_inode_operations = { .readlink = generic_readlink, .follow_link = page_follow_link_light, @@ -43,7 +35,7 @@ const struct inode_operations ext3_symlink_inode_operations = { const struct inode_operations ext3_fast_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = ext3_follow_link, + .follow_link = simple_follow_link, .setattr = ext3_setattr, #ifdef CONFIG_EXT3_FS_XATTR .setxattr = generic_setxattr, diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig index 024f2284d..bf8bc8aba 100644 --- a/fs/ext4/Kconfig +++ b/fs/ext4/Kconfig @@ -72,6 +72,7 @@ config EXT4_ENCRYPTION select CRYPTO_ECB select CRYPTO_XTS select CRYPTO_CTS + select CRYPTO_CTR select CRYPTO_SHA256 select KEYS select ENCRYPTED_KEYS diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index 955bf49a7..cd6ea29be 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -369,7 +369,7 @@ static void ext4_validate_block_bitmap(struct super_block *sb, struct ext4_group_info *grp = ext4_get_group_info(sb, block_group); struct ext4_sb_info *sbi = EXT4_SB(sb); - if (buffer_verified(bh)) + if (buffer_verified(bh) || EXT4_MB_GRP_BBITMAP_CORRUPT(grp)) return; ext4_lock_group(sb, block_group); @@ -446,7 +446,7 @@ ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group) unlock_buffer(bh); if (err) ext4_error(sb, "Checksum bad for grp %u", block_group); - return bh; + goto verify; } ext4_unlock_group(sb, block_group); if (buffer_uptodate(bh)) { diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c index 8ff15273a..457315581 100644 --- a/fs/ext4/crypto.c +++ b/fs/ext4/crypto.c @@ -55,6 +55,9 @@ static mempool_t *ext4_bounce_page_pool; static LIST_HEAD(ext4_free_crypto_ctxs); static DEFINE_SPINLOCK(ext4_crypto_ctx_lock); +static struct kmem_cache *ext4_crypto_ctx_cachep; +struct kmem_cache *ext4_crypt_info_cachep; + /** * ext4_release_crypto_ctx() - Releases an encryption context * @ctx: The encryption context to release. @@ -68,18 +71,12 @@ void ext4_release_crypto_ctx(struct ext4_crypto_ctx *ctx) { unsigned long flags; - if (ctx->bounce_page) { - if (ctx->flags & EXT4_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL) - __free_page(ctx->bounce_page); - else - mempool_free(ctx->bounce_page, ext4_bounce_page_pool); - ctx->bounce_page = NULL; - } - ctx->control_page = NULL; + if (ctx->flags & EXT4_WRITE_PATH_FL && ctx->w.bounce_page) + mempool_free(ctx->w.bounce_page, ext4_bounce_page_pool); + ctx->w.bounce_page = NULL; + ctx->w.control_page = NULL; if (ctx->flags & EXT4_CTX_REQUIRES_FREE_ENCRYPT_FL) { - if (ctx->tfm) - crypto_free_tfm(ctx->tfm); - kfree(ctx); + kmem_cache_free(ext4_crypto_ctx_cachep, ctx); } else { spin_lock_irqsave(&ext4_crypto_ctx_lock, flags); list_add(&ctx->free_list, &ext4_free_crypto_ctxs); @@ -88,23 +85,6 @@ void ext4_release_crypto_ctx(struct ext4_crypto_ctx *ctx) } /** - * ext4_alloc_and_init_crypto_ctx() - Allocates and inits an encryption context - * @mask: The allocation mask. - * - * Return: An allocated and initialized encryption context on success. An error - * value or NULL otherwise. - */ -static struct ext4_crypto_ctx *ext4_alloc_and_init_crypto_ctx(gfp_t mask) -{ - struct ext4_crypto_ctx *ctx = kzalloc(sizeof(struct ext4_crypto_ctx), - mask); - - if (!ctx) - return ERR_PTR(-ENOMEM); - return ctx; -} - -/** * ext4_get_crypto_ctx() - Gets an encryption context * @inode: The inode for which we are doing the crypto * @@ -118,10 +98,10 @@ struct ext4_crypto_ctx *ext4_get_crypto_ctx(struct inode *inode) struct ext4_crypto_ctx *ctx = NULL; int res = 0; unsigned long flags; - struct ext4_encryption_key *key = &EXT4_I(inode)->i_encryption_key; + struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info; - if (!ext4_read_workqueue) - ext4_init_crypto(); + if (ci == NULL) + return ERR_PTR(-ENOKEY); /* * We first try getting the ctx from a free list because in @@ -140,50 +120,16 @@ struct ext4_crypto_ctx *ext4_get_crypto_ctx(struct inode *inode) list_del(&ctx->free_list); spin_unlock_irqrestore(&ext4_crypto_ctx_lock, flags); if (!ctx) { - ctx = ext4_alloc_and_init_crypto_ctx(GFP_NOFS); - if (IS_ERR(ctx)) { - res = PTR_ERR(ctx); + ctx = kmem_cache_zalloc(ext4_crypto_ctx_cachep, GFP_NOFS); + if (!ctx) { + res = -ENOMEM; goto out; } ctx->flags |= EXT4_CTX_REQUIRES_FREE_ENCRYPT_FL; } else { ctx->flags &= ~EXT4_CTX_REQUIRES_FREE_ENCRYPT_FL; } - - /* Allocate a new Crypto API context if we don't already have - * one or if it isn't the right mode. */ - BUG_ON(key->mode == EXT4_ENCRYPTION_MODE_INVALID); - if (ctx->tfm && (ctx->mode != key->mode)) { - crypto_free_tfm(ctx->tfm); - ctx->tfm = NULL; - ctx->mode = EXT4_ENCRYPTION_MODE_INVALID; - } - if (!ctx->tfm) { - switch (key->mode) { - case EXT4_ENCRYPTION_MODE_AES_256_XTS: - ctx->tfm = crypto_ablkcipher_tfm( - crypto_alloc_ablkcipher("xts(aes)", 0, 0)); - break; - case EXT4_ENCRYPTION_MODE_AES_256_GCM: - /* TODO(mhalcrow): AEAD w/ gcm(aes); - * crypto_aead_setauthsize() */ - ctx->tfm = ERR_PTR(-ENOTSUPP); - break; - default: - BUG(); - } - if (IS_ERR_OR_NULL(ctx->tfm)) { - res = PTR_ERR(ctx->tfm); - ctx->tfm = NULL; - goto out; - } - ctx->mode = key->mode; - } - BUG_ON(key->size != ext4_encryption_key_size(key->mode)); - - /* There shouldn't be a bounce page attached to the crypto - * context at this point. */ - BUG_ON(ctx->bounce_page); + ctx->flags &= ~EXT4_WRITE_PATH_FL; out: if (res) { @@ -204,20 +150,8 @@ void ext4_exit_crypto(void) { struct ext4_crypto_ctx *pos, *n; - list_for_each_entry_safe(pos, n, &ext4_free_crypto_ctxs, free_list) { - if (pos->bounce_page) { - if (pos->flags & - EXT4_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL) { - __free_page(pos->bounce_page); - } else { - mempool_free(pos->bounce_page, - ext4_bounce_page_pool); - } - } - if (pos->tfm) - crypto_free_tfm(pos->tfm); - kfree(pos); - } + list_for_each_entry_safe(pos, n, &ext4_free_crypto_ctxs, free_list) + kmem_cache_free(ext4_crypto_ctx_cachep, pos); INIT_LIST_HEAD(&ext4_free_crypto_ctxs); if (ext4_bounce_page_pool) mempool_destroy(ext4_bounce_page_pool); @@ -225,6 +159,12 @@ void ext4_exit_crypto(void) if (ext4_read_workqueue) destroy_workqueue(ext4_read_workqueue); ext4_read_workqueue = NULL; + if (ext4_crypto_ctx_cachep) + kmem_cache_destroy(ext4_crypto_ctx_cachep); + ext4_crypto_ctx_cachep = NULL; + if (ext4_crypt_info_cachep) + kmem_cache_destroy(ext4_crypt_info_cachep); + ext4_crypt_info_cachep = NULL; } /** @@ -237,23 +177,31 @@ void ext4_exit_crypto(void) */ int ext4_init_crypto(void) { - int i, res; + int i, res = -ENOMEM; mutex_lock(&crypto_init); if (ext4_read_workqueue) goto already_initialized; ext4_read_workqueue = alloc_workqueue("ext4_crypto", WQ_HIGHPRI, 0); - if (!ext4_read_workqueue) { - res = -ENOMEM; + if (!ext4_read_workqueue) + goto fail; + + ext4_crypto_ctx_cachep = KMEM_CACHE(ext4_crypto_ctx, + SLAB_RECLAIM_ACCOUNT); + if (!ext4_crypto_ctx_cachep) + goto fail; + + ext4_crypt_info_cachep = KMEM_CACHE(ext4_crypt_info, + SLAB_RECLAIM_ACCOUNT); + if (!ext4_crypt_info_cachep) goto fail; - } for (i = 0; i < num_prealloc_crypto_ctxs; i++) { struct ext4_crypto_ctx *ctx; - ctx = ext4_alloc_and_init_crypto_ctx(GFP_KERNEL); - if (IS_ERR(ctx)) { - res = PTR_ERR(ctx); + ctx = kmem_cache_zalloc(ext4_crypto_ctx_cachep, GFP_NOFS); + if (!ctx) { + res = -ENOMEM; goto fail; } list_add(&ctx->free_list, &ext4_free_crypto_ctxs); @@ -317,32 +265,11 @@ static int ext4_page_crypto(struct ext4_crypto_ctx *ctx, struct ablkcipher_request *req = NULL; DECLARE_EXT4_COMPLETION_RESULT(ecr); struct scatterlist dst, src; - struct ext4_inode_info *ei = EXT4_I(inode); - struct crypto_ablkcipher *atfm = __crypto_ablkcipher_cast(ctx->tfm); + struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info; + struct crypto_ablkcipher *tfm = ci->ci_ctfm; int res = 0; - BUG_ON(!ctx->tfm); - BUG_ON(ctx->mode != ei->i_encryption_key.mode); - - if (ctx->mode != EXT4_ENCRYPTION_MODE_AES_256_XTS) { - printk_ratelimited(KERN_ERR - "%s: unsupported crypto algorithm: %d\n", - __func__, ctx->mode); - return -ENOTSUPP; - } - - crypto_ablkcipher_clear_flags(atfm, ~0); - crypto_tfm_set_flags(ctx->tfm, CRYPTO_TFM_REQ_WEAK_KEY); - - res = crypto_ablkcipher_setkey(atfm, ei->i_encryption_key.raw, - ei->i_encryption_key.size); - if (res) { - printk_ratelimited(KERN_ERR - "%s: crypto_ablkcipher_setkey() failed\n", - __func__); - return res; - } - req = ablkcipher_request_alloc(atfm, GFP_NOFS); + req = ablkcipher_request_alloc(tfm, GFP_NOFS); if (!req) { printk_ratelimited(KERN_ERR "%s: crypto_request_alloc() failed\n", @@ -384,6 +311,15 @@ static int ext4_page_crypto(struct ext4_crypto_ctx *ctx, return 0; } +static struct page *alloc_bounce_page(struct ext4_crypto_ctx *ctx) +{ + ctx->w.bounce_page = mempool_alloc(ext4_bounce_page_pool, GFP_NOWAIT); + if (ctx->w.bounce_page == NULL) + return ERR_PTR(-ENOMEM); + ctx->flags |= EXT4_WRITE_PATH_FL; + return ctx->w.bounce_page; +} + /** * ext4_encrypt() - Encrypts a page * @inode: The inode for which the encryption should take place @@ -413,27 +349,17 @@ struct page *ext4_encrypt(struct inode *inode, return (struct page *) ctx; /* The encryption operation will require a bounce page. */ - ciphertext_page = alloc_page(GFP_NOFS); - if (!ciphertext_page) { - /* This is a potential bottleneck, but at least we'll have - * forward progress. */ - ciphertext_page = mempool_alloc(ext4_bounce_page_pool, - GFP_NOFS); - if (WARN_ON_ONCE(!ciphertext_page)) { - ciphertext_page = mempool_alloc(ext4_bounce_page_pool, - GFP_NOFS | __GFP_WAIT); - } - ctx->flags &= ~EXT4_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL; - } else { - ctx->flags |= EXT4_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL; - } - ctx->bounce_page = ciphertext_page; - ctx->control_page = plaintext_page; + ciphertext_page = alloc_bounce_page(ctx); + if (IS_ERR(ciphertext_page)) + goto errout; + ctx->w.control_page = plaintext_page; err = ext4_page_crypto(ctx, inode, EXT4_ENCRYPT, plaintext_page->index, plaintext_page, ciphertext_page); if (err) { + ciphertext_page = ERR_PTR(err); + errout: ext4_release_crypto_ctx(ctx); - return ERR_PTR(err); + return ciphertext_page; } SetPagePrivate(ciphertext_page); set_page_private(ciphertext_page, (unsigned long)ctx); @@ -470,8 +396,8 @@ int ext4_decrypt_one(struct inode *inode, struct page *page) struct ext4_crypto_ctx *ctx = ext4_get_crypto_ctx(inode); - if (!ctx) - return -ENOMEM; + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ret = ext4_decrypt(ctx, page); ext4_release_crypto_ctx(ctx); return ret; @@ -493,21 +419,11 @@ int ext4_encrypted_zeroout(struct inode *inode, struct ext4_extent *ex) if (IS_ERR(ctx)) return PTR_ERR(ctx); - ciphertext_page = alloc_page(GFP_NOFS); - if (!ciphertext_page) { - /* This is a potential bottleneck, but at least we'll have - * forward progress. */ - ciphertext_page = mempool_alloc(ext4_bounce_page_pool, - GFP_NOFS); - if (WARN_ON_ONCE(!ciphertext_page)) { - ciphertext_page = mempool_alloc(ext4_bounce_page_pool, - GFP_NOFS | __GFP_WAIT); - } - ctx->flags &= ~EXT4_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL; - } else { - ctx->flags |= EXT4_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL; + ciphertext_page = alloc_bounce_page(ctx); + if (IS_ERR(ciphertext_page)) { + err = PTR_ERR(ciphertext_page); + goto errout; } - ctx->bounce_page = ciphertext_page; while (len--) { err = ext4_page_crypto(ctx, inode, EXT4_ENCRYPT, lblk, @@ -529,6 +445,7 @@ int ext4_encrypted_zeroout(struct inode *inode, struct ext4_extent *ex) goto errout; } err = submit_bio_wait(WRITE, bio); + bio_put(bio); if (err) goto errout; } diff --git a/fs/ext4/crypto_fname.c b/fs/ext4/crypto_fname.c index fded02f72..7dc4eb559 100644 --- a/fs/ext4/crypto_fname.c +++ b/fs/ext4/crypto_fname.c @@ -48,6 +48,12 @@ bool ext4_valid_filenames_enc_mode(uint32_t mode) return (mode == EXT4_ENCRYPTION_MODE_AES_256_CTS); } +static unsigned max_name_len(struct inode *inode) +{ + return S_ISLNK(inode->i_mode) ? inode->i_sb->s_blocksize : + EXT4_NAME_LEN; +} + /** * ext4_fname_encrypt() - * @@ -55,43 +61,52 @@ bool ext4_valid_filenames_enc_mode(uint32_t mode) * ciphertext. Errors are returned as negative numbers. We trust the caller to * allocate sufficient memory to oname string. */ -static int ext4_fname_encrypt(struct ext4_fname_crypto_ctx *ctx, +static int ext4_fname_encrypt(struct inode *inode, const struct qstr *iname, struct ext4_str *oname) { u32 ciphertext_len; struct ablkcipher_request *req = NULL; DECLARE_EXT4_COMPLETION_RESULT(ecr); - struct crypto_ablkcipher *tfm = ctx->ctfm; + struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info; + struct crypto_ablkcipher *tfm = ci->ci_ctfm; int res = 0; char iv[EXT4_CRYPTO_BLOCK_SIZE]; - struct scatterlist sg[1]; - int padding = 4 << (ctx->flags & EXT4_POLICY_FLAGS_PAD_MASK); - char *workbuf; + struct scatterlist src_sg, dst_sg; + int padding = 4 << (ci->ci_flags & EXT4_POLICY_FLAGS_PAD_MASK); + char *workbuf, buf[32], *alloc_buf = NULL; + unsigned lim = max_name_len(inode); - if (iname->len <= 0 || iname->len > ctx->lim) + if (iname->len <= 0 || iname->len > lim) return -EIO; ciphertext_len = (iname->len < EXT4_CRYPTO_BLOCK_SIZE) ? EXT4_CRYPTO_BLOCK_SIZE : iname->len; ciphertext_len = ext4_fname_crypto_round_up(ciphertext_len, padding); - ciphertext_len = (ciphertext_len > ctx->lim) - ? ctx->lim : ciphertext_len; + ciphertext_len = (ciphertext_len > lim) + ? lim : ciphertext_len; + + if (ciphertext_len <= sizeof(buf)) { + workbuf = buf; + } else { + alloc_buf = kmalloc(ciphertext_len, GFP_NOFS); + if (!alloc_buf) + return -ENOMEM; + workbuf = alloc_buf; + } /* Allocate request */ req = ablkcipher_request_alloc(tfm, GFP_NOFS); if (!req) { printk_ratelimited( KERN_ERR "%s: crypto_request_alloc() failed\n", __func__); + kfree(alloc_buf); return -ENOMEM; } ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, ext4_dir_crypt_complete, &ecr); - /* Map the workpage */ - workbuf = kmap(ctx->workpage); - /* Copy the input */ memcpy(workbuf, iname->name, iname->len); if (iname->len < ciphertext_len) @@ -101,21 +116,16 @@ static int ext4_fname_encrypt(struct ext4_fname_crypto_ctx *ctx, memset(iv, 0, EXT4_CRYPTO_BLOCK_SIZE); /* Create encryption request */ - sg_init_table(sg, 1); - sg_set_page(sg, ctx->workpage, PAGE_SIZE, 0); - ablkcipher_request_set_crypt(req, sg, sg, ciphertext_len, iv); + sg_init_one(&src_sg, workbuf, ciphertext_len); + sg_init_one(&dst_sg, oname->name, ciphertext_len); + ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, ciphertext_len, iv); res = crypto_ablkcipher_encrypt(req); if (res == -EINPROGRESS || res == -EBUSY) { BUG_ON(req->base.data != &ecr); wait_for_completion(&ecr.completion); res = ecr.res; } - if (res >= 0) { - /* Copy the result to output */ - memcpy(oname->name, workbuf, ciphertext_len); - res = ciphertext_len; - } - kunmap(ctx->workpage); + kfree(alloc_buf); ablkcipher_request_free(req); if (res < 0) { printk_ratelimited( @@ -132,20 +142,21 @@ static int ext4_fname_encrypt(struct ext4_fname_crypto_ctx *ctx, * Errors are returned as negative numbers. * We trust the caller to allocate sufficient memory to oname string. */ -static int ext4_fname_decrypt(struct ext4_fname_crypto_ctx *ctx, +static int ext4_fname_decrypt(struct inode *inode, const struct ext4_str *iname, struct ext4_str *oname) { struct ext4_str tmp_in[2], tmp_out[1]; struct ablkcipher_request *req = NULL; DECLARE_EXT4_COMPLETION_RESULT(ecr); - struct scatterlist sg[1]; - struct crypto_ablkcipher *tfm = ctx->ctfm; + struct scatterlist src_sg, dst_sg; + struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info; + struct crypto_ablkcipher *tfm = ci->ci_ctfm; int res = 0; char iv[EXT4_CRYPTO_BLOCK_SIZE]; - char *workbuf; + unsigned lim = max_name_len(inode); - if (iname->len <= 0 || iname->len > ctx->lim) + if (iname->len <= 0 || iname->len > lim) return -EIO; tmp_in[0].name = iname->name; @@ -163,31 +174,19 @@ static int ext4_fname_decrypt(struct ext4_fname_crypto_ctx *ctx, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, ext4_dir_crypt_complete, &ecr); - /* Map the workpage */ - workbuf = kmap(ctx->workpage); - - /* Copy the input */ - memcpy(workbuf, iname->name, iname->len); - /* Initialize IV */ memset(iv, 0, EXT4_CRYPTO_BLOCK_SIZE); /* Create encryption request */ - sg_init_table(sg, 1); - sg_set_page(sg, ctx->workpage, PAGE_SIZE, 0); - ablkcipher_request_set_crypt(req, sg, sg, iname->len, iv); + sg_init_one(&src_sg, iname->name, iname->len); + sg_init_one(&dst_sg, oname->name, oname->len); + ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, iname->len, iv); res = crypto_ablkcipher_decrypt(req); if (res == -EINPROGRESS || res == -EBUSY) { BUG_ON(req->base.data != &ecr); wait_for_completion(&ecr.completion); res = ecr.res; } - if (res >= 0) { - /* Copy the result to output */ - memcpy(oname->name, workbuf, iname->len); - res = iname->len; - } - kunmap(ctx->workpage); ablkcipher_request_free(req); if (res < 0) { printk_ratelimited( @@ -254,207 +253,6 @@ static int digest_decode(const char *src, int len, char *dst) } /** - * ext4_free_fname_crypto_ctx() - - * - * Frees up a crypto context. - */ -void ext4_free_fname_crypto_ctx(struct ext4_fname_crypto_ctx *ctx) -{ - if (ctx == NULL || IS_ERR(ctx)) - return; - - if (ctx->ctfm && !IS_ERR(ctx->ctfm)) - crypto_free_ablkcipher(ctx->ctfm); - if (ctx->htfm && !IS_ERR(ctx->htfm)) - crypto_free_hash(ctx->htfm); - if (ctx->workpage && !IS_ERR(ctx->workpage)) - __free_page(ctx->workpage); - kfree(ctx); -} - -/** - * ext4_put_fname_crypto_ctx() - - * - * Return: The crypto context onto free list. If the free list is above a - * threshold, completely frees up the context, and returns the memory. - * - * TODO: Currently we directly free the crypto context. Eventually we should - * add code it to return to free list. Such an approach will increase - * efficiency of directory lookup. - */ -void ext4_put_fname_crypto_ctx(struct ext4_fname_crypto_ctx **ctx) -{ - if (*ctx == NULL || IS_ERR(*ctx)) - return; - ext4_free_fname_crypto_ctx(*ctx); - *ctx = NULL; -} - -/** - * ext4_search_fname_crypto_ctx() - - */ -static struct ext4_fname_crypto_ctx *ext4_search_fname_crypto_ctx( - const struct ext4_encryption_key *key) -{ - return NULL; -} - -/** - * ext4_alloc_fname_crypto_ctx() - - */ -struct ext4_fname_crypto_ctx *ext4_alloc_fname_crypto_ctx( - const struct ext4_encryption_key *key) -{ - struct ext4_fname_crypto_ctx *ctx; - - ctx = kmalloc(sizeof(struct ext4_fname_crypto_ctx), GFP_NOFS); - if (ctx == NULL) - return ERR_PTR(-ENOMEM); - if (key->mode == EXT4_ENCRYPTION_MODE_INVALID) { - /* This will automatically set key mode to invalid - * As enum for ENCRYPTION_MODE_INVALID is zero */ - memset(&ctx->key, 0, sizeof(ctx->key)); - } else { - memcpy(&ctx->key, key, sizeof(struct ext4_encryption_key)); - } - ctx->has_valid_key = (EXT4_ENCRYPTION_MODE_INVALID == key->mode) - ? 0 : 1; - ctx->ctfm_key_is_ready = 0; - ctx->ctfm = NULL; - ctx->htfm = NULL; - ctx->workpage = NULL; - return ctx; -} - -/** - * ext4_get_fname_crypto_ctx() - - * - * Allocates a free crypto context and initializes it to hold - * the crypto material for the inode. - * - * Return: NULL if not encrypted. Error value on error. Valid pointer otherwise. - */ -struct ext4_fname_crypto_ctx *ext4_get_fname_crypto_ctx( - struct inode *inode, u32 max_ciphertext_len) -{ - struct ext4_fname_crypto_ctx *ctx; - struct ext4_inode_info *ei = EXT4_I(inode); - int res; - - /* Check if the crypto policy is set on the inode */ - res = ext4_encrypted_inode(inode); - if (res == 0) - return NULL; - - if (!ext4_has_encryption_key(inode)) - ext4_generate_encryption_key(inode); - - /* Get a crypto context based on the key. - * A new context is allocated if no context matches the requested key. - */ - ctx = ext4_search_fname_crypto_ctx(&(ei->i_encryption_key)); - if (ctx == NULL) - ctx = ext4_alloc_fname_crypto_ctx(&(ei->i_encryption_key)); - if (IS_ERR(ctx)) - return ctx; - - ctx->flags = ei->i_crypt_policy_flags; - if (ctx->has_valid_key) { - if (ctx->key.mode != EXT4_ENCRYPTION_MODE_AES_256_CTS) { - printk_once(KERN_WARNING - "ext4: unsupported key mode %d\n", - ctx->key.mode); - return ERR_PTR(-ENOKEY); - } - - /* As a first cut, we will allocate new tfm in every call. - * later, we will keep the tfm around, in case the key gets - * re-used */ - if (ctx->ctfm == NULL) { - ctx->ctfm = crypto_alloc_ablkcipher("cts(cbc(aes))", - 0, 0); - } - if (IS_ERR(ctx->ctfm)) { - res = PTR_ERR(ctx->ctfm); - printk( - KERN_DEBUG "%s: error (%d) allocating crypto tfm\n", - __func__, res); - ctx->ctfm = NULL; - ext4_put_fname_crypto_ctx(&ctx); - return ERR_PTR(res); - } - if (ctx->ctfm == NULL) { - printk( - KERN_DEBUG "%s: could not allocate crypto tfm\n", - __func__); - ext4_put_fname_crypto_ctx(&ctx); - return ERR_PTR(-ENOMEM); - } - if (ctx->workpage == NULL) - ctx->workpage = alloc_page(GFP_NOFS); - if (IS_ERR(ctx->workpage)) { - res = PTR_ERR(ctx->workpage); - printk( - KERN_DEBUG "%s: error (%d) allocating work page\n", - __func__, res); - ctx->workpage = NULL; - ext4_put_fname_crypto_ctx(&ctx); - return ERR_PTR(res); - } - if (ctx->workpage == NULL) { - printk( - KERN_DEBUG "%s: could not allocate work page\n", - __func__); - ext4_put_fname_crypto_ctx(&ctx); - return ERR_PTR(-ENOMEM); - } - ctx->lim = max_ciphertext_len; - crypto_ablkcipher_clear_flags(ctx->ctfm, ~0); - crypto_tfm_set_flags(crypto_ablkcipher_tfm(ctx->ctfm), - CRYPTO_TFM_REQ_WEAK_KEY); - - /* If we are lucky, we will get a context that is already - * set up with the right key. Else, we will have to - * set the key */ - if (!ctx->ctfm_key_is_ready) { - /* Since our crypto objectives for filename encryption - * are pretty weak, - * we directly use the inode master key */ - res = crypto_ablkcipher_setkey(ctx->ctfm, - ctx->key.raw, ctx->key.size); - if (res) { - ext4_put_fname_crypto_ctx(&ctx); - return ERR_PTR(-EIO); - } - ctx->ctfm_key_is_ready = 1; - } else { - /* In the current implementation, key should never be - * marked "ready" for a context that has just been - * allocated. So we should never reach here */ - BUG(); - } - } - if (ctx->htfm == NULL) - ctx->htfm = crypto_alloc_hash("sha256", 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(ctx->htfm)) { - res = PTR_ERR(ctx->htfm); - printk(KERN_DEBUG "%s: error (%d) allocating hash tfm\n", - __func__, res); - ctx->htfm = NULL; - ext4_put_fname_crypto_ctx(&ctx); - return ERR_PTR(res); - } - if (ctx->htfm == NULL) { - printk(KERN_DEBUG "%s: could not allocate hash tfm\n", - __func__); - ext4_put_fname_crypto_ctx(&ctx); - return ERR_PTR(-ENOMEM); - } - - return ctx; -} - -/** * ext4_fname_crypto_round_up() - * * Return: The next multiple of block size @@ -464,44 +262,29 @@ u32 ext4_fname_crypto_round_up(u32 size, u32 blksize) return ((size+blksize-1)/blksize)*blksize; } -/** - * ext4_fname_crypto_namelen_on_disk() - - */ -int ext4_fname_crypto_namelen_on_disk(struct ext4_fname_crypto_ctx *ctx, - u32 namelen) +unsigned ext4_fname_encrypted_size(struct inode *inode, u32 ilen) { - u32 ciphertext_len; - int padding = 4 << (ctx->flags & EXT4_POLICY_FLAGS_PAD_MASK); - - if (ctx == NULL) - return -EIO; - if (!(ctx->has_valid_key)) - return -EACCES; - ciphertext_len = (namelen < EXT4_CRYPTO_BLOCK_SIZE) ? - EXT4_CRYPTO_BLOCK_SIZE : namelen; - ciphertext_len = ext4_fname_crypto_round_up(ciphertext_len, padding); - ciphertext_len = (ciphertext_len > ctx->lim) - ? ctx->lim : ciphertext_len; - return (int) ciphertext_len; + struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info; + int padding = 32; + + if (ci) + padding = 4 << (ci->ci_flags & EXT4_POLICY_FLAGS_PAD_MASK); + if (ilen < EXT4_CRYPTO_BLOCK_SIZE) + ilen = EXT4_CRYPTO_BLOCK_SIZE; + return ext4_fname_crypto_round_up(ilen, padding); } -/** - * ext4_fname_crypto_alloc_obuff() - +/* + * ext4_fname_crypto_alloc_buffer() - * * Allocates an output buffer that is sufficient for the crypto operation * specified by the context and the direction. */ -int ext4_fname_crypto_alloc_buffer(struct ext4_fname_crypto_ctx *ctx, +int ext4_fname_crypto_alloc_buffer(struct inode *inode, u32 ilen, struct ext4_str *crypto_str) { - unsigned int olen; - int padding = 4 << (ctx->flags & EXT4_POLICY_FLAGS_PAD_MASK); + unsigned int olen = ext4_fname_encrypted_size(inode, ilen); - if (!ctx) - return -EIO; - if (padding < EXT4_CRYPTO_BLOCK_SIZE) - padding = EXT4_CRYPTO_BLOCK_SIZE; - olen = ext4_fname_crypto_round_up(ilen, padding); crypto_str->len = olen; if (olen < EXT4_FNAME_CRYPTO_DIGEST_SIZE*2) olen = EXT4_FNAME_CRYPTO_DIGEST_SIZE*2; @@ -529,7 +312,7 @@ void ext4_fname_crypto_free_buffer(struct ext4_str *crypto_str) /** * ext4_fname_disk_to_usr() - converts a filename from disk space to user space */ -int _ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx, +int _ext4_fname_disk_to_usr(struct inode *inode, struct dx_hash_info *hinfo, const struct ext4_str *iname, struct ext4_str *oname) @@ -537,8 +320,6 @@ int _ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx, char buf[24]; int ret; - if (ctx == NULL) - return -EIO; if (iname->len < 3) { /*Check for . and .. */ if (iname->name[0] == '.' && iname->name[iname->len-1] == '.') { @@ -548,8 +329,8 @@ int _ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx, return oname->len; } } - if (ctx->has_valid_key) - return ext4_fname_decrypt(ctx, iname, oname); + if (EXT4_I(inode)->i_crypt_info) + return ext4_fname_decrypt(inode, iname, oname); if (iname->len <= EXT4_FNAME_CRYPTO_DIGEST_SIZE) { ret = digest_encode(iname->name, iname->len, oname->name); @@ -568,7 +349,7 @@ int _ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx, return ret + 1; } -int ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx, +int ext4_fname_disk_to_usr(struct inode *inode, struct dx_hash_info *hinfo, const struct ext4_dir_entry_2 *de, struct ext4_str *oname) @@ -576,21 +357,20 @@ int ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx, struct ext4_str iname = {.name = (unsigned char *) de->name, .len = de->name_len }; - return _ext4_fname_disk_to_usr(ctx, hinfo, &iname, oname); + return _ext4_fname_disk_to_usr(inode, hinfo, &iname, oname); } /** * ext4_fname_usr_to_disk() - converts a filename from user space to disk space */ -int ext4_fname_usr_to_disk(struct ext4_fname_crypto_ctx *ctx, +int ext4_fname_usr_to_disk(struct inode *inode, const struct qstr *iname, struct ext4_str *oname) { int res; + struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info; - if (ctx == NULL) - return -EIO; if (iname->len < 3) { /*Check for . and .. */ if (iname->name[0] == '.' && @@ -601,8 +381,8 @@ int ext4_fname_usr_to_disk(struct ext4_fname_crypto_ctx *ctx, return oname->len; } } - if (ctx->has_valid_key) { - res = ext4_fname_encrypt(ctx, iname, oname); + if (ci) { + res = ext4_fname_encrypt(inode, iname, oname); return res; } /* Without a proper key, a user is not allowed to modify the filenames @@ -611,109 +391,79 @@ int ext4_fname_usr_to_disk(struct ext4_fname_crypto_ctx *ctx, return -EACCES; } -/* - * Calculate the htree hash from a filename from user space - */ -int ext4_fname_usr_to_hash(struct ext4_fname_crypto_ctx *ctx, - const struct qstr *iname, - struct dx_hash_info *hinfo) +int ext4_fname_setup_filename(struct inode *dir, const struct qstr *iname, + int lookup, struct ext4_filename *fname) { - struct ext4_str tmp; - int ret = 0; - char buf[EXT4_FNAME_CRYPTO_DIGEST_SIZE+1]; + struct ext4_crypt_info *ci; + int ret = 0, bigname = 0; + + memset(fname, 0, sizeof(struct ext4_filename)); + fname->usr_fname = iname; - if (!ctx || + if (!ext4_encrypted_inode(dir) || ((iname->name[0] == '.') && ((iname->len == 1) || ((iname->name[1] == '.') && (iname->len == 2))))) { - ext4fs_dirhash(iname->name, iname->len, hinfo); + fname->disk_name.name = (unsigned char *) iname->name; + fname->disk_name.len = iname->len; return 0; } - - if (!ctx->has_valid_key && iname->name[0] == '_') { - if (iname->len != 33) - return -ENOENT; - ret = digest_decode(iname->name+1, iname->len, buf); - if (ret != 24) - return -ENOENT; - memcpy(&hinfo->hash, buf, 4); - memcpy(&hinfo->minor_hash, buf + 4, 4); + ret = ext4_get_encryption_info(dir); + if (ret) + return ret; + ci = EXT4_I(dir)->i_crypt_info; + if (ci) { + ret = ext4_fname_crypto_alloc_buffer(dir, iname->len, + &fname->crypto_buf); + if (ret < 0) + return ret; + ret = ext4_fname_encrypt(dir, iname, &fname->crypto_buf); + if (ret < 0) + goto errout; + fname->disk_name.name = fname->crypto_buf.name; + fname->disk_name.len = fname->crypto_buf.len; return 0; } + if (!lookup) + return -EACCES; - if (!ctx->has_valid_key && iname->name[0] != '_') { - if (iname->len > 43) - return -ENOENT; - ret = digest_decode(iname->name, iname->len, buf); - ext4fs_dirhash(buf, ret, hinfo); - return 0; + /* We don't have the key and we are doing a lookup; decode the + * user-supplied name + */ + if (iname->name[0] == '_') + bigname = 1; + if ((bigname && (iname->len != 33)) || + (!bigname && (iname->len > 43))) + return -ENOENT; + + fname->crypto_buf.name = kmalloc(32, GFP_KERNEL); + if (fname->crypto_buf.name == NULL) + return -ENOMEM; + ret = digest_decode(iname->name + bigname, iname->len - bigname, + fname->crypto_buf.name); + if (ret < 0) { + ret = -ENOENT; + goto errout; } - - /* First encrypt the plaintext name */ - ret = ext4_fname_crypto_alloc_buffer(ctx, iname->len, &tmp); - if (ret < 0) - return ret; - - ret = ext4_fname_encrypt(ctx, iname, &tmp); - if (ret >= 0) { - ext4fs_dirhash(tmp.name, tmp.len, hinfo); - ret = 0; + fname->crypto_buf.len = ret; + if (bigname) { + memcpy(&fname->hinfo.hash, fname->crypto_buf.name, 4); + memcpy(&fname->hinfo.minor_hash, fname->crypto_buf.name + 4, 4); + } else { + fname->disk_name.name = fname->crypto_buf.name; + fname->disk_name.len = fname->crypto_buf.len; } - - ext4_fname_crypto_free_buffer(&tmp); + return 0; +errout: + kfree(fname->crypto_buf.name); + fname->crypto_buf.name = NULL; return ret; } -int ext4_fname_match(struct ext4_fname_crypto_ctx *ctx, struct ext4_str *cstr, - int len, const char * const name, - struct ext4_dir_entry_2 *de) +void ext4_fname_free_filename(struct ext4_filename *fname) { - int ret = -ENOENT; - int bigname = (*name == '_'); - - if (ctx->has_valid_key) { - if (cstr->name == NULL) { - struct qstr istr; - - ret = ext4_fname_crypto_alloc_buffer(ctx, len, cstr); - if (ret < 0) - goto errout; - istr.name = name; - istr.len = len; - ret = ext4_fname_encrypt(ctx, &istr, cstr); - if (ret < 0) - goto errout; - } - } else { - if (cstr->name == NULL) { - cstr->name = kmalloc(32, GFP_KERNEL); - if (cstr->name == NULL) - return -ENOMEM; - if ((bigname && (len != 33)) || - (!bigname && (len > 43))) - goto errout; - ret = digest_decode(name+bigname, len-bigname, - cstr->name); - if (ret < 0) { - ret = -ENOENT; - goto errout; - } - cstr->len = ret; - } - if (bigname) { - if (de->name_len < 16) - return 0; - ret = memcmp(de->name + de->name_len - 16, - cstr->name + 8, 16); - return (ret == 0) ? 1 : 0; - } - } - if (de->name_len != cstr->len) - return 0; - ret = memcmp(de->name, cstr->name, cstr->len); - return (ret == 0) ? 1 : 0; -errout: - kfree(cstr->name); - cstr->name = NULL; - return ret; + kfree(fname->crypto_buf.name); + fname->crypto_buf.name = NULL; + fname->usr_fname = NULL; + fname->disk_name.name = NULL; } diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c index 52170d0b7..442d24e8e 100644 --- a/fs/ext4/crypto_key.c +++ b/fs/ext4/crypto_key.c @@ -84,14 +84,38 @@ out: return res; } -/** - * ext4_generate_encryption_key() - generates an encryption key - * @inode: The inode to generate the encryption key for. - */ -int ext4_generate_encryption_key(struct inode *inode) +void ext4_free_crypt_info(struct ext4_crypt_info *ci) +{ + if (!ci) + return; + + if (ci->ci_keyring_key) + key_put(ci->ci_keyring_key); + crypto_free_ablkcipher(ci->ci_ctfm); + kmem_cache_free(ext4_crypt_info_cachep, ci); +} + +void ext4_free_encryption_info(struct inode *inode, + struct ext4_crypt_info *ci) +{ + struct ext4_inode_info *ei = EXT4_I(inode); + struct ext4_crypt_info *prev; + + if (ci == NULL) + ci = ACCESS_ONCE(ei->i_crypt_info); + if (ci == NULL) + return; + prev = cmpxchg(&ei->i_crypt_info, ci, NULL); + if (prev != ci) + return; + + ext4_free_crypt_info(ci); +} + +int _ext4_get_encryption_info(struct inode *inode) { struct ext4_inode_info *ei = EXT4_I(inode); - struct ext4_encryption_key *crypt_key = &ei->i_encryption_key; + struct ext4_crypt_info *crypt_info; char full_key_descriptor[EXT4_KEY_DESC_PREFIX_SIZE + (EXT4_KEY_DESCRIPTOR_SIZE * 2) + 1]; struct key *keyring_key = NULL; @@ -99,31 +123,76 @@ int ext4_generate_encryption_key(struct inode *inode) struct ext4_encryption_context ctx; struct user_key_payload *ukp; struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); - int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION, - EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, - &ctx, sizeof(ctx)); + struct crypto_ablkcipher *ctfm; + const char *cipher_str; + char raw_key[EXT4_MAX_KEY_SIZE]; + char mode; + int res; - if (res != sizeof(ctx)) { - if (res > 0) - res = -EINVAL; - goto out; + if (!ext4_read_workqueue) { + res = ext4_init_crypto(); + if (res) + return res; + } + +retry: + crypt_info = ACCESS_ONCE(ei->i_crypt_info); + if (crypt_info) { + if (!crypt_info->ci_keyring_key || + key_validate(crypt_info->ci_keyring_key) == 0) + return 0; + ext4_free_encryption_info(inode, crypt_info); + goto retry; } + + res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION, + EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, + &ctx, sizeof(ctx)); + if (res < 0) { + if (!DUMMY_ENCRYPTION_ENABLED(sbi)) + return res; + ctx.contents_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS; + ctx.filenames_encryption_mode = + EXT4_ENCRYPTION_MODE_AES_256_CTS; + ctx.flags = 0; + } else if (res != sizeof(ctx)) + return -EINVAL; res = 0; - ei->i_crypt_policy_flags = ctx.flags; + crypt_info = kmem_cache_alloc(ext4_crypt_info_cachep, GFP_KERNEL); + if (!crypt_info) + return -ENOMEM; + + crypt_info->ci_flags = ctx.flags; + crypt_info->ci_data_mode = ctx.contents_encryption_mode; + crypt_info->ci_filename_mode = ctx.filenames_encryption_mode; + crypt_info->ci_ctfm = NULL; + crypt_info->ci_keyring_key = NULL; + memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor, + sizeof(crypt_info->ci_master_key)); if (S_ISREG(inode->i_mode)) - crypt_key->mode = ctx.contents_encryption_mode; + mode = crypt_info->ci_data_mode; else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) - crypt_key->mode = ctx.filenames_encryption_mode; - else { - printk(KERN_ERR "ext4 crypto: Unsupported inode type.\n"); + mode = crypt_info->ci_filename_mode; + else BUG(); + switch (mode) { + case EXT4_ENCRYPTION_MODE_AES_256_XTS: + cipher_str = "xts(aes)"; + break; + case EXT4_ENCRYPTION_MODE_AES_256_CTS: + cipher_str = "cts(cbc(aes))"; + break; + default: + printk_once(KERN_WARNING + "ext4: unsupported key mode %d (ino %u)\n", + mode, (unsigned) inode->i_ino); + res = -ENOKEY; + goto out; } - crypt_key->size = ext4_encryption_key_size(crypt_key->mode); - BUG_ON(!crypt_key->size); if (DUMMY_ENCRYPTION_ENABLED(sbi)) { - memset(crypt_key->raw, 0x42, EXT4_AES_256_XTS_KEY_SIZE); - goto out; + memset(raw_key, 0x42, EXT4_AES_256_XTS_KEY_SIZE); + goto got_key; } memcpy(full_key_descriptor, EXT4_KEY_DESC_PREFIX, EXT4_KEY_DESC_PREFIX_SIZE); @@ -138,6 +207,7 @@ int ext4_generate_encryption_key(struct inode *inode) keyring_key = NULL; goto out; } + crypt_info->ci_keyring_key = keyring_key; BUG_ON(keyring_key->type != &key_type_logon); ukp = ((struct user_key_payload *)keyring_key->payload.data); if (ukp->datalen != sizeof(struct ext4_encryption_key)) { @@ -148,19 +218,43 @@ int ext4_generate_encryption_key(struct inode *inode) BUILD_BUG_ON(EXT4_AES_128_ECB_KEY_SIZE != EXT4_KEY_DERIVATION_NONCE_SIZE); BUG_ON(master_key->size != EXT4_AES_256_XTS_KEY_SIZE); - res = ext4_derive_key_aes(ctx.nonce, master_key->raw, crypt_key->raw); + res = ext4_derive_key_aes(ctx.nonce, master_key->raw, + raw_key); +got_key: + ctfm = crypto_alloc_ablkcipher(cipher_str, 0, 0); + if (!ctfm || IS_ERR(ctfm)) { + res = ctfm ? PTR_ERR(ctfm) : -ENOMEM; + printk(KERN_DEBUG + "%s: error %d (inode %u) allocating crypto tfm\n", + __func__, res, (unsigned) inode->i_ino); + goto out; + } + crypt_info->ci_ctfm = ctfm; + crypto_ablkcipher_clear_flags(ctfm, ~0); + crypto_tfm_set_flags(crypto_ablkcipher_tfm(ctfm), + CRYPTO_TFM_REQ_WEAK_KEY); + res = crypto_ablkcipher_setkey(ctfm, raw_key, + ext4_encryption_key_size(mode)); + if (res) + goto out; + memzero_explicit(raw_key, sizeof(raw_key)); + if (cmpxchg(&ei->i_crypt_info, NULL, crypt_info) != NULL) { + ext4_free_crypt_info(crypt_info); + goto retry; + } + return 0; + out: - if (keyring_key) - key_put(keyring_key); - if (res < 0) - crypt_key->mode = EXT4_ENCRYPTION_MODE_INVALID; + if (res == -ENOKEY) + res = 0; + ext4_free_crypt_info(crypt_info); + memzero_explicit(raw_key, sizeof(raw_key)); return res; } int ext4_has_encryption_key(struct inode *inode) { struct ext4_inode_info *ei = EXT4_I(inode); - struct ext4_encryption_key *crypt_key = &ei->i_encryption_key; - return (crypt_key->mode != EXT4_ENCRYPTION_MODE_INVALID); + return (ei->i_crypt_info != NULL); } diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c index a6d6291ae..02c4e5df7 100644 --- a/fs/ext4/crypto_policy.c +++ b/fs/ext4/crypto_policy.c @@ -51,6 +51,10 @@ static int ext4_create_encryption_context_from_policy( struct ext4_encryption_context ctx; int res = 0; + res = ext4_convert_inline_data(inode); + if (res) + return res; + ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V1; memcpy(ctx.master_key_descriptor, policy->master_key_descriptor, EXT4_KEY_DESCRIPTOR_SIZE); @@ -89,6 +93,8 @@ int ext4_process_policy(const struct ext4_encryption_policy *policy, return -EINVAL; if (!ext4_inode_has_encryption_context(inode)) { + if (!S_ISDIR(inode->i_mode)) + return -EINVAL; if (!ext4_empty_dir(inode)) return -ENOTEMPTY; return ext4_create_encryption_context_from_policy(inode, @@ -126,7 +132,7 @@ int ext4_get_policy(struct inode *inode, struct ext4_encryption_policy *policy) int ext4_is_child_context_consistent_with_parent(struct inode *parent, struct inode *child) { - struct ext4_encryption_context parent_ctx, child_ctx; + struct ext4_crypt_info *parent_ci, *child_ci; int res; if ((parent == NULL) || (child == NULL)) { @@ -136,26 +142,28 @@ int ext4_is_child_context_consistent_with_parent(struct inode *parent, /* no restrictions if the parent directory is not encrypted */ if (!ext4_encrypted_inode(parent)) return 1; - res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION, - EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, - &parent_ctx, sizeof(parent_ctx)); - if (res != sizeof(parent_ctx)) - return 0; /* if the child directory is not encrypted, this is always a problem */ if (!ext4_encrypted_inode(child)) return 0; - res = ext4_xattr_get(child, EXT4_XATTR_INDEX_ENCRYPTION, - EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, - &child_ctx, sizeof(child_ctx)); - if (res != sizeof(child_ctx)) + res = ext4_get_encryption_info(parent); + if (res) return 0; - return (memcmp(parent_ctx.master_key_descriptor, - child_ctx.master_key_descriptor, + res = ext4_get_encryption_info(child); + if (res) + return 0; + parent_ci = EXT4_I(parent)->i_crypt_info; + child_ci = EXT4_I(child)->i_crypt_info; + if (!parent_ci && !child_ci) + return 1; + if (!parent_ci || !child_ci) + return 0; + + return (memcmp(parent_ci->ci_master_key, + child_ci->ci_master_key, EXT4_KEY_DESCRIPTOR_SIZE) == 0 && - (parent_ctx.contents_encryption_mode == - child_ctx.contents_encryption_mode) && - (parent_ctx.filenames_encryption_mode == - child_ctx.filenames_encryption_mode)); + (parent_ci->ci_data_mode == child_ci->ci_data_mode) && + (parent_ci->ci_filename_mode == child_ci->ci_filename_mode) && + (parent_ci->ci_flags == child_ci->ci_flags)); } /** @@ -168,31 +176,40 @@ int ext4_is_child_context_consistent_with_parent(struct inode *parent, int ext4_inherit_context(struct inode *parent, struct inode *child) { struct ext4_encryption_context ctx; - int res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION, - EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, - &ctx, sizeof(ctx)); + struct ext4_crypt_info *ci; + int res; + + res = ext4_get_encryption_info(parent); + if (res < 0) + return res; + ci = EXT4_I(parent)->i_crypt_info; + if (ci == NULL) + return -ENOKEY; - if (res != sizeof(ctx)) { - if (DUMMY_ENCRYPTION_ENABLED(EXT4_SB(parent->i_sb))) { - ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V1; - ctx.contents_encryption_mode = - EXT4_ENCRYPTION_MODE_AES_256_XTS; - ctx.filenames_encryption_mode = - EXT4_ENCRYPTION_MODE_AES_256_CTS; - ctx.flags = 0; - memset(ctx.master_key_descriptor, 0x42, - EXT4_KEY_DESCRIPTOR_SIZE); - res = 0; - } else { - goto out; - } + ctx.format = EXT4_ENCRYPTION_CONTEXT_FORMAT_V1; + if (DUMMY_ENCRYPTION_ENABLED(EXT4_SB(parent->i_sb))) { + ctx.contents_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS; + ctx.filenames_encryption_mode = + EXT4_ENCRYPTION_MODE_AES_256_CTS; + ctx.flags = 0; + memset(ctx.master_key_descriptor, 0x42, + EXT4_KEY_DESCRIPTOR_SIZE); + res = 0; + } else { + ctx.contents_encryption_mode = ci->ci_data_mode; + ctx.filenames_encryption_mode = ci->ci_filename_mode; + ctx.flags = ci->ci_flags; + memcpy(ctx.master_key_descriptor, ci->ci_master_key, + EXT4_KEY_DESCRIPTOR_SIZE); } get_random_bytes(ctx.nonce, EXT4_KEY_DERIVATION_NONCE_SIZE); res = ext4_xattr_set(child, EXT4_XATTR_INDEX_ENCRYPTION, EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx, sizeof(ctx), 0); -out: - if (!res) + if (!res) { ext4_set_inode_flag(child, EXT4_INODE_ENCRYPT); + ext4_clear_inode_state(child, EXT4_STATE_MAY_INLINE_DATA); + res = ext4_get_encryption_info(child); + } return res; } diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index 5665d82d2..f9e149119 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -110,7 +110,6 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) struct super_block *sb = inode->i_sb; struct buffer_head *bh = NULL; int dir_has_error = 0; - struct ext4_fname_crypto_ctx *enc_ctx = NULL; struct ext4_str fname_crypto_str = {.name = NULL, .len = 0}; if (is_dx_dir(inode)) { @@ -134,16 +133,11 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) return err; } - enc_ctx = ext4_get_fname_crypto_ctx(inode, EXT4_NAME_LEN); - if (IS_ERR(enc_ctx)) - return PTR_ERR(enc_ctx); - if (enc_ctx) { - err = ext4_fname_crypto_alloc_buffer(enc_ctx, EXT4_NAME_LEN, + if (ext4_encrypted_inode(inode)) { + err = ext4_fname_crypto_alloc_buffer(inode, EXT4_NAME_LEN, &fname_crypto_str); - if (err < 0) { - ext4_put_fname_crypto_ctx(&enc_ctx); + if (err < 0) return err; - } } offset = ctx->pos & (sb->s_blocksize - 1); @@ -239,17 +233,19 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) offset += ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize); if (le32_to_cpu(de->inode)) { - if (enc_ctx == NULL) { - /* Directory is not encrypted */ + if (!ext4_encrypted_inode(inode)) { if (!dir_emit(ctx, de->name, de->name_len, le32_to_cpu(de->inode), get_dtype(sb, de->file_type))) goto done; } else { + int save_len = fname_crypto_str.len; + /* Directory is encrypted */ - err = ext4_fname_disk_to_usr(enc_ctx, + err = ext4_fname_disk_to_usr(inode, NULL, de, &fname_crypto_str); + fname_crypto_str.len = save_len; if (err < 0) goto errout; if (!dir_emit(ctx, @@ -272,7 +268,6 @@ done: err = 0; errout: #ifdef CONFIG_EXT4_FS_ENCRYPTION - ext4_put_fname_crypto_ctx(&enc_ctx); ext4_fname_crypto_free_buffer(&fname_crypto_str); #endif brelse(bh); @@ -598,6 +593,13 @@ finished: return 0; } +static int ext4_dir_open(struct inode * inode, struct file * filp) +{ + if (ext4_encrypted_inode(inode)) + return ext4_get_encryption_info(inode) ? -EACCES : 0; + return 0; +} + static int ext4_release_dir(struct inode *inode, struct file *filp) { if (filp->private_data) @@ -640,5 +642,6 @@ const struct file_operations ext4_dir_operations = { .compat_ioctl = ext4_compat_ioctl, #endif .fsync = ext4_sync_file, + .open = ext4_dir_open, .release = ext4_release_dir, }; diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 9a83f149a..f5e9f0422 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -69,15 +69,6 @@ #define ext_debug(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif -#define EXT4_ERROR_INODE(inode, fmt, a...) \ - ext4_error_inode((inode), __func__, __LINE__, 0, (fmt), ## a) - -#define EXT4_ERROR_INODE_BLOCK(inode, block, fmt, a...) \ - ext4_error_inode((inode), __func__, __LINE__, (block), (fmt), ## a) - -#define EXT4_ERROR_FILE(file, block, fmt, a...) \ - ext4_error_file((file), __func__, __LINE__, (block), (fmt), ## a) - /* data type for block offset of block group */ typedef int ext4_grpblk_t; @@ -90,6 +81,11 @@ typedef __u32 ext4_lblk_t; /* data type for block group number */ typedef unsigned int ext4_group_t; +enum SHIFT_DIRECTION { + SHIFT_LEFT = 0, + SHIFT_RIGHT, +}; + /* * Flags used in mballoc's allocation_context flags field. * @@ -911,7 +907,6 @@ struct ext4_inode_info { /* on-disk additional length */ __u16 i_extra_isize; - char i_crypt_policy_flags; /* Indicate the inline data space. */ u16 i_inline_off; @@ -955,7 +950,7 @@ struct ext4_inode_info { #ifdef CONFIG_EXT4_FS_ENCRYPTION /* Encryption params */ - struct ext4_encryption_key i_encryption_key; + struct ext4_crypt_info *i_crypt_info; #endif }; @@ -1374,12 +1369,6 @@ struct ext4_sb_info { struct ratelimit_state s_err_ratelimit_state; struct ratelimit_state s_warning_ratelimit_state; struct ratelimit_state s_msg_ratelimit_state; - -#ifdef CONFIG_EXT4_FS_ENCRYPTION - /* Encryption */ - uint32_t s_file_encryption_mode; - uint32_t s_dir_encryption_mode; -#endif }; static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb) @@ -1838,6 +1827,17 @@ struct dx_hash_info */ #define HASH_NB_ALWAYS 1 +struct ext4_filename { + const struct qstr *usr_fname; + struct ext4_str disk_name; + struct dx_hash_info hinfo; +#ifdef CONFIG_EXT4_FS_ENCRYPTION + struct ext4_str crypto_buf; +#endif +}; + +#define fname_name(p) ((p)->disk_name.name) +#define fname_len(p) ((p)->disk_name.len) /* * Describe an inode's exact location on disk and in memory @@ -2054,6 +2054,7 @@ int ext4_get_policy(struct inode *inode, struct ext4_encryption_policy *policy); /* crypto.c */ +extern struct kmem_cache *ext4_crypt_info_cachep; bool ext4_valid_contents_enc_mode(uint32_t mode); uint32_t ext4_validate_encryption_key_size(uint32_t mode, uint32_t size); extern struct workqueue_struct *ext4_read_workqueue; @@ -2085,57 +2086,84 @@ static inline int ext4_sb_has_crypto(struct super_block *sb) /* crypto_fname.c */ bool ext4_valid_filenames_enc_mode(uint32_t mode); u32 ext4_fname_crypto_round_up(u32 size, u32 blksize); -int ext4_fname_crypto_alloc_buffer(struct ext4_fname_crypto_ctx *ctx, +unsigned ext4_fname_encrypted_size(struct inode *inode, u32 ilen); +int ext4_fname_crypto_alloc_buffer(struct inode *inode, u32 ilen, struct ext4_str *crypto_str); -int _ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx, +int _ext4_fname_disk_to_usr(struct inode *inode, struct dx_hash_info *hinfo, const struct ext4_str *iname, struct ext4_str *oname); -int ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx, +int ext4_fname_disk_to_usr(struct inode *inode, struct dx_hash_info *hinfo, const struct ext4_dir_entry_2 *de, struct ext4_str *oname); -int ext4_fname_usr_to_disk(struct ext4_fname_crypto_ctx *ctx, +int ext4_fname_usr_to_disk(struct inode *inode, const struct qstr *iname, struct ext4_str *oname); -int ext4_fname_usr_to_hash(struct ext4_fname_crypto_ctx *ctx, - const struct qstr *iname, - struct dx_hash_info *hinfo); -int ext4_fname_crypto_namelen_on_disk(struct ext4_fname_crypto_ctx *ctx, - u32 namelen); -int ext4_fname_match(struct ext4_fname_crypto_ctx *ctx, struct ext4_str *cstr, - int len, const char * const name, - struct ext4_dir_entry_2 *de); - - #ifdef CONFIG_EXT4_FS_ENCRYPTION -void ext4_put_fname_crypto_ctx(struct ext4_fname_crypto_ctx **ctx); -struct ext4_fname_crypto_ctx *ext4_get_fname_crypto_ctx(struct inode *inode, - u32 max_len); void ext4_fname_crypto_free_buffer(struct ext4_str *crypto_str); +int ext4_fname_setup_filename(struct inode *dir, const struct qstr *iname, + int lookup, struct ext4_filename *fname); +void ext4_fname_free_filename(struct ext4_filename *fname); #else static inline -void ext4_put_fname_crypto_ctx(struct ext4_fname_crypto_ctx **ctx) { } -static inline -struct ext4_fname_crypto_ctx *ext4_get_fname_crypto_ctx(struct inode *inode, - u32 max_len) +int ext4_setup_fname_crypto(struct inode *inode) { - return NULL; + return 0; } static inline void ext4_fname_crypto_free_buffer(struct ext4_str *p) { } +static inline int ext4_fname_setup_filename(struct inode *dir, + const struct qstr *iname, + int lookup, struct ext4_filename *fname) +{ + fname->usr_fname = iname; + fname->disk_name.name = (unsigned char *) iname->name; + fname->disk_name.len = iname->len; + return 0; +} +static inline void ext4_fname_free_filename(struct ext4_filename *fname) { } #endif /* crypto_key.c */ -int ext4_generate_encryption_key(struct inode *inode); +void ext4_free_crypt_info(struct ext4_crypt_info *ci); +void ext4_free_encryption_info(struct inode *inode, struct ext4_crypt_info *ci); +int _ext4_get_encryption_info(struct inode *inode); #ifdef CONFIG_EXT4_FS_ENCRYPTION int ext4_has_encryption_key(struct inode *inode); + +static inline int ext4_get_encryption_info(struct inode *inode) +{ + struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info; + + if (!ci || + (ci->ci_keyring_key && + (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) | + (1 << KEY_FLAG_REVOKED) | + (1 << KEY_FLAG_DEAD))))) + return _ext4_get_encryption_info(inode); + return 0; +} + +static inline struct ext4_crypt_info *ext4_encryption_info(struct inode *inode) +{ + return EXT4_I(inode)->i_crypt_info; +} + #else static inline int ext4_has_encryption_key(struct inode *inode) { return 0; } +static inline int ext4_get_encryption_info(struct inode *inode) +{ + return 0; +} +static inline struct ext4_crypt_info *ext4_encryption_info(struct inode *inode) +{ + return NULL; +} #endif @@ -2156,14 +2184,13 @@ extern void ext4_htree_free_dir_info(struct dir_private_info *p); extern int ext4_find_dest_de(struct inode *dir, struct inode *inode, struct buffer_head *bh, void *buf, int buf_size, - const char *name, int namelen, + struct ext4_filename *fname, struct ext4_dir_entry_2 **dest_de); int ext4_insert_dentry(struct inode *dir, - struct inode *inode, - struct ext4_dir_entry_2 *de, - int buf_size, - const struct qstr *iname, - const char *name, int namelen); + struct inode *inode, + struct ext4_dir_entry_2 *de, + int buf_size, + struct ext4_filename *fname); static inline void ext4_update_dx_flag(struct inode *inode) { if (!EXT4_HAS_COMPAT_FEATURE(inode->i_sb, @@ -2317,13 +2344,14 @@ extern int ext4_orphan_add(handle_t *, struct inode *); extern int ext4_orphan_del(handle_t *, struct inode *); extern int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash, __u32 start_minor_hash, __u32 *next_hash); -extern int search_dir(struct buffer_head *bh, - char *search_buf, - int buf_size, - struct inode *dir, - const struct qstr *d_name, - unsigned int offset, - struct ext4_dir_entry_2 **res_dir); +extern int ext4_search_dir(struct buffer_head *bh, + char *search_buf, + int buf_size, + struct inode *dir, + struct ext4_filename *fname, + const struct qstr *d_name, + unsigned int offset, + struct ext4_dir_entry_2 **res_dir); extern int ext4_generic_delete_entry(handle_t *handle, struct inode *dir, struct ext4_dir_entry_2 *de_del, @@ -2368,6 +2396,9 @@ void __ext4_abort(struct super_block *, const char *, unsigned int, extern __printf(4, 5) void __ext4_warning(struct super_block *, const char *, unsigned int, const char *, ...); +extern __printf(4, 5) +void __ext4_warning_inode(const struct inode *inode, const char *function, + unsigned int line, const char *fmt, ...); extern __printf(3, 4) void __ext4_msg(struct super_block *, const char *, const char *, ...); extern void __dump_mmp_msg(struct super_block *, struct mmp_struct *mmp, @@ -2378,6 +2409,15 @@ void __ext4_grp_locked_error(const char *, unsigned int, unsigned long, ext4_fsblk_t, const char *, ...); +#define EXT4_ERROR_INODE(inode, fmt, a...) \ + ext4_error_inode((inode), __func__, __LINE__, 0, (fmt), ## a) + +#define EXT4_ERROR_INODE_BLOCK(inode, block, fmt, a...) \ + ext4_error_inode((inode), __func__, __LINE__, (block), (fmt), ## a) + +#define EXT4_ERROR_FILE(file, block, fmt, a...) \ + ext4_error_file((file), __func__, __LINE__, (block), (fmt), ## a) + #ifdef CONFIG_PRINTK #define ext4_error_inode(inode, func, line, block, fmt, ...) \ @@ -2390,6 +2430,8 @@ void __ext4_grp_locked_error(const char *, unsigned int, __ext4_abort(sb, __func__, __LINE__, fmt, ##__VA_ARGS__) #define ext4_warning(sb, fmt, ...) \ __ext4_warning(sb, __func__, __LINE__, fmt, ##__VA_ARGS__) +#define ext4_warning_inode(inode, fmt, ...) \ + __ext4_warning_inode(inode, __func__, __LINE__, fmt, ##__VA_ARGS__) #define ext4_msg(sb, level, fmt, ...) \ __ext4_msg(sb, level, fmt, ##__VA_ARGS__) #define dump_mmp_msg(sb, mmp, msg) \ @@ -2425,6 +2467,11 @@ do { \ no_printk(fmt, ##__VA_ARGS__); \ __ext4_warning(sb, "", 0, " "); \ } while (0) +#define ext4_warning_inode(inode, fmt, ...) \ +do { \ + no_printk(fmt, ##__VA_ARGS__); \ + __ext4_warning_inode(inode, "", 0, " "); \ +} while (0) #define ext4_msg(sb, level, fmt, ...) \ do { \ no_printk(fmt, ##__VA_ARGS__); \ @@ -2768,7 +2815,9 @@ extern int ext4_da_write_inline_data_begin(struct address_space *mapping, extern int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len, unsigned copied, struct page *page); -extern int ext4_try_add_inline_entry(handle_t *handle, struct dentry *dentry, +extern int ext4_try_add_inline_entry(handle_t *handle, + struct ext4_filename *fname, + struct dentry *dentry, struct inode *inode); extern int ext4_try_create_inline_dir(handle_t *handle, struct inode *parent, @@ -2782,6 +2831,7 @@ extern int htree_inlinedir_to_tree(struct file *dir_file, __u32 start_hash, __u32 start_minor_hash, int *has_inline_data); extern struct buffer_head *ext4_find_inline_entry(struct inode *dir, + struct ext4_filename *fname, const struct qstr *d_name, struct ext4_dir_entry_2 **res_dir, int *has_inline_data); @@ -2847,6 +2897,7 @@ extern int ext4_mpage_readpages(struct address_space *mapping, unsigned nr_pages); /* symlink.c */ +extern const struct inode_operations ext4_encrypted_symlink_inode_operations; extern const struct inode_operations ext4_symlink_inode_operations; extern const struct inode_operations ext4_fast_symlink_inode_operations; @@ -2912,6 +2963,7 @@ extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, __u64 start, __u64 len); extern int ext4_ext_precache(struct inode *inode); extern int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len); +extern int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len); extern int ext4_swap_extents(handle_t *handle, struct inode *inode1, struct inode *inode2, ext4_lblk_t lblk1, ext4_lblk_t lblk2, ext4_lblk_t count, diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h index d75159c10..ac7d4e813 100644 --- a/fs/ext4/ext4_crypto.h +++ b/fs/ext4/ext4_crypto.h @@ -66,24 +66,39 @@ struct ext4_encryption_context { #define EXT4_KEY_DESC_PREFIX "ext4:" #define EXT4_KEY_DESC_PREFIX_SIZE 5 +/* This is passed in from userspace into the kernel keyring */ struct ext4_encryption_key { - uint32_t mode; - char raw[EXT4_MAX_KEY_SIZE]; - uint32_t size; + __u32 mode; + char raw[EXT4_MAX_KEY_SIZE]; + __u32 size; +} __attribute__((__packed__)); + +struct ext4_crypt_info { + char ci_data_mode; + char ci_filename_mode; + char ci_flags; + struct crypto_ablkcipher *ci_ctfm; + struct key *ci_keyring_key; + char ci_master_key[EXT4_KEY_DESCRIPTOR_SIZE]; }; #define EXT4_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001 -#define EXT4_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL 0x00000002 +#define EXT4_WRITE_PATH_FL 0x00000002 struct ext4_crypto_ctx { - struct crypto_tfm *tfm; /* Crypto API context */ - struct page *bounce_page; /* Ciphertext page on write path */ - struct page *control_page; /* Original page on write path */ - struct bio *bio; /* The bio for this context */ - struct work_struct work; /* Work queue for read complete path */ - struct list_head free_list; /* Free list */ - int flags; /* Flags */ - int mode; /* Encryption mode for tfm */ + union { + struct { + struct page *bounce_page; /* Ciphertext page */ + struct page *control_page; /* Original page */ + } w; + struct { + struct bio *bio; + struct work_struct work; + } r; + struct list_head free_list; /* Free list */ + }; + char flags; /* Flags */ + char mode; /* Encryption mode for tfm */ }; struct ext4_completion_result { @@ -121,18 +136,6 @@ struct ext4_str { u32 len; }; -struct ext4_fname_crypto_ctx { - u32 lim; - char tmp_buf[EXT4_CRYPTO_BLOCK_SIZE]; - struct crypto_ablkcipher *ctfm; - struct crypto_hash *htfm; - struct page *workpage; - struct ext4_encryption_key key; - unsigned flags : 8; - unsigned has_valid_key : 1; - unsigned ctfm_key_is_ready : 1; -}; - /** * For encrypted symlinks, the ciphertext length is stored at the beginning * of the string in little-endian format. diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 87ba10d1d..2553aa8b6 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -39,6 +39,7 @@ #include <linux/slab.h> #include <asm/uaccess.h> #include <linux/fiemap.h> +#include <linux/backing-dev.h> #include "ext4_jbd2.h" #include "ext4_extents.h" #include "xattr.h" @@ -4456,6 +4457,8 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, ar.flags |= EXT4_MB_HINT_NOPREALLOC; if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) ar.flags |= EXT4_MB_DELALLOC_RESERVED; + if (flags & EXT4_GET_BLOCKS_METADATA_NOFAIL) + ar.flags |= EXT4_MB_USE_RESERVED; newblock = ext4_mb_new_blocks(handle, &ar, &err); if (!newblock) goto out2; @@ -4663,6 +4666,7 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset, int ret = 0; int ret2 = 0; int retries = 0; + int depth = 0; struct ext4_map_blocks map; unsigned int credits; loff_t epos; @@ -4677,13 +4681,32 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset, if (len <= EXT_UNWRITTEN_MAX_LEN) flags |= EXT4_GET_BLOCKS_NO_NORMALIZE; + /* Wait all existing dio workers, newcomers will block on i_mutex */ + ext4_inode_block_unlocked_dio(inode); + inode_dio_wait(inode); + /* * credits to insert 1 extent into extent tree */ credits = ext4_chunk_trans_blocks(inode, len); + /* + * We can only call ext_depth() on extent based inodes + */ + if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) + depth = ext_depth(inode); + else + depth = -1; retry: while (ret >= 0 && len) { + /* + * Recalculate credits when extent tree depth changes. + */ + if (depth >= 0 && depth != ext_depth(inode)) { + credits = ext4_chunk_trans_blocks(inode, len); + depth = ext_depth(inode); + } + handle = ext4_journal_start(inode, EXT4_HT_MAP_BLOCKS, credits); if (IS_ERR(handle)) { @@ -4725,6 +4748,8 @@ retry: goto retry; } + ext4_inode_resume_unlocked_dio(inode); + return ret > 0 ? ret2 : ret; } @@ -4912,12 +4937,14 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len) * bug we should fix.... */ if (ext4_encrypted_inode(inode) && - (mode & (FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE))) + (mode & (FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_INSERT_RANGE | + FALLOC_FL_ZERO_RANGE))) return -EOPNOTSUPP; /* Return error if mode is not supported */ if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | - FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE)) + FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE | + FALLOC_FL_INSERT_RANGE)) return -EOPNOTSUPP; if (mode & FALLOC_FL_PUNCH_HOLE) @@ -4930,6 +4957,9 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len) if (mode & FALLOC_FL_COLLAPSE_RANGE) return ext4_collapse_range(inode, offset, len); + if (mode & FALLOC_FL_INSERT_RANGE) + return ext4_insert_range(inode, offset, len); + if (mode & FALLOC_FL_ZERO_RANGE) return ext4_zero_range(file, offset, len, mode); @@ -5224,13 +5254,13 @@ ext4_access_path(handle_t *handle, struct inode *inode, /* * ext4_ext_shift_path_extents: * Shift the extents of a path structure lying between path[depth].p_ext - * and EXT_LAST_EXTENT(path[depth].p_hdr) downwards, by subtracting shift - * from starting block for each extent. + * and EXT_LAST_EXTENT(path[depth].p_hdr), by @shift blocks. @SHIFT tells + * if it is right shift or left shift operation. */ static int ext4_ext_shift_path_extents(struct ext4_ext_path *path, ext4_lblk_t shift, struct inode *inode, handle_t *handle, - ext4_lblk_t *start) + enum SHIFT_DIRECTION SHIFT) { int depth, err = 0; struct ext4_extent *ex_start, *ex_last; @@ -5252,19 +5282,25 @@ ext4_ext_shift_path_extents(struct ext4_ext_path *path, ext4_lblk_t shift, if (ex_start == EXT_FIRST_EXTENT(path[depth].p_hdr)) update = 1; - *start = le32_to_cpu(ex_last->ee_block) + - ext4_ext_get_actual_len(ex_last); - while (ex_start <= ex_last) { - le32_add_cpu(&ex_start->ee_block, -shift); - /* Try to merge to the left. */ - if ((ex_start > - EXT_FIRST_EXTENT(path[depth].p_hdr)) && - ext4_ext_try_to_merge_right(inode, - path, ex_start - 1)) + if (SHIFT == SHIFT_LEFT) { + le32_add_cpu(&ex_start->ee_block, + -shift); + /* Try to merge to the left. */ + if ((ex_start > + EXT_FIRST_EXTENT(path[depth].p_hdr)) + && + ext4_ext_try_to_merge_right(inode, + path, ex_start - 1)) + ex_last--; + else + ex_start++; + } else { + le32_add_cpu(&ex_last->ee_block, shift); + ext4_ext_try_to_merge_right(inode, path, + ex_last); ex_last--; - else - ex_start++; + } } err = ext4_ext_dirty(handle, inode, path + depth); if (err) @@ -5279,7 +5315,10 @@ ext4_ext_shift_path_extents(struct ext4_ext_path *path, ext4_lblk_t shift, if (err) goto out; - le32_add_cpu(&path[depth].p_idx->ei_block, -shift); + if (SHIFT == SHIFT_LEFT) + le32_add_cpu(&path[depth].p_idx->ei_block, -shift); + else + le32_add_cpu(&path[depth].p_idx->ei_block, shift); err = ext4_ext_dirty(handle, inode, path + depth); if (err) goto out; @@ -5297,19 +5336,20 @@ out: /* * ext4_ext_shift_extents: - * All the extents which lies in the range from start to the last allocated - * block for the file are shifted downwards by shift blocks. + * All the extents which lies in the range from @start to the last allocated + * block for the @inode are shifted either towards left or right (depending + * upon @SHIFT) by @shift blocks. * On success, 0 is returned, error otherwise. */ static int ext4_ext_shift_extents(struct inode *inode, handle_t *handle, - ext4_lblk_t start, ext4_lblk_t shift) + ext4_lblk_t start, ext4_lblk_t shift, + enum SHIFT_DIRECTION SHIFT) { struct ext4_ext_path *path; int ret = 0, depth; struct ext4_extent *extent; - ext4_lblk_t stop_block; - ext4_lblk_t ex_start, ex_end; + ext4_lblk_t stop, *iterator, ex_start, ex_end; /* Let path point to the last extent */ path = ext4_find_extent(inode, EXT_MAX_BLOCKS - 1, NULL, 0); @@ -5321,58 +5361,84 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle, if (!extent) goto out; - stop_block = le32_to_cpu(extent->ee_block) + + stop = le32_to_cpu(extent->ee_block) + ext4_ext_get_actual_len(extent); - /* Nothing to shift, if hole is at the end of file */ - if (start >= stop_block) - goto out; + /* + * In case of left shift, Don't start shifting extents until we make + * sure the hole is big enough to accommodate the shift. + */ + if (SHIFT == SHIFT_LEFT) { + path = ext4_find_extent(inode, start - 1, &path, 0); + if (IS_ERR(path)) + return PTR_ERR(path); + depth = path->p_depth; + extent = path[depth].p_ext; + if (extent) { + ex_start = le32_to_cpu(extent->ee_block); + ex_end = le32_to_cpu(extent->ee_block) + + ext4_ext_get_actual_len(extent); + } else { + ex_start = 0; + ex_end = 0; + } - /* - * Don't start shifting extents until we make sure the hole is big - * enough to accomodate the shift. - */ - path = ext4_find_extent(inode, start - 1, &path, 0); - if (IS_ERR(path)) - return PTR_ERR(path); - depth = path->p_depth; - extent = path[depth].p_ext; - if (extent) { - ex_start = le32_to_cpu(extent->ee_block); - ex_end = le32_to_cpu(extent->ee_block) + - ext4_ext_get_actual_len(extent); - } else { - ex_start = 0; - ex_end = 0; + if ((start == ex_start && shift > ex_start) || + (shift > start - ex_end)) { + ext4_ext_drop_refs(path); + kfree(path); + return -EINVAL; + } } - if ((start == ex_start && shift > ex_start) || - (shift > start - ex_end)) - return -EINVAL; + /* + * In case of left shift, iterator points to start and it is increased + * till we reach stop. In case of right shift, iterator points to stop + * and it is decreased till we reach start. + */ + if (SHIFT == SHIFT_LEFT) + iterator = &start; + else + iterator = &stop; /* Its safe to start updating extents */ - while (start < stop_block) { - path = ext4_find_extent(inode, start, &path, 0); + while (start < stop) { + path = ext4_find_extent(inode, *iterator, &path, 0); if (IS_ERR(path)) return PTR_ERR(path); depth = path->p_depth; extent = path[depth].p_ext; if (!extent) { EXT4_ERROR_INODE(inode, "unexpected hole at %lu", - (unsigned long) start); + (unsigned long) *iterator); return -EIO; } - if (start > le32_to_cpu(extent->ee_block)) { + if (SHIFT == SHIFT_LEFT && *iterator > + le32_to_cpu(extent->ee_block)) { /* Hole, move to the next extent */ if (extent < EXT_LAST_EXTENT(path[depth].p_hdr)) { path[depth].p_ext++; } else { - start = ext4_ext_next_allocated_block(path); + *iterator = ext4_ext_next_allocated_block(path); continue; } } + + if (SHIFT == SHIFT_LEFT) { + extent = EXT_LAST_EXTENT(path[depth].p_hdr); + *iterator = le32_to_cpu(extent->ee_block) + + ext4_ext_get_actual_len(extent); + } else { + extent = EXT_FIRST_EXTENT(path[depth].p_hdr); + *iterator = le32_to_cpu(extent->ee_block) > 0 ? + le32_to_cpu(extent->ee_block) - 1 : 0; + /* Update path extent in case we need to stop */ + while (le32_to_cpu(extent->ee_block) < start) + extent++; + path[depth].p_ext = extent; + } ret = ext4_ext_shift_path_extents(path, shift, inode, - handle, &start); + handle, SHIFT); if (ret) break; } @@ -5485,7 +5551,7 @@ int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len) ext4_discard_preallocations(inode); ret = ext4_ext_shift_extents(inode, handle, punch_stop, - punch_stop - punch_start); + punch_stop - punch_start, SHIFT_LEFT); if (ret) { up_write(&EXT4_I(inode)->i_data_sem); goto out_stop; @@ -5510,6 +5576,174 @@ out_mutex: return ret; } +/* + * ext4_insert_range: + * This function implements the FALLOC_FL_INSERT_RANGE flag of fallocate. + * The data blocks starting from @offset to the EOF are shifted by @len + * towards right to create a hole in the @inode. Inode size is increased + * by len bytes. + * Returns 0 on success, error otherwise. + */ +int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len) +{ + struct super_block *sb = inode->i_sb; + handle_t *handle; + struct ext4_ext_path *path; + struct ext4_extent *extent; + ext4_lblk_t offset_lblk, len_lblk, ee_start_lblk = 0; + unsigned int credits, ee_len; + int ret = 0, depth, split_flag = 0; + loff_t ioffset; + + /* + * We need to test this early because xfstests assumes that an + * insert range of (0, 1) will return EOPNOTSUPP if the file + * system does not support insert range. + */ + if (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) + return -EOPNOTSUPP; + + /* Insert range works only on fs block size aligned offsets. */ + if (offset & (EXT4_CLUSTER_SIZE(sb) - 1) || + len & (EXT4_CLUSTER_SIZE(sb) - 1)) + return -EINVAL; + + if (!S_ISREG(inode->i_mode)) + return -EOPNOTSUPP; + + trace_ext4_insert_range(inode, offset, len); + + offset_lblk = offset >> EXT4_BLOCK_SIZE_BITS(sb); + len_lblk = len >> EXT4_BLOCK_SIZE_BITS(sb); + + /* Call ext4_force_commit to flush all data in case of data=journal */ + if (ext4_should_journal_data(inode)) { + ret = ext4_force_commit(inode->i_sb); + if (ret) + return ret; + } + + /* + * Need to round down to align start offset to page size boundary + * for page size > block size. + */ + ioffset = round_down(offset, PAGE_SIZE); + + /* Write out all dirty pages */ + ret = filemap_write_and_wait_range(inode->i_mapping, ioffset, + LLONG_MAX); + if (ret) + return ret; + + /* Take mutex lock */ + mutex_lock(&inode->i_mutex); + + /* Currently just for extent based files */ + if (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) { + ret = -EOPNOTSUPP; + goto out_mutex; + } + + /* Check for wrap through zero */ + if (inode->i_size + len > inode->i_sb->s_maxbytes) { + ret = -EFBIG; + goto out_mutex; + } + + /* Offset should be less than i_size */ + if (offset >= i_size_read(inode)) { + ret = -EINVAL; + goto out_mutex; + } + + truncate_pagecache(inode, ioffset); + + /* Wait for existing dio to complete */ + ext4_inode_block_unlocked_dio(inode); + inode_dio_wait(inode); + + credits = ext4_writepage_trans_blocks(inode); + handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE, credits); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + goto out_dio; + } + + /* Expand file to avoid data loss if there is error while shifting */ + inode->i_size += len; + EXT4_I(inode)->i_disksize += len; + inode->i_mtime = inode->i_ctime = ext4_current_time(inode); + ret = ext4_mark_inode_dirty(handle, inode); + if (ret) + goto out_stop; + + down_write(&EXT4_I(inode)->i_data_sem); + ext4_discard_preallocations(inode); + + path = ext4_find_extent(inode, offset_lblk, NULL, 0); + if (IS_ERR(path)) { + up_write(&EXT4_I(inode)->i_data_sem); + goto out_stop; + } + + depth = ext_depth(inode); + extent = path[depth].p_ext; + if (extent) { + ee_start_lblk = le32_to_cpu(extent->ee_block); + ee_len = ext4_ext_get_actual_len(extent); + + /* + * If offset_lblk is not the starting block of extent, split + * the extent @offset_lblk + */ + if ((offset_lblk > ee_start_lblk) && + (offset_lblk < (ee_start_lblk + ee_len))) { + if (ext4_ext_is_unwritten(extent)) + split_flag = EXT4_EXT_MARK_UNWRIT1 | + EXT4_EXT_MARK_UNWRIT2; + ret = ext4_split_extent_at(handle, inode, &path, + offset_lblk, split_flag, + EXT4_EX_NOCACHE | + EXT4_GET_BLOCKS_PRE_IO | + EXT4_GET_BLOCKS_METADATA_NOFAIL); + } + + ext4_ext_drop_refs(path); + kfree(path); + if (ret < 0) { + up_write(&EXT4_I(inode)->i_data_sem); + goto out_stop; + } + } + + ret = ext4_es_remove_extent(inode, offset_lblk, + EXT_MAX_BLOCKS - offset_lblk); + if (ret) { + up_write(&EXT4_I(inode)->i_data_sem); + goto out_stop; + } + + /* + * if offset_lblk lies in a hole which is at start of file, use + * ee_start_lblk to shift extents + */ + ret = ext4_ext_shift_extents(inode, handle, + ee_start_lblk > offset_lblk ? ee_start_lblk : offset_lblk, + len_lblk, SHIFT_RIGHT); + + up_write(&EXT4_I(inode)->i_data_sem); + if (IS_SYNC(inode)) + ext4_handle_sync(handle); + +out_stop: + ext4_journal_stop(handle); +out_dio: + ext4_inode_resume_unlocked_dio(inode); +out_mutex: + mutex_unlock(&inode->i_mutex); + return ret; +} + /** * ext4_swap_extents - Swap extents between two inodes * @@ -5542,7 +5776,7 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1, BUG_ON(!rwsem_is_locked(&EXT4_I(inode1)->i_data_sem)); BUG_ON(!rwsem_is_locked(&EXT4_I(inode2)->i_data_sem)); BUG_ON(!mutex_is_locked(&inode1->i_mutex)); - BUG_ON(!mutex_is_locked(&inode1->i_mutex)); + BUG_ON(!mutex_is_locked(&inode2->i_mutex)); *erp = ext4_es_remove_extent(inode1, lblk1, count); if (unlikely(*erp)) diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 0613c256c..bc313ac5d 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -192,15 +192,27 @@ out: } #ifdef CONFIG_FS_DAX +static void ext4_end_io_unwritten(struct buffer_head *bh, int uptodate) +{ + struct inode *inode = bh->b_assoc_map->host; + /* XXX: breaks on 32-bit > 16GB. Is that even supported? */ + loff_t offset = (loff_t)(uintptr_t)bh->b_private << inode->i_blkbits; + int err; + if (!uptodate) + return; + WARN_ON(!buffer_unwritten(bh)); + err = ext4_convert_unwritten_extents(NULL, inode, offset, bh->b_size); +} + static int ext4_dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { - return dax_fault(vma, vmf, ext4_get_block); + return dax_fault(vma, vmf, ext4_get_block, ext4_end_io_unwritten); /* Is this the right get_block? */ } static int ext4_dax_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { - return dax_mkwrite(vma, vmf, ext4_get_block); + return dax_mkwrite(vma, vmf, ext4_get_block, ext4_end_io_unwritten); } static const struct vm_operations_struct ext4_dax_vm_ops = { @@ -223,9 +235,11 @@ static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma) struct inode *inode = file->f_mapping->host; if (ext4_encrypted_inode(inode)) { - int err = ext4_generate_encryption_key(inode); + int err = ext4_get_encryption_info(inode); if (err) return 0; + if (ext4_encryption_info(inode) == NULL) + return -ENOKEY; } file_accessed(file); if (IS_DAX(file_inode(file))) { @@ -278,6 +292,13 @@ static int ext4_file_open(struct inode * inode, struct file * filp) ext4_journal_stop(handle); } } + if (ext4_encrypted_inode(inode)) { + ret = ext4_get_encryption_info(inode); + if (ret) + return -EACCES; + if (ext4_encryption_info(inode) == NULL) + return -ENOKEY; + } /* * Set up the jbd2_inode if we are opening the inode for * writing and the journal is present @@ -287,13 +308,7 @@ static int ext4_file_open(struct inode * inode, struct file * filp) if (ret < 0) return ret; } - ret = dquot_file_open(inode, filp); - if (!ret && ext4_encrypted_inode(inode)) { - ret = ext4_generate_encryption_key(inode); - if (ret) - ret = -EACCES; - } - return ret; + return dquot_file_open(inode, filp); } /* diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 1eaa6cb96..173c1ae21 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -726,11 +726,25 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir, ext4_group_t i; ext4_group_t flex_group; struct ext4_group_info *grp; + int encrypt = 0; /* Cannot create files in a deleted directory */ if (!dir || !dir->i_nlink) return ERR_PTR(-EPERM); + if ((ext4_encrypted_inode(dir) || + DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb))) && + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) { + err = ext4_get_encryption_info(dir); + if (err) + return ERR_PTR(err); + if (ext4_encryption_info(dir) == NULL) + return ERR_PTR(-EPERM); + if (!handle) + nblocks += EXT4_DATA_TRANS_BLOCKS(dir->i_sb); + encrypt = 1; + } + sb = dir->i_sb; ngroups = ext4_get_groups_count(sb); trace_ext4_request_inode(dir, mode); @@ -996,12 +1010,6 @@ got: ei->i_block_group = group; ei->i_last_alloc_group = ~0; - /* If the directory encrypted, then we should encrypt the inode. */ - if ((S_ISDIR(mode) || S_ISREG(mode) || S_ISLNK(mode)) && - (ext4_encrypted_inode(dir) || - DUMMY_ENCRYPTION_ENABLED(sbi))) - ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT); - ext4_set_inode_flags(inode); if (IS_DIRSYNC(inode)) ext4_handle_sync(handle); @@ -1034,28 +1042,9 @@ got: ext4_set_inode_state(inode, EXT4_STATE_NEW); ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize; -#ifdef CONFIG_EXT4_FS_ENCRYPTION - if ((sbi->s_file_encryption_mode == EXT4_ENCRYPTION_MODE_INVALID) && - (sbi->s_dir_encryption_mode == EXT4_ENCRYPTION_MODE_INVALID)) { - ei->i_inline_off = 0; - if (EXT4_HAS_INCOMPAT_FEATURE(sb, - EXT4_FEATURE_INCOMPAT_INLINE_DATA)) - ext4_set_inode_state(inode, - EXT4_STATE_MAY_INLINE_DATA); - } else { - /* Inline data and encryption are incompatible - * We turn off inline data since encryption is enabled */ - ei->i_inline_off = 1; - if (EXT4_HAS_INCOMPAT_FEATURE(sb, - EXT4_FEATURE_INCOMPAT_INLINE_DATA)) - ext4_clear_inode_state(inode, - EXT4_STATE_MAY_INLINE_DATA); - } -#else ei->i_inline_off = 0; if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_INLINE_DATA)) ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA); -#endif ret = inode; err = dquot_alloc_inode(inode); if (err) @@ -1082,6 +1071,12 @@ got: ei->i_datasync_tid = handle->h_transaction->t_tid; } + if (encrypt) { + err = ext4_inherit_context(dir, inode); + if (err) + goto fail_free_drop; + } + err = ext4_mark_inode_dirty(handle, inode); if (err) { ext4_std_error(sb, err); diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c index 94ae6874c..4f6ac499f 100644 --- a/fs/ext4/indirect.c +++ b/fs/ext4/indirect.c @@ -576,6 +576,8 @@ int ext4_ind_map_blocks(handle_t *handle, struct inode *inode, ar.flags = EXT4_MB_HINT_DATA; if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) ar.flags |= EXT4_MB_DELALLOC_RESERVED; + if (flags & EXT4_GET_BLOCKS_METADATA_NOFAIL) + ar.flags |= EXT4_MB_USE_RESERVED; ar.goal = ext4_find_goal(inode, map->m_lblk, partial); diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index 095c7a258..cd944a7a9 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -995,20 +995,18 @@ void ext4_show_inline_dir(struct inode *dir, struct buffer_head *bh, * and -EEXIST if directory entry already exists. */ static int ext4_add_dirent_to_inline(handle_t *handle, + struct ext4_filename *fname, struct dentry *dentry, struct inode *inode, struct ext4_iloc *iloc, void *inline_start, int inline_size) { struct inode *dir = d_inode(dentry->d_parent); - const char *name = dentry->d_name.name; - int namelen = dentry->d_name.len; int err; struct ext4_dir_entry_2 *de; - err = ext4_find_dest_de(dir, inode, iloc->bh, - inline_start, inline_size, - name, namelen, &de); + err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start, + inline_size, fname, &de); if (err) return err; @@ -1016,8 +1014,7 @@ static int ext4_add_dirent_to_inline(handle_t *handle, err = ext4_journal_get_write_access(handle, iloc->bh); if (err) return err; - ext4_insert_dentry(dir, inode, de, inline_size, &dentry->d_name, - name, namelen); + ext4_insert_dentry(dir, inode, de, inline_size, fname); ext4_show_inline_dir(dir, iloc->bh, inline_start, inline_size); @@ -1248,8 +1245,8 @@ out: * If succeeds, return 0. If not, extended the inline dir and copied data to * the new created block. */ -int ext4_try_add_inline_entry(handle_t *handle, struct dentry *dentry, - struct inode *inode) +int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname, + struct dentry *dentry, struct inode *inode) { int ret, inline_size; void *inline_start; @@ -1268,7 +1265,7 @@ int ext4_try_add_inline_entry(handle_t *handle, struct dentry *dentry, EXT4_INLINE_DOTDOT_SIZE; inline_size = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DOTDOT_SIZE; - ret = ext4_add_dirent_to_inline(handle, dentry, inode, &iloc, + ret = ext4_add_dirent_to_inline(handle, fname, dentry, inode, &iloc, inline_start, inline_size); if (ret != -ENOSPC) goto out; @@ -1289,8 +1286,9 @@ int ext4_try_add_inline_entry(handle_t *handle, struct dentry *dentry, if (inline_size) { inline_start = ext4_get_inline_xattr_pos(dir, &iloc); - ret = ext4_add_dirent_to_inline(handle, dentry, inode, &iloc, - inline_start, inline_size); + ret = ext4_add_dirent_to_inline(handle, fname, dentry, + inode, &iloc, inline_start, + inline_size); if (ret != -ENOSPC) goto out; @@ -1611,6 +1609,7 @@ out: } struct buffer_head *ext4_find_inline_entry(struct inode *dir, + struct ext4_filename *fname, const struct qstr *d_name, struct ext4_dir_entry_2 **res_dir, int *has_inline_data) @@ -1632,8 +1631,8 @@ struct buffer_head *ext4_find_inline_entry(struct inode *dir, inline_start = (void *)ext4_raw_inode(&iloc)->i_block + EXT4_INLINE_DOTDOT_SIZE; inline_size = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DOTDOT_SIZE; - ret = search_dir(iloc.bh, inline_start, inline_size, - dir, d_name, 0, res_dir); + ret = ext4_search_dir(iloc.bh, inline_start, inline_size, + dir, fname, d_name, 0, res_dir); if (ret == 1) goto out_find; if (ret < 0) @@ -1645,8 +1644,8 @@ struct buffer_head *ext4_find_inline_entry(struct inode *dir, inline_start = ext4_get_inline_xattr_pos(dir, &iloc); inline_size = ext4_get_inline_size(dir) - EXT4_MIN_INLINE_DATA_SIZE; - ret = search_dir(iloc.bh, inline_start, inline_size, - dir, d_name, 0, res_dir); + ret = ext4_search_dir(iloc.bh, inline_start, inline_size, + dir, fname, d_name, 0, res_dir); if (ret == 1) goto out_find; diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 966c61482..cecf9aa10 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -656,18 +656,6 @@ has_zeroout: return retval; } -static void ext4_end_io_unwritten(struct buffer_head *bh, int uptodate) -{ - struct inode *inode = bh->b_assoc_map->host; - /* XXX: breaks on 32-bit > 16GB. Is that even supported? */ - loff_t offset = (loff_t)(uintptr_t)bh->b_private << inode->i_blkbits; - int err; - if (!uptodate) - return; - WARN_ON(!buffer_unwritten(bh)); - err = ext4_convert_unwritten_extents(NULL, inode, offset, bh->b_size); -} - /* Maximum number of blocks we map for direct IO at once. */ #define DIO_MAX_BLOCKS 4096 @@ -705,10 +693,15 @@ static int _ext4_get_block(struct inode *inode, sector_t iblock, map_bh(bh, inode->i_sb, map.m_pblk); bh->b_state = (bh->b_state & ~EXT4_MAP_FLAGS) | map.m_flags; - if (IS_DAX(inode) && buffer_unwritten(bh) && !io_end) { + if (IS_DAX(inode) && buffer_unwritten(bh)) { + /* + * dgc: I suspect unwritten conversion on ext4+DAX is + * fundamentally broken here when there are concurrent + * read/write in progress on this inode. + */ + WARN_ON_ONCE(io_end); bh->b_assoc_map = inode->i_mapping; bh->b_private = (void *)(unsigned long)iblock; - bh->b_end_io = ext4_end_io_unwritten; } if (io_end && io_end->flag & EXT4_IO_END_UNWRITTEN) set_buffer_defer_completion(bh); @@ -731,18 +724,18 @@ int ext4_get_block(struct inode *inode, sector_t iblock, * `handle' can be NULL if create is zero */ struct buffer_head *ext4_getblk(handle_t *handle, struct inode *inode, - ext4_lblk_t block, int create) + ext4_lblk_t block, int map_flags) { struct ext4_map_blocks map; struct buffer_head *bh; + int create = map_flags & EXT4_GET_BLOCKS_CREATE; int err; J_ASSERT(handle != NULL || create == 0); map.m_lblk = block; map.m_len = 1; - err = ext4_map_blocks(handle, inode, &map, - create ? EXT4_GET_BLOCKS_CREATE : 0); + err = ext4_map_blocks(handle, inode, &map, map_flags); if (err == 0) return create ? ERR_PTR(-ENOSPC) : NULL; @@ -788,11 +781,11 @@ errout: } struct buffer_head *ext4_bread(handle_t *handle, struct inode *inode, - ext4_lblk_t block, int create) + ext4_lblk_t block, int map_flags) { struct buffer_head *bh; - bh = ext4_getblk(handle, inode, block, create); + bh = ext4_getblk(handle, inode, block, map_flags); if (IS_ERR(bh)) return bh; if (!bh || buffer_uptodate(bh)) @@ -1261,13 +1254,12 @@ static int ext4_journalled_write_end(struct file *file, } /* - * Reserve a single cluster located at lblock + * Reserve space for a single cluster */ -static int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock) +static int ext4_da_reserve_space(struct inode *inode) { struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct ext4_inode_info *ei = EXT4_I(inode); - unsigned int md_needed; int ret; /* @@ -1279,25 +1271,14 @@ static int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock) if (ret) return ret; - /* - * recalculate the amount of metadata blocks to reserve - * in order to allocate nrblocks - * worse case is one extent per block - */ spin_lock(&ei->i_block_reservation_lock); - /* - * ext4_calc_metadata_amount() has side effects, which we have - * to be prepared undo if we fail to claim space. - */ - md_needed = 0; - trace_ext4_da_reserve_space(inode, 0); - if (ext4_claim_free_clusters(sbi, 1, 0)) { spin_unlock(&ei->i_block_reservation_lock); dquot_release_reservation_block(inode, EXT4_C2B(sbi, 1)); return -ENOSPC; } ei->i_reserved_data_blocks++; + trace_ext4_da_reserve_space(inode); spin_unlock(&ei->i_block_reservation_lock); return 0; /* success */ @@ -1575,9 +1556,9 @@ add_delayed: * then we don't need to reserve it again. However we still need * to reserve metadata for every block we're going to write. */ - if (EXT4_SB(inode->i_sb)->s_cluster_ratio <= 1 || + if (EXT4_SB(inode->i_sb)->s_cluster_ratio == 1 || !ext4_find_delalloc_cluster(inode, map->m_lblk)) { - ret = ext4_da_reserve_space(inode, iblock); + ret = ext4_da_reserve_space(inode); if (ret) { /* not enough space to reserve */ retval = ret; @@ -4237,8 +4218,11 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) inode->i_op = &ext4_dir_inode_operations; inode->i_fop = &ext4_dir_operations; } else if (S_ISLNK(inode->i_mode)) { - if (ext4_inode_is_fast_symlink(inode) && - !ext4_encrypted_inode(inode)) { + if (ext4_encrypted_inode(inode)) { + inode->i_op = &ext4_encrypted_symlink_inode_operations; + ext4_set_aops(inode); + } else if (ext4_inode_is_fast_symlink(inode)) { + inode->i_link = (char *)ei->i_data; inode->i_op = &ext4_fast_symlink_inode_operations; nd_terminate_link(ei->i_data, inode->i_size, sizeof(ei->i_data) - 1); @@ -4707,8 +4691,10 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) ext4_journal_stop(handle); } - if (attr->ia_valid & ATTR_SIZE && attr->ia_size != inode->i_size) { + if (attr->ia_valid & ATTR_SIZE) { handle_t *handle; + loff_t oldsize = inode->i_size; + int shrink = (attr->ia_size <= inode->i_size); if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) { struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); @@ -4716,24 +4702,26 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) if (attr->ia_size > sbi->s_bitmap_maxbytes) return -EFBIG; } + if (!S_ISREG(inode->i_mode)) + return -EINVAL; if (IS_I_VERSION(inode) && attr->ia_size != inode->i_size) inode_inc_iversion(inode); - if (S_ISREG(inode->i_mode) && + if (ext4_should_order_data(inode) && (attr->ia_size < inode->i_size)) { - if (ext4_should_order_data(inode)) { - error = ext4_begin_ordered_truncate(inode, + error = ext4_begin_ordered_truncate(inode, attr->ia_size); - if (error) - goto err_out; - } + if (error) + goto err_out; + } + if (attr->ia_size != inode->i_size) { handle = ext4_journal_start(inode, EXT4_HT_INODE, 3); if (IS_ERR(handle)) { error = PTR_ERR(handle); goto err_out; } - if (ext4_handle_valid(handle)) { + if (ext4_handle_valid(handle) && shrink) { error = ext4_orphan_add(handle, inode); orphan = 1; } @@ -4752,15 +4740,13 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) up_write(&EXT4_I(inode)->i_data_sem); ext4_journal_stop(handle); if (error) { - ext4_orphan_del(NULL, inode); + if (orphan) + ext4_orphan_del(NULL, inode); goto err_out; } - } else { - loff_t oldsize = inode->i_size; - - i_size_write(inode, attr->ia_size); - pagecache_isize_extended(inode, oldsize, inode->i_size); } + if (!shrink) + pagecache_isize_extended(inode, oldsize, inode->i_size); /* * Blocks are going to be removed from the inode. Wait @@ -4780,13 +4766,9 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) * in data=journal mode to make pages freeable. */ truncate_pagecache(inode, inode->i_size); + if (shrink) + ext4_truncate(inode); } - /* - * We want to call ext4_truncate() even if attr->ia_size == - * inode->i_size for cases like truncation of fallocated space - */ - if (attr->ia_valid & ATTR_SIZE) - ext4_truncate(inode); if (!rc) { setattr_copy(inode, attr); diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 2cb9e178d..1346cfa35 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -31,14 +31,11 @@ static void memswap(void *a, void *b, size_t len) { unsigned char *ap, *bp; - unsigned char tmp; ap = (unsigned char *)a; bp = (unsigned char *)b; while (len-- > 0) { - tmp = *ap; - *ap = *bp; - *bp = tmp; + swap(*ap, *bp); ap++; bp++; } @@ -675,8 +672,8 @@ encryption_policy_out: if (err) return err; } - if (copy_to_user((void *) arg, sbi->s_es->s_encrypt_pw_salt, - 16)) + if (copy_to_user((void __user *) arg, + sbi->s_es->s_encrypt_pw_salt, 16)) return -EFAULT; return 0; } @@ -690,7 +687,7 @@ encryption_policy_out: err = ext4_get_policy(inode, &policy); if (err) return err; - if (copy_to_user((void *)arg, &policy, sizeof(policy))) + if (copy_to_user((void __user *)arg, &policy, sizeof(policy))) return -EFAULT; return 0; #else @@ -758,7 +755,6 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return err; } case EXT4_IOC_MOVE_EXT: - case FITRIM: case EXT4_IOC_RESIZE_FS: case EXT4_IOC_PRECACHE_EXTENTS: case EXT4_IOC_SET_ENCRYPTION_POLICY: diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 41260489d..34b610ea5 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -26,6 +26,7 @@ #include <linux/log2.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/backing-dev.h> #include <trace/events/ext4.h> #ifdef CONFIG_EXT4_DEBUG @@ -882,10 +883,8 @@ static int ext4_mb_init_cache(struct page *page, char *incore) /* wait for I/O completion */ for (i = 0, group = first_group; i < groups_per_page; i++, group++) { - if (bh[i] && ext4_wait_block_bitmap(sb, group, bh[i])) { + if (bh[i] && ext4_wait_block_bitmap(sb, group, bh[i])) err = -EIO; - goto out; - } } first_block = page->index * blocks_per_page; @@ -898,6 +897,11 @@ static int ext4_mb_init_cache(struct page *page, char *incore) /* skip initialized uptodate buddy */ continue; + if (!buffer_verified(bh[group - first_group])) + /* Skip faulty bitmaps */ + continue; + err = 0; + /* * data carry information regarding this * particular group in the format specified @@ -2008,7 +2012,12 @@ void ext4_mb_scan_aligned(struct ext4_allocation_context *ac, } } -/* This is now called BEFORE we load the buddy bitmap. */ +/* + * This is now called BEFORE we load the buddy bitmap. + * Returns either 1 or 0 indicating that the group is either suitable + * for the allocation or not. In addition it can also return negative + * error code when something goes wrong. + */ static int ext4_mb_good_group(struct ext4_allocation_context *ac, ext4_group_t group, int cr) { @@ -2031,7 +2040,7 @@ static int ext4_mb_good_group(struct ext4_allocation_context *ac, if (unlikely(EXT4_MB_GRP_NEED_INIT(grp))) { int ret = ext4_mb_init_group(ac->ac_sb, group); if (ret) - return 0; + return ret; } fragments = grp->bb_fragments; @@ -2078,7 +2087,7 @@ ext4_mb_regular_allocator(struct ext4_allocation_context *ac) { ext4_group_t ngroups, group, i; int cr; - int err = 0; + int err = 0, first_err = 0; struct ext4_sb_info *sbi; struct super_block *sb; struct ext4_buddy e4b; @@ -2145,6 +2154,7 @@ repeat: group = ac->ac_g_ex.fe_group; for (i = 0; i < ngroups; group++, i++) { + int ret = 0; cond_resched(); /* * Artificially restricted ngroups for non-extent @@ -2154,8 +2164,12 @@ repeat: group = 0; /* This now checks without needing the buddy page */ - if (!ext4_mb_good_group(ac, group, cr)) + ret = ext4_mb_good_group(ac, group, cr); + if (ret <= 0) { + if (!first_err) + first_err = ret; continue; + } err = ext4_mb_load_buddy(sb, group, &e4b); if (err) @@ -2167,9 +2181,12 @@ repeat: * We need to check again after locking the * block group */ - if (!ext4_mb_good_group(ac, group, cr)) { + ret = ext4_mb_good_group(ac, group, cr); + if (ret <= 0) { ext4_unlock_group(sb, group); ext4_mb_unload_buddy(&e4b); + if (!first_err) + first_err = ret; continue; } @@ -2216,6 +2233,8 @@ repeat: } } out: + if (!err && ac->ac_status != AC_STATUS_FOUND && first_err) + err = first_err; return err; } @@ -2257,12 +2276,9 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v) group--; if (group == 0) - seq_printf(seq, "#%-5s: %-5s %-5s %-5s " - "[ %-5s %-5s %-5s %-5s %-5s %-5s %-5s " - "%-5s %-5s %-5s %-5s %-5s %-5s %-5s ]\n", - "group", "free", "frags", "first", - "2^0", "2^1", "2^2", "2^3", "2^4", "2^5", "2^6", - "2^7", "2^8", "2^9", "2^10", "2^11", "2^12", "2^13"); + seq_puts(seq, "#group: free frags first [" + " 2^0 2^1 2^2 2^3 2^4 2^5 2^6 " + " 2^7 2^8 2^9 2^10 2^11 2^12 2^13 ]"); i = (sb->s_blocksize_bits + 2) * sizeof(sg.info.bb_counters[0]) + sizeof(struct ext4_group_info); diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index 370420bfa..fb6f11709 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c @@ -166,12 +166,9 @@ mext_page_double_lock(struct inode *inode1, struct inode *inode2, */ wait_on_page_writeback(page[0]); wait_on_page_writeback(page[1]); - if (inode1 > inode2) { - struct page *tmp; - tmp = page[0]; - page[0] = page[1]; - page[1] = tmp; - } + if (inode1 > inode2) + swap(page[0], page[1]); + return 0; } @@ -574,12 +571,16 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, __u64 orig_blk, orig_inode->i_ino, donor_inode->i_ino); return -EINVAL; } - /* TODO: This is non obvious task to swap blocks for inodes with full - jornaling enabled */ + + /* TODO: it's not obvious how to swap blocks for inodes with full + journaling enabled */ if (ext4_should_journal_data(orig_inode) || ext4_should_journal_data(donor_inode)) { - return -EINVAL; + ext4_msg(orig_inode->i_sb, KERN_ERR, + "Online defrag not supported with data journaling"); + return -EOPNOTSUPP; } + /* Protect orig and donor inodes against a truncate */ lock_two_nondirectories(orig_inode, donor_inode); diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 814f3beb4..011dcfb5c 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -61,7 +61,7 @@ static struct buffer_head *ext4_append(handle_t *handle, *block = inode->i_size >> inode->i_sb->s_blocksize_bits; - bh = ext4_bread(handle, inode, *block, 1); + bh = ext4_bread(handle, inode, *block, EXT4_GET_BLOCKS_CREATE); if (IS_ERR(bh)) return bh; inode->i_size += inode->i_sb->s_blocksize; @@ -84,12 +84,13 @@ typedef enum { } dirblock_type_t; #define ext4_read_dirblock(inode, block, type) \ - __ext4_read_dirblock((inode), (block), (type), __LINE__) + __ext4_read_dirblock((inode), (block), (type), __func__, __LINE__) static struct buffer_head *__ext4_read_dirblock(struct inode *inode, - ext4_lblk_t block, - dirblock_type_t type, - unsigned int line) + ext4_lblk_t block, + dirblock_type_t type, + const char *func, + unsigned int line) { struct buffer_head *bh; struct ext4_dir_entry *dirent; @@ -97,15 +98,17 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode, bh = ext4_bread(NULL, inode, block, 0); if (IS_ERR(bh)) { - __ext4_warning(inode->i_sb, __func__, line, - "error %ld reading directory block " - "(ino %lu, block %lu)", PTR_ERR(bh), inode->i_ino, - (unsigned long) block); + __ext4_warning(inode->i_sb, func, line, + "inode #%lu: lblock %lu: comm %s: " + "error %ld reading directory block", + inode->i_ino, (unsigned long)block, + current->comm, PTR_ERR(bh)); return bh; } if (!bh) { - ext4_error_inode(inode, __func__, line, block, "Directory hole found"); + ext4_error_inode(inode, func, line, block, + "Directory hole found"); return ERR_PTR(-EIO); } dirent = (struct ext4_dir_entry *) bh->b_data; @@ -119,7 +122,7 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode, is_dx_block = 1; } if (!is_dx_block && type == INDEX) { - ext4_error_inode(inode, __func__, line, block, + ext4_error_inode(inode, func, line, block, "directory leaf block found instead of index block"); return ERR_PTR(-EIO); } @@ -136,8 +139,8 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode, if (ext4_dx_csum_verify(inode, dirent)) set_buffer_verified(bh); else { - ext4_error_inode(inode, __func__, line, block, - "Directory index failed checksum"); + ext4_error_inode(inode, func, line, block, + "Directory index failed checksum"); brelse(bh); return ERR_PTR(-EIO); } @@ -146,8 +149,8 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode, if (ext4_dirent_csum_verify(inode, dirent)) set_buffer_verified(bh); else { - ext4_error_inode(inode, __func__, line, block, - "Directory block failed checksum"); + ext4_error_inode(inode, func, line, block, + "Directory block failed checksum"); brelse(bh); return ERR_PTR(-EIO); } @@ -248,7 +251,7 @@ static void dx_set_count(struct dx_entry *entries, unsigned value); static void dx_set_limit(struct dx_entry *entries, unsigned value); static unsigned dx_root_limit(struct inode *dir, unsigned infosize); static unsigned dx_node_limit(struct inode *dir); -static struct dx_frame *dx_probe(const struct qstr *d_name, +static struct dx_frame *dx_probe(struct ext4_filename *fname, struct inode *dir, struct dx_hash_info *hinfo, struct dx_frame *frame); @@ -267,10 +270,10 @@ static int ext4_htree_next_block(struct inode *dir, __u32 hash, struct dx_frame *frames, __u32 *start_hash); static struct buffer_head * ext4_dx_find_entry(struct inode *dir, - const struct qstr *d_name, + struct ext4_filename *fname, struct ext4_dir_entry_2 **res_dir); -static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, - struct inode *inode); +static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname, + struct dentry *dentry, struct inode *inode); /* checksumming functions */ void initialize_dirent_tail(struct ext4_dir_entry_tail *t, @@ -327,10 +330,14 @@ static __le32 ext4_dirent_csum(struct inode *inode, return cpu_to_le32(csum); } -static void warn_no_space_for_csum(struct inode *inode) +#define warn_no_space_for_csum(inode) \ + __warn_no_space_for_csum((inode), __func__, __LINE__) + +static void __warn_no_space_for_csum(struct inode *inode, const char *func, + unsigned int line) { - ext4_warning(inode->i_sb, "no space in directory inode %lu leaf for " - "checksum. Please run e2fsck -D.", inode->i_ino); + __ext4_warning_inode(inode, func, line, + "No space for directory leaf checksum. Please run e2fsck -D."); } int ext4_dirent_csum_verify(struct inode *inode, struct ext4_dir_entry *dirent) @@ -607,17 +614,15 @@ static struct stats dx_show_leaf(struct inode *dir, char *name; struct ext4_str fname_crypto_str = {.name = NULL, .len = 0}; - struct ext4_fname_crypto_ctx *ctx = NULL; - int res; + int res = 0; name = de->name; len = de->name_len; - ctx = ext4_get_fname_crypto_ctx(dir, - EXT4_NAME_LEN); - if (IS_ERR(ctx)) { - printk(KERN_WARNING "Error acquiring" - " crypto ctxt--skipping crypto\n"); - ctx = NULL; + if (ext4_encrypted_inode(inode)) + res = ext4_get_encryption_info(dir); + if (res) { + printk(KERN_WARNING "Error setting up" + " fname crypto: %d\n", res); } if (ctx == NULL) { /* Directory is not encrypted */ @@ -637,7 +642,6 @@ static struct stats dx_show_leaf(struct inode *dir, "allocating crypto " "buffer--skipping " "crypto\n"); - ext4_put_fname_crypto_ctx(&ctx); ctx = NULL; } res = ext4_fname_disk_to_usr(ctx, NULL, de, @@ -658,7 +662,6 @@ static struct stats dx_show_leaf(struct inode *dir, printk("%*.s:(E)%x.%u ", len, name, h.hash, (unsigned) ((char *) de - base)); - ext4_put_fname_crypto_ctx(&ctx); ext4_fname_crypto_free_buffer( &fname_crypto_str); } @@ -724,7 +727,7 @@ struct stats dx_show_entries(struct dx_hash_info *hinfo, struct inode *dir, * back to userspace. */ static struct dx_frame * -dx_probe(const struct qstr *d_name, struct inode *dir, +dx_probe(struct ext4_filename *fname, struct inode *dir, struct dx_hash_info *hinfo, struct dx_frame *frame_in) { unsigned count, indirect; @@ -742,56 +745,41 @@ dx_probe(const struct qstr *d_name, struct inode *dir, if (root->info.hash_version != DX_HASH_TEA && root->info.hash_version != DX_HASH_HALF_MD4 && root->info.hash_version != DX_HASH_LEGACY) { - ext4_warning(dir->i_sb, "Unrecognised inode hash code %d", - root->info.hash_version); + ext4_warning_inode(dir, "Unrecognised inode hash code %u", + root->info.hash_version); goto fail; } + if (fname) + hinfo = &fname->hinfo; hinfo->hash_version = root->info.hash_version; if (hinfo->hash_version <= DX_HASH_TEA) hinfo->hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned; hinfo->seed = EXT4_SB(dir->i_sb)->s_hash_seed; -#ifdef CONFIG_EXT4_FS_ENCRYPTION - if (d_name) { - struct ext4_fname_crypto_ctx *ctx = NULL; - int res; - - /* Check if the directory is encrypted */ - ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN); - if (IS_ERR(ctx)) { - ret_err = ERR_PTR(PTR_ERR(ctx)); - goto fail; - } - res = ext4_fname_usr_to_hash(ctx, d_name, hinfo); - if (res < 0) { - ret_err = ERR_PTR(res); - goto fail; - } - ext4_put_fname_crypto_ctx(&ctx); - } -#else - if (d_name) - ext4fs_dirhash(d_name->name, d_name->len, hinfo); -#endif + if (fname && fname_name(fname)) + ext4fs_dirhash(fname_name(fname), fname_len(fname), hinfo); hash = hinfo->hash; if (root->info.unused_flags & 1) { - ext4_warning(dir->i_sb, "Unimplemented inode hash flags: %#06x", - root->info.unused_flags); + ext4_warning_inode(dir, "Unimplemented hash flags: %#06x", + root->info.unused_flags); goto fail; } - if ((indirect = root->info.indirect_levels) > 1) { - ext4_warning(dir->i_sb, "Unimplemented inode hash depth: %#06x", - root->info.indirect_levels); + indirect = root->info.indirect_levels; + if (indirect > 1) { + ext4_warning_inode(dir, "Unimplemented hash depth: %#06x", + root->info.indirect_levels); goto fail; } - entries = (struct dx_entry *) (((char *)&root->info) + - root->info.info_length); + entries = (struct dx_entry *)(((char *)&root->info) + + root->info.info_length); if (dx_get_limit(entries) != dx_root_limit(dir, root->info.info_length)) { - ext4_warning(dir->i_sb, "dx entry: limit != root limit"); + ext4_warning_inode(dir, "dx entry: limit %u != root limit %u", + dx_get_limit(entries), + dx_root_limit(dir, root->info.info_length)); goto fail; } @@ -799,15 +787,16 @@ dx_probe(const struct qstr *d_name, struct inode *dir, while (1) { count = dx_get_count(entries); if (!count || count > dx_get_limit(entries)) { - ext4_warning(dir->i_sb, - "dx entry: no count or count > limit"); + ext4_warning_inode(dir, + "dx entry: count %u beyond limit %u", + count, dx_get_limit(entries)); goto fail; } p = entries + 1; q = entries + count - 1; while (p <= q) { - m = p + (q - p)/2; + m = p + (q - p) / 2; dxtrace(printk(".")); if (dx_get_hash(m) > hash) q = m - 1; @@ -831,7 +820,8 @@ dx_probe(const struct qstr *d_name, struct inode *dir, } at = p - 1; - dxtrace(printk(" %x->%u\n", at == entries? 0: dx_get_hash(at), dx_get_block(at))); + dxtrace(printk(" %x->%u\n", at == entries ? 0 : dx_get_hash(at), + dx_get_block(at))); frame->entries = entries; frame->at = at; if (!indirect--) @@ -845,9 +835,10 @@ dx_probe(const struct qstr *d_name, struct inode *dir, } entries = ((struct dx_node *) frame->bh->b_data)->entries; - if (dx_get_limit(entries) != dx_node_limit (dir)) { - ext4_warning(dir->i_sb, - "dx entry: limit != node limit"); + if (dx_get_limit(entries) != dx_node_limit(dir)) { + ext4_warning_inode(dir, + "dx entry: limit %u != node limit %u", + dx_get_limit(entries), dx_node_limit(dir)); goto fail; } } @@ -858,18 +849,17 @@ fail: } if (ret_err == ERR_PTR(ERR_BAD_DX_DIR)) - ext4_warning(dir->i_sb, - "Corrupt dir inode %lu, running e2fsck is " - "recommended.", dir->i_ino); + ext4_warning_inode(dir, + "Corrupt directory, running e2fsck is recommended"); return ret_err; } -static void dx_release (struct dx_frame *frames) +static void dx_release(struct dx_frame *frames) { if (frames[0].bh == NULL) return; - if (((struct dx_root *) frames[0].bh->b_data)->info.indirect_levels) + if (((struct dx_root *)frames[0].bh->b_data)->info.indirect_levels) brelse(frames[1].bh); brelse(frames[0].bh); } @@ -962,7 +952,6 @@ static int htree_dirblock_to_tree(struct file *dir_file, struct buffer_head *bh; struct ext4_dir_entry_2 *de, *top; int err = 0, count = 0; - struct ext4_fname_crypto_ctx *ctx = NULL; struct ext4_str fname_crypto_str = {.name = NULL, .len = 0}, tmp_str; dxtrace(printk(KERN_INFO "In htree dirblock_to_tree: block %lu\n", @@ -977,17 +966,15 @@ static int htree_dirblock_to_tree(struct file *dir_file, EXT4_DIR_REC_LEN(0)); #ifdef CONFIG_EXT4_FS_ENCRYPTION /* Check if the directory is encrypted */ - ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN); - if (IS_ERR(ctx)) { - err = PTR_ERR(ctx); - brelse(bh); - return err; - } - if (ctx != NULL) { - err = ext4_fname_crypto_alloc_buffer(ctx, EXT4_NAME_LEN, + if (ext4_encrypted_inode(dir)) { + err = ext4_get_encryption_info(dir); + if (err < 0) { + brelse(bh); + return err; + } + err = ext4_fname_crypto_alloc_buffer(dir, EXT4_NAME_LEN, &fname_crypto_str); if (err < 0) { - ext4_put_fname_crypto_ctx(&ctx); brelse(bh); return err; } @@ -1008,16 +995,17 @@ static int htree_dirblock_to_tree(struct file *dir_file, continue; if (de->inode == 0) continue; - if (ctx == NULL) { - /* Directory is not encrypted */ + if (!ext4_encrypted_inode(dir)) { tmp_str.name = de->name; tmp_str.len = de->name_len; err = ext4_htree_store_dirent(dir_file, hinfo->hash, hinfo->minor_hash, de, &tmp_str); } else { + int save_len = fname_crypto_str.len; + /* Directory is encrypted */ - err = ext4_fname_disk_to_usr(ctx, hinfo, de, + err = ext4_fname_disk_to_usr(dir, hinfo, de, &fname_crypto_str); if (err < 0) { count = err; @@ -1026,6 +1014,7 @@ static int htree_dirblock_to_tree(struct file *dir_file, err = ext4_htree_store_dirent(dir_file, hinfo->hash, hinfo->minor_hash, de, &fname_crypto_str); + fname_crypto_str.len = save_len; } if (err != 0) { count = err; @@ -1036,7 +1025,6 @@ static int htree_dirblock_to_tree(struct file *dir_file, errout: brelse(bh); #ifdef CONFIG_EXT4_FS_ENCRYPTION - ext4_put_fname_crypto_ctx(&ctx); ext4_fname_crypto_free_buffer(&fname_crypto_str); #endif return count; @@ -1155,12 +1143,13 @@ errout: static inline int search_dirblock(struct buffer_head *bh, struct inode *dir, + struct ext4_filename *fname, const struct qstr *d_name, unsigned int offset, struct ext4_dir_entry_2 **res_dir) { - return search_dir(bh, bh->b_data, dir->i_sb->s_blocksize, dir, - d_name, offset, res_dir); + return ext4_search_dir(bh, bh->b_data, dir->i_sb->s_blocksize, dir, + fname, d_name, offset, res_dir); } /* @@ -1242,54 +1231,54 @@ static void dx_insert_block(struct dx_frame *frame, u32 hash, ext4_lblk_t block) * `len <= EXT4_NAME_LEN' is guaranteed by caller. * `de != NULL' is guaranteed by caller. */ -static inline int ext4_match(struct ext4_fname_crypto_ctx *ctx, - struct ext4_str *fname_crypto_str, - int len, const char * const name, +static inline int ext4_match(struct ext4_filename *fname, struct ext4_dir_entry_2 *de) { - int res; + const void *name = fname_name(fname); + u32 len = fname_len(fname); if (!de->inode) return 0; #ifdef CONFIG_EXT4_FS_ENCRYPTION - if (ctx) - return ext4_fname_match(ctx, fname_crypto_str, len, name, de); + if (unlikely(!name)) { + if (fname->usr_fname->name[0] == '_') { + int ret; + if (de->name_len < 16) + return 0; + ret = memcmp(de->name + de->name_len - 16, + fname->crypto_buf.name + 8, 16); + return (ret == 0) ? 1 : 0; + } + name = fname->crypto_buf.name; + len = fname->crypto_buf.len; + } #endif - if (len != de->name_len) + if (de->name_len != len) return 0; - res = memcmp(name, de->name, len); - return (res == 0) ? 1 : 0; + return (memcmp(de->name, name, len) == 0) ? 1 : 0; } /* * Returns 0 if not found, -1 on failure, and 1 on success */ -int search_dir(struct buffer_head *bh, char *search_buf, int buf_size, - struct inode *dir, const struct qstr *d_name, - unsigned int offset, struct ext4_dir_entry_2 **res_dir) +int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size, + struct inode *dir, struct ext4_filename *fname, + const struct qstr *d_name, + unsigned int offset, struct ext4_dir_entry_2 **res_dir) { struct ext4_dir_entry_2 * de; char * dlimit; int de_len; - const char *name = d_name->name; - int namelen = d_name->len; - struct ext4_fname_crypto_ctx *ctx = NULL; - struct ext4_str fname_crypto_str = {.name = NULL, .len = 0}; int res; - ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN); - if (IS_ERR(ctx)) - return -1; - de = (struct ext4_dir_entry_2 *)search_buf; dlimit = search_buf + buf_size; while ((char *) de < dlimit) { /* this code is executed quadratically often */ /* do minimal checking `by hand' */ if ((char *) de + de->name_len <= dlimit) { - res = ext4_match(ctx, &fname_crypto_str, namelen, - name, de); + res = ext4_match(fname, de); if (res < 0) { res = -1; goto return_result; @@ -1322,8 +1311,6 @@ int search_dir(struct buffer_head *bh, char *search_buf, int buf_size, res = 0; return_result: - ext4_put_fname_crypto_ctx(&ctx); - ext4_fname_crypto_free_buffer(&fname_crypto_str); return res; } @@ -1370,7 +1357,8 @@ static struct buffer_head * ext4_find_entry (struct inode *dir, buffer */ int num = 0; ext4_lblk_t nblocks; - int i, namelen; + int i, namelen, retval; + struct ext4_filename fname; *res_dir = NULL; sb = dir->i_sb; @@ -1378,14 +1366,18 @@ static struct buffer_head * ext4_find_entry (struct inode *dir, if (namelen > EXT4_NAME_LEN) return NULL; + retval = ext4_fname_setup_filename(dir, d_name, 1, &fname); + if (retval) + return ERR_PTR(retval); + if (ext4_has_inline_data(dir)) { int has_inline_data = 1; - ret = ext4_find_inline_entry(dir, d_name, res_dir, + ret = ext4_find_inline_entry(dir, &fname, d_name, res_dir, &has_inline_data); if (has_inline_data) { if (inlined) *inlined = 1; - return ret; + goto cleanup_and_exit; } } @@ -1400,14 +1392,14 @@ static struct buffer_head * ext4_find_entry (struct inode *dir, goto restart; } if (is_dx(dir)) { - bh = ext4_dx_find_entry(dir, d_name, res_dir); + ret = ext4_dx_find_entry(dir, &fname, res_dir); /* * On success, or if the error was file not found, * return. Otherwise, fall back to doing a search the * old fashioned way. */ - if (!IS_ERR(bh) || PTR_ERR(bh) != ERR_BAD_DX_DIR) - return bh; + if (!IS_ERR(ret) || PTR_ERR(ret) != ERR_BAD_DX_DIR) + goto cleanup_and_exit; dxtrace(printk(KERN_DEBUG "ext4_find_entry: dx failed, " "falling back\n")); } @@ -1438,8 +1430,10 @@ restart: num++; bh = ext4_getblk(NULL, dir, b++, 0); if (unlikely(IS_ERR(bh))) { - if (ra_max == 0) - return bh; + if (ra_max == 0) { + ret = bh; + goto cleanup_and_exit; + } break; } bh_use[ra_max] = bh; @@ -1469,7 +1463,7 @@ restart: goto next; } set_buffer_verified(bh); - i = search_dirblock(bh, dir, d_name, + i = search_dirblock(bh, dir, &fname, d_name, block << EXT4_BLOCK_SIZE_BITS(sb), res_dir); if (i == 1) { EXT4_I(dir)->i_dir_start_lookup = block; @@ -1500,15 +1494,17 @@ cleanup_and_exit: /* Clean up the read-ahead blocks */ for (; ra_ptr < ra_max; ra_ptr++) brelse(bh_use[ra_ptr]); + ext4_fname_free_filename(&fname); return ret; } -static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct qstr *d_name, - struct ext4_dir_entry_2 **res_dir) +static struct buffer_head * ext4_dx_find_entry(struct inode *dir, + struct ext4_filename *fname, + struct ext4_dir_entry_2 **res_dir) { struct super_block * sb = dir->i_sb; - struct dx_hash_info hinfo; struct dx_frame frames[2], *frame; + const struct qstr *d_name = fname->usr_fname; struct buffer_head *bh; ext4_lblk_t block; int retval; @@ -1516,7 +1512,7 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct q #ifdef CONFIG_EXT4_FS_ENCRYPTION *res_dir = NULL; #endif - frame = dx_probe(d_name, dir, &hinfo, frames); + frame = dx_probe(fname, dir, NULL, frames); if (IS_ERR(frame)) return (struct buffer_head *) frame; do { @@ -1525,7 +1521,7 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct q if (IS_ERR(bh)) goto errout; - retval = search_dirblock(bh, dir, d_name, + retval = search_dirblock(bh, dir, fname, d_name, block << EXT4_BLOCK_SIZE_BITS(sb), res_dir); if (retval == 1) @@ -1537,12 +1533,12 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct q } /* Check to see if we should continue to search */ - retval = ext4_htree_next_block(dir, hinfo.hash, frame, + retval = ext4_htree_next_block(dir, fname->hinfo.hash, frame, frames, NULL); if (retval < 0) { - ext4_warning(sb, - "error %d reading index page in directory #%lu", - retval, dir->i_ino); + ext4_warning_inode(dir, + "error %d reading directory index block", + retval); bh = ERR_PTR(retval); goto errout; } @@ -1796,32 +1792,16 @@ journal_error: int ext4_find_dest_de(struct inode *dir, struct inode *inode, struct buffer_head *bh, void *buf, int buf_size, - const char *name, int namelen, + struct ext4_filename *fname, struct ext4_dir_entry_2 **dest_de) { struct ext4_dir_entry_2 *de; - unsigned short reclen = EXT4_DIR_REC_LEN(namelen); + unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname)); int nlen, rlen; unsigned int offset = 0; char *top; - struct ext4_fname_crypto_ctx *ctx = NULL; - struct ext4_str fname_crypto_str = {.name = NULL, .len = 0}; int res; - ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN); - if (IS_ERR(ctx)) - return -1; - - if (ctx != NULL) { - /* Calculate record length needed to store the entry */ - res = ext4_fname_crypto_namelen_on_disk(ctx, namelen); - if (res < 0) { - ext4_put_fname_crypto_ctx(&ctx); - return res; - } - reclen = EXT4_DIR_REC_LEN(res); - } - de = (struct ext4_dir_entry_2 *)buf; top = buf + buf_size - reclen; while ((char *) de <= top) { @@ -1831,7 +1811,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, goto return_result; } /* Provide crypto context and crypto buffer to ext4 match */ - res = ext4_match(ctx, &fname_crypto_str, namelen, name, de); + res = ext4_match(fname, de); if (res < 0) goto return_result; if (res > 0) { @@ -1853,8 +1833,6 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode, res = 0; } return_result: - ext4_put_fname_crypto_ctx(&ctx); - ext4_fname_crypto_free_buffer(&fname_crypto_str); return res; } @@ -1862,39 +1840,10 @@ int ext4_insert_dentry(struct inode *dir, struct inode *inode, struct ext4_dir_entry_2 *de, int buf_size, - const struct qstr *iname, - const char *name, int namelen) + struct ext4_filename *fname) { int nlen, rlen; - struct ext4_fname_crypto_ctx *ctx = NULL; - struct ext4_str fname_crypto_str = {.name = NULL, .len = 0}; - struct ext4_str tmp_str; - int res; - - ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN); - if (IS_ERR(ctx)) - return -EIO; - /* By default, the input name would be written to the disk */ - tmp_str.name = (unsigned char *)name; - tmp_str.len = namelen; - if (ctx != NULL) { - /* Directory is encrypted */ - res = ext4_fname_crypto_alloc_buffer(ctx, EXT4_NAME_LEN, - &fname_crypto_str); - if (res < 0) { - ext4_put_fname_crypto_ctx(&ctx); - return -ENOMEM; - } - res = ext4_fname_usr_to_disk(ctx, iname, &fname_crypto_str); - if (res < 0) { - ext4_put_fname_crypto_ctx(&ctx); - ext4_fname_crypto_free_buffer(&fname_crypto_str); - return res; - } - tmp_str.name = fname_crypto_str.name; - tmp_str.len = fname_crypto_str.len; - } nlen = EXT4_DIR_REC_LEN(de->name_len); rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); @@ -1908,11 +1857,8 @@ int ext4_insert_dentry(struct inode *dir, de->file_type = EXT4_FT_UNKNOWN; de->inode = cpu_to_le32(inode->i_ino); ext4_set_de_type(inode->i_sb, de, inode->i_mode); - de->name_len = tmp_str.len; - - memcpy(de->name, tmp_str.name, tmp_str.len); - ext4_put_fname_crypto_ctx(&ctx); - ext4_fname_crypto_free_buffer(&fname_crypto_str); + de->name_len = fname_len(fname); + memcpy(de->name, fname_name(fname), fname_len(fname)); return 0; } @@ -1924,13 +1870,11 @@ int ext4_insert_dentry(struct inode *dir, * space. It will return -ENOSPC if no space is available, and -EIO * and -EEXIST if directory entry already exists. */ -static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, +static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, + struct inode *dir, struct inode *inode, struct ext4_dir_entry_2 *de, struct buffer_head *bh) { - struct inode *dir = d_inode(dentry->d_parent); - const char *name = dentry->d_name.name; - int namelen = dentry->d_name.len; unsigned int blocksize = dir->i_sb->s_blocksize; int csum_size = 0; int err; @@ -1939,9 +1883,8 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, csum_size = sizeof(struct ext4_dir_entry_tail); if (!de) { - err = ext4_find_dest_de(dir, inode, - bh, bh->b_data, blocksize - csum_size, - name, namelen, &de); + err = ext4_find_dest_de(dir, inode, bh, bh->b_data, + blocksize - csum_size, fname, &de); if (err) return err; } @@ -1954,8 +1897,7 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, /* By now the buffer is marked for journaling. Due to crypto operations, * the following function call may fail */ - err = ext4_insert_dentry(dir, inode, de, blocksize, &dentry->d_name, - name, namelen); + err = ext4_insert_dentry(dir, inode, de, blocksize, fname); if (err < 0) return err; @@ -1985,17 +1927,11 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, * This converts a one block unindexed directory to a 3 block indexed * directory, and adds the dentry to the indexed directory. */ -static int make_indexed_dir(handle_t *handle, struct dentry *dentry, +static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname, + struct dentry *dentry, struct inode *inode, struct buffer_head *bh) { struct inode *dir = d_inode(dentry->d_parent); -#ifdef CONFIG_EXT4_FS_ENCRYPTION - struct ext4_fname_crypto_ctx *ctx = NULL; - int res; -#else - const char *name = dentry->d_name.name; - int namelen = dentry->d_name.len; -#endif struct buffer_head *bh2; struct dx_root *root; struct dx_frame frames[2], *frame; @@ -2006,17 +1942,10 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry, unsigned len; int retval; unsigned blocksize; - struct dx_hash_info hinfo; ext4_lblk_t block; struct fake_dirent *fde; int csum_size = 0; -#ifdef CONFIG_EXT4_FS_ENCRYPTION - ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN); - if (IS_ERR(ctx)) - return PTR_ERR(ctx); -#endif - if (ext4_has_metadata_csum(inode->i_sb)) csum_size = sizeof(struct ext4_dir_entry_tail); @@ -2078,22 +2007,12 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry, dx_set_limit(entries, dx_root_limit(dir, sizeof(root->info))); /* Initialize as for dx_probe */ - hinfo.hash_version = root->info.hash_version; - if (hinfo.hash_version <= DX_HASH_TEA) - hinfo.hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned; - hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed; -#ifdef CONFIG_EXT4_FS_ENCRYPTION - res = ext4_fname_usr_to_hash(ctx, &dentry->d_name, &hinfo); - if (res < 0) { - ext4_put_fname_crypto_ctx(&ctx); - ext4_mark_inode_dirty(handle, dir); - brelse(bh); - return res; - } - ext4_put_fname_crypto_ctx(&ctx); -#else - ext4fs_dirhash(name, namelen, &hinfo); -#endif + fname->hinfo.hash_version = root->info.hash_version; + if (fname->hinfo.hash_version <= DX_HASH_TEA) + fname->hinfo.hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned; + fname->hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed; + ext4fs_dirhash(fname_name(fname), fname_len(fname), &fname->hinfo); + memset(frames, 0, sizeof(frames)); frame = frames; frame->entries = entries; @@ -2108,14 +2027,14 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry, if (retval) goto out_frames; - de = do_split(handle,dir, &bh, frame, &hinfo); + de = do_split(handle,dir, &bh, frame, &fname->hinfo); if (IS_ERR(de)) { retval = PTR_ERR(de); goto out_frames; } dx_release(frames); - retval = add_dirent_to_buf(handle, dentry, inode, de, bh); + retval = add_dirent_to_buf(handle, fname, dir, inode, de, bh); brelse(bh); return retval; out_frames: @@ -2147,6 +2066,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, struct ext4_dir_entry_2 *de; struct ext4_dir_entry_tail *t; struct super_block *sb; + struct ext4_filename fname; int retval; int dx_fallback=0; unsigned blocksize; @@ -2161,10 +2081,15 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, if (!dentry->d_name.len) return -EINVAL; + retval = ext4_fname_setup_filename(dir, &dentry->d_name, 0, &fname); + if (retval) + return retval; + if (ext4_has_inline_data(dir)) { - retval = ext4_try_add_inline_entry(handle, dentry, inode); + retval = ext4_try_add_inline_entry(handle, &fname, + dentry, inode); if (retval < 0) - return retval; + goto out; if (retval == 1) { retval = 0; goto out; @@ -2172,7 +2097,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, } if (is_dx(dir)) { - retval = ext4_dx_add_entry(handle, dentry, inode); + retval = ext4_dx_add_entry(handle, &fname, dentry, inode); if (!retval || (retval != ERR_BAD_DX_DIR)) goto out; ext4_clear_inode_flag(dir, EXT4_INODE_INDEX); @@ -2182,24 +2107,31 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, blocks = dir->i_size >> sb->s_blocksize_bits; for (block = 0; block < blocks; block++) { bh = ext4_read_dirblock(dir, block, DIRENT); - if (IS_ERR(bh)) - return PTR_ERR(bh); - - retval = add_dirent_to_buf(handle, dentry, inode, NULL, bh); + if (IS_ERR(bh)) { + retval = PTR_ERR(bh); + bh = NULL; + goto out; + } + retval = add_dirent_to_buf(handle, &fname, dir, inode, + NULL, bh); if (retval != -ENOSPC) goto out; if (blocks == 1 && !dx_fallback && EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_DIR_INDEX)) { - retval = make_indexed_dir(handle, dentry, inode, bh); + retval = make_indexed_dir(handle, &fname, dentry, + inode, bh); bh = NULL; /* make_indexed_dir releases bh */ goto out; } brelse(bh); } bh = ext4_append(handle, dir, &block); - if (IS_ERR(bh)) - return PTR_ERR(bh); + if (IS_ERR(bh)) { + retval = PTR_ERR(bh); + bh = NULL; + goto out; + } de = (struct ext4_dir_entry_2 *) bh->b_data; de->inode = 0; de->rec_len = ext4_rec_len_to_disk(blocksize - csum_size, blocksize); @@ -2209,8 +2141,9 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, initialize_dirent_tail(t, blocksize); } - retval = add_dirent_to_buf(handle, dentry, inode, de, bh); + retval = add_dirent_to_buf(handle, &fname, dir, inode, de, bh); out: + ext4_fname_free_filename(&fname); brelse(bh); if (retval == 0) ext4_set_inode_state(inode, EXT4_STATE_NEWENTRY); @@ -2220,19 +2153,18 @@ out: /* * Returns 0 for success, or a negative error value */ -static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, - struct inode *inode) +static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname, + struct dentry *dentry, struct inode *inode) { struct dx_frame frames[2], *frame; struct dx_entry *entries, *at; - struct dx_hash_info hinfo; struct buffer_head *bh; struct inode *dir = d_inode(dentry->d_parent); struct super_block *sb = dir->i_sb; struct ext4_dir_entry_2 *de; int err; - frame = dx_probe(&dentry->d_name, dir, &hinfo, frames); + frame = dx_probe(fname, dir, NULL, frames); if (IS_ERR(frame)) return PTR_ERR(frame); entries = frame->entries; @@ -2249,7 +2181,7 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, if (err) goto journal_error; - err = add_dirent_to_buf(handle, dentry, inode, NULL, bh); + err = add_dirent_to_buf(handle, fname, dir, inode, NULL, bh); if (err != -ENOSPC) goto cleanup; @@ -2267,7 +2199,7 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, if (levels && (dx_get_count(frames->entries) == dx_get_limit(frames->entries))) { - ext4_warning(sb, "Directory index full!"); + ext4_warning_inode(dir, "Directory index full!"); err = -ENOSPC; goto cleanup; } @@ -2345,12 +2277,12 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, goto cleanup; } } - de = do_split(handle, dir, &bh, frame, &hinfo); + de = do_split(handle, dir, &bh, frame, &fname->hinfo); if (IS_ERR(de)) { err = PTR_ERR(de); goto cleanup; } - err = add_dirent_to_buf(handle, dentry, inode, de, bh); + err = add_dirent_to_buf(handle, fname, dir, inode, de, bh); goto cleanup; journal_error: @@ -2517,20 +2449,7 @@ retry: inode->i_op = &ext4_file_inode_operations; inode->i_fop = &ext4_file_operations; ext4_set_aops(inode); - err = 0; -#ifdef CONFIG_EXT4_FS_ENCRYPTION - if (!err && (ext4_encrypted_inode(dir) || - DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb)))) { - err = ext4_inherit_context(dir, inode); - if (err) { - clear_nlink(inode); - unlock_new_inode(inode); - iput(inode); - } - } -#endif - if (!err) - err = ext4_add_nondir(handle, dentry, inode); + err = ext4_add_nondir(handle, dentry, inode); if (!err && IS_DIRSYNC(dir)) ext4_handle_sync(handle); } @@ -2711,14 +2630,6 @@ retry: err = ext4_init_new_dir(handle, dir, inode); if (err) goto out_clear_inode; -#ifdef CONFIG_EXT4_FS_ENCRYPTION - if (ext4_encrypted_inode(dir) || - DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb))) { - err = ext4_inherit_context(dir, inode); - if (err) - goto out_clear_inode; - } -#endif err = ext4_mark_inode_dirty(handle, inode); if (!err) err = ext4_add_entry(handle, dentry, inode); @@ -2779,12 +2690,9 @@ int ext4_empty_dir(struct inode *inode) de = (struct ext4_dir_entry_2 *) bh->b_data; de1 = ext4_next_entry(de, sb->s_blocksize); if (le32_to_cpu(de->inode) != inode->i_ino || - !le32_to_cpu(de1->inode) || - strcmp(".", de->name) || - strcmp("..", de1->name)) { - ext4_warning(inode->i_sb, - "bad directory (dir #%lu) - no `.' or `..'", - inode->i_ino); + le32_to_cpu(de1->inode) == 0 || + strcmp(".", de->name) || strcmp("..", de1->name)) { + ext4_warning_inode(inode, "directory missing '.' and/or '..'"); brelse(bh); return 1; } @@ -3037,8 +2945,9 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry) if (retval) goto end_rmdir; if (!EXT4_DIR_LINK_EMPTY(inode)) - ext4_warning(inode->i_sb, - "empty directory has too many links (%d)", + ext4_warning_inode(inode, + "empty directory '%.*s' has too many links (%u)", + dentry->d_name.len, dentry->d_name.name, inode->i_nlink); inode->i_version++; clear_nlink(inode); @@ -3098,10 +3007,9 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry) if (IS_DIRSYNC(dir)) ext4_handle_sync(handle); - if (!inode->i_nlink) { - ext4_warning(inode->i_sb, - "Deleting nonexistent file (%lu), %d", - inode->i_ino, inode->i_nlink); + if (inode->i_nlink == 0) { + ext4_warning_inode(inode, "Deleting file '%.*s' with no links", + dentry->d_name.len, dentry->d_name.name); set_nlink(inode, 1); } retval = ext4_delete_entry(handle, dir, de, bh); @@ -3140,10 +3048,23 @@ static int ext4_symlink(struct inode *dir, encryption_required = (ext4_encrypted_inode(dir) || DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb))); - if (encryption_required) - disk_link.len = encrypted_symlink_data_len(len) + 1; - if (disk_link.len > dir->i_sb->s_blocksize) - return -ENAMETOOLONG; + if (encryption_required) { + err = ext4_get_encryption_info(dir); + if (err) + return err; + if (ext4_encryption_info(dir) == NULL) + return -EPERM; + disk_link.len = (ext4_fname_encrypted_size(dir, len) + + sizeof(struct ext4_encrypted_symlink_data)); + sd = kzalloc(disk_link.len, GFP_KERNEL); + if (!sd) + return -ENOMEM; + } + + if (disk_link.len > dir->i_sb->s_blocksize) { + err = -ENAMETOOLONG; + goto err_free_sd; + } dquot_initialize(dir); @@ -3174,42 +3095,29 @@ static int ext4_symlink(struct inode *dir, if (IS_ERR(inode)) { if (handle) ext4_journal_stop(handle); - return PTR_ERR(inode); + err = PTR_ERR(inode); + goto err_free_sd; } if (encryption_required) { - struct ext4_fname_crypto_ctx *ctx = NULL; struct qstr istr; struct ext4_str ostr; - sd = kzalloc(disk_link.len, GFP_NOFS); - if (!sd) { - err = -ENOMEM; - goto err_drop_inode; - } - err = ext4_inherit_context(dir, inode); - if (err) - goto err_drop_inode; - ctx = ext4_get_fname_crypto_ctx(inode, - inode->i_sb->s_blocksize); - if (IS_ERR_OR_NULL(ctx)) { - /* We just set the policy, so ctx should not be NULL */ - err = (ctx == NULL) ? -EIO : PTR_ERR(ctx); - goto err_drop_inode; - } istr.name = (const unsigned char *) symname; istr.len = len; ostr.name = sd->encrypted_path; - err = ext4_fname_usr_to_disk(ctx, &istr, &ostr); - ext4_put_fname_crypto_ctx(&ctx); + ostr.len = disk_link.len; + err = ext4_fname_usr_to_disk(inode, &istr, &ostr); if (err < 0) goto err_drop_inode; sd->len = cpu_to_le16(ostr.len); disk_link.name = (char *) sd; + inode->i_op = &ext4_encrypted_symlink_inode_operations; } if ((disk_link.len > EXT4_N_BLOCKS * 4)) { - inode->i_op = &ext4_symlink_inode_operations; + if (!encryption_required) + inode->i_op = &ext4_symlink_inode_operations; ext4_set_aops(inode); /* * We cannot call page_symlink() with transaction started @@ -3249,9 +3157,10 @@ static int ext4_symlink(struct inode *dir, } else { /* clear the extent format for fast symlink */ ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS); - inode->i_op = encryption_required ? - &ext4_symlink_inode_operations : - &ext4_fast_symlink_inode_operations; + if (!encryption_required) { + inode->i_op = &ext4_fast_symlink_inode_operations; + inode->i_link = (char *)&EXT4_I(inode)->i_data; + } memcpy((char *)&EXT4_I(inode)->i_data, disk_link.name, disk_link.len); inode->i_size = disk_link.len - 1; @@ -3268,10 +3177,11 @@ static int ext4_symlink(struct inode *dir, err_drop_inode: if (handle) ext4_journal_stop(handle); - kfree(sd); clear_nlink(inode); unlock_new_inode(inode); iput(inode); +err_free_sd: + kfree(sd); return err; } @@ -3487,9 +3397,9 @@ static void ext4_rename_delete(handle_t *handle, struct ext4_renament *ent, } if (retval) { - ext4_warning(ent->dir->i_sb, - "Deleting old file (%lu), %d, error=%d", - ent->dir->i_ino, ent->dir->i_nlink, retval); + ext4_warning_inode(ent->dir, + "Deleting old file: nlink %d, error=%d", + ent->dir->i_nlink, retval); } } @@ -3759,6 +3669,15 @@ static int ext4_cross_rename(struct inode *old_dir, struct dentry *old_dentry, u8 new_file_type; int retval; + if ((ext4_encrypted_inode(old_dir) || + ext4_encrypted_inode(new_dir)) && + (old_dir != new_dir) && + (!ext4_is_child_context_consistent_with_parent(new_dir, + old.inode) || + !ext4_is_child_context_consistent_with_parent(old_dir, + new.inode))) + return -EPERM; + dquot_initialize(old.dir); dquot_initialize(new.dir); diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index 5765f88b3..5602450f0 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -84,7 +84,7 @@ static void ext4_finish_bio(struct bio *bio) /* The bounce data pages are unmapped. */ data_page = page; ctx = (struct ext4_crypto_ctx *)page_private(data_page); - page = ctx->control_page; + page = ctx->w.control_page; } #endif @@ -359,7 +359,6 @@ void ext4_io_submit(struct ext4_io_submit *io) if (bio) { bio_get(io->io_bio); submit_bio(io->io_op, io->io_bio); - BUG_ON(bio_flagged(io->io_bio, BIO_EOPNOTSUPP)); bio_put(io->io_bio); } io->io_bio = NULL; diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c index 171b9ac4b..ec3ef93a5 100644 --- a/fs/ext4/readpage.c +++ b/fs/ext4/readpage.c @@ -54,8 +54,8 @@ static void completion_pages(struct work_struct *work) { #ifdef CONFIG_EXT4_FS_ENCRYPTION struct ext4_crypto_ctx *ctx = - container_of(work, struct ext4_crypto_ctx, work); - struct bio *bio = ctx->bio; + container_of(work, struct ext4_crypto_ctx, r.work); + struct bio *bio = ctx->r.bio; struct bio_vec *bv; int i; @@ -109,9 +109,9 @@ static void mpage_end_io(struct bio *bio, int err) if (err) { ext4_release_crypto_ctx(ctx); } else { - INIT_WORK(&ctx->work, completion_pages); - ctx->bio = bio; - queue_work(ext4_read_workqueue, &ctx->work); + INIT_WORK(&ctx->r.work, completion_pages); + ctx->r.bio = bio; + queue_work(ext4_read_workqueue, &ctx->r.work); return; } } diff --git a/fs/ext4/super.c b/fs/ext4/super.c index ca12affdb..58987b5c5 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -24,6 +24,7 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/blkdev.h> +#include <linux/backing-dev.h> #include <linux/parser.h> #include <linux/buffer_head.h> #include <linux/exportfs.h> @@ -451,7 +452,7 @@ void __ext4_error_file(struct file *file, const char *function, es = EXT4_SB(inode->i_sb)->s_es; es->s_last_error_ino = cpu_to_le32(inode->i_ino); if (ext4_error_ratelimit(inode->i_sb)) { - path = d_path(&(file->f_path), pathname, sizeof(pathname)); + path = file_path(file, pathname, sizeof(pathname)); if (IS_ERR(path)) path = "(unknown)"; va_start(args, fmt); @@ -591,14 +592,17 @@ void __ext4_msg(struct super_block *sb, va_end(args); } +#define ext4_warning_ratelimit(sb) \ + ___ratelimit(&(EXT4_SB(sb)->s_warning_ratelimit_state), \ + "EXT4-fs warning") + void __ext4_warning(struct super_block *sb, const char *function, unsigned int line, const char *fmt, ...) { struct va_format vaf; va_list args; - if (!___ratelimit(&(EXT4_SB(sb)->s_warning_ratelimit_state), - "EXT4-fs warning")) + if (!ext4_warning_ratelimit(sb)) return; va_start(args, fmt); @@ -609,6 +613,24 @@ void __ext4_warning(struct super_block *sb, const char *function, va_end(args); } +void __ext4_warning_inode(const struct inode *inode, const char *function, + unsigned int line, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (!ext4_warning_ratelimit(inode->i_sb)) + return; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + printk(KERN_WARNING "EXT4-fs warning (device %s): %s:%d: " + "inode #%lu: comm %s: %pV\n", inode->i_sb->s_id, + function, line, inode->i_ino, current->comm, &vaf); + va_end(args); +} + void __ext4_grp_locked_error(const char *function, unsigned int line, struct super_block *sb, ext4_group_t grp, unsigned long ino, ext4_fsblk_t block, @@ -880,9 +902,8 @@ static struct inode *ext4_alloc_inode(struct super_block *sb) atomic_set(&ei->i_unwritten, 0); INIT_WORK(&ei->i_rsv_conversion_work, ext4_end_io_rsv_work); #ifdef CONFIG_EXT4_FS_ENCRYPTION - ei->i_encryption_key.mode = EXT4_ENCRYPTION_MODE_INVALID; + ei->i_crypt_info = NULL; #endif - return &ei->vfs_inode; } @@ -959,6 +980,10 @@ void ext4_clear_inode(struct inode *inode) jbd2_free_inode(EXT4_I(inode)->jinode); EXT4_I(inode)->jinode = NULL; } +#ifdef CONFIG_EXT4_FS_ENCRYPTION + if (EXT4_I(inode)->i_crypt_info) + ext4_free_encryption_info(inode, EXT4_I(inode)->i_crypt_info); +#endif } static struct inode *ext4_nfs_get_inode(struct super_block *sb, @@ -3421,7 +3446,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) unsigned long journal_devnum = 0; unsigned long def_mount_opts; struct inode *root; - char *cp; const char *descr; int ret = -ENOMEM; int blocksize, clustersize; @@ -3450,15 +3474,9 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) if (sb->s_bdev->bd_part) sbi->s_sectors_written_start = part_stat_read(sb->s_bdev->bd_part, sectors[1]); -#ifdef CONFIG_EXT4_FS_ENCRYPTION - /* Modes of operations for file and directory encryption. */ - sbi->s_file_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS; - sbi->s_dir_encryption_mode = EXT4_ENCRYPTION_MODE_INVALID; -#endif /* Cleanup superblock name */ - for (cp = sb->s_id; (cp = strchr(cp, '/'));) - *cp = '!'; + strreplace(sb->s_id, '/', '!'); /* -EINVAL is default */ ret = -EINVAL; @@ -4068,7 +4086,15 @@ no_journal: } } - if (unlikely(sbi->s_mount_flags & EXT4_MF_TEST_DUMMY_ENCRYPTION) && + if ((DUMMY_ENCRYPTION_ENABLED(sbi) || + EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_ENCRYPT)) && + (blocksize != PAGE_CACHE_SIZE)) { + ext4_msg(sb, KERN_ERR, + "Unsupported blocksize for fs encryption"); + goto failed_mount_wq; + } + + if (DUMMY_ENCRYPTION_ENABLED(sbi) && !(sb->s_flags & MS_RDONLY) && !EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_ENCRYPT)) { EXT4_SET_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_ENCRYPT); @@ -5414,6 +5440,7 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type, struct inode *inode = sb_dqopt(sb)->files[type]; ext4_lblk_t blk = off >> EXT4_BLOCK_SIZE_BITS(sb); int err, offset = off & (sb->s_blocksize - 1); + int retries = 0; struct buffer_head *bh; handle_t *handle = journal_current_handle(); @@ -5434,7 +5461,12 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type, return -EIO; } - bh = ext4_bread(handle, inode, blk, 1); + do { + bh = ext4_bread(handle, inode, blk, + EXT4_GET_BLOCKS_CREATE | + EXT4_GET_BLOCKS_METADATA_NOFAIL); + } while (IS_ERR(bh) && (PTR_ERR(bh) == -ENOSPC) && + ext4_should_retry_alloc(inode->i_sb, &retries)); if (IS_ERR(bh)) return PTR_ERR(bh); if (!bh) @@ -5651,6 +5683,7 @@ out7: static void __exit ext4_exit_fs(void) { + ext4_exit_crypto(); ext4_destroy_lazyinit_thread(); unregister_as_ext2(); unregister_as_ext3(); diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c index 187b78920..c677f2c10 100644 --- a/fs/ext4/symlink.c +++ b/fs/ext4/symlink.c @@ -23,34 +23,28 @@ #include "xattr.h" #ifdef CONFIG_EXT4_FS_ENCRYPTION -static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cookie) { struct page *cpage = NULL; char *caddr, *paddr = NULL; struct ext4_str cstr, pstr; struct inode *inode = d_inode(dentry); - struct ext4_fname_crypto_ctx *ctx = NULL; struct ext4_encrypted_symlink_data *sd; loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1); int res; u32 plen, max_size = inode->i_sb->s_blocksize; - if (!ext4_encrypted_inode(inode)) - return page_follow_link_light(dentry, nd); - - ctx = ext4_get_fname_crypto_ctx(inode, inode->i_sb->s_blocksize); - if (IS_ERR(ctx)) - return ctx; + res = ext4_get_encryption_info(inode); + if (res) + return ERR_PTR(res); if (ext4_inode_is_fast_symlink(inode)) { caddr = (char *) EXT4_I(inode)->i_data; max_size = sizeof(EXT4_I(inode)->i_data); } else { cpage = read_mapping_page(inode->i_mapping, 0, NULL); - if (IS_ERR(cpage)) { - ext4_put_fname_crypto_ctx(&ctx); - return cpage; - } + if (IS_ERR(cpage)) + return ERR_CAST(cpage); caddr = kmap(cpage); caddr[size] = 0; } @@ -74,21 +68,19 @@ static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd) goto errout; } pstr.name = paddr; - res = _ext4_fname_disk_to_usr(ctx, NULL, &cstr, &pstr); + pstr.len = plen; + res = _ext4_fname_disk_to_usr(inode, NULL, &cstr, &pstr); if (res < 0) goto errout; /* Null-terminate the name */ if (res <= plen) paddr[res] = '\0'; - nd_set_link(nd, paddr); - ext4_put_fname_crypto_ctx(&ctx); if (cpage) { kunmap(cpage); page_cache_release(cpage); } - return NULL; + return *cookie = paddr; errout: - ext4_put_fname_crypto_ctx(&ctx); if (cpage) { kunmap(cpage); page_cache_release(cpage); @@ -97,36 +89,22 @@ errout: return ERR_PTR(res); } -static void ext4_put_link(struct dentry *dentry, struct nameidata *nd, - void *cookie) -{ - struct page *page = cookie; - - if (!page) { - kfree(nd_get_link(nd)); - } else { - kunmap(page); - page_cache_release(page); - } -} +const struct inode_operations ext4_encrypted_symlink_inode_operations = { + .readlink = generic_readlink, + .follow_link = ext4_encrypted_follow_link, + .put_link = kfree_put_link, + .setattr = ext4_setattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .listxattr = ext4_listxattr, + .removexattr = generic_removexattr, +}; #endif -static void *ext4_follow_fast_link(struct dentry *dentry, struct nameidata *nd) -{ - struct ext4_inode_info *ei = EXT4_I(d_inode(dentry)); - nd_set_link(nd, (char *) ei->i_data); - return NULL; -} - const struct inode_operations ext4_symlink_inode_operations = { .readlink = generic_readlink, -#ifdef CONFIG_EXT4_FS_ENCRYPTION - .follow_link = ext4_follow_link, - .put_link = ext4_put_link, -#else .follow_link = page_follow_link_light, .put_link = page_put_link, -#endif .setattr = ext4_setattr, .setxattr = generic_setxattr, .getxattr = generic_getxattr, @@ -136,7 +114,7 @@ const struct inode_operations ext4_symlink_inode_operations = { const struct inode_operations ext4_fast_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = ext4_follow_fast_link, + .follow_link = simple_follow_link, .setattr = ext4_setattr, .setxattr = generic_setxattr, .getxattr = generic_getxattr, diff --git a/fs/f2fs/Kconfig b/fs/f2fs/Kconfig index 05f0f663f..c62976200 100644 --- a/fs/f2fs/Kconfig +++ b/fs/f2fs/Kconfig @@ -72,6 +72,25 @@ config F2FS_CHECK_FS If you want to improve the performance, say N. +config F2FS_FS_ENCRYPTION + bool "F2FS Encryption" + depends on F2FS_FS + depends on F2FS_FS_XATTR + select CRYPTO_AES + select CRYPTO_CBC + select CRYPTO_ECB + select CRYPTO_XTS + select CRYPTO_CTS + select CRYPTO_CTR + select CRYPTO_SHA256 + select KEYS + select ENCRYPTED_KEYS + help + Enable encryption of f2fs files and directories. This + feature is similar to ecryptfs, but it is more memory + efficient since it avoids caching the encrypted and + decrypted pages in the page cache. + config F2FS_IO_TRACE bool "F2FS IO tracer" depends on F2FS_FS diff --git a/fs/f2fs/Makefile b/fs/f2fs/Makefile index d92397731..396be1a39 100644 --- a/fs/f2fs/Makefile +++ b/fs/f2fs/Makefile @@ -6,3 +6,5 @@ f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o f2fs-$(CONFIG_F2FS_IO_TRACE) += trace.o +f2fs-$(CONFIG_F2FS_FS_ENCRYPTION) += crypto_policy.o crypto.o \ + crypto_key.o crypto_fname.o diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c index 4320ffab3..c8f25f724 100644 --- a/fs/f2fs/acl.c +++ b/fs/f2fs/acl.c @@ -334,51 +334,45 @@ static int f2fs_acl_create(struct inode *dir, umode_t *mode, struct page *dpage) { struct posix_acl *p; + struct posix_acl *clone; int ret; + *acl = NULL; + *default_acl = NULL; + if (S_ISLNK(*mode) || !IS_POSIXACL(dir)) - goto no_acl; + return 0; p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage); - if (IS_ERR(p)) { - if (p == ERR_PTR(-EOPNOTSUPP)) - goto apply_umask; - return PTR_ERR(p); + if (!p || p == ERR_PTR(-EOPNOTSUPP)) { + *mode &= ~current_umask(); + return 0; } + if (IS_ERR(p)) + return PTR_ERR(p); - if (!p) - goto apply_umask; - - *acl = f2fs_acl_clone(p, GFP_NOFS); - if (!*acl) + clone = f2fs_acl_clone(p, GFP_NOFS); + if (!clone) goto no_mem; - ret = f2fs_acl_create_masq(*acl, mode); + ret = f2fs_acl_create_masq(clone, mode); if (ret < 0) goto no_mem_clone; - if (ret == 0) { - posix_acl_release(*acl); - *acl = NULL; - } + if (ret == 0) + posix_acl_release(clone); + else + *acl = clone; - if (!S_ISDIR(*mode)) { + if (!S_ISDIR(*mode)) posix_acl_release(p); - *default_acl = NULL; - } else { + else *default_acl = p; - } - return 0; -apply_umask: - *mode &= ~current_umask(); -no_acl: - *default_acl = NULL; - *acl = NULL; return 0; no_mem_clone: - posix_acl_release(*acl); + posix_acl_release(clone); no_mem: posix_acl_release(p); return -ENOMEM; diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index a5e17a2a0..b70bbe1a6 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -52,9 +52,11 @@ struct page *get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index) struct address_space *mapping = META_MAPPING(sbi); struct page *page; struct f2fs_io_info fio = { + .sbi = sbi, .type = META, .rw = READ_SYNC | REQ_META | REQ_PRIO, .blk_addr = index, + .encrypted_page = NULL, }; repeat: page = grab_cache_page(mapping, index); @@ -65,7 +67,9 @@ repeat: if (PageUptodate(page)) goto out; - if (f2fs_submit_page_bio(sbi, page, &fio)) + fio.page = page; + + if (f2fs_submit_page_bio(&fio)) goto repeat; lock_page(page); @@ -77,8 +81,7 @@ out: return page; } -static inline bool is_valid_blkaddr(struct f2fs_sb_info *sbi, - block_t blkaddr, int type) +bool is_valid_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr, int type) { switch (type) { case META_NAT: @@ -118,8 +121,10 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, int type struct page *page; block_t blkno = start; struct f2fs_io_info fio = { + .sbi = sbi, .type = META, - .rw = READ_SYNC | REQ_META | REQ_PRIO + .rw = READ_SYNC | REQ_META | REQ_PRIO, + .encrypted_page = NULL, }; for (; nrpages-- > 0; blkno++) { @@ -161,7 +166,8 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, int type continue; } - f2fs_submit_page_mbio(sbi, page, &fio); + fio.page = page; + f2fs_submit_page_mbio(&fio); f2fs_put_page(page, 0); } out: @@ -510,7 +516,12 @@ static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk) grab_meta_page(sbi, start_blk + index); index = 1; - spin_lock(&im->ino_lock); + + /* + * we don't need to do spin_lock(&im->ino_lock) here, since all the + * orphan inode operations are covered under f2fs_lock_op(). + * And, spin_lock should be avoided due to page operations below. + */ head = &im->ino_list; /* loop for each orphan inode entry and write them in Jornal block */ @@ -550,8 +561,6 @@ static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk) set_page_dirty(page); f2fs_put_page(page, 1); } - - spin_unlock(&im->ino_lock); } static struct page *validate_checkpoint(struct f2fs_sb_info *sbi, @@ -879,10 +888,8 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) unsigned long orphan_num = sbi->im[ORPHAN_INO].ino_num; nid_t last_nid = nm_i->next_scan_nid; block_t start_blk; - struct page *cp_page; unsigned int data_sum_blocks, orphan_blocks; __u32 crc32 = 0; - void *kaddr; int i; int cp_payload_blks = __cp_payload(sbi); @@ -979,19 +986,11 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) start_blk = __start_cp_addr(sbi); /* write out checkpoint buffer at block 0 */ - cp_page = grab_meta_page(sbi, start_blk++); - kaddr = page_address(cp_page); - memcpy(kaddr, ckpt, F2FS_BLKSIZE); - set_page_dirty(cp_page); - f2fs_put_page(cp_page, 1); - - for (i = 1; i < 1 + cp_payload_blks; i++) { - cp_page = grab_meta_page(sbi, start_blk++); - kaddr = page_address(cp_page); - memcpy(kaddr, (char *)ckpt + i * F2FS_BLKSIZE, F2FS_BLKSIZE); - set_page_dirty(cp_page); - f2fs_put_page(cp_page, 1); - } + update_meta_page(sbi, ckpt, start_blk++); + + for (i = 1; i < 1 + cp_payload_blks; i++) + update_meta_page(sbi, (char *)ckpt + i * F2FS_BLKSIZE, + start_blk++); if (orphan_num) { write_orphan_inodes(sbi, start_blk); @@ -1006,11 +1005,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) } /* writeout checkpoint block */ - cp_page = grab_meta_page(sbi, start_blk); - kaddr = page_address(cp_page); - memcpy(kaddr, ckpt, F2FS_BLKSIZE); - set_page_dirty(cp_page); - f2fs_put_page(cp_page, 1); + update_meta_page(sbi, ckpt, start_blk); /* wait for previous submitted node/meta pages writeback */ wait_on_all_pages_writeback(sbi); @@ -1036,7 +1031,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) if (unlikely(f2fs_cp_error(sbi))) return; - clear_prefree_segments(sbi); + clear_prefree_segments(sbi, cpc); clear_sbi_flag(sbi, SBI_IS_DIRTY); } @@ -1051,7 +1046,8 @@ void write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) mutex_lock(&sbi->cp_mutex); if (!is_sbi_flag_set(sbi, SBI_IS_DIRTY) && - (cpc->reason == CP_FASTBOOT || cpc->reason == CP_SYNC)) + (cpc->reason == CP_FASTBOOT || cpc->reason == CP_SYNC || + (cpc->reason == CP_DISCARD && !sbi->discard_blks))) goto out; if (unlikely(f2fs_cp_error(sbi))) goto out; diff --git a/fs/f2fs/crypto.c b/fs/f2fs/crypto.c new file mode 100644 index 000000000..4a62ef14e --- /dev/null +++ b/fs/f2fs/crypto.c @@ -0,0 +1,491 @@ +/* + * linux/fs/f2fs/crypto.c + * + * Copied from linux/fs/ext4/crypto.c + * + * Copyright (C) 2015, Google, Inc. + * Copyright (C) 2015, Motorola Mobility + * + * This contains encryption functions for f2fs + * + * Written by Michael Halcrow, 2014. + * + * Filename encryption additions + * Uday Savagaonkar, 2014 + * Encryption policy handling additions + * Ildar Muslukhov, 2014 + * Remove ext4_encrypted_zeroout(), + * add f2fs_restore_and_release_control_page() + * Jaegeuk Kim, 2015. + * + * This has not yet undergone a rigorous security audit. + * + * The usage of AES-XTS should conform to recommendations in NIST + * Special Publication 800-38E and IEEE P1619/D16. + */ +#include <crypto/hash.h> +#include <crypto/sha.h> +#include <keys/user-type.h> +#include <keys/encrypted-type.h> +#include <linux/crypto.h> +#include <linux/ecryptfs.h> +#include <linux/gfp.h> +#include <linux/kernel.h> +#include <linux/key.h> +#include <linux/list.h> +#include <linux/mempool.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/random.h> +#include <linux/scatterlist.h> +#include <linux/spinlock_types.h> +#include <linux/f2fs_fs.h> +#include <linux/ratelimit.h> +#include <linux/bio.h> + +#include "f2fs.h" +#include "xattr.h" + +/* Encryption added and removed here! (L: */ + +static unsigned int num_prealloc_crypto_pages = 32; +static unsigned int num_prealloc_crypto_ctxs = 128; + +module_param(num_prealloc_crypto_pages, uint, 0444); +MODULE_PARM_DESC(num_prealloc_crypto_pages, + "Number of crypto pages to preallocate"); +module_param(num_prealloc_crypto_ctxs, uint, 0444); +MODULE_PARM_DESC(num_prealloc_crypto_ctxs, + "Number of crypto contexts to preallocate"); + +static mempool_t *f2fs_bounce_page_pool; + +static LIST_HEAD(f2fs_free_crypto_ctxs); +static DEFINE_SPINLOCK(f2fs_crypto_ctx_lock); + +static struct workqueue_struct *f2fs_read_workqueue; +static DEFINE_MUTEX(crypto_init); + +static struct kmem_cache *f2fs_crypto_ctx_cachep; +struct kmem_cache *f2fs_crypt_info_cachep; + +/** + * f2fs_release_crypto_ctx() - Releases an encryption context + * @ctx: The encryption context to release. + * + * If the encryption context was allocated from the pre-allocated pool, returns + * it to that pool. Else, frees it. + * + * If there's a bounce page in the context, this frees that. + */ +void f2fs_release_crypto_ctx(struct f2fs_crypto_ctx *ctx) +{ + unsigned long flags; + + if (ctx->flags & F2FS_WRITE_PATH_FL && ctx->w.bounce_page) { + mempool_free(ctx->w.bounce_page, f2fs_bounce_page_pool); + ctx->w.bounce_page = NULL; + } + ctx->w.control_page = NULL; + if (ctx->flags & F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL) { + kmem_cache_free(f2fs_crypto_ctx_cachep, ctx); + } else { + spin_lock_irqsave(&f2fs_crypto_ctx_lock, flags); + list_add(&ctx->free_list, &f2fs_free_crypto_ctxs); + spin_unlock_irqrestore(&f2fs_crypto_ctx_lock, flags); + } +} + +/** + * f2fs_get_crypto_ctx() - Gets an encryption context + * @inode: The inode for which we are doing the crypto + * + * Allocates and initializes an encryption context. + * + * Return: An allocated and initialized encryption context on success; error + * value or NULL otherwise. + */ +struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *inode) +{ + struct f2fs_crypto_ctx *ctx = NULL; + unsigned long flags; + struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; + + if (ci == NULL) + return ERR_PTR(-ENOKEY); + + /* + * We first try getting the ctx from a free list because in + * the common case the ctx will have an allocated and + * initialized crypto tfm, so it's probably a worthwhile + * optimization. For the bounce page, we first try getting it + * from the kernel allocator because that's just about as fast + * as getting it from a list and because a cache of free pages + * should generally be a "last resort" option for a filesystem + * to be able to do its job. + */ + spin_lock_irqsave(&f2fs_crypto_ctx_lock, flags); + ctx = list_first_entry_or_null(&f2fs_free_crypto_ctxs, + struct f2fs_crypto_ctx, free_list); + if (ctx) + list_del(&ctx->free_list); + spin_unlock_irqrestore(&f2fs_crypto_ctx_lock, flags); + if (!ctx) { + ctx = kmem_cache_zalloc(f2fs_crypto_ctx_cachep, GFP_NOFS); + if (!ctx) + return ERR_PTR(-ENOMEM); + ctx->flags |= F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL; + } else { + ctx->flags &= ~F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL; + } + ctx->flags &= ~F2FS_WRITE_PATH_FL; + return ctx; +} + +/* + * Call f2fs_decrypt on every single page, reusing the encryption + * context. + */ +static void completion_pages(struct work_struct *work) +{ + struct f2fs_crypto_ctx *ctx = + container_of(work, struct f2fs_crypto_ctx, r.work); + struct bio *bio = ctx->r.bio; + struct bio_vec *bv; + int i; + + bio_for_each_segment_all(bv, bio, i) { + struct page *page = bv->bv_page; + int ret = f2fs_decrypt(ctx, page); + + if (ret) { + WARN_ON_ONCE(1); + SetPageError(page); + } else + SetPageUptodate(page); + unlock_page(page); + } + f2fs_release_crypto_ctx(ctx); + bio_put(bio); +} + +void f2fs_end_io_crypto_work(struct f2fs_crypto_ctx *ctx, struct bio *bio) +{ + INIT_WORK(&ctx->r.work, completion_pages); + ctx->r.bio = bio; + queue_work(f2fs_read_workqueue, &ctx->r.work); +} + +static void f2fs_crypto_destroy(void) +{ + struct f2fs_crypto_ctx *pos, *n; + + list_for_each_entry_safe(pos, n, &f2fs_free_crypto_ctxs, free_list) + kmem_cache_free(f2fs_crypto_ctx_cachep, pos); + INIT_LIST_HEAD(&f2fs_free_crypto_ctxs); + if (f2fs_bounce_page_pool) + mempool_destroy(f2fs_bounce_page_pool); + f2fs_bounce_page_pool = NULL; +} + +/** + * f2fs_crypto_initialize() - Set up for f2fs encryption. + * + * We only call this when we start accessing encrypted files, since it + * results in memory getting allocated that wouldn't otherwise be used. + * + * Return: Zero on success, non-zero otherwise. + */ +int f2fs_crypto_initialize(void) +{ + int i, res = -ENOMEM; + + if (f2fs_bounce_page_pool) + return 0; + + mutex_lock(&crypto_init); + if (f2fs_bounce_page_pool) + goto already_initialized; + + for (i = 0; i < num_prealloc_crypto_ctxs; i++) { + struct f2fs_crypto_ctx *ctx; + + ctx = kmem_cache_zalloc(f2fs_crypto_ctx_cachep, GFP_KERNEL); + if (!ctx) + goto fail; + list_add(&ctx->free_list, &f2fs_free_crypto_ctxs); + } + + /* must be allocated at the last step to avoid race condition above */ + f2fs_bounce_page_pool = + mempool_create_page_pool(num_prealloc_crypto_pages, 0); + if (!f2fs_bounce_page_pool) + goto fail; + +already_initialized: + mutex_unlock(&crypto_init); + return 0; +fail: + f2fs_crypto_destroy(); + mutex_unlock(&crypto_init); + return res; +} + +/** + * f2fs_exit_crypto() - Shutdown the f2fs encryption system + */ +void f2fs_exit_crypto(void) +{ + f2fs_crypto_destroy(); + + if (f2fs_read_workqueue) + destroy_workqueue(f2fs_read_workqueue); + if (f2fs_crypto_ctx_cachep) + kmem_cache_destroy(f2fs_crypto_ctx_cachep); + if (f2fs_crypt_info_cachep) + kmem_cache_destroy(f2fs_crypt_info_cachep); +} + +int __init f2fs_init_crypto(void) +{ + int res = -ENOMEM; + + f2fs_read_workqueue = alloc_workqueue("f2fs_crypto", WQ_HIGHPRI, 0); + if (!f2fs_read_workqueue) + goto fail; + + f2fs_crypto_ctx_cachep = KMEM_CACHE(f2fs_crypto_ctx, + SLAB_RECLAIM_ACCOUNT); + if (!f2fs_crypto_ctx_cachep) + goto fail; + + f2fs_crypt_info_cachep = KMEM_CACHE(f2fs_crypt_info, + SLAB_RECLAIM_ACCOUNT); + if (!f2fs_crypt_info_cachep) + goto fail; + + return 0; +fail: + f2fs_exit_crypto(); + return res; +} + +void f2fs_restore_and_release_control_page(struct page **page) +{ + struct f2fs_crypto_ctx *ctx; + struct page *bounce_page; + + /* The bounce data pages are unmapped. */ + if ((*page)->mapping) + return; + + /* The bounce data page is unmapped. */ + bounce_page = *page; + ctx = (struct f2fs_crypto_ctx *)page_private(bounce_page); + + /* restore control page */ + *page = ctx->w.control_page; + + f2fs_restore_control_page(bounce_page); +} + +void f2fs_restore_control_page(struct page *data_page) +{ + struct f2fs_crypto_ctx *ctx = + (struct f2fs_crypto_ctx *)page_private(data_page); + + set_page_private(data_page, (unsigned long)NULL); + ClearPagePrivate(data_page); + unlock_page(data_page); + f2fs_release_crypto_ctx(ctx); +} + +/** + * f2fs_crypt_complete() - The completion callback for page encryption + * @req: The asynchronous encryption request context + * @res: The result of the encryption operation + */ +static void f2fs_crypt_complete(struct crypto_async_request *req, int res) +{ + struct f2fs_completion_result *ecr = req->data; + + if (res == -EINPROGRESS) + return; + ecr->res = res; + complete(&ecr->completion); +} + +typedef enum { + F2FS_DECRYPT = 0, + F2FS_ENCRYPT, +} f2fs_direction_t; + +static int f2fs_page_crypto(struct f2fs_crypto_ctx *ctx, + struct inode *inode, + f2fs_direction_t rw, + pgoff_t index, + struct page *src_page, + struct page *dest_page) +{ + u8 xts_tweak[F2FS_XTS_TWEAK_SIZE]; + struct ablkcipher_request *req = NULL; + DECLARE_F2FS_COMPLETION_RESULT(ecr); + struct scatterlist dst, src; + struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; + struct crypto_ablkcipher *tfm = ci->ci_ctfm; + int res = 0; + + req = ablkcipher_request_alloc(tfm, GFP_NOFS); + if (!req) { + printk_ratelimited(KERN_ERR + "%s: crypto_request_alloc() failed\n", + __func__); + return -ENOMEM; + } + ablkcipher_request_set_callback( + req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, + f2fs_crypt_complete, &ecr); + + BUILD_BUG_ON(F2FS_XTS_TWEAK_SIZE < sizeof(index)); + memcpy(xts_tweak, &index, sizeof(index)); + memset(&xts_tweak[sizeof(index)], 0, + F2FS_XTS_TWEAK_SIZE - sizeof(index)); + + sg_init_table(&dst, 1); + sg_set_page(&dst, dest_page, PAGE_CACHE_SIZE, 0); + sg_init_table(&src, 1); + sg_set_page(&src, src_page, PAGE_CACHE_SIZE, 0); + ablkcipher_request_set_crypt(req, &src, &dst, PAGE_CACHE_SIZE, + xts_tweak); + if (rw == F2FS_DECRYPT) + res = crypto_ablkcipher_decrypt(req); + else + res = crypto_ablkcipher_encrypt(req); + if (res == -EINPROGRESS || res == -EBUSY) { + BUG_ON(req->base.data != &ecr); + wait_for_completion(&ecr.completion); + res = ecr.res; + } + ablkcipher_request_free(req); + if (res) { + printk_ratelimited(KERN_ERR + "%s: crypto_ablkcipher_encrypt() returned %d\n", + __func__, res); + return res; + } + return 0; +} + +static struct page *alloc_bounce_page(struct f2fs_crypto_ctx *ctx) +{ + ctx->w.bounce_page = mempool_alloc(f2fs_bounce_page_pool, GFP_NOWAIT); + if (ctx->w.bounce_page == NULL) + return ERR_PTR(-ENOMEM); + ctx->flags |= F2FS_WRITE_PATH_FL; + return ctx->w.bounce_page; +} + +/** + * f2fs_encrypt() - Encrypts a page + * @inode: The inode for which the encryption should take place + * @plaintext_page: The page to encrypt. Must be locked. + * + * Allocates a ciphertext page and encrypts plaintext_page into it using the ctx + * encryption context. + * + * Called on the page write path. The caller must call + * f2fs_restore_control_page() on the returned ciphertext page to + * release the bounce buffer and the encryption context. + * + * Return: An allocated page with the encrypted content on success. Else, an + * error value or NULL. + */ +struct page *f2fs_encrypt(struct inode *inode, + struct page *plaintext_page) +{ + struct f2fs_crypto_ctx *ctx; + struct page *ciphertext_page = NULL; + int err; + + BUG_ON(!PageLocked(plaintext_page)); + + ctx = f2fs_get_crypto_ctx(inode); + if (IS_ERR(ctx)) + return (struct page *)ctx; + + /* The encryption operation will require a bounce page. */ + ciphertext_page = alloc_bounce_page(ctx); + if (IS_ERR(ciphertext_page)) + goto err_out; + + ctx->w.control_page = plaintext_page; + err = f2fs_page_crypto(ctx, inode, F2FS_ENCRYPT, plaintext_page->index, + plaintext_page, ciphertext_page); + if (err) { + ciphertext_page = ERR_PTR(err); + goto err_out; + } + + SetPagePrivate(ciphertext_page); + set_page_private(ciphertext_page, (unsigned long)ctx); + lock_page(ciphertext_page); + return ciphertext_page; + +err_out: + f2fs_release_crypto_ctx(ctx); + return ciphertext_page; +} + +/** + * f2fs_decrypt() - Decrypts a page in-place + * @ctx: The encryption context. + * @page: The page to decrypt. Must be locked. + * + * Decrypts page in-place using the ctx encryption context. + * + * Called from the read completion callback. + * + * Return: Zero on success, non-zero otherwise. + */ +int f2fs_decrypt(struct f2fs_crypto_ctx *ctx, struct page *page) +{ + BUG_ON(!PageLocked(page)); + + return f2fs_page_crypto(ctx, page->mapping->host, + F2FS_DECRYPT, page->index, page, page); +} + +/* + * Convenience function which takes care of allocating and + * deallocating the encryption context + */ +int f2fs_decrypt_one(struct inode *inode, struct page *page) +{ + struct f2fs_crypto_ctx *ctx = f2fs_get_crypto_ctx(inode); + int ret; + + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + ret = f2fs_decrypt(ctx, page); + f2fs_release_crypto_ctx(ctx); + return ret; +} + +bool f2fs_valid_contents_enc_mode(uint32_t mode) +{ + return (mode == F2FS_ENCRYPTION_MODE_AES_256_XTS); +} + +/** + * f2fs_validate_encryption_key_size() - Validate the encryption key size + * @mode: The key mode. + * @size: The key size to validate. + * + * Return: The validated key size for @mode. Zero if invalid. + */ +uint32_t f2fs_validate_encryption_key_size(uint32_t mode, uint32_t size) +{ + if (size == f2fs_encryption_key_size(mode)) + return size; + return 0; +} diff --git a/fs/f2fs/crypto_fname.c b/fs/f2fs/crypto_fname.c new file mode 100644 index 000000000..ab377d496 --- /dev/null +++ b/fs/f2fs/crypto_fname.c @@ -0,0 +1,440 @@ +/* + * linux/fs/f2fs/crypto_fname.c + * + * Copied from linux/fs/ext4/crypto.c + * + * Copyright (C) 2015, Google, Inc. + * Copyright (C) 2015, Motorola Mobility + * + * This contains functions for filename crypto management in f2fs + * + * Written by Uday Savagaonkar, 2014. + * + * Adjust f2fs dentry structure + * Jaegeuk Kim, 2015. + * + * This has not yet undergone a rigorous security audit. + */ +#include <crypto/hash.h> +#include <crypto/sha.h> +#include <keys/encrypted-type.h> +#include <keys/user-type.h> +#include <linux/crypto.h> +#include <linux/gfp.h> +#include <linux/kernel.h> +#include <linux/key.h> +#include <linux/list.h> +#include <linux/mempool.h> +#include <linux/random.h> +#include <linux/scatterlist.h> +#include <linux/spinlock_types.h> +#include <linux/f2fs_fs.h> +#include <linux/ratelimit.h> + +#include "f2fs.h" +#include "f2fs_crypto.h" +#include "xattr.h" + +/** + * f2fs_dir_crypt_complete() - + */ +static void f2fs_dir_crypt_complete(struct crypto_async_request *req, int res) +{ + struct f2fs_completion_result *ecr = req->data; + + if (res == -EINPROGRESS) + return; + ecr->res = res; + complete(&ecr->completion); +} + +bool f2fs_valid_filenames_enc_mode(uint32_t mode) +{ + return (mode == F2FS_ENCRYPTION_MODE_AES_256_CTS); +} + +static unsigned max_name_len(struct inode *inode) +{ + return S_ISLNK(inode->i_mode) ? inode->i_sb->s_blocksize : + F2FS_NAME_LEN; +} + +/** + * f2fs_fname_encrypt() - + * + * This function encrypts the input filename, and returns the length of the + * ciphertext. Errors are returned as negative numbers. We trust the caller to + * allocate sufficient memory to oname string. + */ +static int f2fs_fname_encrypt(struct inode *inode, + const struct qstr *iname, struct f2fs_str *oname) +{ + u32 ciphertext_len; + struct ablkcipher_request *req = NULL; + DECLARE_F2FS_COMPLETION_RESULT(ecr); + struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; + struct crypto_ablkcipher *tfm = ci->ci_ctfm; + int res = 0; + char iv[F2FS_CRYPTO_BLOCK_SIZE]; + struct scatterlist src_sg, dst_sg; + int padding = 4 << (ci->ci_flags & F2FS_POLICY_FLAGS_PAD_MASK); + char *workbuf, buf[32], *alloc_buf = NULL; + unsigned lim = max_name_len(inode); + + if (iname->len <= 0 || iname->len > lim) + return -EIO; + + ciphertext_len = (iname->len < F2FS_CRYPTO_BLOCK_SIZE) ? + F2FS_CRYPTO_BLOCK_SIZE : iname->len; + ciphertext_len = f2fs_fname_crypto_round_up(ciphertext_len, padding); + ciphertext_len = (ciphertext_len > lim) ? lim : ciphertext_len; + + if (ciphertext_len <= sizeof(buf)) { + workbuf = buf; + } else { + alloc_buf = kmalloc(ciphertext_len, GFP_NOFS); + if (!alloc_buf) + return -ENOMEM; + workbuf = alloc_buf; + } + + /* Allocate request */ + req = ablkcipher_request_alloc(tfm, GFP_NOFS); + if (!req) { + printk_ratelimited(KERN_ERR + "%s: crypto_request_alloc() failed\n", __func__); + kfree(alloc_buf); + return -ENOMEM; + } + ablkcipher_request_set_callback(req, + CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, + f2fs_dir_crypt_complete, &ecr); + + /* Copy the input */ + memcpy(workbuf, iname->name, iname->len); + if (iname->len < ciphertext_len) + memset(workbuf + iname->len, 0, ciphertext_len - iname->len); + + /* Initialize IV */ + memset(iv, 0, F2FS_CRYPTO_BLOCK_SIZE); + + /* Create encryption request */ + sg_init_one(&src_sg, workbuf, ciphertext_len); + sg_init_one(&dst_sg, oname->name, ciphertext_len); + ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, ciphertext_len, iv); + res = crypto_ablkcipher_encrypt(req); + if (res == -EINPROGRESS || res == -EBUSY) { + BUG_ON(req->base.data != &ecr); + wait_for_completion(&ecr.completion); + res = ecr.res; + } + kfree(alloc_buf); + ablkcipher_request_free(req); + if (res < 0) { + printk_ratelimited(KERN_ERR + "%s: Error (error code %d)\n", __func__, res); + } + oname->len = ciphertext_len; + return res; +} + +/* + * f2fs_fname_decrypt() + * This function decrypts the input filename, and returns + * the length of the plaintext. + * Errors are returned as negative numbers. + * We trust the caller to allocate sufficient memory to oname string. + */ +static int f2fs_fname_decrypt(struct inode *inode, + const struct f2fs_str *iname, struct f2fs_str *oname) +{ + struct ablkcipher_request *req = NULL; + DECLARE_F2FS_COMPLETION_RESULT(ecr); + struct scatterlist src_sg, dst_sg; + struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; + struct crypto_ablkcipher *tfm = ci->ci_ctfm; + int res = 0; + char iv[F2FS_CRYPTO_BLOCK_SIZE]; + unsigned lim = max_name_len(inode); + + if (iname->len <= 0 || iname->len > lim) + return -EIO; + + /* Allocate request */ + req = ablkcipher_request_alloc(tfm, GFP_NOFS); + if (!req) { + printk_ratelimited(KERN_ERR + "%s: crypto_request_alloc() failed\n", __func__); + return -ENOMEM; + } + ablkcipher_request_set_callback(req, + CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, + f2fs_dir_crypt_complete, &ecr); + + /* Initialize IV */ + memset(iv, 0, F2FS_CRYPTO_BLOCK_SIZE); + + /* Create decryption request */ + sg_init_one(&src_sg, iname->name, iname->len); + sg_init_one(&dst_sg, oname->name, oname->len); + ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, iname->len, iv); + res = crypto_ablkcipher_decrypt(req); + if (res == -EINPROGRESS || res == -EBUSY) { + BUG_ON(req->base.data != &ecr); + wait_for_completion(&ecr.completion); + res = ecr.res; + } + ablkcipher_request_free(req); + if (res < 0) { + printk_ratelimited(KERN_ERR + "%s: Error in f2fs_fname_decrypt (error code %d)\n", + __func__, res); + return res; + } + + oname->len = strnlen(oname->name, iname->len); + return oname->len; +} + +static const char *lookup_table = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; + +/** + * f2fs_fname_encode_digest() - + * + * Encodes the input digest using characters from the set [a-zA-Z0-9_+]. + * The encoded string is roughly 4/3 times the size of the input string. + */ +static int digest_encode(const char *src, int len, char *dst) +{ + int i = 0, bits = 0, ac = 0; + char *cp = dst; + + while (i < len) { + ac += (((unsigned char) src[i]) << bits); + bits += 8; + do { + *cp++ = lookup_table[ac & 0x3f]; + ac >>= 6; + bits -= 6; + } while (bits >= 6); + i++; + } + if (bits) + *cp++ = lookup_table[ac & 0x3f]; + return cp - dst; +} + +static int digest_decode(const char *src, int len, char *dst) +{ + int i = 0, bits = 0, ac = 0; + const char *p; + char *cp = dst; + + while (i < len) { + p = strchr(lookup_table, src[i]); + if (p == NULL || src[i] == 0) + return -2; + ac += (p - lookup_table) << bits; + bits += 6; + if (bits >= 8) { + *cp++ = ac & 0xff; + ac >>= 8; + bits -= 8; + } + i++; + } + if (ac) + return -1; + return cp - dst; +} + +/** + * f2fs_fname_crypto_round_up() - + * + * Return: The next multiple of block size + */ +u32 f2fs_fname_crypto_round_up(u32 size, u32 blksize) +{ + return ((size + blksize - 1) / blksize) * blksize; +} + +/** + * f2fs_fname_crypto_alloc_obuff() - + * + * Allocates an output buffer that is sufficient for the crypto operation + * specified by the context and the direction. + */ +int f2fs_fname_crypto_alloc_buffer(struct inode *inode, + u32 ilen, struct f2fs_str *crypto_str) +{ + unsigned int olen; + int padding = 16; + struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; + + if (ci) + padding = 4 << (ci->ci_flags & F2FS_POLICY_FLAGS_PAD_MASK); + if (padding < F2FS_CRYPTO_BLOCK_SIZE) + padding = F2FS_CRYPTO_BLOCK_SIZE; + olen = f2fs_fname_crypto_round_up(ilen, padding); + crypto_str->len = olen; + if (olen < F2FS_FNAME_CRYPTO_DIGEST_SIZE * 2) + olen = F2FS_FNAME_CRYPTO_DIGEST_SIZE * 2; + /* Allocated buffer can hold one more character to null-terminate the + * string */ + crypto_str->name = kmalloc(olen + 1, GFP_NOFS); + if (!(crypto_str->name)) + return -ENOMEM; + return 0; +} + +/** + * f2fs_fname_crypto_free_buffer() - + * + * Frees the buffer allocated for crypto operation. + */ +void f2fs_fname_crypto_free_buffer(struct f2fs_str *crypto_str) +{ + if (!crypto_str) + return; + kfree(crypto_str->name); + crypto_str->name = NULL; +} + +/** + * f2fs_fname_disk_to_usr() - converts a filename from disk space to user space + */ +int f2fs_fname_disk_to_usr(struct inode *inode, + f2fs_hash_t *hash, + const struct f2fs_str *iname, + struct f2fs_str *oname) +{ + const struct qstr qname = FSTR_TO_QSTR(iname); + char buf[24]; + int ret; + + if (is_dot_dotdot(&qname)) { + oname->name[0] = '.'; + oname->name[iname->len - 1] = '.'; + oname->len = iname->len; + return oname->len; + } + + if (F2FS_I(inode)->i_crypt_info) + return f2fs_fname_decrypt(inode, iname, oname); + + if (iname->len <= F2FS_FNAME_CRYPTO_DIGEST_SIZE) { + ret = digest_encode(iname->name, iname->len, oname->name); + oname->len = ret; + return ret; + } + if (hash) { + memcpy(buf, hash, 4); + memset(buf + 4, 0, 4); + } else + memset(buf, 0, 8); + memcpy(buf + 8, iname->name + iname->len - 16, 16); + oname->name[0] = '_'; + ret = digest_encode(buf, 24, oname->name + 1); + oname->len = ret + 1; + return ret + 1; +} + +/** + * f2fs_fname_usr_to_disk() - converts a filename from user space to disk space + */ +int f2fs_fname_usr_to_disk(struct inode *inode, + const struct qstr *iname, + struct f2fs_str *oname) +{ + int res; + struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; + + if (is_dot_dotdot(iname)) { + oname->name[0] = '.'; + oname->name[iname->len - 1] = '.'; + oname->len = iname->len; + return oname->len; + } + + if (ci) { + res = f2fs_fname_encrypt(inode, iname, oname); + return res; + } + /* Without a proper key, a user is not allowed to modify the filenames + * in a directory. Consequently, a user space name cannot be mapped to + * a disk-space name */ + return -EACCES; +} + +int f2fs_fname_setup_filename(struct inode *dir, const struct qstr *iname, + int lookup, struct f2fs_filename *fname) +{ + struct f2fs_crypt_info *ci; + int ret = 0, bigname = 0; + + memset(fname, 0, sizeof(struct f2fs_filename)); + fname->usr_fname = iname; + + if (!f2fs_encrypted_inode(dir) || is_dot_dotdot(iname)) { + fname->disk_name.name = (unsigned char *)iname->name; + fname->disk_name.len = iname->len; + return 0; + } + ret = f2fs_get_encryption_info(dir); + if (ret) + return ret; + ci = F2FS_I(dir)->i_crypt_info; + if (ci) { + ret = f2fs_fname_crypto_alloc_buffer(dir, iname->len, + &fname->crypto_buf); + if (ret < 0) + return ret; + ret = f2fs_fname_encrypt(dir, iname, &fname->crypto_buf); + if (ret < 0) + goto errout; + fname->disk_name.name = fname->crypto_buf.name; + fname->disk_name.len = fname->crypto_buf.len; + return 0; + } + if (!lookup) + return -EACCES; + + /* We don't have the key and we are doing a lookup; decode the + * user-supplied name + */ + if (iname->name[0] == '_') + bigname = 1; + if ((bigname && (iname->len != 33)) || + (!bigname && (iname->len > 43))) + return -ENOENT; + + fname->crypto_buf.name = kmalloc(32, GFP_KERNEL); + if (fname->crypto_buf.name == NULL) + return -ENOMEM; + ret = digest_decode(iname->name + bigname, iname->len - bigname, + fname->crypto_buf.name); + if (ret < 0) { + ret = -ENOENT; + goto errout; + } + fname->crypto_buf.len = ret; + if (bigname) { + memcpy(&fname->hash, fname->crypto_buf.name, 4); + } else { + fname->disk_name.name = fname->crypto_buf.name; + fname->disk_name.len = fname->crypto_buf.len; + } + return 0; +errout: + f2fs_fname_crypto_free_buffer(&fname->crypto_buf); + return ret; +} + +void f2fs_fname_free_filename(struct f2fs_filename *fname) +{ + kfree(fname->crypto_buf.name); + fname->crypto_buf.name = NULL; + fname->usr_fname = NULL; + fname->disk_name.name = NULL; +} diff --git a/fs/f2fs/crypto_key.c b/fs/f2fs/crypto_key.c new file mode 100644 index 000000000..95b8f936f --- /dev/null +++ b/fs/f2fs/crypto_key.c @@ -0,0 +1,255 @@ +/* + * linux/fs/f2fs/crypto_key.c + * + * Copied from linux/fs/f2fs/crypto_key.c + * + * Copyright (C) 2015, Google, Inc. + * + * This contains encryption key functions for f2fs + * + * Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015. + */ +#include <keys/encrypted-type.h> +#include <keys/user-type.h> +#include <linux/random.h> +#include <linux/scatterlist.h> +#include <uapi/linux/keyctl.h> +#include <crypto/hash.h> +#include <linux/f2fs_fs.h> + +#include "f2fs.h" +#include "xattr.h" + +static void derive_crypt_complete(struct crypto_async_request *req, int rc) +{ + struct f2fs_completion_result *ecr = req->data; + + if (rc == -EINPROGRESS) + return; + + ecr->res = rc; + complete(&ecr->completion); +} + +/** + * f2fs_derive_key_aes() - Derive a key using AES-128-ECB + * @deriving_key: Encryption key used for derivatio. + * @source_key: Source key to which to apply derivation. + * @derived_key: Derived key. + * + * Return: Zero on success; non-zero otherwise. + */ +static int f2fs_derive_key_aes(char deriving_key[F2FS_AES_128_ECB_KEY_SIZE], + char source_key[F2FS_AES_256_XTS_KEY_SIZE], + char derived_key[F2FS_AES_256_XTS_KEY_SIZE]) +{ + int res = 0; + struct ablkcipher_request *req = NULL; + DECLARE_F2FS_COMPLETION_RESULT(ecr); + struct scatterlist src_sg, dst_sg; + struct crypto_ablkcipher *tfm = crypto_alloc_ablkcipher("ecb(aes)", 0, + 0); + + if (IS_ERR(tfm)) { + res = PTR_ERR(tfm); + tfm = NULL; + goto out; + } + crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_REQ_WEAK_KEY); + req = ablkcipher_request_alloc(tfm, GFP_NOFS); + if (!req) { + res = -ENOMEM; + goto out; + } + ablkcipher_request_set_callback(req, + CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, + derive_crypt_complete, &ecr); + res = crypto_ablkcipher_setkey(tfm, deriving_key, + F2FS_AES_128_ECB_KEY_SIZE); + if (res < 0) + goto out; + + sg_init_one(&src_sg, source_key, F2FS_AES_256_XTS_KEY_SIZE); + sg_init_one(&dst_sg, derived_key, F2FS_AES_256_XTS_KEY_SIZE); + ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, + F2FS_AES_256_XTS_KEY_SIZE, NULL); + res = crypto_ablkcipher_encrypt(req); + if (res == -EINPROGRESS || res == -EBUSY) { + BUG_ON(req->base.data != &ecr); + wait_for_completion(&ecr.completion); + res = ecr.res; + } +out: + if (req) + ablkcipher_request_free(req); + if (tfm) + crypto_free_ablkcipher(tfm); + return res; +} + +static void f2fs_free_crypt_info(struct f2fs_crypt_info *ci) +{ + if (!ci) + return; + + if (ci->ci_keyring_key) + key_put(ci->ci_keyring_key); + crypto_free_ablkcipher(ci->ci_ctfm); + kmem_cache_free(f2fs_crypt_info_cachep, ci); +} + +void f2fs_free_encryption_info(struct inode *inode, struct f2fs_crypt_info *ci) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + struct f2fs_crypt_info *prev; + + if (ci == NULL) + ci = ACCESS_ONCE(fi->i_crypt_info); + if (ci == NULL) + return; + prev = cmpxchg(&fi->i_crypt_info, ci, NULL); + if (prev != ci) + return; + + f2fs_free_crypt_info(ci); +} + +int _f2fs_get_encryption_info(struct inode *inode) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + struct f2fs_crypt_info *crypt_info; + char full_key_descriptor[F2FS_KEY_DESC_PREFIX_SIZE + + (F2FS_KEY_DESCRIPTOR_SIZE * 2) + 1]; + struct key *keyring_key = NULL; + struct f2fs_encryption_key *master_key; + struct f2fs_encryption_context ctx; + struct user_key_payload *ukp; + struct crypto_ablkcipher *ctfm; + const char *cipher_str; + char raw_key[F2FS_MAX_KEY_SIZE]; + char mode; + int res; + + res = f2fs_crypto_initialize(); + if (res) + return res; +retry: + crypt_info = ACCESS_ONCE(fi->i_crypt_info); + if (crypt_info) { + if (!crypt_info->ci_keyring_key || + key_validate(crypt_info->ci_keyring_key) == 0) + return 0; + f2fs_free_encryption_info(inode, crypt_info); + goto retry; + } + + res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION, + F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, + &ctx, sizeof(ctx), NULL); + if (res < 0) + return res; + else if (res != sizeof(ctx)) + return -EINVAL; + res = 0; + + crypt_info = kmem_cache_alloc(f2fs_crypt_info_cachep, GFP_NOFS); + if (!crypt_info) + return -ENOMEM; + + crypt_info->ci_flags = ctx.flags; + crypt_info->ci_data_mode = ctx.contents_encryption_mode; + crypt_info->ci_filename_mode = ctx.filenames_encryption_mode; + crypt_info->ci_ctfm = NULL; + crypt_info->ci_keyring_key = NULL; + memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor, + sizeof(crypt_info->ci_master_key)); + if (S_ISREG(inode->i_mode)) + mode = crypt_info->ci_data_mode; + else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) + mode = crypt_info->ci_filename_mode; + else + BUG(); + + switch (mode) { + case F2FS_ENCRYPTION_MODE_AES_256_XTS: + cipher_str = "xts(aes)"; + break; + case F2FS_ENCRYPTION_MODE_AES_256_CTS: + cipher_str = "cts(cbc(aes))"; + break; + default: + printk_once(KERN_WARNING + "f2fs: unsupported key mode %d (ino %u)\n", + mode, (unsigned) inode->i_ino); + res = -ENOKEY; + goto out; + } + + memcpy(full_key_descriptor, F2FS_KEY_DESC_PREFIX, + F2FS_KEY_DESC_PREFIX_SIZE); + sprintf(full_key_descriptor + F2FS_KEY_DESC_PREFIX_SIZE, + "%*phN", F2FS_KEY_DESCRIPTOR_SIZE, + ctx.master_key_descriptor); + full_key_descriptor[F2FS_KEY_DESC_PREFIX_SIZE + + (2 * F2FS_KEY_DESCRIPTOR_SIZE)] = '\0'; + keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL); + if (IS_ERR(keyring_key)) { + res = PTR_ERR(keyring_key); + keyring_key = NULL; + goto out; + } + crypt_info->ci_keyring_key = keyring_key; + BUG_ON(keyring_key->type != &key_type_logon); + ukp = ((struct user_key_payload *)keyring_key->payload.data); + if (ukp->datalen != sizeof(struct f2fs_encryption_key)) { + res = -EINVAL; + goto out; + } + master_key = (struct f2fs_encryption_key *)ukp->data; + BUILD_BUG_ON(F2FS_AES_128_ECB_KEY_SIZE != + F2FS_KEY_DERIVATION_NONCE_SIZE); + BUG_ON(master_key->size != F2FS_AES_256_XTS_KEY_SIZE); + res = f2fs_derive_key_aes(ctx.nonce, master_key->raw, + raw_key); + if (res) + goto out; + + ctfm = crypto_alloc_ablkcipher(cipher_str, 0, 0); + if (!ctfm || IS_ERR(ctfm)) { + res = ctfm ? PTR_ERR(ctfm) : -ENOMEM; + printk(KERN_DEBUG + "%s: error %d (inode %u) allocating crypto tfm\n", + __func__, res, (unsigned) inode->i_ino); + goto out; + } + crypt_info->ci_ctfm = ctfm; + crypto_ablkcipher_clear_flags(ctfm, ~0); + crypto_tfm_set_flags(crypto_ablkcipher_tfm(ctfm), + CRYPTO_TFM_REQ_WEAK_KEY); + res = crypto_ablkcipher_setkey(ctfm, raw_key, + f2fs_encryption_key_size(mode)); + if (res) + goto out; + + memzero_explicit(raw_key, sizeof(raw_key)); + if (cmpxchg(&fi->i_crypt_info, NULL, crypt_info) != NULL) { + f2fs_free_crypt_info(crypt_info); + goto retry; + } + return 0; + +out: + if (res == -ENOKEY && !S_ISREG(inode->i_mode)) + res = 0; + + f2fs_free_crypt_info(crypt_info); + memzero_explicit(raw_key, sizeof(raw_key)); + return res; +} + +int f2fs_has_encryption_key(struct inode *inode) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + + return (fi->i_crypt_info != NULL); +} diff --git a/fs/f2fs/crypto_policy.c b/fs/f2fs/crypto_policy.c new file mode 100644 index 000000000..d4a96af51 --- /dev/null +++ b/fs/f2fs/crypto_policy.c @@ -0,0 +1,209 @@ +/* + * copied from linux/fs/ext4/crypto_policy.c + * + * Copyright (C) 2015, Google, Inc. + * Copyright (C) 2015, Motorola Mobility. + * + * This contains encryption policy functions for f2fs with some modifications + * to support f2fs-specific xattr APIs. + * + * Written by Michael Halcrow, 2015. + * Modified by Jaegeuk Kim, 2015. + */ +#include <linux/random.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/f2fs_fs.h> + +#include "f2fs.h" +#include "xattr.h" + +static int f2fs_inode_has_encryption_context(struct inode *inode) +{ + int res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION, + F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, NULL, 0, NULL); + return (res > 0); +} + +/* + * check whether the policy is consistent with the encryption context + * for the inode + */ +static int f2fs_is_encryption_context_consistent_with_policy( + struct inode *inode, const struct f2fs_encryption_policy *policy) +{ + struct f2fs_encryption_context ctx; + int res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION, + F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx, + sizeof(ctx), NULL); + + if (res != sizeof(ctx)) + return 0; + + return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor, + F2FS_KEY_DESCRIPTOR_SIZE) == 0 && + (ctx.flags == policy->flags) && + (ctx.contents_encryption_mode == + policy->contents_encryption_mode) && + (ctx.filenames_encryption_mode == + policy->filenames_encryption_mode)); +} + +static int f2fs_create_encryption_context_from_policy( + struct inode *inode, const struct f2fs_encryption_policy *policy) +{ + struct f2fs_encryption_context ctx; + + ctx.format = F2FS_ENCRYPTION_CONTEXT_FORMAT_V1; + memcpy(ctx.master_key_descriptor, policy->master_key_descriptor, + F2FS_KEY_DESCRIPTOR_SIZE); + + if (!f2fs_valid_contents_enc_mode(policy->contents_encryption_mode)) { + printk(KERN_WARNING + "%s: Invalid contents encryption mode %d\n", __func__, + policy->contents_encryption_mode); + return -EINVAL; + } + + if (!f2fs_valid_filenames_enc_mode(policy->filenames_encryption_mode)) { + printk(KERN_WARNING + "%s: Invalid filenames encryption mode %d\n", __func__, + policy->filenames_encryption_mode); + return -EINVAL; + } + + if (policy->flags & ~F2FS_POLICY_FLAGS_VALID) + return -EINVAL; + + ctx.contents_encryption_mode = policy->contents_encryption_mode; + ctx.filenames_encryption_mode = policy->filenames_encryption_mode; + ctx.flags = policy->flags; + BUILD_BUG_ON(sizeof(ctx.nonce) != F2FS_KEY_DERIVATION_NONCE_SIZE); + get_random_bytes(ctx.nonce, F2FS_KEY_DERIVATION_NONCE_SIZE); + + return f2fs_setxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION, + F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx, + sizeof(ctx), NULL, XATTR_CREATE); +} + +int f2fs_process_policy(const struct f2fs_encryption_policy *policy, + struct inode *inode) +{ + if (policy->version != 0) + return -EINVAL; + + if (!S_ISDIR(inode->i_mode)) + return -EINVAL; + + if (!f2fs_inode_has_encryption_context(inode)) { + if (!f2fs_empty_dir(inode)) + return -ENOTEMPTY; + return f2fs_create_encryption_context_from_policy(inode, + policy); + } + + if (f2fs_is_encryption_context_consistent_with_policy(inode, policy)) + return 0; + + printk(KERN_WARNING "%s: Policy inconsistent with encryption context\n", + __func__); + return -EINVAL; +} + +int f2fs_get_policy(struct inode *inode, struct f2fs_encryption_policy *policy) +{ + struct f2fs_encryption_context ctx; + int res; + + if (!f2fs_encrypted_inode(inode)) + return -ENODATA; + + res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION, + F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, + &ctx, sizeof(ctx), NULL); + if (res != sizeof(ctx)) + return -ENODATA; + if (ctx.format != F2FS_ENCRYPTION_CONTEXT_FORMAT_V1) + return -EINVAL; + + policy->version = 0; + policy->contents_encryption_mode = ctx.contents_encryption_mode; + policy->filenames_encryption_mode = ctx.filenames_encryption_mode; + policy->flags = ctx.flags; + memcpy(&policy->master_key_descriptor, ctx.master_key_descriptor, + F2FS_KEY_DESCRIPTOR_SIZE); + return 0; +} + +int f2fs_is_child_context_consistent_with_parent(struct inode *parent, + struct inode *child) +{ + struct f2fs_crypt_info *parent_ci, *child_ci; + int res; + + if ((parent == NULL) || (child == NULL)) { + pr_err("parent %p child %p\n", parent, child); + BUG_ON(1); + } + + /* no restrictions if the parent directory is not encrypted */ + if (!f2fs_encrypted_inode(parent)) + return 1; + /* if the child directory is not encrypted, this is always a problem */ + if (!f2fs_encrypted_inode(child)) + return 0; + res = f2fs_get_encryption_info(parent); + if (res) + return 0; + res = f2fs_get_encryption_info(child); + if (res) + return 0; + parent_ci = F2FS_I(parent)->i_crypt_info; + child_ci = F2FS_I(child)->i_crypt_info; + if (!parent_ci && !child_ci) + return 1; + if (!parent_ci || !child_ci) + return 0; + + return (memcmp(parent_ci->ci_master_key, + child_ci->ci_master_key, + F2FS_KEY_DESCRIPTOR_SIZE) == 0 && + (parent_ci->ci_data_mode == child_ci->ci_data_mode) && + (parent_ci->ci_filename_mode == child_ci->ci_filename_mode) && + (parent_ci->ci_flags == child_ci->ci_flags)); +} + +/** + * f2fs_inherit_context() - Sets a child context from its parent + * @parent: Parent inode from which the context is inherited. + * @child: Child inode that inherits the context from @parent. + * + * Return: Zero on success, non-zero otherwise + */ +int f2fs_inherit_context(struct inode *parent, struct inode *child, + struct page *ipage) +{ + struct f2fs_encryption_context ctx; + struct f2fs_crypt_info *ci; + int res; + + res = f2fs_get_encryption_info(parent); + if (res < 0) + return res; + + ci = F2FS_I(parent)->i_crypt_info; + BUG_ON(ci == NULL); + + ctx.format = F2FS_ENCRYPTION_CONTEXT_FORMAT_V1; + + ctx.contents_encryption_mode = ci->ci_data_mode; + ctx.filenames_encryption_mode = ci->ci_filename_mode; + ctx.flags = ci->ci_flags; + memcpy(ctx.master_key_descriptor, ci->ci_master_key, + F2FS_KEY_DESCRIPTOR_SIZE); + + get_random_bytes(ctx.nonce, F2FS_KEY_DERIVATION_NONCE_SIZE); + return f2fs_setxattr(child, F2FS_XATTR_INDEX_ENCRYPTION, + F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx, + sizeof(ctx), ipage, XATTR_CREATE); +} diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 1e1aae669..f71e19a9d 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -18,6 +18,7 @@ #include <linux/bio.h> #include <linux/prefetch.h> #include <linux/uio.h> +#include <linux/cleancache.h> #include "f2fs.h" #include "node.h" @@ -33,6 +34,15 @@ static void f2fs_read_end_io(struct bio *bio, int err) struct bio_vec *bvec; int i; + if (f2fs_bio_encrypted(bio)) { + if (err) { + f2fs_release_crypto_ctx(bio->bi_private); + } else { + f2fs_end_io_crypto_work(bio->bi_private, bio); + return; + } + } + bio_for_each_segment_all(bvec, bio, i) { struct page *page = bvec->bv_page; @@ -56,6 +66,8 @@ static void f2fs_write_end_io(struct bio *bio, int err) bio_for_each_segment_all(bvec, bio, i) { struct page *page = bvec->bv_page; + f2fs_restore_and_release_control_page(&page); + if (unlikely(err)) { set_page_dirty(page); set_bit(AS_EIO, &page->mapping->flags); @@ -86,7 +98,7 @@ static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr, bio->bi_bdev = sbi->sb->s_bdev; bio->bi_iter.bi_sector = SECTOR_FROM_BLOCK(blk_addr); bio->bi_end_io = is_read ? f2fs_read_end_io : f2fs_write_end_io; - bio->bi_private = sbi; + bio->bi_private = is_read ? NULL : sbi; return bio; } @@ -133,16 +145,16 @@ void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, * Fill the locked page with data located in the block address. * Return unlocked page. */ -int f2fs_submit_page_bio(struct f2fs_sb_info *sbi, struct page *page, - struct f2fs_io_info *fio) +int f2fs_submit_page_bio(struct f2fs_io_info *fio) { struct bio *bio; + struct page *page = fio->encrypted_page ? fio->encrypted_page : fio->page; trace_f2fs_submit_page_bio(page, fio); - f2fs_trace_ios(page, fio, 0); + f2fs_trace_ios(fio, 0); /* Allocate a new bio */ - bio = __bio_alloc(sbi, fio->blk_addr, 1, is_read_io(fio->rw)); + bio = __bio_alloc(fio->sbi, fio->blk_addr, 1, is_read_io(fio->rw)); if (bio_add_page(bio, page, PAGE_CACHE_SIZE, 0) < PAGE_CACHE_SIZE) { bio_put(bio); @@ -154,12 +166,13 @@ int f2fs_submit_page_bio(struct f2fs_sb_info *sbi, struct page *page, return 0; } -void f2fs_submit_page_mbio(struct f2fs_sb_info *sbi, struct page *page, - struct f2fs_io_info *fio) +void f2fs_submit_page_mbio(struct f2fs_io_info *fio) { + struct f2fs_sb_info *sbi = fio->sbi; enum page_type btype = PAGE_TYPE_OF_BIO(fio->type); struct f2fs_bio_info *io; bool is_read = is_read_io(fio->rw); + struct page *bio_page; io = is_read ? &sbi->read_io : &sbi->write_io[btype]; @@ -181,17 +194,19 @@ alloc_new: io->fio = *fio; } - if (bio_add_page(io->bio, page, PAGE_CACHE_SIZE, 0) < + bio_page = fio->encrypted_page ? fio->encrypted_page : fio->page; + + if (bio_add_page(io->bio, bio_page, PAGE_CACHE_SIZE, 0) < PAGE_CACHE_SIZE) { __submit_merged_bio(io); goto alloc_new; } io->last_block_in_bio = fio->blk_addr; - f2fs_trace_ios(page, fio, 0); + f2fs_trace_ios(fio, 0); up_write(&io->io_rwsem); - trace_f2fs_submit_page_mbio(page, fio); + trace_f2fs_submit_page_mbio(fio->page, fio); } /* @@ -251,19 +266,6 @@ int f2fs_reserve_block(struct dnode_of_data *dn, pgoff_t index) return err; } -static void f2fs_map_bh(struct super_block *sb, pgoff_t pgofs, - struct extent_info *ei, struct buffer_head *bh_result) -{ - unsigned int blkbits = sb->s_blocksize_bits; - size_t max_size = bh_result->b_size; - size_t mapped_size; - - clear_buffer_new(bh_result); - map_bh(bh_result, sb, ei->blk + pgofs - ei->fofs); - mapped_size = (ei->fofs + ei->len - pgofs) << blkbits; - bh_result->b_size = min(max_size, mapped_size); -} - static bool lookup_extent_info(struct inode *inode, pgoff_t pgofs, struct extent_info *ei) { @@ -905,7 +907,7 @@ void f2fs_update_extent_cache(struct dnode_of_data *dn) sync_inode_page(dn); } -struct page *find_data_page(struct inode *inode, pgoff_t index, bool sync) +struct page *get_read_data_page(struct inode *inode, pgoff_t index, int rw) { struct address_space *mapping = inode->i_mapping; struct dnode_of_data dn; @@ -913,83 +915,15 @@ struct page *find_data_page(struct inode *inode, pgoff_t index, bool sync) struct extent_info ei; int err; struct f2fs_io_info fio = { + .sbi = F2FS_I_SB(inode), .type = DATA, - .rw = sync ? READ_SYNC : READA, + .rw = rw, + .encrypted_page = NULL, }; - /* - * If sync is false, it needs to check its block allocation. - * This is need and triggered by two flows: - * gc and truncate_partial_data_page. - */ - if (!sync) - goto search; - - page = find_get_page(mapping, index); - if (page && PageUptodate(page)) - return page; - f2fs_put_page(page, 0); -search: - if (f2fs_lookup_extent_cache(inode, index, &ei)) { - dn.data_blkaddr = ei.blk + index - ei.fofs; - goto got_it; - } - - set_new_dnode(&dn, inode, NULL, NULL, 0); - err = get_dnode_of_data(&dn, index, LOOKUP_NODE); - if (err) - return ERR_PTR(err); - f2fs_put_dnode(&dn); + if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + return read_mapping_page(mapping, index, NULL); - if (dn.data_blkaddr == NULL_ADDR) - return ERR_PTR(-ENOENT); - - /* By fallocate(), there is no cached page, but with NEW_ADDR */ - if (unlikely(dn.data_blkaddr == NEW_ADDR)) - return ERR_PTR(-EINVAL); - -got_it: - page = grab_cache_page(mapping, index); - if (!page) - return ERR_PTR(-ENOMEM); - - if (PageUptodate(page)) { - unlock_page(page); - return page; - } - - fio.blk_addr = dn.data_blkaddr; - err = f2fs_submit_page_bio(F2FS_I_SB(inode), page, &fio); - if (err) - return ERR_PTR(err); - - if (sync) { - wait_on_page_locked(page); - if (unlikely(!PageUptodate(page))) { - f2fs_put_page(page, 0); - return ERR_PTR(-EIO); - } - } - return page; -} - -/* - * If it tries to access a hole, return an error. - * Because, the callers, functions in dir.c and GC, should be able to know - * whether this page exists or not. - */ -struct page *get_lock_data_page(struct inode *inode, pgoff_t index) -{ - struct address_space *mapping = inode->i_mapping; - struct dnode_of_data dn; - struct page *page; - struct extent_info ei; - int err; - struct f2fs_io_info fio = { - .type = DATA, - .rw = READ_SYNC, - }; -repeat: page = grab_cache_page(mapping, index); if (!page) return ERR_PTR(-ENOMEM); @@ -1011,10 +945,11 @@ repeat: f2fs_put_page(page, 1); return ERR_PTR(-ENOENT); } - got_it: - if (PageUptodate(page)) + if (PageUptodate(page)) { + unlock_page(page); return page; + } /* * A new dentry page is allocated but not able to be written, since its @@ -1025,14 +960,58 @@ got_it: if (dn.data_blkaddr == NEW_ADDR) { zero_user_segment(page, 0, PAGE_CACHE_SIZE); SetPageUptodate(page); + unlock_page(page); return page; } fio.blk_addr = dn.data_blkaddr; - err = f2fs_submit_page_bio(F2FS_I_SB(inode), page, &fio); + fio.page = page; + err = f2fs_submit_page_bio(&fio); if (err) return ERR_PTR(err); + return page; +} + +struct page *find_data_page(struct inode *inode, pgoff_t index) +{ + struct address_space *mapping = inode->i_mapping; + struct page *page; + + page = find_get_page(mapping, index); + if (page && PageUptodate(page)) + return page; + f2fs_put_page(page, 0); + page = get_read_data_page(inode, index, READ_SYNC); + if (IS_ERR(page)) + return page; + + if (PageUptodate(page)) + return page; + + wait_on_page_locked(page); + if (unlikely(!PageUptodate(page))) { + f2fs_put_page(page, 0); + return ERR_PTR(-EIO); + } + return page; +} + +/* + * If it tries to access a hole, return an error. + * Because, the callers, functions in dir.c and GC, should be able to know + * whether this page exists or not. + */ +struct page *get_lock_data_page(struct inode *inode, pgoff_t index) +{ + struct address_space *mapping = inode->i_mapping; + struct page *page; +repeat: + page = get_read_data_page(inode, index, READ_SYNC); + if (IS_ERR(page)) + return page; + + /* wait for read completion */ lock_page(page); if (unlikely(!PageUptodate(page))) { f2fs_put_page(page, 1); @@ -1060,46 +1039,37 @@ struct page *get_new_data_page(struct inode *inode, struct page *page; struct dnode_of_data dn; int err; +repeat: + page = grab_cache_page(mapping, index); + if (!page) + return ERR_PTR(-ENOMEM); set_new_dnode(&dn, inode, ipage, NULL, 0); err = f2fs_reserve_block(&dn, index); - if (err) + if (err) { + f2fs_put_page(page, 1); return ERR_PTR(err); -repeat: - page = grab_cache_page(mapping, index); - if (!page) { - err = -ENOMEM; - goto put_err; } + if (!ipage) + f2fs_put_dnode(&dn); if (PageUptodate(page)) - return page; + goto got_it; if (dn.data_blkaddr == NEW_ADDR) { zero_user_segment(page, 0, PAGE_CACHE_SIZE); SetPageUptodate(page); } else { - struct f2fs_io_info fio = { - .type = DATA, - .rw = READ_SYNC, - .blk_addr = dn.data_blkaddr, - }; - err = f2fs_submit_page_bio(F2FS_I_SB(inode), page, &fio); - if (err) - goto put_err; + f2fs_put_page(page, 1); - lock_page(page); - if (unlikely(!PageUptodate(page))) { - f2fs_put_page(page, 1); - err = -EIO; - goto put_err; - } - if (unlikely(page->mapping != mapping)) { - f2fs_put_page(page, 1); + page = get_read_data_page(inode, index, READ_SYNC); + if (IS_ERR(page)) goto repeat; - } - } + /* wait for read completion */ + lock_page(page); + } +got_it: if (new_i_size && i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) { i_size_write(inode, ((index + 1) << PAGE_CACHE_SHIFT)); @@ -1107,10 +1077,6 @@ repeat: set_inode_flag(F2FS_I(inode), FI_UPDATE_DIR); } return page; - -put_err: - f2fs_put_dnode(&dn); - return ERR_PTR(err); } static int __allocate_data_block(struct dnode_of_data *dn) @@ -1208,18 +1174,18 @@ out: } /* - * get_data_block() now supported readahead/bmap/rw direct_IO with mapped bh. + * f2fs_map_blocks() now supported readahead/bmap/rw direct_IO with + * f2fs_map_blocks structure. * If original data blocks are allocated, then give them to blockdev. * Otherwise, * a. preallocate requested block addresses * b. do not use extent cache for better performance * c. give the block addresses to blockdev */ -static int __get_data_block(struct inode *inode, sector_t iblock, - struct buffer_head *bh_result, int create, bool fiemap) +static int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, + int create, bool fiemap) { - unsigned int blkbits = inode->i_sb->s_blocksize_bits; - unsigned maxblocks = bh_result->b_size >> blkbits; + unsigned int maxblocks = map->m_len; struct dnode_of_data dn; int mode = create ? ALLOC_NODE : LOOKUP_NODE_RA; pgoff_t pgofs, end_offset; @@ -1227,11 +1193,16 @@ static int __get_data_block(struct inode *inode, sector_t iblock, struct extent_info ei; bool allocated = false; - /* Get the page offset from the block offset(iblock) */ - pgofs = (pgoff_t)(iblock >> (PAGE_CACHE_SHIFT - blkbits)); + map->m_len = 0; + map->m_flags = 0; + + /* it only supports block size == page size */ + pgofs = (pgoff_t)map->m_lblk; if (f2fs_lookup_extent_cache(inode, pgofs, &ei)) { - f2fs_map_bh(inode->i_sb, pgofs, &ei, bh_result); + map->m_pblk = ei.blk + pgofs - ei.fofs; + map->m_len = min((pgoff_t)maxblocks, ei.fofs + ei.len - pgofs); + map->m_flags = F2FS_MAP_MAPPED; goto out; } @@ -1250,21 +1221,23 @@ static int __get_data_block(struct inode *inode, sector_t iblock, goto put_out; if (dn.data_blkaddr != NULL_ADDR) { - clear_buffer_new(bh_result); - map_bh(bh_result, inode->i_sb, dn.data_blkaddr); + map->m_flags = F2FS_MAP_MAPPED; + map->m_pblk = dn.data_blkaddr; + if (dn.data_blkaddr == NEW_ADDR) + map->m_flags |= F2FS_MAP_UNWRITTEN; } else if (create) { err = __allocate_data_block(&dn); if (err) goto put_out; allocated = true; - set_buffer_new(bh_result); - map_bh(bh_result, inode->i_sb, dn.data_blkaddr); + map->m_flags = F2FS_MAP_NEW | F2FS_MAP_MAPPED; + map->m_pblk = dn.data_blkaddr; } else { goto put_out; } end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); - bh_result->b_size = (((size_t)1) << blkbits); + map->m_len = 1; dn.ofs_in_node++; pgofs++; @@ -1288,22 +1261,25 @@ get_next: end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); } - if (maxblocks > (bh_result->b_size >> blkbits)) { + if (maxblocks > map->m_len) { block_t blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); if (blkaddr == NULL_ADDR && create) { err = __allocate_data_block(&dn); if (err) goto sync_out; allocated = true; - set_buffer_new(bh_result); + map->m_flags |= F2FS_MAP_NEW; blkaddr = dn.data_blkaddr; } /* Give more consecutive addresses for the readahead */ - if (blkaddr == (bh_result->b_blocknr + ofs)) { + if ((map->m_pblk != NEW_ADDR && + blkaddr == (map->m_pblk + ofs)) || + (map->m_pblk == NEW_ADDR && + blkaddr == NEW_ADDR)) { ofs++; dn.ofs_in_node++; pgofs++; - bh_result->b_size += (((size_t)1) << blkbits); + map->m_len++; goto get_next; } } @@ -1316,10 +1292,28 @@ unlock_out: if (create) f2fs_unlock_op(F2FS_I_SB(inode)); out: - trace_f2fs_get_data_block(inode, iblock, bh_result, err); + trace_f2fs_map_blocks(inode, map, err); return err; } +static int __get_data_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh, int create, bool fiemap) +{ + struct f2fs_map_blocks map; + int ret; + + map.m_lblk = iblock; + map.m_len = bh->b_size >> inode->i_blkbits; + + ret = f2fs_map_blocks(inode, &map, create, fiemap); + if (!ret) { + map_bh(bh, inode->i_sb, map.m_pblk); + bh->b_state = (bh->b_state & ~F2FS_MAP_FLAGS) | map.m_flags; + bh->b_size = map.m_len << inode->i_blkbits; + } + return ret; +} + static int get_data_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { @@ -1332,11 +1326,268 @@ static int get_data_block_fiemap(struct inode *inode, sector_t iblock, return __get_data_block(inode, iblock, bh_result, create, true); } +static inline sector_t logical_to_blk(struct inode *inode, loff_t offset) +{ + return (offset >> inode->i_blkbits); +} + +static inline loff_t blk_to_logical(struct inode *inode, sector_t blk) +{ + return (blk << inode->i_blkbits); +} + int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len) { - return generic_block_fiemap(inode, fieinfo, - start, len, get_data_block_fiemap); + struct buffer_head map_bh; + sector_t start_blk, last_blk; + loff_t isize = i_size_read(inode); + u64 logical = 0, phys = 0, size = 0; + u32 flags = 0; + bool past_eof = false, whole_file = false; + int ret = 0; + + ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC); + if (ret) + return ret; + + mutex_lock(&inode->i_mutex); + + if (len >= isize) { + whole_file = true; + len = isize; + } + + if (logical_to_blk(inode, len) == 0) + len = blk_to_logical(inode, 1); + + start_blk = logical_to_blk(inode, start); + last_blk = logical_to_blk(inode, start + len - 1); +next: + memset(&map_bh, 0, sizeof(struct buffer_head)); + map_bh.b_size = len; + + ret = get_data_block_fiemap(inode, start_blk, &map_bh, 0); + if (ret) + goto out; + + /* HOLE */ + if (!buffer_mapped(&map_bh)) { + start_blk++; + + if (!past_eof && blk_to_logical(inode, start_blk) >= isize) + past_eof = 1; + + if (past_eof && size) { + flags |= FIEMAP_EXTENT_LAST; + ret = fiemap_fill_next_extent(fieinfo, logical, + phys, size, flags); + } else if (size) { + ret = fiemap_fill_next_extent(fieinfo, logical, + phys, size, flags); + size = 0; + } + + /* if we have holes up to/past EOF then we're done */ + if (start_blk > last_blk || past_eof || ret) + goto out; + } else { + if (start_blk > last_blk && !whole_file) { + ret = fiemap_fill_next_extent(fieinfo, logical, + phys, size, flags); + goto out; + } + + /* + * if size != 0 then we know we already have an extent + * to add, so add it. + */ + if (size) { + ret = fiemap_fill_next_extent(fieinfo, logical, + phys, size, flags); + if (ret) + goto out; + } + + logical = blk_to_logical(inode, start_blk); + phys = blk_to_logical(inode, map_bh.b_blocknr); + size = map_bh.b_size; + flags = 0; + if (buffer_unwritten(&map_bh)) + flags = FIEMAP_EXTENT_UNWRITTEN; + + start_blk += logical_to_blk(inode, size); + + /* + * If we are past the EOF, then we need to make sure as + * soon as we find a hole that the last extent we found + * is marked with FIEMAP_EXTENT_LAST + */ + if (!past_eof && logical + size >= isize) + past_eof = true; + } + cond_resched(); + if (fatal_signal_pending(current)) + ret = -EINTR; + else + goto next; +out: + if (ret == 1) + ret = 0; + + mutex_unlock(&inode->i_mutex); + return ret; +} + +/* + * This function was originally taken from fs/mpage.c, and customized for f2fs. + * Major change was from block_size == page_size in f2fs by default. + */ +static int f2fs_mpage_readpages(struct address_space *mapping, + struct list_head *pages, struct page *page, + unsigned nr_pages) +{ + struct bio *bio = NULL; + unsigned page_idx; + sector_t last_block_in_bio = 0; + struct inode *inode = mapping->host; + const unsigned blkbits = inode->i_blkbits; + const unsigned blocksize = 1 << blkbits; + sector_t block_in_file; + sector_t last_block; + sector_t last_block_in_file; + sector_t block_nr; + struct block_device *bdev = inode->i_sb->s_bdev; + struct f2fs_map_blocks map; + + map.m_pblk = 0; + map.m_lblk = 0; + map.m_len = 0; + map.m_flags = 0; + + for (page_idx = 0; nr_pages; page_idx++, nr_pages--) { + + prefetchw(&page->flags); + if (pages) { + page = list_entry(pages->prev, struct page, lru); + list_del(&page->lru); + if (add_to_page_cache_lru(page, mapping, + page->index, GFP_KERNEL)) + goto next_page; + } + + block_in_file = (sector_t)page->index; + last_block = block_in_file + nr_pages; + last_block_in_file = (i_size_read(inode) + blocksize - 1) >> + blkbits; + if (last_block > last_block_in_file) + last_block = last_block_in_file; + + /* + * Map blocks using the previous result first. + */ + if ((map.m_flags & F2FS_MAP_MAPPED) && + block_in_file > map.m_lblk && + block_in_file < (map.m_lblk + map.m_len)) + goto got_it; + + /* + * Then do more f2fs_map_blocks() calls until we are + * done with this page. + */ + map.m_flags = 0; + + if (block_in_file < last_block) { + map.m_lblk = block_in_file; + map.m_len = last_block - block_in_file; + + if (f2fs_map_blocks(inode, &map, 0, false)) + goto set_error_page; + } +got_it: + if ((map.m_flags & F2FS_MAP_MAPPED)) { + block_nr = map.m_pblk + block_in_file - map.m_lblk; + SetPageMappedToDisk(page); + + if (!PageUptodate(page) && !cleancache_get_page(page)) { + SetPageUptodate(page); + goto confused; + } + } else { + zero_user_segment(page, 0, PAGE_CACHE_SIZE); + SetPageUptodate(page); + unlock_page(page); + goto next_page; + } + + /* + * This page will go to BIO. Do we need to send this + * BIO off first? + */ + if (bio && (last_block_in_bio != block_nr - 1)) { +submit_and_realloc: + submit_bio(READ, bio); + bio = NULL; + } + if (bio == NULL) { + struct f2fs_crypto_ctx *ctx = NULL; + + if (f2fs_encrypted_inode(inode) && + S_ISREG(inode->i_mode)) { + struct page *cpage; + + ctx = f2fs_get_crypto_ctx(inode); + if (IS_ERR(ctx)) + goto set_error_page; + + /* wait the page to be moved by cleaning */ + cpage = find_lock_page( + META_MAPPING(F2FS_I_SB(inode)), + block_nr); + if (cpage) { + f2fs_wait_on_page_writeback(cpage, + DATA); + f2fs_put_page(cpage, 1); + } + } + + bio = bio_alloc(GFP_KERNEL, + min_t(int, nr_pages, bio_get_nr_vecs(bdev))); + if (!bio) { + if (ctx) + f2fs_release_crypto_ctx(ctx); + goto set_error_page; + } + bio->bi_bdev = bdev; + bio->bi_iter.bi_sector = SECTOR_FROM_BLOCK(block_nr); + bio->bi_end_io = f2fs_read_end_io; + bio->bi_private = ctx; + } + + if (bio_add_page(bio, page, blocksize, 0) < blocksize) + goto submit_and_realloc; + + last_block_in_bio = block_nr; + goto next_page; +set_error_page: + SetPageError(page); + zero_user_segment(page, 0, PAGE_CACHE_SIZE); + unlock_page(page); + goto next_page; +confused: + if (bio) { + submit_bio(READ, bio); + bio = NULL; + } + unlock_page(page); +next_page: + if (pages) + page_cache_release(page); + } + BUG_ON(pages && !list_empty(pages)); + if (bio) + submit_bio(READ, bio); + return 0; } static int f2fs_read_data_page(struct file *file, struct page *page) @@ -1350,8 +1601,7 @@ static int f2fs_read_data_page(struct file *file, struct page *page) if (f2fs_has_inline_data(inode)) ret = f2fs_read_inline_data(inode, page); if (ret == -EAGAIN) - ret = mpage_readpage(page, get_data_block); - + ret = f2fs_mpage_readpages(page->mapping, NULL, page, 1); return ret; } @@ -1365,11 +1615,12 @@ static int f2fs_read_data_pages(struct file *file, if (f2fs_has_inline_data(inode)) return 0; - return mpage_readpages(mapping, pages, nr_pages, get_data_block); + return f2fs_mpage_readpages(mapping, pages, NULL, nr_pages); } -int do_write_data_page(struct page *page, struct f2fs_io_info *fio) +int do_write_data_page(struct f2fs_io_info *fio) { + struct page *page = fio->page; struct inode *inode = page->mapping->host; struct dnode_of_data dn; int err = 0; @@ -1387,6 +1638,14 @@ int do_write_data_page(struct page *page, struct f2fs_io_info *fio) goto out_writepage; } + if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) { + fio->encrypted_page = f2fs_encrypt(inode, fio->page); + if (IS_ERR(fio->encrypted_page)) { + err = PTR_ERR(fio->encrypted_page); + goto out_writepage; + } + } + set_page_writeback(page); /* @@ -1396,11 +1655,11 @@ int do_write_data_page(struct page *page, struct f2fs_io_info *fio) if (unlikely(fio->blk_addr != NEW_ADDR && !is_cold_data(page) && need_inplace_update(inode))) { - rewrite_data_page(page, fio); + rewrite_data_page(fio); set_inode_flag(F2FS_I(inode), FI_UPDATE_WRITE); trace_f2fs_do_write_data_page(page, IPU); } else { - write_data_page(page, &dn, fio); + write_data_page(&dn, fio); set_data_blkaddr(&dn); f2fs_update_extent_cache(&dn); trace_f2fs_do_write_data_page(page, OPU); @@ -1425,8 +1684,11 @@ static int f2fs_write_data_page(struct page *page, bool need_balance_fs = false; int err = 0; struct f2fs_io_info fio = { + .sbi = sbi, .type = DATA, .rw = (wbc->sync_mode == WB_SYNC_ALL) ? WRITE_SYNC : WRITE, + .page = page, + .encrypted_page = NULL, }; trace_f2fs_writepage(page, DATA); @@ -1456,7 +1718,7 @@ write: if (S_ISDIR(inode->i_mode)) { if (unlikely(f2fs_cp_error(sbi))) goto redirty_out; - err = do_write_data_page(page, &fio); + err = do_write_data_page(&fio); goto done; } @@ -1476,7 +1738,7 @@ write: if (f2fs_has_inline_data(inode)) err = f2fs_write_inline_data(inode, page); if (err == -EAGAIN) - err = do_write_data_page(page, &fio); + err = do_write_data_page(&fio); f2fs_unlock_op(sbi); done: if (err && err != -ENOENT) @@ -1645,11 +1907,14 @@ put_next: zero_user_segment(page, 0, PAGE_CACHE_SIZE); } else { struct f2fs_io_info fio = { + .sbi = sbi, .type = DATA, .rw = READ_SYNC, .blk_addr = dn.data_blkaddr, + .page = page, + .encrypted_page = NULL, }; - err = f2fs_submit_page_bio(sbi, page, &fio); + err = f2fs_submit_page_bio(&fio); if (err) goto fail; @@ -1663,6 +1928,15 @@ put_next: f2fs_put_page(page, 1); goto repeat; } + + /* avoid symlink page */ + if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) { + err = f2fs_decrypt_one(inode, page); + if (err) { + f2fs_put_page(page, 1); + goto fail; + } + } } out: SetPageUptodate(page); @@ -1733,6 +2007,9 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter, return err; } + if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + return 0; + if (check_direct_IO(inode, iter, offset)) return 0; @@ -1795,8 +2072,6 @@ static int f2fs_set_data_page_dirty(struct page *page) return 1; } - mark_inode_dirty(inode); - if (!PageDirty(page)) { __set_page_dirty_nobuffers(page); update_dirty_page(inode, page); diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index f5388f372..75176e0dd 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -94,7 +94,8 @@ static void update_general_status(struct f2fs_sb_info *sbi) static void update_sit_info(struct f2fs_sb_info *sbi) { struct f2fs_stat_info *si = F2FS_STAT(sbi); - unsigned int blks_per_sec, hblks_per_sec, total_vblocks, bimodal, dist; + unsigned long long blks_per_sec, hblks_per_sec, total_vblocks; + unsigned long long bimodal, dist; unsigned int segno, vblocks; int ndirty = 0; @@ -112,10 +113,10 @@ static void update_sit_info(struct f2fs_sb_info *sbi) ndirty++; } } - dist = MAIN_SECS(sbi) * hblks_per_sec * hblks_per_sec / 100; - si->bimodal = bimodal / dist; + dist = div_u64(MAIN_SECS(sbi) * hblks_per_sec * hblks_per_sec, 100); + si->bimodal = div_u64(bimodal, dist); if (si->dirty_count) - si->avg_vblocks = total_vblocks / ndirty; + si->avg_vblocks = div_u64(total_vblocks, ndirty); else si->avg_vblocks = 0; } @@ -143,7 +144,7 @@ static void update_mem_info(struct f2fs_sb_info *sbi) si->base_mem += sizeof(struct sit_info); si->base_mem += MAIN_SEGS(sbi) * sizeof(struct seg_entry); si->base_mem += f2fs_bitmap_size(MAIN_SEGS(sbi)); - si->base_mem += 2 * SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); + si->base_mem += 3 * SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); si->base_mem += SIT_VBLOCK_MAP_SIZE; if (sbi->segs_per_sec > 1) si->base_mem += MAIN_SECS(sbi) * sizeof(struct sec_entry); diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 3a3302ab7..a34ebd831 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -76,20 +76,10 @@ static unsigned long dir_block_index(unsigned int level, return bidx; } -static bool early_match_name(size_t namelen, f2fs_hash_t namehash, - struct f2fs_dir_entry *de) -{ - if (le16_to_cpu(de->name_len) != namelen) - return false; - - if (de->hash_code != namehash) - return false; - - return true; -} - static struct f2fs_dir_entry *find_in_block(struct page *dentry_page, - struct qstr *name, int *max_slots, + struct f2fs_filename *fname, + f2fs_hash_t namehash, + int *max_slots, struct page **res_page) { struct f2fs_dentry_block *dentry_blk; @@ -98,9 +88,8 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page, dentry_blk = (struct f2fs_dentry_block *)kmap(dentry_page); - make_dentry_ptr(&d, (void *)dentry_blk, 1); - de = find_target_dentry(name, max_slots, &d); - + make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1); + de = find_target_dentry(fname, namehash, max_slots, &d); if (de) *res_page = dentry_page; else @@ -114,13 +103,15 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page, return de; } -struct f2fs_dir_entry *find_target_dentry(struct qstr *name, int *max_slots, - struct f2fs_dentry_ptr *d) +struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *fname, + f2fs_hash_t namehash, int *max_slots, + struct f2fs_dentry_ptr *d) { struct f2fs_dir_entry *de; unsigned long bit_pos = 0; - f2fs_hash_t namehash = f2fs_dentry_hash(name); int max_len = 0; + struct f2fs_str de_name = FSTR_INIT(NULL, 0); + struct f2fs_str *name = &fname->disk_name; if (max_slots) *max_slots = 0; @@ -132,8 +123,18 @@ struct f2fs_dir_entry *find_target_dentry(struct qstr *name, int *max_slots, } de = &d->dentry[bit_pos]; - if (early_match_name(name->len, namehash, de) && - !memcmp(d->filename[bit_pos], name->name, name->len)) + + /* encrypted case */ + de_name.name = d->filename[bit_pos]; + de_name.len = le16_to_cpu(de->name_len); + + /* show encrypted name */ + if (fname->hash) { + if (de->hash_code == fname->hash) + goto found; + } else if (de_name.len == name->len && + de->hash_code == namehash && + !memcmp(de_name.name, name->name, name->len)) goto found; if (max_slots && max_len > *max_slots) @@ -155,16 +156,21 @@ found: } static struct f2fs_dir_entry *find_in_level(struct inode *dir, - unsigned int level, struct qstr *name, - f2fs_hash_t namehash, struct page **res_page) + unsigned int level, + struct f2fs_filename *fname, + struct page **res_page) { - int s = GET_DENTRY_SLOTS(name->len); + struct qstr name = FSTR_TO_QSTR(&fname->disk_name); + int s = GET_DENTRY_SLOTS(name.len); unsigned int nbucket, nblock; unsigned int bidx, end_block; struct page *dentry_page; struct f2fs_dir_entry *de = NULL; bool room = false; int max_slots; + f2fs_hash_t namehash; + + namehash = f2fs_dentry_hash(&name); f2fs_bug_on(F2FS_I_SB(dir), level > MAX_DIR_HASH_DEPTH); @@ -177,13 +183,14 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir, for (; bidx < end_block; bidx++) { /* no need to allocate new dentry pages to all the indices */ - dentry_page = find_data_page(dir, bidx, true); + dentry_page = find_data_page(dir, bidx); if (IS_ERR(dentry_page)) { room = true; continue; } - de = find_in_block(dentry_page, name, &max_slots, res_page); + de = find_in_block(dentry_page, fname, namehash, &max_slots, + res_page); if (de) break; @@ -211,30 +218,34 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, { unsigned long npages = dir_blocks(dir); struct f2fs_dir_entry *de = NULL; - f2fs_hash_t name_hash; unsigned int max_depth; unsigned int level; + struct f2fs_filename fname; + int err; *res_page = NULL; - if (f2fs_has_inline_dentry(dir)) - return find_in_inline_dir(dir, child, res_page); + err = f2fs_fname_setup_filename(dir, child, 1, &fname); + if (err) + return NULL; + + if (f2fs_has_inline_dentry(dir)) { + de = find_in_inline_dir(dir, &fname, res_page); + goto out; + } if (npages == 0) - return NULL; + goto out; - name_hash = f2fs_dentry_hash(child); max_depth = F2FS_I(dir)->i_current_depth; for (level = 0; level < max_depth; level++) { - de = find_in_level(dir, level, child, name_hash, res_page); + de = find_in_level(dir, level, &fname, res_page); if (de) break; } - if (!de && F2FS_I(dir)->chash != name_hash) { - F2FS_I(dir)->chash = name_hash; - F2FS_I(dir)->clevel = level - 1; - } +out: + f2fs_fname_free_filename(&fname); return de; } @@ -303,10 +314,14 @@ static void init_dent_inode(const struct qstr *name, struct page *ipage) set_page_dirty(ipage); } -int update_dent_inode(struct inode *inode, const struct qstr *name) +int update_dent_inode(struct inode *inode, struct inode *to, + const struct qstr *name) { struct page *page; + if (file_enc_name(to)) + return 0; + page = get_node_page(F2FS_I_SB(inode), inode->i_ino); if (IS_ERR(page)) return PTR_ERR(page); @@ -356,7 +371,7 @@ static int make_empty_dir(struct inode *inode, dentry_blk = kmap_atomic(dentry_page); - make_dentry_ptr(&d, (void *)dentry_blk, 1); + make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1); do_make_empty_dir(inode, parent, &d); kunmap_atomic(dentry_blk); @@ -390,6 +405,12 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir, err = f2fs_init_security(inode, dir, name, page); if (err) goto put_error; + + if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode)) { + err = f2fs_inherit_context(dir, inode, page); + if (err) + goto put_error; + } } else { page = get_node_page(F2FS_I_SB(dir), inode->i_ino); if (IS_ERR(page)) @@ -501,24 +522,33 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, unsigned long bidx, block; f2fs_hash_t dentry_hash; unsigned int nbucket, nblock; - size_t namelen = name->len; struct page *dentry_page = NULL; struct f2fs_dentry_block *dentry_blk = NULL; struct f2fs_dentry_ptr d; - int slots = GET_DENTRY_SLOTS(namelen); struct page *page = NULL; - int err = 0; + struct f2fs_filename fname; + struct qstr new_name; + int slots, err; + + err = f2fs_fname_setup_filename(dir, name, 0, &fname); + if (err) + return err; + + new_name.name = fname_name(&fname); + new_name.len = fname_len(&fname); if (f2fs_has_inline_dentry(dir)) { - err = f2fs_add_inline_entry(dir, name, inode, ino, mode); + err = f2fs_add_inline_entry(dir, &new_name, inode, ino, mode); if (!err || err != -EAGAIN) - return err; + goto out; else err = 0; } - dentry_hash = f2fs_dentry_hash(name); level = 0; + slots = GET_DENTRY_SLOTS(new_name.len); + dentry_hash = f2fs_dentry_hash(&new_name); + current_depth = F2FS_I(dir)->i_current_depth; if (F2FS_I(dir)->chash == dentry_hash) { level = F2FS_I(dir)->clevel; @@ -526,8 +556,10 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, } start: - if (unlikely(current_depth == MAX_DIR_HASH_DEPTH)) - return -ENOSPC; + if (unlikely(current_depth == MAX_DIR_HASH_DEPTH)) { + err = -ENOSPC; + goto out; + } /* Increase the depth, if required */ if (level == current_depth) @@ -541,8 +573,10 @@ start: for (block = bidx; block <= (bidx + nblock - 1); block++) { dentry_page = get_new_data_page(dir, NULL, block, true); - if (IS_ERR(dentry_page)) - return PTR_ERR(dentry_page); + if (IS_ERR(dentry_page)) { + err = PTR_ERR(dentry_page); + goto out; + } dentry_blk = kmap(dentry_page); bit_pos = room_for_filename(&dentry_blk->dentry_bitmap, @@ -562,15 +596,17 @@ add_dentry: if (inode) { down_write(&F2FS_I(inode)->i_sem); - page = init_inode_metadata(inode, dir, name, NULL); + page = init_inode_metadata(inode, dir, &new_name, NULL); if (IS_ERR(page)) { err = PTR_ERR(page); goto fail; } + if (f2fs_encrypted_inode(dir)) + file_set_enc_name(inode); } - make_dentry_ptr(&d, (void *)dentry_blk, 1); - f2fs_update_dentry(ino, mode, &d, name, dentry_hash, bit_pos); + make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1); + f2fs_update_dentry(ino, mode, &d, &new_name, dentry_hash, bit_pos); set_page_dirty(dentry_page); @@ -592,6 +628,8 @@ fail: } kunmap(dentry_page); f2fs_put_page(dentry_page, 1); +out: + f2fs_fname_free_filename(&fname); return err; } @@ -729,11 +767,12 @@ bool f2fs_empty_dir(struct inode *dir) } bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, - unsigned int start_pos) + unsigned int start_pos, struct f2fs_str *fstr) { unsigned char d_type = DT_UNKNOWN; unsigned int bit_pos; struct f2fs_dir_entry *de = NULL; + struct f2fs_str de_name = FSTR_INIT(NULL, 0); bit_pos = ((unsigned long)ctx->pos % d->max); @@ -747,8 +786,24 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d, d_type = f2fs_filetype_table[de->file_type]; else d_type = DT_UNKNOWN; - if (!dir_emit(ctx, d->filename[bit_pos], - le16_to_cpu(de->name_len), + + /* encrypted case */ + de_name.name = d->filename[bit_pos]; + de_name.len = le16_to_cpu(de->name_len); + + if (f2fs_encrypted_inode(d->inode)) { + int save_len = fstr->len; + int ret; + + ret = f2fs_fname_disk_to_usr(d->inode, &de->hash_code, + &de_name, fstr); + de_name = *fstr; + fstr->len = save_len; + if (ret < 0) + return true; + } + + if (!dir_emit(ctx, de_name.name, de_name.len, le32_to_cpu(de->ino), d_type)) return true; @@ -767,9 +822,24 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) struct file_ra_state *ra = &file->f_ra; unsigned int n = ((unsigned long)ctx->pos / NR_DENTRY_IN_BLOCK); struct f2fs_dentry_ptr d; + struct f2fs_str fstr = FSTR_INIT(NULL, 0); + int err = 0; - if (f2fs_has_inline_dentry(inode)) - return f2fs_read_inline_dir(file, ctx); + if (f2fs_encrypted_inode(inode)) { + err = f2fs_get_encryption_info(inode); + if (err) + return err; + + err = f2fs_fname_crypto_alloc_buffer(inode, F2FS_NAME_LEN, + &fstr); + if (err < 0) + return err; + } + + if (f2fs_has_inline_dentry(inode)) { + err = f2fs_read_inline_dir(file, ctx, &fstr); + goto out; + } /* readahead for multi pages of dir */ if (npages - n > 1 && !ra_has_index(ra, n)) @@ -783,9 +853,9 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) dentry_blk = kmap(dentry_page); - make_dentry_ptr(&d, (void *)dentry_blk, 1); + make_dentry_ptr(inode, &d, (void *)dentry_blk, 1); - if (f2fs_fill_dentries(ctx, &d, n * NR_DENTRY_IN_BLOCK)) + if (f2fs_fill_dentries(ctx, &d, n * NR_DENTRY_IN_BLOCK, &fstr)) goto stop; ctx->pos = (n + 1) * NR_DENTRY_IN_BLOCK; @@ -798,8 +868,9 @@ stop: kunmap(dentry_page); f2fs_put_page(dentry_page, 1); } - - return 0; +out: + f2fs_fname_crypto_free_buffer(&fstr); + return err; } const struct file_operations f2fs_dir_operations = { @@ -808,4 +879,7 @@ const struct file_operations f2fs_dir_operations = { .iterate = f2fs_readdir, .fsync = f2fs_sync_file, .unlocked_ioctl = f2fs_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = f2fs_compat_ioctl, +#endif }; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8de34ab6d..a8327ed73 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -70,6 +70,15 @@ struct f2fs_mount_info { unsigned int opt; }; +#define F2FS_FEATURE_ENCRYPT 0x0001 + +#define F2FS_HAS_FEATURE(sb, mask) \ + ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) +#define F2FS_SET_FEATURE(sb, mask) \ + F2FS_SB(sb)->raw_super->feature |= cpu_to_le32(mask) +#define F2FS_CLEAR_FEATURE(sb, mask) \ + F2FS_SB(sb)->raw_super->feature &= ~cpu_to_le32(mask) + #define CRCPOLY_LE 0xedb88320 static inline __u32 f2fs_crc32(void *buf, size_t len) @@ -110,6 +119,8 @@ enum { #define DEF_BATCHED_TRIM_SECTIONS 32 #define BATCHED_TRIM_SEGMENTS(sbi) \ (SM_I(sbi)->trim_sections * (sbi)->segs_per_sec) +#define BATCHED_TRIM_BLOCKS(sbi) \ + (BATCHED_TRIM_SEGMENTS(sbi) << (sbi)->log_blocks_per_seg) struct cp_control { int reason; @@ -218,6 +229,13 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size, #define F2FS_IOC_RELEASE_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 4) #define F2FS_IOC_ABORT_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 5) +#define F2FS_IOC_SET_ENCRYPTION_POLICY \ + _IOR('f', 19, struct f2fs_encryption_policy) +#define F2FS_IOC_GET_ENCRYPTION_PWSALT \ + _IOW('f', 20, __u8[16]) +#define F2FS_IOC_GET_ENCRYPTION_POLICY \ + _IOW('f', 21, struct f2fs_encryption_policy) + /* * should be same as XFS_IOC_GOINGDOWN. * Flags for going down operation used by FS_IOC_GOINGDOWN @@ -239,16 +257,38 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size, * For INODE and NODE manager */ /* for directory operations */ +struct f2fs_str { + unsigned char *name; + u32 len; +}; + +struct f2fs_filename { + const struct qstr *usr_fname; + struct f2fs_str disk_name; + f2fs_hash_t hash; +#ifdef CONFIG_F2FS_FS_ENCRYPTION + struct f2fs_str crypto_buf; +#endif +}; + +#define FSTR_INIT(n, l) { .name = n, .len = l } +#define FSTR_TO_QSTR(f) QSTR_INIT((f)->name, (f)->len) +#define fname_name(p) ((p)->disk_name.name) +#define fname_len(p) ((p)->disk_name.len) + struct f2fs_dentry_ptr { + struct inode *inode; const void *bitmap; struct f2fs_dir_entry *dentry; __u8 (*filename)[F2FS_SLOT_LEN]; int max; }; -static inline void make_dentry_ptr(struct f2fs_dentry_ptr *d, - void *src, int type) +static inline void make_dentry_ptr(struct inode *inode, + struct f2fs_dentry_ptr *d, void *src, int type) { + d->inode = inode; + if (type == 1) { struct f2fs_dentry_block *t = (struct f2fs_dentry_block *)src; d->max = NR_DENTRY_IN_BLOCK; @@ -315,10 +355,51 @@ struct extent_tree { }; /* + * This structure is taken from ext4_map_blocks. + * + * Note that, however, f2fs uses NEW and MAPPED flags for f2fs_map_blocks(). + */ +#define F2FS_MAP_NEW (1 << BH_New) +#define F2FS_MAP_MAPPED (1 << BH_Mapped) +#define F2FS_MAP_UNWRITTEN (1 << BH_Unwritten) +#define F2FS_MAP_FLAGS (F2FS_MAP_NEW | F2FS_MAP_MAPPED |\ + F2FS_MAP_UNWRITTEN) + +struct f2fs_map_blocks { + block_t m_pblk; + block_t m_lblk; + unsigned int m_len; + unsigned int m_flags; +}; + +/* * i_advise uses FADVISE_XXX_BIT. We can add additional hints later. */ #define FADVISE_COLD_BIT 0x01 #define FADVISE_LOST_PINO_BIT 0x02 +#define FADVISE_ENCRYPT_BIT 0x04 +#define FADVISE_ENC_NAME_BIT 0x08 + +#define file_is_cold(inode) is_file(inode, FADVISE_COLD_BIT) +#define file_wrong_pino(inode) is_file(inode, FADVISE_LOST_PINO_BIT) +#define file_set_cold(inode) set_file(inode, FADVISE_COLD_BIT) +#define file_lost_pino(inode) set_file(inode, FADVISE_LOST_PINO_BIT) +#define file_clear_cold(inode) clear_file(inode, FADVISE_COLD_BIT) +#define file_got_pino(inode) clear_file(inode, FADVISE_LOST_PINO_BIT) +#define file_is_encrypt(inode) is_file(inode, FADVISE_ENCRYPT_BIT) +#define file_set_encrypt(inode) set_file(inode, FADVISE_ENCRYPT_BIT) +#define file_clear_encrypt(inode) clear_file(inode, FADVISE_ENCRYPT_BIT) +#define file_enc_name(inode) is_file(inode, FADVISE_ENC_NAME_BIT) +#define file_set_enc_name(inode) set_file(inode, FADVISE_ENC_NAME_BIT) + +/* Encryption algorithms */ +#define F2FS_ENCRYPTION_MODE_INVALID 0 +#define F2FS_ENCRYPTION_MODE_AES_256_XTS 1 +#define F2FS_ENCRYPTION_MODE_AES_256_GCM 2 +#define F2FS_ENCRYPTION_MODE_AES_256_CBC 3 +#define F2FS_ENCRYPTION_MODE_AES_256_CTS 4 + +#include "f2fs_crypto.h" #define DEF_DIR_LEVEL 0 @@ -346,6 +427,11 @@ struct f2fs_inode_info { struct radix_tree_root inmem_root; /* radix tree for inmem pages */ struct list_head inmem_pages; /* inmemory pages managed by f2fs */ struct mutex inmem_lock; /* lock for inmemory pages */ + +#ifdef CONFIG_F2FS_FS_ENCRYPTION + /* Encryption params */ + struct f2fs_crypt_info *i_crypt_info; +#endif }; static inline void get_extent_info(struct extent_info *ext, @@ -571,9 +657,12 @@ enum page_type { }; struct f2fs_io_info { + struct f2fs_sb_info *sbi; /* f2fs_sb_info pointer */ enum page_type type; /* contains DATA/NODE/META/META_FLUSH */ int rw; /* contains R/RS/W/WS with REQ_META/REQ_PRIO */ block_t blk_addr; /* block address to be written */ + struct page *page; /* page to be written */ + struct page *encrypted_page; /* encrypted page */ }; #define is_read_io(rw) (((rw) & 1) == READ) @@ -666,6 +755,7 @@ struct f2fs_sb_info { block_t user_block_count; /* # of user blocks */ block_t total_valid_block_count; /* # of valid blocks */ block_t alloc_valid_block_count; /* # of allocated blocks */ + block_t discard_blks; /* discard command candidats */ block_t last_valid_block_count; /* for recovery */ u32 s_next_generation; /* for NFS support */ atomic_t nr_pages[NR_COUNT_TYPE]; /* # of pages, see count_type */ @@ -1193,6 +1283,24 @@ static inline int f2fs_test_bit(unsigned int nr, char *addr) return mask & *addr; } +static inline void f2fs_set_bit(unsigned int nr, char *addr) +{ + int mask; + + addr += (nr >> 3); + mask = 1 << (7 - (nr & 0x07)); + *addr |= mask; +} + +static inline void f2fs_clear_bit(unsigned int nr, char *addr) +{ + int mask; + + addr += (nr >> 3); + mask = 1 << (7 - (nr & 0x07)); + *addr &= ~mask; +} + static inline int f2fs_test_and_set_bit(unsigned int nr, char *addr) { int mask; @@ -1391,6 +1499,21 @@ static inline void f2fs_dentry_kunmap(struct inode *dir, struct page *page) kunmap(page); } +static inline int is_file(struct inode *inode, int type) +{ + return F2FS_I(inode)->i_advise & type; +} + +static inline void set_file(struct inode *inode, int type) +{ + F2FS_I(inode)->i_advise |= type; +} + +static inline void clear_file(struct inode *inode, int type) +{ + F2FS_I(inode)->i_advise &= ~type; +} + static inline int f2fs_readonly(struct super_block *sb) { return sb->s_flags & MS_RDONLY; @@ -1407,6 +1530,17 @@ static inline void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi) sbi->sb->s_flags |= MS_RDONLY; } +static inline bool is_dot_dotdot(const struct qstr *str) +{ + if (str->len == 1 && str->name[0] == '.') + return true; + + if (str->len == 2 && str->name[0] == '.' && str->name[1] == '.') + return true; + + return false; +} + #define get_inode_mode(i) \ ((is_inode_flag_set(F2FS_I(i), FI_ACL_MODE)) ? \ (F2FS_I(i)->i_acl_mode) : ((i)->i_mode)) @@ -1453,10 +1587,11 @@ struct dentry *f2fs_get_parent(struct dentry *child); */ extern unsigned char f2fs_filetype_table[F2FS_FT_MAX]; void set_de_type(struct f2fs_dir_entry *, umode_t); -struct f2fs_dir_entry *find_target_dentry(struct qstr *, int *, - struct f2fs_dentry_ptr *); + +struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *, + f2fs_hash_t, int *, struct f2fs_dentry_ptr *); bool f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *, - unsigned int); + unsigned int, struct f2fs_str *); void do_make_empty_dir(struct inode *, struct inode *, struct f2fs_dentry_ptr *); struct page *init_inode_metadata(struct inode *, struct inode *, @@ -1470,7 +1605,7 @@ struct f2fs_dir_entry *f2fs_parent_dir(struct inode *, struct page **); ino_t f2fs_inode_by_name(struct inode *, struct qstr *); void f2fs_set_link(struct inode *, struct f2fs_dir_entry *, struct page *, struct inode *); -int update_dent_inode(struct inode *, const struct qstr *); +int update_dent_inode(struct inode *, struct inode *, const struct qstr *); void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *, const struct qstr *, f2fs_hash_t , unsigned int); int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *, nid_t, @@ -1478,7 +1613,6 @@ int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *, nid_t, void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *, struct inode *); int f2fs_do_tmpfile(struct inode *, struct inode *); -int f2fs_make_empty(struct inode *, struct inode *); bool f2fs_empty_dir(struct inode *); static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) @@ -1490,6 +1624,7 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) /* * super.c */ +int f2fs_commit_super(struct f2fs_sb_info *, bool); int f2fs_sync_fs(struct super_block *, int); extern __printf(3, 4) void f2fs_msg(struct super_block *, const char *, const char *, ...); @@ -1506,8 +1641,8 @@ struct dnode_of_data; struct node_info; bool available_free_memory(struct f2fs_sb_info *, int); +int need_dentry_mark(struct f2fs_sb_info *, nid_t); bool is_checkpointed_node(struct f2fs_sb_info *, nid_t); -bool has_fsynced_inode(struct f2fs_sb_info *, nid_t); bool need_inode_block_update(struct f2fs_sb_info *, nid_t); void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *); int get_dnode_of_data(struct dnode_of_data *, pgoff_t, int); @@ -1548,21 +1683,20 @@ int create_flush_cmd_control(struct f2fs_sb_info *); void destroy_flush_cmd_control(struct f2fs_sb_info *); void invalidate_blocks(struct f2fs_sb_info *, block_t); void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t); -void clear_prefree_segments(struct f2fs_sb_info *); +void clear_prefree_segments(struct f2fs_sb_info *, struct cp_control *); void release_discard_addrs(struct f2fs_sb_info *); void discard_next_dnode(struct f2fs_sb_info *, block_t); int npages_for_summary_flush(struct f2fs_sb_info *, bool); void allocate_new_segments(struct f2fs_sb_info *); int f2fs_trim_fs(struct f2fs_sb_info *, struct fstrim_range *); struct page *get_sum_page(struct f2fs_sb_info *, unsigned int); +void update_meta_page(struct f2fs_sb_info *, void *, block_t); void write_meta_page(struct f2fs_sb_info *, struct page *); -void write_node_page(struct f2fs_sb_info *, struct page *, - unsigned int, struct f2fs_io_info *); -void write_data_page(struct page *, struct dnode_of_data *, - struct f2fs_io_info *); -void rewrite_data_page(struct page *, struct f2fs_io_info *); -void recover_data_page(struct f2fs_sb_info *, struct page *, - struct f2fs_summary *, block_t, block_t); +void write_node_page(unsigned int, struct f2fs_io_info *); +void write_data_page(struct dnode_of_data *, struct f2fs_io_info *); +void rewrite_data_page(struct f2fs_io_info *); +void f2fs_replace_block(struct f2fs_sb_info *, struct dnode_of_data *, + block_t, block_t, unsigned char, bool); void allocate_data_block(struct f2fs_sb_info *, struct page *, block_t, block_t *, struct f2fs_summary *, int); void f2fs_wait_on_page_writeback(struct page *, enum page_type); @@ -1581,6 +1715,7 @@ void destroy_segment_manager_caches(void); */ struct page *grab_meta_page(struct f2fs_sb_info *, pgoff_t); struct page *get_meta_page(struct f2fs_sb_info *, pgoff_t); +bool is_valid_blkaddr(struct f2fs_sb_info *, block_t, int); int ra_meta_pages(struct f2fs_sb_info *, block_t, int, int); void ra_meta_pages_cond(struct f2fs_sb_info *, pgoff_t); long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long); @@ -1607,10 +1742,8 @@ void destroy_checkpoint_caches(void); * data.c */ void f2fs_submit_merged_bio(struct f2fs_sb_info *, enum page_type, int); -int f2fs_submit_page_bio(struct f2fs_sb_info *, struct page *, - struct f2fs_io_info *); -void f2fs_submit_page_mbio(struct f2fs_sb_info *, struct page *, - struct f2fs_io_info *); +int f2fs_submit_page_bio(struct f2fs_io_info *); +void f2fs_submit_page_mbio(struct f2fs_io_info *); void set_data_blkaddr(struct dnode_of_data *); int reserve_new_block(struct dnode_of_data *); int f2fs_reserve_block(struct dnode_of_data *, pgoff_t); @@ -1619,10 +1752,11 @@ void f2fs_destroy_extent_tree(struct inode *); void f2fs_init_extent_cache(struct inode *, struct f2fs_extent *); void f2fs_update_extent_cache(struct dnode_of_data *); void f2fs_preserve_extent_tree(struct inode *); -struct page *find_data_page(struct inode *, pgoff_t, bool); +struct page *get_read_data_page(struct inode *, pgoff_t, int); +struct page *find_data_page(struct inode *, pgoff_t); struct page *get_lock_data_page(struct inode *, pgoff_t); struct page *get_new_data_page(struct inode *, struct page *, pgoff_t, bool); -int do_write_data_page(struct page *, struct f2fs_io_info *); +int do_write_data_page(struct f2fs_io_info *); int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *, u64, u64); void init_extent_cache_info(struct f2fs_sb_info *); int __init create_extent_cache(void); @@ -1787,13 +1921,15 @@ extern const struct address_space_operations f2fs_node_aops; extern const struct address_space_operations f2fs_meta_aops; extern const struct inode_operations f2fs_dir_inode_operations; extern const struct inode_operations f2fs_symlink_inode_operations; +extern const struct inode_operations f2fs_encrypted_symlink_inode_operations; extern const struct inode_operations f2fs_special_inode_operations; extern struct kmem_cache *inode_entry_slab; /* * inline.c */ -bool f2fs_may_inline(struct inode *); +bool f2fs_may_inline_data(struct inode *); +bool f2fs_may_inline_dentry(struct inode *); void read_inline_data(struct page *, struct page *); bool truncate_inline_inode(struct page *, u64); int f2fs_read_inline_data(struct inode *, struct page *); @@ -1801,8 +1937,8 @@ int f2fs_convert_inline_page(struct dnode_of_data *, struct page *); int f2fs_convert_inline_inode(struct inode *); int f2fs_write_inline_data(struct inode *, struct page *); bool recover_inline_data(struct inode *, struct page *); -struct f2fs_dir_entry *find_in_inline_dir(struct inode *, struct qstr *, - struct page **); +struct f2fs_dir_entry *find_in_inline_dir(struct inode *, + struct f2fs_filename *, struct page **); struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *, struct page **); int make_empty_inline_dir(struct inode *inode, struct inode *, struct page *); int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *, @@ -1810,5 +1946,137 @@ int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *, void f2fs_delete_inline_entry(struct f2fs_dir_entry *, struct page *, struct inode *, struct inode *); bool f2fs_empty_inline_dir(struct inode *); -int f2fs_read_inline_dir(struct file *, struct dir_context *); +int f2fs_read_inline_dir(struct file *, struct dir_context *, + struct f2fs_str *); + +/* + * crypto support + */ +static inline int f2fs_encrypted_inode(struct inode *inode) +{ +#ifdef CONFIG_F2FS_FS_ENCRYPTION + return file_is_encrypt(inode); +#else + return 0; +#endif +} + +static inline void f2fs_set_encrypted_inode(struct inode *inode) +{ +#ifdef CONFIG_F2FS_FS_ENCRYPTION + file_set_encrypt(inode); +#endif +} + +static inline bool f2fs_bio_encrypted(struct bio *bio) +{ +#ifdef CONFIG_F2FS_FS_ENCRYPTION + return unlikely(bio->bi_private != NULL); +#else + return false; +#endif +} + +static inline int f2fs_sb_has_crypto(struct super_block *sb) +{ +#ifdef CONFIG_F2FS_FS_ENCRYPTION + return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_ENCRYPT); +#else + return 0; +#endif +} + +static inline bool f2fs_may_encrypt(struct inode *inode) +{ +#ifdef CONFIG_F2FS_FS_ENCRYPTION + mode_t mode = inode->i_mode; + + return (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)); +#else + return 0; +#endif +} + +/* crypto_policy.c */ +int f2fs_is_child_context_consistent_with_parent(struct inode *, + struct inode *); +int f2fs_inherit_context(struct inode *, struct inode *, struct page *); +int f2fs_process_policy(const struct f2fs_encryption_policy *, struct inode *); +int f2fs_get_policy(struct inode *, struct f2fs_encryption_policy *); + +/* crypt.c */ +extern struct kmem_cache *f2fs_crypt_info_cachep; +bool f2fs_valid_contents_enc_mode(uint32_t); +uint32_t f2fs_validate_encryption_key_size(uint32_t, uint32_t); +struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *); +void f2fs_release_crypto_ctx(struct f2fs_crypto_ctx *); +struct page *f2fs_encrypt(struct inode *, struct page *); +int f2fs_decrypt(struct f2fs_crypto_ctx *, struct page *); +int f2fs_decrypt_one(struct inode *, struct page *); +void f2fs_end_io_crypto_work(struct f2fs_crypto_ctx *, struct bio *); + +/* crypto_key.c */ +void f2fs_free_encryption_info(struct inode *, struct f2fs_crypt_info *); +int _f2fs_get_encryption_info(struct inode *inode); + +/* crypto_fname.c */ +bool f2fs_valid_filenames_enc_mode(uint32_t); +u32 f2fs_fname_crypto_round_up(u32, u32); +int f2fs_fname_crypto_alloc_buffer(struct inode *, u32, struct f2fs_str *); +int f2fs_fname_disk_to_usr(struct inode *, f2fs_hash_t *, + const struct f2fs_str *, struct f2fs_str *); +int f2fs_fname_usr_to_disk(struct inode *, const struct qstr *, + struct f2fs_str *); + +#ifdef CONFIG_F2FS_FS_ENCRYPTION +void f2fs_restore_and_release_control_page(struct page **); +void f2fs_restore_control_page(struct page *); + +int __init f2fs_init_crypto(void); +int f2fs_crypto_initialize(void); +void f2fs_exit_crypto(void); + +int f2fs_has_encryption_key(struct inode *); + +static inline int f2fs_get_encryption_info(struct inode *inode) +{ + struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info; + + if (!ci || + (ci->ci_keyring_key && + (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) | + (1 << KEY_FLAG_REVOKED) | + (1 << KEY_FLAG_DEAD))))) + return _f2fs_get_encryption_info(inode); + return 0; +} + +void f2fs_fname_crypto_free_buffer(struct f2fs_str *); +int f2fs_fname_setup_filename(struct inode *, const struct qstr *, + int lookup, struct f2fs_filename *); +void f2fs_fname_free_filename(struct f2fs_filename *); +#else +static inline void f2fs_restore_and_release_control_page(struct page **p) { } +static inline void f2fs_restore_control_page(struct page *p) { } + +static inline int __init f2fs_init_crypto(void) { return 0; } +static inline void f2fs_exit_crypto(void) { } + +static inline int f2fs_has_encryption_key(struct inode *i) { return 0; } +static inline int f2fs_get_encryption_info(struct inode *i) { return 0; } +static inline void f2fs_fname_crypto_free_buffer(struct f2fs_str *p) { } + +static inline int f2fs_fname_setup_filename(struct inode *dir, + const struct qstr *iname, + int lookup, struct f2fs_filename *fname) +{ + memset(fname, 0, sizeof(struct f2fs_filename)); + fname->usr_fname = iname; + fname->disk_name.name = (unsigned char *)iname->name; + fname->disk_name.len = iname->len; + return 0; +} + +static inline void f2fs_fname_free_filename(struct f2fs_filename *fname) { } +#endif #endif diff --git a/fs/f2fs/f2fs_crypto.h b/fs/f2fs/f2fs_crypto.h new file mode 100644 index 000000000..c2c1c2b63 --- /dev/null +++ b/fs/f2fs/f2fs_crypto.h @@ -0,0 +1,151 @@ +/* + * linux/fs/f2fs/f2fs_crypto.h + * + * Copied from linux/fs/ext4/ext4_crypto.h + * + * Copyright (C) 2015, Google, Inc. + * + * This contains encryption header content for f2fs + * + * Written by Michael Halcrow, 2015. + * Modified by Jaegeuk Kim, 2015. + */ +#ifndef _F2FS_CRYPTO_H +#define _F2FS_CRYPTO_H + +#include <linux/fs.h> + +#define F2FS_KEY_DESCRIPTOR_SIZE 8 + +/* Policy provided via an ioctl on the topmost directory */ +struct f2fs_encryption_policy { + char version; + char contents_encryption_mode; + char filenames_encryption_mode; + char flags; + char master_key_descriptor[F2FS_KEY_DESCRIPTOR_SIZE]; +} __attribute__((__packed__)); + +#define F2FS_ENCRYPTION_CONTEXT_FORMAT_V1 1 +#define F2FS_KEY_DERIVATION_NONCE_SIZE 16 + +#define F2FS_POLICY_FLAGS_PAD_4 0x00 +#define F2FS_POLICY_FLAGS_PAD_8 0x01 +#define F2FS_POLICY_FLAGS_PAD_16 0x02 +#define F2FS_POLICY_FLAGS_PAD_32 0x03 +#define F2FS_POLICY_FLAGS_PAD_MASK 0x03 +#define F2FS_POLICY_FLAGS_VALID 0x03 + +/** + * Encryption context for inode + * + * Protector format: + * 1 byte: Protector format (1 = this version) + * 1 byte: File contents encryption mode + * 1 byte: File names encryption mode + * 1 byte: Flags + * 8 bytes: Master Key descriptor + * 16 bytes: Encryption Key derivation nonce + */ +struct f2fs_encryption_context { + char format; + char contents_encryption_mode; + char filenames_encryption_mode; + char flags; + char master_key_descriptor[F2FS_KEY_DESCRIPTOR_SIZE]; + char nonce[F2FS_KEY_DERIVATION_NONCE_SIZE]; +} __attribute__((__packed__)); + +/* Encryption parameters */ +#define F2FS_XTS_TWEAK_SIZE 16 +#define F2FS_AES_128_ECB_KEY_SIZE 16 +#define F2FS_AES_256_GCM_KEY_SIZE 32 +#define F2FS_AES_256_CBC_KEY_SIZE 32 +#define F2FS_AES_256_CTS_KEY_SIZE 32 +#define F2FS_AES_256_XTS_KEY_SIZE 64 +#define F2FS_MAX_KEY_SIZE 64 + +#define F2FS_KEY_DESC_PREFIX "f2fs:" +#define F2FS_KEY_DESC_PREFIX_SIZE 5 + +struct f2fs_encryption_key { + __u32 mode; + char raw[F2FS_MAX_KEY_SIZE]; + __u32 size; +} __attribute__((__packed__)); + +struct f2fs_crypt_info { + char ci_data_mode; + char ci_filename_mode; + char ci_flags; + struct crypto_ablkcipher *ci_ctfm; + struct key *ci_keyring_key; + char ci_master_key[F2FS_KEY_DESCRIPTOR_SIZE]; +}; + +#define F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL 0x00000001 +#define F2FS_WRITE_PATH_FL 0x00000002 + +struct f2fs_crypto_ctx { + union { + struct { + struct page *bounce_page; /* Ciphertext page */ + struct page *control_page; /* Original page */ + } w; + struct { + struct bio *bio; + struct work_struct work; + } r; + struct list_head free_list; /* Free list */ + }; + char flags; /* Flags */ +}; + +struct f2fs_completion_result { + struct completion completion; + int res; +}; + +#define DECLARE_F2FS_COMPLETION_RESULT(ecr) \ + struct f2fs_completion_result ecr = { \ + COMPLETION_INITIALIZER((ecr).completion), 0 } + +static inline int f2fs_encryption_key_size(int mode) +{ + switch (mode) { + case F2FS_ENCRYPTION_MODE_AES_256_XTS: + return F2FS_AES_256_XTS_KEY_SIZE; + case F2FS_ENCRYPTION_MODE_AES_256_GCM: + return F2FS_AES_256_GCM_KEY_SIZE; + case F2FS_ENCRYPTION_MODE_AES_256_CBC: + return F2FS_AES_256_CBC_KEY_SIZE; + case F2FS_ENCRYPTION_MODE_AES_256_CTS: + return F2FS_AES_256_CTS_KEY_SIZE; + default: + BUG(); + } + return 0; +} + +#define F2FS_FNAME_NUM_SCATTER_ENTRIES 4 +#define F2FS_CRYPTO_BLOCK_SIZE 16 +#define F2FS_FNAME_CRYPTO_DIGEST_SIZE 32 + +/** + * For encrypted symlinks, the ciphertext length is stored at the beginning + * of the string in little-endian format. + */ +struct f2fs_encrypted_symlink_data { + __le16 len; + char encrypted_path[1]; +} __attribute__((__packed__)); + +/** + * This function is used to calculate the disk space required to + * store a filename of length l in encrypted symlink format. + */ +static inline u32 encrypted_symlink_data_len(u32 l) +{ + return (l + sizeof(struct f2fs_encrypted_symlink_data) - 1); +} +#endif /* _F2FS_CRYPTO_H */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 2b52e48d7..b0f38c3b3 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -20,6 +20,7 @@ #include <linux/uaccess.h> #include <linux/mount.h> #include <linux/pagevec.h> +#include <linux/random.h> #include "f2fs.h" #include "node.h" @@ -105,7 +106,7 @@ static int get_parent_ino(struct inode *inode, nid_t *pino) if (!dentry) return 0; - if (update_dent_inode(inode, &dentry->d_name)) { + if (update_dent_inode(inode, inode, &dentry->d_name)) { dput(dentry); return 0; } @@ -122,6 +123,8 @@ static inline bool need_do_checkpoint(struct inode *inode) if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1) need_cp = true; + else if (file_enc_name(inode) && need_dentry_mark(sbi, inode->i_ino)) + need_cp = true; else if (file_wrong_pino(inode)) need_cp = true; else if (!space_for_roll_forward(sbi)) @@ -271,7 +274,7 @@ flush_out: ret = f2fs_issue_flush(sbi); out: trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); - f2fs_trace_ios(NULL, NULL, 1); + f2fs_trace_ios(NULL, 1); return ret; } @@ -407,6 +410,12 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) { struct inode *inode = file_inode(file); + if (f2fs_encrypted_inode(inode)) { + int err = f2fs_get_encryption_info(inode); + if (err) + return 0; + } + /* we don't need to use inline_data strictly */ if (f2fs_has_inline_data(inode)) { int err = f2fs_convert_inline_inode(inode); @@ -419,6 +428,18 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) return 0; } +static int f2fs_file_open(struct inode *inode, struct file *filp) +{ + int ret = generic_file_open(inode, filp); + + if (!ret && f2fs_encrypted_inode(inode)) { + ret = f2fs_get_encryption_info(inode); + if (ret) + ret = -EACCES; + } + return ret; +} + int truncate_data_blocks_range(struct dnode_of_data *dn, int count) { int nr_free = 0, ofs = dn->ofs_in_node; @@ -461,28 +482,32 @@ void truncate_data_blocks(struct dnode_of_data *dn) } static int truncate_partial_data_page(struct inode *inode, u64 from, - bool force) + bool cache_only) { unsigned offset = from & (PAGE_CACHE_SIZE - 1); + pgoff_t index = from >> PAGE_CACHE_SHIFT; + struct address_space *mapping = inode->i_mapping; struct page *page; - if (!offset && !force) + if (!offset && !cache_only) return 0; - page = find_data_page(inode, from >> PAGE_CACHE_SHIFT, force); - if (IS_ERR(page)) + if (cache_only) { + page = grab_cache_page(mapping, index); + if (page && PageUptodate(page)) + goto truncate_out; + f2fs_put_page(page, 1); return 0; + } - lock_page(page); - if (unlikely(!PageUptodate(page) || - page->mapping != inode->i_mapping)) - goto out; - + page = get_lock_data_page(inode, index); + if (IS_ERR(page)) + return 0; +truncate_out: f2fs_wait_on_page_writeback(page, DATA); zero_user(page, offset, PAGE_CACHE_SIZE - offset); - if (!force) + if (!cache_only || !f2fs_encrypted_inode(inode) || !S_ISREG(inode->i_mode)) set_page_dirty(page); -out: f2fs_put_page(page, 1); return 0; } @@ -560,7 +585,7 @@ void f2fs_truncate(struct inode *inode) trace_f2fs_truncate(inode); /* we should check inline_data size */ - if (f2fs_has_inline_data(inode) && !f2fs_may_inline(inode)) { + if (f2fs_has_inline_data(inode) && !f2fs_may_inline_data(inode)) { if (f2fs_convert_inline_inode(inode)) return; } @@ -622,16 +647,20 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) return err; if (attr->ia_valid & ATTR_SIZE) { - if (attr->ia_size != i_size_read(inode)) { + if (f2fs_encrypted_inode(inode) && + f2fs_get_encryption_info(inode)) + return -EACCES; + + if (attr->ia_size <= i_size_read(inode)) { truncate_setsize(inode, attr->ia_size); f2fs_truncate(inode); f2fs_balance_fs(F2FS_I_SB(inode)); } else { /* - * giving a chance to truncate blocks past EOF which - * are fallocated with FALLOC_FL_KEEP_SIZE. + * do not trim all blocks after i_size if target size is + * larger than i_size. */ - f2fs_truncate(inode); + truncate_setsize(inode, attr->ia_size); } } @@ -718,10 +747,6 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len) if (!S_ISREG(inode->i_mode)) return -EOPNOTSUPP; - /* skip punching hole beyond i_size */ - if (offset >= inode->i_size) - return ret; - if (f2fs_has_inline_data(inode)) { ret = f2fs_convert_inline_inode(inode); if (ret) @@ -765,6 +790,320 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len) return ret; } +static int f2fs_do_collapse(struct inode *inode, pgoff_t start, pgoff_t end) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct dnode_of_data dn; + pgoff_t nrpages = (i_size_read(inode) + PAGE_SIZE - 1) / PAGE_SIZE; + int ret = 0; + + f2fs_lock_op(sbi); + + for (; end < nrpages; start++, end++) { + block_t new_addr, old_addr; + + set_new_dnode(&dn, inode, NULL, NULL, 0); + ret = get_dnode_of_data(&dn, end, LOOKUP_NODE_RA); + if (ret && ret != -ENOENT) { + goto out; + } else if (ret == -ENOENT) { + new_addr = NULL_ADDR; + } else { + new_addr = dn.data_blkaddr; + truncate_data_blocks_range(&dn, 1); + f2fs_put_dnode(&dn); + } + + if (new_addr == NULL_ADDR) { + set_new_dnode(&dn, inode, NULL, NULL, 0); + ret = get_dnode_of_data(&dn, start, LOOKUP_NODE_RA); + if (ret && ret != -ENOENT) + goto out; + else if (ret == -ENOENT) + continue; + + if (dn.data_blkaddr == NULL_ADDR) { + f2fs_put_dnode(&dn); + continue; + } else { + truncate_data_blocks_range(&dn, 1); + } + + f2fs_put_dnode(&dn); + } else { + struct page *ipage; + + ipage = get_node_page(sbi, inode->i_ino); + if (IS_ERR(ipage)) { + ret = PTR_ERR(ipage); + goto out; + } + + set_new_dnode(&dn, inode, ipage, NULL, 0); + ret = f2fs_reserve_block(&dn, start); + if (ret) + goto out; + + old_addr = dn.data_blkaddr; + if (old_addr != NEW_ADDR && new_addr == NEW_ADDR) { + dn.data_blkaddr = NULL_ADDR; + f2fs_update_extent_cache(&dn); + invalidate_blocks(sbi, old_addr); + + dn.data_blkaddr = new_addr; + set_data_blkaddr(&dn); + } else if (new_addr != NEW_ADDR) { + struct node_info ni; + + get_node_info(sbi, dn.nid, &ni); + f2fs_replace_block(sbi, &dn, old_addr, new_addr, + ni.version, true); + } + + f2fs_put_dnode(&dn); + } + } + ret = 0; +out: + f2fs_unlock_op(sbi); + return ret; +} + +static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len) +{ + pgoff_t pg_start, pg_end; + loff_t new_size; + int ret; + + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + + if (offset + len >= i_size_read(inode)) + return -EINVAL; + + /* collapse range should be aligned to block size of f2fs. */ + if (offset & (F2FS_BLKSIZE - 1) || len & (F2FS_BLKSIZE - 1)) + return -EINVAL; + + pg_start = offset >> PAGE_CACHE_SHIFT; + pg_end = (offset + len) >> PAGE_CACHE_SHIFT; + + /* write out all dirty pages from offset */ + ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); + if (ret) + return ret; + + truncate_pagecache(inode, offset); + + ret = f2fs_do_collapse(inode, pg_start, pg_end); + if (ret) + return ret; + + new_size = i_size_read(inode) - len; + + ret = truncate_blocks(inode, new_size, true); + if (!ret) + i_size_write(inode, new_size); + + return ret; +} + +static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, + int mode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct address_space *mapping = inode->i_mapping; + pgoff_t index, pg_start, pg_end; + loff_t new_size = i_size_read(inode); + loff_t off_start, off_end; + int ret = 0; + + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + + ret = inode_newsize_ok(inode, (len + offset)); + if (ret) + return ret; + + f2fs_balance_fs(sbi); + + if (f2fs_has_inline_data(inode)) { + ret = f2fs_convert_inline_inode(inode); + if (ret) + return ret; + } + + ret = filemap_write_and_wait_range(mapping, offset, offset + len - 1); + if (ret) + return ret; + + truncate_pagecache_range(inode, offset, offset + len - 1); + + pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT; + pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT; + + off_start = offset & (PAGE_CACHE_SIZE - 1); + off_end = (offset + len) & (PAGE_CACHE_SIZE - 1); + + if (pg_start == pg_end) { + fill_zero(inode, pg_start, off_start, off_end - off_start); + if (offset + len > new_size) + new_size = offset + len; + new_size = max_t(loff_t, new_size, offset + len); + } else { + if (off_start) { + fill_zero(inode, pg_start++, off_start, + PAGE_CACHE_SIZE - off_start); + new_size = max_t(loff_t, new_size, + pg_start << PAGE_CACHE_SHIFT); + } + + for (index = pg_start; index < pg_end; index++) { + struct dnode_of_data dn; + struct page *ipage; + + f2fs_lock_op(sbi); + + ipage = get_node_page(sbi, inode->i_ino); + if (IS_ERR(ipage)) { + ret = PTR_ERR(ipage); + f2fs_unlock_op(sbi); + goto out; + } + + set_new_dnode(&dn, inode, ipage, NULL, 0); + ret = f2fs_reserve_block(&dn, index); + if (ret) { + f2fs_unlock_op(sbi); + goto out; + } + + if (dn.data_blkaddr != NEW_ADDR) { + invalidate_blocks(sbi, dn.data_blkaddr); + + dn.data_blkaddr = NEW_ADDR; + set_data_blkaddr(&dn); + + dn.data_blkaddr = NULL_ADDR; + f2fs_update_extent_cache(&dn); + } + f2fs_put_dnode(&dn); + f2fs_unlock_op(sbi); + + new_size = max_t(loff_t, new_size, + (index + 1) << PAGE_CACHE_SHIFT); + } + + if (off_end) { + fill_zero(inode, pg_end, 0, off_end); + new_size = max_t(loff_t, new_size, offset + len); + } + } + +out: + if (!(mode & FALLOC_FL_KEEP_SIZE) && i_size_read(inode) < new_size) { + i_size_write(inode, new_size); + mark_inode_dirty(inode); + update_inode_page(inode); + } + + return ret; +} + +static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + pgoff_t pg_start, pg_end, delta, nrpages, idx; + loff_t new_size; + int ret; + + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + + new_size = i_size_read(inode) + len; + if (new_size > inode->i_sb->s_maxbytes) + return -EFBIG; + + if (offset >= i_size_read(inode)) + return -EINVAL; + + /* insert range should be aligned to block size of f2fs. */ + if (offset & (F2FS_BLKSIZE - 1) || len & (F2FS_BLKSIZE - 1)) + return -EINVAL; + + f2fs_balance_fs(sbi); + + ret = truncate_blocks(inode, i_size_read(inode), true); + if (ret) + return ret; + + /* write out all dirty pages from offset */ + ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); + if (ret) + return ret; + + truncate_pagecache(inode, offset); + + pg_start = offset >> PAGE_CACHE_SHIFT; + pg_end = (offset + len) >> PAGE_CACHE_SHIFT; + delta = pg_end - pg_start; + nrpages = (i_size_read(inode) + PAGE_SIZE - 1) / PAGE_SIZE; + + for (idx = nrpages - 1; idx >= pg_start && idx != -1; idx--) { + struct dnode_of_data dn; + struct page *ipage; + block_t new_addr, old_addr; + + f2fs_lock_op(sbi); + + set_new_dnode(&dn, inode, NULL, NULL, 0); + ret = get_dnode_of_data(&dn, idx, LOOKUP_NODE_RA); + if (ret && ret != -ENOENT) { + goto out; + } else if (ret == -ENOENT) { + goto next; + } else if (dn.data_blkaddr == NULL_ADDR) { + f2fs_put_dnode(&dn); + goto next; + } else { + new_addr = dn.data_blkaddr; + truncate_data_blocks_range(&dn, 1); + f2fs_put_dnode(&dn); + } + + ipage = get_node_page(sbi, inode->i_ino); + if (IS_ERR(ipage)) { + ret = PTR_ERR(ipage); + goto out; + } + + set_new_dnode(&dn, inode, ipage, NULL, 0); + ret = f2fs_reserve_block(&dn, idx + delta); + if (ret) + goto out; + + old_addr = dn.data_blkaddr; + f2fs_bug_on(sbi, old_addr != NEW_ADDR); + + if (new_addr != NEW_ADDR) { + struct node_info ni; + + get_node_info(sbi, dn.nid, &ni); + f2fs_replace_block(sbi, &dn, old_addr, new_addr, + ni.version, true); + } + f2fs_put_dnode(&dn); +next: + f2fs_unlock_op(sbi); + } + + i_size_write(inode, new_size); + return 0; +out: + f2fs_unlock_op(sbi); + return ret; +} + static int expand_inode_data(struct inode *inode, loff_t offset, loff_t len, int mode) { @@ -830,23 +1169,40 @@ static long f2fs_fallocate(struct file *file, int mode, loff_t offset, loff_t len) { struct inode *inode = file_inode(file); - long ret; + long ret = 0; + + if (f2fs_encrypted_inode(inode) && + (mode & (FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_INSERT_RANGE))) + return -EOPNOTSUPP; - if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE)) + if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | + FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE | + FALLOC_FL_INSERT_RANGE)) return -EOPNOTSUPP; mutex_lock(&inode->i_mutex); - if (mode & FALLOC_FL_PUNCH_HOLE) + if (mode & FALLOC_FL_PUNCH_HOLE) { + if (offset >= inode->i_size) + goto out; + ret = punch_hole(inode, offset, len); - else + } else if (mode & FALLOC_FL_COLLAPSE_RANGE) { + ret = f2fs_collapse_range(inode, offset, len); + } else if (mode & FALLOC_FL_ZERO_RANGE) { + ret = f2fs_zero_range(inode, offset, len, mode); + } else if (mode & FALLOC_FL_INSERT_RANGE) { + ret = f2fs_insert_range(inode, offset, len); + } else { ret = expand_inode_data(inode, offset, len, mode); + } if (!ret) { inode->i_mtime = inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); } +out: mutex_unlock(&inode->i_mutex); trace_f2fs_fallocate(inode, mode, offset, len, ret); @@ -975,12 +1331,13 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) if (ret) return ret; - if (f2fs_is_atomic_file(inode)) + if (f2fs_is_atomic_file(inode)) { + clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); commit_inmem_pages(inode, false); + } ret = f2fs_sync_file(filp, 0, LONG_MAX, 0); mnt_drop_write_file(filp); - clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); return ret; } @@ -1031,15 +1388,13 @@ static int f2fs_ioc_abort_volatile_write(struct file *filp) f2fs_balance_fs(F2FS_I_SB(inode)); if (f2fs_is_atomic_file(inode)) { - commit_inmem_pages(inode, false); clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); + commit_inmem_pages(inode, false); } - if (f2fs_is_volatile_file(inode)) { + if (f2fs_is_volatile_file(inode)) clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); - filemap_fdatawrite(inode->i_mapping); - set_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); - } + mnt_drop_write_file(filp); return ret; } @@ -1109,6 +1464,86 @@ static int f2fs_ioc_fitrim(struct file *filp, unsigned long arg) return 0; } +static bool uuid_is_nonzero(__u8 u[16]) +{ + int i; + + for (i = 0; i < 16; i++) + if (u[i]) + return true; + return false; +} + +static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg) +{ +#ifdef CONFIG_F2FS_FS_ENCRYPTION + struct f2fs_encryption_policy policy; + struct inode *inode = file_inode(filp); + + if (copy_from_user(&policy, (struct f2fs_encryption_policy __user *)arg, + sizeof(policy))) + return -EFAULT; + + return f2fs_process_policy(&policy, inode); +#else + return -EOPNOTSUPP; +#endif +} + +static int f2fs_ioc_get_encryption_policy(struct file *filp, unsigned long arg) +{ +#ifdef CONFIG_F2FS_FS_ENCRYPTION + struct f2fs_encryption_policy policy; + struct inode *inode = file_inode(filp); + int err; + + err = f2fs_get_policy(inode, &policy); + if (err) + return err; + + if (copy_to_user((struct f2fs_encryption_policy __user *)arg, &policy, + sizeof(policy))) + return -EFAULT; + return 0; +#else + return -EOPNOTSUPP; +#endif +} + +static int f2fs_ioc_get_encryption_pwsalt(struct file *filp, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + int err; + + if (!f2fs_sb_has_crypto(inode->i_sb)) + return -EOPNOTSUPP; + + if (uuid_is_nonzero(sbi->raw_super->encrypt_pw_salt)) + goto got_it; + + err = mnt_want_write_file(filp); + if (err) + return err; + + /* update superblock with uuid */ + generate_random_uuid(sbi->raw_super->encrypt_pw_salt); + + err = f2fs_commit_super(sbi, false); + + mnt_drop_write_file(filp); + if (err) { + /* undo new data */ + memset(sbi->raw_super->encrypt_pw_salt, 0, 16); + return err; + } +got_it: + if (copy_to_user((__u8 __user *)arg, sbi->raw_super->encrypt_pw_salt, + 16)) + return -EFAULT; + return 0; +} + long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { switch (cmd) { @@ -1132,11 +1567,29 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return f2fs_ioc_shutdown(filp, arg); case FITRIM: return f2fs_ioc_fitrim(filp, arg); + case F2FS_IOC_SET_ENCRYPTION_POLICY: + return f2fs_ioc_set_encryption_policy(filp, arg); + case F2FS_IOC_GET_ENCRYPTION_POLICY: + return f2fs_ioc_get_encryption_policy(filp, arg); + case F2FS_IOC_GET_ENCRYPTION_PWSALT: + return f2fs_ioc_get_encryption_pwsalt(filp, arg); default: return -ENOTTY; } } +static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) +{ + struct inode *inode = file_inode(iocb->ki_filp); + + if (f2fs_encrypted_inode(inode) && + !f2fs_has_encryption_key(inode) && + f2fs_get_encryption_info(inode)) + return -EACCES; + + return generic_file_write_iter(iocb, from); +} + #ifdef CONFIG_COMPAT long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -1157,8 +1610,8 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) const struct file_operations f2fs_file_operations = { .llseek = f2fs_llseek, .read_iter = generic_file_read_iter, - .write_iter = generic_file_write_iter, - .open = generic_file_open, + .write_iter = f2fs_file_write_iter, + .open = f2fs_file_open, .release = f2fs_release_file, .mmap = f2fs_file_mmap, .fsync = f2fs_sync_file, diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index ed58211fe..22fb5ef37 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -518,12 +518,91 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, return 1; } -static void move_data_page(struct inode *inode, struct page *page, int gc_type) +static void move_encrypted_block(struct inode *inode, block_t bidx) { struct f2fs_io_info fio = { + .sbi = F2FS_I_SB(inode), .type = DATA, - .rw = WRITE_SYNC, + .rw = READ_SYNC, + .encrypted_page = NULL, }; + struct dnode_of_data dn; + struct f2fs_summary sum; + struct node_info ni; + struct page *page; + int err; + + /* do not read out */ + page = grab_cache_page(inode->i_mapping, bidx); + if (!page) + return; + + set_new_dnode(&dn, inode, NULL, NULL, 0); + err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE); + if (err) + goto out; + + if (unlikely(dn.data_blkaddr == NULL_ADDR)) + goto put_out; + + get_node_info(fio.sbi, dn.nid, &ni); + set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version); + + /* read page */ + fio.page = page; + fio.blk_addr = dn.data_blkaddr; + + fio.encrypted_page = grab_cache_page(META_MAPPING(fio.sbi), fio.blk_addr); + if (!fio.encrypted_page) + goto put_out; + + err = f2fs_submit_page_bio(&fio); + if (err) + goto put_page_out; + + /* write page */ + lock_page(fio.encrypted_page); + + if (unlikely(!PageUptodate(fio.encrypted_page))) + goto put_page_out; + if (unlikely(fio.encrypted_page->mapping != META_MAPPING(fio.sbi))) + goto put_page_out; + + set_page_dirty(fio.encrypted_page); + f2fs_wait_on_page_writeback(fio.encrypted_page, META); + if (clear_page_dirty_for_io(fio.encrypted_page)) + dec_page_count(fio.sbi, F2FS_DIRTY_META); + + set_page_writeback(fio.encrypted_page); + + /* allocate block address */ + f2fs_wait_on_page_writeback(dn.node_page, NODE); + allocate_data_block(fio.sbi, NULL, fio.blk_addr, + &fio.blk_addr, &sum, CURSEG_COLD_DATA); + fio.rw = WRITE_SYNC; + f2fs_submit_page_mbio(&fio); + + dn.data_blkaddr = fio.blk_addr; + set_data_blkaddr(&dn); + f2fs_update_extent_cache(&dn); + set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE); + if (page->index == 0) + set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN); +put_page_out: + f2fs_put_page(fio.encrypted_page, 1); +put_out: + f2fs_put_dnode(&dn); +out: + f2fs_put_page(page, 1); +} + +static void move_data_page(struct inode *inode, block_t bidx, int gc_type) +{ + struct page *page; + + page = get_lock_data_page(inode, bidx); + if (IS_ERR(page)) + return; if (gc_type == BG_GC) { if (PageWriteback(page)) @@ -531,12 +610,19 @@ static void move_data_page(struct inode *inode, struct page *page, int gc_type) set_page_dirty(page); set_cold_data(page); } else { + struct f2fs_io_info fio = { + .sbi = F2FS_I_SB(inode), + .type = DATA, + .rw = WRITE_SYNC, + .page = page, + .encrypted_page = NULL, + }; + set_page_dirty(page); f2fs_wait_on_page_writeback(page, DATA); - if (clear_page_dirty_for_io(page)) inode_dec_dirty_pages(inode); set_cold_data(page); - do_write_data_page(page, &fio); + do_write_data_page(&fio); clear_cold_data(page); } out: @@ -599,10 +685,16 @@ next_step: if (IS_ERR(inode) || is_bad_inode(inode)) continue; - start_bidx = start_bidx_of_node(nofs, F2FS_I(inode)); + /* if encrypted inode, let's go phase 3 */ + if (f2fs_encrypted_inode(inode) && + S_ISREG(inode->i_mode)) { + add_gc_inode(gc_list, inode); + continue; + } - data_page = find_data_page(inode, - start_bidx + ofs_in_node, false); + start_bidx = start_bidx_of_node(nofs, F2FS_I(inode)); + data_page = get_read_data_page(inode, + start_bidx + ofs_in_node, READA); if (IS_ERR(data_page)) { iput(inode); continue; @@ -616,12 +708,12 @@ next_step: /* phase 3 */ inode = find_gc_inode(gc_list, dni.ino); if (inode) { - start_bidx = start_bidx_of_node(nofs, F2FS_I(inode)); - data_page = get_lock_data_page(inode, - start_bidx + ofs_in_node); - if (IS_ERR(data_page)) - continue; - move_data_page(inode, data_page, gc_type); + start_bidx = start_bidx_of_node(nofs, F2FS_I(inode)) + + ofs_in_node; + if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + move_encrypted_block(inode, start_bidx); + else + move_data_page(inode, start_bidx, gc_type); stat_inc_data_blk_count(sbi, 1, gc_type); } } @@ -670,6 +762,15 @@ static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, sum = page_address(sum_page); + /* + * this is to avoid deadlock: + * - lock_page(sum_page) - f2fs_replace_block + * - check_valid_map() - mutex_lock(sentry_lock) + * - mutex_lock(sentry_lock) - change_curseg() + * - lock_page(sum_page) + */ + unlock_page(sum_page); + switch (GET_SUM_TYPE((&sum->footer))) { case SUM_TYPE_NODE: gc_node_segment(sbi, sum->entries, segno, gc_type); @@ -683,7 +784,7 @@ static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer)), gc_type); stat_inc_call_count(sbi->stat_info); - f2fs_put_page(sum_page, 1); + f2fs_put_page(sum_page, 0); } int f2fs_gc(struct f2fs_sb_info *sbi) diff --git a/fs/f2fs/hash.c b/fs/f2fs/hash.c index a844fcfb9..71b7206c4 100644 --- a/fs/f2fs/hash.c +++ b/fs/f2fs/hash.c @@ -79,8 +79,7 @@ f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info) const unsigned char *name = name_info->name; size_t len = name_info->len; - if ((len <= 2) && (name[0] == '.') && - (name[1] == '.' || name[1] == '\0')) + if (is_dot_dotdot(name_info)) return 0; /* Initialize the default seed for the hash checksum functions */ diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 8140e4f0e..a13ffcc32 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -13,7 +13,7 @@ #include "f2fs.h" -bool f2fs_may_inline(struct inode *inode) +bool f2fs_may_inline_data(struct inode *inode) { if (!test_opt(F2FS_I_SB(inode), INLINE_DATA)) return false; @@ -27,6 +27,20 @@ bool f2fs_may_inline(struct inode *inode) if (i_size_read(inode) > MAX_INLINE_DATA) return false; + if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) + return false; + + return true; +} + +bool f2fs_may_inline_dentry(struct inode *inode) +{ + if (!test_opt(F2FS_I_SB(inode), INLINE_DENTRY)) + return false; + + if (!S_ISDIR(inode->i_mode)) + return false; + return true; } @@ -95,8 +109,11 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) { void *src_addr, *dst_addr; struct f2fs_io_info fio = { + .sbi = F2FS_I_SB(dn->inode), .type = DATA, .rw = WRITE_SYNC | REQ_PRIO, + .page = page, + .encrypted_page = NULL, }; int dirty, err; @@ -124,13 +141,15 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) kunmap_atomic(dst_addr); SetPageUptodate(page); no_update: + set_page_dirty(page); + /* clear dirty state */ dirty = clear_page_dirty_for_io(page); /* write data page to try to make data consistent */ set_page_writeback(page); fio.blk_addr = dn->data_blkaddr; - write_data_page(page, dn, &fio); + write_data_page(dn, &fio); set_data_blkaddr(dn); f2fs_update_extent_cache(dn); f2fs_wait_on_page_writeback(page, DATA); @@ -267,23 +286,26 @@ process_inline: } struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, - struct qstr *name, struct page **res_page) + struct f2fs_filename *fname, struct page **res_page) { struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); struct f2fs_inline_dentry *inline_dentry; + struct qstr name = FSTR_TO_QSTR(&fname->disk_name); struct f2fs_dir_entry *de; struct f2fs_dentry_ptr d; struct page *ipage; + f2fs_hash_t namehash; ipage = get_node_page(sbi, dir->i_ino); if (IS_ERR(ipage)) return NULL; - inline_dentry = inline_data_addr(ipage); + namehash = f2fs_dentry_hash(&name); - make_dentry_ptr(&d, (void *)inline_dentry, 2); - de = find_target_dentry(name, NULL, &d); + inline_dentry = inline_data_addr(ipage); + make_dentry_ptr(NULL, &d, (void *)inline_dentry, 2); + de = find_target_dentry(fname, namehash, NULL, &d); unlock_page(ipage); if (de) *res_page = ipage; @@ -325,7 +347,7 @@ int make_empty_inline_dir(struct inode *inode, struct inode *parent, dentry_blk = inline_data_addr(ipage); - make_dentry_ptr(&d, (void *)dentry_blk, 2); + make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2); do_make_empty_dir(inode, parent, &d); set_page_dirty(ipage); @@ -429,7 +451,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name, f2fs_wait_on_page_writeback(ipage, NODE); name_hash = f2fs_dentry_hash(name); - make_dentry_ptr(&d, (void *)dentry_blk, 2); + make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2); f2fs_update_dentry(ino, mode, &d, name, name_hash, bit_pos); set_page_dirty(ipage); @@ -506,7 +528,8 @@ bool f2fs_empty_inline_dir(struct inode *dir) return true; } -int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx) +int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx, + struct f2fs_str *fstr) { struct inode *inode = file_inode(file); struct f2fs_inline_dentry *inline_dentry = NULL; @@ -522,9 +545,9 @@ int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx) inline_dentry = inline_data_addr(ipage); - make_dentry_ptr(&d, (void *)inline_dentry, 2); + make_dentry_ptr(inode, &d, (void *)inline_dentry, 2); - if (!f2fs_fill_dentries(ctx, &d, 0)) + if (!f2fs_fill_dentries(ctx, &d, 0, fstr)) ctx->pos = NR_INLINE_DENTRY; f2fs_put_page(ipage, 1); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index e622ec954..2550868dc 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -198,7 +198,10 @@ make_now: inode->i_mapping->a_ops = &f2fs_dblock_aops; mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO); } else if (S_ISLNK(inode->i_mode)) { - inode->i_op = &f2fs_symlink_inode_operations; + if (f2fs_encrypted_inode(inode)) + inode->i_op = &f2fs_encrypted_symlink_inode_operations; + else + inode->i_op = &f2fs_symlink_inode_operations; inode->i_mapping->a_ops = &f2fs_dblock_aops; } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { @@ -359,6 +362,10 @@ no_delete: if (is_inode_flag_set(F2FS_I(inode), FI_UPDATE_WRITE)) add_dirty_inode(sbi, inode->i_ino, UPDATE_INO); out_clear: +#ifdef CONFIG_F2FS_FS_ENCRYPTION + if (F2FS_I(inode)->i_crypt_info) + f2fs_free_encryption_info(inode, F2FS_I(inode)->i_crypt_info); +#endif clear_inode(inode); } diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 658e8079a..fdbae21ee 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -56,11 +56,18 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) goto out; } - if (f2fs_may_inline(inode)) + /* If the directory encrypted, then we should encrypt the inode. */ + if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode)) + f2fs_set_encrypted_inode(inode); + + if (f2fs_may_inline_data(inode)) set_inode_flag(F2FS_I(inode), FI_INLINE_DATA); - if (test_opt(sbi, INLINE_DENTRY) && S_ISDIR(inode->i_mode)) + if (f2fs_may_inline_dentry(inode)) set_inode_flag(F2FS_I(inode), FI_INLINE_DENTRY); + stat_inc_inline_inode(inode); + stat_inc_inline_dir(inode); + trace_f2fs_new_inode(inode, 0); mark_inode_dirty(inode); return inode; @@ -136,7 +143,6 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, alloc_nid_done(sbi, ino); - stat_inc_inline_inode(inode); d_instantiate(dentry, inode); unlock_new_inode(inode); @@ -155,6 +161,10 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, struct f2fs_sb_info *sbi = F2FS_I_SB(dir); int err; + if (f2fs_encrypted_inode(dir) && + !f2fs_is_child_context_consistent_with_parent(dir, inode)) + return -EPERM; + f2fs_balance_fs(sbi); inode->i_ctime = CURRENT_TIME; @@ -232,32 +242,34 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, struct inode *inode = NULL; struct f2fs_dir_entry *de; struct page *page; + nid_t ino; + int err = 0; if (dentry->d_name.len > F2FS_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); de = f2fs_find_entry(dir, &dentry->d_name, &page); - if (de) { - nid_t ino = le32_to_cpu(de->ino); - f2fs_dentry_kunmap(dir, page); - f2fs_put_page(page, 0); + if (!de) + return d_splice_alias(inode, dentry); - inode = f2fs_iget(dir->i_sb, ino); - if (IS_ERR(inode)) - return ERR_CAST(inode); + ino = le32_to_cpu(de->ino); + f2fs_dentry_kunmap(dir, page); + f2fs_put_page(page, 0); - if (f2fs_has_inline_dots(inode)) { - int err; + inode = f2fs_iget(dir->i_sb, ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); - err = __recover_dot_dentries(inode, dir->i_ino); - if (err) { - iget_failed(inode); - return ERR_PTR(err); - } - } + if (f2fs_has_inline_dots(inode)) { + err = __recover_dot_dentries(inode, dir->i_ino); + if (err) + goto err_out; } - return d_splice_alias(inode, dentry); + +err_out: + iget_failed(inode); + return ERR_PTR(err); } static int f2fs_unlink(struct inode *dir, struct dentry *dentry) @@ -296,19 +308,15 @@ fail: return err; } -static void *f2fs_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *f2fs_follow_link(struct dentry *dentry, void **cookie) { - struct page *page = page_follow_link_light(dentry, nd); - - if (IS_ERR_OR_NULL(page)) - return page; - - /* this is broken symlink case */ - if (*nd_get_link(nd) == 0) { - page_put_link(dentry, nd, page); - return ERR_PTR(-ENOENT); + const char *link = page_follow_link_light(dentry, cookie); + if (!IS_ERR(link) && !*link) { + /* this is broken symlink case */ + page_put_link(NULL, *cookie); + link = ERR_PTR(-ENOENT); } - return page; + return link; } static int f2fs_symlink(struct inode *dir, struct dentry *dentry, @@ -316,16 +324,26 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, { struct f2fs_sb_info *sbi = F2FS_I_SB(dir); struct inode *inode; - size_t symlen = strlen(symname) + 1; + size_t len = strlen(symname); + size_t p_len; + char *p_str; + struct f2fs_str disk_link = FSTR_INIT(NULL, 0); + struct f2fs_encrypted_symlink_data *sd = NULL; int err; + if (len > dir->i_sb->s_blocksize) + return -ENAMETOOLONG; + f2fs_balance_fs(sbi); inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO); if (IS_ERR(inode)) return PTR_ERR(inode); - inode->i_op = &f2fs_symlink_inode_operations; + if (f2fs_encrypted_inode(inode)) + inode->i_op = &f2fs_encrypted_symlink_inode_operations; + else + inode->i_op = &f2fs_symlink_inode_operations; inode->i_mapping->a_ops = &f2fs_dblock_aops; f2fs_lock_op(sbi); @@ -333,10 +351,46 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, if (err) goto out; f2fs_unlock_op(sbi); - - err = page_symlink(inode, symname, symlen); alloc_nid_done(sbi, inode->i_ino); + if (f2fs_encrypted_inode(dir)) { + struct qstr istr = QSTR_INIT(symname, len); + + err = f2fs_get_encryption_info(inode); + if (err) + goto err_out; + + err = f2fs_fname_crypto_alloc_buffer(inode, len, &disk_link); + if (err) + goto err_out; + + err = f2fs_fname_usr_to_disk(inode, &istr, &disk_link); + if (err < 0) + goto err_out; + + p_len = encrypted_symlink_data_len(disk_link.len) + 1; + + if (p_len > dir->i_sb->s_blocksize) { + err = -ENAMETOOLONG; + goto err_out; + } + + sd = kzalloc(p_len, GFP_NOFS); + if (!sd) { + err = -ENOMEM; + goto err_out; + } + memcpy(sd->encrypted_path, disk_link.name, disk_link.len); + sd->len = cpu_to_le16(disk_link.len); + p_str = (char *)sd; + } else { + p_len = len + 1; + p_str = (char *)symname; + } + + err = page_symlink(inode, p_str, p_len); + +err_out: d_instantiate(dentry, inode); unlock_new_inode(inode); @@ -349,10 +403,14 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, * If the symlink path is stored into inline_data, there is no * performance regression. */ - filemap_write_and_wait_range(inode->i_mapping, 0, symlen - 1); + if (!err) + filemap_write_and_wait_range(inode->i_mapping, 0, p_len - 1); if (IS_DIRSYNC(dir)) f2fs_sync_fs(sbi->sb, 1); + + kfree(sd); + f2fs_fname_crypto_free_buffer(&disk_link); return err; out: handle_failed_inode(inode); @@ -383,7 +441,6 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) goto out_fail; f2fs_unlock_op(sbi); - stat_inc_inline_dir(inode); alloc_nid_done(sbi, inode->i_ino); d_instantiate(dentry, inode); @@ -445,19 +502,101 @@ out: return err; } +static int __f2fs_tmpfile(struct inode *dir, struct dentry *dentry, + umode_t mode, struct inode **whiteout) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct inode *inode; + int err; + + if (!whiteout) + f2fs_balance_fs(sbi); + + inode = f2fs_new_inode(dir, mode); + if (IS_ERR(inode)) + return PTR_ERR(inode); + + if (whiteout) { + init_special_inode(inode, inode->i_mode, WHITEOUT_DEV); + inode->i_op = &f2fs_special_inode_operations; + } else { + inode->i_op = &f2fs_file_inode_operations; + inode->i_fop = &f2fs_file_operations; + inode->i_mapping->a_ops = &f2fs_dblock_aops; + } + + f2fs_lock_op(sbi); + err = acquire_orphan_inode(sbi); + if (err) + goto out; + + err = f2fs_do_tmpfile(inode, dir); + if (err) + goto release_out; + + /* + * add this non-linked tmpfile to orphan list, in this way we could + * remove all unused data of tmpfile after abnormal power-off. + */ + add_orphan_inode(sbi, inode->i_ino); + f2fs_unlock_op(sbi); + + alloc_nid_done(sbi, inode->i_ino); + + if (whiteout) { + inode_dec_link_count(inode); + *whiteout = inode; + } else { + d_tmpfile(dentry, inode); + } + unlock_new_inode(inode); + return 0; + +release_out: + release_orphan_inode(sbi); +out: + handle_failed_inode(inode); + return err; +} + +static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + if (f2fs_encrypted_inode(dir)) { + int err = f2fs_get_encryption_info(dir); + if (err) + return err; + } + + return __f2fs_tmpfile(dir, dentry, mode, NULL); +} + +static int f2fs_create_whiteout(struct inode *dir, struct inode **whiteout) +{ + return __f2fs_tmpfile(dir, NULL, S_IFCHR | WHITEOUT_MODE, whiteout); +} + static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) { struct f2fs_sb_info *sbi = F2FS_I_SB(old_dir); struct inode *old_inode = d_inode(old_dentry); struct inode *new_inode = d_inode(new_dentry); + struct inode *whiteout = NULL; struct page *old_dir_page; - struct page *old_page, *new_page; + struct page *old_page, *new_page = NULL; struct f2fs_dir_entry *old_dir_entry = NULL; struct f2fs_dir_entry *old_entry; struct f2fs_dir_entry *new_entry; int err = -ENOENT; + if ((old_dir != new_dir) && f2fs_encrypted_inode(new_dir) && + !f2fs_is_child_context_consistent_with_parent(new_dir, + old_inode)) { + err = -EPERM; + goto out; + } + f2fs_balance_fs(sbi); old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page); @@ -471,17 +610,23 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out_old; } + if (flags & RENAME_WHITEOUT) { + err = f2fs_create_whiteout(old_dir, &whiteout); + if (err) + goto out_dir; + } + if (new_inode) { err = -ENOTEMPTY; if (old_dir_entry && !f2fs_empty_dir(new_inode)) - goto out_dir; + goto out_whiteout; err = -ENOENT; new_entry = f2fs_find_entry(new_dir, &new_dentry->d_name, &new_page); if (!new_entry) - goto out_dir; + goto out_whiteout; f2fs_lock_op(sbi); @@ -489,7 +634,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, if (err) goto put_out_dir; - if (update_dent_inode(old_inode, &new_dentry->d_name)) { + if (update_dent_inode(old_inode, new_inode, + &new_dentry->d_name)) { release_orphan_inode(sbi); goto put_out_dir; } @@ -518,7 +664,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, err = f2fs_add_link(new_dentry, old_inode); if (err) { f2fs_unlock_op(sbi); - goto out_dir; + goto out_whiteout; } if (old_dir_entry) { @@ -529,6 +675,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, down_write(&F2FS_I(old_inode)->i_sem); file_lost_pino(old_inode); + if (new_inode && file_enc_name(new_inode)) + file_set_enc_name(old_inode); up_write(&F2FS_I(old_inode)->i_sem); old_inode->i_ctime = CURRENT_TIME; @@ -536,8 +684,18 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, f2fs_delete_entry(old_entry, old_page, old_dir, NULL); + if (whiteout) { + whiteout->i_state |= I_LINKABLE; + set_inode_flag(F2FS_I(whiteout), FI_INC_LINK); + err = f2fs_add_link(old_dentry, whiteout); + if (err) + goto put_out_dir; + whiteout->i_state &= ~I_LINKABLE; + iput(whiteout); + } + if (old_dir_entry) { - if (old_dir != new_dir) { + if (old_dir != new_dir && !whiteout) { f2fs_set_link(old_inode, old_dir_entry, old_dir_page, new_dir); update_inode_page(old_inode); @@ -558,8 +716,13 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, put_out_dir: f2fs_unlock_op(sbi); - f2fs_dentry_kunmap(new_dir, new_page); - f2fs_put_page(new_page, 0); + if (new_page) { + f2fs_dentry_kunmap(new_dir, new_page); + f2fs_put_page(new_page, 0); + } +out_whiteout: + if (whiteout) + iput(whiteout); out_dir: if (old_dir_entry) { f2fs_dentry_kunmap(old_inode, old_dir_page); @@ -585,6 +748,14 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, int old_nlink = 0, new_nlink = 0; int err = -ENOENT; + if ((f2fs_encrypted_inode(old_dir) || f2fs_encrypted_inode(new_dir)) && + (old_dir != new_dir) && + (!f2fs_is_child_context_consistent_with_parent(new_dir, + old_inode) || + !f2fs_is_child_context_consistent_with_parent(old_dir, + new_inode))) + return -EPERM; + f2fs_balance_fs(sbi); old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page); @@ -631,13 +802,17 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, f2fs_lock_op(sbi); - err = update_dent_inode(old_inode, &new_dentry->d_name); + err = update_dent_inode(old_inode, new_inode, &new_dentry->d_name); if (err) goto out_unlock; + if (file_enc_name(new_inode)) + file_set_enc_name(old_inode); - err = update_dent_inode(new_inode, &old_dentry->d_name); + err = update_dent_inode(new_inode, old_inode, &old_dentry->d_name); if (err) goto out_undo; + if (file_enc_name(old_inode)) + file_set_enc_name(new_inode); /* update ".." directory entry info of old dentry */ if (old_dir_entry) @@ -695,8 +870,11 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, f2fs_sync_fs(sbi->sb, 1); return 0; out_undo: - /* Still we may fail to recover name info of f2fs_inode here */ - update_dent_inode(old_inode, &old_dentry->d_name); + /* + * Still we may fail to recover name info of f2fs_inode here + * Drop it, once its name is set as encrypted + */ + update_dent_inode(old_inode, old_inode, &old_dentry->d_name); out_unlock: f2fs_unlock_op(sbi); out_new_dir: @@ -723,7 +901,7 @@ static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { - if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE)) + if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT)) return -EINVAL; if (flags & RENAME_EXCHANGE) { @@ -734,53 +912,85 @@ static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry, * VFS has already handled the new dentry existence case, * here, we just deal with "RENAME_NOREPLACE" as regular rename. */ - return f2fs_rename(old_dir, old_dentry, new_dir, new_dentry); + return f2fs_rename(old_dir, old_dentry, new_dir, new_dentry, flags); } -static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) +#ifdef CONFIG_F2FS_FS_ENCRYPTION +static const char *f2fs_encrypted_follow_link(struct dentry *dentry, void **cookie) { - struct f2fs_sb_info *sbi = F2FS_I_SB(dir); - struct inode *inode; - int err; - - inode = f2fs_new_inode(dir, mode); - if (IS_ERR(inode)) - return PTR_ERR(inode); - - inode->i_op = &f2fs_file_inode_operations; - inode->i_fop = &f2fs_file_operations; - inode->i_mapping->a_ops = &f2fs_dblock_aops; - - f2fs_lock_op(sbi); - err = acquire_orphan_inode(sbi); - if (err) - goto out; - - err = f2fs_do_tmpfile(inode, dir); - if (err) - goto release_out; - - /* - * add this non-linked tmpfile to orphan list, in this way we could - * remove all unused data of tmpfile after abnormal power-off. - */ - add_orphan_inode(sbi, inode->i_ino); - f2fs_unlock_op(sbi); - - alloc_nid_done(sbi, inode->i_ino); + struct page *cpage = NULL; + char *caddr, *paddr = NULL; + struct f2fs_str cstr; + struct f2fs_str pstr = FSTR_INIT(NULL, 0); + struct inode *inode = d_inode(dentry); + struct f2fs_encrypted_symlink_data *sd; + loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1); + u32 max_size = inode->i_sb->s_blocksize; + int res; + + res = f2fs_get_encryption_info(inode); + if (res) + return ERR_PTR(res); + + cpage = read_mapping_page(inode->i_mapping, 0, NULL); + if (IS_ERR(cpage)) + return ERR_CAST(cpage); + caddr = kmap(cpage); + caddr[size] = 0; + + /* Symlink is encrypted */ + sd = (struct f2fs_encrypted_symlink_data *)caddr; + cstr.name = sd->encrypted_path; + cstr.len = le16_to_cpu(sd->len); - stat_inc_inline_inode(inode); - d_tmpfile(dentry, inode); - unlock_new_inode(inode); - return 0; + /* this is broken symlink case */ + if (cstr.name[0] == 0 && cstr.len == 0) { + res = -ENOENT; + goto errout; + } -release_out: - release_orphan_inode(sbi); -out: - handle_failed_inode(inode); - return err; + if ((cstr.len + sizeof(struct f2fs_encrypted_symlink_data) - 1) > + max_size) { + /* Symlink data on the disk is corrupted */ + res = -EIO; + goto errout; + } + res = f2fs_fname_crypto_alloc_buffer(inode, cstr.len, &pstr); + if (res) + goto errout; + + res = f2fs_fname_disk_to_usr(inode, NULL, &cstr, &pstr); + if (res < 0) + goto errout; + + paddr = pstr.name; + + /* Null-terminate the name */ + paddr[res] = '\0'; + + kunmap(cpage); + page_cache_release(cpage); + return *cookie = paddr; +errout: + f2fs_fname_crypto_free_buffer(&pstr); + kunmap(cpage); + page_cache_release(cpage); + return ERR_PTR(res); } +const struct inode_operations f2fs_encrypted_symlink_inode_operations = { + .readlink = generic_readlink, + .follow_link = f2fs_encrypted_follow_link, + .put_link = kfree_put_link, + .getattr = f2fs_getattr, + .setattr = f2fs_setattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .listxattr = f2fs_listxattr, + .removexattr = generic_removexattr, +}; +#endif + const struct inode_operations f2fs_dir_inode_operations = { .create = f2fs_create, .lookup = f2fs_lookup, diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 8ab0cf193..7dd63b794 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -53,7 +53,7 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type) PAGE_CACHE_SHIFT; res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2); } else if (type == DIRTY_DENTS) { - if (sbi->sb->s_bdi->dirty_exceeded) + if (sbi->sb->s_bdi->wb.dirty_exceeded) return false; mem_size = get_pages(sbi, F2FS_DIRTY_DENTS); res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1); @@ -70,7 +70,7 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type) sizeof(struct extent_node)) >> PAGE_CACHE_SHIFT; res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1); } else { - if (sbi->sb->s_bdi->dirty_exceeded) + if (sbi->sb->s_bdi->wb.dirty_exceeded) return false; } return res; @@ -195,32 +195,35 @@ static unsigned int __gang_lookup_nat_set(struct f2fs_nm_info *nm_i, start, nr); } -bool is_checkpointed_node(struct f2fs_sb_info *sbi, nid_t nid) +int need_dentry_mark(struct f2fs_sb_info *sbi, nid_t nid) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct nat_entry *e; - bool is_cp = true; + bool need = false; down_read(&nm_i->nat_tree_lock); e = __lookup_nat_cache(nm_i, nid); - if (e && !get_nat_flag(e, IS_CHECKPOINTED)) - is_cp = false; + if (e) { + if (!get_nat_flag(e, IS_CHECKPOINTED) && + !get_nat_flag(e, HAS_FSYNCED_INODE)) + need = true; + } up_read(&nm_i->nat_tree_lock); - return is_cp; + return need; } -bool has_fsynced_inode(struct f2fs_sb_info *sbi, nid_t ino) +bool is_checkpointed_node(struct f2fs_sb_info *sbi, nid_t nid) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct nat_entry *e; - bool fsynced = false; + bool is_cp = true; down_read(&nm_i->nat_tree_lock); - e = __lookup_nat_cache(nm_i, ino); - if (e && get_nat_flag(e, HAS_FSYNCED_INODE)) - fsynced = true; + e = __lookup_nat_cache(nm_i, nid); + if (e && !get_nat_flag(e, IS_CHECKPOINTED)) + is_cp = false; up_read(&nm_i->nat_tree_lock); - return fsynced; + return is_cp; } bool need_inode_block_update(struct f2fs_sb_info *sbi, nid_t ino) @@ -312,7 +315,8 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, __set_nat_cache_dirty(nm_i, e); /* update fsync_mark if its inode nat entry is still alive */ - e = __lookup_nat_cache(nm_i, ni->ino); + if (ni->nid != ni->ino) + e = __lookup_nat_cache(nm_i, ni->ino); if (e) { if (fsync_done && ni->nid == ni->ino) set_nat_flag(e, HAS_FSYNCED_INODE, true); @@ -995,8 +999,11 @@ static int read_node_page(struct page *page, int rw) struct f2fs_sb_info *sbi = F2FS_P_SB(page); struct node_info ni; struct f2fs_io_info fio = { + .sbi = sbi, .type = NODE, .rw = rw, + .page = page, + .encrypted_page = NULL, }; get_node_info(sbi, page->index, &ni); @@ -1011,7 +1018,7 @@ static int read_node_page(struct page *page, int rw) return LOCKED_PAGE; fio.blk_addr = ni.blk_addr; - return f2fs_submit_page_bio(sbi, page, &fio); + return f2fs_submit_page_bio(&fio); } /* @@ -1204,13 +1211,9 @@ continue_unlock: /* called by fsync() */ if (ino && IS_DNODE(page)) { set_fsync_mark(page, 1); - if (IS_INODE(page)) { - if (!is_checkpointed_node(sbi, ino) && - !has_fsynced_inode(sbi, ino)) - set_dentry_mark(page, 1); - else - set_dentry_mark(page, 0); - } + if (IS_INODE(page)) + set_dentry_mark(page, + need_dentry_mark(sbi, ino)); nwritten++; } else { set_fsync_mark(page, 0); @@ -1293,8 +1296,11 @@ static int f2fs_write_node_page(struct page *page, nid_t nid; struct node_info ni; struct f2fs_io_info fio = { + .sbi = sbi, .type = NODE, .rw = (wbc->sync_mode == WB_SYNC_ALL) ? WRITE_SYNC : WRITE, + .page = page, + .encrypted_page = NULL, }; trace_f2fs_writepage(page, NODE); @@ -1329,7 +1335,7 @@ static int f2fs_write_node_page(struct page *page, set_page_writeback(page); fio.blk_addr = ni.blk_addr; - write_node_page(sbi, page, nid, &fio); + write_node_page(nid, &fio); set_node_addr(sbi, &ni, fio.blk_addr, is_fsync_dnode(page)); dec_page_count(sbi, F2FS_DIRTY_NODES); up_read(&sbi->node_write); diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index c56026f17..7427e956a 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -343,28 +343,6 @@ static inline nid_t get_nid(struct page *p, int off, bool i) * - Mark cold node blocks in their node footer * - Mark cold data pages in page cache */ -static inline int is_file(struct inode *inode, int type) -{ - return F2FS_I(inode)->i_advise & type; -} - -static inline void set_file(struct inode *inode, int type) -{ - F2FS_I(inode)->i_advise |= type; -} - -static inline void clear_file(struct inode *inode, int type) -{ - F2FS_I(inode)->i_advise &= ~type; -} - -#define file_is_cold(inode) is_file(inode, FADVISE_COLD_BIT) -#define file_wrong_pino(inode) is_file(inode, FADVISE_LOST_PINO_BIT) -#define file_set_cold(inode) set_file(inode, FADVISE_COLD_BIT) -#define file_lost_pino(inode) set_file(inode, FADVISE_LOST_PINO_BIT) -#define file_clear_cold(inode) clear_file(inode, FADVISE_COLD_BIT) -#define file_got_pino(inode) clear_file(inode, FADVISE_LOST_PINO_BIT) - static inline int is_cold_data(struct page *page) { return PageChecked(page); diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 8d8ea99f2..24a8c1d4f 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -83,6 +83,11 @@ static int recover_dentry(struct inode *inode, struct page *ipage) goto out; } + if (file_enc_name(inode)) { + iput(dir); + return 0; + } + name.len = le32_to_cpu(raw_inode->i_namelen); name.name = raw_inode->i_name; @@ -143,6 +148,7 @@ out: static void recover_inode(struct inode *inode, struct page *page) { struct f2fs_inode *raw = F2FS_INODE(page); + char *name; inode->i_mode = le16_to_cpu(raw->i_mode); i_size_write(inode, le64_to_cpu(raw->i_size)); @@ -153,8 +159,13 @@ static void recover_inode(struct inode *inode, struct page *page) inode->i_ctime.tv_nsec = le32_to_cpu(raw->i_ctime_nsec); inode->i_mtime.tv_nsec = le32_to_cpu(raw->i_mtime_nsec); + if (file_enc_name(inode)) + name = "<encrypted>"; + else + name = F2FS_INODE(page)->i_name; + f2fs_msg(inode->i_sb, KERN_NOTICE, "recover_inode: ino = %x, name = %s", - ino_of_node(page), F2FS_INODE(page)->i_name); + ino_of_node(page), name); } static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) @@ -174,7 +185,7 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) while (1) { struct fsync_inode_entry *entry; - if (blkaddr < MAIN_BLKADDR(sbi) || blkaddr >= MAX_BLKADDR(sbi)) + if (!is_valid_blkaddr(sbi, blkaddr, META_POR)) return 0; page = get_meta_page(sbi, blkaddr); @@ -349,7 +360,6 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, struct f2fs_inode_info *fi = F2FS_I(inode); unsigned int start, end; struct dnode_of_data dn; - struct f2fs_summary sum; struct node_info ni; int err = 0, recovered = 0; @@ -396,7 +406,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, dest = datablock_addr(page, dn.ofs_in_node); if (src != dest && dest != NEW_ADDR && dest != NULL_ADDR && - dest >= MAIN_BLKADDR(sbi) && dest < MAX_BLKADDR(sbi)) { + is_valid_blkaddr(sbi, dest, META_POR)) { if (src == NULL_ADDR) { err = reserve_new_block(&dn); @@ -409,13 +419,9 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, if (err) goto err; - set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version); - /* write dummy data page */ - recover_data_page(sbi, NULL, &sum, src, dest); - dn.data_blkaddr = dest; - set_data_blkaddr(&dn); - f2fs_update_extent_cache(&dn); + f2fs_replace_block(sbi, &dn, src, dest, + ni.version, false); recovered++; } dn.ofs_in_node++; @@ -454,7 +460,7 @@ static int recover_data(struct f2fs_sb_info *sbi, while (1) { struct fsync_inode_entry *entry; - if (blkaddr < MAIN_BLKADDR(sbi) || blkaddr >= MAX_BLKADDR(sbi)) + if (!is_valid_blkaddr(sbi, blkaddr, META_POR)) break; ra_meta_pages_cond(sbi, blkaddr); diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index f93966094..61b97f9cb 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -75,6 +75,14 @@ static inline unsigned long __reverse_ffs(unsigned long word) static unsigned long __find_rev_next_bit(const unsigned long *addr, unsigned long size, unsigned long offset) { + while (!f2fs_test_bit(offset, (unsigned char *)addr)) + offset++; + + if (offset > size) + offset = size; + + return offset; +#if 0 const unsigned long *p = addr + BIT_WORD(offset); unsigned long result = offset & ~(BITS_PER_LONG - 1); unsigned long tmp; @@ -121,11 +129,20 @@ found_first: return result + size; /* Nope. */ found_middle: return result + __reverse_ffs(tmp); +#endif } static unsigned long __find_rev_next_zero_bit(const unsigned long *addr, unsigned long size, unsigned long offset) { + while (f2fs_test_bit(offset, (unsigned char *)addr)) + offset++; + + if (offset > size) + offset = size; + + return offset; +#if 0 const unsigned long *p = addr + BIT_WORD(offset); unsigned long result = offset & ~(BITS_PER_LONG - 1); unsigned long tmp; @@ -173,6 +190,7 @@ found_first: return result + size; /* Nope. */ found_middle: return result + __reverse_ffz(tmp); +#endif } void register_inmem_page(struct inode *inode, struct page *page) @@ -216,8 +234,10 @@ void commit_inmem_pages(struct inode *inode, bool abort) struct inmem_pages *cur, *tmp; bool submit_bio = false; struct f2fs_io_info fio = { + .sbi = sbi, .type = DATA, .rw = WRITE_SYNC | REQ_PRIO, + .encrypted_page = NULL, }; /* @@ -237,11 +257,13 @@ void commit_inmem_pages(struct inode *inode, bool abort) if (!abort) { lock_page(cur->page); if (cur->page->mapping == inode->i_mapping) { + set_page_dirty(cur->page); f2fs_wait_on_page_writeback(cur->page, DATA); if (clear_page_dirty_for_io(cur->page)) inode_dec_dirty_pages(inode); trace_f2fs_commit_inmem_page(cur->page, INMEM); - do_write_data_page(cur->page, &fio); + fio.page = cur->page; + do_write_data_page(&fio); submit_bio = true; } f2fs_put_page(cur->page, 1); @@ -466,22 +488,43 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi, { sector_t start = SECTOR_FROM_BLOCK(blkstart); sector_t len = SECTOR_FROM_BLOCK(blklen); + struct seg_entry *se; + unsigned int offset; + block_t i; + + for (i = blkstart; i < blkstart + blklen; i++) { + se = get_seg_entry(sbi, GET_SEGNO(sbi, i)); + offset = GET_BLKOFF_FROM_SEG0(sbi, i); + + if (!f2fs_test_and_set_bit(offset, se->discard_map)) + sbi->discard_blks--; + } trace_f2fs_issue_discard(sbi->sb, blkstart, blklen); return blkdev_issue_discard(sbi->sb->s_bdev, start, len, GFP_NOFS, 0); } void discard_next_dnode(struct f2fs_sb_info *sbi, block_t blkaddr) { - if (f2fs_issue_discard(sbi, blkaddr, 1)) { - struct page *page = grab_meta_page(sbi, blkaddr); - /* zero-filled page */ - set_page_dirty(page); - f2fs_put_page(page, 1); + int err = -ENOTSUPP; + + if (test_opt(sbi, DISCARD)) { + struct seg_entry *se = get_seg_entry(sbi, + GET_SEGNO(sbi, blkaddr)); + unsigned int offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); + + if (f2fs_test_bit(offset, se->discard_map)) + return; + + err = f2fs_issue_discard(sbi, blkaddr, 1); } + + if (err) + update_meta_page(sbi, NULL, blkaddr); } static void __add_discard_entry(struct f2fs_sb_info *sbi, - struct cp_control *cpc, unsigned int start, unsigned int end) + struct cp_control *cpc, struct seg_entry *se, + unsigned int start, unsigned int end) { struct list_head *head = &SM_I(sbi)->discard_list; struct discard_entry *new, *last; @@ -502,7 +545,6 @@ static void __add_discard_entry(struct f2fs_sb_info *sbi, list_add_tail(&new->list, head); done: SM_I(sbi)->nr_discards += end - start; - cpc->trimmed += end - start; } static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc) @@ -512,41 +554,24 @@ static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc) struct seg_entry *se = get_seg_entry(sbi, cpc->trim_start); unsigned long *cur_map = (unsigned long *)se->cur_valid_map; unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map; + unsigned long *discard_map = (unsigned long *)se->discard_map; unsigned long *dmap = SIT_I(sbi)->tmp_map; unsigned int start = 0, end = -1; bool force = (cpc->reason == CP_DISCARD); int i; - if (!force && (!test_opt(sbi, DISCARD) || - SM_I(sbi)->nr_discards >= SM_I(sbi)->max_discards)) + if (se->valid_blocks == max_blocks) return; - if (force && !se->valid_blocks) { - struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); - /* - * if this segment is registered in the prefree list, then - * we should skip adding a discard candidate, and let the - * checkpoint do that later. - */ - mutex_lock(&dirty_i->seglist_lock); - if (test_bit(cpc->trim_start, dirty_i->dirty_segmap[PRE])) { - mutex_unlock(&dirty_i->seglist_lock); - cpc->trimmed += sbi->blocks_per_seg; + if (!force) { + if (!test_opt(sbi, DISCARD) || !se->valid_blocks || + SM_I(sbi)->nr_discards >= SM_I(sbi)->max_discards) return; - } - mutex_unlock(&dirty_i->seglist_lock); - - __add_discard_entry(sbi, cpc, 0, sbi->blocks_per_seg); - return; } - /* zero block will be discarded through the prefree list */ - if (!se->valid_blocks || se->valid_blocks == max_blocks) - return; - /* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */ for (i = 0; i < entries; i++) - dmap[i] = force ? ~ckpt_map[i] : + dmap[i] = force ? ~ckpt_map[i] & ~discard_map[i] : (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; while (force || SM_I(sbi)->nr_discards <= SM_I(sbi)->max_discards) { @@ -555,11 +580,7 @@ static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc) break; end = __find_rev_next_zero_bit(dmap, max_blocks, start + 1); - - if (force && end - start < cpc->trim_minlen) - continue; - - __add_discard_entry(sbi, cpc, start, end); + __add_discard_entry(sbi, cpc, se, start, end); } } @@ -589,7 +610,7 @@ static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi) mutex_unlock(&dirty_i->seglist_lock); } -void clear_prefree_segments(struct f2fs_sb_info *sbi) +void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) { struct list_head *head = &(SM_I(sbi)->discard_list); struct discard_entry *entry, *this; @@ -622,7 +643,11 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi) /* send small discards */ list_for_each_entry_safe(entry, this, head, list) { + if (cpc->reason == CP_DISCARD && entry->len < cpc->trim_minlen) + goto skip; f2fs_issue_discard(sbi, entry->blkaddr, entry->len); + cpc->trimmed += entry->len; +skip: list_del(&entry->list); SM_I(sbi)->nr_discards -= entry->len; kmem_cache_free(discard_entry_slab, entry); @@ -673,9 +698,13 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) if (del > 0) { if (f2fs_test_and_set_bit(offset, se->cur_valid_map)) f2fs_bug_on(sbi, 1); + if (!f2fs_test_and_set_bit(offset, se->discard_map)) + sbi->discard_blks--; } else { if (!f2fs_test_and_clear_bit(offset, se->cur_valid_map)) f2fs_bug_on(sbi, 1); + if (f2fs_test_and_clear_bit(offset, se->discard_map)) + sbi->discard_blks++; } if (!f2fs_test_bit(offset, se->ckpt_valid_map)) se->ckpt_valid_blocks += del; @@ -769,16 +798,25 @@ struct page *get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno) return get_meta_page(sbi, GET_SUM_BLOCK(sbi, segno)); } -static void write_sum_page(struct f2fs_sb_info *sbi, - struct f2fs_summary_block *sum_blk, block_t blk_addr) +void update_meta_page(struct f2fs_sb_info *sbi, void *src, block_t blk_addr) { struct page *page = grab_meta_page(sbi, blk_addr); - void *kaddr = page_address(page); - memcpy(kaddr, sum_blk, PAGE_CACHE_SIZE); + void *dst = page_address(page); + + if (src) + memcpy(dst, src, PAGE_CACHE_SIZE); + else + memset(dst, 0, PAGE_CACHE_SIZE); set_page_dirty(page); f2fs_put_page(page, 1); } +static void write_sum_page(struct f2fs_sb_info *sbi, + struct f2fs_summary_block *sum_blk, block_t blk_addr) +{ + update_meta_page(sbi, (void *)sum_blk, blk_addr); +} + static int is_next_segment_free(struct f2fs_sb_info *sbi, int type) { struct curseg_info *curseg = CURSEG_I(sbi, type); @@ -1060,8 +1098,7 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) unsigned int start_segno, end_segno; struct cp_control cpc; - if (range->minlen > SEGMENT_SIZE(sbi) || start >= MAX_BLKADDR(sbi) || - range->len < sbi->blocksize) + if (start >= MAX_BLKADDR(sbi) || range->len < sbi->blocksize) return -EINVAL; cpc.trimmed = 0; @@ -1073,12 +1110,19 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) end_segno = (end >= MAX_BLKADDR(sbi)) ? MAIN_SEGS(sbi) - 1 : GET_SEGNO(sbi, end); cpc.reason = CP_DISCARD; - cpc.trim_minlen = F2FS_BYTES_TO_BLK(range->minlen); + cpc.trim_minlen = max_t(__u64, 1, F2FS_BYTES_TO_BLK(range->minlen)); /* do checkpoint to issue discard commands safely */ for (; start_segno <= end_segno; start_segno = cpc.trim_end + 1) { cpc.trim_start = start_segno; - cpc.trim_end = min_t(unsigned int, rounddown(start_segno + + + if (sbi->discard_blks == 0) + break; + else if (sbi->discard_blks < BATCHED_TRIM_BLOCKS(sbi)) + cpc.trim_end = end_segno; + else + cpc.trim_end = min_t(unsigned int, + rounddown(start_segno + BATCHED_TRIM_SEGMENTS(sbi), sbi->segs_per_sec) - 1, end_segno); @@ -1206,84 +1250,95 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, mutex_unlock(&curseg->curseg_mutex); } -static void do_write_page(struct f2fs_sb_info *sbi, struct page *page, - struct f2fs_summary *sum, - struct f2fs_io_info *fio) +static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) { - int type = __get_segment_type(page, fio->type); + int type = __get_segment_type(fio->page, fio->type); - allocate_data_block(sbi, page, fio->blk_addr, &fio->blk_addr, sum, type); + allocate_data_block(fio->sbi, fio->page, fio->blk_addr, + &fio->blk_addr, sum, type); /* writeout dirty page into bdev */ - f2fs_submit_page_mbio(sbi, page, fio); + f2fs_submit_page_mbio(fio); } void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) { struct f2fs_io_info fio = { + .sbi = sbi, .type = META, .rw = WRITE_SYNC | REQ_META | REQ_PRIO, .blk_addr = page->index, + .page = page, + .encrypted_page = NULL, }; set_page_writeback(page); - f2fs_submit_page_mbio(sbi, page, &fio); + f2fs_submit_page_mbio(&fio); } -void write_node_page(struct f2fs_sb_info *sbi, struct page *page, - unsigned int nid, struct f2fs_io_info *fio) +void write_node_page(unsigned int nid, struct f2fs_io_info *fio) { struct f2fs_summary sum; + set_summary(&sum, nid, 0, 0); - do_write_page(sbi, page, &sum, fio); + do_write_page(&sum, fio); } -void write_data_page(struct page *page, struct dnode_of_data *dn, - struct f2fs_io_info *fio) +void write_data_page(struct dnode_of_data *dn, struct f2fs_io_info *fio) { - struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); + struct f2fs_sb_info *sbi = fio->sbi; struct f2fs_summary sum; struct node_info ni; f2fs_bug_on(sbi, dn->data_blkaddr == NULL_ADDR); get_node_info(sbi, dn->nid, &ni); set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version); - do_write_page(sbi, page, &sum, fio); + do_write_page(&sum, fio); dn->data_blkaddr = fio->blk_addr; } -void rewrite_data_page(struct page *page, struct f2fs_io_info *fio) +void rewrite_data_page(struct f2fs_io_info *fio) { - stat_inc_inplace_blocks(F2FS_P_SB(page)); - f2fs_submit_page_mbio(F2FS_P_SB(page), page, fio); + stat_inc_inplace_blocks(fio->sbi); + f2fs_submit_page_mbio(fio); } -void recover_data_page(struct f2fs_sb_info *sbi, - struct page *page, struct f2fs_summary *sum, - block_t old_blkaddr, block_t new_blkaddr) +static void __f2fs_replace_block(struct f2fs_sb_info *sbi, + struct f2fs_summary *sum, + block_t old_blkaddr, block_t new_blkaddr, + bool recover_curseg) { struct sit_info *sit_i = SIT_I(sbi); struct curseg_info *curseg; unsigned int segno, old_cursegno; struct seg_entry *se; int type; + unsigned short old_blkoff; segno = GET_SEGNO(sbi, new_blkaddr); se = get_seg_entry(sbi, segno); type = se->type; - if (se->valid_blocks == 0 && !IS_CURSEG(sbi, segno)) { - if (old_blkaddr == NULL_ADDR) - type = CURSEG_COLD_DATA; - else + if (!recover_curseg) { + /* for recovery flow */ + if (se->valid_blocks == 0 && !IS_CURSEG(sbi, segno)) { + if (old_blkaddr == NULL_ADDR) + type = CURSEG_COLD_DATA; + else + type = CURSEG_WARM_DATA; + } + } else { + if (!IS_CURSEG(sbi, segno)) type = CURSEG_WARM_DATA; } + curseg = CURSEG_I(sbi, type); mutex_lock(&curseg->curseg_mutex); mutex_lock(&sit_i->sentry_lock); old_cursegno = curseg->segno; + old_blkoff = curseg->next_blkoff; /* change the current segment */ if (segno != curseg->segno) { @@ -1297,30 +1352,67 @@ void recover_data_page(struct f2fs_sb_info *sbi, refresh_sit_entry(sbi, old_blkaddr, new_blkaddr); locate_dirty_segment(sbi, old_cursegno); + if (recover_curseg) { + if (old_cursegno != curseg->segno) { + curseg->next_segno = old_cursegno; + change_curseg(sbi, type, true); + } + curseg->next_blkoff = old_blkoff; + } + mutex_unlock(&sit_i->sentry_lock); mutex_unlock(&curseg->curseg_mutex); } +void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn, + block_t old_addr, block_t new_addr, + unsigned char version, bool recover_curseg) +{ + struct f2fs_summary sum; + + set_summary(&sum, dn->nid, dn->ofs_in_node, version); + + __f2fs_replace_block(sbi, &sum, old_addr, new_addr, recover_curseg); + + dn->data_blkaddr = new_addr; + set_data_blkaddr(dn); + f2fs_update_extent_cache(dn); +} + static inline bool is_merged_page(struct f2fs_sb_info *sbi, struct page *page, enum page_type type) { enum page_type btype = PAGE_TYPE_OF_BIO(type); struct f2fs_bio_info *io = &sbi->write_io[btype]; struct bio_vec *bvec; + struct page *target; int i; down_read(&io->io_rwsem); - if (!io->bio) - goto out; + if (!io->bio) { + up_read(&io->io_rwsem); + return false; + } bio_for_each_segment_all(bvec, io->bio, i) { - if (page == bvec->bv_page) { + + if (bvec->bv_page->mapping) { + target = bvec->bv_page; + } else { + struct f2fs_crypto_ctx *ctx; + + /* encrypted page */ + ctx = (struct f2fs_crypto_ctx *)page_private( + bvec->bv_page); + target = ctx->w.control_page; + } + + if (page == target) { up_read(&io->io_rwsem); return true; } } -out: up_read(&io->io_rwsem); return false; } @@ -1857,8 +1949,11 @@ static int build_sit_info(struct f2fs_sb_info *sbi) = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); sit_i->sentries[start].ckpt_valid_map = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); - if (!sit_i->sentries[start].cur_valid_map - || !sit_i->sentries[start].ckpt_valid_map) + sit_i->sentries[start].discard_map + = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + if (!sit_i->sentries[start].cur_valid_map || + !sit_i->sentries[start].ckpt_valid_map || + !sit_i->sentries[start].discard_map) return -ENOMEM; } @@ -1996,6 +2091,11 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) got_it: check_block_count(sbi, start, &sit); seg_info_from_raw_sit(se, &sit); + + /* build discard map only one time */ + memcpy(se->discard_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE); + sbi->discard_blks += sbi->blocks_per_seg - se->valid_blocks; + if (sbi->segs_per_sec > 1) { struct sec_entry *e = get_sec_entry(sbi, start); e->valid_blocks += se->valid_blocks; @@ -2245,6 +2345,7 @@ static void destroy_sit_info(struct f2fs_sb_info *sbi) for (start = 0; start < MAIN_SEGS(sbi); start++) { kfree(sit_i->sentries[start].cur_valid_map); kfree(sit_i->sentries[start].ckpt_valid_map); + kfree(sit_i->sentries[start].discard_map); } } kfree(sit_i->tmp_map); diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 85d7fa751..79e7b879a 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -9,6 +9,7 @@ * published by the Free Software Foundation. */ #include <linux/blkdev.h> +#include <linux/backing-dev.h> /* constant macro */ #define NULL_SEGNO ((unsigned int)(~0)) @@ -163,6 +164,7 @@ struct seg_entry { */ unsigned short ckpt_valid_blocks; unsigned char *ckpt_valid_map; + unsigned char *discard_map; unsigned char type; /* segment type like CURSEG_XXX_TYPE */ unsigned long long mtime; /* modification time of the segment */ }; @@ -713,7 +715,7 @@ static inline unsigned int max_hw_blocks(struct f2fs_sb_info *sbi) */ static inline int nr_pages_to_skip(struct f2fs_sb_info *sbi, int type) { - if (sbi->sb->s_bdi->dirty_exceeded) + if (sbi->sb->s_bdi->wb.dirty_exceeded) return 0; if (type == DATA) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index b2dd1b01f..a06b0b46f 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -258,6 +258,7 @@ static void init_once(void *foo) static int parse_options(struct super_block *sb, char *options) { struct f2fs_sb_info *sbi = F2FS_SB(sb); + struct request_queue *q; substring_t args[MAX_OPT_ARGS]; char *p, *name; int arg = 0; @@ -302,7 +303,14 @@ static int parse_options(struct super_block *sb, char *options) return -EINVAL; break; case Opt_discard: - set_opt(sbi, DISCARD); + q = bdev_get_queue(sb->s_bdev); + if (blk_queue_discard(q)) { + set_opt(sbi, DISCARD); + } else { + f2fs_msg(sb, KERN_WARNING, + "mounting with \"discard\" option, but " + "the device does not support discard"); + } break; case Opt_noheap: set_opt(sbi, NOHEAP); @@ -416,6 +424,9 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) /* Will be used by directory only */ fi->i_dir_level = F2FS_SB(sb)->dir_level; +#ifdef CONFIG_F2FS_FS_ENCRYPTION + fi->i_crypt_info = NULL; +#endif return &fi->vfs_inode; } @@ -428,8 +439,31 @@ static int f2fs_drop_inode(struct inode *inode) * - f2fs_gc -> iput -> evict * - inode_wait_for_writeback(inode) */ - if (!inode_unhashed(inode) && inode->i_state & I_SYNC) + if (!inode_unhashed(inode) && inode->i_state & I_SYNC) { + if (!inode->i_nlink && !is_bad_inode(inode)) { + spin_unlock(&inode->i_lock); + + /* some remained atomic pages should discarded */ + if (f2fs_is_atomic_file(inode)) + commit_inmem_pages(inode, true); + + sb_start_intwrite(inode->i_sb); + i_size_write(inode, 0); + + if (F2FS_HAS_BLOCKS(inode)) + f2fs_truncate(inode); + + sb_end_intwrite(inode->i_sb); + +#ifdef CONFIG_F2FS_FS_ENCRYPTION + if (F2FS_I(inode)->i_crypt_info) + f2fs_free_encryption_info(inode, + F2FS_I(inode)->i_crypt_info); +#endif + spin_lock(&inode->i_lock); + } return 0; + } return generic_drop_inode(inode); } @@ -520,7 +554,7 @@ int f2fs_sync_fs(struct super_block *sb, int sync) } else { f2fs_balance_fs(sbi); } - f2fs_trace_ios(NULL, NULL, 1); + f2fs_trace_ios(NULL, 1); return 0; } @@ -658,6 +692,22 @@ static const struct file_operations f2fs_seq_segment_info_fops = { .release = single_release, }; +static void default_options(struct f2fs_sb_info *sbi) +{ + /* init some FS parameters */ + sbi->active_logs = NR_CURSEG_TYPE; + + set_opt(sbi, BG_GC); + set_opt(sbi, INLINE_DATA); + +#ifdef CONFIG_F2FS_FS_XATTR + set_opt(sbi, XATTR_USER); +#endif +#ifdef CONFIG_F2FS_FS_POSIX_ACL + set_opt(sbi, POSIX_ACL); +#endif +} + static int f2fs_remount(struct super_block *sb, int *flags, char *data) { struct f2fs_sb_info *sbi = F2FS_SB(sb); @@ -676,7 +726,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) active_logs = sbi->active_logs; sbi->mount_opt.opt = 0; - sbi->active_logs = NR_CURSEG_TYPE; + default_options(sbi); /* parse mount options */ err = parse_options(sb, data); @@ -929,29 +979,36 @@ static void init_sb_info(struct f2fs_sb_info *sbi) */ static int read_raw_super_block(struct super_block *sb, struct f2fs_super_block **raw_super, - struct buffer_head **raw_super_buf) + struct buffer_head **raw_super_buf, + int *recovery) { int block = 0; + struct buffer_head *buffer; + struct f2fs_super_block *super; + int err = 0; retry: - *raw_super_buf = sb_bread(sb, block); - if (!*raw_super_buf) { + buffer = sb_bread(sb, block); + if (!buffer) { + *recovery = 1; f2fs_msg(sb, KERN_ERR, "Unable to read %dth superblock", block + 1); if (block == 0) { block++; goto retry; } else { - return -EIO; + err = -EIO; + goto out; } } - *raw_super = (struct f2fs_super_block *) - ((char *)(*raw_super_buf)->b_data + F2FS_SUPER_OFFSET); + super = (struct f2fs_super_block *) + ((char *)(buffer)->b_data + F2FS_SUPER_OFFSET); /* sanity checking of raw super */ - if (sanity_check_raw_super(sb, *raw_super)) { - brelse(*raw_super_buf); + if (sanity_check_raw_super(sb, super)) { + brelse(buffer); + *recovery = 1; f2fs_msg(sb, KERN_ERR, "Can't find valid F2FS filesystem in %dth superblock", block + 1); @@ -959,25 +1016,76 @@ retry: block++; goto retry; } else { - return -EINVAL; + err = -EINVAL; + goto out; } } + if (!*raw_super) { + *raw_super_buf = buffer; + *raw_super = super; + } else { + /* already have a valid superblock */ + brelse(buffer); + } + + /* check the validity of the second superblock */ + if (block == 0) { + block++; + goto retry; + } + +out: + /* No valid superblock */ + if (!*raw_super) + return err; + return 0; } +int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover) +{ + struct buffer_head *sbh = sbi->raw_super_buf; + sector_t block = sbh->b_blocknr; + int err; + + /* write back-up superblock first */ + sbh->b_blocknr = block ? 0 : 1; + mark_buffer_dirty(sbh); + err = sync_dirty_buffer(sbh); + + sbh->b_blocknr = block; + + /* if we are in recovery path, skip writing valid superblock */ + if (recover || err) + goto out; + + /* write current valid superblock */ + mark_buffer_dirty(sbh); + err = sync_dirty_buffer(sbh); +out: + clear_buffer_write_io_error(sbh); + set_buffer_uptodate(sbh); + return err; +} + static int f2fs_fill_super(struct super_block *sb, void *data, int silent) { struct f2fs_sb_info *sbi; - struct f2fs_super_block *raw_super = NULL; + struct f2fs_super_block *raw_super; struct buffer_head *raw_super_buf; struct inode *root; - long err = -EINVAL; + long err; bool retry = true, need_fsck = false; char *options = NULL; - int i; + int recovery, i; try_onemore: + err = -EINVAL; + raw_super = NULL; + raw_super_buf = NULL; + recovery = 0; + /* allocate memory for f2fs-specific super block info */ sbi = kzalloc(sizeof(struct f2fs_sb_info), GFP_KERNEL); if (!sbi) @@ -989,23 +1097,12 @@ try_onemore: goto free_sbi; } - err = read_raw_super_block(sb, &raw_super, &raw_super_buf); + err = read_raw_super_block(sb, &raw_super, &raw_super_buf, &recovery); if (err) goto free_sbi; sb->s_fs_info = sbi; - /* init some FS parameters */ - sbi->active_logs = NR_CURSEG_TYPE; - - set_opt(sbi, BG_GC); - set_opt(sbi, INLINE_DATA); - -#ifdef CONFIG_F2FS_FS_XATTR - set_opt(sbi, XATTR_USER); -#endif -#ifdef CONFIG_F2FS_FS_POSIX_ACL - set_opt(sbi, POSIX_ACL); -#endif + default_options(sbi); /* parse mount options */ options = kstrdup((const char *)data, GFP_KERNEL); if (data && !options) { @@ -1148,14 +1245,6 @@ try_onemore: proc_create_data("segment_info", S_IRUGO, sbi->s_proc, &f2fs_seq_segment_info_fops, sb); - if (test_opt(sbi, DISCARD)) { - struct request_queue *q = bdev_get_queue(sb->s_bdev); - if (!blk_queue_discard(q)) - f2fs_msg(sb, KERN_WARNING, - "mounting with \"discard\" option, but " - "the device does not support discard"); - } - sbi->s_kobj.kset = f2fs_kset; init_completion(&sbi->s_kobj_unregister); err = kobject_init_and_add(&sbi->s_kobj, &f2fs_ktype, NULL, @@ -1198,6 +1287,13 @@ try_onemore: goto free_kobj; } kfree(options); + + /* recover broken superblock */ + if (recovery && !f2fs_readonly(sb) && !bdev_read_only(sb->s_bdev)) { + f2fs_msg(sb, KERN_INFO, "Recover invalid superblock"); + f2fs_commit_super(sbi, true); + } + return 0; free_kobj: @@ -1305,13 +1401,18 @@ static int __init init_f2fs_fs(void) err = -ENOMEM; goto free_extent_cache; } - err = register_filesystem(&f2fs_fs_type); + err = f2fs_init_crypto(); if (err) goto free_kset; + err = register_filesystem(&f2fs_fs_type); + if (err) + goto free_crypto; f2fs_create_root_stats(); f2fs_proc_root = proc_mkdir("fs/f2fs", NULL); return 0; +free_crypto: + f2fs_exit_crypto(); free_kset: kset_unregister(f2fs_kset); free_extent_cache: @@ -1333,6 +1434,7 @@ static void __exit exit_f2fs_fs(void) remove_proc_entry("fs/f2fs", NULL); f2fs_destroy_root_stats(); unregister_filesystem(&f2fs_fs_type); + f2fs_exit_crypto(); destroy_extent_cache(); destroy_checkpoint_caches(); destroy_segment_manager_caches(); diff --git a/fs/f2fs/trace.c b/fs/f2fs/trace.c index 875aa8179..145fb659a 100644 --- a/fs/f2fs/trace.c +++ b/fs/f2fs/trace.c @@ -80,7 +80,7 @@ out: radix_tree_preload_end(); } -void f2fs_trace_ios(struct page *page, struct f2fs_io_info *fio, int flush) +void f2fs_trace_ios(struct f2fs_io_info *fio, int flush) { struct inode *inode; pid_t pid; @@ -91,8 +91,8 @@ void f2fs_trace_ios(struct page *page, struct f2fs_io_info *fio, int flush) return; } - inode = page->mapping->host; - pid = page_private(page); + inode = fio->page->mapping->host; + pid = page_private(fio->page); major = MAJOR(inode->i_sb->s_dev); minor = MINOR(inode->i_sb->s_dev); diff --git a/fs/f2fs/trace.h b/fs/f2fs/trace.h index 1041dbeb5..67db24ac1 100644 --- a/fs/f2fs/trace.h +++ b/fs/f2fs/trace.h @@ -33,12 +33,12 @@ struct last_io_info { }; extern void f2fs_trace_pid(struct page *); -extern void f2fs_trace_ios(struct page *, struct f2fs_io_info *, int); +extern void f2fs_trace_ios(struct f2fs_io_info *, int); extern void f2fs_build_trace_ios(void); extern void f2fs_destroy_trace_ios(void); #else #define f2fs_trace_pid(p) -#define f2fs_trace_ios(p, i, n) +#define f2fs_trace_ios(i, n) #define f2fs_build_trace_ios() #define f2fs_destroy_trace_ios() diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 9757f65a0..07449b980 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -584,6 +584,9 @@ static int __f2fs_setxattr(struct inode *inode, int index, inode->i_ctime = CURRENT_TIME; clear_inode_flag(fi, FI_ACL_MODE); } + if (index == F2FS_XATTR_INDEX_ENCRYPTION && + !strcmp(name, F2FS_XATTR_NAME_ENCRYPTION_CONTEXT)) + f2fs_set_encrypted_inode(inode); if (ipage) update_inode(inode, ipage); diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h index 969d792ca..71a7100d5 100644 --- a/fs/f2fs/xattr.h +++ b/fs/f2fs/xattr.h @@ -35,6 +35,10 @@ #define F2FS_XATTR_INDEX_LUSTRE 5 #define F2FS_XATTR_INDEX_SECURITY 6 #define F2FS_XATTR_INDEX_ADVISE 7 +/* Should be same as EXT4_XATTR_INDEX_ENCRYPTION */ +#define F2FS_XATTR_INDEX_ENCRYPTION 9 + +#define F2FS_XATTR_NAME_ENCRYPTION_CONTEXT "c" struct f2fs_xattr_header { __le32 h_magic; /* magic number for identification */ diff --git a/fs/fat/file.c b/fs/fat/file.c index 442d50a0e..a08f10399 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -11,6 +11,7 @@ #include <linux/compat.h> #include <linux/mount.h> #include <linux/blkdev.h> +#include <linux/backing-dev.h> #include <linux/fsnotify.h> #include <linux/security.h> #include "fat.h" diff --git a/fs/fat/inode.c b/fs/fat/inode.c index c06774658..509411dd3 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -18,6 +18,7 @@ #include <linux/parser.h> #include <linux/uio.h> #include <linux/blkdev.h> +#include <linux/backing-dev.h> #include <asm/unaligned.h> #include "fat.h" @@ -147,6 +147,13 @@ static int expand_fdtable(struct files_struct *files, int nr) spin_unlock(&files->file_lock); new_fdt = alloc_fdtable(nr); + + /* make sure all __fd_install() have seen resize_in_progress + * or have finished their rcu_read_lock_sched() section. + */ + if (atomic_read(&files->count) > 1) + synchronize_sched(); + spin_lock(&files->file_lock); if (!new_fdt) return -ENOMEM; @@ -158,21 +165,14 @@ static int expand_fdtable(struct files_struct *files, int nr) __free_fdtable(new_fdt); return -EMFILE; } - /* - * Check again since another task may have expanded the fd table while - * we dropped the lock - */ cur_fdt = files_fdtable(files); - if (nr >= cur_fdt->max_fds) { - /* Continue as planned */ - copy_fdtable(new_fdt, cur_fdt); - rcu_assign_pointer(files->fdt, new_fdt); - if (cur_fdt != &files->fdtab) - call_rcu(&cur_fdt->rcu, free_fdtable_rcu); - } else { - /* Somebody else expanded, so undo our attempt */ - __free_fdtable(new_fdt); - } + BUG_ON(nr < cur_fdt->max_fds); + copy_fdtable(new_fdt, cur_fdt); + rcu_assign_pointer(files->fdt, new_fdt); + if (cur_fdt != &files->fdtab) + call_rcu(&cur_fdt->rcu, free_fdtable_rcu); + /* coupled with smp_rmb() in __fd_install() */ + smp_wmb(); return 1; } @@ -185,21 +185,38 @@ static int expand_fdtable(struct files_struct *files, int nr) * The files->file_lock should be held on entry, and will be held on exit. */ static int expand_files(struct files_struct *files, int nr) + __releases(files->file_lock) + __acquires(files->file_lock) { struct fdtable *fdt; + int expanded = 0; +repeat: fdt = files_fdtable(files); /* Do we need to expand? */ if (nr < fdt->max_fds) - return 0; + return expanded; /* Can we expand? */ if (nr >= sysctl_nr_open) return -EMFILE; + if (unlikely(files->resize_in_progress)) { + spin_unlock(&files->file_lock); + expanded = 1; + wait_event(files->resize_wait, !files->resize_in_progress); + spin_lock(&files->file_lock); + goto repeat; + } + /* All good, so we try */ - return expand_fdtable(files, nr); + files->resize_in_progress = true; + expanded = expand_fdtable(files, nr); + files->resize_in_progress = false; + + wake_up_all(&files->resize_wait); + return expanded; } static inline void __set_close_on_exec(int fd, struct fdtable *fdt) @@ -256,6 +273,8 @@ struct files_struct *dup_fd(struct files_struct *oldf, int *errorp) atomic_set(&newf->count, 1); spin_lock_init(&newf->file_lock); + newf->resize_in_progress = false; + init_waitqueue_head(&newf->resize_wait); newf->next_fd = 0; new_fdt = &newf->fdtab; new_fdt->max_fds = NR_OPEN_DEFAULT; @@ -553,11 +572,21 @@ void __fd_install(struct files_struct *files, unsigned int fd, struct file *file) { struct fdtable *fdt; - spin_lock(&files->file_lock); - fdt = files_fdtable(files); + + might_sleep(); + rcu_read_lock_sched(); + + while (unlikely(files->resize_in_progress)) { + rcu_read_unlock_sched(); + wait_event(files->resize_wait, !files->resize_in_progress); + rcu_read_lock_sched(); + } + /* coupled with smp_wmb() in expand_fdtable() */ + smp_rmb(); + fdt = rcu_dereference_sched(files->fdt); BUG_ON(fdt->fd[fd] != NULL); rcu_assign_pointer(fdt->fd[fd], file); - spin_unlock(&files->file_lock); + rcu_read_unlock_sched(); } void fd_install(unsigned int fd, struct file *file) @@ -635,11 +664,17 @@ static struct file *__fget(unsigned int fd, fmode_t mask) struct file *file; rcu_read_lock(); +loop: file = fcheck_files(files, fd); if (file) { - /* File object ref couldn't be taken */ - if ((file->f_mode & mask) || !get_file_rcu(file)) + /* File object ref couldn't be taken. + * dup2() atomicity guarantee is the reason + * we loop to catch the new file (or NULL pointer) + */ + if (file->f_mode & mask) file = NULL; + else if (!get_file_rcu(file)) + goto loop; } rcu_read_unlock(); diff --git a/fs/file_table.c b/fs/file_table.c index 3cea02778..ad17e05eb 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -20,12 +20,12 @@ #include <linux/cdev.h> #include <linux/fsnotify.h> #include <linux/sysctl.h> -#include <linux/lglock.h> #include <linux/percpu_counter.h> #include <linux/percpu.h> #include <linux/hardirq.h> #include <linux/task_work.h> #include <linux/ima.h> +#include <linux/swap.h> #include <linux/atomic.h> @@ -147,7 +147,6 @@ over: } return ERR_PTR(-ENFILE); } -EXPORT_SYMBOL(get_empty_filp); /** * alloc_file - allocate and initialize a 'struct file' @@ -309,21 +308,25 @@ void put_filp(struct file *file) file_free(file); } } -EXPORT_SYMBOL(put_filp); -void __init files_init(unsigned long mempages) +void __init files_init(void) { - unsigned long n; - filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); + percpu_counter_init(&nr_files, 0, GFP_KERNEL); +} - /* - * One file with associated inode and dcache is very roughly 1K. - * Per default don't use more than 10% of our memory for files. - */ +/* + * One file with associated inode and dcache is very roughly 1K. Per default + * do not use more than 10% of our memory for files. + */ +void __init files_maxfiles_init(void) +{ + unsigned long n; + unsigned long memreserve = (totalram_pages - nr_free_pages()) * 3/2; + + memreserve = min(memreserve, totalram_pages - 1); + n = ((totalram_pages - memreserve) * (PAGE_SIZE / 1024)) / 10; - n = (mempages * (PAGE_SIZE / 1024)) / 10; files_stat.max_files = max_t(unsigned long, n, NR_FILE); - percpu_counter_init(&nr_files, 0, GFP_KERNEL); } diff --git a/fs/freevxfs/vxfs_extern.h b/fs/freevxfs/vxfs_extern.h index 881aa3d21..e3dcb4467 100644 --- a/fs/freevxfs/vxfs_extern.h +++ b/fs/freevxfs/vxfs_extern.h @@ -50,9 +50,6 @@ extern daddr_t vxfs_bmap1(struct inode *, long); /* vxfs_fshead.c */ extern int vxfs_read_fshead(struct super_block *); -/* vxfs_immed.c */ -extern const struct inode_operations vxfs_immed_symlink_iops; - /* vxfs_inode.c */ extern const struct address_space_operations vxfs_immed_aops; extern struct kmem_cache *vxfs_inode_cachep; diff --git a/fs/freevxfs/vxfs_immed.c b/fs/freevxfs/vxfs_immed.c index 8b9229e2c..cb84f0fcc 100644 --- a/fs/freevxfs/vxfs_immed.c +++ b/fs/freevxfs/vxfs_immed.c @@ -32,29 +32,15 @@ */ #include <linux/fs.h> #include <linux/pagemap.h> -#include <linux/namei.h> #include "vxfs.h" #include "vxfs_extern.h" #include "vxfs_inode.h" -static void * vxfs_immed_follow_link(struct dentry *, struct nameidata *); - static int vxfs_immed_readpage(struct file *, struct page *); /* - * Inode operations for immed symlinks. - * - * Unliked all other operations we do not go through the pagecache, - * but do all work directly on the inode. - */ -const struct inode_operations vxfs_immed_symlink_iops = { - .readlink = generic_readlink, - .follow_link = vxfs_immed_follow_link, -}; - -/* * Address space operations for immed files and directories. */ const struct address_space_operations vxfs_immed_aops = { @@ -62,26 +48,6 @@ const struct address_space_operations vxfs_immed_aops = { }; /** - * vxfs_immed_follow_link - follow immed symlink - * @dp: dentry for the link - * @np: pathname lookup data for the current path walk - * - * Description: - * vxfs_immed_follow_link restarts the pathname lookup with - * the data obtained from @dp. - * - * Returns: - * Zero on success, else a negative error code. - */ -static void * -vxfs_immed_follow_link(struct dentry *dp, struct nameidata *np) -{ - struct vxfs_inode_info *vip = VXFS_INO(d_inode(dp)); - nd_set_link(np, vip->vii_immed.vi_immed); - return NULL; -} - -/** * vxfs_immed_readpage - read part of an immed inode into pagecache * @file: file context (unused) * @page: page frame to fill in. diff --git a/fs/freevxfs/vxfs_inode.c b/fs/freevxfs/vxfs_inode.c index 363e3ae25..ef73ed674 100644 --- a/fs/freevxfs/vxfs_inode.c +++ b/fs/freevxfs/vxfs_inode.c @@ -35,6 +35,7 @@ #include <linux/pagemap.h> #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/namei.h> #include "vxfs.h" #include "vxfs_inode.h" @@ -327,8 +328,10 @@ vxfs_iget(struct super_block *sbp, ino_t ino) ip->i_op = &page_symlink_inode_operations; ip->i_mapping->a_ops = &vxfs_aops; } else { - ip->i_op = &vxfs_immed_symlink_iops; - vip->vii_immed.vi_immed[ip->i_size] = '\0'; + ip->i_op = &simple_symlink_inode_operations; + ip->i_link = vip->vii_immed.vi_immed; + nd_terminate_link(ip->i_link, ip->i_size, + sizeof(vip->vii_immed.vi_immed) - 1); } } else init_special_inode(ip, ip->i_mode, old_decode_dev(vip->vii_rdev)); diff --git a/fs/freevxfs/vxfs_lookup.c b/fs/freevxfs/vxfs_lookup.c index 99c7f0a37..484b32d32 100644 --- a/fs/freevxfs/vxfs_lookup.c +++ b/fs/freevxfs/vxfs_lookup.c @@ -61,13 +61,6 @@ const struct file_operations vxfs_dir_operations = { .iterate = vxfs_readdir, }; - -static inline u_long -dir_pages(struct inode *inode) -{ - return (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; -} - static inline u_long dir_blocks(struct inode *ip) { diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 32a8bbd7a..5fa588e93 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -27,6 +27,7 @@ #include <linux/backing-dev.h> #include <linux/tracepoint.h> #include <linux/device.h> +#include <linux/memcontrol.h> #include "internal.h" /* @@ -34,6 +35,10 @@ */ #define MIN_WRITEBACK_PAGES (4096UL >> (PAGE_CACHE_SHIFT - 10)) +struct wb_completion { + atomic_t cnt; +}; + /* * Passed into wb_writeback(), essentially a subset of writeback_control */ @@ -47,13 +52,29 @@ struct wb_writeback_work { unsigned int range_cyclic:1; unsigned int for_background:1; unsigned int for_sync:1; /* sync(2) WB_SYNC_ALL writeback */ + unsigned int auto_free:1; /* free on completion */ + unsigned int single_wait:1; + unsigned int single_done:1; enum wb_reason reason; /* why was writeback initiated? */ struct list_head list; /* pending work list */ - struct completion *done; /* set if the caller waits */ + struct wb_completion *done; /* set if the caller waits */ }; /* + * If one wants to wait for one or more wb_writeback_works, each work's + * ->done should be set to a wb_completion defined using the following + * macro. Once all work items are issued with wb_queue_work(), the caller + * can wait for the completion of all using wb_wait_for_completion(). Work + * items which are waited upon aren't freed automatically on completion. + */ +#define DEFINE_WB_COMPLETION_ONSTACK(cmpl) \ + struct wb_completion cmpl = { \ + .cnt = ATOMIC_INIT(1), \ + } + + +/* * If an inode is constantly having its pages dirtied, but then the * updates stop dirtytime_expire_interval seconds in the past, it's * possible for the worst case time between when an inode has its @@ -65,35 +86,6 @@ struct wb_writeback_work { */ unsigned int dirtytime_expire_interval = 12 * 60 * 60; -/** - * writeback_in_progress - determine whether there is writeback in progress - * @bdi: the device's backing_dev_info structure. - * - * Determine whether there is writeback waiting to be handled against a - * backing device. - */ -int writeback_in_progress(struct backing_dev_info *bdi) -{ - return test_bit(BDI_writeback_running, &bdi->state); -} -EXPORT_SYMBOL(writeback_in_progress); - -struct backing_dev_info *inode_to_bdi(struct inode *inode) -{ - struct super_block *sb; - - if (!inode) - return &noop_backing_dev_info; - - sb = inode->i_sb; -#ifdef CONFIG_BLOCK - if (sb_is_blkdev_sb(sb)) - return blk_get_backing_dev_info(I_BDEV(inode)); -#endif - return sb->s_bdi; -} -EXPORT_SYMBOL_GPL(inode_to_bdi); - static inline struct inode *wb_inode(struct list_head *head) { return list_entry(head, struct inode, i_wb_list); @@ -109,45 +101,831 @@ static inline struct inode *wb_inode(struct list_head *head) EXPORT_TRACEPOINT_SYMBOL_GPL(wbc_writepage); -static void bdi_wakeup_thread(struct backing_dev_info *bdi) +static bool wb_io_lists_populated(struct bdi_writeback *wb) +{ + if (wb_has_dirty_io(wb)) { + return false; + } else { + set_bit(WB_has_dirty_io, &wb->state); + WARN_ON_ONCE(!wb->avg_write_bandwidth); + atomic_long_add(wb->avg_write_bandwidth, + &wb->bdi->tot_write_bandwidth); + return true; + } +} + +static void wb_io_lists_depopulated(struct bdi_writeback *wb) { - spin_lock_bh(&bdi->wb_lock); - if (test_bit(BDI_registered, &bdi->state)) - mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0); - spin_unlock_bh(&bdi->wb_lock); + if (wb_has_dirty_io(wb) && list_empty(&wb->b_dirty) && + list_empty(&wb->b_io) && list_empty(&wb->b_more_io)) { + clear_bit(WB_has_dirty_io, &wb->state); + WARN_ON_ONCE(atomic_long_sub_return(wb->avg_write_bandwidth, + &wb->bdi->tot_write_bandwidth) < 0); + } } -static void bdi_queue_work(struct backing_dev_info *bdi, - struct wb_writeback_work *work) +/** + * inode_wb_list_move_locked - move an inode onto a bdi_writeback IO list + * @inode: inode to be moved + * @wb: target bdi_writeback + * @head: one of @wb->b_{dirty|io|more_io} + * + * Move @inode->i_wb_list to @list of @wb and set %WB_has_dirty_io. + * Returns %true if @inode is the first occupant of the !dirty_time IO + * lists; otherwise, %false. + */ +static bool inode_wb_list_move_locked(struct inode *inode, + struct bdi_writeback *wb, + struct list_head *head) { - trace_writeback_queue(bdi, work); + assert_spin_locked(&wb->list_lock); + + list_move(&inode->i_wb_list, head); - spin_lock_bh(&bdi->wb_lock); - if (!test_bit(BDI_registered, &bdi->state)) { - if (work->done) - complete(work->done); + /* dirty_time doesn't count as dirty_io until expiration */ + if (head != &wb->b_dirty_time) + return wb_io_lists_populated(wb); + + wb_io_lists_depopulated(wb); + return false; +} + +/** + * inode_wb_list_del_locked - remove an inode from its bdi_writeback IO list + * @inode: inode to be removed + * @wb: bdi_writeback @inode is being removed from + * + * Remove @inode which may be on one of @wb->b_{dirty|io|more_io} lists and + * clear %WB_has_dirty_io if all are empty afterwards. + */ +static void inode_wb_list_del_locked(struct inode *inode, + struct bdi_writeback *wb) +{ + assert_spin_locked(&wb->list_lock); + + list_del_init(&inode->i_wb_list); + wb_io_lists_depopulated(wb); +} + +static void wb_wakeup(struct bdi_writeback *wb) +{ + spin_lock_bh(&wb->work_lock); + if (test_bit(WB_registered, &wb->state)) + mod_delayed_work(bdi_wq, &wb->dwork, 0); + spin_unlock_bh(&wb->work_lock); +} + +static void wb_queue_work(struct bdi_writeback *wb, + struct wb_writeback_work *work) +{ + trace_writeback_queue(wb->bdi, work); + + spin_lock_bh(&wb->work_lock); + if (!test_bit(WB_registered, &wb->state)) { + if (work->single_wait) + work->single_done = 1; goto out_unlock; } - list_add_tail(&work->list, &bdi->work_list); - mod_delayed_work(bdi_wq, &bdi->wb.dwork, 0); + if (work->done) + atomic_inc(&work->done->cnt); + list_add_tail(&work->list, &wb->work_list); + mod_delayed_work(bdi_wq, &wb->dwork, 0); out_unlock: - spin_unlock_bh(&bdi->wb_lock); + spin_unlock_bh(&wb->work_lock); +} + +/** + * wb_wait_for_completion - wait for completion of bdi_writeback_works + * @bdi: bdi work items were issued to + * @done: target wb_completion + * + * Wait for one or more work items issued to @bdi with their ->done field + * set to @done, which should have been defined with + * DEFINE_WB_COMPLETION_ONSTACK(). This function returns after all such + * work items are completed. Work items which are waited upon aren't freed + * automatically on completion. + */ +static void wb_wait_for_completion(struct backing_dev_info *bdi, + struct wb_completion *done) +{ + atomic_dec(&done->cnt); /* put down the initial count */ + wait_event(bdi->wb_waitq, !atomic_read(&done->cnt)); +} + +#ifdef CONFIG_CGROUP_WRITEBACK + +/* parameters for foreign inode detection, see wb_detach_inode() */ +#define WB_FRN_TIME_SHIFT 13 /* 1s = 2^13, upto 8 secs w/ 16bit */ +#define WB_FRN_TIME_AVG_SHIFT 3 /* avg = avg * 7/8 + new * 1/8 */ +#define WB_FRN_TIME_CUT_DIV 2 /* ignore rounds < avg / 2 */ +#define WB_FRN_TIME_PERIOD (2 * (1 << WB_FRN_TIME_SHIFT)) /* 2s */ + +#define WB_FRN_HIST_SLOTS 16 /* inode->i_wb_frn_history is 16bit */ +#define WB_FRN_HIST_UNIT (WB_FRN_TIME_PERIOD / WB_FRN_HIST_SLOTS) + /* each slot's duration is 2s / 16 */ +#define WB_FRN_HIST_THR_SLOTS (WB_FRN_HIST_SLOTS / 2) + /* if foreign slots >= 8, switch */ +#define WB_FRN_HIST_MAX_SLOTS (WB_FRN_HIST_THR_SLOTS / 2 + 1) + /* one round can affect upto 5 slots */ + +void __inode_attach_wb(struct inode *inode, struct page *page) +{ + struct backing_dev_info *bdi = inode_to_bdi(inode); + struct bdi_writeback *wb = NULL; + + if (inode_cgwb_enabled(inode)) { + struct cgroup_subsys_state *memcg_css; + + if (page) { + memcg_css = mem_cgroup_css_from_page(page); + wb = wb_get_create(bdi, memcg_css, GFP_ATOMIC); + } else { + /* must pin memcg_css, see wb_get_create() */ + memcg_css = task_get_css(current, memory_cgrp_id); + wb = wb_get_create(bdi, memcg_css, GFP_ATOMIC); + css_put(memcg_css); + } + } + + if (!wb) + wb = &bdi->wb; + + /* + * There may be multiple instances of this function racing to + * update the same inode. Use cmpxchg() to tell the winner. + */ + if (unlikely(cmpxchg(&inode->i_wb, NULL, wb))) + wb_put(wb); +} + +/** + * locked_inode_to_wb_and_lock_list - determine a locked inode's wb and lock it + * @inode: inode of interest with i_lock held + * + * Returns @inode's wb with its list_lock held. @inode->i_lock must be + * held on entry and is released on return. The returned wb is guaranteed + * to stay @inode's associated wb until its list_lock is released. + */ +static struct bdi_writeback * +locked_inode_to_wb_and_lock_list(struct inode *inode) + __releases(&inode->i_lock) + __acquires(&wb->list_lock) +{ + while (true) { + struct bdi_writeback *wb = inode_to_wb(inode); + + /* + * inode_to_wb() association is protected by both + * @inode->i_lock and @wb->list_lock but list_lock nests + * outside i_lock. Drop i_lock and verify that the + * association hasn't changed after acquiring list_lock. + */ + wb_get(wb); + spin_unlock(&inode->i_lock); + spin_lock(&wb->list_lock); + wb_put(wb); /* not gonna deref it anymore */ + + /* i_wb may have changed inbetween, can't use inode_to_wb() */ + if (likely(wb == inode->i_wb)) + return wb; /* @inode already has ref */ + + spin_unlock(&wb->list_lock); + cpu_relax(); + spin_lock(&inode->i_lock); + } +} + +/** + * inode_to_wb_and_lock_list - determine an inode's wb and lock it + * @inode: inode of interest + * + * Same as locked_inode_to_wb_and_lock_list() but @inode->i_lock isn't held + * on entry. + */ +static struct bdi_writeback *inode_to_wb_and_lock_list(struct inode *inode) + __acquires(&wb->list_lock) +{ + spin_lock(&inode->i_lock); + return locked_inode_to_wb_and_lock_list(inode); +} + +struct inode_switch_wbs_context { + struct inode *inode; + struct bdi_writeback *new_wb; + + struct rcu_head rcu_head; + struct work_struct work; +}; + +static void inode_switch_wbs_work_fn(struct work_struct *work) +{ + struct inode_switch_wbs_context *isw = + container_of(work, struct inode_switch_wbs_context, work); + struct inode *inode = isw->inode; + struct address_space *mapping = inode->i_mapping; + struct bdi_writeback *old_wb = inode->i_wb; + struct bdi_writeback *new_wb = isw->new_wb; + struct radix_tree_iter iter; + bool switched = false; + void **slot; + + /* + * By the time control reaches here, RCU grace period has passed + * since I_WB_SWITCH assertion and all wb stat update transactions + * between unlocked_inode_to_wb_begin/end() are guaranteed to be + * synchronizing against mapping->tree_lock. + * + * Grabbing old_wb->list_lock, inode->i_lock and mapping->tree_lock + * gives us exclusion against all wb related operations on @inode + * including IO list manipulations and stat updates. + */ + if (old_wb < new_wb) { + spin_lock(&old_wb->list_lock); + spin_lock_nested(&new_wb->list_lock, SINGLE_DEPTH_NESTING); + } else { + spin_lock(&new_wb->list_lock); + spin_lock_nested(&old_wb->list_lock, SINGLE_DEPTH_NESTING); + } + spin_lock(&inode->i_lock); + spin_lock_irq(&mapping->tree_lock); + + /* + * Once I_FREEING is visible under i_lock, the eviction path owns + * the inode and we shouldn't modify ->i_wb_list. + */ + if (unlikely(inode->i_state & I_FREEING)) + goto skip_switch; + + /* + * Count and transfer stats. Note that PAGECACHE_TAG_DIRTY points + * to possibly dirty pages while PAGECACHE_TAG_WRITEBACK points to + * pages actually under underwriteback. + */ + radix_tree_for_each_tagged(slot, &mapping->page_tree, &iter, 0, + PAGECACHE_TAG_DIRTY) { + struct page *page = radix_tree_deref_slot_protected(slot, + &mapping->tree_lock); + if (likely(page) && PageDirty(page)) { + __dec_wb_stat(old_wb, WB_RECLAIMABLE); + __inc_wb_stat(new_wb, WB_RECLAIMABLE); + } + } + + radix_tree_for_each_tagged(slot, &mapping->page_tree, &iter, 0, + PAGECACHE_TAG_WRITEBACK) { + struct page *page = radix_tree_deref_slot_protected(slot, + &mapping->tree_lock); + if (likely(page)) { + WARN_ON_ONCE(!PageWriteback(page)); + __dec_wb_stat(old_wb, WB_WRITEBACK); + __inc_wb_stat(new_wb, WB_WRITEBACK); + } + } + + wb_get(new_wb); + + /* + * Transfer to @new_wb's IO list if necessary. The specific list + * @inode was on is ignored and the inode is put on ->b_dirty which + * is always correct including from ->b_dirty_time. The transfer + * preserves @inode->dirtied_when ordering. + */ + if (!list_empty(&inode->i_wb_list)) { + struct inode *pos; + + inode_wb_list_del_locked(inode, old_wb); + inode->i_wb = new_wb; + list_for_each_entry(pos, &new_wb->b_dirty, i_wb_list) + if (time_after_eq(inode->dirtied_when, + pos->dirtied_when)) + break; + inode_wb_list_move_locked(inode, new_wb, pos->i_wb_list.prev); + } else { + inode->i_wb = new_wb; + } + + /* ->i_wb_frn updates may race wbc_detach_inode() but doesn't matter */ + inode->i_wb_frn_winner = 0; + inode->i_wb_frn_avg_time = 0; + inode->i_wb_frn_history = 0; + switched = true; +skip_switch: + /* + * Paired with load_acquire in unlocked_inode_to_wb_begin() and + * ensures that the new wb is visible if they see !I_WB_SWITCH. + */ + smp_store_release(&inode->i_state, inode->i_state & ~I_WB_SWITCH); + + spin_unlock_irq(&mapping->tree_lock); + spin_unlock(&inode->i_lock); + spin_unlock(&new_wb->list_lock); + spin_unlock(&old_wb->list_lock); + + if (switched) { + wb_wakeup(new_wb); + wb_put(old_wb); + } + wb_put(new_wb); + + iput(inode); + kfree(isw); } -static void -__bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages, - bool range_cyclic, enum wb_reason reason) +static void inode_switch_wbs_rcu_fn(struct rcu_head *rcu_head) +{ + struct inode_switch_wbs_context *isw = container_of(rcu_head, + struct inode_switch_wbs_context, rcu_head); + + /* needs to grab bh-unsafe locks, bounce to work item */ + INIT_WORK(&isw->work, inode_switch_wbs_work_fn); + schedule_work(&isw->work); +} + +/** + * inode_switch_wbs - change the wb association of an inode + * @inode: target inode + * @new_wb_id: ID of the new wb + * + * Switch @inode's wb association to the wb identified by @new_wb_id. The + * switching is performed asynchronously and may fail silently. + */ +static void inode_switch_wbs(struct inode *inode, int new_wb_id) +{ + struct backing_dev_info *bdi = inode_to_bdi(inode); + struct cgroup_subsys_state *memcg_css; + struct inode_switch_wbs_context *isw; + + /* noop if seems to be already in progress */ + if (inode->i_state & I_WB_SWITCH) + return; + + isw = kzalloc(sizeof(*isw), GFP_ATOMIC); + if (!isw) + return; + + /* find and pin the new wb */ + rcu_read_lock(); + memcg_css = css_from_id(new_wb_id, &memory_cgrp_subsys); + if (memcg_css) + isw->new_wb = wb_get_create(bdi, memcg_css, GFP_ATOMIC); + rcu_read_unlock(); + if (!isw->new_wb) + goto out_free; + + /* while holding I_WB_SWITCH, no one else can update the association */ + spin_lock(&inode->i_lock); + if (inode->i_state & (I_WB_SWITCH | I_FREEING) || + inode_to_wb(inode) == isw->new_wb) { + spin_unlock(&inode->i_lock); + goto out_free; + } + inode->i_state |= I_WB_SWITCH; + spin_unlock(&inode->i_lock); + + ihold(inode); + isw->inode = inode; + + /* + * In addition to synchronizing among switchers, I_WB_SWITCH tells + * the RCU protected stat update paths to grab the mapping's + * tree_lock so that stat transfer can synchronize against them. + * Let's continue after I_WB_SWITCH is guaranteed to be visible. + */ + call_rcu(&isw->rcu_head, inode_switch_wbs_rcu_fn); + return; + +out_free: + if (isw->new_wb) + wb_put(isw->new_wb); + kfree(isw); +} + +/** + * wbc_attach_and_unlock_inode - associate wbc with target inode and unlock it + * @wbc: writeback_control of interest + * @inode: target inode + * + * @inode is locked and about to be written back under the control of @wbc. + * Record @inode's writeback context into @wbc and unlock the i_lock. On + * writeback completion, wbc_detach_inode() should be called. This is used + * to track the cgroup writeback context. + */ +void wbc_attach_and_unlock_inode(struct writeback_control *wbc, + struct inode *inode) +{ + if (!inode_cgwb_enabled(inode)) { + spin_unlock(&inode->i_lock); + return; + } + + wbc->wb = inode_to_wb(inode); + wbc->inode = inode; + + wbc->wb_id = wbc->wb->memcg_css->id; + wbc->wb_lcand_id = inode->i_wb_frn_winner; + wbc->wb_tcand_id = 0; + wbc->wb_bytes = 0; + wbc->wb_lcand_bytes = 0; + wbc->wb_tcand_bytes = 0; + + wb_get(wbc->wb); + spin_unlock(&inode->i_lock); + + /* + * A dying wb indicates that the memcg-blkcg mapping has changed + * and a new wb is already serving the memcg. Switch immediately. + */ + if (unlikely(wb_dying(wbc->wb))) + inode_switch_wbs(inode, wbc->wb_id); +} + +/** + * wbc_detach_inode - disassociate wbc from inode and perform foreign detection + * @wbc: writeback_control of the just finished writeback + * + * To be called after a writeback attempt of an inode finishes and undoes + * wbc_attach_and_unlock_inode(). Can be called under any context. + * + * As concurrent write sharing of an inode is expected to be very rare and + * memcg only tracks page ownership on first-use basis severely confining + * the usefulness of such sharing, cgroup writeback tracks ownership + * per-inode. While the support for concurrent write sharing of an inode + * is deemed unnecessary, an inode being written to by different cgroups at + * different points in time is a lot more common, and, more importantly, + * charging only by first-use can too readily lead to grossly incorrect + * behaviors (single foreign page can lead to gigabytes of writeback to be + * incorrectly attributed). + * + * To resolve this issue, cgroup writeback detects the majority dirtier of + * an inode and transfers the ownership to it. To avoid unnnecessary + * oscillation, the detection mechanism keeps track of history and gives + * out the switch verdict only if the foreign usage pattern is stable over + * a certain amount of time and/or writeback attempts. + * + * On each writeback attempt, @wbc tries to detect the majority writer + * using Boyer-Moore majority vote algorithm. In addition to the byte + * count from the majority voting, it also counts the bytes written for the + * current wb and the last round's winner wb (max of last round's current + * wb, the winner from two rounds ago, and the last round's majority + * candidate). Keeping track of the historical winner helps the algorithm + * to semi-reliably detect the most active writer even when it's not the + * absolute majority. + * + * Once the winner of the round is determined, whether the winner is + * foreign or not and how much IO time the round consumed is recorded in + * inode->i_wb_frn_history. If the amount of recorded foreign IO time is + * over a certain threshold, the switch verdict is given. + */ +void wbc_detach_inode(struct writeback_control *wbc) +{ + struct bdi_writeback *wb = wbc->wb; + struct inode *inode = wbc->inode; + unsigned long avg_time, max_bytes, max_time; + u16 history; + int max_id; + + if (!wb) + return; + + history = inode->i_wb_frn_history; + avg_time = inode->i_wb_frn_avg_time; + + /* pick the winner of this round */ + if (wbc->wb_bytes >= wbc->wb_lcand_bytes && + wbc->wb_bytes >= wbc->wb_tcand_bytes) { + max_id = wbc->wb_id; + max_bytes = wbc->wb_bytes; + } else if (wbc->wb_lcand_bytes >= wbc->wb_tcand_bytes) { + max_id = wbc->wb_lcand_id; + max_bytes = wbc->wb_lcand_bytes; + } else { + max_id = wbc->wb_tcand_id; + max_bytes = wbc->wb_tcand_bytes; + } + + /* + * Calculate the amount of IO time the winner consumed and fold it + * into the running average kept per inode. If the consumed IO + * time is lower than avag / WB_FRN_TIME_CUT_DIV, ignore it for + * deciding whether to switch or not. This is to prevent one-off + * small dirtiers from skewing the verdict. + */ + max_time = DIV_ROUND_UP((max_bytes >> PAGE_SHIFT) << WB_FRN_TIME_SHIFT, + wb->avg_write_bandwidth); + if (avg_time) + avg_time += (max_time >> WB_FRN_TIME_AVG_SHIFT) - + (avg_time >> WB_FRN_TIME_AVG_SHIFT); + else + avg_time = max_time; /* immediate catch up on first run */ + + if (max_time >= avg_time / WB_FRN_TIME_CUT_DIV) { + int slots; + + /* + * The switch verdict is reached if foreign wb's consume + * more than a certain proportion of IO time in a + * WB_FRN_TIME_PERIOD. This is loosely tracked by 16 slot + * history mask where each bit represents one sixteenth of + * the period. Determine the number of slots to shift into + * history from @max_time. + */ + slots = min(DIV_ROUND_UP(max_time, WB_FRN_HIST_UNIT), + (unsigned long)WB_FRN_HIST_MAX_SLOTS); + history <<= slots; + if (wbc->wb_id != max_id) + history |= (1U << slots) - 1; + + /* + * Switch if the current wb isn't the consistent winner. + * If there are multiple closely competing dirtiers, the + * inode may switch across them repeatedly over time, which + * is okay. The main goal is avoiding keeping an inode on + * the wrong wb for an extended period of time. + */ + if (hweight32(history) > WB_FRN_HIST_THR_SLOTS) + inode_switch_wbs(inode, max_id); + } + + /* + * Multiple instances of this function may race to update the + * following fields but we don't mind occassional inaccuracies. + */ + inode->i_wb_frn_winner = max_id; + inode->i_wb_frn_avg_time = min(avg_time, (unsigned long)U16_MAX); + inode->i_wb_frn_history = history; + + wb_put(wbc->wb); + wbc->wb = NULL; +} + +/** + * wbc_account_io - account IO issued during writeback + * @wbc: writeback_control of the writeback in progress + * @page: page being written out + * @bytes: number of bytes being written out + * + * @bytes from @page are about to written out during the writeback + * controlled by @wbc. Keep the book for foreign inode detection. See + * wbc_detach_inode(). + */ +void wbc_account_io(struct writeback_control *wbc, struct page *page, + size_t bytes) +{ + int id; + + /* + * pageout() path doesn't attach @wbc to the inode being written + * out. This is intentional as we don't want the function to block + * behind a slow cgroup. Ultimately, we want pageout() to kick off + * regular writeback instead of writing things out itself. + */ + if (!wbc->wb) + return; + + rcu_read_lock(); + id = mem_cgroup_css_from_page(page)->id; + rcu_read_unlock(); + + if (id == wbc->wb_id) { + wbc->wb_bytes += bytes; + return; + } + + if (id == wbc->wb_lcand_id) + wbc->wb_lcand_bytes += bytes; + + /* Boyer-Moore majority vote algorithm */ + if (!wbc->wb_tcand_bytes) + wbc->wb_tcand_id = id; + if (id == wbc->wb_tcand_id) + wbc->wb_tcand_bytes += bytes; + else + wbc->wb_tcand_bytes -= min(bytes, wbc->wb_tcand_bytes); +} +EXPORT_SYMBOL_GPL(wbc_account_io); + +/** + * inode_congested - test whether an inode is congested + * @inode: inode to test for congestion + * @cong_bits: mask of WB_[a]sync_congested bits to test + * + * Tests whether @inode is congested. @cong_bits is the mask of congestion + * bits to test and the return value is the mask of set bits. + * + * If cgroup writeback is enabled for @inode, the congestion state is + * determined by whether the cgwb (cgroup bdi_writeback) for the blkcg + * associated with @inode is congested; otherwise, the root wb's congestion + * state is used. + */ +int inode_congested(struct inode *inode, int cong_bits) +{ + /* + * Once set, ->i_wb never becomes NULL while the inode is alive. + * Start transaction iff ->i_wb is visible. + */ + if (inode && inode_to_wb_is_valid(inode)) { + struct bdi_writeback *wb; + bool locked, congested; + + wb = unlocked_inode_to_wb_begin(inode, &locked); + congested = wb_congested(wb, cong_bits); + unlocked_inode_to_wb_end(inode, locked); + return congested; + } + + return wb_congested(&inode_to_bdi(inode)->wb, cong_bits); +} +EXPORT_SYMBOL_GPL(inode_congested); + +/** + * wb_wait_for_single_work - wait for completion of a single bdi_writeback_work + * @bdi: bdi the work item was issued to + * @work: work item to wait for + * + * Wait for the completion of @work which was issued to one of @bdi's + * bdi_writeback's. The caller must have set @work->single_wait before + * issuing it. This wait operates independently fo + * wb_wait_for_completion() and also disables automatic freeing of @work. + */ +static void wb_wait_for_single_work(struct backing_dev_info *bdi, + struct wb_writeback_work *work) +{ + if (WARN_ON_ONCE(!work->single_wait)) + return; + + wait_event(bdi->wb_waitq, work->single_done); + + /* + * Paired with smp_wmb() in wb_do_writeback() and ensures that all + * modifications to @work prior to assertion of ->single_done is + * visible to the caller once this function returns. + */ + smp_rmb(); +} + +/** + * wb_split_bdi_pages - split nr_pages to write according to bandwidth + * @wb: target bdi_writeback to split @nr_pages to + * @nr_pages: number of pages to write for the whole bdi + * + * Split @wb's portion of @nr_pages according to @wb's write bandwidth in + * relation to the total write bandwidth of all wb's w/ dirty inodes on + * @wb->bdi. + */ +static long wb_split_bdi_pages(struct bdi_writeback *wb, long nr_pages) +{ + unsigned long this_bw = wb->avg_write_bandwidth; + unsigned long tot_bw = atomic_long_read(&wb->bdi->tot_write_bandwidth); + + if (nr_pages == LONG_MAX) + return LONG_MAX; + + /* + * This may be called on clean wb's and proportional distribution + * may not make sense, just use the original @nr_pages in those + * cases. In general, we wanna err on the side of writing more. + */ + if (!tot_bw || this_bw >= tot_bw) + return nr_pages; + else + return DIV_ROUND_UP_ULL((u64)nr_pages * this_bw, tot_bw); +} + +/** + * wb_clone_and_queue_work - clone a wb_writeback_work and issue it to a wb + * @wb: target bdi_writeback + * @base_work: source wb_writeback_work + * + * Try to make a clone of @base_work and issue it to @wb. If cloning + * succeeds, %true is returned; otherwise, @base_work is issued directly + * and %false is returned. In the latter case, the caller is required to + * wait for @base_work's completion using wb_wait_for_single_work(). + * + * A clone is auto-freed on completion. @base_work never is. + */ +static bool wb_clone_and_queue_work(struct bdi_writeback *wb, + struct wb_writeback_work *base_work) { struct wb_writeback_work *work; + work = kmalloc(sizeof(*work), GFP_ATOMIC); + if (work) { + *work = *base_work; + work->auto_free = 1; + work->single_wait = 0; + } else { + work = base_work; + work->auto_free = 0; + work->single_wait = 1; + } + work->single_done = 0; + wb_queue_work(wb, work); + return work != base_work; +} + +/** + * bdi_split_work_to_wbs - split a wb_writeback_work to all wb's of a bdi + * @bdi: target backing_dev_info + * @base_work: wb_writeback_work to issue + * @skip_if_busy: skip wb's which already have writeback in progress + * + * Split and issue @base_work to all wb's (bdi_writeback's) of @bdi which + * have dirty inodes. If @base_work->nr_page isn't %LONG_MAX, it's + * distributed to the busy wbs according to each wb's proportion in the + * total active write bandwidth of @bdi. + */ +static void bdi_split_work_to_wbs(struct backing_dev_info *bdi, + struct wb_writeback_work *base_work, + bool skip_if_busy) +{ + long nr_pages = base_work->nr_pages; + int next_blkcg_id = 0; + struct bdi_writeback *wb; + struct wb_iter iter; + + might_sleep(); +restart: + rcu_read_lock(); + bdi_for_each_wb(wb, bdi, &iter, next_blkcg_id) { + /* SYNC_ALL writes out I_DIRTY_TIME too */ + if (!wb_has_dirty_io(wb) && + (base_work->sync_mode == WB_SYNC_NONE || + list_empty(&wb->b_dirty_time))) + continue; + if (skip_if_busy && writeback_in_progress(wb)) + continue; + + base_work->nr_pages = wb_split_bdi_pages(wb, nr_pages); + if (!wb_clone_and_queue_work(wb, base_work)) { + next_blkcg_id = wb->blkcg_css->id + 1; + rcu_read_unlock(); + wb_wait_for_single_work(bdi, base_work); + goto restart; + } + } + rcu_read_unlock(); +} + +#else /* CONFIG_CGROUP_WRITEBACK */ + +static struct bdi_writeback * +locked_inode_to_wb_and_lock_list(struct inode *inode) + __releases(&inode->i_lock) + __acquires(&wb->list_lock) +{ + struct bdi_writeback *wb = inode_to_wb(inode); + + spin_unlock(&inode->i_lock); + spin_lock(&wb->list_lock); + return wb; +} + +static struct bdi_writeback *inode_to_wb_and_lock_list(struct inode *inode) + __acquires(&wb->list_lock) +{ + struct bdi_writeback *wb = inode_to_wb(inode); + + spin_lock(&wb->list_lock); + return wb; +} + +static long wb_split_bdi_pages(struct bdi_writeback *wb, long nr_pages) +{ + return nr_pages; +} + +static void bdi_split_work_to_wbs(struct backing_dev_info *bdi, + struct wb_writeback_work *base_work, + bool skip_if_busy) +{ + might_sleep(); + + if (!skip_if_busy || !writeback_in_progress(&bdi->wb)) { + base_work->auto_free = 0; + base_work->single_wait = 0; + base_work->single_done = 0; + wb_queue_work(&bdi->wb, base_work); + } +} + +#endif /* CONFIG_CGROUP_WRITEBACK */ + +void wb_start_writeback(struct bdi_writeback *wb, long nr_pages, + bool range_cyclic, enum wb_reason reason) +{ + struct wb_writeback_work *work; + + if (!wb_has_dirty_io(wb)) + return; + /* * This is WB_SYNC_NONE writeback, so if allocation fails just * wakeup the thread for old dirty data writeback */ work = kzalloc(sizeof(*work), GFP_ATOMIC); if (!work) { - trace_writeback_nowork(bdi); - bdi_wakeup_thread(bdi); + trace_writeback_nowork(wb->bdi); + wb_wakeup(wb); return; } @@ -155,46 +933,29 @@ __bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages, work->nr_pages = nr_pages; work->range_cyclic = range_cyclic; work->reason = reason; + work->auto_free = 1; - bdi_queue_work(bdi, work); -} - -/** - * bdi_start_writeback - start writeback - * @bdi: the backing device to write from - * @nr_pages: the number of pages to write - * @reason: reason why some writeback work was initiated - * - * Description: - * This does WB_SYNC_NONE opportunistic writeback. The IO is only - * started when this function returns, we make no guarantees on - * completion. Caller need not hold sb s_umount semaphore. - * - */ -void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages, - enum wb_reason reason) -{ - __bdi_start_writeback(bdi, nr_pages, true, reason); + wb_queue_work(wb, work); } /** - * bdi_start_background_writeback - start background writeback - * @bdi: the backing device to write from + * wb_start_background_writeback - start background writeback + * @wb: bdi_writback to write from * * Description: * This makes sure WB_SYNC_NONE background writeback happens. When - * this function returns, it is only guaranteed that for given BDI + * this function returns, it is only guaranteed that for given wb * some IO is happening if we are over background dirty threshold. * Caller need not hold sb s_umount semaphore. */ -void bdi_start_background_writeback(struct backing_dev_info *bdi) +void wb_start_background_writeback(struct bdi_writeback *wb) { /* * We just wake up the flusher thread. It will perform background * writeback as soon as there is no other work to do. */ - trace_writeback_wake_background(bdi); - bdi_wakeup_thread(bdi); + trace_writeback_wake_background(wb->bdi); + wb_wakeup(wb); } /* @@ -202,11 +963,11 @@ void bdi_start_background_writeback(struct backing_dev_info *bdi) */ void inode_wb_list_del(struct inode *inode) { - struct backing_dev_info *bdi = inode_to_bdi(inode); + struct bdi_writeback *wb; - spin_lock(&bdi->wb.list_lock); - list_del_init(&inode->i_wb_list); - spin_unlock(&bdi->wb.list_lock); + wb = inode_to_wb_and_lock_list(inode); + inode_wb_list_del_locked(inode, wb); + spin_unlock(&wb->list_lock); } /* @@ -220,7 +981,6 @@ void inode_wb_list_del(struct inode *inode) */ static void redirty_tail(struct inode *inode, struct bdi_writeback *wb) { - assert_spin_locked(&wb->list_lock); if (!list_empty(&wb->b_dirty)) { struct inode *tail; @@ -228,7 +988,7 @@ static void redirty_tail(struct inode *inode, struct bdi_writeback *wb) if (time_before(inode->dirtied_when, tail->dirtied_when)) inode->dirtied_when = jiffies; } - list_move(&inode->i_wb_list, &wb->b_dirty); + inode_wb_list_move_locked(inode, wb, &wb->b_dirty); } /* @@ -236,8 +996,7 @@ static void redirty_tail(struct inode *inode, struct bdi_writeback *wb) */ static void requeue_io(struct inode *inode, struct bdi_writeback *wb) { - assert_spin_locked(&wb->list_lock); - list_move(&inode->i_wb_list, &wb->b_more_io); + inode_wb_list_move_locked(inode, wb, &wb->b_more_io); } static void inode_sync_complete(struct inode *inode) @@ -346,6 +1105,8 @@ static void queue_io(struct bdi_writeback *wb, struct wb_writeback_work *work) moved = move_expired_inodes(&wb->b_dirty, &wb->b_io, 0, work); moved += move_expired_inodes(&wb->b_dirty_time, &wb->b_io, EXPIRE_DIRTY_ATIME, work); + if (moved) + wb_io_lists_populated(wb); trace_writeback_queue_io(wb, work, moved); } @@ -471,10 +1232,10 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb, redirty_tail(inode, wb); } else if (inode->i_state & I_DIRTY_TIME) { inode->dirtied_when = jiffies; - list_move(&inode->i_wb_list, &wb->b_dirty_time); + inode_wb_list_move_locked(inode, wb, &wb->b_dirty_time); } else { /* The inode is clean. Remove from writeback lists. */ - list_del_init(&inode->i_wb_list); + inode_wb_list_del_locked(inode, wb); } } @@ -605,10 +1366,11 @@ writeback_single_inode(struct inode *inode, struct bdi_writeback *wb, !mapping_tagged(inode->i_mapping, PAGECACHE_TAG_WRITEBACK))) goto out; inode->i_state |= I_SYNC; - spin_unlock(&inode->i_lock); + wbc_attach_and_unlock_inode(wbc, inode); ret = __writeback_single_inode(inode, wbc); + wbc_detach_inode(wbc); spin_lock(&wb->list_lock); spin_lock(&inode->i_lock); /* @@ -616,7 +1378,7 @@ writeback_single_inode(struct inode *inode, struct bdi_writeback *wb, * touch it. See comment above for explanation. */ if (!(inode->i_state & I_DIRTY_ALL)) - list_del_init(&inode->i_wb_list); + inode_wb_list_del_locked(inode, wb); spin_unlock(&wb->list_lock); inode_sync_complete(inode); out: @@ -624,7 +1386,7 @@ out: return ret; } -static long writeback_chunk_size(struct backing_dev_info *bdi, +static long writeback_chunk_size(struct bdi_writeback *wb, struct wb_writeback_work *work) { long pages; @@ -645,8 +1407,8 @@ static long writeback_chunk_size(struct backing_dev_info *bdi, if (work->sync_mode == WB_SYNC_ALL || work->tagged_writepages) pages = LONG_MAX; else { - pages = min(bdi->avg_write_bandwidth / 2, - global_dirty_limit / DIRTY_SCOPE); + pages = min(wb->avg_write_bandwidth / 2, + global_wb_domain.dirty_limit / DIRTY_SCOPE); pages = min(pages, work->nr_pages); pages = round_down(pages + MIN_WRITEBACK_PAGES, MIN_WRITEBACK_PAGES); @@ -741,9 +1503,9 @@ static long writeback_sb_inodes(struct super_block *sb, continue; } inode->i_state |= I_SYNC; - spin_unlock(&inode->i_lock); + wbc_attach_and_unlock_inode(&wbc, inode); - write_chunk = writeback_chunk_size(wb->bdi, work); + write_chunk = writeback_chunk_size(wb, work); wbc.nr_to_write = write_chunk; wbc.pages_skipped = 0; @@ -753,6 +1515,7 @@ static long writeback_sb_inodes(struct super_block *sb, */ __writeback_single_inode(inode, &wbc); + wbc_detach_inode(&wbc); work->nr_pages -= write_chunk - wbc.nr_to_write; wrote += write_chunk - wbc.nr_to_write; spin_lock(&wb->list_lock); @@ -830,33 +1593,6 @@ static long writeback_inodes_wb(struct bdi_writeback *wb, long nr_pages, return nr_pages - work.nr_pages; } -static bool over_bground_thresh(struct backing_dev_info *bdi) -{ - unsigned long background_thresh, dirty_thresh; - - global_dirty_limits(&background_thresh, &dirty_thresh); - - if (global_page_state(NR_FILE_DIRTY) + - global_page_state(NR_UNSTABLE_NFS) > background_thresh) - return true; - - if (bdi_stat(bdi, BDI_RECLAIMABLE) > - bdi_dirty_limit(bdi, background_thresh)) - return true; - - return false; -} - -/* - * Called under wb->list_lock. If there are multiple wb per bdi, - * only the flusher working on the first wb should do it. - */ -static void wb_update_bandwidth(struct bdi_writeback *wb, - unsigned long start_time) -{ - __bdi_update_bandwidth(wb->bdi, 0, 0, 0, 0, 0, start_time); -} - /* * Explicit flushing or periodic writeback of "old" data. * @@ -899,14 +1635,14 @@ static long wb_writeback(struct bdi_writeback *wb, * after the other works are all done. */ if ((work->for_background || work->for_kupdate) && - !list_empty(&wb->bdi->work_list)) + !list_empty(&wb->work_list)) break; /* * For background writeout, stop when we are below the * background dirty threshold */ - if (work->for_background && !over_bground_thresh(wb->bdi)) + if (work->for_background && !wb_over_bg_thresh(wb)) break; /* @@ -970,18 +1706,17 @@ static long wb_writeback(struct bdi_writeback *wb, /* * Return the next wb_writeback_work struct that hasn't been processed yet. */ -static struct wb_writeback_work * -get_next_work_item(struct backing_dev_info *bdi) +static struct wb_writeback_work *get_next_work_item(struct bdi_writeback *wb) { struct wb_writeback_work *work = NULL; - spin_lock_bh(&bdi->wb_lock); - if (!list_empty(&bdi->work_list)) { - work = list_entry(bdi->work_list.next, + spin_lock_bh(&wb->work_lock); + if (!list_empty(&wb->work_list)) { + work = list_entry(wb->work_list.next, struct wb_writeback_work, list); list_del_init(&work->list); } - spin_unlock_bh(&bdi->wb_lock); + spin_unlock_bh(&wb->work_lock); return work; } @@ -998,7 +1733,7 @@ static unsigned long get_nr_dirty_pages(void) static long wb_check_background_flush(struct bdi_writeback *wb) { - if (over_bground_thresh(wb->bdi)) { + if (wb_over_bg_thresh(wb)) { struct wb_writeback_work work = { .nr_pages = LONG_MAX, @@ -1053,25 +1788,33 @@ static long wb_check_old_data_flush(struct bdi_writeback *wb) */ static long wb_do_writeback(struct bdi_writeback *wb) { - struct backing_dev_info *bdi = wb->bdi; struct wb_writeback_work *work; long wrote = 0; - set_bit(BDI_writeback_running, &wb->bdi->state); - while ((work = get_next_work_item(bdi)) != NULL) { + set_bit(WB_writeback_running, &wb->state); + while ((work = get_next_work_item(wb)) != NULL) { + struct wb_completion *done = work->done; + bool need_wake_up = false; - trace_writeback_exec(bdi, work); + trace_writeback_exec(wb->bdi, work); wrote += wb_writeback(wb, work); - /* - * Notify the caller of completion if this is a synchronous - * work item, otherwise just free it. - */ - if (work->done) - complete(work->done); - else + if (work->single_wait) { + WARN_ON_ONCE(work->auto_free); + /* paired w/ rmb in wb_wait_for_single_work() */ + smp_wmb(); + work->single_done = 1; + need_wake_up = true; + } else if (work->auto_free) { kfree(work); + } + + if (done && atomic_dec_and_test(&done->cnt)) + need_wake_up = true; + + if (need_wake_up) + wake_up_all(&wb->bdi->wb_waitq); } /* @@ -1079,7 +1822,7 @@ static long wb_do_writeback(struct bdi_writeback *wb) */ wrote += wb_check_old_data_flush(wb); wrote += wb_check_background_flush(wb); - clear_bit(BDI_writeback_running, &wb->bdi->state); + clear_bit(WB_writeback_running, &wb->state); return wrote; } @@ -1088,43 +1831,42 @@ static long wb_do_writeback(struct bdi_writeback *wb) * Handle writeback of dirty data for the device backed by this bdi. Also * reschedules periodically and does kupdated style flushing. */ -void bdi_writeback_workfn(struct work_struct *work) +void wb_workfn(struct work_struct *work) { struct bdi_writeback *wb = container_of(to_delayed_work(work), struct bdi_writeback, dwork); - struct backing_dev_info *bdi = wb->bdi; long pages_written; - set_worker_desc("flush-%s", dev_name(bdi->dev)); + set_worker_desc("flush-%s", dev_name(wb->bdi->dev)); current->flags |= PF_SWAPWRITE; if (likely(!current_is_workqueue_rescuer() || - !test_bit(BDI_registered, &bdi->state))) { + !test_bit(WB_registered, &wb->state))) { /* - * The normal path. Keep writing back @bdi until its + * The normal path. Keep writing back @wb until its * work_list is empty. Note that this path is also taken - * if @bdi is shutting down even when we're running off the + * if @wb is shutting down even when we're running off the * rescuer as work_list needs to be drained. */ do { pages_written = wb_do_writeback(wb); trace_writeback_pages_written(pages_written); - } while (!list_empty(&bdi->work_list)); + } while (!list_empty(&wb->work_list)); } else { /* * bdi_wq can't get enough workers and we're running off * the emergency worker. Don't hog it. Hopefully, 1024 is * enough for efficient IO. */ - pages_written = writeback_inodes_wb(&bdi->wb, 1024, + pages_written = writeback_inodes_wb(wb, 1024, WB_REASON_FORKER_THREAD); trace_writeback_pages_written(pages_written); } - if (!list_empty(&bdi->work_list)) + if (!list_empty(&wb->work_list)) mod_delayed_work(bdi_wq, &wb->dwork, 0); else if (wb_has_dirty_io(wb) && dirty_writeback_interval) - bdi_wakeup_thread_delayed(bdi); + wb_wakeup_delayed(wb); current->flags &= ~PF_SWAPWRITE; } @@ -1142,9 +1884,15 @@ void wakeup_flusher_threads(long nr_pages, enum wb_reason reason) rcu_read_lock(); list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) { + struct bdi_writeback *wb; + struct wb_iter iter; + if (!bdi_has_dirty_io(bdi)) continue; - __bdi_start_writeback(bdi, nr_pages, false, reason); + + bdi_for_each_wb(wb, bdi, &iter, 0) + wb_start_writeback(wb, wb_split_bdi_pages(wb, nr_pages), + false, reason); } rcu_read_unlock(); } @@ -1173,9 +1921,12 @@ static void wakeup_dirtytime_writeback(struct work_struct *w) rcu_read_lock(); list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) { - if (list_empty(&bdi->wb.b_dirty_time)) - continue; - bdi_wakeup_thread(bdi); + struct bdi_writeback *wb; + struct wb_iter iter; + + bdi_for_each_wb(wb, bdi, &iter, 0) + if (!list_empty(&bdi->wb.b_dirty_time)) + wb_wakeup(&bdi->wb); } rcu_read_unlock(); schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ); @@ -1249,7 +2000,6 @@ static noinline void block_dump___mark_inode_dirty(struct inode *inode) void __mark_inode_dirty(struct inode *inode, int flags) { struct super_block *sb = inode->i_sb; - struct backing_dev_info *bdi = NULL; int dirtytime; trace_writeback_mark_inode_dirty(inode, flags); @@ -1289,6 +2039,8 @@ void __mark_inode_dirty(struct inode *inode, int flags) if ((inode->i_state & flags) != flags) { const int was_dirty = inode->i_state & I_DIRTY; + inode_attach_wb(inode, NULL); + if (flags & I_DIRTY_INODE) inode->i_state &= ~I_DIRTY_TIME; inode->i_state |= flags; @@ -1317,38 +2069,39 @@ void __mark_inode_dirty(struct inode *inode, int flags) * reposition it (that would break b_dirty time-ordering). */ if (!was_dirty) { + struct bdi_writeback *wb; + struct list_head *dirty_list; bool wakeup_bdi = false; - bdi = inode_to_bdi(inode); - spin_unlock(&inode->i_lock); - spin_lock(&bdi->wb.list_lock); - if (bdi_cap_writeback_dirty(bdi)) { - WARN(!test_bit(BDI_registered, &bdi->state), - "bdi-%s not registered\n", bdi->name); + wb = locked_inode_to_wb_and_lock_list(inode); - /* - * If this is the first dirty inode for this - * bdi, we have to wake-up the corresponding - * bdi thread to make sure background - * write-back happens later. - */ - if (!wb_has_dirty_io(&bdi->wb)) - wakeup_bdi = true; - } + WARN(bdi_cap_writeback_dirty(wb->bdi) && + !test_bit(WB_registered, &wb->state), + "bdi-%s not registered\n", wb->bdi->name); inode->dirtied_when = jiffies; if (dirtytime) inode->dirtied_time_when = jiffies; + if (inode->i_state & (I_DIRTY_INODE | I_DIRTY_PAGES)) - list_move(&inode->i_wb_list, &bdi->wb.b_dirty); + dirty_list = &wb->b_dirty; else - list_move(&inode->i_wb_list, - &bdi->wb.b_dirty_time); - spin_unlock(&bdi->wb.list_lock); + dirty_list = &wb->b_dirty_time; + + wakeup_bdi = inode_wb_list_move_locked(inode, wb, + dirty_list); + + spin_unlock(&wb->list_lock); trace_writeback_dirty_inode_enqueue(inode); - if (wakeup_bdi) - bdi_wakeup_thread_delayed(bdi); + /* + * If this is the first dirty inode for this bdi, + * we have to wake-up the corresponding bdi thread + * to make sure background write-back happens + * later. + */ + if (bdi_cap_writeback_dirty(wb->bdi) && wakeup_bdi) + wb_wakeup_delayed(wb); return; } } @@ -1411,6 +2164,28 @@ static void wait_sb_inodes(struct super_block *sb) iput(old_inode); } +static void __writeback_inodes_sb_nr(struct super_block *sb, unsigned long nr, + enum wb_reason reason, bool skip_if_busy) +{ + DEFINE_WB_COMPLETION_ONSTACK(done); + struct wb_writeback_work work = { + .sb = sb, + .sync_mode = WB_SYNC_NONE, + .tagged_writepages = 1, + .done = &done, + .nr_pages = nr, + .reason = reason, + }; + struct backing_dev_info *bdi = sb->s_bdi; + + if (!bdi_has_dirty_io(bdi) || bdi == &noop_backing_dev_info) + return; + WARN_ON(!rwsem_is_locked(&sb->s_umount)); + + bdi_split_work_to_wbs(sb->s_bdi, &work, skip_if_busy); + wb_wait_for_completion(bdi, &done); +} + /** * writeback_inodes_sb_nr - writeback dirty inodes from given super_block * @sb: the superblock @@ -1425,21 +2200,7 @@ void writeback_inodes_sb_nr(struct super_block *sb, unsigned long nr, enum wb_reason reason) { - DECLARE_COMPLETION_ONSTACK(done); - struct wb_writeback_work work = { - .sb = sb, - .sync_mode = WB_SYNC_NONE, - .tagged_writepages = 1, - .done = &done, - .nr_pages = nr, - .reason = reason, - }; - - if (sb->s_bdi == &noop_backing_dev_info) - return; - WARN_ON(!rwsem_is_locked(&sb->s_umount)); - bdi_queue_work(sb->s_bdi, &work); - wait_for_completion(&done); + __writeback_inodes_sb_nr(sb, nr, reason, false); } EXPORT_SYMBOL(writeback_inodes_sb_nr); @@ -1467,19 +2228,15 @@ EXPORT_SYMBOL(writeback_inodes_sb); * Invoke writeback_inodes_sb_nr if no writeback is currently underway. * Returns 1 if writeback was started, 0 if not. */ -int try_to_writeback_inodes_sb_nr(struct super_block *sb, - unsigned long nr, - enum wb_reason reason) +bool try_to_writeback_inodes_sb_nr(struct super_block *sb, unsigned long nr, + enum wb_reason reason) { - if (writeback_in_progress(sb->s_bdi)) - return 1; - if (!down_read_trylock(&sb->s_umount)) - return 0; + return false; - writeback_inodes_sb_nr(sb, nr, reason); + __writeback_inodes_sb_nr(sb, nr, reason, true); up_read(&sb->s_umount); - return 1; + return true; } EXPORT_SYMBOL(try_to_writeback_inodes_sb_nr); @@ -1491,7 +2248,7 @@ EXPORT_SYMBOL(try_to_writeback_inodes_sb_nr); * Implement by try_to_writeback_inodes_sb_nr() * Returns 1 if writeback was started, 0 if not. */ -int try_to_writeback_inodes_sb(struct super_block *sb, enum wb_reason reason) +bool try_to_writeback_inodes_sb(struct super_block *sb, enum wb_reason reason) { return try_to_writeback_inodes_sb_nr(sb, get_nr_dirty_pages(), reason); } @@ -1506,7 +2263,7 @@ EXPORT_SYMBOL(try_to_writeback_inodes_sb); */ void sync_inodes_sb(struct super_block *sb) { - DECLARE_COMPLETION_ONSTACK(done); + DEFINE_WB_COMPLETION_ONSTACK(done); struct wb_writeback_work work = { .sb = sb, .sync_mode = WB_SYNC_ALL, @@ -1516,14 +2273,19 @@ void sync_inodes_sb(struct super_block *sb) .reason = WB_REASON_SYNC, .for_sync = 1, }; + struct backing_dev_info *bdi = sb->s_bdi; - /* Nothing to do? */ - if (sb->s_bdi == &noop_backing_dev_info) + /* + * Can't skip on !bdi_has_dirty() because we should wait for !dirty + * inodes under writeback and I_DIRTY_TIME inodes ignored by + * bdi_has_dirty() need to be written out too. + */ + if (bdi == &noop_backing_dev_info) return; WARN_ON(!rwsem_is_locked(&sb->s_umount)); - bdi_queue_work(sb->s_bdi, &work); - wait_for_completion(&done); + bdi_split_work_to_wbs(bdi, &work, false); + wb_wait_for_completion(bdi, &done); wait_sb_inodes(sb); } diff --git a/fs/fscache/cookie.c b/fs/fscache/cookie.c index 89acec742..d403c69be 100644 --- a/fs/fscache/cookie.c +++ b/fs/fscache/cookie.c @@ -327,7 +327,8 @@ static int fscache_alloc_object(struct fscache_cache *cache, object_already_extant: ret = -ENOBUFS; - if (fscache_object_is_dead(object)) { + if (fscache_object_is_dying(object) || + fscache_cache_is_broken(object)) { spin_unlock(&cookie->lock); goto error; } @@ -671,7 +672,7 @@ int __fscache_check_consistency(struct fscache_cookie *cookie) if (!op) return -ENOMEM; - fscache_operation_init(op, NULL, NULL); + fscache_operation_init(op, NULL, NULL, NULL); op->flags = FSCACHE_OP_MYTHREAD | (1 << FSCACHE_OP_WAITING) | (1 << FSCACHE_OP_UNUSE_COOKIE); @@ -695,8 +696,7 @@ int __fscache_check_consistency(struct fscache_cookie *cookie) /* the work queue now carries its own ref on the object */ spin_unlock(&cookie->lock); - ret = fscache_wait_for_operation_activation(object, op, - NULL, NULL, NULL); + ret = fscache_wait_for_operation_activation(object, op, NULL, NULL); if (ret == 0) { /* ask the cache to honour the operation */ ret = object->cache->ops->check_consistency(op); diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index 7872a62ef..97ec45110 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h @@ -124,8 +124,7 @@ extern int fscache_submit_exclusive_op(struct fscache_object *, struct fscache_operation *); extern int fscache_submit_op(struct fscache_object *, struct fscache_operation *); -extern int fscache_cancel_op(struct fscache_operation *, - void (*)(struct fscache_operation *)); +extern int fscache_cancel_op(struct fscache_operation *, bool); extern void fscache_cancel_all_ops(struct fscache_object *); extern void fscache_abort_object(struct fscache_object *); extern void fscache_start_operations(struct fscache_object *); @@ -138,8 +137,7 @@ extern int fscache_wait_for_deferred_lookup(struct fscache_cookie *); extern int fscache_wait_for_operation_activation(struct fscache_object *, struct fscache_operation *, atomic_t *, - atomic_t *, - void (*)(struct fscache_operation *)); + atomic_t *); extern void fscache_invalidate_writes(struct fscache_cookie *); /* @@ -164,6 +162,7 @@ extern atomic_t fscache_n_op_pend; extern atomic_t fscache_n_op_run; extern atomic_t fscache_n_op_enqueue; extern atomic_t fscache_n_op_deferred_release; +extern atomic_t fscache_n_op_initialised; extern atomic_t fscache_n_op_release; extern atomic_t fscache_n_op_gc; extern atomic_t fscache_n_op_cancelled; @@ -271,6 +270,11 @@ extern atomic_t fscache_n_cop_write_page; extern atomic_t fscache_n_cop_uncache_page; extern atomic_t fscache_n_cop_dissociate_pages; +extern atomic_t fscache_n_cache_no_space_reject; +extern atomic_t fscache_n_cache_stale_objects; +extern atomic_t fscache_n_cache_retired_objects; +extern atomic_t fscache_n_cache_culled_objects; + static inline void fscache_stat(atomic_t *stat) { atomic_inc(stat); diff --git a/fs/fscache/object.c b/fs/fscache/object.c index da032daf0..9e792e30f 100644 --- a/fs/fscache/object.c +++ b/fs/fscache/object.c @@ -328,6 +328,17 @@ void fscache_object_init(struct fscache_object *object, EXPORT_SYMBOL(fscache_object_init); /* + * Mark the object as no longer being live, making sure that we synchronise + * against op submission. + */ +static inline void fscache_mark_object_dead(struct fscache_object *object) +{ + spin_lock(&object->lock); + clear_bit(FSCACHE_OBJECT_IS_LIVE, &object->flags); + spin_unlock(&object->lock); +} + +/* * Abort object initialisation before we start it. */ static const struct fscache_state *fscache_abort_initialisation(struct fscache_object *object, @@ -610,6 +621,8 @@ static const struct fscache_state *fscache_lookup_failure(struct fscache_object object->cache->ops->lookup_complete(object); fscache_stat_d(&fscache_n_cop_lookup_complete); + set_bit(FSCACHE_OBJECT_KILLED_BY_CACHE, &object->flags); + cookie = object->cookie; set_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags); if (test_and_clear_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags)) @@ -629,7 +642,7 @@ static const struct fscache_state *fscache_kill_object(struct fscache_object *ob _enter("{OBJ%x,%d,%d},%d", object->debug_id, object->n_ops, object->n_children, event); - clear_bit(FSCACHE_OBJECT_IS_LIVE, &object->flags); + fscache_mark_object_dead(object); object->oob_event_mask = 0; if (list_empty(&object->dependents) && @@ -948,7 +961,8 @@ static const struct fscache_state *_fscache_invalidate_object(struct fscache_obj if (!op) goto nomem; - fscache_operation_init(op, object->cache->ops->invalidate_object, NULL); + fscache_operation_init(op, object->cache->ops->invalidate_object, + NULL, NULL); op->flags = FSCACHE_OP_ASYNC | (1 << FSCACHE_OP_EXCLUSIVE) | (1 << FSCACHE_OP_UNUSE_COOKIE); @@ -974,13 +988,13 @@ static const struct fscache_state *_fscache_invalidate_object(struct fscache_obj return transit_to(UPDATE_OBJECT); nomem: - clear_bit(FSCACHE_OBJECT_IS_LIVE, &object->flags); + fscache_mark_object_dead(object); fscache_unuse_cookie(object); _leave(" [ENOMEM]"); return transit_to(KILL_OBJECT); submit_op_failed: - clear_bit(FSCACHE_OBJECT_IS_LIVE, &object->flags); + fscache_mark_object_dead(object); spin_unlock(&cookie->lock); fscache_unuse_cookie(object); kfree(op); @@ -1016,3 +1030,50 @@ static const struct fscache_state *fscache_update_object(struct fscache_object * _leave(""); return transit_to(WAIT_FOR_CMD); } + +/** + * fscache_object_retrying_stale - Note retrying stale object + * @object: The object that will be retried + * + * Note that an object lookup found an on-disk object that was adjudged to be + * stale and has been deleted. The lookup will be retried. + */ +void fscache_object_retrying_stale(struct fscache_object *object) +{ + fscache_stat(&fscache_n_cache_no_space_reject); +} +EXPORT_SYMBOL(fscache_object_retrying_stale); + +/** + * fscache_object_mark_killed - Note that an object was killed + * @object: The object that was culled + * @why: The reason the object was killed. + * + * Note that an object was killed. Returns true if the object was + * already marked killed, false if it wasn't. + */ +void fscache_object_mark_killed(struct fscache_object *object, + enum fscache_why_object_killed why) +{ + if (test_and_set_bit(FSCACHE_OBJECT_KILLED_BY_CACHE, &object->flags)) { + pr_err("Error: Object already killed by cache [%s]\n", + object->cache->identifier); + return; + } + + switch (why) { + case FSCACHE_OBJECT_NO_SPACE: + fscache_stat(&fscache_n_cache_no_space_reject); + break; + case FSCACHE_OBJECT_IS_STALE: + fscache_stat(&fscache_n_cache_stale_objects); + break; + case FSCACHE_OBJECT_WAS_RETIRED: + fscache_stat(&fscache_n_cache_retired_objects); + break; + case FSCACHE_OBJECT_WAS_CULLED: + fscache_stat(&fscache_n_cache_culled_objects); + break; + } +} +EXPORT_SYMBOL(fscache_object_mark_killed); diff --git a/fs/fscache/operation.c b/fs/fscache/operation.c index e7b87a0e5..de67745e1 100644 --- a/fs/fscache/operation.c +++ b/fs/fscache/operation.c @@ -20,6 +20,35 @@ atomic_t fscache_op_debug_id; EXPORT_SYMBOL(fscache_op_debug_id); +static void fscache_operation_dummy_cancel(struct fscache_operation *op) +{ +} + +/** + * fscache_operation_init - Do basic initialisation of an operation + * @op: The operation to initialise + * @release: The release function to assign + * + * Do basic initialisation of an operation. The caller must still set flags, + * object and processor if needed. + */ +void fscache_operation_init(struct fscache_operation *op, + fscache_operation_processor_t processor, + fscache_operation_cancel_t cancel, + fscache_operation_release_t release) +{ + INIT_WORK(&op->work, fscache_op_work_func); + atomic_set(&op->usage, 1); + op->state = FSCACHE_OP_ST_INITIALISED; + op->debug_id = atomic_inc_return(&fscache_op_debug_id); + op->processor = processor; + op->cancel = cancel ?: fscache_operation_dummy_cancel; + op->release = release; + INIT_LIST_HEAD(&op->pend_link); + fscache_stat(&fscache_n_op_initialised); +} +EXPORT_SYMBOL(fscache_operation_init); + /** * fscache_enqueue_operation - Enqueue an operation for processing * @op: The operation to enqueue @@ -76,6 +105,43 @@ static void fscache_run_op(struct fscache_object *object, } /* + * report an unexpected submission + */ +static void fscache_report_unexpected_submission(struct fscache_object *object, + struct fscache_operation *op, + const struct fscache_state *ostate) +{ + static bool once_only; + struct fscache_operation *p; + unsigned n; + + if (once_only) + return; + once_only = true; + + kdebug("unexpected submission OP%x [OBJ%x %s]", + op->debug_id, object->debug_id, object->state->name); + kdebug("objstate=%s [%s]", object->state->name, ostate->name); + kdebug("objflags=%lx", object->flags); + kdebug("objevent=%lx [%lx]", object->events, object->event_mask); + kdebug("ops=%u inp=%u exc=%u", + object->n_ops, object->n_in_progress, object->n_exclusive); + + if (!list_empty(&object->pending_ops)) { + n = 0; + list_for_each_entry(p, &object->pending_ops, pend_link) { + ASSERTCMP(p->object, ==, object); + kdebug("%p %p", op->processor, op->release); + n++; + } + + kdebug("n=%u", n); + } + + dump_stack(); +} + +/* * submit an exclusive operation for an object * - other ops are excluded from running simultaneously with this one * - this gets any extra refs it needs on an op @@ -83,6 +149,8 @@ static void fscache_run_op(struct fscache_object *object, int fscache_submit_exclusive_op(struct fscache_object *object, struct fscache_operation *op) { + const struct fscache_state *ostate; + unsigned long flags; int ret; _enter("{OBJ%x OP%x},", object->debug_id, op->debug_id); @@ -95,8 +163,21 @@ int fscache_submit_exclusive_op(struct fscache_object *object, ASSERTCMP(object->n_ops, >=, object->n_exclusive); ASSERT(list_empty(&op->pend_link)); + ostate = object->state; + smp_rmb(); + op->state = FSCACHE_OP_ST_PENDING; - if (fscache_object_is_active(object)) { + flags = READ_ONCE(object->flags); + if (unlikely(!(flags & BIT(FSCACHE_OBJECT_IS_LIVE)))) { + fscache_stat(&fscache_n_op_rejected); + op->cancel(op); + op->state = FSCACHE_OP_ST_CANCELLED; + ret = -ENOBUFS; + } else if (unlikely(fscache_cache_is_broken(object))) { + op->cancel(op); + op->state = FSCACHE_OP_ST_CANCELLED; + ret = -EIO; + } else if (flags & BIT(FSCACHE_OBJECT_IS_AVAILABLE)) { op->object = object; object->n_ops++; object->n_exclusive++; /* reads and writes must wait */ @@ -118,7 +199,7 @@ int fscache_submit_exclusive_op(struct fscache_object *object, /* need to issue a new write op after this */ clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags); ret = 0; - } else if (test_bit(FSCACHE_OBJECT_IS_LOOKED_UP, &object->flags)) { + } else if (flags & BIT(FSCACHE_OBJECT_IS_LOOKED_UP)) { op->object = object; object->n_ops++; object->n_exclusive++; /* reads and writes must wait */ @@ -126,12 +207,15 @@ int fscache_submit_exclusive_op(struct fscache_object *object, list_add_tail(&op->pend_link, &object->pending_ops); fscache_stat(&fscache_n_op_pend); ret = 0; + } else if (flags & BIT(FSCACHE_OBJECT_KILLED_BY_CACHE)) { + op->cancel(op); + op->state = FSCACHE_OP_ST_CANCELLED; + ret = -ENOBUFS; } else { - /* If we're in any other state, there must have been an I/O - * error of some nature. - */ - ASSERT(test_bit(FSCACHE_IOERROR, &object->cache->flags)); - ret = -EIO; + fscache_report_unexpected_submission(object, op, ostate); + op->cancel(op); + op->state = FSCACHE_OP_ST_CANCELLED; + ret = -ENOBUFS; } spin_unlock(&object->lock); @@ -139,43 +223,6 @@ int fscache_submit_exclusive_op(struct fscache_object *object, } /* - * report an unexpected submission - */ -static void fscache_report_unexpected_submission(struct fscache_object *object, - struct fscache_operation *op, - const struct fscache_state *ostate) -{ - static bool once_only; - struct fscache_operation *p; - unsigned n; - - if (once_only) - return; - once_only = true; - - kdebug("unexpected submission OP%x [OBJ%x %s]", - op->debug_id, object->debug_id, object->state->name); - kdebug("objstate=%s [%s]", object->state->name, ostate->name); - kdebug("objflags=%lx", object->flags); - kdebug("objevent=%lx [%lx]", object->events, object->event_mask); - kdebug("ops=%u inp=%u exc=%u", - object->n_ops, object->n_in_progress, object->n_exclusive); - - if (!list_empty(&object->pending_ops)) { - n = 0; - list_for_each_entry(p, &object->pending_ops, pend_link) { - ASSERTCMP(p->object, ==, object); - kdebug("%p %p", op->processor, op->release); - n++; - } - - kdebug("n=%u", n); - } - - dump_stack(); -} - -/* * submit an operation for an object * - objects may be submitted only in the following states: * - during object creation (write ops may be submitted) @@ -187,6 +234,7 @@ int fscache_submit_op(struct fscache_object *object, struct fscache_operation *op) { const struct fscache_state *ostate; + unsigned long flags; int ret; _enter("{OBJ%x OP%x},{%u}", @@ -204,7 +252,17 @@ int fscache_submit_op(struct fscache_object *object, smp_rmb(); op->state = FSCACHE_OP_ST_PENDING; - if (fscache_object_is_active(object)) { + flags = READ_ONCE(object->flags); + if (unlikely(!(flags & BIT(FSCACHE_OBJECT_IS_LIVE)))) { + fscache_stat(&fscache_n_op_rejected); + op->cancel(op); + op->state = FSCACHE_OP_ST_CANCELLED; + ret = -ENOBUFS; + } else if (unlikely(fscache_cache_is_broken(object))) { + op->cancel(op); + op->state = FSCACHE_OP_ST_CANCELLED; + ret = -EIO; + } else if (flags & BIT(FSCACHE_OBJECT_IS_AVAILABLE)) { op->object = object; object->n_ops++; @@ -222,23 +280,21 @@ int fscache_submit_op(struct fscache_object *object, fscache_run_op(object, op); } ret = 0; - } else if (test_bit(FSCACHE_OBJECT_IS_LOOKED_UP, &object->flags)) { + } else if (flags & BIT(FSCACHE_OBJECT_IS_LOOKED_UP)) { op->object = object; object->n_ops++; atomic_inc(&op->usage); list_add_tail(&op->pend_link, &object->pending_ops); fscache_stat(&fscache_n_op_pend); ret = 0; - } else if (fscache_object_is_dying(object)) { - fscache_stat(&fscache_n_op_rejected); + } else if (flags & BIT(FSCACHE_OBJECT_KILLED_BY_CACHE)) { + op->cancel(op); op->state = FSCACHE_OP_ST_CANCELLED; ret = -ENOBUFS; - } else if (!test_bit(FSCACHE_IOERROR, &object->cache->flags)) { + } else { fscache_report_unexpected_submission(object, op, ostate); ASSERT(!fscache_object_is_active(object)); - op->state = FSCACHE_OP_ST_CANCELLED; - ret = -ENOBUFS; - } else { + op->cancel(op); op->state = FSCACHE_OP_ST_CANCELLED; ret = -ENOBUFS; } @@ -293,9 +349,10 @@ void fscache_start_operations(struct fscache_object *object) * cancel an operation that's pending on an object */ int fscache_cancel_op(struct fscache_operation *op, - void (*do_cancel)(struct fscache_operation *)) + bool cancel_in_progress_op) { struct fscache_object *object = op->object; + bool put = false; int ret; _enter("OBJ%x OP%x}", op->object->debug_id, op->debug_id); @@ -309,19 +366,37 @@ int fscache_cancel_op(struct fscache_operation *op, ret = -EBUSY; if (op->state == FSCACHE_OP_ST_PENDING) { ASSERT(!list_empty(&op->pend_link)); - fscache_stat(&fscache_n_op_cancelled); list_del_init(&op->pend_link); - if (do_cancel) - do_cancel(op); + put = true; + + fscache_stat(&fscache_n_op_cancelled); + op->cancel(op); + op->state = FSCACHE_OP_ST_CANCELLED; + if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) + object->n_exclusive--; + if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags)) + wake_up_bit(&op->flags, FSCACHE_OP_WAITING); + ret = 0; + } else if (op->state == FSCACHE_OP_ST_IN_PROGRESS && cancel_in_progress_op) { + ASSERTCMP(object->n_in_progress, >, 0); + if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) + object->n_exclusive--; + object->n_in_progress--; + if (object->n_in_progress == 0) + fscache_start_operations(object); + + fscache_stat(&fscache_n_op_cancelled); + op->cancel(op); op->state = FSCACHE_OP_ST_CANCELLED; if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) object->n_exclusive--; if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags)) wake_up_bit(&op->flags, FSCACHE_OP_WAITING); - fscache_put_operation(op); ret = 0; } + if (put) + fscache_put_operation(op); spin_unlock(&object->lock); _leave(" = %d", ret); return ret; @@ -345,6 +420,7 @@ void fscache_cancel_all_ops(struct fscache_object *object) list_del_init(&op->pend_link); ASSERTCMP(op->state, ==, FSCACHE_OP_ST_PENDING); + op->cancel(op); op->state = FSCACHE_OP_ST_CANCELLED; if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) @@ -377,8 +453,12 @@ void fscache_op_complete(struct fscache_operation *op, bool cancelled) spin_lock(&object->lock); - op->state = cancelled ? - FSCACHE_OP_ST_CANCELLED : FSCACHE_OP_ST_COMPLETE; + if (!cancelled) { + op->state = FSCACHE_OP_ST_COMPLETE; + } else { + op->cancel(op); + op->state = FSCACHE_OP_ST_CANCELLED; + } if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) object->n_exclusive--; @@ -409,9 +489,9 @@ void fscache_put_operation(struct fscache_operation *op) return; _debug("PUT OP"); - ASSERTIFCMP(op->state != FSCACHE_OP_ST_COMPLETE, + ASSERTIFCMP(op->state != FSCACHE_OP_ST_INITIALISED && + op->state != FSCACHE_OP_ST_COMPLETE, op->state, ==, FSCACHE_OP_ST_CANCELLED); - op->state = FSCACHE_OP_ST_DEAD; fscache_stat(&fscache_n_op_release); @@ -419,37 +499,39 @@ void fscache_put_operation(struct fscache_operation *op) op->release(op); op->release = NULL; } + op->state = FSCACHE_OP_ST_DEAD; object = op->object; + if (likely(object)) { + if (test_bit(FSCACHE_OP_DEC_READ_CNT, &op->flags)) + atomic_dec(&object->n_reads); + if (test_bit(FSCACHE_OP_UNUSE_COOKIE, &op->flags)) + fscache_unuse_cookie(object); + + /* now... we may get called with the object spinlock held, so we + * complete the cleanup here only if we can immediately acquire the + * lock, and defer it otherwise */ + if (!spin_trylock(&object->lock)) { + _debug("defer put"); + fscache_stat(&fscache_n_op_deferred_release); + + cache = object->cache; + spin_lock(&cache->op_gc_list_lock); + list_add_tail(&op->pend_link, &cache->op_gc_list); + spin_unlock(&cache->op_gc_list_lock); + schedule_work(&cache->op_gc); + _leave(" [defer]"); + return; + } - if (test_bit(FSCACHE_OP_DEC_READ_CNT, &op->flags)) - atomic_dec(&object->n_reads); - if (test_bit(FSCACHE_OP_UNUSE_COOKIE, &op->flags)) - fscache_unuse_cookie(object); - - /* now... we may get called with the object spinlock held, so we - * complete the cleanup here only if we can immediately acquire the - * lock, and defer it otherwise */ - if (!spin_trylock(&object->lock)) { - _debug("defer put"); - fscache_stat(&fscache_n_op_deferred_release); + ASSERTCMP(object->n_ops, >, 0); + object->n_ops--; + if (object->n_ops == 0) + fscache_raise_event(object, FSCACHE_OBJECT_EV_CLEARED); - cache = object->cache; - spin_lock(&cache->op_gc_list_lock); - list_add_tail(&op->pend_link, &cache->op_gc_list); - spin_unlock(&cache->op_gc_list_lock); - schedule_work(&cache->op_gc); - _leave(" [defer]"); - return; + spin_unlock(&object->lock); } - ASSERTCMP(object->n_ops, >, 0); - object->n_ops--; - if (object->n_ops == 0) - fscache_raise_event(object, FSCACHE_OBJECT_EV_CLEARED); - - spin_unlock(&object->lock); - kfree(op); _leave(" [done]"); } diff --git a/fs/fscache/page.c b/fs/fscache/page.c index de33b3fcc..483bbc613 100644 --- a/fs/fscache/page.c +++ b/fs/fscache/page.c @@ -213,7 +213,7 @@ int __fscache_attr_changed(struct fscache_cookie *cookie) return -ENOMEM; } - fscache_operation_init(op, fscache_attr_changed_op, NULL); + fscache_operation_init(op, fscache_attr_changed_op, NULL, NULL); op->flags = FSCACHE_OP_ASYNC | (1 << FSCACHE_OP_EXCLUSIVE) | (1 << FSCACHE_OP_UNUSE_COOKIE); @@ -239,7 +239,7 @@ nobufs_dec: wake_cookie = __fscache_unuse_cookie(cookie); nobufs: spin_unlock(&cookie->lock); - kfree(op); + fscache_put_operation(op); if (wake_cookie) __fscache_wake_unused_cookie(cookie); fscache_stat(&fscache_n_attr_changed_nobufs); @@ -249,6 +249,17 @@ nobufs: EXPORT_SYMBOL(__fscache_attr_changed); /* + * Handle cancellation of a pending retrieval op + */ +static void fscache_do_cancel_retrieval(struct fscache_operation *_op) +{ + struct fscache_retrieval *op = + container_of(_op, struct fscache_retrieval, op); + + atomic_set(&op->n_pages, 0); +} + +/* * release a retrieval op reference */ static void fscache_release_retrieval_op(struct fscache_operation *_op) @@ -258,11 +269,12 @@ static void fscache_release_retrieval_op(struct fscache_operation *_op) _enter("{OP%x}", op->op.debug_id); - ASSERTCMP(atomic_read(&op->n_pages), ==, 0); + ASSERTIFCMP(op->op.state != FSCACHE_OP_ST_INITIALISED, + atomic_read(&op->n_pages), ==, 0); fscache_hist(fscache_retrieval_histogram, op->start_time); if (op->context) - fscache_put_context(op->op.object->cookie, op->context); + fscache_put_context(op->cookie, op->context); _leave(""); } @@ -285,15 +297,24 @@ static struct fscache_retrieval *fscache_alloc_retrieval( return NULL; } - fscache_operation_init(&op->op, NULL, fscache_release_retrieval_op); + fscache_operation_init(&op->op, NULL, + fscache_do_cancel_retrieval, + fscache_release_retrieval_op); op->op.flags = FSCACHE_OP_MYTHREAD | (1UL << FSCACHE_OP_WAITING) | (1UL << FSCACHE_OP_UNUSE_COOKIE); + op->cookie = cookie; op->mapping = mapping; op->end_io_func = end_io_func; op->context = context; op->start_time = jiffies; INIT_LIST_HEAD(&op->to_do); + + /* Pin the netfs read context in case we need to do the actual netfs + * read because we've encountered a cache read failure. + */ + if (context) + fscache_get_context(op->cookie, context); return op; } @@ -330,24 +351,12 @@ int fscache_wait_for_deferred_lookup(struct fscache_cookie *cookie) } /* - * Handle cancellation of a pending retrieval op - */ -static void fscache_do_cancel_retrieval(struct fscache_operation *_op) -{ - struct fscache_retrieval *op = - container_of(_op, struct fscache_retrieval, op); - - atomic_set(&op->n_pages, 0); -} - -/* * wait for an object to become active (or dead) */ int fscache_wait_for_operation_activation(struct fscache_object *object, struct fscache_operation *op, atomic_t *stat_op_waits, - atomic_t *stat_object_dead, - void (*do_cancel)(struct fscache_operation *)) + atomic_t *stat_object_dead) { int ret; @@ -359,7 +368,7 @@ int fscache_wait_for_operation_activation(struct fscache_object *object, fscache_stat(stat_op_waits); if (wait_on_bit(&op->flags, FSCACHE_OP_WAITING, TASK_INTERRUPTIBLE) != 0) { - ret = fscache_cancel_op(op, do_cancel); + ret = fscache_cancel_op(op, false); if (ret == 0) return -ERESTARTSYS; @@ -377,11 +386,13 @@ check_if_dead: _leave(" = -ENOBUFS [cancelled]"); return -ENOBUFS; } - if (unlikely(fscache_object_is_dead(object))) { - pr_err("%s() = -ENOBUFS [obj dead %d]\n", __func__, op->state); - fscache_cancel_op(op, do_cancel); + if (unlikely(fscache_object_is_dying(object) || + fscache_cache_is_broken(object))) { + enum fscache_operation_state state = op->state; + fscache_cancel_op(op, true); if (stat_object_dead) fscache_stat(stat_object_dead); + _leave(" = -ENOBUFS [obj dead %d]", state); return -ENOBUFS; } return 0; @@ -453,17 +464,12 @@ int __fscache_read_or_alloc_page(struct fscache_cookie *cookie, fscache_stat(&fscache_n_retrieval_ops); - /* pin the netfs read context in case we need to do the actual netfs - * read because we've encountered a cache read failure */ - fscache_get_context(object->cookie, op->context); - /* we wait for the operation to become active, and then process it * *here*, in this thread, and not in the thread pool */ ret = fscache_wait_for_operation_activation( object, &op->op, __fscache_stat(&fscache_n_retrieval_op_waits), - __fscache_stat(&fscache_n_retrievals_object_dead), - fscache_do_cancel_retrieval); + __fscache_stat(&fscache_n_retrievals_object_dead)); if (ret < 0) goto error; @@ -503,7 +509,7 @@ nobufs_unlock: spin_unlock(&cookie->lock); if (wake_cookie) __fscache_wake_unused_cookie(cookie); - kfree(op); + fscache_put_retrieval(op); nobufs: fscache_stat(&fscache_n_retrievals_nobufs); _leave(" = -ENOBUFS"); @@ -584,17 +590,12 @@ int __fscache_read_or_alloc_pages(struct fscache_cookie *cookie, fscache_stat(&fscache_n_retrieval_ops); - /* pin the netfs read context in case we need to do the actual netfs - * read because we've encountered a cache read failure */ - fscache_get_context(object->cookie, op->context); - /* we wait for the operation to become active, and then process it * *here*, in this thread, and not in the thread pool */ ret = fscache_wait_for_operation_activation( object, &op->op, __fscache_stat(&fscache_n_retrieval_op_waits), - __fscache_stat(&fscache_n_retrievals_object_dead), - fscache_do_cancel_retrieval); + __fscache_stat(&fscache_n_retrievals_object_dead)); if (ret < 0) goto error; @@ -632,7 +633,7 @@ nobufs_unlock_dec: wake_cookie = __fscache_unuse_cookie(cookie); nobufs_unlock: spin_unlock(&cookie->lock); - kfree(op); + fscache_put_retrieval(op); if (wake_cookie) __fscache_wake_unused_cookie(cookie); nobufs: @@ -700,8 +701,7 @@ int __fscache_alloc_page(struct fscache_cookie *cookie, ret = fscache_wait_for_operation_activation( object, &op->op, __fscache_stat(&fscache_n_alloc_op_waits), - __fscache_stat(&fscache_n_allocs_object_dead), - fscache_do_cancel_retrieval); + __fscache_stat(&fscache_n_allocs_object_dead)); if (ret < 0) goto error; @@ -726,7 +726,7 @@ nobufs_unlock_dec: wake_cookie = __fscache_unuse_cookie(cookie); nobufs_unlock: spin_unlock(&cookie->lock); - kfree(op); + fscache_put_retrieval(op); if (wake_cookie) __fscache_wake_unused_cookie(cookie); nobufs: @@ -944,7 +944,7 @@ int __fscache_write_page(struct fscache_cookie *cookie, if (!op) goto nomem; - fscache_operation_init(&op->op, fscache_write_op, + fscache_operation_init(&op->op, fscache_write_op, NULL, fscache_release_write_op); op->op.flags = FSCACHE_OP_ASYNC | (1 << FSCACHE_OP_WAITING) | @@ -1016,7 +1016,7 @@ already_pending: spin_unlock(&object->lock); spin_unlock(&cookie->lock); radix_tree_preload_end(); - kfree(op); + fscache_put_operation(&op->op); fscache_stat(&fscache_n_stores_ok); _leave(" = 0"); return 0; @@ -1036,7 +1036,7 @@ nobufs_unlock_obj: nobufs: spin_unlock(&cookie->lock); radix_tree_preload_end(); - kfree(op); + fscache_put_operation(&op->op); if (wake_cookie) __fscache_wake_unused_cookie(cookie); fscache_stat(&fscache_n_stores_nobufs); @@ -1044,7 +1044,7 @@ nobufs: return -ENOBUFS; nomem_free: - kfree(op); + fscache_put_operation(&op->op); nomem: fscache_stat(&fscache_n_stores_oom); _leave(" = -ENOMEM"); diff --git a/fs/fscache/stats.c b/fs/fscache/stats.c index 40d13c70e..7cfa0aacd 100644 --- a/fs/fscache/stats.c +++ b/fs/fscache/stats.c @@ -23,6 +23,7 @@ atomic_t fscache_n_op_run; atomic_t fscache_n_op_enqueue; atomic_t fscache_n_op_requeue; atomic_t fscache_n_op_deferred_release; +atomic_t fscache_n_op_initialised; atomic_t fscache_n_op_release; atomic_t fscache_n_op_gc; atomic_t fscache_n_op_cancelled; @@ -130,6 +131,11 @@ atomic_t fscache_n_cop_write_page; atomic_t fscache_n_cop_uncache_page; atomic_t fscache_n_cop_dissociate_pages; +atomic_t fscache_n_cache_no_space_reject; +atomic_t fscache_n_cache_stale_objects; +atomic_t fscache_n_cache_retired_objects; +atomic_t fscache_n_cache_culled_objects; + /* * display the general statistics */ @@ -246,7 +252,8 @@ static int fscache_stats_show(struct seq_file *m, void *v) atomic_read(&fscache_n_op_enqueue), atomic_read(&fscache_n_op_cancelled), atomic_read(&fscache_n_op_rejected)); - seq_printf(m, "Ops : dfr=%u rel=%u gc=%u\n", + seq_printf(m, "Ops : ini=%u dfr=%u rel=%u gc=%u\n", + atomic_read(&fscache_n_op_initialised), atomic_read(&fscache_n_op_deferred_release), atomic_read(&fscache_n_op_release), atomic_read(&fscache_n_op_gc)); @@ -271,6 +278,11 @@ static int fscache_stats_show(struct seq_file *m, void *v) atomic_read(&fscache_n_cop_write_page), atomic_read(&fscache_n_cop_uncache_page), atomic_read(&fscache_n_cop_dissociate_pages)); + seq_printf(m, "CacheEv: nsp=%d stl=%d rtr=%d cul=%d\n", + atomic_read(&fscache_n_cache_no_space_reject), + atomic_read(&fscache_n_cache_stale_objects), + atomic_read(&fscache_n_cache_retired_objects), + atomic_read(&fscache_n_cache_culled_objects)); return 0; } diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index e5bbf748b..eae2c1126 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -489,6 +489,7 @@ static void cuse_fc_release(struct fuse_conn *fc) */ static int cuse_channel_open(struct inode *inode, struct file *file) { + struct fuse_dev *fud; struct cuse_conn *cc; int rc; @@ -499,17 +500,22 @@ static int cuse_channel_open(struct inode *inode, struct file *file) fuse_conn_init(&cc->fc); + fud = fuse_dev_alloc(&cc->fc); + if (!fud) { + kfree(cc); + return -ENOMEM; + } + INIT_LIST_HEAD(&cc->list); cc->fc.release = cuse_fc_release; - cc->fc.connected = 1; cc->fc.initialized = 1; rc = cuse_send_init(cc); if (rc) { - fuse_conn_put(&cc->fc); + fuse_dev_free(fud); return rc; } - file->private_data = &cc->fc; /* channel owns base reference to cc */ + file->private_data = fud; return 0; } @@ -527,7 +533,8 @@ static int cuse_channel_open(struct inode *inode, struct file *file) */ static int cuse_channel_release(struct inode *inode, struct file *file) { - struct cuse_conn *cc = fc_to_cc(file->private_data); + struct fuse_dev *fud = file->private_data; + struct cuse_conn *cc = fc_to_cc(fud->fc); int rc; /* remove from the conntbl, no more access from this point on */ diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index c8b68ab2e..ebb5e3745 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -25,13 +25,13 @@ MODULE_ALIAS("devname:fuse"); static struct kmem_cache *fuse_req_cachep; -static struct fuse_conn *fuse_get_conn(struct file *file) +static struct fuse_dev *fuse_get_dev(struct file *file) { /* * Lockless access is OK, because file->private data is set * once during mount and is valid until the file is released. */ - return file->private_data; + return ACCESS_ONCE(file->private_data); } static void fuse_request_init(struct fuse_req *req, struct page **pages, @@ -48,6 +48,7 @@ static void fuse_request_init(struct fuse_req *req, struct page **pages, req->pages = pages; req->page_descs = page_descs; req->max_pages = npages; + __set_bit(FR_PENDING, &req->flags); } static struct fuse_req *__fuse_request_alloc(unsigned npages, gfp_t flags) @@ -168,6 +169,10 @@ static struct fuse_req *__fuse_get_req(struct fuse_conn *fc, unsigned npages, if (!fc->connected) goto out; + err = -ECONNREFUSED; + if (fc->conn_error) + goto out; + req = fuse_request_alloc(npages); err = -ENOMEM; if (!req) { @@ -177,8 +182,10 @@ static struct fuse_req *__fuse_get_req(struct fuse_conn *fc, unsigned npages, } fuse_req_init_context(req); - req->waiting = 1; - req->background = for_background; + __set_bit(FR_WAITING, &req->flags); + if (for_background) + __set_bit(FR_BACKGROUND, &req->flags); + return req; out: @@ -268,15 +275,15 @@ struct fuse_req *fuse_get_req_nofail_nopages(struct fuse_conn *fc, req = get_reserved_req(fc, file); fuse_req_init_context(req); - req->waiting = 1; - req->background = 0; + __set_bit(FR_WAITING, &req->flags); + __clear_bit(FR_BACKGROUND, &req->flags); return req; } void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) { if (atomic_dec_and_test(&req->count)) { - if (unlikely(req->background)) { + if (test_bit(FR_BACKGROUND, &req->flags)) { /* * We get here in the unlikely case that a background * request was allocated but not sent @@ -287,8 +294,10 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) spin_unlock(&fc->lock); } - if (req->waiting) + if (test_bit(FR_WAITING, &req->flags)) { + __clear_bit(FR_WAITING, &req->flags); atomic_dec(&fc->num_waiting); + } if (req->stolen_file) put_reserved_req(fc, req); @@ -309,46 +318,38 @@ static unsigned len_args(unsigned numargs, struct fuse_arg *args) return nbytes; } -static u64 fuse_get_unique(struct fuse_conn *fc) +static u64 fuse_get_unique(struct fuse_iqueue *fiq) { - fc->reqctr++; - /* zero is special */ - if (fc->reqctr == 0) - fc->reqctr = 1; - - return fc->reqctr; + return ++fiq->reqctr; } -static void queue_request(struct fuse_conn *fc, struct fuse_req *req) +static void queue_request(struct fuse_iqueue *fiq, struct fuse_req *req) { req->in.h.len = sizeof(struct fuse_in_header) + len_args(req->in.numargs, (struct fuse_arg *) req->in.args); - list_add_tail(&req->list, &fc->pending); - req->state = FUSE_REQ_PENDING; - if (!req->waiting) { - req->waiting = 1; - atomic_inc(&fc->num_waiting); - } - wake_up(&fc->waitq); - kill_fasync(&fc->fasync, SIGIO, POLL_IN); + list_add_tail(&req->list, &fiq->pending); + wake_up_locked(&fiq->waitq); + kill_fasync(&fiq->fasync, SIGIO, POLL_IN); } void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget, u64 nodeid, u64 nlookup) { + struct fuse_iqueue *fiq = &fc->iq; + forget->forget_one.nodeid = nodeid; forget->forget_one.nlookup = nlookup; - spin_lock(&fc->lock); - if (fc->connected) { - fc->forget_list_tail->next = forget; - fc->forget_list_tail = forget; - wake_up(&fc->waitq); - kill_fasync(&fc->fasync, SIGIO, POLL_IN); + spin_lock(&fiq->waitq.lock); + if (fiq->connected) { + fiq->forget_list_tail->next = forget; + fiq->forget_list_tail = forget; + wake_up_locked(&fiq->waitq); + kill_fasync(&fiq->fasync, SIGIO, POLL_IN); } else { kfree(forget); } - spin_unlock(&fc->lock); + spin_unlock(&fiq->waitq.lock); } static void flush_bg_queue(struct fuse_conn *fc) @@ -356,12 +357,15 @@ static void flush_bg_queue(struct fuse_conn *fc) while (fc->active_background < fc->max_background && !list_empty(&fc->bg_queue)) { struct fuse_req *req; + struct fuse_iqueue *fiq = &fc->iq; req = list_entry(fc->bg_queue.next, struct fuse_req, list); list_del(&req->list); fc->active_background++; - req->in.h.unique = fuse_get_unique(fc); - queue_request(fc, req); + spin_lock(&fiq->waitq.lock); + req->in.h.unique = fuse_get_unique(fiq); + queue_request(fiq, req); + spin_unlock(&fiq->waitq.lock); } } @@ -372,20 +376,22 @@ static void flush_bg_queue(struct fuse_conn *fc) * was closed. The requester thread is woken up (if still waiting), * the 'end' callback is called if given, else the reference to the * request is released - * - * Called with fc->lock, unlocks it */ static void request_end(struct fuse_conn *fc, struct fuse_req *req) -__releases(fc->lock) { - void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; - req->end = NULL; - list_del(&req->list); - list_del(&req->intr_entry); - req->state = FUSE_REQ_FINISHED; - if (req->background) { - req->background = 0; + struct fuse_iqueue *fiq = &fc->iq; + + if (test_and_set_bit(FR_FINISHED, &req->flags)) + return; + spin_lock(&fiq->waitq.lock); + list_del_init(&req->intr_entry); + spin_unlock(&fiq->waitq.lock); + WARN_ON(test_bit(FR_PENDING, &req->flags)); + WARN_ON(test_bit(FR_SENT, &req->flags)); + if (test_bit(FR_BACKGROUND, &req->flags)) { + spin_lock(&fc->lock); + clear_bit(FR_BACKGROUND, &req->flags); if (fc->num_background == fc->max_background) fc->blocked = 0; @@ -401,122 +407,105 @@ __releases(fc->lock) fc->num_background--; fc->active_background--; flush_bg_queue(fc); + spin_unlock(&fc->lock); } - spin_unlock(&fc->lock); wake_up(&req->waitq); - if (end) - end(fc, req); + if (req->end) + req->end(fc, req); fuse_put_request(fc, req); } -static void wait_answer_interruptible(struct fuse_conn *fc, - struct fuse_req *req) -__releases(fc->lock) -__acquires(fc->lock) -{ - if (signal_pending(current)) - return; - - spin_unlock(&fc->lock); - wait_event_interruptible(req->waitq, req->state == FUSE_REQ_FINISHED); - spin_lock(&fc->lock); -} - -static void queue_interrupt(struct fuse_conn *fc, struct fuse_req *req) +static void queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req) { - list_add_tail(&req->intr_entry, &fc->interrupts); - wake_up(&fc->waitq); - kill_fasync(&fc->fasync, SIGIO, POLL_IN); + spin_lock(&fiq->waitq.lock); + if (list_empty(&req->intr_entry)) { + list_add_tail(&req->intr_entry, &fiq->interrupts); + wake_up_locked(&fiq->waitq); + } + spin_unlock(&fiq->waitq.lock); + kill_fasync(&fiq->fasync, SIGIO, POLL_IN); } static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) -__releases(fc->lock) -__acquires(fc->lock) { + struct fuse_iqueue *fiq = &fc->iq; + int err; + if (!fc->no_interrupt) { /* Any signal may interrupt this */ - wait_answer_interruptible(fc, req); - - if (req->aborted) - goto aborted; - if (req->state == FUSE_REQ_FINISHED) + err = wait_event_interruptible(req->waitq, + test_bit(FR_FINISHED, &req->flags)); + if (!err) return; - req->interrupted = 1; - if (req->state == FUSE_REQ_SENT) - queue_interrupt(fc, req); + set_bit(FR_INTERRUPTED, &req->flags); + /* matches barrier in fuse_dev_do_read() */ + smp_mb__after_atomic(); + if (test_bit(FR_SENT, &req->flags)) + queue_interrupt(fiq, req); } - if (!req->force) { + if (!test_bit(FR_FORCE, &req->flags)) { sigset_t oldset; /* Only fatal signals may interrupt this */ block_sigs(&oldset); - wait_answer_interruptible(fc, req); + err = wait_event_interruptible(req->waitq, + test_bit(FR_FINISHED, &req->flags)); restore_sigs(&oldset); - if (req->aborted) - goto aborted; - if (req->state == FUSE_REQ_FINISHED) + if (!err) return; + spin_lock(&fiq->waitq.lock); /* Request is not yet in userspace, bail out */ - if (req->state == FUSE_REQ_PENDING) { + if (test_bit(FR_PENDING, &req->flags)) { list_del(&req->list); + spin_unlock(&fiq->waitq.lock); __fuse_put_request(req); req->out.h.error = -EINTR; return; } + spin_unlock(&fiq->waitq.lock); } /* * Either request is already in userspace, or it was forced. * Wait it out. */ - spin_unlock(&fc->lock); - wait_event(req->waitq, req->state == FUSE_REQ_FINISHED); - spin_lock(&fc->lock); - - if (!req->aborted) - return; - - aborted: - BUG_ON(req->state != FUSE_REQ_FINISHED); - if (req->locked) { - /* This is uninterruptible sleep, because data is - being copied to/from the buffers of req. During - locked state, there mustn't be any filesystem - operation (e.g. page fault), since that could lead - to deadlock */ - spin_unlock(&fc->lock); - wait_event(req->waitq, !req->locked); - spin_lock(&fc->lock); - } + wait_event(req->waitq, test_bit(FR_FINISHED, &req->flags)); } static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req) { - BUG_ON(req->background); - spin_lock(&fc->lock); - if (!fc->connected) + struct fuse_iqueue *fiq = &fc->iq; + + BUG_ON(test_bit(FR_BACKGROUND, &req->flags)); + spin_lock(&fiq->waitq.lock); + if (!fiq->connected) { + spin_unlock(&fiq->waitq.lock); req->out.h.error = -ENOTCONN; - else if (fc->conn_error) - req->out.h.error = -ECONNREFUSED; - else { - req->in.h.unique = fuse_get_unique(fc); - queue_request(fc, req); + } else { + req->in.h.unique = fuse_get_unique(fiq); + queue_request(fiq, req); /* acquire extra reference, since request is still needed after request_end() */ __fuse_get_request(req); + spin_unlock(&fiq->waitq.lock); request_wait_answer(fc, req); + /* Pairs with smp_wmb() in request_end() */ + smp_rmb(); } - spin_unlock(&fc->lock); } void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req) { - req->isreply = 1; + __set_bit(FR_ISREPLY, &req->flags); + if (!test_bit(FR_WAITING, &req->flags)) { + __set_bit(FR_WAITING, &req->flags); + atomic_inc(&fc->num_waiting); + } __fuse_request_send(fc, req); } EXPORT_SYMBOL_GPL(fuse_request_send); @@ -586,10 +575,20 @@ ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args) return ret; } -static void fuse_request_send_nowait_locked(struct fuse_conn *fc, - struct fuse_req *req) +/* + * Called under fc->lock + * + * fc->connected must have been checked previously + */ +void fuse_request_send_background_locked(struct fuse_conn *fc, + struct fuse_req *req) { - BUG_ON(!req->background); + BUG_ON(!test_bit(FR_BACKGROUND, &req->flags)); + if (!test_bit(FR_WAITING, &req->flags)) { + __set_bit(FR_WAITING, &req->flags); + atomic_inc(&fc->num_waiting); + } + __set_bit(FR_ISREPLY, &req->flags); fc->num_background++; if (fc->num_background == fc->max_background) fc->blocked = 1; @@ -602,54 +601,40 @@ static void fuse_request_send_nowait_locked(struct fuse_conn *fc, flush_bg_queue(fc); } -static void fuse_request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) +void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req) { + BUG_ON(!req->end); spin_lock(&fc->lock); if (fc->connected) { - fuse_request_send_nowait_locked(fc, req); + fuse_request_send_background_locked(fc, req); spin_unlock(&fc->lock); } else { + spin_unlock(&fc->lock); req->out.h.error = -ENOTCONN; - request_end(fc, req); + req->end(fc, req); + fuse_put_request(fc, req); } } - -void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req) -{ - req->isreply = 1; - fuse_request_send_nowait(fc, req); -} EXPORT_SYMBOL_GPL(fuse_request_send_background); static int fuse_request_send_notify_reply(struct fuse_conn *fc, struct fuse_req *req, u64 unique) { int err = -ENODEV; + struct fuse_iqueue *fiq = &fc->iq; - req->isreply = 0; + __clear_bit(FR_ISREPLY, &req->flags); req->in.h.unique = unique; - spin_lock(&fc->lock); - if (fc->connected) { - queue_request(fc, req); + spin_lock(&fiq->waitq.lock); + if (fiq->connected) { + queue_request(fiq, req); err = 0; } - spin_unlock(&fc->lock); + spin_unlock(&fiq->waitq.lock); return err; } -/* - * Called under fc->lock - * - * fc->connected must have been checked previously - */ -void fuse_request_send_background_locked(struct fuse_conn *fc, - struct fuse_req *req) -{ - req->isreply = 1; - fuse_request_send_nowait_locked(fc, req); -} - void fuse_force_forget(struct file *file, u64 nodeid) { struct inode *inode = file_inode(file); @@ -665,7 +650,7 @@ void fuse_force_forget(struct file *file, u64 nodeid) req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; - req->isreply = 0; + __clear_bit(FR_ISREPLY, &req->flags); __fuse_request_send(fc, req); /* ignore errors */ fuse_put_request(fc, req); @@ -676,38 +661,39 @@ void fuse_force_forget(struct file *file, u64 nodeid) * anything that could cause a page-fault. If the request was already * aborted bail out. */ -static int lock_request(struct fuse_conn *fc, struct fuse_req *req) +static int lock_request(struct fuse_req *req) { int err = 0; if (req) { - spin_lock(&fc->lock); - if (req->aborted) + spin_lock(&req->waitq.lock); + if (test_bit(FR_ABORTED, &req->flags)) err = -ENOENT; else - req->locked = 1; - spin_unlock(&fc->lock); + set_bit(FR_LOCKED, &req->flags); + spin_unlock(&req->waitq.lock); } return err; } /* - * Unlock request. If it was aborted during being locked, the - * requester thread is currently waiting for it to be unlocked, so - * wake it up. + * Unlock request. If it was aborted while locked, caller is responsible + * for unlocking and ending the request. */ -static void unlock_request(struct fuse_conn *fc, struct fuse_req *req) +static int unlock_request(struct fuse_req *req) { + int err = 0; if (req) { - spin_lock(&fc->lock); - req->locked = 0; - if (req->aborted) - wake_up(&req->waitq); - spin_unlock(&fc->lock); + spin_lock(&req->waitq.lock); + if (test_bit(FR_ABORTED, &req->flags)) + err = -ENOENT; + else + clear_bit(FR_LOCKED, &req->flags); + spin_unlock(&req->waitq.lock); } + return err; } struct fuse_copy_state { - struct fuse_conn *fc; int write; struct fuse_req *req; struct iov_iter *iter; @@ -721,13 +707,10 @@ struct fuse_copy_state { unsigned move_pages:1; }; -static void fuse_copy_init(struct fuse_copy_state *cs, - struct fuse_conn *fc, - int write, +static void fuse_copy_init(struct fuse_copy_state *cs, int write, struct iov_iter *iter) { memset(cs, 0, sizeof(*cs)); - cs->fc = fc; cs->write = write; cs->iter = iter; } @@ -760,7 +743,10 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) struct page *page; int err; - unlock_request(cs->fc, cs->req); + err = unlock_request(cs->req); + if (err) + return err; + fuse_copy_finish(cs); if (cs->pipebufs) { struct pipe_buffer *buf = cs->pipebufs; @@ -809,7 +795,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) iov_iter_advance(cs->iter, err); } - return lock_request(cs->fc, cs->req); + return lock_request(cs->req); } /* Do as much copy to/from userspace buffer as we can */ @@ -860,7 +846,10 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep) struct page *newpage; struct pipe_buffer *buf = cs->pipebufs; - unlock_request(cs->fc, cs->req); + err = unlock_request(cs->req); + if (err) + return err; + fuse_copy_finish(cs); err = buf->ops->confirm(cs->pipe, buf); @@ -914,12 +903,12 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep) lru_cache_add_file(newpage); err = 0; - spin_lock(&cs->fc->lock); - if (cs->req->aborted) + spin_lock(&cs->req->waitq.lock); + if (test_bit(FR_ABORTED, &cs->req->flags)) err = -ENOENT; else *pagep = newpage; - spin_unlock(&cs->fc->lock); + spin_unlock(&cs->req->waitq.lock); if (err) { unlock_page(newpage); @@ -939,7 +928,7 @@ out_fallback: cs->pg = buf->page; cs->offset = buf->offset; - err = lock_request(cs->fc, cs->req); + err = lock_request(cs->req); if (err) return err; @@ -950,11 +939,15 @@ static int fuse_ref_page(struct fuse_copy_state *cs, struct page *page, unsigned offset, unsigned count) { struct pipe_buffer *buf; + int err; if (cs->nr_segs == cs->pipe->buffers) return -EIO; - unlock_request(cs->fc, cs->req); + err = unlock_request(cs->req); + if (err) + return err; + fuse_copy_finish(cs); buf = cs->pipebufs; @@ -1065,36 +1058,15 @@ static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs, return err; } -static int forget_pending(struct fuse_conn *fc) +static int forget_pending(struct fuse_iqueue *fiq) { - return fc->forget_list_head.next != NULL; + return fiq->forget_list_head.next != NULL; } -static int request_pending(struct fuse_conn *fc) +static int request_pending(struct fuse_iqueue *fiq) { - return !list_empty(&fc->pending) || !list_empty(&fc->interrupts) || - forget_pending(fc); -} - -/* Wait until a request is available on the pending list */ -static void request_wait(struct fuse_conn *fc) -__releases(fc->lock) -__acquires(fc->lock) -{ - DECLARE_WAITQUEUE(wait, current); - - add_wait_queue_exclusive(&fc->waitq, &wait); - while (fc->connected && !request_pending(fc)) { - set_current_state(TASK_INTERRUPTIBLE); - if (signal_pending(current)) - break; - - spin_unlock(&fc->lock); - schedule(); - spin_lock(&fc->lock); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&fc->waitq, &wait); + return !list_empty(&fiq->pending) || !list_empty(&fiq->interrupts) || + forget_pending(fiq); } /* @@ -1103,11 +1075,12 @@ __acquires(fc->lock) * Unlike other requests this is assembled on demand, without a need * to allocate a separate fuse_req structure. * - * Called with fc->lock held, releases it + * Called with fiq->waitq.lock held, releases it */ -static int fuse_read_interrupt(struct fuse_conn *fc, struct fuse_copy_state *cs, +static int fuse_read_interrupt(struct fuse_iqueue *fiq, + struct fuse_copy_state *cs, size_t nbytes, struct fuse_req *req) -__releases(fc->lock) +__releases(fiq->waitq.lock) { struct fuse_in_header ih; struct fuse_interrupt_in arg; @@ -1115,7 +1088,7 @@ __releases(fc->lock) int err; list_del_init(&req->intr_entry); - req->intr_unique = fuse_get_unique(fc); + req->intr_unique = fuse_get_unique(fiq); memset(&ih, 0, sizeof(ih)); memset(&arg, 0, sizeof(arg)); ih.len = reqsize; @@ -1123,7 +1096,7 @@ __releases(fc->lock) ih.unique = req->intr_unique; arg.unique = req->in.h.unique; - spin_unlock(&fc->lock); + spin_unlock(&fiq->waitq.lock); if (nbytes < reqsize) return -EINVAL; @@ -1135,21 +1108,21 @@ __releases(fc->lock) return err ? err : reqsize; } -static struct fuse_forget_link *dequeue_forget(struct fuse_conn *fc, +static struct fuse_forget_link *dequeue_forget(struct fuse_iqueue *fiq, unsigned max, unsigned *countp) { - struct fuse_forget_link *head = fc->forget_list_head.next; + struct fuse_forget_link *head = fiq->forget_list_head.next; struct fuse_forget_link **newhead = &head; unsigned count; for (count = 0; *newhead != NULL && count < max; count++) newhead = &(*newhead)->next; - fc->forget_list_head.next = *newhead; + fiq->forget_list_head.next = *newhead; *newhead = NULL; - if (fc->forget_list_head.next == NULL) - fc->forget_list_tail = &fc->forget_list_head; + if (fiq->forget_list_head.next == NULL) + fiq->forget_list_tail = &fiq->forget_list_head; if (countp != NULL) *countp = count; @@ -1157,24 +1130,24 @@ static struct fuse_forget_link *dequeue_forget(struct fuse_conn *fc, return head; } -static int fuse_read_single_forget(struct fuse_conn *fc, +static int fuse_read_single_forget(struct fuse_iqueue *fiq, struct fuse_copy_state *cs, size_t nbytes) -__releases(fc->lock) +__releases(fiq->waitq.lock) { int err; - struct fuse_forget_link *forget = dequeue_forget(fc, 1, NULL); + struct fuse_forget_link *forget = dequeue_forget(fiq, 1, NULL); struct fuse_forget_in arg = { .nlookup = forget->forget_one.nlookup, }; struct fuse_in_header ih = { .opcode = FUSE_FORGET, .nodeid = forget->forget_one.nodeid, - .unique = fuse_get_unique(fc), + .unique = fuse_get_unique(fiq), .len = sizeof(ih) + sizeof(arg), }; - spin_unlock(&fc->lock); + spin_unlock(&fiq->waitq.lock); kfree(forget); if (nbytes < ih.len) return -EINVAL; @@ -1190,9 +1163,9 @@ __releases(fc->lock) return ih.len; } -static int fuse_read_batch_forget(struct fuse_conn *fc, +static int fuse_read_batch_forget(struct fuse_iqueue *fiq, struct fuse_copy_state *cs, size_t nbytes) -__releases(fc->lock) +__releases(fiq->waitq.lock) { int err; unsigned max_forgets; @@ -1201,18 +1174,18 @@ __releases(fc->lock) struct fuse_batch_forget_in arg = { .count = 0 }; struct fuse_in_header ih = { .opcode = FUSE_BATCH_FORGET, - .unique = fuse_get_unique(fc), + .unique = fuse_get_unique(fiq), .len = sizeof(ih) + sizeof(arg), }; if (nbytes < ih.len) { - spin_unlock(&fc->lock); + spin_unlock(&fiq->waitq.lock); return -EINVAL; } max_forgets = (nbytes - ih.len) / sizeof(struct fuse_forget_one); - head = dequeue_forget(fc, max_forgets, &count); - spin_unlock(&fc->lock); + head = dequeue_forget(fiq, max_forgets, &count); + spin_unlock(&fiq->waitq.lock); arg.count = count; ih.len += count * sizeof(struct fuse_forget_one); @@ -1239,14 +1212,15 @@ __releases(fc->lock) return ih.len; } -static int fuse_read_forget(struct fuse_conn *fc, struct fuse_copy_state *cs, +static int fuse_read_forget(struct fuse_conn *fc, struct fuse_iqueue *fiq, + struct fuse_copy_state *cs, size_t nbytes) -__releases(fc->lock) +__releases(fiq->waitq.lock) { - if (fc->minor < 16 || fc->forget_list_head.next->next == NULL) - return fuse_read_single_forget(fc, cs, nbytes); + if (fc->minor < 16 || fiq->forget_list_head.next->next == NULL) + return fuse_read_single_forget(fiq, cs, nbytes); else - return fuse_read_batch_forget(fc, cs, nbytes); + return fuse_read_batch_forget(fiq, cs, nbytes); } /* @@ -1258,46 +1232,51 @@ __releases(fc->lock) * request_end(). Otherwise add it to the processing list, and set * the 'sent' flag. */ -static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, +static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file, struct fuse_copy_state *cs, size_t nbytes) { - int err; + ssize_t err; + struct fuse_conn *fc = fud->fc; + struct fuse_iqueue *fiq = &fc->iq; + struct fuse_pqueue *fpq = &fud->pq; struct fuse_req *req; struct fuse_in *in; unsigned reqsize; restart: - spin_lock(&fc->lock); + spin_lock(&fiq->waitq.lock); err = -EAGAIN; - if ((file->f_flags & O_NONBLOCK) && fc->connected && - !request_pending(fc)) + if ((file->f_flags & O_NONBLOCK) && fiq->connected && + !request_pending(fiq)) goto err_unlock; - request_wait(fc); - err = -ENODEV; - if (!fc->connected) + err = wait_event_interruptible_exclusive_locked(fiq->waitq, + !fiq->connected || request_pending(fiq)); + if (err) goto err_unlock; - err = -ERESTARTSYS; - if (!request_pending(fc)) + + err = -ENODEV; + if (!fiq->connected) goto err_unlock; - if (!list_empty(&fc->interrupts)) { - req = list_entry(fc->interrupts.next, struct fuse_req, + if (!list_empty(&fiq->interrupts)) { + req = list_entry(fiq->interrupts.next, struct fuse_req, intr_entry); - return fuse_read_interrupt(fc, cs, nbytes, req); + return fuse_read_interrupt(fiq, cs, nbytes, req); } - if (forget_pending(fc)) { - if (list_empty(&fc->pending) || fc->forget_batch-- > 0) - return fuse_read_forget(fc, cs, nbytes); + if (forget_pending(fiq)) { + if (list_empty(&fiq->pending) || fiq->forget_batch-- > 0) + return fuse_read_forget(fc, fiq, cs, nbytes); - if (fc->forget_batch <= -8) - fc->forget_batch = 16; + if (fiq->forget_batch <= -8) + fiq->forget_batch = 16; } - req = list_entry(fc->pending.next, struct fuse_req, list); - req->state = FUSE_REQ_READING; - list_move(&req->list, &fc->io); + req = list_entry(fiq->pending.next, struct fuse_req, list); + clear_bit(FR_PENDING, &req->flags); + list_del_init(&req->list); + spin_unlock(&fiq->waitq.lock); in = &req->in; reqsize = in->h.len; @@ -1310,37 +1289,48 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, request_end(fc, req); goto restart; } - spin_unlock(&fc->lock); + spin_lock(&fpq->lock); + list_add(&req->list, &fpq->io); + spin_unlock(&fpq->lock); cs->req = req; err = fuse_copy_one(cs, &in->h, sizeof(in->h)); if (!err) err = fuse_copy_args(cs, in->numargs, in->argpages, (struct fuse_arg *) in->args, 0); fuse_copy_finish(cs); - spin_lock(&fc->lock); - req->locked = 0; - if (req->aborted) { - request_end(fc, req); - return -ENODEV; + spin_lock(&fpq->lock); + clear_bit(FR_LOCKED, &req->flags); + if (!fpq->connected) { + err = -ENODEV; + goto out_end; } if (err) { req->out.h.error = -EIO; - request_end(fc, req); - return err; + goto out_end; } - if (!req->isreply) - request_end(fc, req); - else { - req->state = FUSE_REQ_SENT; - list_move_tail(&req->list, &fc->processing); - if (req->interrupted) - queue_interrupt(fc, req); - spin_unlock(&fc->lock); + if (!test_bit(FR_ISREPLY, &req->flags)) { + err = reqsize; + goto out_end; } + list_move_tail(&req->list, &fpq->processing); + spin_unlock(&fpq->lock); + set_bit(FR_SENT, &req->flags); + /* matches barrier in request_wait_answer() */ + smp_mb__after_atomic(); + if (test_bit(FR_INTERRUPTED, &req->flags)) + queue_interrupt(fiq, req); + return reqsize; +out_end: + if (!test_bit(FR_PRIVATE, &req->flags)) + list_del_init(&req->list); + spin_unlock(&fpq->lock); + request_end(fc, req); + return err; + err_unlock: - spin_unlock(&fc->lock); + spin_unlock(&fiq->waitq.lock); return err; } @@ -1359,16 +1349,17 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, struct iov_iter *to) { struct fuse_copy_state cs; struct file *file = iocb->ki_filp; - struct fuse_conn *fc = fuse_get_conn(file); - if (!fc) + struct fuse_dev *fud = fuse_get_dev(file); + + if (!fud) return -EPERM; if (!iter_is_iovec(to)) return -EINVAL; - fuse_copy_init(&cs, fc, 1, to); + fuse_copy_init(&cs, 1, to); - return fuse_dev_do_read(fc, file, &cs, iov_iter_count(to)); + return fuse_dev_do_read(fud, file, &cs, iov_iter_count(to)); } static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos, @@ -1380,18 +1371,19 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos, int do_wakeup = 0; struct pipe_buffer *bufs; struct fuse_copy_state cs; - struct fuse_conn *fc = fuse_get_conn(in); - if (!fc) + struct fuse_dev *fud = fuse_get_dev(in); + + if (!fud) return -EPERM; bufs = kmalloc(pipe->buffers * sizeof(struct pipe_buffer), GFP_KERNEL); if (!bufs) return -ENOMEM; - fuse_copy_init(&cs, fc, 1, NULL); + fuse_copy_init(&cs, 1, NULL); cs.pipebufs = bufs; cs.pipe = pipe; - ret = fuse_dev_do_read(fc, in, &cs, len); + ret = fuse_dev_do_read(fud, in, &cs, len); if (ret < 0) goto out; @@ -1830,11 +1822,11 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, } /* Look up request on processing list by unique ID */ -static struct fuse_req *request_find(struct fuse_conn *fc, u64 unique) +static struct fuse_req *request_find(struct fuse_pqueue *fpq, u64 unique) { struct fuse_req *req; - list_for_each_entry(req, &fc->processing, list) { + list_for_each_entry(req, &fpq->processing, list) { if (req->in.h.unique == unique || req->intr_unique == unique) return req; } @@ -1871,10 +1863,12 @@ static int copy_out_args(struct fuse_copy_state *cs, struct fuse_out *out, * it from the list and copy the rest of the buffer to the request. * The request is finished by calling request_end() */ -static ssize_t fuse_dev_do_write(struct fuse_conn *fc, +static ssize_t fuse_dev_do_write(struct fuse_dev *fud, struct fuse_copy_state *cs, size_t nbytes) { int err; + struct fuse_conn *fc = fud->fc; + struct fuse_pqueue *fpq = &fud->pq; struct fuse_req *req; struct fuse_out_header oh; @@ -1902,63 +1896,60 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, if (oh.error <= -1000 || oh.error > 0) goto err_finish; - spin_lock(&fc->lock); + spin_lock(&fpq->lock); err = -ENOENT; - if (!fc->connected) - goto err_unlock; + if (!fpq->connected) + goto err_unlock_pq; - req = request_find(fc, oh.unique); + req = request_find(fpq, oh.unique); if (!req) - goto err_unlock; + goto err_unlock_pq; - if (req->aborted) { - spin_unlock(&fc->lock); - fuse_copy_finish(cs); - spin_lock(&fc->lock); - request_end(fc, req); - return -ENOENT; - } /* Is it an interrupt reply? */ if (req->intr_unique == oh.unique) { + spin_unlock(&fpq->lock); + err = -EINVAL; if (nbytes != sizeof(struct fuse_out_header)) - goto err_unlock; + goto err_finish; if (oh.error == -ENOSYS) fc->no_interrupt = 1; else if (oh.error == -EAGAIN) - queue_interrupt(fc, req); + queue_interrupt(&fc->iq, req); - spin_unlock(&fc->lock); fuse_copy_finish(cs); return nbytes; } - req->state = FUSE_REQ_WRITING; - list_move(&req->list, &fc->io); + clear_bit(FR_SENT, &req->flags); + list_move(&req->list, &fpq->io); req->out.h = oh; - req->locked = 1; + set_bit(FR_LOCKED, &req->flags); + spin_unlock(&fpq->lock); cs->req = req; if (!req->out.page_replace) cs->move_pages = 0; - spin_unlock(&fc->lock); err = copy_out_args(cs, &req->out, nbytes); fuse_copy_finish(cs); - spin_lock(&fc->lock); - req->locked = 0; - if (!err) { - if (req->aborted) - err = -ENOENT; - } else if (!req->aborted) + spin_lock(&fpq->lock); + clear_bit(FR_LOCKED, &req->flags); + if (!fpq->connected) + err = -ENOENT; + else if (err) req->out.h.error = -EIO; + if (!test_bit(FR_PRIVATE, &req->flags)) + list_del_init(&req->list); + spin_unlock(&fpq->lock); + request_end(fc, req); return err ? err : nbytes; - err_unlock: - spin_unlock(&fc->lock); + err_unlock_pq: + spin_unlock(&fpq->lock); err_finish: fuse_copy_finish(cs); return err; @@ -1967,16 +1958,17 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, static ssize_t fuse_dev_write(struct kiocb *iocb, struct iov_iter *from) { struct fuse_copy_state cs; - struct fuse_conn *fc = fuse_get_conn(iocb->ki_filp); - if (!fc) + struct fuse_dev *fud = fuse_get_dev(iocb->ki_filp); + + if (!fud) return -EPERM; if (!iter_is_iovec(from)) return -EINVAL; - fuse_copy_init(&cs, fc, 0, from); + fuse_copy_init(&cs, 0, from); - return fuse_dev_do_write(fc, &cs, iov_iter_count(from)); + return fuse_dev_do_write(fud, &cs, iov_iter_count(from)); } static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, @@ -1987,12 +1979,12 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, unsigned idx; struct pipe_buffer *bufs; struct fuse_copy_state cs; - struct fuse_conn *fc; + struct fuse_dev *fud; size_t rem; ssize_t ret; - fc = fuse_get_conn(out); - if (!fc) + fud = fuse_get_dev(out); + if (!fud) return -EPERM; bufs = kmalloc(pipe->buffers * sizeof(struct pipe_buffer), GFP_KERNEL); @@ -2039,7 +2031,7 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, } pipe_unlock(pipe); - fuse_copy_init(&cs, fc, 0, NULL); + fuse_copy_init(&cs, 0, NULL); cs.pipebufs = bufs; cs.nr_segs = nbuf; cs.pipe = pipe; @@ -2047,7 +2039,7 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, if (flags & SPLICE_F_MOVE) cs.move_pages = 1; - ret = fuse_dev_do_write(fc, &cs, len); + ret = fuse_dev_do_write(fud, &cs, len); for (idx = 0; idx < nbuf; idx++) { struct pipe_buffer *buf = &bufs[idx]; @@ -2061,18 +2053,21 @@ out: static unsigned fuse_dev_poll(struct file *file, poll_table *wait) { unsigned mask = POLLOUT | POLLWRNORM; - struct fuse_conn *fc = fuse_get_conn(file); - if (!fc) + struct fuse_iqueue *fiq; + struct fuse_dev *fud = fuse_get_dev(file); + + if (!fud) return POLLERR; - poll_wait(file, &fc->waitq, wait); + fiq = &fud->fc->iq; + poll_wait(file, &fiq->waitq, wait); - spin_lock(&fc->lock); - if (!fc->connected) + spin_lock(&fiq->waitq.lock); + if (!fiq->connected) mask = POLLERR; - else if (request_pending(fc)) + else if (request_pending(fiq)) mask |= POLLIN | POLLRDNORM; - spin_unlock(&fc->lock); + spin_unlock(&fiq->waitq.lock); return mask; } @@ -2083,67 +2078,18 @@ static unsigned fuse_dev_poll(struct file *file, poll_table *wait) * This function releases and reacquires fc->lock */ static void end_requests(struct fuse_conn *fc, struct list_head *head) -__releases(fc->lock) -__acquires(fc->lock) { while (!list_empty(head)) { struct fuse_req *req; req = list_entry(head->next, struct fuse_req, list); req->out.h.error = -ECONNABORTED; - request_end(fc, req); - spin_lock(&fc->lock); - } -} - -/* - * Abort requests under I/O - * - * The requests are set to aborted and finished, and the request - * waiter is woken up. This will make request_wait_answer() wait - * until the request is unlocked and then return. - * - * If the request is asynchronous, then the end function needs to be - * called after waiting for the request to be unlocked (if it was - * locked). - */ -static void end_io_requests(struct fuse_conn *fc) -__releases(fc->lock) -__acquires(fc->lock) -{ - while (!list_empty(&fc->io)) { - struct fuse_req *req = - list_entry(fc->io.next, struct fuse_req, list); - void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; - - req->aborted = 1; - req->out.h.error = -ECONNABORTED; - req->state = FUSE_REQ_FINISHED; + clear_bit(FR_PENDING, &req->flags); + clear_bit(FR_SENT, &req->flags); list_del_init(&req->list); - wake_up(&req->waitq); - if (end) { - req->end = NULL; - __fuse_get_request(req); - spin_unlock(&fc->lock); - wait_event(req->waitq, !req->locked); - end(fc, req); - fuse_put_request(fc, req); - spin_lock(&fc->lock); - } + request_end(fc, req); } } -static void end_queued_requests(struct fuse_conn *fc) -__releases(fc->lock) -__acquires(fc->lock) -{ - fc->max_background = UINT_MAX; - flush_bg_queue(fc); - end_requests(fc, &fc->pending); - end_requests(fc, &fc->processing); - while (forget_pending(fc)) - kfree(dequeue_forget(fc, 1, NULL)); -} - static void end_polls(struct fuse_conn *fc) { struct rb_node *p; @@ -2162,67 +2108,164 @@ static void end_polls(struct fuse_conn *fc) /* * Abort all requests. * - * Emergency exit in case of a malicious or accidental deadlock, or - * just a hung filesystem. - * - * The same effect is usually achievable through killing the - * filesystem daemon and all users of the filesystem. The exception - * is the combination of an asynchronous request and the tricky - * deadlock (see Documentation/filesystems/fuse.txt). + * Emergency exit in case of a malicious or accidental deadlock, or just a hung + * filesystem. * - * During the aborting, progression of requests from the pending and - * processing lists onto the io list, and progression of new requests - * onto the pending list is prevented by req->connected being false. + * The same effect is usually achievable through killing the filesystem daemon + * and all users of the filesystem. The exception is the combination of an + * asynchronous request and the tricky deadlock (see + * Documentation/filesystems/fuse.txt). * - * Progression of requests under I/O to the processing list is - * prevented by the req->aborted flag being true for these requests. - * For this reason requests on the io list must be aborted first. + * Aborting requests under I/O goes as follows: 1: Separate out unlocked + * requests, they should be finished off immediately. Locked requests will be + * finished after unlock; see unlock_request(). 2: Finish off the unlocked + * requests. It is possible that some request will finish before we can. This + * is OK, the request will in that case be removed from the list before we touch + * it. */ void fuse_abort_conn(struct fuse_conn *fc) { + struct fuse_iqueue *fiq = &fc->iq; + spin_lock(&fc->lock); if (fc->connected) { + struct fuse_dev *fud; + struct fuse_req *req, *next; + LIST_HEAD(to_end1); + LIST_HEAD(to_end2); + fc->connected = 0; fc->blocked = 0; fuse_set_initialized(fc); - end_io_requests(fc); - end_queued_requests(fc); + list_for_each_entry(fud, &fc->devices, entry) { + struct fuse_pqueue *fpq = &fud->pq; + + spin_lock(&fpq->lock); + fpq->connected = 0; + list_for_each_entry_safe(req, next, &fpq->io, list) { + req->out.h.error = -ECONNABORTED; + spin_lock(&req->waitq.lock); + set_bit(FR_ABORTED, &req->flags); + if (!test_bit(FR_LOCKED, &req->flags)) { + set_bit(FR_PRIVATE, &req->flags); + list_move(&req->list, &to_end1); + } + spin_unlock(&req->waitq.lock); + } + list_splice_init(&fpq->processing, &to_end2); + spin_unlock(&fpq->lock); + } + fc->max_background = UINT_MAX; + flush_bg_queue(fc); + + spin_lock(&fiq->waitq.lock); + fiq->connected = 0; + list_splice_init(&fiq->pending, &to_end2); + while (forget_pending(fiq)) + kfree(dequeue_forget(fiq, 1, NULL)); + wake_up_all_locked(&fiq->waitq); + spin_unlock(&fiq->waitq.lock); + kill_fasync(&fiq->fasync, SIGIO, POLL_IN); end_polls(fc); - wake_up_all(&fc->waitq); wake_up_all(&fc->blocked_waitq); - kill_fasync(&fc->fasync, SIGIO, POLL_IN); + spin_unlock(&fc->lock); + + while (!list_empty(&to_end1)) { + req = list_first_entry(&to_end1, struct fuse_req, list); + __fuse_get_request(req); + list_del_init(&req->list); + request_end(fc, req); + } + end_requests(fc, &to_end2); + } else { + spin_unlock(&fc->lock); } - spin_unlock(&fc->lock); } EXPORT_SYMBOL_GPL(fuse_abort_conn); int fuse_dev_release(struct inode *inode, struct file *file) { - struct fuse_conn *fc = fuse_get_conn(file); - if (fc) { - spin_lock(&fc->lock); - fc->connected = 0; - fc->blocked = 0; - fuse_set_initialized(fc); - end_queued_requests(fc); - end_polls(fc); - wake_up_all(&fc->blocked_waitq); - spin_unlock(&fc->lock); - fuse_conn_put(fc); - } + struct fuse_dev *fud = fuse_get_dev(file); + + if (fud) { + struct fuse_conn *fc = fud->fc; + struct fuse_pqueue *fpq = &fud->pq; + WARN_ON(!list_empty(&fpq->io)); + end_requests(fc, &fpq->processing); + /* Are we the last open device? */ + if (atomic_dec_and_test(&fc->dev_count)) { + WARN_ON(fc->iq.fasync != NULL); + fuse_abort_conn(fc); + } + fuse_dev_free(fud); + } return 0; } EXPORT_SYMBOL_GPL(fuse_dev_release); static int fuse_dev_fasync(int fd, struct file *file, int on) { - struct fuse_conn *fc = fuse_get_conn(file); - if (!fc) + struct fuse_dev *fud = fuse_get_dev(file); + + if (!fud) return -EPERM; /* No locking - fasync_helper does its own locking */ - return fasync_helper(fd, file, on, &fc->fasync); + return fasync_helper(fd, file, on, &fud->fc->iq.fasync); +} + +static int fuse_device_clone(struct fuse_conn *fc, struct file *new) +{ + struct fuse_dev *fud; + + if (new->private_data) + return -EINVAL; + + fud = fuse_dev_alloc(fc); + if (!fud) + return -ENOMEM; + + new->private_data = fud; + atomic_inc(&fc->dev_count); + + return 0; +} + +static long fuse_dev_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int err = -ENOTTY; + + if (cmd == FUSE_DEV_IOC_CLONE) { + int oldfd; + + err = -EFAULT; + if (!get_user(oldfd, (__u32 __user *) arg)) { + struct file *old = fget(oldfd); + + err = -EINVAL; + if (old) { + struct fuse_dev *fud = NULL; + + /* + * Check against file->f_op because CUSE + * uses the same ioctl handler. + */ + if (old->f_op == file->f_op && + old->f_cred->user_ns == file->f_cred->user_ns) + fud = fuse_get_dev(old); + + if (fud) { + mutex_lock(&fuse_mutex); + err = fuse_device_clone(fud->fc, file); + mutex_unlock(&fuse_mutex); + } + fput(old); + } + } + } + return err; } const struct file_operations fuse_dev_operations = { @@ -2236,6 +2279,8 @@ const struct file_operations fuse_dev_operations = { .poll = fuse_dev_poll, .release = fuse_dev_release, .fasync = fuse_dev_fasync, + .unlocked_ioctl = fuse_dev_ioctl, + .compat_ioctl = fuse_dev_ioctl, }; EXPORT_SYMBOL_GPL(fuse_dev_operations); diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 0572bca49..5e2e08712 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1365,7 +1365,7 @@ static int fuse_readdir(struct file *file, struct dir_context *ctx) return err; } -static char *read_link(struct dentry *dentry) +static const char *fuse_follow_link(struct dentry *dentry, void **cookie) { struct inode *inode = d_inode(dentry); struct fuse_conn *fc = get_fuse_conn(inode); @@ -1389,28 +1389,12 @@ static char *read_link(struct dentry *dentry) link = ERR_PTR(ret); } else { link[ret] = '\0'; + *cookie = link; } fuse_invalidate_atime(inode); return link; } -static void free_link(char *link) -{ - if (!IS_ERR(link)) - free_page((unsigned long) link); -} - -static void *fuse_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - nd_set_link(nd, read_link(dentry)); - return NULL; -} - -static void fuse_put_link(struct dentry *dentry, struct nameidata *nd, void *c) -{ - free_link(nd_get_link(nd)); -} - static int fuse_dir_open(struct inode *inode, struct file *file) { return fuse_open_common(inode, file, true); @@ -1926,7 +1910,7 @@ static const struct inode_operations fuse_common_inode_operations = { static const struct inode_operations fuse_symlink_inode_operations = { .setattr = fuse_setattr, .follow_link = fuse_follow_link, - .put_link = fuse_put_link, + .put_link = free_page_put_link, .readlink = generic_readlink, .getattr = fuse_getattr, .setxattr = fuse_setxattr, diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 5ef05b5c4..f523f2f04 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -96,17 +96,17 @@ static void fuse_file_put(struct fuse_file *ff, bool sync) * Drop the release request when client does not * implement 'open' */ - req->background = 0; + __clear_bit(FR_BACKGROUND, &req->flags); iput(req->misc.release.inode); fuse_put_request(ff->fc, req); } else if (sync) { - req->background = 0; + __clear_bit(FR_BACKGROUND, &req->flags); fuse_request_send(ff->fc, req); iput(req->misc.release.inode); fuse_put_request(ff->fc, req); } else { req->end = fuse_release_end; - req->background = 1; + __set_bit(FR_BACKGROUND, &req->flags); fuse_request_send_background(ff->fc, req); } kfree(ff); @@ -299,8 +299,8 @@ void fuse_sync_release(struct fuse_file *ff, int flags) { WARN_ON(atomic_read(&ff->count) > 1); fuse_prepare_release(ff, flags, FUSE_RELEASE); - ff->reserved_req->force = 1; - ff->reserved_req->background = 0; + __set_bit(FR_FORCE, &ff->reserved_req->flags); + __clear_bit(FR_BACKGROUND, &ff->reserved_req->flags); fuse_request_send(ff->fc, ff->reserved_req); fuse_put_request(ff->fc, ff->reserved_req); kfree(ff); @@ -426,7 +426,7 @@ static int fuse_flush(struct file *file, fl_owner_t id) req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; - req->force = 1; + __set_bit(FR_FORCE, &req->flags); fuse_request_send(fc, req); err = req->out.h.error; fuse_put_request(fc, req); @@ -1169,7 +1169,7 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) if (err <= 0) goto out; - err = file_remove_suid(file); + err = file_remove_privs(file); if (err) goto out; @@ -1445,9 +1445,9 @@ static void fuse_writepage_finish(struct fuse_conn *fc, struct fuse_req *req) list_del(&req->writepages_entry); for (i = 0; i < req->num_pages; i++) { - dec_bdi_stat(bdi, BDI_WRITEBACK); + dec_wb_stat(&bdi->wb, WB_WRITEBACK); dec_zone_page_state(req->pages[i], NR_WRITEBACK_TEMP); - bdi_writeout_inc(bdi); + wb_writeout_inc(&bdi->wb); } wake_up(&fi->page_waitq); } @@ -1611,7 +1611,8 @@ static int fuse_writepage_locked(struct page *page) if (!req) goto err; - req->background = 1; /* writeback always goes to bg_queue */ + /* writeback always goes to bg_queue */ + __set_bit(FR_BACKGROUND, &req->flags); tmp_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); if (!tmp_page) goto err_free; @@ -1634,7 +1635,7 @@ static int fuse_writepage_locked(struct page *page) req->end = fuse_writepage_end; req->inode = inode; - inc_bdi_stat(inode_to_bdi(inode), BDI_WRITEBACK); + inc_wb_stat(&inode_to_bdi(inode)->wb, WB_WRITEBACK); inc_zone_page_state(tmp_page, NR_WRITEBACK_TEMP); spin_lock(&fc->lock); @@ -1742,16 +1743,15 @@ static bool fuse_writepage_in_flight(struct fuse_req *new_req, } } - if (old_req->num_pages == 1 && (old_req->state == FUSE_REQ_INIT || - old_req->state == FUSE_REQ_PENDING)) { + if (old_req->num_pages == 1 && test_bit(FR_PENDING, &old_req->flags)) { struct backing_dev_info *bdi = inode_to_bdi(page->mapping->host); copy_highpage(old_req->pages[0], page); spin_unlock(&fc->lock); - dec_bdi_stat(bdi, BDI_WRITEBACK); + dec_wb_stat(&bdi->wb, WB_WRITEBACK); dec_zone_page_state(page, NR_WRITEBACK_TEMP); - bdi_writeout_inc(bdi); + wb_writeout_inc(&bdi->wb); fuse_writepage_free(fc, new_req); fuse_request_free(new_req); goto out; @@ -1830,7 +1830,7 @@ static int fuse_writepages_fill(struct page *page, req->misc.write.in.write_flags |= FUSE_WRITE_CACHE; req->misc.write.next = NULL; req->in.argpages = 1; - req->background = 1; + __set_bit(FR_BACKGROUND, &req->flags); req->num_pages = 0; req->end = fuse_writepage_end; req->inode = inode; @@ -1848,7 +1848,7 @@ static int fuse_writepages_fill(struct page *page, req->page_descs[req->num_pages].offset = 0; req->page_descs[req->num_pages].length = PAGE_SIZE; - inc_bdi_stat(inode_to_bdi(inode), BDI_WRITEBACK); + inc_wb_stat(&inode_to_bdi(inode)->wb, WB_WRITEBACK); inc_zone_page_state(tmp_page, NR_WRITEBACK_TEMP); err = 0; diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 7354dc142..405113101 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -241,16 +241,6 @@ struct fuse_args { #define FUSE_ARGS(args) struct fuse_args args = {} -/** The request state */ -enum fuse_req_state { - FUSE_REQ_INIT = 0, - FUSE_REQ_PENDING, - FUSE_REQ_READING, - FUSE_REQ_SENT, - FUSE_REQ_WRITING, - FUSE_REQ_FINISHED -}; - /** The request IO state (for asynchronous processing) */ struct fuse_io_priv { int async; @@ -267,7 +257,40 @@ struct fuse_io_priv { }; /** + * Request flags + * + * FR_ISREPLY: set if the request has reply + * FR_FORCE: force sending of the request even if interrupted + * FR_BACKGROUND: request is sent in the background + * FR_WAITING: request is counted as "waiting" + * FR_ABORTED: the request was aborted + * FR_INTERRUPTED: the request has been interrupted + * FR_LOCKED: data is being copied to/from the request + * FR_PENDING: request is not yet in userspace + * FR_SENT: request is in userspace, waiting for an answer + * FR_FINISHED: request is finished + * FR_PRIVATE: request is on private list + */ +enum fuse_req_flag { + FR_ISREPLY, + FR_FORCE, + FR_BACKGROUND, + FR_WAITING, + FR_ABORTED, + FR_INTERRUPTED, + FR_LOCKED, + FR_PENDING, + FR_SENT, + FR_FINISHED, + FR_PRIVATE, +}; + +/** * A request to the client + * + * .waitq.lock protects the following fields: + * - FR_ABORTED + * - FR_LOCKED (may also be modified under fc->lock, tested under both) */ struct fuse_req { /** This can be on either pending processing or io lists in @@ -283,35 +306,8 @@ struct fuse_req { /** Unique ID for the interrupt request */ u64 intr_unique; - /* - * The following bitfields are either set once before the - * request is queued or setting/clearing them is protected by - * fuse_conn->lock - */ - - /** True if the request has reply */ - unsigned isreply:1; - - /** Force sending of the request even if interrupted */ - unsigned force:1; - - /** The request was aborted */ - unsigned aborted:1; - - /** Request is sent in the background */ - unsigned background:1; - - /** The request has been interrupted */ - unsigned interrupted:1; - - /** Data is being copied to/from the request */ - unsigned locked:1; - - /** Request is counted as "waiting" */ - unsigned waiting:1; - - /** State of the request */ - enum fuse_req_state state; + /* Request flags, updated with test/set/clear_bit() */ + unsigned long flags; /** The request input */ struct fuse_in in; @@ -380,6 +376,61 @@ struct fuse_req { struct file *stolen_file; }; +struct fuse_iqueue { + /** Connection established */ + unsigned connected; + + /** Readers of the connection are waiting on this */ + wait_queue_head_t waitq; + + /** The next unique request id */ + u64 reqctr; + + /** The list of pending requests */ + struct list_head pending; + + /** Pending interrupts */ + struct list_head interrupts; + + /** Queue of pending forgets */ + struct fuse_forget_link forget_list_head; + struct fuse_forget_link *forget_list_tail; + + /** Batching of FORGET requests (positive indicates FORGET batch) */ + int forget_batch; + + /** O_ASYNC requests */ + struct fasync_struct *fasync; +}; + +struct fuse_pqueue { + /** Connection established */ + unsigned connected; + + /** Lock protecting accessess to members of this structure */ + spinlock_t lock; + + /** The list of requests being processed */ + struct list_head processing; + + /** The list of requests under I/O */ + struct list_head io; +}; + +/** + * Fuse device instance + */ +struct fuse_dev { + /** Fuse connection for this device */ + struct fuse_conn *fc; + + /** Processing queue */ + struct fuse_pqueue pq; + + /** list entry on fc->devices */ + struct list_head entry; +}; + /** * A Fuse connection. * @@ -394,6 +445,9 @@ struct fuse_conn { /** Refcount */ atomic_t count; + /** Number of fuse_dev's */ + atomic_t dev_count; + struct rcu_head rcu; /** The user id for this mount */ @@ -411,17 +465,8 @@ struct fuse_conn { /** Maximum write size */ unsigned max_write; - /** Readers of the connection are waiting on this */ - wait_queue_head_t waitq; - - /** The list of pending requests */ - struct list_head pending; - - /** The list of requests being processed */ - struct list_head processing; - - /** The list of requests under I/O */ - struct list_head io; + /** Input queue */ + struct fuse_iqueue iq; /** The next unique kernel file handle */ u64 khctr; @@ -444,16 +489,6 @@ struct fuse_conn { /** The list of background requests set aside for later queuing */ struct list_head bg_queue; - /** Pending interrupts */ - struct list_head interrupts; - - /** Queue of pending forgets */ - struct fuse_forget_link forget_list_head; - struct fuse_forget_link *forget_list_tail; - - /** Batching of FORGET requests (positive indicates FORGET batch) */ - int forget_batch; - /** Flag indicating that INIT reply has been received. Allocating * any fuse request will be suspended until the flag is set */ int initialized; @@ -469,9 +504,6 @@ struct fuse_conn { /** waitq for reserved requests */ wait_queue_head_t reserved_req_waitq; - /** The next unique request id */ - u64 reqctr; - /** Connection established, cleared on umount, connection abort and device release */ unsigned connected; @@ -594,9 +626,6 @@ struct fuse_conn { /** number of dentries used in the above array */ int ctl_ndents; - /** O_ASYNC requests */ - struct fasync_struct *fasync; - /** Key for lock owner ID scrambling */ u32 scramble_key[4]; @@ -614,6 +643,9 @@ struct fuse_conn { /** Read/write semaphore to hold when accessing sb. */ struct rw_semaphore killsb; + + /** List of device instances belonging to this connection */ + struct list_head devices; }; static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb) @@ -826,6 +858,9 @@ void fuse_conn_init(struct fuse_conn *fc); */ void fuse_conn_put(struct fuse_conn *fc); +struct fuse_dev *fuse_dev_alloc(struct fuse_conn *fc); +void fuse_dev_free(struct fuse_dev *fud); + /** * Add connection to control filesystem */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 708d69711..2913db2a5 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -362,8 +362,8 @@ static void fuse_send_destroy(struct fuse_conn *fc) if (req && fc->conn_init) { fc->destroy_req = NULL; req->in.h.opcode = FUSE_DESTROY; - req->force = 1; - req->background = 0; + __set_bit(FR_FORCE, &req->flags); + __clear_bit(FR_BACKGROUND, &req->flags); fuse_request_send(fc, req); fuse_put_request(fc, req); } @@ -567,30 +567,46 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root) return 0; } +static void fuse_iqueue_init(struct fuse_iqueue *fiq) +{ + memset(fiq, 0, sizeof(struct fuse_iqueue)); + init_waitqueue_head(&fiq->waitq); + INIT_LIST_HEAD(&fiq->pending); + INIT_LIST_HEAD(&fiq->interrupts); + fiq->forget_list_tail = &fiq->forget_list_head; + fiq->connected = 1; +} + +static void fuse_pqueue_init(struct fuse_pqueue *fpq) +{ + memset(fpq, 0, sizeof(struct fuse_pqueue)); + spin_lock_init(&fpq->lock); + INIT_LIST_HEAD(&fpq->processing); + INIT_LIST_HEAD(&fpq->io); + fpq->connected = 1; +} + void fuse_conn_init(struct fuse_conn *fc) { memset(fc, 0, sizeof(*fc)); spin_lock_init(&fc->lock); init_rwsem(&fc->killsb); atomic_set(&fc->count, 1); - init_waitqueue_head(&fc->waitq); + atomic_set(&fc->dev_count, 1); init_waitqueue_head(&fc->blocked_waitq); init_waitqueue_head(&fc->reserved_req_waitq); - INIT_LIST_HEAD(&fc->pending); - INIT_LIST_HEAD(&fc->processing); - INIT_LIST_HEAD(&fc->io); - INIT_LIST_HEAD(&fc->interrupts); + fuse_iqueue_init(&fc->iq); INIT_LIST_HEAD(&fc->bg_queue); INIT_LIST_HEAD(&fc->entry); - fc->forget_list_tail = &fc->forget_list_head; + INIT_LIST_HEAD(&fc->devices); atomic_set(&fc->num_waiting, 0); fc->max_background = FUSE_DEFAULT_MAX_BACKGROUND; fc->congestion_threshold = FUSE_DEFAULT_CONGESTION_THRESHOLD; fc->khctr = 0; fc->polled_files = RB_ROOT; - fc->reqctr = 0; fc->blocked = 0; fc->initialized = 0; + fc->connected = 1; fc->attr_version = 1; get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key)); } @@ -930,6 +946,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req) static void fuse_free_conn(struct fuse_conn *fc) { + WARN_ON(!list_empty(&fc->devices)); kfree_rcu(fc, rcu); } @@ -975,8 +992,42 @@ static int fuse_bdi_init(struct fuse_conn *fc, struct super_block *sb) return 0; } +struct fuse_dev *fuse_dev_alloc(struct fuse_conn *fc) +{ + struct fuse_dev *fud; + + fud = kzalloc(sizeof(struct fuse_dev), GFP_KERNEL); + if (fud) { + fud->fc = fuse_conn_get(fc); + fuse_pqueue_init(&fud->pq); + + spin_lock(&fc->lock); + list_add_tail(&fud->entry, &fc->devices); + spin_unlock(&fc->lock); + } + + return fud; +} +EXPORT_SYMBOL_GPL(fuse_dev_alloc); + +void fuse_dev_free(struct fuse_dev *fud) +{ + struct fuse_conn *fc = fud->fc; + + if (fc) { + spin_lock(&fc->lock); + list_del(&fud->entry); + spin_unlock(&fc->lock); + + fuse_conn_put(fc); + } + kfree(fud); +} +EXPORT_SYMBOL_GPL(fuse_dev_free); + static int fuse_fill_super(struct super_block *sb, void *data, int silent) { + struct fuse_dev *fud; struct fuse_conn *fc; struct inode *root; struct fuse_mount_data d; @@ -1028,11 +1079,15 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) fuse_conn_init(fc); fc->release = fuse_free_conn; + fud = fuse_dev_alloc(fc); + if (!fud) + goto err_put_conn; + fc->dev = sb->s_dev; fc->sb = sb; err = fuse_bdi_init(fc, sb); if (err) - goto err_put_conn; + goto err_dev_free; sb->s_bdi = &fc->bdi; @@ -1053,14 +1108,14 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) root = fuse_get_root_inode(sb, d.rootmode); root_dentry = d_make_root(root); if (!root_dentry) - goto err_put_conn; + goto err_dev_free; /* only now - we want root dentry with NULL ->d_op */ sb->s_d_op = &fuse_dentry_operations; init_req = fuse_request_alloc(0); if (!init_req) goto err_put_root; - init_req->background = 1; + __set_bit(FR_BACKGROUND, &init_req->flags); if (is_bdev) { fc->destroy_req = fuse_request_alloc(0); @@ -1079,8 +1134,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) list_add_tail(&fc->entry, &fuse_conn_list); sb->s_root = root_dentry; - fc->connected = 1; - file->private_data = fuse_conn_get(fc); + file->private_data = fud; mutex_unlock(&fuse_mutex); /* * atomic_dec_and_test() in fput() provides the necessary @@ -1099,6 +1153,8 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) fuse_request_free(init_req); err_put_root: dput(root_dentry); + err_dev_free: + fuse_dev_free(fud); err_put_conn: fuse_bdi_destroy(fc); fuse_conn_put(fc); diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 5551fea0a..1caee0534 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -171,6 +171,7 @@ static int __gfs2_jdata_writepage(struct page *page, struct writeback_control *w /** * gfs2_jdata_writepage - Write complete page * @page: Page to write + * @wbc: The writeback control * * Returns: errno * @@ -221,9 +222,10 @@ static int gfs2_writepages(struct address_space *mapping, * gfs2_write_jdata_pagevec - Write back a pagevec's worth of pages * @mapping: The mapping * @wbc: The writeback control - * @writepage: The writepage function to call for each page * @pvec: The vector of pages * @nr_pages: The number of pages to write + * @end: End position + * @done_index: Page index * * Returns: non-zero if loop should terminate, zero otherwise */ @@ -333,8 +335,6 @@ continue_unlock: * gfs2_write_cache_jdata - Like write_cache_pages but different * @mapping: The mapping to write * @wbc: The writeback control - * @writepage: The writepage function to call - * @data: The data to pass to writepage * * The reason that we use our own function here is that we need to * start transactions before we grab page locks. This allows us @@ -588,6 +588,10 @@ int gfs2_internal_read(struct gfs2_inode *ip, char *buf, loff_t *pos, /** * gfs2_readpages - Read a bunch of pages at once + * @file: The file to read from + * @mapping: Address space info + * @pages: List of pages to read + * @nr_pages: Number of pages to read * * Some notes: * 1. This is only for readahead, so we can simply ignore any things @@ -853,7 +857,7 @@ static int gfs2_stuffed_write_end(struct inode *inode, struct buffer_head *dibh, * @mapping: The address space to write to * @pos: The file position * @len: The length of the data - * @copied: + * @copied: How much was actually copied by the VFS * @page: The page that has been written * @fsdata: The fsdata (unused in GFS2) * diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 31892871e..cf4ab8915 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -180,7 +180,7 @@ void gfs2_set_inode_flags(struct inode *inode) flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC|S_NOSEC); if ((ip->i_eattr == 0) && !is_sxid(inode->i_mode)) - inode->i_flags |= S_NOSEC; + flags |= S_NOSEC; if (ip->i_diskflags & GFS2_DIF_IMMUTABLE) flags |= S_IMMUTABLE; if (ip->i_diskflags & GFS2_DIF_APPENDONLY) @@ -917,7 +917,7 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t le struct gfs2_holder gh; int ret; - if (mode & ~FALLOC_FL_KEEP_SIZE) + if ((mode & ~FALLOC_FL_KEEP_SIZE) || gfs2_is_jdata(ip)) return -EOPNOTSUPP; mutex_lock(&inode->i_mutex); diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 0fa8062f8..a38e38f7b 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -1076,7 +1076,8 @@ void gfs2_glock_dq(struct gfs2_holder *gh) !test_bit(GLF_DEMOTE, &gl->gl_flags)) fast_path = 1; } - if (!test_bit(GLF_LFLUSH, &gl->gl_flags) && demote_ok(gl)) + if (!test_bit(GLF_LFLUSH, &gl->gl_flags) && demote_ok(gl) && + (glops->go_flags & GLOF_LRU)) gfs2_glock_add_to_lru(gl); trace_gfs2_glock_queue(gh, 0); diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index fe91951c3..fa3fa5e94 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -144,6 +144,12 @@ static void rgrp_go_sync(struct gfs2_glock *gl) struct gfs2_rgrpd *rgd; int error; + spin_lock(&gl->gl_spin); + rgd = gl->gl_object; + if (rgd) + gfs2_rgrp_brelse(rgd); + spin_unlock(&gl->gl_spin); + if (!test_and_clear_bit(GLF_DIRTY, &gl->gl_flags)) return; GLOCK_BUG_ON(gl, gl->gl_state != LM_ST_EXCLUSIVE); @@ -175,15 +181,17 @@ static void rgrp_go_inval(struct gfs2_glock *gl, int flags) { struct gfs2_sbd *sdp = gl->gl_sbd; struct address_space *mapping = &sdp->sd_aspace; + struct gfs2_rgrpd *rgd = gl->gl_object; + + if (rgd) + gfs2_rgrp_brelse(rgd); WARN_ON_ONCE(!(flags & DIO_METADATA)); gfs2_assert_withdraw(sdp, !atomic_read(&gl->gl_ail_count)); truncate_inode_pages_range(mapping, gl->gl_vm.start, gl->gl_vm.end); - if (gl->gl_object) { - struct gfs2_rgrpd *rgd = (struct gfs2_rgrpd *)gl->gl_object; + if (rgd) rgd->rd_flags &= ~GFS2_RDF_UPTODATE; - } } /** @@ -561,7 +569,7 @@ const struct gfs2_glock_operations gfs2_inode_glops = { .go_lock = inode_go_lock, .go_dump = inode_go_dump, .go_type = LM_TYPE_INODE, - .go_flags = GLOF_ASPACE, + .go_flags = GLOF_ASPACE | GLOF_LRU, }; const struct gfs2_glock_operations gfs2_rgrp_glops = { @@ -584,10 +592,12 @@ const struct gfs2_glock_operations gfs2_freeze_glops = { const struct gfs2_glock_operations gfs2_iopen_glops = { .go_type = LM_TYPE_IOPEN, .go_callback = iopen_go_callback, + .go_flags = GLOF_LRU, }; const struct gfs2_glock_operations gfs2_flock_glops = { .go_type = LM_TYPE_FLOCK, + .go_flags = GLOF_LRU, }; const struct gfs2_glock_operations gfs2_nondisk_glops = { @@ -596,7 +606,7 @@ const struct gfs2_glock_operations gfs2_nondisk_glops = { const struct gfs2_glock_operations gfs2_quota_glops = { .go_type = LM_TYPE_QUOTA, - .go_flags = GLOF_LVB, + .go_flags = GLOF_LVB | GLOF_LRU, }; const struct gfs2_glock_operations gfs2_journal_glops = { diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 58b75abf6..a1ec7c20e 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -225,6 +225,7 @@ struct gfs2_glock_operations { const unsigned long go_flags; #define GLOF_ASPACE 1 #define GLOF_LVB 2 +#define GLOF_LRU 4 }; enum { @@ -432,6 +433,7 @@ enum { QDF_CHANGE = 1, QDF_LOCKED = 2, QDF_REFRESH = 3, + QDF_QMSG_QUIET = 4, }; struct gfs2_quota_data { diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 1b3ca7a2e..063fdfcf8 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1227,8 +1227,8 @@ static int gfs2_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, */ static int gfs2_atomic_open(struct inode *dir, struct dentry *dentry, - struct file *file, unsigned flags, - umode_t mode, int *opened) + struct file *file, unsigned flags, + umode_t mode, int *opened) { struct dentry *d; bool excl = !!(flags & O_EXCL); @@ -1307,6 +1307,35 @@ static int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to) } /** + * update_moved_ino - Update an inode that's being moved + * @ip: The inode being moved + * @ndip: The parent directory of the new filename + * @dir_rename: True of ip is a directory + * + * Returns: errno + */ + +static int update_moved_ino(struct gfs2_inode *ip, struct gfs2_inode *ndip, + int dir_rename) +{ + int error; + struct buffer_head *dibh; + + if (dir_rename) + return gfs2_dir_mvino(ip, &gfs2_qdotdot, ndip, DT_DIR); + + error = gfs2_meta_inode_buffer(ip, &dibh); + if (error) + return error; + ip->i_inode.i_ctime = CURRENT_TIME; + gfs2_trans_add_meta(ip->i_gl, dibh); + gfs2_dinode_out(ip, dibh->b_data); + brelse(dibh); + return 0; +} + + +/** * gfs2_rename - Rename a file * @odir: Parent directory of old file name * @odentry: The old dentry of the file @@ -1354,7 +1383,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, if (S_ISDIR(ip->i_inode.i_mode)) { dir_rename = 1; - /* don't move a dirctory into it's subdir */ + /* don't move a directory into its subdir */ error = gfs2_ok_to_move(ip, ndip); if (error) goto out_gunlock_r; @@ -1494,20 +1523,9 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, if (nip) error = gfs2_unlink_inode(ndip, ndentry); - if (dir_rename) { - error = gfs2_dir_mvino(ip, &gfs2_qdotdot, ndip, DT_DIR); - if (error) - goto out_end_trans; - } else { - struct buffer_head *dibh; - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - goto out_end_trans; - ip->i_inode.i_ctime = CURRENT_TIME; - gfs2_trans_add_meta(ip->i_gl, dibh); - gfs2_dinode_out(ip, dibh->b_data); - brelse(dibh); - } + error = update_moved_ino(ip, ndip, dir_rename); + if (error) + goto out_end_trans; error = gfs2_dir_del(odip, odentry); if (error) @@ -1539,6 +1557,161 @@ out: } /** + * gfs2_exchange - exchange two files + * @odir: Parent directory of old file name + * @odentry: The old dentry of the file + * @ndir: Parent directory of new file name + * @ndentry: The new dentry of the file + * @flags: The rename flags + * + * Returns: errno + */ + +static int gfs2_exchange(struct inode *odir, struct dentry *odentry, + struct inode *ndir, struct dentry *ndentry, + unsigned int flags) +{ + struct gfs2_inode *odip = GFS2_I(odir); + struct gfs2_inode *ndip = GFS2_I(ndir); + struct gfs2_inode *oip = GFS2_I(odentry->d_inode); + struct gfs2_inode *nip = GFS2_I(ndentry->d_inode); + struct gfs2_sbd *sdp = GFS2_SB(odir); + struct gfs2_holder ghs[5], r_gh = { .gh_gl = NULL, }; + unsigned int num_gh; + unsigned int x; + umode_t old_mode = oip->i_inode.i_mode; + umode_t new_mode = nip->i_inode.i_mode; + int error; + + error = gfs2_rindex_update(sdp); + if (error) + return error; + + if (odip != ndip) { + error = gfs2_glock_nq_init(sdp->sd_rename_gl, LM_ST_EXCLUSIVE, + 0, &r_gh); + if (error) + goto out; + + if (S_ISDIR(old_mode)) { + /* don't move a directory into its subdir */ + error = gfs2_ok_to_move(oip, ndip); + if (error) + goto out_gunlock_r; + } + + if (S_ISDIR(new_mode)) { + /* don't move a directory into its subdir */ + error = gfs2_ok_to_move(nip, odip); + if (error) + goto out_gunlock_r; + } + } + + num_gh = 1; + gfs2_holder_init(odip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); + if (odip != ndip) { + gfs2_holder_init(ndip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); + num_gh++; + } + gfs2_holder_init(oip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); + num_gh++; + + gfs2_holder_init(nip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); + num_gh++; + + for (x = 0; x < num_gh; x++) { + error = gfs2_glock_nq(ghs + x); + if (error) + goto out_gunlock; + } + + error = -ENOENT; + if (oip->i_inode.i_nlink == 0 || nip->i_inode.i_nlink == 0) + goto out_gunlock; + + error = gfs2_unlink_ok(odip, &odentry->d_name, oip); + if (error) + goto out_gunlock; + error = gfs2_unlink_ok(ndip, &ndentry->d_name, nip); + if (error) + goto out_gunlock; + + if (S_ISDIR(old_mode)) { + error = gfs2_permission(odentry->d_inode, MAY_WRITE); + if (error) + goto out_gunlock; + } + if (S_ISDIR(new_mode)) { + error = gfs2_permission(ndentry->d_inode, MAY_WRITE); + if (error) + goto out_gunlock; + } + error = gfs2_trans_begin(sdp, 4 * RES_DINODE + 4 * RES_LEAF, 0); + if (error) + goto out_gunlock; + + error = update_moved_ino(oip, ndip, S_ISDIR(old_mode)); + if (error) + goto out_end_trans; + + error = update_moved_ino(nip, odip, S_ISDIR(new_mode)); + if (error) + goto out_end_trans; + + error = gfs2_dir_mvino(ndip, &ndentry->d_name, oip, + IF2DT(old_mode)); + if (error) + goto out_end_trans; + + error = gfs2_dir_mvino(odip, &odentry->d_name, nip, + IF2DT(new_mode)); + if (error) + goto out_end_trans; + + if (odip != ndip) { + if (S_ISDIR(new_mode) && !S_ISDIR(old_mode)) { + inc_nlink(&odip->i_inode); + drop_nlink(&ndip->i_inode); + } else if (S_ISDIR(old_mode) && !S_ISDIR(new_mode)) { + inc_nlink(&ndip->i_inode); + drop_nlink(&odip->i_inode); + } + } + mark_inode_dirty(&ndip->i_inode); + if (odip != ndip) + mark_inode_dirty(&odip->i_inode); + +out_end_trans: + gfs2_trans_end(sdp); +out_gunlock: + while (x--) { + gfs2_glock_dq(ghs + x); + gfs2_holder_uninit(ghs + x); + } +out_gunlock_r: + if (r_gh.gh_gl) + gfs2_glock_dq_uninit(&r_gh); +out: + return error; +} + +static int gfs2_rename2(struct inode *odir, struct dentry *odentry, + struct inode *ndir, struct dentry *ndentry, + unsigned int flags) +{ + flags &= ~RENAME_NOREPLACE; + + if (flags & ~RENAME_EXCHANGE) + return -EINVAL; + + if (flags & RENAME_EXCHANGE) + return gfs2_exchange(odir, odentry, ndir, ndentry, flags); + + return gfs2_rename(odir, odentry, ndir, ndentry); +} + +/** * gfs2_follow_link - Follow a symbolic link * @dentry: The dentry of the link * @nd: Data that we pass to vfs_follow_link() @@ -1548,7 +1721,7 @@ out: * Returns: 0 on success or error code */ -static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *gfs2_follow_link(struct dentry *dentry, void **cookie) { struct gfs2_inode *ip = GFS2_I(d_inode(dentry)); struct gfs2_holder i_gh; @@ -1561,8 +1734,7 @@ static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd) error = gfs2_glock_nq(&i_gh); if (error) { gfs2_holder_uninit(&i_gh); - nd_set_link(nd, ERR_PTR(error)); - return NULL; + return ERR_PTR(error); } size = (unsigned int)i_size_read(&ip->i_inode); @@ -1586,8 +1758,9 @@ static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd) brelse(dibh); out: gfs2_glock_dq_uninit(&i_gh); - nd_set_link(nd, buf); - return NULL; + if (!IS_ERR(buf)) + *cookie = buf; + return buf; } /** @@ -1716,7 +1889,7 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) || !gid_eq(ogid, NO_GID_QUOTA_CHANGE)) { - gfs2_quota_change(ip, -ap.target, ouid, ogid); + gfs2_quota_change(ip, -(s64)ap.target, ouid, ogid); gfs2_quota_change(ip, ap.target, nuid, ngid); } @@ -1943,7 +2116,7 @@ const struct inode_operations gfs2_dir_iops = { .mkdir = gfs2_mkdir, .rmdir = gfs2_unlink, .mknod = gfs2_mknod, - .rename = gfs2_rename, + .rename2 = gfs2_rename2, .permission = gfs2_permission, .setattr = gfs2_setattr, .getattr = gfs2_getattr, diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 35b49f44c..1e3a93f2f 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -756,6 +756,7 @@ static int init_journal(struct gfs2_sbd *sdp, int undo) } } + sdp->sd_log_idle = 1; set_bit(SDF_JOURNAL_CHECKED, &sdp->sd_flags); gfs2_glock_dq_uninit(&ji_gh); jindex = 0; diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index e3065cb9a..9b61f92fc 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -649,9 +649,117 @@ static void do_qc(struct gfs2_quota_data *qd, s64 change) slot_hold(qd); } + if (change < 0) /* Reset quiet flag if we freed some blocks */ + clear_bit(QDF_QMSG_QUIET, &qd->qd_flags); mutex_unlock(&sdp->sd_quota_mutex); } +static int gfs2_write_buf_to_page(struct gfs2_inode *ip, unsigned long index, + unsigned off, void *buf, unsigned bytes) +{ + struct inode *inode = &ip->i_inode; + struct gfs2_sbd *sdp = GFS2_SB(inode); + struct address_space *mapping = inode->i_mapping; + struct page *page; + struct buffer_head *bh; + void *kaddr; + u64 blk; + unsigned bsize = sdp->sd_sb.sb_bsize, bnum = 0, boff = 0; + unsigned to_write = bytes, pg_off = off; + int done = 0; + + blk = index << (PAGE_CACHE_SHIFT - sdp->sd_sb.sb_bsize_shift); + boff = off % bsize; + + page = find_or_create_page(mapping, index, GFP_NOFS); + if (!page) + return -ENOMEM; + if (!page_has_buffers(page)) + create_empty_buffers(page, bsize, 0); + + bh = page_buffers(page); + while (!done) { + /* Find the beginning block within the page */ + if (pg_off >= ((bnum * bsize) + bsize)) { + bh = bh->b_this_page; + bnum++; + blk++; + continue; + } + if (!buffer_mapped(bh)) { + gfs2_block_map(inode, blk, bh, 1); + if (!buffer_mapped(bh)) + goto unlock_out; + /* If it's a newly allocated disk block, zero it */ + if (buffer_new(bh)) + zero_user(page, bnum * bsize, bh->b_size); + } + if (PageUptodate(page)) + set_buffer_uptodate(bh); + if (!buffer_uptodate(bh)) { + ll_rw_block(READ | REQ_META, 1, &bh); + wait_on_buffer(bh); + if (!buffer_uptodate(bh)) + goto unlock_out; + } + gfs2_trans_add_data(ip->i_gl, bh); + + /* If we need to write to the next block as well */ + if (to_write > (bsize - boff)) { + pg_off += (bsize - boff); + to_write -= (bsize - boff); + boff = pg_off % bsize; + continue; + } + done = 1; + } + + /* Write to the page, now that we have setup the buffer(s) */ + kaddr = kmap_atomic(page); + memcpy(kaddr + off, buf, bytes); + flush_dcache_page(page); + kunmap_atomic(kaddr); + unlock_page(page); + page_cache_release(page); + + return 0; + +unlock_out: + unlock_page(page); + page_cache_release(page); + return -EIO; +} + +static int gfs2_write_disk_quota(struct gfs2_inode *ip, struct gfs2_quota *qp, + loff_t loc) +{ + unsigned long pg_beg; + unsigned pg_off, nbytes, overflow = 0; + int pg_oflow = 0, error; + void *ptr; + + nbytes = sizeof(struct gfs2_quota); + + pg_beg = loc >> PAGE_CACHE_SHIFT; + pg_off = loc % PAGE_CACHE_SIZE; + + /* If the quota straddles a page boundary, split the write in two */ + if ((pg_off + nbytes) > PAGE_CACHE_SIZE) { + pg_oflow = 1; + overflow = (pg_off + nbytes) - PAGE_CACHE_SIZE; + } + + ptr = qp; + error = gfs2_write_buf_to_page(ip, pg_beg, pg_off, ptr, + nbytes - overflow); + /* If there's an overflow, write the remaining bytes to the next page */ + if (!error && pg_oflow) + error = gfs2_write_buf_to_page(ip, pg_beg + 1, 0, + ptr + nbytes - overflow, + overflow); + return error; +} + /** * gfs2_adjust_quota - adjust record of current block usage * @ip: The quota inode @@ -672,15 +780,8 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, { struct inode *inode = &ip->i_inode; struct gfs2_sbd *sdp = GFS2_SB(inode); - struct address_space *mapping = inode->i_mapping; - unsigned long index = loc >> PAGE_CACHE_SHIFT; - unsigned offset = loc & (PAGE_CACHE_SIZE - 1); - unsigned blocksize, iblock, pos; - struct buffer_head *bh; - struct page *page; - void *kaddr, *ptr; struct gfs2_quota q; - int err, nbytes; + int err; u64 size; if (gfs2_is_stuffed(ip)) { @@ -694,8 +795,11 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, if (err < 0) return err; + loc -= sizeof(q); /* gfs2_internal_read would've advanced the loc ptr */ err = -EIO; be64_add_cpu(&q.qu_value, change); + if (((s64)be64_to_cpu(q.qu_value)) < 0) + q.qu_value = 0; /* Never go negative on quota usage */ qd->qd_qb.qb_value = q.qu_value; if (fdq) { if (fdq->d_fieldmask & QC_SPC_SOFT) { @@ -712,79 +816,16 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, } } - /* Write the quota into the quota file on disk */ - ptr = &q; - nbytes = sizeof(struct gfs2_quota); -get_a_page: - page = find_or_create_page(mapping, index, GFP_NOFS); - if (!page) - return -ENOMEM; - - blocksize = inode->i_sb->s_blocksize; - iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits); - - if (!page_has_buffers(page)) - create_empty_buffers(page, blocksize, 0); - - bh = page_buffers(page); - pos = blocksize; - while (offset >= pos) { - bh = bh->b_this_page; - iblock++; - pos += blocksize; - } - - if (!buffer_mapped(bh)) { - gfs2_block_map(inode, iblock, bh, 1); - if (!buffer_mapped(bh)) - goto unlock_out; - /* If it's a newly allocated disk block for quota, zero it */ - if (buffer_new(bh)) - zero_user(page, pos - blocksize, bh->b_size); - } - - if (PageUptodate(page)) - set_buffer_uptodate(bh); - - if (!buffer_uptodate(bh)) { - ll_rw_block(READ | REQ_META, 1, &bh); - wait_on_buffer(bh); - if (!buffer_uptodate(bh)) - goto unlock_out; - } - - gfs2_trans_add_data(ip->i_gl, bh); - - kaddr = kmap_atomic(page); - if (offset + sizeof(struct gfs2_quota) > PAGE_CACHE_SIZE) - nbytes = PAGE_CACHE_SIZE - offset; - memcpy(kaddr + offset, ptr, nbytes); - flush_dcache_page(page); - kunmap_atomic(kaddr); - unlock_page(page); - page_cache_release(page); - - /* If quota straddles page boundary, we need to update the rest of the - * quota at the beginning of the next page */ - if ((offset + sizeof(struct gfs2_quota)) > PAGE_CACHE_SIZE) { - ptr = ptr + nbytes; - nbytes = sizeof(struct gfs2_quota) - nbytes; - offset = 0; - index++; - goto get_a_page; + err = gfs2_write_disk_quota(ip, &q, loc); + if (!err) { + size = loc + sizeof(struct gfs2_quota); + if (size > inode->i_size) + i_size_write(inode, size); + inode->i_mtime = inode->i_atime = CURRENT_TIME; + mark_inode_dirty(inode); + set_bit(QDF_REFRESH, &qd->qd_flags); } - size = loc + sizeof(struct gfs2_quota); - if (size > inode->i_size) - i_size_write(inode, size); - inode->i_mtime = inode->i_atime = CURRENT_TIME; - mark_inode_dirty(inode); - set_bit(QDF_REFRESH, &qd->qd_flags); - return 0; - -unlock_out: - unlock_page(page); - page_cache_release(page); return err; } @@ -1148,10 +1189,13 @@ int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid, /* If no min_target specified or we don't meet * min_target, return -EDQUOT */ if (!ap->min_target || ap->min_target > ap->allowed) { - print_message(qd, "exceeded"); - quota_send_warning(qd->qd_id, - sdp->sd_vfs->s_dev, - QUOTA_NL_BHARDWARN); + if (!test_and_set_bit(QDF_QMSG_QUIET, + &qd->qd_flags)) { + print_message(qd, "exceeded"); + quota_send_warning(qd->qd_id, + sdp->sd_vfs->s_dev, + QUOTA_NL_BHARDWARN); + } error = -EDQUOT; break; } @@ -1648,6 +1692,8 @@ static int gfs2_set_dqblk(struct super_block *sb, struct kqid qid, /* Apply changes */ error = gfs2_adjust_quota(ip, offset, 0, qd, fdq); + if (!error) + clear_bit(QDF_QMSG_QUIET, &qd->qd_flags); gfs2_trans_end(sdp); out_release: diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 6af2396a3..c6c62321d 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -978,10 +978,10 @@ static void set_rgrp_preferences(struct gfs2_sbd *sdp) rgd->rd_flags |= GFS2_RDF_PREFERRED; for (i = 0; i < sdp->sd_journals; i++) { rgd = gfs2_rgrpd_get_next(rgd); - if (rgd == first) + if (!rgd || rgd == first) break; } - } while (rgd != first); + } while (rgd && rgd != first); } /** @@ -1244,14 +1244,13 @@ int gfs2_rgrp_go_lock(struct gfs2_holder *gh) } /** - * gfs2_rgrp_go_unlock - Release RG bitmaps read in with gfs2_rgrp_bh_get() - * @gh: The glock holder for the resource group + * gfs2_rgrp_brelse - Release RG bitmaps read in with gfs2_rgrp_bh_get() + * @rgd: The resource group * */ -void gfs2_rgrp_go_unlock(struct gfs2_holder *gh) +void gfs2_rgrp_brelse(struct gfs2_rgrpd *rgd) { - struct gfs2_rgrpd *rgd = gh->gh_gl->gl_object; int x, length = rgd->rd_length; for (x = 0; x < length; x++) { @@ -1264,6 +1263,22 @@ void gfs2_rgrp_go_unlock(struct gfs2_holder *gh) } +/** + * gfs2_rgrp_go_unlock - Unlock a rgrp glock + * @gh: The glock holder for the resource group + * + */ + +void gfs2_rgrp_go_unlock(struct gfs2_holder *gh) +{ + struct gfs2_rgrpd *rgd = gh->gh_gl->gl_object; + int demote_requested = test_bit(GLF_DEMOTE, &gh->gh_gl->gl_flags) | + test_bit(GLF_PENDING_DEMOTE, &gh->gh_gl->gl_flags); + + if (rgd && demote_requested) + gfs2_rgrp_brelse(rgd); +} + int gfs2_rgrp_send_discards(struct gfs2_sbd *sdp, u64 offset, struct buffer_head *bh, const struct gfs2_bitmap *bi, unsigned minlen, u64 *ptrimmed) @@ -1711,10 +1726,8 @@ static int gfs2_rbm_find(struct gfs2_rbm *rbm, u8 state, u32 *minext, return ret; bitmap_full: /* Mark bitmap as full and fall through */ - if ((state == GFS2_BLKST_FREE) && initial_offset == 0) { - struct gfs2_bitmap *bi = rbm_bi(rbm); + if ((state == GFS2_BLKST_FREE) && initial_offset == 0) set_bit(GBF_FULL, &bi->bi_flags); - } next_bitmap: /* Find next bitmap in the rgrp */ rbm->offset = 0; @@ -1850,14 +1863,23 @@ static bool gfs2_rgrp_congested(const struct gfs2_rgrpd *rgd, int loops) const struct gfs2_sbd *sdp = gl->gl_sbd; struct gfs2_lkstats *st; s64 r_dcount, l_dcount; - s64 r_srttb, l_srttb; + s64 l_srttb, a_srttb = 0; s64 srttb_diff; s64 sqr_diff; s64 var; + int cpu, nonzero = 0; preempt_disable(); + for_each_present_cpu(cpu) { + st = &per_cpu_ptr(sdp->sd_lkstats, cpu)->lkstats[LM_TYPE_RGRP]; + if (st->stats[GFS2_LKS_SRTTB]) { + a_srttb += st->stats[GFS2_LKS_SRTTB]; + nonzero++; + } + } st = &this_cpu_ptr(sdp->sd_lkstats)->lkstats[LM_TYPE_RGRP]; - r_srttb = st->stats[GFS2_LKS_SRTTB]; + if (nonzero) + do_div(a_srttb, nonzero); r_dcount = st->stats[GFS2_LKS_DCOUNT]; var = st->stats[GFS2_LKS_SRTTVARB] + gl->gl_stats.stats[GFS2_LKS_SRTTVARB]; @@ -1866,10 +1888,10 @@ static bool gfs2_rgrp_congested(const struct gfs2_rgrpd *rgd, int loops) l_srttb = gl->gl_stats.stats[GFS2_LKS_SRTTB]; l_dcount = gl->gl_stats.stats[GFS2_LKS_DCOUNT]; - if ((l_dcount < 1) || (r_dcount < 1) || (r_srttb == 0)) + if ((l_dcount < 1) || (r_dcount < 1) || (a_srttb == 0)) return false; - srttb_diff = r_srttb - l_srttb; + srttb_diff = a_srttb - l_srttb; sqr_diff = srttb_diff * srttb_diff; var *= 2; diff --git a/fs/gfs2/rgrp.h b/fs/gfs2/rgrp.h index 68972ecfb..c0ab33fa3 100644 --- a/fs/gfs2/rgrp.h +++ b/fs/gfs2/rgrp.h @@ -36,6 +36,7 @@ extern void gfs2_clear_rgrpd(struct gfs2_sbd *sdp); extern int gfs2_rindex_update(struct gfs2_sbd *sdp); extern void gfs2_free_clones(struct gfs2_rgrpd *rgd); extern int gfs2_rgrp_go_lock(struct gfs2_holder *gh); +extern void gfs2_rgrp_brelse(struct gfs2_rgrpd *rgd); extern void gfs2_rgrp_go_unlock(struct gfs2_holder *gh); extern struct gfs2_alloc *gfs2_alloc_get(struct gfs2_inode *ip); diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 859c6edbf..298244594 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -748,7 +748,7 @@ static int gfs2_write_inode(struct inode *inode, struct writeback_control *wbc) if (wbc->sync_mode == WB_SYNC_ALL) gfs2_log_flush(GFS2_SB(inode), ip->i_gl, NORMAL_FLUSH); - if (bdi->dirty_exceeded) + if (bdi->wb.dirty_exceeded) gfs2_ail1_flush(sdp, wbc); else filemap_fdatawrite(metamapping); diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index ae8e8811f..c9ff1cf7d 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -101,8 +101,11 @@ static ssize_t freeze_show(struct gfs2_sbd *sdp, char *buf) static ssize_t freeze_store(struct gfs2_sbd *sdp, const char *buf, size_t len) { - int error; - int n = simple_strtol(buf, NULL, 0); + int error, n; + + error = kstrtoint(buf, 0, &n); + if (error) + return error; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -134,10 +137,16 @@ static ssize_t withdraw_show(struct gfs2_sbd *sdp, char *buf) static ssize_t withdraw_store(struct gfs2_sbd *sdp, const char *buf, size_t len) { + int error, val; + if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (simple_strtol(buf, NULL, 0) != 1) + error = kstrtoint(buf, 0, &val); + if (error) + return error; + + if (val != 1) return -EINVAL; gfs2_lm_withdraw(sdp, "withdrawing from cluster at user's request\n"); @@ -148,10 +157,16 @@ static ssize_t withdraw_store(struct gfs2_sbd *sdp, const char *buf, size_t len) static ssize_t statfs_sync_store(struct gfs2_sbd *sdp, const char *buf, size_t len) { + int error, val; + if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (simple_strtol(buf, NULL, 0) != 1) + error = kstrtoint(buf, 0, &val); + if (error) + return error; + + if (val != 1) return -EINVAL; gfs2_statfs_sync(sdp->sd_vfs, 0); @@ -161,10 +176,16 @@ static ssize_t statfs_sync_store(struct gfs2_sbd *sdp, const char *buf, static ssize_t quota_sync_store(struct gfs2_sbd *sdp, const char *buf, size_t len) { + int error, val; + if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (simple_strtol(buf, NULL, 0) != 1) + error = kstrtoint(buf, 0, &val); + if (error) + return error; + + if (val != 1) return -EINVAL; gfs2_quota_sync(sdp->sd_vfs, 0); @@ -181,7 +202,9 @@ static ssize_t quota_refresh_user_store(struct gfs2_sbd *sdp, const char *buf, if (!capable(CAP_SYS_ADMIN)) return -EPERM; - id = simple_strtoul(buf, NULL, 0); + error = kstrtou32(buf, 0, &id); + if (error) + return error; qid = make_kqid(current_user_ns(), USRQUOTA, id); if (!qid_valid(qid)) @@ -201,7 +224,9 @@ static ssize_t quota_refresh_group_store(struct gfs2_sbd *sdp, const char *buf, if (!capable(CAP_SYS_ADMIN)) return -EPERM; - id = simple_strtoul(buf, NULL, 0); + error = kstrtou32(buf, 0, &id); + if (error) + return error; qid = make_kqid(current_user_ns(), GRPQUOTA, id); if (!qid_valid(qid)) @@ -324,10 +349,11 @@ static ssize_t block_show(struct gfs2_sbd *sdp, char *buf) static ssize_t block_store(struct gfs2_sbd *sdp, const char *buf, size_t len) { struct lm_lockstruct *ls = &sdp->sd_lockstruct; - ssize_t ret = len; - int val; + int ret, val; - val = simple_strtol(buf, NULL, 0); + ret = kstrtoint(buf, 0, &val); + if (ret) + return ret; if (val == 1) set_bit(DFL_BLOCK_LOCKS, &ls->ls_recover_flags); @@ -336,9 +362,9 @@ static ssize_t block_store(struct gfs2_sbd *sdp, const char *buf, size_t len) smp_mb__after_atomic(); gfs2_glock_thaw(sdp); } else { - ret = -EINVAL; + return -EINVAL; } - return ret; + return len; } static ssize_t wdack_show(struct gfs2_sbd *sdp, char *buf) @@ -350,17 +376,18 @@ static ssize_t wdack_show(struct gfs2_sbd *sdp, char *buf) static ssize_t wdack_store(struct gfs2_sbd *sdp, const char *buf, size_t len) { - ssize_t ret = len; - int val; + int ret, val; - val = simple_strtol(buf, NULL, 0); + ret = kstrtoint(buf, 0, &val); + if (ret) + return ret; if ((val == 1) && !strcmp(sdp->sd_lockstruct.ls_ops->lm_proto_name, "lock_dlm")) complete(&sdp->sd_wdack); else - ret = -EINVAL; - return ret; + return -EINVAL; + return len; } static ssize_t lkfirst_show(struct gfs2_sbd *sdp, char *buf) @@ -553,11 +580,14 @@ static ssize_t tune_set(struct gfs2_sbd *sdp, unsigned int *field, { struct gfs2_tune *gt = &sdp->sd_tune; unsigned int x; + int error; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - x = simple_strtoul(buf, NULL, 0); + error = kstrtouint(buf, 0, &x); + if (error) + return error; if (check_zero && !x) return -EINVAL; diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h index 95d255219..1f1c7dcbc 100644 --- a/fs/hfs/hfs_fs.h +++ b/fs/hfs/hfs_fs.h @@ -252,7 +252,7 @@ extern void hfs_mark_mdb_dirty(struct super_block *sb); #define __hfs_u_to_mtime(sec) cpu_to_be32(sec + 2082844800U - sys_tz.tz_minuteswest * 60) #define __hfs_m_to_utime(sec) (be32_to_cpu(sec) - 2082844800U + sys_tz.tz_minuteswest * 60) -#define HFS_I(inode) (list_entry(inode, struct hfs_inode_info, vfs_inode)) +#define HFS_I(inode) (container_of(inode, struct hfs_inode_info, vfs_inode)) #define HFS_SB(sb) ((struct hfs_sb_info *)(sb)->s_fs_info) #define hfs_m_to_utime(time) (struct timespec){ .tv_sec = __hfs_m_to_utime(time) } diff --git a/fs/hfs/super.c b/fs/hfs/super.c index eee7206c3..55c03b9e9 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -14,6 +14,7 @@ #include <linux/module.h> #include <linux/blkdev.h> +#include <linux/backing-dev.h> #include <linux/mount.h> #include <linux/init.h> #include <linux/nls.h> diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index b0441d65f..f91a1faf8 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -263,7 +263,7 @@ struct hfsplus_inode_info { static inline struct hfsplus_inode_info *HFSPLUS_I(struct inode *inode) { - return list_entry(inode, struct hfsplus_inode_info, vfs_inode); + return container_of(inode, struct hfsplus_inode_info, vfs_inode); } /* diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 593af2fdc..7302d96ae 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -11,6 +11,7 @@ #include <linux/init.h> #include <linux/pagemap.h> #include <linux/blkdev.h> +#include <linux/backing-dev.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/vfs.h> diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 07d8d8f52..059597b23 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -892,7 +892,7 @@ static const struct inode_operations hostfs_dir_iops = { .setattr = hostfs_setattr, }; -static void *hostfs_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *hostfs_follow_link(struct dentry *dentry, void **cookie) { char *link = __getname(); if (link) { @@ -906,21 +906,18 @@ static void *hostfs_follow_link(struct dentry *dentry, struct nameidata *nd) } if (err < 0) { __putname(link); - link = ERR_PTR(err); + return ERR_PTR(err); } } else { - link = ERR_PTR(-ENOMEM); + return ERR_PTR(-ENOMEM); } - nd_set_link(nd, link); - return NULL; + return *cookie = link; } -static void hostfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) +static void hostfs_put_link(struct inode *unused, void *cookie) { - char *s = nd_get_link(nd); - if (!IS_ERR(s)) - __putname(s); + __putname(cookie); } static const struct inode_operations hostfs_link_iops = { diff --git a/fs/hpfs/alloc.c b/fs/hpfs/alloc.c index f005046e1..d6a4b55d2 100644 --- a/fs/hpfs/alloc.c +++ b/fs/hpfs/alloc.c @@ -484,3 +484,98 @@ struct anode *hpfs_alloc_anode(struct super_block *s, secno near, anode_secno *a a->btree.first_free = cpu_to_le16(8); return a; } + +static unsigned find_run(__le32 *bmp, unsigned *idx) +{ + unsigned len; + while (tstbits(bmp, *idx, 1)) { + (*idx)++; + if (unlikely(*idx >= 0x4000)) + return 0; + } + len = 1; + while (!tstbits(bmp, *idx + len, 1)) + len++; + return len; +} + +static int do_trim(struct super_block *s, secno start, unsigned len, secno limit_start, secno limit_end, unsigned minlen, unsigned *result) +{ + int err; + secno end; + if (fatal_signal_pending(current)) + return -EINTR; + end = start + len; + if (start < limit_start) + start = limit_start; + if (end > limit_end) + end = limit_end; + if (start >= end) + return 0; + if (end - start < minlen) + return 0; + err = sb_issue_discard(s, start, end - start, GFP_NOFS, 0); + if (err) + return err; + *result += end - start; + return 0; +} + +int hpfs_trim_fs(struct super_block *s, u64 start, u64 end, u64 minlen, unsigned *result) +{ + int err = 0; + struct hpfs_sb_info *sbi = hpfs_sb(s); + unsigned idx, len, start_bmp, end_bmp; + __le32 *bmp; + struct quad_buffer_head qbh; + + *result = 0; + if (!end || end > sbi->sb_fs_size) + end = sbi->sb_fs_size; + if (start >= sbi->sb_fs_size) + return 0; + if (minlen > 0x4000) + return 0; + if (start < sbi->sb_dirband_start + sbi->sb_dirband_size && end > sbi->sb_dirband_start) { + hpfs_lock(s); + if (s->s_flags & MS_RDONLY) { + err = -EROFS; + goto unlock_1; + } + if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) { + err = -EIO; + goto unlock_1; + } + idx = 0; + while ((len = find_run(bmp, &idx)) && !err) { + err = do_trim(s, sbi->sb_dirband_start + idx * 4, len * 4, start, end, minlen, result); + idx += len; + } + hpfs_brelse4(&qbh); +unlock_1: + hpfs_unlock(s); + } + start_bmp = start >> 14; + end_bmp = (end + 0x3fff) >> 14; + while (start_bmp < end_bmp && !err) { + hpfs_lock(s); + if (s->s_flags & MS_RDONLY) { + err = -EROFS; + goto unlock_2; + } + if (!(bmp = hpfs_map_bitmap(s, start_bmp, &qbh, "trim"))) { + err = -EIO; + goto unlock_2; + } + idx = 0; + while ((len = find_run(bmp, &idx)) && !err) { + err = do_trim(s, (start_bmp << 14) + idx, len, start, end, minlen, result); + idx += len; + } + hpfs_brelse4(&qbh); +unlock_2: + hpfs_unlock(s); + start_bmp++; + } + return err; +} diff --git a/fs/hpfs/dir.c b/fs/hpfs/dir.c index 2a8e07425..dc540bfce 100644 --- a/fs/hpfs/dir.c +++ b/fs/hpfs/dir.c @@ -327,4 +327,5 @@ const struct file_operations hpfs_dir_ops = .iterate = hpfs_readdir, .release = hpfs_dir_release, .fsync = hpfs_file_fsync, + .unlocked_ioctl = hpfs_ioctl, }; diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c index 6d8cfe9b5..7ca28d604 100644 --- a/fs/hpfs/file.c +++ b/fs/hpfs/file.c @@ -203,6 +203,7 @@ const struct file_operations hpfs_file_ops = .release = hpfs_file_release, .fsync = hpfs_file_fsync, .splice_read = generic_file_splice_read, + .unlocked_ioctl = hpfs_ioctl, }; const struct inode_operations hpfs_file_iops = diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h index b63b75fa0..c4867b511 100644 --- a/fs/hpfs/hpfs_fn.h +++ b/fs/hpfs/hpfs_fn.h @@ -18,6 +18,8 @@ #include <linux/pagemap.h> #include <linux/buffer_head.h> #include <linux/slab.h> +#include <linux/sched.h> +#include <linux/blkdev.h> #include <asm/unaligned.h> #include "hpfs.h" @@ -200,6 +202,7 @@ void hpfs_free_dnode(struct super_block *, secno); struct dnode *hpfs_alloc_dnode(struct super_block *, secno, dnode_secno *, struct quad_buffer_head *); struct fnode *hpfs_alloc_fnode(struct super_block *, secno, fnode_secno *, struct buffer_head **); struct anode *hpfs_alloc_anode(struct super_block *, secno, anode_secno *, struct buffer_head **); +int hpfs_trim_fs(struct super_block *, u64, u64, u64, unsigned *); /* anode.c */ @@ -304,7 +307,7 @@ extern const struct address_space_operations hpfs_symlink_aops; static inline struct hpfs_inode_info *hpfs_i(struct inode *inode) { - return list_entry(inode, struct hpfs_inode_info, vfs_inode); + return container_of(inode, struct hpfs_inode_info, vfs_inode); } static inline struct hpfs_sb_info *hpfs_sb(struct super_block *sb) @@ -318,6 +321,7 @@ __printf(2, 3) void hpfs_error(struct super_block *, const char *, ...); int hpfs_stop_cycles(struct super_block *, int, int *, int *, char *); unsigned hpfs_get_free_dnodes(struct super_block *); +long hpfs_ioctl(struct file *file, unsigned cmd, unsigned long arg); /* * local time (HPFS) to GMT (Unix) diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index 8685c6557..68a9bed05 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -199,12 +199,39 @@ static int hpfs_statfs(struct dentry *dentry, struct kstatfs *buf) return 0; } + +long hpfs_ioctl(struct file *file, unsigned cmd, unsigned long arg) +{ + switch (cmd) { + case FITRIM: { + struct fstrim_range range; + secno n_trimmed; + int r; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (copy_from_user(&range, (struct fstrim_range __user *)arg, sizeof(range))) + return -EFAULT; + r = hpfs_trim_fs(file_inode(file)->i_sb, range.start >> 9, (range.start + range.len) >> 9, (range.minlen + 511) >> 9, &n_trimmed); + if (r) + return r; + range.len = (u64)n_trimmed << 9; + if (copy_to_user((struct fstrim_range __user *)arg, &range, sizeof(range))) + return -EFAULT; + return 0; + } + default: { + return -ENOIOCTLCMD; + } + } +} + + static struct kmem_cache * hpfs_inode_cachep; static struct inode *hpfs_alloc_inode(struct super_block *sb) { struct hpfs_inode_info *ei; - ei = (struct hpfs_inode_info *)kmem_cache_alloc(hpfs_inode_cachep, GFP_NOFS); + ei = kmem_cache_alloc(hpfs_inode_cachep, GFP_NOFS); if (!ei) return NULL; ei->vfs_inode.i_version = 1; diff --git a/fs/hppfs/Makefile b/fs/hppfs/Makefile deleted file mode 100644 index 3a982bd97..000000000 --- a/fs/hppfs/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# -# Copyright (C) 2002 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com) -# Licensed under the GPL -# - -obj-$(CONFIG_HPPFS) += hppfs.o diff --git a/fs/hppfs/hppfs.c b/fs/hppfs/hppfs.c deleted file mode 100644 index fa2bd5366..000000000 --- a/fs/hppfs/hppfs.c +++ /dev/null @@ -1,766 +0,0 @@ -/* - * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) - * Licensed under the GPL - */ - -#include <linux/ctype.h> -#include <linux/dcache.h> -#include <linux/file.h> -#include <linux/fs.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/module.h> -#include <linux/mount.h> -#include <linux/slab.h> -#include <linux/statfs.h> -#include <linux/types.h> -#include <linux/pid_namespace.h> -#include <linux/namei.h> -#include <asm/uaccess.h> -#include <os.h> - -static struct inode *get_inode(struct super_block *, struct dentry *); - -struct hppfs_data { - struct list_head list; - char contents[PAGE_SIZE - sizeof(struct list_head)]; -}; - -struct hppfs_private { - struct file *proc_file; - int host_fd; - loff_t len; - struct hppfs_data *contents; -}; - -struct hppfs_inode_info { - struct dentry *proc_dentry; - struct inode vfs_inode; -}; - -static inline struct hppfs_inode_info *HPPFS_I(struct inode *inode) -{ - return container_of(inode, struct hppfs_inode_info, vfs_inode); -} - -#define HPPFS_SUPER_MAGIC 0xb00000ee - -static const struct super_operations hppfs_sbops; - -static int is_pid(struct dentry *dentry) -{ - struct super_block *sb; - int i; - - sb = dentry->d_sb; - if (dentry->d_parent != sb->s_root) - return 0; - - for (i = 0; i < dentry->d_name.len; i++) { - if (!isdigit(dentry->d_name.name[i])) - return 0; - } - return 1; -} - -static char *dentry_name(struct dentry *dentry, int extra) -{ - struct dentry *parent; - char *root, *name; - const char *seg_name; - int len, seg_len, root_len; - - len = 0; - parent = dentry; - while (parent->d_parent != parent) { - if (is_pid(parent)) - len += strlen("pid") + 1; - else len += parent->d_name.len + 1; - parent = parent->d_parent; - } - - root = "proc"; - root_len = strlen(root); - len += root_len; - name = kmalloc(len + extra + 1, GFP_KERNEL); - if (name == NULL) - return NULL; - - name[len] = '\0'; - parent = dentry; - while (parent->d_parent != parent) { - if (is_pid(parent)) { - seg_name = "pid"; - seg_len = strlen(seg_name); - } - else { - seg_name = parent->d_name.name; - seg_len = parent->d_name.len; - } - - len -= seg_len + 1; - name[len] = '/'; - memcpy(&name[len + 1], seg_name, seg_len); - parent = parent->d_parent; - } - memcpy(name, root, root_len); - return name; -} - -static int file_removed(struct dentry *dentry, const char *file) -{ - char *host_file; - int extra, fd; - - extra = 0; - if (file != NULL) - extra += strlen(file) + 1; - - host_file = dentry_name(dentry, extra + strlen("/remove")); - if (host_file == NULL) { - printk(KERN_ERR "file_removed : allocation failed\n"); - return -ENOMEM; - } - - if (file != NULL) { - strcat(host_file, "/"); - strcat(host_file, file); - } - strcat(host_file, "/remove"); - - fd = os_open_file(host_file, of_read(OPENFLAGS()), 0); - kfree(host_file); - if (fd > 0) { - os_close_file(fd); - return 1; - } - return 0; -} - -static struct dentry *hppfs_lookup(struct inode *ino, struct dentry *dentry, - unsigned int flags) -{ - struct dentry *proc_dentry, *parent; - struct qstr *name = &dentry->d_name; - struct inode *inode; - int err, deleted; - - deleted = file_removed(dentry, NULL); - if (deleted < 0) - return ERR_PTR(deleted); - else if (deleted) - return ERR_PTR(-ENOENT); - - parent = HPPFS_I(ino)->proc_dentry; - mutex_lock(&d_inode(parent)->i_mutex); - proc_dentry = lookup_one_len(name->name, parent, name->len); - mutex_unlock(&d_inode(parent)->i_mutex); - - if (IS_ERR(proc_dentry)) - return proc_dentry; - - err = -ENOMEM; - inode = get_inode(ino->i_sb, proc_dentry); - if (!inode) - goto out; - - d_add(dentry, inode); - return NULL; - - out: - return ERR_PTR(err); -} - -static const struct inode_operations hppfs_file_iops = { -}; - -static ssize_t read_proc(struct file *file, char __user *buf, ssize_t count, - loff_t *ppos, int is_user) -{ - ssize_t (*read)(struct file *, char __user *, size_t, loff_t *); - ssize_t n; - - read = file_inode(file)->i_fop->read; - - if (!is_user) - set_fs(KERNEL_DS); - - n = (*read)(file, buf, count, &file->f_pos); - - if (!is_user) - set_fs(USER_DS); - - if (ppos) - *ppos = file->f_pos; - return n; -} - -static ssize_t hppfs_read_file(int fd, char __user *buf, ssize_t count) -{ - ssize_t n; - int cur, err; - char *new_buf; - - n = -ENOMEM; - new_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (new_buf == NULL) { - printk(KERN_ERR "hppfs_read_file : kmalloc failed\n"); - goto out; - } - n = 0; - while (count > 0) { - cur = min_t(ssize_t, count, PAGE_SIZE); - err = os_read_file(fd, new_buf, cur); - if (err < 0) { - printk(KERN_ERR "hppfs_read : read failed, " - "errno = %d\n", err); - n = err; - goto out_free; - } else if (err == 0) - break; - - if (copy_to_user(buf, new_buf, err)) { - n = -EFAULT; - goto out_free; - } - n += err; - count -= err; - } - out_free: - kfree(new_buf); - out: - return n; -} - -static ssize_t hppfs_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - struct hppfs_private *hppfs = file->private_data; - struct hppfs_data *data; - loff_t off; - int err; - - if (hppfs->contents != NULL) { - int rem; - - if (*ppos >= hppfs->len) - return 0; - - data = hppfs->contents; - off = *ppos; - while (off >= sizeof(data->contents)) { - data = list_entry(data->list.next, struct hppfs_data, - list); - off -= sizeof(data->contents); - } - - if (off + count > hppfs->len) - count = hppfs->len - off; - rem = copy_to_user(buf, &data->contents[off], count); - *ppos += count - rem; - if (rem > 0) - return -EFAULT; - } else if (hppfs->host_fd != -1) { - err = os_seek_file(hppfs->host_fd, *ppos); - if (err) { - printk(KERN_ERR "hppfs_read : seek failed, " - "errno = %d\n", err); - return err; - } - err = hppfs_read_file(hppfs->host_fd, buf, count); - if (err < 0) { - printk(KERN_ERR "hppfs_read: read failed: %d\n", err); - return err; - } - count = err; - if (count > 0) - *ppos += count; - } - else count = read_proc(hppfs->proc_file, buf, count, ppos, 1); - - return count; -} - -static ssize_t hppfs_write(struct file *file, const char __user *buf, - size_t len, loff_t *ppos) -{ - struct hppfs_private *data = file->private_data; - struct file *proc_file = data->proc_file; - ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *); - - write = file_inode(proc_file)->i_fop->write; - return (*write)(proc_file, buf, len, ppos); -} - -static int open_host_sock(char *host_file, int *filter_out) -{ - char *end; - int fd; - - end = &host_file[strlen(host_file)]; - strcpy(end, "/rw"); - *filter_out = 1; - fd = os_connect_socket(host_file); - if (fd > 0) - return fd; - - strcpy(end, "/r"); - *filter_out = 0; - fd = os_connect_socket(host_file); - return fd; -} - -static void free_contents(struct hppfs_data *head) -{ - struct hppfs_data *data; - struct list_head *ele, *next; - - if (head == NULL) - return; - - list_for_each_safe(ele, next, &head->list) { - data = list_entry(ele, struct hppfs_data, list); - kfree(data); - } - kfree(head); -} - -static struct hppfs_data *hppfs_get_data(int fd, int filter, - struct file *proc_file, - struct file *hppfs_file, - loff_t *size_out) -{ - struct hppfs_data *data, *new, *head; - int n, err; - - err = -ENOMEM; - data = kmalloc(sizeof(*data), GFP_KERNEL); - if (data == NULL) { - printk(KERN_ERR "hppfs_get_data : head allocation failed\n"); - goto failed; - } - - INIT_LIST_HEAD(&data->list); - - head = data; - *size_out = 0; - - if (filter) { - while ((n = read_proc(proc_file, data->contents, - sizeof(data->contents), NULL, 0)) > 0) - os_write_file(fd, data->contents, n); - err = os_shutdown_socket(fd, 0, 1); - if (err) { - printk(KERN_ERR "hppfs_get_data : failed to shut down " - "socket\n"); - goto failed_free; - } - } - while (1) { - n = os_read_file(fd, data->contents, sizeof(data->contents)); - if (n < 0) { - err = n; - printk(KERN_ERR "hppfs_get_data : read failed, " - "errno = %d\n", err); - goto failed_free; - } else if (n == 0) - break; - - *size_out += n; - - if (n < sizeof(data->contents)) - break; - - new = kmalloc(sizeof(*data), GFP_KERNEL); - if (new == 0) { - printk(KERN_ERR "hppfs_get_data : data allocation " - "failed\n"); - err = -ENOMEM; - goto failed_free; - } - - INIT_LIST_HEAD(&new->list); - list_add(&new->list, &data->list); - data = new; - } - return head; - - failed_free: - free_contents(head); - failed: - return ERR_PTR(err); -} - -static struct hppfs_private *hppfs_data(void) -{ - struct hppfs_private *data; - - data = kmalloc(sizeof(*data), GFP_KERNEL); - if (data == NULL) - return data; - - *data = ((struct hppfs_private ) { .host_fd = -1, - .len = -1, - .contents = NULL } ); - return data; -} - -static int file_mode(int fmode) -{ - if (fmode == (FMODE_READ | FMODE_WRITE)) - return O_RDWR; - if (fmode == FMODE_READ) - return O_RDONLY; - if (fmode == FMODE_WRITE) - return O_WRONLY; - return 0; -} - -static int hppfs_open(struct inode *inode, struct file *file) -{ - const struct cred *cred = file->f_cred; - struct hppfs_private *data; - struct path path; - char *host_file; - int err, fd, type, filter; - - err = -ENOMEM; - data = hppfs_data(); - if (data == NULL) - goto out; - - host_file = dentry_name(file->f_path.dentry, strlen("/rw")); - if (host_file == NULL) - goto out_free2; - - path.mnt = inode->i_sb->s_fs_info; - path.dentry = HPPFS_I(inode)->proc_dentry; - - data->proc_file = dentry_open(&path, file_mode(file->f_mode), cred); - err = PTR_ERR(data->proc_file); - if (IS_ERR(data->proc_file)) - goto out_free1; - - type = os_file_type(host_file); - if (type == OS_TYPE_FILE) { - fd = os_open_file(host_file, of_read(OPENFLAGS()), 0); - if (fd >= 0) - data->host_fd = fd; - else - printk(KERN_ERR "hppfs_open : failed to open '%s', " - "errno = %d\n", host_file, -fd); - - data->contents = NULL; - } else if (type == OS_TYPE_DIR) { - fd = open_host_sock(host_file, &filter); - if (fd > 0) { - data->contents = hppfs_get_data(fd, filter, - data->proc_file, - file, &data->len); - if (!IS_ERR(data->contents)) - data->host_fd = fd; - } else - printk(KERN_ERR "hppfs_open : failed to open a socket " - "in '%s', errno = %d\n", host_file, -fd); - } - kfree(host_file); - - file->private_data = data; - return 0; - - out_free1: - kfree(host_file); - out_free2: - free_contents(data->contents); - kfree(data); - out: - return err; -} - -static int hppfs_dir_open(struct inode *inode, struct file *file) -{ - const struct cred *cred = file->f_cred; - struct hppfs_private *data; - struct path path; - int err; - - err = -ENOMEM; - data = hppfs_data(); - if (data == NULL) - goto out; - - path.mnt = inode->i_sb->s_fs_info; - path.dentry = HPPFS_I(inode)->proc_dentry; - data->proc_file = dentry_open(&path, file_mode(file->f_mode), cred); - err = PTR_ERR(data->proc_file); - if (IS_ERR(data->proc_file)) - goto out_free; - - file->private_data = data; - return 0; - - out_free: - kfree(data); - out: - return err; -} - -static loff_t hppfs_llseek(struct file *file, loff_t off, int where) -{ - struct hppfs_private *data = file->private_data; - struct file *proc_file = data->proc_file; - loff_t (*llseek)(struct file *, loff_t, int); - loff_t ret; - - llseek = file_inode(proc_file)->i_fop->llseek; - if (llseek != NULL) { - ret = (*llseek)(proc_file, off, where); - if (ret < 0) - return ret; - } - - return default_llseek(file, off, where); -} - -static int hppfs_release(struct inode *inode, struct file *file) -{ - struct hppfs_private *data = file->private_data; - struct file *proc_file = data->proc_file; - if (proc_file) - fput(proc_file); - kfree(data); - return 0; -} - -static const struct file_operations hppfs_file_fops = { - .owner = NULL, - .llseek = hppfs_llseek, - .read = hppfs_read, - .write = hppfs_write, - .open = hppfs_open, - .release = hppfs_release, -}; - -struct hppfs_dirent { - struct dir_context ctx; - struct dir_context *caller; - struct dentry *dentry; -}; - -static int hppfs_filldir(struct dir_context *ctx, const char *name, int size, - loff_t offset, u64 inode, unsigned int type) -{ - struct hppfs_dirent *dirent = - container_of(ctx, struct hppfs_dirent, ctx); - - if (file_removed(dirent->dentry, name)) - return 0; - - dirent->caller->pos = dirent->ctx.pos; - return !dir_emit(dirent->caller, name, size, inode, type); -} - -static int hppfs_readdir(struct file *file, struct dir_context *ctx) -{ - struct hppfs_private *data = file->private_data; - struct file *proc_file = data->proc_file; - struct hppfs_dirent d = { - .ctx.actor = hppfs_filldir, - .caller = ctx, - .dentry = file->f_path.dentry - }; - int err; - proc_file->f_pos = ctx->pos; - err = iterate_dir(proc_file, &d.ctx); - ctx->pos = d.ctx.pos; - return err; -} - -static const struct file_operations hppfs_dir_fops = { - .owner = NULL, - .iterate = hppfs_readdir, - .open = hppfs_dir_open, - .llseek = default_llseek, - .release = hppfs_release, -}; - -static int hppfs_statfs(struct dentry *dentry, struct kstatfs *sf) -{ - sf->f_blocks = 0; - sf->f_bfree = 0; - sf->f_bavail = 0; - sf->f_files = 0; - sf->f_ffree = 0; - sf->f_type = HPPFS_SUPER_MAGIC; - return 0; -} - -static struct inode *hppfs_alloc_inode(struct super_block *sb) -{ - struct hppfs_inode_info *hi; - - hi = kmalloc(sizeof(*hi), GFP_KERNEL); - if (!hi) - return NULL; - - hi->proc_dentry = NULL; - inode_init_once(&hi->vfs_inode); - return &hi->vfs_inode; -} - -void hppfs_evict_inode(struct inode *ino) -{ - clear_inode(ino); - dput(HPPFS_I(ino)->proc_dentry); - mntput(ino->i_sb->s_fs_info); -} - -static void hppfs_i_callback(struct rcu_head *head) -{ - struct inode *inode = container_of(head, struct inode, i_rcu); - kfree(HPPFS_I(inode)); -} - -static void hppfs_destroy_inode(struct inode *inode) -{ - call_rcu(&inode->i_rcu, hppfs_i_callback); -} - -static const struct super_operations hppfs_sbops = { - .alloc_inode = hppfs_alloc_inode, - .destroy_inode = hppfs_destroy_inode, - .evict_inode = hppfs_evict_inode, - .statfs = hppfs_statfs, -}; - -static int hppfs_readlink(struct dentry *dentry, char __user *buffer, - int buflen) -{ - struct dentry *proc_dentry = HPPFS_I(d_inode(dentry))->proc_dentry; - return d_inode(proc_dentry)->i_op->readlink(proc_dentry, buffer, - buflen); -} - -static void *hppfs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct dentry *proc_dentry = HPPFS_I(d_inode(dentry))->proc_dentry; - - return d_inode(proc_dentry)->i_op->follow_link(proc_dentry, nd); -} - -static void hppfs_put_link(struct dentry *dentry, struct nameidata *nd, - void *cookie) -{ - struct dentry *proc_dentry = HPPFS_I(d_inode(dentry))->proc_dentry; - - if (d_inode(proc_dentry)->i_op->put_link) - d_inode(proc_dentry)->i_op->put_link(proc_dentry, nd, cookie); -} - -static const struct inode_operations hppfs_dir_iops = { - .lookup = hppfs_lookup, -}; - -static const struct inode_operations hppfs_link_iops = { - .readlink = hppfs_readlink, - .follow_link = hppfs_follow_link, - .put_link = hppfs_put_link, -}; - -static struct inode *get_inode(struct super_block *sb, struct dentry *dentry) -{ - struct inode *proc_ino = d_inode(dentry); - struct inode *inode = new_inode(sb); - - if (!inode) { - dput(dentry); - return NULL; - } - - if (d_is_dir(dentry)) { - inode->i_op = &hppfs_dir_iops; - inode->i_fop = &hppfs_dir_fops; - } else if (d_is_symlink(dentry)) { - inode->i_op = &hppfs_link_iops; - inode->i_fop = &hppfs_file_fops; - } else { - inode->i_op = &hppfs_file_iops; - inode->i_fop = &hppfs_file_fops; - } - - HPPFS_I(inode)->proc_dentry = dentry; - - inode->i_uid = proc_ino->i_uid; - inode->i_gid = proc_ino->i_gid; - inode->i_atime = proc_ino->i_atime; - inode->i_mtime = proc_ino->i_mtime; - inode->i_ctime = proc_ino->i_ctime; - inode->i_ino = proc_ino->i_ino; - inode->i_mode = proc_ino->i_mode; - set_nlink(inode, proc_ino->i_nlink); - inode->i_size = proc_ino->i_size; - inode->i_blocks = proc_ino->i_blocks; - - return inode; -} - -static int hppfs_fill_super(struct super_block *sb, void *d, int silent) -{ - struct inode *root_inode; - struct vfsmount *proc_mnt; - int err = -ENOENT; - - proc_mnt = mntget(task_active_pid_ns(current)->proc_mnt); - if (IS_ERR(proc_mnt)) - goto out; - - sb->s_blocksize = 1024; - sb->s_blocksize_bits = 10; - sb->s_magic = HPPFS_SUPER_MAGIC; - sb->s_op = &hppfs_sbops; - sb->s_fs_info = proc_mnt; - - err = -ENOMEM; - root_inode = get_inode(sb, dget(proc_mnt->mnt_root)); - sb->s_root = d_make_root(root_inode); - if (!sb->s_root) - goto out_mntput; - - return 0; - - out_mntput: - mntput(proc_mnt); - out: - return(err); -} - -static struct dentry *hppfs_read_super(struct file_system_type *type, - int flags, const char *dev_name, - void *data) -{ - return mount_nodev(type, flags, data, hppfs_fill_super); -} - -static struct file_system_type hppfs_type = { - .owner = THIS_MODULE, - .name = "hppfs", - .mount = hppfs_read_super, - .kill_sb = kill_anon_super, - .fs_flags = 0, -}; -MODULE_ALIAS_FS("hppfs"); - -static int __init init_hppfs(void) -{ - return register_filesystem(&hppfs_type); -} - -static void __exit exit_hppfs(void) -{ - unregister_filesystem(&hppfs_type); -} - -module_init(init_hppfs) -module_exit(exit_hppfs) -MODULE_LICENSE("GPL"); diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 87724c1d7..973c24ce5 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -130,7 +130,6 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) goto out; ret = 0; - hugetlb_prefault_arch_hook(vma->vm_mm); if (vma->vm_flags & VM_WRITE && inode->i_size < len) inode->i_size = len; out: @@ -1011,6 +1010,8 @@ struct file *hugetlb_file_setup(const char *name, size_t size, inode = hugetlbfs_get_inode(sb, NULL, S_IFREG | S_IRWXUGO, 0); if (!inode) goto out_dentry; + if (creat_flags == HUGETLB_SHMFS_INODE) + inode->i_flags |= S_PRIVATE; file = ERR_PTR(-ENOMEM); if (hugetlb_reserve_pages(inode, 0, diff --git a/fs/inode.c b/fs/inode.c index 205733701..d30640f7a 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -58,7 +58,6 @@ static struct hlist_head *inode_hashtable __read_mostly; static __cacheline_aligned_in_smp DEFINE_SPINLOCK(inode_hash_lock); __cacheline_aligned_in_smp DEFINE_SPINLOCK(inode_sb_list_lock); -EXPORT_SYMBOL(inode_sb_list_lock); /* * Empty aops. Can be used for the cases where the user does not @@ -153,6 +152,7 @@ int inode_init_always(struct super_block *sb, struct inode *inode) inode->i_pipe = NULL; inode->i_bdev = NULL; inode->i_cdev = NULL; + inode->i_link = NULL; inode->i_rdev = 0; inode->dirtied_when = 0; @@ -224,6 +224,7 @@ EXPORT_SYMBOL(free_inode_nonrcu); void __destroy_inode(struct inode *inode) { BUG_ON(inode_has_buffers(inode)); + inode_detach_wb(inode); security_inode_free(inode); fsnotify_inode_delete(inode); locks_free_lock_context(inode->i_flctx); @@ -831,8 +832,6 @@ unsigned int get_next_ino(void) unsigned int *p = &get_cpu_var(last_ino); unsigned int res = *p; -start: - #ifdef CONFIG_SMP if (unlikely((res & (LAST_INO_BATCH-1)) == 0)) { static atomic_t shared_last_ino; @@ -842,8 +841,10 @@ start: } #endif - if (unlikely(!++res)) - goto start; /* never zero */ + res++; + /* get_next_ino should not provide a 0 inode number */ + if (unlikely(!res)) + res++; *p = res; put_cpu_var(last_ino); return res; @@ -1589,36 +1590,47 @@ static int update_time(struct inode *inode, struct timespec *time, int flags) * This function automatically handles read only file systems and media, * as well as the "noatime" flag and inode specific "noatime" markers. */ -void touch_atime(const struct path *path) +bool atime_needs_update(const struct path *path, struct inode *inode) { struct vfsmount *mnt = path->mnt; - struct inode *inode = d_inode(path->dentry); struct timespec now; if (inode->i_flags & S_NOATIME) - return; + return false; if (IS_NOATIME(inode)) - return; + return false; if ((inode->i_sb->s_flags & MS_NODIRATIME) && S_ISDIR(inode->i_mode)) - return; + return false; if (mnt->mnt_flags & MNT_NOATIME) - return; + return false; if ((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode)) - return; + return false; now = current_fs_time(inode->i_sb); if (!relatime_need_update(mnt, inode, now)) - return; + return false; if (timespec_equal(&inode->i_atime, &now)) + return false; + + return true; +} + +void touch_atime(const struct path *path) +{ + struct vfsmount *mnt = path->mnt; + struct inode *inode = d_inode(path->dentry); + struct timespec now; + + if (!atime_needs_update(path, inode)) return; if (!sb_start_write_trylock(inode->i_sb)) return; - if (__mnt_want_write(mnt)) + if (__mnt_want_write(mnt) != 0) goto skip_update; /* * File systems can error out when updating inodes if they need to @@ -1629,6 +1641,7 @@ void touch_atime(const struct path *path) * We may also fail on filesystems that have the ability to make parts * of the fs read only, e.g. subvolumes in Btrfs. */ + now = current_fs_time(inode->i_sb); update_time(inode, &now, S_ATIME); __mnt_drop_write(mnt); skip_update: @@ -1665,7 +1678,31 @@ int should_remove_suid(struct dentry *dentry) } EXPORT_SYMBOL(should_remove_suid); -static int __remove_suid(struct dentry *dentry, int kill) +/* + * Return mask of changes for notify_change() that need to be done as a + * response to write or truncate. Return 0 if nothing has to be changed. + * Negative value on error (change should be denied). + */ +int dentry_needs_remove_privs(struct dentry *dentry) +{ + struct inode *inode = d_inode(dentry); + int mask = 0; + int ret; + + if (IS_NOSEC(inode)) + return 0; + + mask = should_remove_suid(dentry); + ret = security_inode_need_killpriv(dentry); + if (ret < 0) + return ret; + if (ret) + mask |= ATTR_KILL_PRIV; + return mask; +} +EXPORT_SYMBOL(dentry_needs_remove_privs); + +static int __remove_privs(struct dentry *dentry, int kill) { struct iattr newattrs; @@ -1677,33 +1714,32 @@ static int __remove_suid(struct dentry *dentry, int kill) return notify_change(dentry, &newattrs, NULL); } -int file_remove_suid(struct file *file) +/* + * Remove special file priviledges (suid, capabilities) when file is written + * to or truncated. + */ +int file_remove_privs(struct file *file) { struct dentry *dentry = file->f_path.dentry; struct inode *inode = d_inode(dentry); - int killsuid; - int killpriv; + int kill; int error = 0; /* Fast path for nothing security related */ if (IS_NOSEC(inode)) return 0; - killsuid = should_remove_suid(dentry); - killpriv = security_inode_need_killpriv(dentry); - - if (killpriv < 0) - return killpriv; - if (killpriv) - error = security_inode_killpriv(dentry); - if (!error && killsuid) - error = __remove_suid(dentry, killsuid); + kill = file_needs_remove_privs(file); + if (kill < 0) + return kill; + if (kill) + error = __remove_privs(dentry, kill); if (!error) inode_has_no_xattr(inode); return error; } -EXPORT_SYMBOL(file_remove_suid); +EXPORT_SYMBOL(file_remove_privs); /** * file_update_time - update mtime and ctime time @@ -1958,9 +1994,8 @@ EXPORT_SYMBOL(inode_dio_wait); * inode is being instantiated). The reason for the cmpxchg() loop * --- which wouldn't be necessary if all code paths which modify * i_flags actually followed this rule, is that there is at least one - * code path which doesn't today --- for example, - * __generic_file_aio_write() calls file_remove_suid() without holding - * i_mutex --- so we use cmpxchg() out of an abundance of caution. + * code path which doesn't today so we use cmpxchg() out of an abundance + * of caution. * * In the long run, i_mutex is overkill, and we should probably look * at using the i_lock spinlock to protect i_flags, and then make sure diff --git a/fs/internal.h b/fs/internal.h index 01dce1d14..4d5af583a 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -107,6 +107,7 @@ extern struct file *do_file_open_root(struct dentry *, struct vfsmount *, extern long do_handle_open(int mountdirfd, struct file_handle __user *ufh, int open_flag); extern int open_check_o_direct(struct file *f); +extern int vfs_open(const struct path *, struct file *, const struct cred *); /* * inode.c diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 112fad9e1..4ff3fad4e 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -371,16 +371,7 @@ int jbd2_journal_write_metadata_buffer(transaction_t *transaction, */ J_ASSERT_BH(bh_in, buffer_jbddirty(bh_in)); -retry_alloc: - new_bh = alloc_buffer_head(GFP_NOFS); - if (!new_bh) { - /* - * Failure is not an option, but __GFP_NOFAIL is going - * away; so we retry ourselves here. - */ - congestion_wait(BLK_RW_ASYNC, HZ/50); - goto retry_alloc; - } + new_bh = alloc_buffer_head(GFP_NOFS|__GFP_NOFAIL); /* keep subsequent assertions sane */ atomic_set(&new_bh->b_count, 1); @@ -1144,7 +1135,6 @@ journal_t * jbd2_journal_init_dev(struct block_device *bdev, { journal_t *journal = journal_init_common(); struct buffer_head *bh; - char *p; int n; if (!journal) @@ -1157,9 +1147,7 @@ journal_t * jbd2_journal_init_dev(struct block_device *bdev, journal->j_blk_offset = start; journal->j_maxlen = len; bdevname(journal->j_dev, journal->j_devname); - p = journal->j_devname; - while ((p = strchr(p, '/'))) - *p = '!'; + strreplace(journal->j_devname, '/', '!'); jbd2_stats_proc_init(journal); n = journal->j_blocksize / sizeof(journal_block_tag_t); journal->j_wbufsize = n; @@ -1211,10 +1199,7 @@ journal_t * jbd2_journal_init_inode (struct inode *inode) journal->j_dev = journal->j_fs_dev = inode->i_sb->s_bdev; journal->j_inode = inode; bdevname(journal->j_dev, journal->j_devname); - p = journal->j_devname; - while ((p = strchr(p, '/'))) - *p = '!'; - p = journal->j_devname + strlen(journal->j_devname); + p = strreplace(journal->j_devname, '/', '!'); sprintf(p, "-%lu", journal->j_inode->i_ino); jbd_debug(1, "journal %p: inode %s/%ld, size %Ld, bits %d, blksize %ld\n", @@ -2354,7 +2339,7 @@ static int jbd2_journal_init_journal_head_cache(void) jbd2_journal_head_cache = kmem_cache_create("jbd2_journal_head", sizeof(struct journal_head), 0, /* offset */ - SLAB_TEMPORARY, /* flags */ + SLAB_TEMPORARY | SLAB_DESTROY_BY_RCU, NULL); /* ctor */ retval = 0; if (!jbd2_journal_head_cache) { @@ -2386,10 +2371,8 @@ static struct journal_head *journal_alloc_journal_head(void) if (!ret) { jbd_debug(1, "out of memory for journal_head\n"); pr_notice_ratelimited("ENOMEM in %s, retrying.\n", __func__); - while (!ret) { - yield(); - ret = kmem_cache_zalloc(jbd2_journal_head_cache, GFP_NOFS); - } + ret = kmem_cache_zalloc(jbd2_journal_head_cache, + GFP_NOFS | __GFP_NOFAIL); } return ret; } diff --git a/fs/jbd2/revoke.c b/fs/jbd2/revoke.c index 14214da80..0abf2e7f7 100644 --- a/fs/jbd2/revoke.c +++ b/fs/jbd2/revoke.c @@ -141,11 +141,13 @@ static int insert_revoke_hash(journal_t *journal, unsigned long long blocknr, { struct list_head *hash_list; struct jbd2_revoke_record_s *record; + gfp_t gfp_mask = GFP_NOFS; -repeat: - record = kmem_cache_alloc(jbd2_revoke_record_cache, GFP_NOFS); + if (journal_oom_retry) + gfp_mask |= __GFP_NOFAIL; + record = kmem_cache_alloc(jbd2_revoke_record_cache, gfp_mask); if (!record) - goto oom; + return -ENOMEM; record->sequence = seq; record->blocknr = blocknr; @@ -154,13 +156,6 @@ repeat: list_add(&record->hash, hash_list); spin_unlock(&journal->j_revoke_lock); return 0; - -oom: - if (!journal_oom_retry) - return -ENOMEM; - jbd_debug(1, "ENOMEM in %s, retrying\n", __func__); - yield(); - goto repeat; } /* Find a revoke record in the journal's hash table. */ diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index ff2f2e6ad..f3d06174b 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -278,22 +278,16 @@ static int start_this_handle(journal_t *journal, handle_t *handle, alloc_transaction: if (!journal->j_running_transaction) { + /* + * If __GFP_FS is not present, then we may be being called from + * inside the fs writeback layer, so we MUST NOT fail. + */ + if ((gfp_mask & __GFP_FS) == 0) + gfp_mask |= __GFP_NOFAIL; new_transaction = kmem_cache_zalloc(transaction_cache, gfp_mask); - if (!new_transaction) { - /* - * If __GFP_FS is not present, then we may be - * being called from inside the fs writeback - * layer, so we MUST NOT fail. Since - * __GFP_NOFAIL is going away, we will arrange - * to retry the allocation ourselves. - */ - if ((gfp_mask & __GFP_FS) == 0) { - congestion_wait(BLK_RW_ASYNC, HZ/50); - goto alloc_transaction; - } + if (!new_transaction) return -ENOMEM; - } } jbd_debug(3, "New handle %p going live.\n", handle); @@ -761,6 +755,30 @@ static void warn_dirty_buffer(struct buffer_head *bh) bdevname(bh->b_bdev, b), (unsigned long long)bh->b_blocknr); } +/* Call t_frozen trigger and copy buffer data into jh->b_frozen_data. */ +static void jbd2_freeze_jh_data(struct journal_head *jh) +{ + struct page *page; + int offset; + char *source; + struct buffer_head *bh = jh2bh(jh); + + J_EXPECT_JH(jh, buffer_uptodate(bh), "Possible IO failure.\n"); + page = bh->b_page; + offset = offset_in_page(bh->b_data); + source = kmap_atomic(page); + /* Fire data frozen trigger just before we copy the data */ + jbd2_buffer_frozen_trigger(jh, source + offset, jh->b_triggers); + memcpy(jh->b_frozen_data, source + offset, bh->b_size); + kunmap_atomic(source); + + /* + * Now that the frozen data is saved off, we need to store any matching + * triggers. + */ + jh->b_frozen_triggers = jh->b_triggers; +} + /* * If the buffer is already part of the current transaction, then there * is nothing we need to do. If it is already part of a prior @@ -780,7 +798,6 @@ do_get_write_access(handle_t *handle, struct journal_head *jh, journal_t *journal; int error; char *frozen_buffer = NULL; - int need_copy = 0; unsigned long start_lock, time_lock; if (is_handle_aborted(handle)) @@ -867,119 +884,96 @@ repeat: jh->b_modified = 0; /* + * If the buffer is not journaled right now, we need to make sure it + * doesn't get written to disk before the caller actually commits the + * new data + */ + if (!jh->b_transaction) { + JBUFFER_TRACE(jh, "no transaction"); + J_ASSERT_JH(jh, !jh->b_next_transaction); + JBUFFER_TRACE(jh, "file as BJ_Reserved"); + /* + * Make sure all stores to jh (b_modified, b_frozen_data) are + * visible before attaching it to the running transaction. + * Paired with barrier in jbd2_write_access_granted() + */ + smp_wmb(); + spin_lock(&journal->j_list_lock); + __jbd2_journal_file_buffer(jh, transaction, BJ_Reserved); + spin_unlock(&journal->j_list_lock); + goto done; + } + /* * If there is already a copy-out version of this buffer, then we don't * need to make another one */ if (jh->b_frozen_data) { JBUFFER_TRACE(jh, "has frozen data"); J_ASSERT_JH(jh, jh->b_next_transaction == NULL); - jh->b_next_transaction = transaction; - goto done; + goto attach_next; } - /* Is there data here we need to preserve? */ + JBUFFER_TRACE(jh, "owned by older transaction"); + J_ASSERT_JH(jh, jh->b_next_transaction == NULL); + J_ASSERT_JH(jh, jh->b_transaction == journal->j_committing_transaction); - if (jh->b_transaction && jh->b_transaction != transaction) { - JBUFFER_TRACE(jh, "owned by older transaction"); - J_ASSERT_JH(jh, jh->b_next_transaction == NULL); - J_ASSERT_JH(jh, jh->b_transaction == - journal->j_committing_transaction); + /* + * There is one case we have to be very careful about. If the + * committing transaction is currently writing this buffer out to disk + * and has NOT made a copy-out, then we cannot modify the buffer + * contents at all right now. The essence of copy-out is that it is + * the extra copy, not the primary copy, which gets journaled. If the + * primary copy is already going to disk then we cannot do copy-out + * here. + */ + if (buffer_shadow(bh)) { + JBUFFER_TRACE(jh, "on shadow: sleep"); + jbd_unlock_bh_state(bh); + wait_on_bit_io(&bh->b_state, BH_Shadow, TASK_UNINTERRUPTIBLE); + goto repeat; + } - /* There is one case we have to be very careful about. - * If the committing transaction is currently writing - * this buffer out to disk and has NOT made a copy-out, - * then we cannot modify the buffer contents at all - * right now. The essence of copy-out is that it is the - * extra copy, not the primary copy, which gets - * journaled. If the primary copy is already going to - * disk then we cannot do copy-out here. */ - - if (buffer_shadow(bh)) { - JBUFFER_TRACE(jh, "on shadow: sleep"); + /* + * Only do the copy if the currently-owning transaction still needs it. + * If buffer isn't on BJ_Metadata list, the committing transaction is + * past that stage (here we use the fact that BH_Shadow is set under + * bh_state lock together with refiling to BJ_Shadow list and at this + * point we know the buffer doesn't have BH_Shadow set). + * + * Subtle point, though: if this is a get_undo_access, then we will be + * relying on the frozen_data to contain the new value of the + * committed_data record after the transaction, so we HAVE to force the + * frozen_data copy in that case. + */ + if (jh->b_jlist == BJ_Metadata || force_copy) { + JBUFFER_TRACE(jh, "generate frozen data"); + if (!frozen_buffer) { + JBUFFER_TRACE(jh, "allocate memory for buffer"); jbd_unlock_bh_state(bh); - wait_on_bit_io(&bh->b_state, BH_Shadow, - TASK_UNINTERRUPTIBLE); - goto repeat; - } - - /* - * Only do the copy if the currently-owning transaction still - * needs it. If buffer isn't on BJ_Metadata list, the - * committing transaction is past that stage (here we use the - * fact that BH_Shadow is set under bh_state lock together with - * refiling to BJ_Shadow list and at this point we know the - * buffer doesn't have BH_Shadow set). - * - * Subtle point, though: if this is a get_undo_access, - * then we will be relying on the frozen_data to contain - * the new value of the committed_data record after the - * transaction, so we HAVE to force the frozen_data copy - * in that case. - */ - if (jh->b_jlist == BJ_Metadata || force_copy) { - JBUFFER_TRACE(jh, "generate frozen data"); + frozen_buffer = jbd2_alloc(jh2bh(jh)->b_size, GFP_NOFS); if (!frozen_buffer) { - JBUFFER_TRACE(jh, "allocate memory for buffer"); - jbd_unlock_bh_state(bh); - frozen_buffer = - jbd2_alloc(jh2bh(jh)->b_size, - GFP_NOFS); - if (!frozen_buffer) { - printk(KERN_ERR - "%s: OOM for frozen_buffer\n", - __func__); - JBUFFER_TRACE(jh, "oom!"); - error = -ENOMEM; - jbd_lock_bh_state(bh); - goto done; - } - goto repeat; + printk(KERN_ERR "%s: OOM for frozen_buffer\n", + __func__); + JBUFFER_TRACE(jh, "oom!"); + error = -ENOMEM; + goto out; } - jh->b_frozen_data = frozen_buffer; - frozen_buffer = NULL; - need_copy = 1; + goto repeat; } - jh->b_next_transaction = transaction; + jh->b_frozen_data = frozen_buffer; + frozen_buffer = NULL; + jbd2_freeze_jh_data(jh); } - - +attach_next: /* - * Finally, if the buffer is not journaled right now, we need to make - * sure it doesn't get written to disk before the caller actually - * commits the new data + * Make sure all stores to jh (b_modified, b_frozen_data) are visible + * before attaching it to the running transaction. Paired with barrier + * in jbd2_write_access_granted() */ - if (!jh->b_transaction) { - JBUFFER_TRACE(jh, "no transaction"); - J_ASSERT_JH(jh, !jh->b_next_transaction); - JBUFFER_TRACE(jh, "file as BJ_Reserved"); - spin_lock(&journal->j_list_lock); - __jbd2_journal_file_buffer(jh, transaction, BJ_Reserved); - spin_unlock(&journal->j_list_lock); - } + smp_wmb(); + jh->b_next_transaction = transaction; done: - if (need_copy) { - struct page *page; - int offset; - char *source; - - J_EXPECT_JH(jh, buffer_uptodate(jh2bh(jh)), - "Possible IO failure.\n"); - page = jh2bh(jh)->b_page; - offset = offset_in_page(jh2bh(jh)->b_data); - source = kmap_atomic(page); - /* Fire data frozen trigger just before we copy the data */ - jbd2_buffer_frozen_trigger(jh, source + offset, - jh->b_triggers); - memcpy(jh->b_frozen_data, source+offset, jh2bh(jh)->b_size); - kunmap_atomic(source); - - /* - * Now that the frozen data is saved off, we need to store - * any matching triggers. - */ - jh->b_frozen_triggers = jh->b_triggers; - } jbd_unlock_bh_state(bh); /* @@ -996,6 +990,55 @@ out: return error; } +/* Fast check whether buffer is already attached to the required transaction */ +static bool jbd2_write_access_granted(handle_t *handle, struct buffer_head *bh) +{ + struct journal_head *jh; + bool ret = false; + + /* Dirty buffers require special handling... */ + if (buffer_dirty(bh)) + return false; + + /* + * RCU protects us from dereferencing freed pages. So the checks we do + * are guaranteed not to oops. However the jh slab object can get freed + * & reallocated while we work with it. So we have to be careful. When + * we see jh attached to the running transaction, we know it must stay + * so until the transaction is committed. Thus jh won't be freed and + * will be attached to the same bh while we run. However it can + * happen jh gets freed, reallocated, and attached to the transaction + * just after we get pointer to it from bh. So we have to be careful + * and recheck jh still belongs to our bh before we return success. + */ + rcu_read_lock(); + if (!buffer_jbd(bh)) + goto out; + /* This should be bh2jh() but that doesn't work with inline functions */ + jh = READ_ONCE(bh->b_private); + if (!jh) + goto out; + if (jh->b_transaction != handle->h_transaction && + jh->b_next_transaction != handle->h_transaction) + goto out; + /* + * There are two reasons for the barrier here: + * 1) Make sure to fetch b_bh after we did previous checks so that we + * detect when jh went through free, realloc, attach to transaction + * while we were checking. Paired with implicit barrier in that path. + * 2) So that access to bh done after jbd2_write_access_granted() + * doesn't get reordered and see inconsistent state of concurrent + * do_get_write_access(). + */ + smp_mb(); + if (unlikely(jh->b_bh != bh)) + goto out; + ret = true; +out: + rcu_read_unlock(); + return ret; +} + /** * int jbd2_journal_get_write_access() - notify intent to modify a buffer for metadata (not data) update. * @handle: transaction to add buffer modifications to @@ -1009,9 +1052,13 @@ out: int jbd2_journal_get_write_access(handle_t *handle, struct buffer_head *bh) { - struct journal_head *jh = jbd2_journal_add_journal_head(bh); + struct journal_head *jh; int rc; + if (jbd2_write_access_granted(handle, bh)) + return 0; + + jh = jbd2_journal_add_journal_head(bh); /* We do not want to get caught playing with fields which the * log thread also manipulates. Make sure that the buffer * completes any outstanding IO before proceeding. */ @@ -1141,11 +1188,14 @@ out: int jbd2_journal_get_undo_access(handle_t *handle, struct buffer_head *bh) { int err; - struct journal_head *jh = jbd2_journal_add_journal_head(bh); + struct journal_head *jh; char *committed_data = NULL; JBUFFER_TRACE(jh, "entry"); + if (jbd2_write_access_granted(handle, bh)) + return 0; + jh = jbd2_journal_add_journal_head(bh); /* * Do this first --- it can drop the journal lock, so we want to * make sure that obtaining the committed_data is done diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 1ba5c9794..811800229 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -354,6 +354,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char ret = -ENOMEM; goto fail; } + inode->i_link = f->target; jffs2_dbg(1, "%s(): symlink's target '%s' cached\n", __func__, (char *)f->target); diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index fe5ea080b..2caf16820 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -272,12 +272,9 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino) mutex_lock(&f->sem); ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node); + if (ret) + goto error; - if (ret) { - mutex_unlock(&f->sem); - iget_failed(inode); - return ERR_PTR(ret); - } inode->i_mode = jemode_to_cpu(latest_node.mode); i_uid_write(inode, je16_to_cpu(latest_node.uid)); i_gid_write(inode, je16_to_cpu(latest_node.gid)); @@ -294,6 +291,7 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino) case S_IFLNK: inode->i_op = &jffs2_symlink_inode_operations; + inode->i_link = f->target; break; case S_IFDIR: diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index d200a9b8f..824e61ede 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -19,7 +19,7 @@ struct kstatfs; struct kvec; -#define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode)) +#define JFFS2_INODE_INFO(i) (container_of(i, struct jffs2_inode_info, vfs_inode)) #define OFNI_EDONI_2SFFJ(f) (&(f)->vfs_inode) #define JFFS2_SB_INFO(sb) (sb->s_fs_info) #define OFNI_BS_2SFFJ(c) ((struct super_block *)c->os_priv) diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index dddbde4f5..28e0aab42 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -1203,17 +1203,13 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, JFFS2_ERROR("failed to read from flash: error %d, %zd of %zd bytes read\n", ret, retlen, sizeof(*latest_node)); /* FIXME: If this fails, there seems to be a memory leak. Find it. */ - mutex_unlock(&f->sem); - jffs2_do_clear_inode(c, f); - return ret?ret:-EIO; + return ret ? ret : -EIO; } crc = crc32(0, latest_node, sizeof(*latest_node)-8); if (crc != je32_to_cpu(latest_node->node_crc)) { JFFS2_ERROR("CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(rii.latest_ref)); - mutex_unlock(&f->sem); - jffs2_do_clear_inode(c, f); return -EIO; } @@ -1250,16 +1246,11 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, * keep in RAM to facilitate quick follow symlink * operation. */ uint32_t csize = je32_to_cpu(latest_node->csize); - if (csize > JFFS2_MAX_NAME_LEN) { - mutex_unlock(&f->sem); - jffs2_do_clear_inode(c, f); + if (csize > JFFS2_MAX_NAME_LEN) return -ENAMETOOLONG; - } f->target = kmalloc(csize + 1, GFP_KERNEL); if (!f->target) { JFFS2_ERROR("can't allocate %u bytes of memory for the symlink target path cache\n", csize); - mutex_unlock(&f->sem); - jffs2_do_clear_inode(c, f); return -ENOMEM; } @@ -1271,8 +1262,6 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, ret = -EIO; kfree(f->target); f->target = NULL; - mutex_unlock(&f->sem); - jffs2_do_clear_inode(c, f); return ret; } @@ -1289,15 +1278,11 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, if (f->metadata) { JFFS2_ERROR("Argh. Special inode #%u with mode 0%o had metadata node\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); - mutex_unlock(&f->sem); - jffs2_do_clear_inode(c, f); return -EIO; } if (!frag_first(&f->fragtree)) { JFFS2_ERROR("Argh. Special inode #%u with mode 0%o has no fragments\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); - mutex_unlock(&f->sem); - jffs2_do_clear_inode(c, f); return -EIO; } /* ASSERT: f->fraglist != NULL */ @@ -1305,8 +1290,6 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, JFFS2_ERROR("Argh. Special inode #%u with mode 0x%x had more than one node\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */ - mutex_unlock(&f->sem); - jffs2_do_clear_inode(c, f); return -EIO; } /* OK. We're happy */ @@ -1400,10 +1383,8 @@ int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *i f->inocache = ic; ret = jffs2_do_read_inode_internal(c, f, &n); - if (!ret) { - mutex_unlock(&f->sem); - jffs2_do_clear_inode(c, f); - } + mutex_unlock(&f->sem); + jffs2_do_clear_inode(c, f); jffs2_xattr_do_crccheck_inode(c, ic); kfree (f); return ret; diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c index 1fefa25d0..8ce2f2401 100644 --- a/fs/jffs2/symlink.c +++ b/fs/jffs2/symlink.c @@ -9,58 +9,15 @@ * */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/kernel.h> -#include <linux/fs.h> -#include <linux/namei.h> #include "nodelist.h" -static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd); - const struct inode_operations jffs2_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = jffs2_follow_link, + .follow_link = simple_follow_link, .setattr = jffs2_setattr, .setxattr = jffs2_setxattr, .getxattr = jffs2_getxattr, .listxattr = jffs2_listxattr, .removexattr = jffs2_removexattr }; - -static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct jffs2_inode_info *f = JFFS2_INODE_INFO(d_inode(dentry)); - char *p = (char *)f->target; - - /* - * We don't acquire the f->sem mutex here since the only data we - * use is f->target. - * - * 1. If we are here the inode has already built and f->target has - * to point to the target path. - * 2. Nobody uses f->target (if the inode is symlink's inode). The - * exception is inode freeing function which frees f->target. But - * it can't be called while we are here and before VFS has - * stopped using our f->target string which we provide by means of - * nd_set_link() call. - */ - - if (!p) { - pr_err("%s(): can't find symlink target\n", __func__); - p = ERR_PTR(-EIO); - } - jffs2_dbg(1, "%s(): target path is '%s'\n", - __func__, (char *)f->target); - - nd_set_link(nd, p); - - /* - * We will unlock the f->sem mutex but VFS will use the f->target string. This is safe - * since the only way that may cause f->target to be changed is iput() operation. - * But VFS will not use f->target after iput() has been called. - */ - return NULL; -} - diff --git a/fs/jfs/file.c b/fs/jfs/file.c index e98d39d75..b9dc23cd0 100644 --- a/fs/jfs/file.c +++ b/fs/jfs/file.c @@ -76,7 +76,7 @@ static int jfs_open(struct inode *inode, struct file *file) if (ji->active_ag == -1) { struct jfs_sb_info *jfs_sb = JFS_SBI(inode->i_sb); ji->active_ag = BLKTOAG(addressPXD(&ji->ixpxd), jfs_sb); - atomic_inc( &jfs_sb->bmap->db_active[ji->active_ag]); + atomic_inc(&jfs_sb->bmap->db_active[ji->active_ag]); } spin_unlock_irq(&ji->ag_lock); } diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index 070dc4b33..41aa3ca6a 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -63,11 +63,12 @@ struct inode *jfs_iget(struct super_block *sb, unsigned long ino) inode->i_mapping->a_ops = &jfs_aops; } else { inode->i_op = &jfs_fast_symlink_inode_operations; + inode->i_link = JFS_IP(inode)->i_inline; /* * The inline data should be null-terminated, but * don't let on-disk corruption crash the kernel */ - JFS_IP(inode)->i_inline[inode->i_size] = '\0'; + inode->i_link[inode->i_size] = '\0'; } } else { inode->i_op = &jfs_file_inode_operations; @@ -133,11 +134,11 @@ int jfs_write_inode(struct inode *inode, struct writeback_control *wbc) * It has been committed since the last change, but was still * on the dirty inode list. */ - if (!test_cflag(COMMIT_Dirty, inode)) { + if (!test_cflag(COMMIT_Dirty, inode)) { /* Make sure committed changes hit the disk */ jfs_flush_journal(JFS_SBI(inode->i_sb)->log, wait); return 0; - } + } if (jfs_commit_inode(inode, wait)) { jfs_err("jfs_write_inode: jfs_commit_inode failed!"); diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c index 93a123289..8db8b7d61 100644 --- a/fs/jfs/ioctl.c +++ b/fs/jfs/ioctl.c @@ -180,9 +180,6 @@ long jfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case JFS_IOC_SETFLAGS32: cmd = JFS_IOC_SETFLAGS; break; - case FITRIM: - cmd = FITRIM; - break; } return jfs_ioctl(filp, cmd, arg); } diff --git a/fs/jfs/jfs_incore.h b/fs/jfs/jfs_incore.h index fa7e795bd..1f26d1910 100644 --- a/fs/jfs/jfs_incore.h +++ b/fs/jfs/jfs_incore.h @@ -206,7 +206,7 @@ struct jfs_sb_info { static inline struct jfs_inode_info *JFS_IP(struct inode *inode) { - return list_entry(inode, struct jfs_inode_info, vfs_inode); + return container_of(inode, struct jfs_inode_info, vfs_inode); } static inline int jfs_dirtable_inline(struct inode *inode) diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 66db7bc0e..a5ac97b9a 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -880,7 +880,6 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry, int ssize; /* source pathname size */ struct btstack btstack; struct inode *ip = d_inode(dentry); - unchar *i_fastsymlink; s64 xlen = 0; int bmask = 0, xsize; s64 xaddr; @@ -946,8 +945,8 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry, if (ssize <= IDATASIZE) { ip->i_op = &jfs_fast_symlink_inode_operations; - i_fastsymlink = JFS_IP(ip)->i_inline; - memcpy(i_fastsymlink, name, ssize); + ip->i_link = JFS_IP(ip)->i_inline; + memcpy(ip->i_link, name, ssize); ip->i_size = ssize - 1; /* @@ -1161,7 +1160,7 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry, rc = dtModify(tid, new_dir, &new_dname, &ino, old_ip->i_ino, JFS_RENAME); if (rc) - goto out4; + goto out_tx; drop_nlink(new_ip); if (S_ISDIR(new_ip->i_mode)) { drop_nlink(new_ip); @@ -1186,7 +1185,7 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry, if ((new_size = commitZeroLink(tid, new_ip)) < 0) { txAbort(tid, 1); /* Marks FS Dirty */ rc = new_size; - goto out4; + goto out_tx; } tblk = tid_to_tblock(tid); tblk->xflag |= COMMIT_DELETE; @@ -1204,7 +1203,7 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (rc) { jfs_err("jfs_rename didn't expect dtSearch to fail " "w/rc = %d", rc); - goto out4; + goto out_tx; } ino = old_ip->i_ino; @@ -1212,7 +1211,7 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (rc) { if (rc == -EIO) jfs_err("jfs_rename: dtInsert returned -EIO"); - goto out4; + goto out_tx; } if (S_ISDIR(old_ip->i_mode)) inc_nlink(new_dir); @@ -1227,7 +1226,7 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry, jfs_err("jfs_rename did not expect dtDelete to return rc = %d", rc); txAbort(tid, 1); /* Marks Filesystem dirty */ - goto out4; + goto out_tx; } if (S_ISDIR(old_ip->i_mode)) { drop_nlink(old_dir); @@ -1286,7 +1285,7 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry, rc = txCommit(tid, ipcount, iplist, commit_flag); - out4: + out_tx: txEnd(tid); if (new_ip) mutex_unlock(&JFS_IP(new_ip)->commit_mutex); @@ -1309,13 +1308,6 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry, } if (new_ip && (new_ip->i_nlink == 0)) set_cflag(COMMIT_Nolink, new_ip); - out3: - free_UCSname(&new_dname); - out2: - free_UCSname(&old_dname); - out1: - if (new_ip && !S_ISDIR(new_ip->i_mode)) - IWRITE_UNLOCK(new_ip); /* * Truncating the directory index table is not guaranteed. It * may need to be done iteratively @@ -1326,7 +1318,13 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry, clear_cflag(COMMIT_Stale, old_dir); } - + if (new_ip && !S_ISDIR(new_ip->i_mode)) + IWRITE_UNLOCK(new_ip); + out3: + free_UCSname(&new_dname); + out2: + free_UCSname(&old_dname); + out1: jfs_info("jfs_rename: returning %d", rc); return rc; } diff --git a/fs/jfs/symlink.c b/fs/jfs/symlink.c index 80f42bcc4..5929e2363 100644 --- a/fs/jfs/symlink.c +++ b/fs/jfs/symlink.c @@ -17,21 +17,13 @@ */ #include <linux/fs.h> -#include <linux/namei.h> #include "jfs_incore.h" #include "jfs_inode.h" #include "jfs_xattr.h" -static void *jfs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - char *s = JFS_IP(d_inode(dentry))->i_inline; - nd_set_link(nd, s); - return NULL; -} - const struct inode_operations jfs_fast_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = jfs_follow_link, + .follow_link = simple_follow_link, .setattr = jfs_setattr, .setxattr = jfs_setxattr, .getxattr = jfs_getxattr, diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 2bacb9988..7247252ee 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -785,7 +785,6 @@ static unsigned int kernfs_fop_poll(struct file *filp, poll_table *wait) struct kernfs_node *kn = filp->f_path.dentry->d_fsdata; struct kernfs_open_node *on = kn->attr.open; - /* need parent for the kobj, grab both */ if (!kernfs_get_active(kn)) goto trigger; diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index af9fa7499..6762bfbd8 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -76,7 +76,6 @@ extern struct kmem_cache *kernfs_node_cache; /* * inode.c */ -struct inode *kernfs_get_inode(struct super_block *sb, struct kernfs_node *kn); void kernfs_evict_inode(struct inode *inode); int kernfs_iop_permission(struct inode *inode, int mask); int kernfs_iop_setattr(struct dentry *dentry, struct iattr *iattr); diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index 8a198898e..db272528a 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -112,25 +112,18 @@ static int kernfs_getlink(struct dentry *dentry, char *path) return error; } -static void *kernfs_iop_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *kernfs_iop_follow_link(struct dentry *dentry, void **cookie) { int error = -ENOMEM; unsigned long page = get_zeroed_page(GFP_KERNEL); - if (page) { - error = kernfs_getlink(dentry, (char *) page); - if (error < 0) - free_page((unsigned long)page); - } - nd_set_link(nd, error ? ERR_PTR(error) : (char *)page); - return NULL; -} - -static void kernfs_iop_put_link(struct dentry *dentry, struct nameidata *nd, - void *cookie) -{ - char *page = nd_get_link(nd); - if (!IS_ERR(page)) + if (!page) + return ERR_PTR(-ENOMEM); + error = kernfs_getlink(dentry, (char *)page); + if (unlikely(error < 0)) { free_page((unsigned long)page); + return ERR_PTR(error); + } + return *cookie = (char *)page; } const struct inode_operations kernfs_symlink_iops = { @@ -140,7 +133,7 @@ const struct inode_operations kernfs_symlink_iops = { .listxattr = kernfs_iop_listxattr, .readlink = generic_readlink, .follow_link = kernfs_iop_follow_link, - .put_link = kernfs_iop_put_link, + .put_link = free_page_put_link, .setattr = kernfs_iop_setattr, .getattr = kernfs_iop_getattr, .permission = kernfs_iop_permission, diff --git a/fs/libfs.c b/fs/libfs.c index 02813592e..102edfd39 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -20,11 +20,6 @@ #include "internal.h" -static inline int simple_positive(struct dentry *dentry) -{ - return d_really_is_positive(dentry) && !d_unhashed(dentry); -} - int simple_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { @@ -1024,15 +1019,18 @@ int noop_fsync(struct file *file, loff_t start, loff_t end, int datasync) } EXPORT_SYMBOL(noop_fsync); -void kfree_put_link(struct dentry *dentry, struct nameidata *nd, - void *cookie) +void kfree_put_link(struct inode *unused, void *cookie) { - char *s = nd_get_link(nd); - if (!IS_ERR(s)) - kfree(s); + kfree(cookie); } EXPORT_SYMBOL(kfree_put_link); +void free_page_put_link(struct inode *unused, void *cookie) +{ + free_page((unsigned long) cookie); +} +EXPORT_SYMBOL(free_page_put_link); + /* * nop .set_page_dirty method so that people can use .page_mkwrite on * anon inodes. @@ -1094,6 +1092,17 @@ simple_nosetlease(struct file *filp, long arg, struct file_lock **flp, } EXPORT_SYMBOL(simple_nosetlease); +const char *simple_follow_link(struct dentry *dentry, void **cookie) +{ + return d_inode(dentry)->i_link; +} +EXPORT_SYMBOL(simple_follow_link); + +const struct inode_operations simple_symlink_inode_operations = { + .follow_link = simple_follow_link, + .readlink = generic_readlink +}; +EXPORT_SYMBOL(simple_symlink_inode_operations); /* * Operations for a permanently empty directory. diff --git a/fs/locks.c b/fs/locks.c index 653faabb0..d3d558ba4 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -862,12 +862,11 @@ static int posix_locks_deadlock(struct file_lock *caller_fl, * whether or not a lock was successfully freed by testing the return * value for -ENOENT. */ -static int flock_lock_file(struct file *filp, struct file_lock *request) +static int flock_lock_inode(struct inode *inode, struct file_lock *request) { struct file_lock *new_fl = NULL; struct file_lock *fl; struct file_lock_context *ctx; - struct inode *inode = file_inode(filp); int error = 0; bool found = false; LIST_HEAD(dispose); @@ -890,7 +889,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) goto find_conflict; list_for_each_entry(fl, &ctx->flc_flock, fl_list) { - if (filp != fl->fl_file) + if (request->fl_file != fl->fl_file) continue; if (request->fl_type == fl->fl_type) goto out; @@ -1164,20 +1163,19 @@ int posix_lock_file(struct file *filp, struct file_lock *fl, EXPORT_SYMBOL(posix_lock_file); /** - * posix_lock_file_wait - Apply a POSIX-style lock to a file - * @filp: The file to apply the lock to + * posix_lock_inode_wait - Apply a POSIX-style lock to a file + * @inode: inode of file to which lock request should be applied * @fl: The lock to be applied * - * Add a POSIX style lock to a file. - * We merge adjacent & overlapping locks whenever possible. - * POSIX locks are sorted by owner task, then by starting address + * Variant of posix_lock_file_wait that does not take a filp, and so can be + * used after the filp has already been torn down. */ -int posix_lock_file_wait(struct file *filp, struct file_lock *fl) +int posix_lock_inode_wait(struct inode *inode, struct file_lock *fl) { int error; might_sleep (); for (;;) { - error = posix_lock_file(filp, fl, NULL); + error = __posix_lock_file(inode, fl, NULL); if (error != FILE_LOCK_DEFERRED) break; error = wait_event_interruptible(fl->fl_wait, !fl->fl_next); @@ -1189,7 +1187,7 @@ int posix_lock_file_wait(struct file *filp, struct file_lock *fl) } return error; } -EXPORT_SYMBOL(posix_lock_file_wait); +EXPORT_SYMBOL(posix_lock_inode_wait); /** * locks_mandatory_locked - Check for an active lock @@ -1851,18 +1849,18 @@ int fcntl_setlease(unsigned int fd, struct file *filp, long arg) } /** - * flock_lock_file_wait - Apply a FLOCK-style lock to a file - * @filp: The file to apply the lock to + * flock_lock_inode_wait - Apply a FLOCK-style lock to a file + * @inode: inode of the file to apply to * @fl: The lock to be applied * - * Add a FLOCK style lock to a file. + * Apply a FLOCK style lock request to an inode. */ -int flock_lock_file_wait(struct file *filp, struct file_lock *fl) +int flock_lock_inode_wait(struct inode *inode, struct file_lock *fl) { int error; might_sleep(); for (;;) { - error = flock_lock_file(filp, fl); + error = flock_lock_inode(inode, fl); if (error != FILE_LOCK_DEFERRED) break; error = wait_event_interruptible(fl->fl_wait, !fl->fl_next); @@ -1874,8 +1872,7 @@ int flock_lock_file_wait(struct file *filp, struct file_lock *fl) } return error; } - -EXPORT_SYMBOL(flock_lock_file_wait); +EXPORT_SYMBOL(flock_lock_inode_wait); /** * sys_flock: - flock() system call. @@ -2401,7 +2398,8 @@ locks_remove_flock(struct file *filp) .fl_type = F_UNLCK, .fl_end = OFFSET_MAX, }; - struct file_lock_context *flctx = file_inode(filp)->i_flctx; + struct inode *inode = file_inode(filp); + struct file_lock_context *flctx = inode->i_flctx; if (list_empty(&flctx->flc_flock)) return; @@ -2409,7 +2407,7 @@ locks_remove_flock(struct file *filp) if (filp->f_op->flock) filp->f_op->flock(filp, F_SETLKW, &fl); else - flock_lock_file(filp, &fl); + flock_lock_inode(inode, &fl); if (fl.fl_ops && fl.fl_ops->fl_release_private) fl.fl_ops->fl_release_private(&fl); diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c index 4cf38f118..f9b45d46d 100644 --- a/fs/logfs/dir.c +++ b/fs/logfs/dir.c @@ -779,6 +779,7 @@ fail: const struct inode_operations logfs_symlink_iops = { .readlink = generic_readlink, .follow_link = page_follow_link_light, + .put_link = page_put_link, }; const struct inode_operations logfs_dir_iops = { diff --git a/fs/minix/dir.c b/fs/minix/dir.c index 118e4e7bc..d19ac2581 100644 --- a/fs/minix/dir.c +++ b/fs/minix/dir.c @@ -45,11 +45,6 @@ minix_last_byte(struct inode *inode, unsigned long page_nr) return last_byte; } -static inline unsigned long dir_pages(struct inode *inode) -{ - return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT; -} - static int dir_commit_chunk(struct page *page, loff_t pos, unsigned len) { struct address_space *mapping = page->mapping; diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 1182d1e26..086cd0a61 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -62,7 +62,7 @@ static struct kmem_cache * minix_inode_cachep; static struct inode *minix_alloc_inode(struct super_block *sb) { struct minix_inode_info *ei; - ei = (struct minix_inode_info *)kmem_cache_alloc(minix_inode_cachep, GFP_KERNEL); + ei = kmem_cache_alloc(minix_inode_cachep, GFP_KERNEL); if (!ei) return NULL; return &ei->vfs_inode; diff --git a/fs/minix/minix.h b/fs/minix/minix.h index 1ebd11854..01ad81dca 100644 --- a/fs/minix/minix.h +++ b/fs/minix/minix.h @@ -84,7 +84,7 @@ static inline struct minix_sb_info *minix_sb(struct super_block *sb) static inline struct minix_inode_info *minix_i(struct inode *inode) { - return list_entry(inode, struct minix_inode_info, vfs_inode); + return container_of(inode, struct minix_inode_info, vfs_inode); } static inline unsigned minix_blocks_needed(unsigned bits, unsigned blocksize) diff --git a/fs/mount.h b/fs/mount.h index 6a61c2b3e..14db05d42 100644 --- a/fs/mount.h +++ b/fs/mount.h @@ -88,6 +88,7 @@ static inline int is_mounted(struct vfsmount *mnt) extern struct mount *__lookup_mnt(struct vfsmount *, struct dentry *); extern struct mount *__lookup_mnt_last(struct vfsmount *, struct dentry *); +extern int __legitimize_mnt(struct vfsmount *, unsigned); extern bool legitimize_mnt(struct vfsmount *, unsigned); extern void __detach_mounts(struct dentry *dentry); @@ -117,7 +118,6 @@ static inline void unlock_mount_hash(void) } struct proc_mounts { - struct seq_file m; struct mnt_namespace *ns; struct path root; int (*show)(struct seq_file *, struct vfsmount *); @@ -126,8 +126,6 @@ struct proc_mounts { loff_t cached_index; }; -#define proc_mounts(p) (container_of((p), struct proc_mounts, m)) - extern const struct seq_operations mounts_op; extern bool __is_local_mountpoint(struct dentry *dentry); diff --git a/fs/mpage.c b/fs/mpage.c index 3e79220ba..ca0244b69 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -605,6 +605,8 @@ alloc_new: bio_get_nr_vecs(bdev), GFP_NOFS|__GFP_HIGH); if (bio == NULL) goto confused; + + wbc_init_bio(wbc, bio); } /* @@ -612,6 +614,7 @@ alloc_new: * the confused fail path above (OOM) will be very confused when * it finds all bh marked clean (i.e. it will not write anything) */ + wbc_account_io(wbc, page, PAGE_SIZE); length = first_unmapped << blkbits; if (bio_add_page(bio, page, length, 0) < length) { bio = mpage_bio_submit(WRITE, bio); diff --git a/fs/namei.c b/fs/namei.c index fe30d3be4..1c2105ed2 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -492,6 +492,7 @@ void path_put(const struct path *path) } EXPORT_SYMBOL(path_put); +#define EMBEDDED_LEVELS 2 struct nameidata { struct path path; struct qstr last; @@ -501,10 +502,139 @@ struct nameidata { unsigned seq, m_seq; int last_type; unsigned depth; - struct file *base; - char *saved_names[MAX_NESTED_LINKS + 1]; + int total_link_count; + struct saved { + struct path link; + void *cookie; + const char *name; + struct inode *inode; + unsigned seq; + } *stack, internal[EMBEDDED_LEVELS]; + struct filename *name; + struct nameidata *saved; + unsigned root_seq; + int dfd; }; +static void set_nameidata(struct nameidata *p, int dfd, struct filename *name) +{ + struct nameidata *old = current->nameidata; + p->stack = p->internal; + p->dfd = dfd; + p->name = name; + p->total_link_count = old ? old->total_link_count : 0; + p->saved = old; + current->nameidata = p; +} + +static void restore_nameidata(void) +{ + struct nameidata *now = current->nameidata, *old = now->saved; + + current->nameidata = old; + if (old) + old->total_link_count = now->total_link_count; + if (now->stack != now->internal) { + kfree(now->stack); + now->stack = now->internal; + } +} + +static int __nd_alloc_stack(struct nameidata *nd) +{ + struct saved *p; + + if (nd->flags & LOOKUP_RCU) { + p= kmalloc(MAXSYMLINKS * sizeof(struct saved), + GFP_ATOMIC); + if (unlikely(!p)) + return -ECHILD; + } else { + p= kmalloc(MAXSYMLINKS * sizeof(struct saved), + GFP_KERNEL); + if (unlikely(!p)) + return -ENOMEM; + } + memcpy(p, nd->internal, sizeof(nd->internal)); + nd->stack = p; + return 0; +} + +static inline int nd_alloc_stack(struct nameidata *nd) +{ + if (likely(nd->depth != EMBEDDED_LEVELS)) + return 0; + if (likely(nd->stack != nd->internal)) + return 0; + return __nd_alloc_stack(nd); +} + +static void drop_links(struct nameidata *nd) +{ + int i = nd->depth; + while (i--) { + struct saved *last = nd->stack + i; + struct inode *inode = last->inode; + if (last->cookie && inode->i_op->put_link) { + inode->i_op->put_link(inode, last->cookie); + last->cookie = NULL; + } + } +} + +static void terminate_walk(struct nameidata *nd) +{ + drop_links(nd); + if (!(nd->flags & LOOKUP_RCU)) { + int i; + path_put(&nd->path); + for (i = 0; i < nd->depth; i++) + path_put(&nd->stack[i].link); + if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) { + path_put(&nd->root); + nd->root.mnt = NULL; + } + } else { + nd->flags &= ~LOOKUP_RCU; + if (!(nd->flags & LOOKUP_ROOT)) + nd->root.mnt = NULL; + rcu_read_unlock(); + } + nd->depth = 0; +} + +/* path_put is needed afterwards regardless of success or failure */ +static bool legitimize_path(struct nameidata *nd, + struct path *path, unsigned seq) +{ + int res = __legitimize_mnt(path->mnt, nd->m_seq); + if (unlikely(res)) { + if (res > 0) + path->mnt = NULL; + path->dentry = NULL; + return false; + } + if (unlikely(!lockref_get_not_dead(&path->dentry->d_lockref))) { + path->dentry = NULL; + return false; + } + return !read_seqcount_retry(&path->dentry->d_seq, seq); +} + +static bool legitimize_links(struct nameidata *nd) +{ + int i; + for (i = 0; i < nd->depth; i++) { + struct saved *last = nd->stack + i; + if (unlikely(!legitimize_path(nd, &last->link, last->seq))) { + drop_links(nd); + nd->depth = i + 1; + return false; + } + } + return true; +} + /* * Path walking has 2 modes, rcu-walk and ref-walk (see * Documentation/filesystems/path-lookup.txt). In situations when we can't @@ -520,35 +650,28 @@ struct nameidata { * unlazy_walk - try to switch to ref-walk mode. * @nd: nameidata pathwalk data * @dentry: child of nd->path.dentry or NULL + * @seq: seq number to check dentry against * Returns: 0 on success, -ECHILD on failure * * unlazy_walk attempts to legitimize the current nd->path, nd->root and dentry * for ref-walk mode. @dentry must be a path found by a do_lookup call on * @nd or NULL. Must be called from rcu-walk context. + * Nothing should touch nameidata between unlazy_walk() failure and + * terminate_walk(). */ -static int unlazy_walk(struct nameidata *nd, struct dentry *dentry) +static int unlazy_walk(struct nameidata *nd, struct dentry *dentry, unsigned seq) { - struct fs_struct *fs = current->fs; struct dentry *parent = nd->path.dentry; BUG_ON(!(nd->flags & LOOKUP_RCU)); - /* - * After legitimizing the bastards, terminate_walk() - * will do the right thing for non-RCU mode, and all our - * subsequent exit cases should rcu_read_unlock() - * before returning. Do vfsmount first; if dentry - * can't be legitimized, just set nd->path.dentry to NULL - * and rely on dput(NULL) being a no-op. - */ - if (!legitimize_mnt(nd->path.mnt, nd->m_seq)) - return -ECHILD; nd->flags &= ~LOOKUP_RCU; - - if (!lockref_get_not_dead(&parent->d_lockref)) { - nd->path.dentry = NULL; - goto out; - } + if (unlikely(!legitimize_links(nd))) + goto out2; + if (unlikely(!legitimize_mnt(nd->path.mnt, nd->m_seq))) + goto out2; + if (unlikely(!lockref_get_not_dead(&parent->d_lockref))) + goto out1; /* * For a negative lookup, the lookup sequence point is the parents @@ -568,7 +691,7 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry) } else { if (!lockref_get_not_dead(&dentry->d_lockref)) goto out; - if (read_seqcount_retry(&dentry->d_seq, nd->seq)) + if (read_seqcount_retry(&dentry->d_seq, seq)) goto drop_dentry; } @@ -577,22 +700,24 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry) * still valid and get it if required. */ if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) { - spin_lock(&fs->lock); - if (nd->root.mnt != fs->root.mnt || nd->root.dentry != fs->root.dentry) - goto unlock_and_drop_dentry; - path_get(&nd->root); - spin_unlock(&fs->lock); + if (unlikely(!legitimize_path(nd, &nd->root, nd->root_seq))) { + rcu_read_unlock(); + dput(dentry); + return -ECHILD; + } } rcu_read_unlock(); return 0; -unlock_and_drop_dentry: - spin_unlock(&fs->lock); drop_dentry: rcu_read_unlock(); dput(dentry); goto drop_root_mnt; +out2: + nd->path.mnt = NULL; +out1: + nd->path.dentry = NULL; out: rcu_read_unlock(); drop_root_mnt: @@ -601,6 +726,24 @@ drop_root_mnt: return -ECHILD; } +static int unlazy_link(struct nameidata *nd, struct path *link, unsigned seq) +{ + if (unlikely(!legitimize_path(nd, link, seq))) { + drop_links(nd); + nd->depth = 0; + nd->flags &= ~LOOKUP_RCU; + nd->path.mnt = NULL; + nd->path.dentry = NULL; + if (!(nd->flags & LOOKUP_ROOT)) + nd->root.mnt = NULL; + rcu_read_unlock(); + } else if (likely(unlazy_walk(nd, NULL, 0)) == 0) { + return 0; + } + path_put(link); + return -ECHILD; +} + static inline int d_revalidate(struct dentry *dentry, unsigned int flags) { return dentry->d_op->d_revalidate(dentry, flags); @@ -622,26 +765,10 @@ static int complete_walk(struct nameidata *nd) int status; if (nd->flags & LOOKUP_RCU) { - nd->flags &= ~LOOKUP_RCU; if (!(nd->flags & LOOKUP_ROOT)) nd->root.mnt = NULL; - - if (!legitimize_mnt(nd->path.mnt, nd->m_seq)) { - rcu_read_unlock(); - return -ECHILD; - } - if (unlikely(!lockref_get_not_dead(&dentry->d_lockref))) { - rcu_read_unlock(); - mntput(nd->path.mnt); + if (unlikely(unlazy_walk(nd, NULL, 0))) return -ECHILD; - } - if (read_seqcount_retry(&dentry->d_seq, nd->seq)) { - rcu_read_unlock(); - dput(dentry); - mntput(nd->path.mnt); - return -ECHILD; - } - rcu_read_unlock(); } if (likely(!(nd->flags & LOOKUP_JUMPED))) @@ -657,28 +784,24 @@ static int complete_walk(struct nameidata *nd) if (!status) status = -ESTALE; - path_put(&nd->path); return status; } -static __always_inline void set_root(struct nameidata *nd) +static void set_root(struct nameidata *nd) { get_fs_root(current->fs, &nd->root); } -static int link_path_walk(const char *, struct nameidata *); - -static __always_inline unsigned set_root_rcu(struct nameidata *nd) +static void set_root_rcu(struct nameidata *nd) { struct fs_struct *fs = current->fs; - unsigned seq, res; + unsigned seq; do { seq = read_seqcount_begin(&fs->seq); nd->root = fs->root; - res = __read_seqcount_begin(&nd->root.dentry->d_seq); + nd->root_seq = __read_seqcount_begin(&nd->root.dentry->d_seq); } while (read_seqcount_retry(&fs->seq, seq)); - return res; } static void path_put_conditional(struct path *path, struct nameidata *nd) @@ -704,8 +827,9 @@ static inline void path_to_nameidata(const struct path *path, * Helper to directly jump to a known parsed path from ->follow_link, * caller must have taken a reference to path beforehand. */ -void nd_jump_link(struct nameidata *nd, struct path *path) +void nd_jump_link(struct path *path) { + struct nameidata *nd = current->nameidata; path_put(&nd->path); nd->path = *path; @@ -713,24 +837,14 @@ void nd_jump_link(struct nameidata *nd, struct path *path) nd->flags |= LOOKUP_JUMPED; } -void nd_set_link(struct nameidata *nd, char *path) -{ - nd->saved_names[nd->depth] = path; -} -EXPORT_SYMBOL(nd_set_link); - -char *nd_get_link(struct nameidata *nd) -{ - return nd->saved_names[nd->depth]; -} -EXPORT_SYMBOL(nd_get_link); - -static inline void put_link(struct nameidata *nd, struct path *link, void *cookie) +static inline void put_link(struct nameidata *nd) { - struct inode *inode = link->dentry->d_inode; - if (inode->i_op->put_link) - inode->i_op->put_link(link->dentry, nd, cookie); - path_put(link); + struct saved *last = nd->stack + --nd->depth; + struct inode *inode = last->inode; + if (last->cookie && inode->i_op->put_link) + inode->i_op->put_link(inode, last->cookie); + if (!(nd->flags & LOOKUP_RCU)) + path_put(&last->link); } int sysctl_protected_symlinks __read_mostly = 0; @@ -738,7 +852,6 @@ int sysctl_protected_hardlinks __read_mostly = 0; /** * may_follow_link - Check symlink following for unsafe situations - * @link: The path of the symlink * @nd: nameidata pathwalk data * * In the case of the sysctl_protected_symlinks sysctl being enabled, @@ -752,7 +865,7 @@ int sysctl_protected_hardlinks __read_mostly = 0; * * Returns 0 if following the symlink is allowed, -ve on error. */ -static inline int may_follow_link(struct path *link, struct nameidata *nd) +static inline int may_follow_link(struct nameidata *nd) { const struct inode *inode; const struct inode *parent; @@ -761,12 +874,12 @@ static inline int may_follow_link(struct path *link, struct nameidata *nd) return 0; /* Allowed if owner and follower match. */ - inode = link->dentry->d_inode; + inode = nd->stack[0].inode; if (uid_eq(current_cred()->fsuid, inode->i_uid)) return 0; /* Allowed if parent directory not sticky and world-writable. */ - parent = nd->path.dentry->d_inode; + parent = nd->inode; if ((parent->i_mode & (S_ISVTX|S_IWOTH)) != (S_ISVTX|S_IWOTH)) return 0; @@ -774,9 +887,10 @@ static inline int may_follow_link(struct path *link, struct nameidata *nd) if (uid_eq(parent->i_uid, inode->i_uid)) return 0; - audit_log_link_denied("follow_link", link); - path_put_conditional(link, nd); - path_put(&nd->path); + if (nd->flags & LOOKUP_RCU) + return -ECHILD; + + audit_log_link_denied("follow_link", &nd->stack[0].link); return -EACCES; } @@ -849,82 +963,68 @@ static int may_linkat(struct path *link) return -EPERM; } -static __always_inline int -follow_link(struct path *link, struct nameidata *nd, void **p) +static __always_inline +const char *get_link(struct nameidata *nd) { - struct dentry *dentry = link->dentry; + struct saved *last = nd->stack + nd->depth - 1; + struct dentry *dentry = last->link.dentry; + struct inode *inode = last->inode; int error; - char *s; + const char *res; - BUG_ON(nd->flags & LOOKUP_RCU); - - if (link->mnt == nd->path.mnt) - mntget(link->mnt); - - error = -ELOOP; - if (unlikely(current->total_link_count >= 40)) - goto out_put_nd_path; - - cond_resched(); - current->total_link_count++; - - touch_atime(link); - nd_set_link(nd, NULL); + if (!(nd->flags & LOOKUP_RCU)) { + touch_atime(&last->link); + cond_resched(); + } else if (atime_needs_update(&last->link, inode)) { + if (unlikely(unlazy_walk(nd, NULL, 0))) + return ERR_PTR(-ECHILD); + touch_atime(&last->link); + } - error = security_inode_follow_link(link->dentry, nd); - if (error) - goto out_put_nd_path; + error = security_inode_follow_link(dentry, inode, + nd->flags & LOOKUP_RCU); + if (unlikely(error)) + return ERR_PTR(error); nd->last_type = LAST_BIND; - *p = dentry->d_inode->i_op->follow_link(dentry, nd); - error = PTR_ERR(*p); - if (IS_ERR(*p)) - goto out_put_nd_path; - - error = 0; - s = nd_get_link(nd); - if (s) { - if (unlikely(IS_ERR(s))) { - path_put(&nd->path); - put_link(nd, link, *p); - return PTR_ERR(s); + res = inode->i_link; + if (!res) { + if (nd->flags & LOOKUP_RCU) { + if (unlikely(unlazy_walk(nd, NULL, 0))) + return ERR_PTR(-ECHILD); } - if (*s == '/') { + res = inode->i_op->follow_link(dentry, &last->cookie); + if (IS_ERR_OR_NULL(res)) { + last->cookie = NULL; + return res; + } + } + if (*res == '/') { + if (nd->flags & LOOKUP_RCU) { + struct dentry *d; + if (!nd->root.mnt) + set_root_rcu(nd); + nd->path = nd->root; + d = nd->path.dentry; + nd->inode = d->d_inode; + nd->seq = nd->root_seq; + if (unlikely(read_seqcount_retry(&d->d_seq, nd->seq))) + return ERR_PTR(-ECHILD); + } else { if (!nd->root.mnt) set_root(nd); path_put(&nd->path); nd->path = nd->root; path_get(&nd->root); - nd->flags |= LOOKUP_JUMPED; + nd->inode = nd->path.dentry->d_inode; } - nd->inode = nd->path.dentry->d_inode; - error = link_path_walk(s, nd); - if (unlikely(error)) - put_link(nd, link, *p); + nd->flags |= LOOKUP_JUMPED; + while (unlikely(*++res == '/')) + ; } - - return error; - -out_put_nd_path: - *p = NULL; - path_put(&nd->path); - path_put(link); - return error; -} - -static int follow_up_rcu(struct path *path) -{ - struct mount *mnt = real_mount(path->mnt); - struct mount *parent; - struct dentry *mountpoint; - - parent = mnt->mnt_parent; - if (&parent->mnt == path->mnt) - return 0; - mountpoint = mnt->mnt_mountpoint; - path->dentry = mountpoint; - path->mnt = &parent->mnt; - return 1; + if (!*res) + res = NULL; + return res; } /* @@ -965,7 +1065,7 @@ EXPORT_SYMBOL(follow_up); * - return -EISDIR to tell follow_managed() to stop and return the path we * were called with. */ -static int follow_automount(struct path *path, unsigned flags, +static int follow_automount(struct path *path, struct nameidata *nd, bool *need_mntput) { struct vfsmount *mnt; @@ -985,13 +1085,13 @@ static int follow_automount(struct path *path, unsigned flags, * as being automount points. These will need the attentions * of the daemon to instantiate them before they can be used. */ - if (!(flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY | - LOOKUP_OPEN | LOOKUP_CREATE | LOOKUP_AUTOMOUNT)) && + if (!(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY | + LOOKUP_OPEN | LOOKUP_CREATE | LOOKUP_AUTOMOUNT)) && path->dentry->d_inode) return -EISDIR; - current->total_link_count++; - if (current->total_link_count >= 40) + nd->total_link_count++; + if (nd->total_link_count >= 40) return -ELOOP; mnt = path->dentry->d_op->d_automount(path); @@ -1005,7 +1105,7 @@ static int follow_automount(struct path *path, unsigned flags, * the path being looked up; if it wasn't then the remainder of * the path is inaccessible and we should say so. */ - if (PTR_ERR(mnt) == -EISDIR && (flags & LOOKUP_PARENT)) + if (PTR_ERR(mnt) == -EISDIR && (nd->flags & LOOKUP_PARENT)) return -EREMOTE; return PTR_ERR(mnt); } @@ -1045,7 +1145,7 @@ static int follow_automount(struct path *path, unsigned flags, * * Serialization is taken care of in namespace.c */ -static int follow_managed(struct path *path, unsigned flags) +static int follow_managed(struct path *path, struct nameidata *nd) { struct vfsmount *mnt = path->mnt; /* held by caller, must be left alone */ unsigned managed; @@ -1089,7 +1189,7 @@ static int follow_managed(struct path *path, unsigned flags) /* Handle an automount point */ if (managed & DCACHE_NEED_AUTOMOUNT) { - ret = follow_automount(path, flags, &need_mntput); + ret = follow_automount(path, nd, &need_mntput); if (ret < 0) break; continue; @@ -1103,7 +1203,11 @@ static int follow_managed(struct path *path, unsigned flags) mntput(path->mnt); if (ret == -EISDIR) ret = 0; - return ret < 0 ? ret : need_mntput; + if (need_mntput) + nd->flags |= LOOKUP_JUMPED; + if (unlikely(ret < 0)) + path_put_conditional(path, nd); + return ret; } int follow_down_one(struct path *path) @@ -1133,7 +1237,7 @@ static inline int managed_dentry_rcu(struct dentry *dentry) * we meet a managed dentry that would need blocking. */ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, - struct inode **inode) + struct inode **inode, unsigned *seqp) { for (;;) { struct mount *mounted; @@ -1160,7 +1264,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, path->mnt = &mounted->mnt; path->dentry = mounted->mnt.mnt_root; nd->flags |= LOOKUP_JUMPED; - nd->seq = read_seqcount_begin(&path->dentry->d_seq); + *seqp = read_seqcount_begin(&path->dentry->d_seq); /* * Update the inode too. We don't need to re-check the * dentry sequence number here after this d_inode read, @@ -1179,10 +1283,8 @@ static int follow_dotdot_rcu(struct nameidata *nd) set_root_rcu(nd); while (1) { - if (nd->path.dentry == nd->root.dentry && - nd->path.mnt == nd->root.mnt) { + if (path_equal(&nd->path, &nd->root)) break; - } if (nd->path.dentry != nd->path.mnt->mnt_root) { struct dentry *old = nd->path.dentry; struct dentry *parent = old->d_parent; @@ -1190,38 +1292,42 @@ static int follow_dotdot_rcu(struct nameidata *nd) inode = parent->d_inode; seq = read_seqcount_begin(&parent->d_seq); - if (read_seqcount_retry(&old->d_seq, nd->seq)) - goto failed; + if (unlikely(read_seqcount_retry(&old->d_seq, nd->seq))) + return -ECHILD; nd->path.dentry = parent; nd->seq = seq; break; + } else { + struct mount *mnt = real_mount(nd->path.mnt); + struct mount *mparent = mnt->mnt_parent; + struct dentry *mountpoint = mnt->mnt_mountpoint; + struct inode *inode2 = mountpoint->d_inode; + unsigned seq = read_seqcount_begin(&mountpoint->d_seq); + if (unlikely(read_seqretry(&mount_lock, nd->m_seq))) + return -ECHILD; + if (&mparent->mnt == nd->path.mnt) + break; + /* we know that mountpoint was pinned */ + nd->path.dentry = mountpoint; + nd->path.mnt = &mparent->mnt; + inode = inode2; + nd->seq = seq; } - if (!follow_up_rcu(&nd->path)) - break; - inode = nd->path.dentry->d_inode; - nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq); } - while (d_mountpoint(nd->path.dentry)) { + while (unlikely(d_mountpoint(nd->path.dentry))) { struct mount *mounted; mounted = __lookup_mnt(nd->path.mnt, nd->path.dentry); + if (unlikely(read_seqretry(&mount_lock, nd->m_seq))) + return -ECHILD; if (!mounted) break; nd->path.mnt = &mounted->mnt; nd->path.dentry = mounted->mnt.mnt_root; inode = nd->path.dentry->d_inode; nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq); - if (read_seqretry(&mount_lock, nd->m_seq)) - goto failed; } nd->inode = inode; return 0; - -failed: - nd->flags &= ~LOOKUP_RCU; - if (!(nd->flags & LOOKUP_ROOT)) - nd->root.mnt = NULL; - rcu_read_unlock(); - return -ECHILD; } /* @@ -1400,7 +1506,8 @@ static struct dentry *__lookup_hash(struct qstr *name, * It _is_ time-critical. */ static int lookup_fast(struct nameidata *nd, - struct path *path, struct inode **inode) + struct path *path, struct inode **inode, + unsigned *seqp) { struct vfsmount *mnt = nd->path.mnt; struct dentry *dentry, *parent = nd->path.dentry; @@ -1424,7 +1531,7 @@ static int lookup_fast(struct nameidata *nd, * This sequence count validates that the inode matches * the dentry name information from lookup. */ - *inode = dentry->d_inode; + *inode = d_backing_inode(dentry); negative = d_is_negative(dentry); if (read_seqcount_retry(&dentry->d_seq, seq)) return -ECHILD; @@ -1440,8 +1547,8 @@ static int lookup_fast(struct nameidata *nd, */ if (__read_seqcount_retry(&parent->d_seq, nd->seq)) return -ECHILD; - nd->seq = seq; + *seqp = seq; if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE)) { status = d_revalidate(dentry, nd->flags); if (unlikely(status <= 0)) { @@ -1452,10 +1559,10 @@ static int lookup_fast(struct nameidata *nd, } path->mnt = mnt; path->dentry = dentry; - if (likely(__follow_mount_rcu(nd, path, inode))) + if (likely(__follow_mount_rcu(nd, path, inode, seqp))) return 0; unlazy: - if (unlazy_walk(nd, dentry)) + if (unlazy_walk(nd, dentry, seq)) return -ECHILD; } else { dentry = __d_lookup(parent, &nd->last); @@ -1482,15 +1589,10 @@ unlazy: } path->mnt = mnt; path->dentry = dentry; - err = follow_managed(path, nd->flags); - if (unlikely(err < 0)) { - path_put_conditional(path, nd); - return err; - } - if (err) - nd->flags |= LOOKUP_JUMPED; - *inode = path->dentry->d_inode; - return 0; + err = follow_managed(path, nd); + if (likely(!err)) + *inode = d_backing_inode(path->dentry); + return err; need_lookup: return 1; @@ -1500,7 +1602,6 @@ need_lookup: static int lookup_slow(struct nameidata *nd, struct path *path) { struct dentry *dentry, *parent; - int err; parent = nd->path.dentry; BUG_ON(nd->inode != parent->d_inode); @@ -1512,14 +1613,7 @@ static int lookup_slow(struct nameidata *nd, struct path *path) return PTR_ERR(dentry); path->mnt = nd->path.mnt; path->dentry = dentry; - err = follow_managed(path, nd->flags); - if (unlikely(err < 0)) { - path_put_conditional(path, nd); - return err; - } - if (err) - nd->flags |= LOOKUP_JUMPED; - return 0; + return follow_managed(path, nd); } static inline int may_lookup(struct nameidata *nd) @@ -1528,7 +1622,7 @@ static inline int may_lookup(struct nameidata *nd) int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK); if (err != -ECHILD) return err; - if (unlazy_walk(nd, NULL)) + if (unlazy_walk(nd, NULL, 0)) return -ECHILD; } return inode_permission(nd->inode, MAY_EXEC); @@ -1538,24 +1632,45 @@ static inline int handle_dots(struct nameidata *nd, int type) { if (type == LAST_DOTDOT) { if (nd->flags & LOOKUP_RCU) { - if (follow_dotdot_rcu(nd)) - return -ECHILD; + return follow_dotdot_rcu(nd); } else follow_dotdot(nd); } return 0; } -static void terminate_walk(struct nameidata *nd) +static int pick_link(struct nameidata *nd, struct path *link, + struct inode *inode, unsigned seq) { + int error; + struct saved *last; + if (unlikely(nd->total_link_count++ >= MAXSYMLINKS)) { + path_to_nameidata(link, nd); + return -ELOOP; + } if (!(nd->flags & LOOKUP_RCU)) { - path_put(&nd->path); - } else { - nd->flags &= ~LOOKUP_RCU; - if (!(nd->flags & LOOKUP_ROOT)) - nd->root.mnt = NULL; - rcu_read_unlock(); + if (link->mnt == nd->path.mnt) + mntget(link->mnt); + } + error = nd_alloc_stack(nd); + if (unlikely(error)) { + if (error == -ECHILD) { + if (unlikely(unlazy_link(nd, link, seq))) + return -ECHILD; + error = nd_alloc_stack(nd); + } + if (error) { + path_put(link); + return error; + } } + + last = nd->stack + nd->depth++; + last->link = *link; + last->cookie = NULL; + last->inode = inode; + last->seq = seq; + return 1; } /* @@ -1564,98 +1679,68 @@ static void terminate_walk(struct nameidata *nd) * so we keep a cache of "no, this doesn't need follow_link" * for the common case. */ -static inline int should_follow_link(struct dentry *dentry, int follow) +static inline int should_follow_link(struct nameidata *nd, struct path *link, + int follow, + struct inode *inode, unsigned seq) { - return unlikely(d_is_symlink(dentry)) ? follow : 0; + if (likely(!d_is_symlink(link->dentry))) + return 0; + if (!follow) + return 0; + return pick_link(nd, link, inode, seq); } -static inline int walk_component(struct nameidata *nd, struct path *path, - int follow) +enum {WALK_GET = 1, WALK_PUT = 2}; + +static int walk_component(struct nameidata *nd, int flags) { + struct path path; struct inode *inode; + unsigned seq; int err; /* * "." and ".." are special - ".." especially so because it has * to be able to know about the current root directory and * parent relationships. */ - if (unlikely(nd->last_type != LAST_NORM)) - return handle_dots(nd, nd->last_type); - err = lookup_fast(nd, path, &inode); + if (unlikely(nd->last_type != LAST_NORM)) { + err = handle_dots(nd, nd->last_type); + if (flags & WALK_PUT) + put_link(nd); + return err; + } + err = lookup_fast(nd, &path, &inode, &seq); if (unlikely(err)) { if (err < 0) - goto out_err; + return err; - err = lookup_slow(nd, path); + err = lookup_slow(nd, &path); if (err < 0) - goto out_err; + return err; - inode = path->dentry->d_inode; + inode = d_backing_inode(path.dentry); + seq = 0; /* we are already out of RCU mode */ err = -ENOENT; - if (d_is_negative(path->dentry)) + if (d_is_negative(path.dentry)) goto out_path_put; } - if (should_follow_link(path->dentry, follow)) { - if (nd->flags & LOOKUP_RCU) { - if (unlikely(nd->path.mnt != path->mnt || - unlazy_walk(nd, path->dentry))) { - err = -ECHILD; - goto out_err; - } - } - BUG_ON(inode != path->dentry->d_inode); - return 1; - } - path_to_nameidata(path, nd); + if (flags & WALK_PUT) + put_link(nd); + err = should_follow_link(nd, &path, flags & WALK_GET, inode, seq); + if (unlikely(err)) + return err; + path_to_nameidata(&path, nd); nd->inode = inode; + nd->seq = seq; return 0; out_path_put: - path_to_nameidata(path, nd); -out_err: - terminate_walk(nd); + path_to_nameidata(&path, nd); return err; } /* - * This limits recursive symlink follows to 8, while - * limiting consecutive symlinks to 40. - * - * Without that kind of total limit, nasty chains of consecutive - * symlinks can cause almost arbitrarily long lookups. - */ -static inline int nested_symlink(struct path *path, struct nameidata *nd) -{ - int res; - - if (unlikely(current->link_count >= MAX_NESTED_LINKS)) { - path_put_conditional(path, nd); - path_put(&nd->path); - return -ELOOP; - } - BUG_ON(nd->depth >= MAX_NESTED_LINKS); - - nd->depth++; - current->link_count++; - - do { - struct path link = *path; - void *cookie; - - res = follow_link(&link, nd, &cookie); - if (res) - break; - res = walk_component(nd, path, LOOKUP_FOLLOW); - put_link(nd, &link, cookie); - } while (res > 0); - - current->link_count--; - nd->depth--; - return res; -} - -/* * We can do the critical dentry name comparison and hashing * operations one word at a time, but we are limited to: * @@ -1781,9 +1866,8 @@ static inline u64 hash_name(const char *name) */ static int link_path_walk(const char *name, struct nameidata *nd) { - struct path next; int err; - + while (*name=='/') name++; if (!*name) @@ -1796,7 +1880,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) err = may_lookup(nd); if (err) - break; + return err; hash_len = hash_name(name); @@ -1818,7 +1902,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) struct qstr this = { { .hash_len = hash_len }, .name = name }; err = parent->d_op->d_hash(parent, &this); if (err < 0) - break; + return err; hash_len = this.hash_len; name = this.name; } @@ -1830,7 +1914,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) name += hashlen_len(hash_len); if (!*name) - return 0; + goto OK; /* * If it wasn't NUL, we know it was '/'. Skip that * slash, and continue until no more slashes. @@ -1838,57 +1922,78 @@ static int link_path_walk(const char *name, struct nameidata *nd) do { name++; } while (unlikely(*name == '/')); - if (!*name) - return 0; - - err = walk_component(nd, &next, LOOKUP_FOLLOW); + if (unlikely(!*name)) { +OK: + /* pathname body, done */ + if (!nd->depth) + return 0; + name = nd->stack[nd->depth - 1].name; + /* trailing symlink, done */ + if (!name) + return 0; + /* last component of nested symlink */ + err = walk_component(nd, WALK_GET | WALK_PUT); + } else { + err = walk_component(nd, WALK_GET); + } if (err < 0) return err; if (err) { - err = nested_symlink(&next, nd); - if (err) - return err; + const char *s = get_link(nd); + + if (unlikely(IS_ERR(s))) + return PTR_ERR(s); + err = 0; + if (unlikely(!s)) { + /* jumped */ + put_link(nd); + } else { + nd->stack[nd->depth - 1].name = name; + name = s; + continue; + } } - if (!d_can_lookup(nd->path.dentry)) { - err = -ENOTDIR; - break; + if (unlikely(!d_can_lookup(nd->path.dentry))) { + if (nd->flags & LOOKUP_RCU) { + if (unlazy_walk(nd, NULL, 0)) + return -ECHILD; + } + return -ENOTDIR; } } - terminate_walk(nd); - return err; } -static int path_init(int dfd, const struct filename *name, unsigned int flags, - struct nameidata *nd) +static const char *path_init(struct nameidata *nd, unsigned flags) { int retval = 0; - const char *s = name->name; + const char *s = nd->name->name; nd->last_type = LAST_ROOT; /* if there are only slashes... */ nd->flags = flags | LOOKUP_JUMPED | LOOKUP_PARENT; nd->depth = 0; - nd->base = NULL; + nd->total_link_count = 0; if (flags & LOOKUP_ROOT) { struct dentry *root = nd->root.dentry; struct inode *inode = root->d_inode; if (*s) { if (!d_can_lookup(root)) - return -ENOTDIR; + return ERR_PTR(-ENOTDIR); retval = inode_permission(inode, MAY_EXEC); if (retval) - return retval; + return ERR_PTR(retval); } nd->path = nd->root; nd->inode = inode; if (flags & LOOKUP_RCU) { rcu_read_lock(); nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq); + nd->root_seq = nd->seq; nd->m_seq = read_seqbegin(&mount_lock); } else { path_get(&nd->path); } - goto done; + return s; } nd->root.mnt = NULL; @@ -1897,13 +2002,14 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags, if (*s == '/') { if (flags & LOOKUP_RCU) { rcu_read_lock(); - nd->seq = set_root_rcu(nd); + set_root_rcu(nd); + nd->seq = nd->root_seq; } else { set_root(nd); path_get(&nd->root); } nd->path = nd->root; - } else if (dfd == AT_FDCWD) { + } else if (nd->dfd == AT_FDCWD) { if (flags & LOOKUP_RCU) { struct fs_struct *fs = current->fs; unsigned seq; @@ -1920,180 +2026,205 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags, } } else { /* Caller must check execute permissions on the starting path component */ - struct fd f = fdget_raw(dfd); + struct fd f = fdget_raw(nd->dfd); struct dentry *dentry; if (!f.file) - return -EBADF; + return ERR_PTR(-EBADF); dentry = f.file->f_path.dentry; if (*s) { if (!d_can_lookup(dentry)) { fdput(f); - return -ENOTDIR; + return ERR_PTR(-ENOTDIR); } } nd->path = f.file->f_path; if (flags & LOOKUP_RCU) { - if (f.flags & FDPUT_FPUT) - nd->base = f.file; - nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq); rcu_read_lock(); + nd->inode = nd->path.dentry->d_inode; + nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq); } else { path_get(&nd->path); - fdput(f); + nd->inode = nd->path.dentry->d_inode; } + fdput(f); + return s; } nd->inode = nd->path.dentry->d_inode; if (!(flags & LOOKUP_RCU)) - goto done; + return s; if (likely(!read_seqcount_retry(&nd->path.dentry->d_seq, nd->seq))) - goto done; + return s; if (!(nd->flags & LOOKUP_ROOT)) nd->root.mnt = NULL; rcu_read_unlock(); - return -ECHILD; -done: - current->total_link_count = 0; - return link_path_walk(s, nd); + return ERR_PTR(-ECHILD); } -static void path_cleanup(struct nameidata *nd) +static const char *trailing_symlink(struct nameidata *nd) { - if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) { - path_put(&nd->root); - nd->root.mnt = NULL; - } - if (unlikely(nd->base)) - fput(nd->base); + const char *s; + int error = may_follow_link(nd); + if (unlikely(error)) + return ERR_PTR(error); + nd->flags |= LOOKUP_PARENT; + nd->stack[0].name = NULL; + s = get_link(nd); + return s ? s : ""; } -static inline int lookup_last(struct nameidata *nd, struct path *path) +static inline int lookup_last(struct nameidata *nd) { if (nd->last_type == LAST_NORM && nd->last.name[nd->last.len]) nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; nd->flags &= ~LOOKUP_PARENT; - return walk_component(nd, path, nd->flags & LOOKUP_FOLLOW); + return walk_component(nd, + nd->flags & LOOKUP_FOLLOW + ? nd->depth + ? WALK_PUT | WALK_GET + : WALK_GET + : 0); } /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ -static int path_lookupat(int dfd, const struct filename *name, - unsigned int flags, struct nameidata *nd) +static int path_lookupat(struct nameidata *nd, unsigned flags, struct path *path) { - struct path path; + const char *s = path_init(nd, flags); int err; - /* - * Path walking is largely split up into 2 different synchronisation - * schemes, rcu-walk and ref-walk (explained in - * Documentation/filesystems/path-lookup.txt). These share much of the - * path walk code, but some things particularly setup, cleanup, and - * following mounts are sufficiently divergent that functions are - * duplicated. Typically there is a function foo(), and its RCU - * analogue, foo_rcu(). - * - * -ECHILD is the error number of choice (just to avoid clashes) that - * is returned if some aspect of an rcu-walk fails. Such an error must - * be handled by restarting a traditional ref-walk (which will always - * be able to complete). - */ - err = path_init(dfd, name, flags, nd); - if (!err && !(flags & LOOKUP_PARENT)) { - err = lookup_last(nd, &path); - while (err > 0) { - void *cookie; - struct path link = path; - err = may_follow_link(&link, nd); - if (unlikely(err)) - break; - nd->flags |= LOOKUP_PARENT; - err = follow_link(&link, nd, &cookie); - if (err) - break; - err = lookup_last(nd, &path); - put_link(nd, &link, cookie); + if (IS_ERR(s)) + return PTR_ERR(s); + while (!(err = link_path_walk(s, nd)) + && ((err = lookup_last(nd)) > 0)) { + s = trailing_symlink(nd); + if (IS_ERR(s)) { + err = PTR_ERR(s); + break; } } - if (!err) err = complete_walk(nd); - if (!err && nd->flags & LOOKUP_DIRECTORY) { - if (!d_can_lookup(nd->path.dentry)) { - path_put(&nd->path); + if (!err && nd->flags & LOOKUP_DIRECTORY) + if (!d_can_lookup(nd->path.dentry)) err = -ENOTDIR; - } + if (!err) { + *path = nd->path; + nd->path.mnt = NULL; + nd->path.dentry = NULL; } - - path_cleanup(nd); + terminate_walk(nd); return err; } -static int filename_lookup(int dfd, struct filename *name, - unsigned int flags, struct nameidata *nd) +static int filename_lookup(int dfd, struct filename *name, unsigned flags, + struct path *path, struct path *root) { - int retval = path_lookupat(dfd, name, flags | LOOKUP_RCU, nd); + int retval; + struct nameidata nd; + if (IS_ERR(name)) + return PTR_ERR(name); + if (unlikely(root)) { + nd.root = *root; + flags |= LOOKUP_ROOT; + } + set_nameidata(&nd, dfd, name); + retval = path_lookupat(&nd, flags | LOOKUP_RCU, path); if (unlikely(retval == -ECHILD)) - retval = path_lookupat(dfd, name, flags, nd); + retval = path_lookupat(&nd, flags, path); if (unlikely(retval == -ESTALE)) - retval = path_lookupat(dfd, name, flags | LOOKUP_REVAL, nd); + retval = path_lookupat(&nd, flags | LOOKUP_REVAL, path); if (likely(!retval)) - audit_inode(name, nd->path.dentry, flags & LOOKUP_PARENT); + audit_inode(name, path->dentry, flags & LOOKUP_PARENT); + restore_nameidata(); + putname(name); return retval; } +/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ +static int path_parentat(struct nameidata *nd, unsigned flags, + struct path *parent) +{ + const char *s = path_init(nd, flags); + int err; + if (IS_ERR(s)) + return PTR_ERR(s); + err = link_path_walk(s, nd); + if (!err) + err = complete_walk(nd); + if (!err) { + *parent = nd->path; + nd->path.mnt = NULL; + nd->path.dentry = NULL; + } + terminate_walk(nd); + return err; +} + +static struct filename *filename_parentat(int dfd, struct filename *name, + unsigned int flags, struct path *parent, + struct qstr *last, int *type) +{ + int retval; + struct nameidata nd; + + if (IS_ERR(name)) + return name; + set_nameidata(&nd, dfd, name); + retval = path_parentat(&nd, flags | LOOKUP_RCU, parent); + if (unlikely(retval == -ECHILD)) + retval = path_parentat(&nd, flags, parent); + if (unlikely(retval == -ESTALE)) + retval = path_parentat(&nd, flags | LOOKUP_REVAL, parent); + if (likely(!retval)) { + *last = nd.last; + *type = nd.last_type; + audit_inode(name, parent->dentry, LOOKUP_PARENT); + } else { + putname(name); + name = ERR_PTR(retval); + } + restore_nameidata(); + return name; +} + /* does lookup, returns the object with parent locked */ struct dentry *kern_path_locked(const char *name, struct path *path) { - struct filename *filename = getname_kernel(name); - struct nameidata nd; + struct filename *filename; struct dentry *d; - int err; + struct qstr last; + int type; + filename = filename_parentat(AT_FDCWD, getname_kernel(name), 0, path, + &last, &type); if (IS_ERR(filename)) return ERR_CAST(filename); - - err = filename_lookup(AT_FDCWD, filename, LOOKUP_PARENT, &nd); - if (err) { - d = ERR_PTR(err); - goto out; - } - if (nd.last_type != LAST_NORM) { - path_put(&nd.path); - d = ERR_PTR(-EINVAL); - goto out; + if (unlikely(type != LAST_NORM)) { + path_put(path); + putname(filename); + return ERR_PTR(-EINVAL); } - mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); - d = __lookup_hash(&nd.last, nd.path.dentry, 0); + mutex_lock_nested(&path->dentry->d_inode->i_mutex, I_MUTEX_PARENT); + d = __lookup_hash(&last, path->dentry, 0); if (IS_ERR(d)) { - mutex_unlock(&nd.path.dentry->d_inode->i_mutex); - path_put(&nd.path); - goto out; + mutex_unlock(&path->dentry->d_inode->i_mutex); + path_put(path); } - *path = nd.path; -out: putname(filename); return d; } int kern_path(const char *name, unsigned int flags, struct path *path) { - struct nameidata nd; - struct filename *filename = getname_kernel(name); - int res = PTR_ERR(filename); - - if (!IS_ERR(filename)) { - res = filename_lookup(AT_FDCWD, filename, flags, &nd); - putname(filename); - if (!res) - *path = nd.path; - } - return res; + return filename_lookup(AT_FDCWD, getname_kernel(name), + flags, path, NULL); } EXPORT_SYMBOL(kern_path); @@ -2109,36 +2240,13 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, const char *name, unsigned int flags, struct path *path) { - struct filename *filename = getname_kernel(name); - int err = PTR_ERR(filename); - - BUG_ON(flags & LOOKUP_PARENT); - - /* the first argument of filename_lookup() is ignored with LOOKUP_ROOT */ - if (!IS_ERR(filename)) { - struct nameidata nd; - nd.root.dentry = dentry; - nd.root.mnt = mnt; - err = filename_lookup(AT_FDCWD, filename, - flags | LOOKUP_ROOT, &nd); - if (!err) - *path = nd.path; - putname(filename); - } - return err; + struct path root = {.mnt = mnt, .dentry = dentry}; + /* the first argument of filename_lookup() is ignored with root */ + return filename_lookup(AT_FDCWD, getname_kernel(name), + flags , path, &root); } EXPORT_SYMBOL(vfs_path_lookup); -/* - * Restricted form of lookup. Doesn't follow links, single-component only, - * needs parent already locked. Doesn't follow mounts. - * SMP-safe. - */ -static struct dentry *lookup_hash(struct nameidata *nd) -{ - return __lookup_hash(&nd->last, nd->path.dentry, nd->flags); -} - /** * lookup_one_len - filesystem helper to lookup single pathname component * @name: pathname component to lookup @@ -2193,27 +2301,10 @@ EXPORT_SYMBOL(lookup_one_len); int user_path_at_empty(int dfd, const char __user *name, unsigned flags, struct path *path, int *empty) { - struct nameidata nd; - struct filename *tmp = getname_flags(name, flags, empty); - int err = PTR_ERR(tmp); - if (!IS_ERR(tmp)) { - - BUG_ON(flags & LOOKUP_PARENT); - - err = filename_lookup(dfd, tmp, flags, &nd); - putname(tmp); - if (!err) - *path = nd.path; - } - return err; -} - -int user_path_at(int dfd, const char __user *name, unsigned flags, - struct path *path) -{ - return user_path_at_empty(dfd, name, flags, path, NULL); + return filename_lookup(dfd, getname_flags(name, flags, empty), + flags, path, NULL); } -EXPORT_SYMBOL(user_path_at); +EXPORT_SYMBOL(user_path_at_empty); /* * NB: most callers don't do anything directly with the reference to the @@ -2221,26 +2312,16 @@ EXPORT_SYMBOL(user_path_at); * allocated by getname. So we must hold the reference to it until all * path-walking is complete. */ -static struct filename * -user_path_parent(int dfd, const char __user *path, struct nameidata *nd, +static inline struct filename * +user_path_parent(int dfd, const char __user *path, + struct path *parent, + struct qstr *last, + int *type, unsigned int flags) { - struct filename *s = getname(path); - int error; - /* only LOOKUP_REVAL is allowed in extra flags */ - flags &= LOOKUP_REVAL; - - if (IS_ERR(s)) - return s; - - error = filename_lookup(dfd, s, flags | LOOKUP_PARENT, nd); - if (error) { - putname(s); - return ERR_PTR(error); - } - - return s; + return filename_parentat(dfd, getname(path), flags & LOOKUP_REVAL, + parent, last, type); } /** @@ -2279,10 +2360,8 @@ mountpoint_last(struct nameidata *nd, struct path *path) /* If we're in rcuwalk, drop out of it to handle last component */ if (nd->flags & LOOKUP_RCU) { - if (unlazy_walk(nd, NULL)) { - error = -ECHILD; - goto out; - } + if (unlazy_walk(nd, NULL, 0)) + return -ECHILD; } nd->flags &= ~LOOKUP_PARENT; @@ -2290,7 +2369,7 @@ mountpoint_last(struct nameidata *nd, struct path *path) if (unlikely(nd->last_type != LAST_NORM)) { error = handle_dots(nd, nd->last_type); if (error) - goto out; + return error; dentry = dget(nd->path.dentry); goto done; } @@ -2305,74 +2384,60 @@ mountpoint_last(struct nameidata *nd, struct path *path) */ dentry = d_alloc(dir, &nd->last); if (!dentry) { - error = -ENOMEM; mutex_unlock(&dir->d_inode->i_mutex); - goto out; + return -ENOMEM; } dentry = lookup_real(dir->d_inode, dentry, nd->flags); - error = PTR_ERR(dentry); if (IS_ERR(dentry)) { mutex_unlock(&dir->d_inode->i_mutex); - goto out; + return PTR_ERR(dentry); } } mutex_unlock(&dir->d_inode->i_mutex); done: if (d_is_negative(dentry)) { - error = -ENOENT; dput(dentry); - goto out; + return -ENOENT; } + if (nd->depth) + put_link(nd); path->dentry = dentry; path->mnt = nd->path.mnt; - if (should_follow_link(dentry, nd->flags & LOOKUP_FOLLOW)) - return 1; + error = should_follow_link(nd, path, nd->flags & LOOKUP_FOLLOW, + d_backing_inode(dentry), 0); + if (unlikely(error)) + return error; mntget(path->mnt); follow_mount(path); - error = 0; -out: - terminate_walk(nd); - return error; + return 0; } /** * path_mountpoint - look up a path to be umounted - * @dfd: directory file descriptor to start walk from - * @name: full pathname to walk - * @path: pointer to container for result + * @nameidata: lookup context * @flags: lookup flags + * @path: pointer to container for result * * Look up the given name, but don't attempt to revalidate the last component. * Returns 0 and "path" will be valid on success; Returns error otherwise. */ static int -path_mountpoint(int dfd, const struct filename *name, struct path *path, - unsigned int flags) +path_mountpoint(struct nameidata *nd, unsigned flags, struct path *path) { - struct nameidata nd; + const char *s = path_init(nd, flags); int err; - - err = path_init(dfd, name, flags, &nd); - if (unlikely(err)) - goto out; - - err = mountpoint_last(&nd, path); - while (err > 0) { - void *cookie; - struct path link = *path; - err = may_follow_link(&link, &nd); - if (unlikely(err)) - break; - nd.flags |= LOOKUP_PARENT; - err = follow_link(&link, &nd, &cookie); - if (err) + if (IS_ERR(s)) + return PTR_ERR(s); + while (!(err = link_path_walk(s, nd)) && + (err = mountpoint_last(nd, path)) > 0) { + s = trailing_symlink(nd); + if (IS_ERR(s)) { + err = PTR_ERR(s); break; - err = mountpoint_last(&nd, path); - put_link(&nd, &link, cookie); + } } -out: - path_cleanup(&nd); + terminate_walk(nd); return err; } @@ -2380,16 +2445,19 @@ static int filename_mountpoint(int dfd, struct filename *name, struct path *path, unsigned int flags) { + struct nameidata nd; int error; if (IS_ERR(name)) return PTR_ERR(name); - error = path_mountpoint(dfd, name, path, flags | LOOKUP_RCU); + set_nameidata(&nd, dfd, name); + error = path_mountpoint(&nd, flags | LOOKUP_RCU, path); if (unlikely(error == -ECHILD)) - error = path_mountpoint(dfd, name, path, flags); + error = path_mountpoint(&nd, flags, path); if (unlikely(error == -ESTALE)) - error = path_mountpoint(dfd, name, path, flags | LOOKUP_REVAL); + error = path_mountpoint(&nd, flags | LOOKUP_REVAL, path); if (likely(!error)) audit_inode(name, path->dentry, 0); + restore_nameidata(); putname(name); return error; } @@ -2456,7 +2524,7 @@ EXPORT_SYMBOL(__check_sticky); */ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir) { - struct inode *inode = victim->d_inode; + struct inode *inode = d_backing_inode(victim); int error; if (d_is_negative(victim)) @@ -2922,18 +2990,19 @@ out_dput: /* * Handle the last step of open() */ -static int do_last(struct nameidata *nd, struct path *path, +static int do_last(struct nameidata *nd, struct file *file, const struct open_flags *op, - int *opened, struct filename *name) + int *opened) { struct dentry *dir = nd->path.dentry; int open_flag = op->open_flag; bool will_truncate = (open_flag & O_TRUNC) != 0; bool got_write = false; int acc_mode = op->acc_mode; + unsigned seq; struct inode *inode; - bool symlink_ok = false; struct path save_parent = { .dentry = NULL, .mnt = NULL }; + struct path path; bool retried = false; int error; @@ -2942,7 +3011,7 @@ static int do_last(struct nameidata *nd, struct path *path, if (nd->last_type != LAST_NORM) { error = handle_dots(nd, nd->last_type); - if (error) + if (unlikely(error)) return error; goto finish_open; } @@ -2950,15 +3019,13 @@ static int do_last(struct nameidata *nd, struct path *path, if (!(open_flag & O_CREAT)) { if (nd->last.name[nd->last.len]) nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; - if (open_flag & O_PATH && !(nd->flags & LOOKUP_FOLLOW)) - symlink_ok = true; /* we _can_ be in RCU mode here */ - error = lookup_fast(nd, path, &inode); + error = lookup_fast(nd, &path, &inode, &seq); if (likely(!error)) goto finish_lookup; if (error < 0) - goto out; + return error; BUG_ON(nd->inode != dir->d_inode); } else { @@ -2972,11 +3039,10 @@ static int do_last(struct nameidata *nd, struct path *path, if (error) return error; - audit_inode(name, dir, LOOKUP_PARENT); - error = -EISDIR; + audit_inode(nd->name, dir, LOOKUP_PARENT); /* trailing slashes? */ - if (nd->last.name[nd->last.len]) - goto out; + if (unlikely(nd->last.name[nd->last.len])) + return -EISDIR; } retry_lookup: @@ -2991,7 +3057,7 @@ retry_lookup: */ } mutex_lock(&dir->d_inode->i_mutex); - error = lookup_open(nd, path, file, op, got_write, opened); + error = lookup_open(nd, &path, file, op, got_write, opened); mutex_unlock(&dir->d_inode->i_mutex); if (error <= 0) { @@ -3002,7 +3068,7 @@ retry_lookup: !S_ISREG(file_inode(file)->i_mode)) will_truncate = false; - audit_inode(name, file->f_path.dentry, 0); + audit_inode(nd->name, file->f_path.dentry, 0); goto opened; } @@ -3011,15 +3077,15 @@ retry_lookup: open_flag &= ~O_TRUNC; will_truncate = false; acc_mode = MAY_OPEN; - path_to_nameidata(path, nd); + path_to_nameidata(&path, nd); goto finish_open_created; } /* * create/update audit record if it already exists. */ - if (d_is_positive(path->dentry)) - audit_inode(name, path->dentry, 0); + if (d_is_positive(path.dentry)) + audit_inode(nd->name, path.dentry, 0); /* * If atomic_open() acquired write access it is dropped now due to @@ -3031,47 +3097,45 @@ retry_lookup: got_write = false; } - error = -EEXIST; - if ((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT)) - goto exit_dput; - - error = follow_managed(path, nd->flags); - if (error < 0) - goto exit_dput; + if (unlikely((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT))) { + path_to_nameidata(&path, nd); + return -EEXIST; + } - if (error) - nd->flags |= LOOKUP_JUMPED; + error = follow_managed(&path, nd); + if (unlikely(error < 0)) + return error; BUG_ON(nd->flags & LOOKUP_RCU); - inode = path->dentry->d_inode; - error = -ENOENT; - if (d_is_negative(path->dentry)) { - path_to_nameidata(path, nd); - goto out; + inode = d_backing_inode(path.dentry); + seq = 0; /* out of RCU mode, so the value doesn't matter */ + if (unlikely(d_is_negative(path.dentry))) { + path_to_nameidata(&path, nd); + return -ENOENT; } finish_lookup: - /* we _can_ be in RCU mode here */ - if (should_follow_link(path->dentry, !symlink_ok)) { - if (nd->flags & LOOKUP_RCU) { - if (unlikely(nd->path.mnt != path->mnt || - unlazy_walk(nd, path->dentry))) { - error = -ECHILD; - goto out; - } - } - BUG_ON(inode != path->dentry->d_inode); - return 1; + if (nd->depth) + put_link(nd); + error = should_follow_link(nd, &path, nd->flags & LOOKUP_FOLLOW, + inode, seq); + if (unlikely(error)) + return error; + + if (unlikely(d_is_symlink(path.dentry)) && !(open_flag & O_PATH)) { + path_to_nameidata(&path, nd); + return -ELOOP; } - if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path->mnt) { - path_to_nameidata(path, nd); + if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path.mnt) { + path_to_nameidata(&path, nd); } else { save_parent.dentry = nd->path.dentry; - save_parent.mnt = mntget(path->mnt); - nd->path.dentry = path->dentry; + save_parent.mnt = mntget(path.mnt); + nd->path.dentry = path.dentry; } nd->inode = inode; + nd->seq = seq; /* Why this, you ask? _Now_ we might have grown LOOKUP_JUMPED... */ finish_open: error = complete_walk(nd); @@ -3079,7 +3143,7 @@ finish_open: path_put(&save_parent); return error; } - audit_inode(name, nd->path.dentry, 0); + audit_inode(nd->name, nd->path.dentry, 0); error = -EISDIR; if ((open_flag & O_CREAT) && d_is_dir(nd->path.dentry)) goto out; @@ -3126,12 +3190,8 @@ out: if (got_write) mnt_drop_write(nd->path.mnt); path_put(&save_parent); - terminate_walk(nd); return error; -exit_dput: - path_put_conditional(path, nd); - goto out; exit_fput: fput(file); goto out; @@ -3155,50 +3215,46 @@ stale_open: goto retry_lookup; } -static int do_tmpfile(int dfd, struct filename *pathname, - struct nameidata *nd, int flags, +static int do_tmpfile(struct nameidata *nd, unsigned flags, const struct open_flags *op, struct file *file, int *opened) { static const struct qstr name = QSTR_INIT("/", 1); - struct dentry *dentry, *child; + struct dentry *child; struct inode *dir; - int error = path_lookupat(dfd, pathname, - flags | LOOKUP_DIRECTORY, nd); + struct path path; + int error = path_lookupat(nd, flags | LOOKUP_DIRECTORY, &path); if (unlikely(error)) return error; - error = mnt_want_write(nd->path.mnt); + error = mnt_want_write(path.mnt); if (unlikely(error)) goto out; + dir = path.dentry->d_inode; /* we want directory to be writable */ - error = inode_permission(nd->inode, MAY_WRITE | MAY_EXEC); + error = inode_permission(dir, MAY_WRITE | MAY_EXEC); if (error) goto out2; - dentry = nd->path.dentry; - dir = dentry->d_inode; if (!dir->i_op->tmpfile) { error = -EOPNOTSUPP; goto out2; } - child = d_alloc(dentry, &name); + child = d_alloc(path.dentry, &name); if (unlikely(!child)) { error = -ENOMEM; goto out2; } - nd->flags &= ~LOOKUP_DIRECTORY; - nd->flags |= op->intent; - dput(nd->path.dentry); - nd->path.dentry = child; - error = dir->i_op->tmpfile(dir, nd->path.dentry, op->mode); + dput(path.dentry); + path.dentry = child; + error = dir->i_op->tmpfile(dir, child, op->mode); if (error) goto out2; - audit_inode(pathname, nd->path.dentry, 0); + audit_inode(nd->name, child, 0); /* Don't check for other permissions, the inode was just created */ - error = may_open(&nd->path, MAY_OPEN, op->open_flag); + error = may_open(&path, MAY_OPEN, op->open_flag); if (error) goto out2; - file->f_path.mnt = nd->path.mnt; - error = finish_open(file, nd->path.dentry, NULL, opened); + file->f_path.mnt = path.mnt; + error = finish_open(file, child, NULL, opened); if (error) goto out2; error = open_check_o_direct(file); @@ -3211,17 +3267,17 @@ static int do_tmpfile(int dfd, struct filename *pathname, spin_unlock(&inode->i_lock); } out2: - mnt_drop_write(nd->path.mnt); + mnt_drop_write(path.mnt); out: - path_put(&nd->path); + path_put(&path); return error; } -static struct file *path_openat(int dfd, struct filename *pathname, - struct nameidata *nd, const struct open_flags *op, int flags) +static struct file *path_openat(struct nameidata *nd, + const struct open_flags *op, unsigned flags) { + const char *s; struct file *file; - struct path path; int opened = 0; int error; @@ -3232,37 +3288,25 @@ static struct file *path_openat(int dfd, struct filename *pathname, file->f_flags = op->open_flag; if (unlikely(file->f_flags & __O_TMPFILE)) { - error = do_tmpfile(dfd, pathname, nd, flags, op, file, &opened); + error = do_tmpfile(nd, flags, op, file, &opened); goto out2; } - error = path_init(dfd, pathname, flags, nd); - if (unlikely(error)) - goto out; - - error = do_last(nd, &path, file, op, &opened, pathname); - while (unlikely(error > 0)) { /* trailing symlink */ - struct path link = path; - void *cookie; - if (!(nd->flags & LOOKUP_FOLLOW)) { - path_put_conditional(&path, nd); - path_put(&nd->path); - error = -ELOOP; - break; - } - error = may_follow_link(&link, nd); - if (unlikely(error)) - break; - nd->flags |= LOOKUP_PARENT; + s = path_init(nd, flags); + if (IS_ERR(s)) { + put_filp(file); + return ERR_CAST(s); + } + while (!(error = link_path_walk(s, nd)) && + (error = do_last(nd, file, op, &opened)) > 0) { nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL); - error = follow_link(&link, nd, &cookie); - if (unlikely(error)) + s = trailing_symlink(nd); + if (IS_ERR(s)) { + error = PTR_ERR(s); break; - error = do_last(nd, &path, file, op, &opened, pathname); - put_link(nd, &link, cookie); + } } -out: - path_cleanup(nd); + terminate_walk(nd); out2: if (!(opened & FILE_OPENED)) { BUG_ON(!error); @@ -3287,11 +3331,13 @@ struct file *do_filp_open(int dfd, struct filename *pathname, int flags = op->lookup_flags; struct file *filp; - filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU); + set_nameidata(&nd, dfd, pathname); + filp = path_openat(&nd, op, flags | LOOKUP_RCU); if (unlikely(filp == ERR_PTR(-ECHILD))) - filp = path_openat(dfd, pathname, &nd, op, flags); + filp = path_openat(&nd, op, flags); if (unlikely(filp == ERR_PTR(-ESTALE))) - filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_REVAL); + filp = path_openat(&nd, op, flags | LOOKUP_REVAL); + restore_nameidata(); return filp; } @@ -3313,11 +3359,13 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt, if (unlikely(IS_ERR(filename))) return ERR_CAST(filename); - file = path_openat(-1, filename, &nd, op, flags | LOOKUP_RCU); + set_nameidata(&nd, -1, filename); + file = path_openat(&nd, op, flags | LOOKUP_RCU); if (unlikely(file == ERR_PTR(-ECHILD))) - file = path_openat(-1, filename, &nd, op, flags); + file = path_openat(&nd, op, flags); if (unlikely(file == ERR_PTR(-ESTALE))) - file = path_openat(-1, filename, &nd, op, flags | LOOKUP_REVAL); + file = path_openat(&nd, op, flags | LOOKUP_REVAL); + restore_nameidata(); putname(filename); return file; } @@ -3326,7 +3374,8 @@ static struct dentry *filename_create(int dfd, struct filename *name, struct path *path, unsigned int lookup_flags) { struct dentry *dentry = ERR_PTR(-EEXIST); - struct nameidata nd; + struct qstr last; + int type; int err2; int error; bool is_dir = (lookup_flags & LOOKUP_DIRECTORY); @@ -3337,26 +3386,25 @@ static struct dentry *filename_create(int dfd, struct filename *name, */ lookup_flags &= LOOKUP_REVAL; - error = filename_lookup(dfd, name, LOOKUP_PARENT|lookup_flags, &nd); - if (error) - return ERR_PTR(error); + name = filename_parentat(dfd, name, lookup_flags, path, &last, &type); + if (IS_ERR(name)) + return ERR_CAST(name); /* * Yucky last component or no last component at all? * (foo/., foo/.., /////) */ - if (nd.last_type != LAST_NORM) + if (unlikely(type != LAST_NORM)) goto out; - nd.flags &= ~LOOKUP_PARENT; - nd.flags |= LOOKUP_CREATE | LOOKUP_EXCL; /* don't fail immediately if it's r/o, at least try to report other errors */ - err2 = mnt_want_write(nd.path.mnt); + err2 = mnt_want_write(path->mnt); /* * Do the final lookup. */ - mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); - dentry = lookup_hash(&nd); + lookup_flags |= LOOKUP_CREATE | LOOKUP_EXCL; + mutex_lock_nested(&path->dentry->d_inode->i_mutex, I_MUTEX_PARENT); + dentry = __lookup_hash(&last, path->dentry, lookup_flags); if (IS_ERR(dentry)) goto unlock; @@ -3370,7 +3418,7 @@ static struct dentry *filename_create(int dfd, struct filename *name, * all is fine. Let's be bastards - you had / on the end, you've * been asking for (non-existent) directory. -ENOENT for you. */ - if (unlikely(!is_dir && nd.last.name[nd.last.len])) { + if (unlikely(!is_dir && last.name[last.len])) { error = -ENOENT; goto fail; } @@ -3378,31 +3426,26 @@ static struct dentry *filename_create(int dfd, struct filename *name, error = err2; goto fail; } - *path = nd.path; + putname(name); return dentry; fail: dput(dentry); dentry = ERR_PTR(error); unlock: - mutex_unlock(&nd.path.dentry->d_inode->i_mutex); + mutex_unlock(&path->dentry->d_inode->i_mutex); if (!err2) - mnt_drop_write(nd.path.mnt); + mnt_drop_write(path->mnt); out: - path_put(&nd.path); + path_put(path); + putname(name); return dentry; } struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path, unsigned int lookup_flags) { - struct filename *filename = getname_kernel(pathname); - struct dentry *res; - - if (IS_ERR(filename)) - return ERR_CAST(filename); - res = filename_create(dfd, filename, path, lookup_flags); - putname(filename); - return res; + return filename_create(dfd, getname_kernel(pathname), + path, lookup_flags); } EXPORT_SYMBOL(kern_path_create); @@ -3415,16 +3458,10 @@ void done_path_create(struct path *path, struct dentry *dentry) } EXPORT_SYMBOL(done_path_create); -struct dentry *user_path_create(int dfd, const char __user *pathname, +inline struct dentry *user_path_create(int dfd, const char __user *pathname, struct path *path, unsigned int lookup_flags) { - struct filename *tmp = getname(pathname); - struct dentry *res; - if (IS_ERR(tmp)) - return ERR_CAST(tmp); - res = filename_create(dfd, tmp, path, lookup_flags); - putname(tmp); - return res; + return filename_create(dfd, getname(pathname), path, lookup_flags); } EXPORT_SYMBOL(user_path_create); @@ -3645,14 +3682,17 @@ static long do_rmdir(int dfd, const char __user *pathname) int error = 0; struct filename *name; struct dentry *dentry; - struct nameidata nd; + struct path path; + struct qstr last; + int type; unsigned int lookup_flags = 0; retry: - name = user_path_parent(dfd, pathname, &nd, lookup_flags); + name = user_path_parent(dfd, pathname, + &path, &last, &type, lookup_flags); if (IS_ERR(name)) return PTR_ERR(name); - switch(nd.last_type) { + switch (type) { case LAST_DOTDOT: error = -ENOTEMPTY; goto exit1; @@ -3664,13 +3704,12 @@ retry: goto exit1; } - nd.flags &= ~LOOKUP_PARENT; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (error) goto exit1; - mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); - dentry = lookup_hash(&nd); + mutex_lock_nested(&path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); + dentry = __lookup_hash(&last, path.dentry, lookup_flags); error = PTR_ERR(dentry); if (IS_ERR(dentry)) goto exit2; @@ -3678,17 +3717,17 @@ retry: error = -ENOENT; goto exit3; } - error = security_path_rmdir(&nd.path, dentry); + error = security_path_rmdir(&path, dentry); if (error) goto exit3; - error = vfs_rmdir(nd.path.dentry->d_inode, dentry); + error = vfs_rmdir(path.dentry->d_inode, dentry); exit3: dput(dentry); exit2: - mutex_unlock(&nd.path.dentry->d_inode->i_mutex); - mnt_drop_write(nd.path.mnt); + mutex_unlock(&path.dentry->d_inode->i_mutex); + mnt_drop_write(path.mnt); exit1: - path_put(&nd.path); + path_put(&path); putname(name); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; @@ -3771,43 +3810,45 @@ static long do_unlinkat(int dfd, const char __user *pathname) int error; struct filename *name; struct dentry *dentry; - struct nameidata nd; + struct path path; + struct qstr last; + int type; struct inode *inode = NULL; struct inode *delegated_inode = NULL; unsigned int lookup_flags = 0; retry: - name = user_path_parent(dfd, pathname, &nd, lookup_flags); + name = user_path_parent(dfd, pathname, + &path, &last, &type, lookup_flags); if (IS_ERR(name)) return PTR_ERR(name); error = -EISDIR; - if (nd.last_type != LAST_NORM) + if (type != LAST_NORM) goto exit1; - nd.flags &= ~LOOKUP_PARENT; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (error) goto exit1; retry_deleg: - mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); - dentry = lookup_hash(&nd); + mutex_lock_nested(&path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); + dentry = __lookup_hash(&last, path.dentry, lookup_flags); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { /* Why not before? Because we want correct error value */ - if (nd.last.name[nd.last.len]) + if (last.name[last.len]) goto slashes; inode = dentry->d_inode; if (d_is_negative(dentry)) goto slashes; ihold(inode); - error = security_path_unlink(&nd.path, dentry); + error = security_path_unlink(&path, dentry); if (error) goto exit2; - error = vfs_unlink(nd.path.dentry->d_inode, dentry, &delegated_inode); + error = vfs_unlink(path.dentry->d_inode, dentry, &delegated_inode); exit2: dput(dentry); } - mutex_unlock(&nd.path.dentry->d_inode->i_mutex); + mutex_unlock(&path.dentry->d_inode->i_mutex); if (inode) iput(inode); /* truncate the inode here */ inode = NULL; @@ -3816,9 +3857,9 @@ exit2: if (!error) goto retry_deleg; } - mnt_drop_write(nd.path.mnt); + mnt_drop_write(path.mnt); exit1: - path_put(&nd.path); + path_put(&path); putname(name); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; @@ -4248,14 +4289,15 @@ EXPORT_SYMBOL(vfs_rename); SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname, unsigned int, flags) { - struct dentry *old_dir, *new_dir; struct dentry *old_dentry, *new_dentry; struct dentry *trap; - struct nameidata oldnd, newnd; + struct path old_path, new_path; + struct qstr old_last, new_last; + int old_type, new_type; struct inode *delegated_inode = NULL; struct filename *from; struct filename *to; - unsigned int lookup_flags = 0; + unsigned int lookup_flags = 0, target_flags = LOOKUP_RENAME_TARGET; bool should_retry = false; int error; @@ -4269,47 +4311,45 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname, if ((flags & RENAME_WHITEOUT) && !capable(CAP_MKNOD)) return -EPERM; + if (flags & RENAME_EXCHANGE) + target_flags = 0; + retry: - from = user_path_parent(olddfd, oldname, &oldnd, lookup_flags); + from = user_path_parent(olddfd, oldname, + &old_path, &old_last, &old_type, lookup_flags); if (IS_ERR(from)) { error = PTR_ERR(from); goto exit; } - to = user_path_parent(newdfd, newname, &newnd, lookup_flags); + to = user_path_parent(newdfd, newname, + &new_path, &new_last, &new_type, lookup_flags); if (IS_ERR(to)) { error = PTR_ERR(to); goto exit1; } error = -EXDEV; - if (oldnd.path.mnt != newnd.path.mnt) + if (old_path.mnt != new_path.mnt) goto exit2; - old_dir = oldnd.path.dentry; error = -EBUSY; - if (oldnd.last_type != LAST_NORM) + if (old_type != LAST_NORM) goto exit2; - new_dir = newnd.path.dentry; if (flags & RENAME_NOREPLACE) error = -EEXIST; - if (newnd.last_type != LAST_NORM) + if (new_type != LAST_NORM) goto exit2; - error = mnt_want_write(oldnd.path.mnt); + error = mnt_want_write(old_path.mnt); if (error) goto exit2; - oldnd.flags &= ~LOOKUP_PARENT; - newnd.flags &= ~LOOKUP_PARENT; - if (!(flags & RENAME_EXCHANGE)) - newnd.flags |= LOOKUP_RENAME_TARGET; - retry_deleg: - trap = lock_rename(new_dir, old_dir); + trap = lock_rename(new_path.dentry, old_path.dentry); - old_dentry = lookup_hash(&oldnd); + old_dentry = __lookup_hash(&old_last, old_path.dentry, lookup_flags); error = PTR_ERR(old_dentry); if (IS_ERR(old_dentry)) goto exit3; @@ -4317,7 +4357,7 @@ retry_deleg: error = -ENOENT; if (d_is_negative(old_dentry)) goto exit4; - new_dentry = lookup_hash(&newnd); + new_dentry = __lookup_hash(&new_last, new_path.dentry, lookup_flags | target_flags); error = PTR_ERR(new_dentry); if (IS_ERR(new_dentry)) goto exit4; @@ -4331,16 +4371,16 @@ retry_deleg: if (!d_is_dir(new_dentry)) { error = -ENOTDIR; - if (newnd.last.name[newnd.last.len]) + if (new_last.name[new_last.len]) goto exit5; } } /* unless the source is a directory trailing slashes give -ENOTDIR */ if (!d_is_dir(old_dentry)) { error = -ENOTDIR; - if (oldnd.last.name[oldnd.last.len]) + if (old_last.name[old_last.len]) goto exit5; - if (!(flags & RENAME_EXCHANGE) && newnd.last.name[newnd.last.len]) + if (!(flags & RENAME_EXCHANGE) && new_last.name[new_last.len]) goto exit5; } /* source should not be ancestor of target */ @@ -4353,32 +4393,32 @@ retry_deleg: if (new_dentry == trap) goto exit5; - error = security_path_rename(&oldnd.path, old_dentry, - &newnd.path, new_dentry, flags); + error = security_path_rename(&old_path, old_dentry, + &new_path, new_dentry, flags); if (error) goto exit5; - error = vfs_rename(old_dir->d_inode, old_dentry, - new_dir->d_inode, new_dentry, + error = vfs_rename(old_path.dentry->d_inode, old_dentry, + new_path.dentry->d_inode, new_dentry, &delegated_inode, flags); exit5: dput(new_dentry); exit4: dput(old_dentry); exit3: - unlock_rename(new_dir, old_dir); + unlock_rename(new_path.dentry, old_path.dentry); if (delegated_inode) { error = break_deleg_wait(&delegated_inode); if (!error) goto retry_deleg; } - mnt_drop_write(oldnd.path.mnt); + mnt_drop_write(old_path.mnt); exit2: if (retry_estale(error, lookup_flags)) should_retry = true; - path_put(&newnd.path); + path_put(&new_path); putname(to); exit1: - path_put(&oldnd.path); + path_put(&old_path); putname(from); if (should_retry) { should_retry = false; @@ -4437,18 +4477,19 @@ EXPORT_SYMBOL(readlink_copy); */ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen) { - struct nameidata nd; void *cookie; + struct inode *inode = d_inode(dentry); + const char *link = inode->i_link; int res; - nd.depth = 0; - cookie = dentry->d_inode->i_op->follow_link(dentry, &nd); - if (IS_ERR(cookie)) - return PTR_ERR(cookie); - - res = readlink_copy(buffer, buflen, nd_get_link(&nd)); - if (dentry->d_inode->i_op->put_link) - dentry->d_inode->i_op->put_link(dentry, &nd, cookie); + if (!link) { + link = inode->i_op->follow_link(dentry, &cookie); + if (IS_ERR(link)) + return PTR_ERR(link); + } + res = readlink_copy(buffer, buflen, link); + if (inode->i_op->put_link) + inode->i_op->put_link(inode, cookie); return res; } EXPORT_SYMBOL(generic_readlink); @@ -4480,22 +4521,21 @@ int page_readlink(struct dentry *dentry, char __user *buffer, int buflen) } EXPORT_SYMBOL(page_readlink); -void *page_follow_link_light(struct dentry *dentry, struct nameidata *nd) +const char *page_follow_link_light(struct dentry *dentry, void **cookie) { struct page *page = NULL; - nd_set_link(nd, page_getlink(dentry, &page)); - return page; + char *res = page_getlink(dentry, &page); + if (!IS_ERR(res)) + *cookie = page; + return res; } EXPORT_SYMBOL(page_follow_link_light); -void page_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) +void page_put_link(struct inode *unused, void *cookie) { struct page *page = cookie; - - if (page) { - kunmap(page); - page_cache_release(page); - } + kunmap(page); + page_cache_release(page); } EXPORT_SYMBOL(page_put_link); diff --git a/fs/namespace.c b/fs/namespace.c index 777b4819c..2b8aa15fd 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -463,7 +463,6 @@ void __mnt_drop_write(struct vfsmount *mnt) mnt_dec_writers(real_mount(mnt)); preempt_enable(); } -EXPORT_SYMBOL_GPL(__mnt_drop_write); /** * mnt_drop_write - give up write access to a mount @@ -591,24 +590,35 @@ static void delayed_free_vfsmnt(struct rcu_head *head) } /* call under rcu_read_lock */ -bool legitimize_mnt(struct vfsmount *bastard, unsigned seq) +int __legitimize_mnt(struct vfsmount *bastard, unsigned seq) { struct mount *mnt; if (read_seqretry(&mount_lock, seq)) - return false; + return 1; if (bastard == NULL) - return true; + return 0; mnt = real_mount(bastard); mnt_add_count(mnt, 1); if (likely(!read_seqretry(&mount_lock, seq))) - return true; + return 0; if (bastard->mnt_flags & MNT_SYNC_UMOUNT) { mnt_add_count(mnt, -1); - return false; + return 1; + } + return -1; +} + +/* call under rcu_read_lock */ +bool legitimize_mnt(struct vfsmount *bastard, unsigned seq) +{ + int res = __legitimize_mnt(bastard, seq); + if (likely(!res)) + return true; + if (unlikely(res < 0)) { + rcu_read_unlock(); + mntput(bastard); + rcu_read_lock(); } - rcu_read_unlock(); - mntput(bastard); - rcu_read_lock(); return false; } @@ -1216,7 +1226,7 @@ EXPORT_SYMBOL(replace_mount_options); /* iterator; we want it to have access to namespace_sem, thus here... */ static void *m_start(struct seq_file *m, loff_t *pos) { - struct proc_mounts *p = proc_mounts(m); + struct proc_mounts *p = m->private; down_read(&namespace_sem); if (p->cached_event == p->ns->event) { @@ -1237,7 +1247,7 @@ static void *m_start(struct seq_file *m, loff_t *pos) static void *m_next(struct seq_file *m, void *v, loff_t *pos) { - struct proc_mounts *p = proc_mounts(m); + struct proc_mounts *p = m->private; p->cached_mount = seq_list_next(v, &p->ns->list, pos); p->cached_index = *pos; @@ -1251,7 +1261,7 @@ static void m_stop(struct seq_file *m, void *v) static int m_show(struct seq_file *m, void *v) { - struct proc_mounts *p = proc_mounts(m); + struct proc_mounts *p = m->private; struct mount *r = list_entry(v, struct mount, mnt_list); return p->show(m, &r->mnt); } @@ -1793,7 +1803,6 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg, } return 0; } -EXPORT_SYMBOL(iterate_mounts); static void cleanup_group_ids(struct mount *mnt, struct mount *end) { diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 80021c709..93575e91a 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -1145,6 +1145,8 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry, case 0x00: ncp_dbg(1, "renamed %pd -> %pd\n", old_dentry, new_dentry); + ncp_d_prune(old_dentry); + ncp_d_prune(new_dentry); break; case 0x9E: error = -ENAMETOOLONG; diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 8d129bb73..682529c00 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -458,7 +458,7 @@ check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp) * pg_authenticate method for nfsv4 callback threads. * * The authflavor has been negotiated, so an incorrect flavor is a server - * bug. Drop packets with incorrect authflavor. + * bug. Deny packets with incorrect authflavor. * * All other checking done after NFS decoding where the nfs_client can be * found in nfs4_callback_compound @@ -468,12 +468,12 @@ static int nfs_callback_authenticate(struct svc_rqst *rqstp) switch (rqstp->rq_authop->flavour) { case RPC_AUTH_NULL: if (rqstp->rq_proc != CB_NULL) - return SVC_DROP; + return SVC_DENIED; break; case RPC_AUTH_GSS: /* No RPC_AUTH_GSS support yet in NFSv4.1 */ if (svc_is_backchannel(rqstp)) - return SVC_DROP; + return SVC_DENIED; } return SVC_OK; } diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index 197806fb8..29e3c1b01 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -327,10 +327,8 @@ validate_seqid(struct nfs4_slot_table *tbl, struct cb_sequenceargs * args) dprintk("%s slot table seqid: %u\n", __func__, slot->seq_nr); /* Normal */ - if (likely(args->csa_sequenceid == slot->seq_nr + 1)) { - slot->seq_nr++; + if (likely(args->csa_sequenceid == slot->seq_nr + 1)) goto out_ok; - } /* Replay */ if (args->csa_sequenceid == slot->seq_nr) { @@ -418,6 +416,7 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args, struct cb_process_state *cps) { struct nfs4_slot_table *tbl; + struct nfs4_slot *slot; struct nfs_client *clp; int i; __be32 status = htonl(NFS4ERR_BADSESSION); @@ -429,25 +428,32 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args, if (!(clp->cl_session->flags & SESSION4_BACK_CHAN)) goto out; + tbl = &clp->cl_session->bc_slot_table; + slot = tbl->slots + args->csa_slotid; spin_lock(&tbl->slot_tbl_lock); /* state manager is resetting the session */ if (test_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state)) { - spin_unlock(&tbl->slot_tbl_lock); status = htonl(NFS4ERR_DELAY); /* Return NFS4ERR_BADSESSION if we're draining the session * in order to reset it. */ if (test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)) status = htonl(NFS4ERR_BADSESSION); - goto out; + goto out_unlock; } - status = validate_seqid(&clp->cl_session->bc_slot_table, args); - spin_unlock(&tbl->slot_tbl_lock); + memcpy(&res->csr_sessionid, &args->csa_sessionid, + sizeof(res->csr_sessionid)); + res->csr_sequenceid = args->csa_sequenceid; + res->csr_slotid = args->csa_slotid; + res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; + res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; + + status = validate_seqid(tbl, args); if (status) - goto out; + goto out_unlock; cps->slotid = args->csa_slotid; @@ -458,15 +464,17 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args, */ if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists)) { status = htonl(NFS4ERR_DELAY); - goto out; + goto out_unlock; } - memcpy(&res->csr_sessionid, &args->csa_sessionid, - sizeof(res->csr_sessionid)); - res->csr_sequenceid = args->csa_sequenceid; - res->csr_slotid = args->csa_slotid; - res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; - res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; + /* + * RFC5661 20.9.3 + * If CB_SEQUENCE returns an error, then the state of the slot + * (sequence ID, cached reply) MUST NOT change. + */ + slot->seq_nr++; +out_unlock: + spin_unlock(&tbl->slot_tbl_lock); out: cps->clp = clp; /* put in nfs4_callback_compound */ diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 19ca95cdf..6b1697a01 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -909,7 +909,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r xdr_init_encode(&xdr_out, &rqstp->rq_res, p); status = decode_compound_hdr_arg(&xdr_in, &hdr_arg); - if (status == __constant_htonl(NFS4ERR_RESOURCE)) + if (status == htonl(NFS4ERR_RESOURCE)) return rpc_garbage_args; if (hdr_arg.minorversion == 0) { diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 892aefff3..4a90c9bb3 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -775,7 +775,7 @@ static int nfs_init_server(struct nfs_server *server, server->options = data->options; server->caps |= NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID| NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|NFS_CAP_OWNER_GROUP| - NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME|NFS_CAP_CHANGE_ATTR; + NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME; if (data->rsize) server->rsize = nfs_block_size(data->rsize, NULL); @@ -825,7 +825,6 @@ error: * Load up the server record from information gained in an fsinfo record */ static void nfs_server_set_fsinfo(struct nfs_server *server, - struct nfs_fh *mntfh, struct nfs_fsinfo *fsinfo) { unsigned long max_rpc_payload; @@ -901,7 +900,7 @@ int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs if (error < 0) goto out_error; - nfs_server_set_fsinfo(server, mntfh, &fsinfo); + nfs_server_set_fsinfo(server, &fsinfo); /* Get some general file system info */ if (server->namelen == 0) { @@ -1193,8 +1192,6 @@ void nfs_clients_init(struct net *net) } #ifdef CONFIG_PROC_FS -static struct proc_dir_entry *proc_fs_nfs; - static int nfs_server_list_open(struct inode *inode, struct file *file); static void *nfs_server_list_start(struct seq_file *p, loff_t *pos); static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos); @@ -1364,27 +1361,29 @@ static int nfs_volume_list_show(struct seq_file *m, void *v) { struct nfs_server *server; struct nfs_client *clp; - char dev[8], fsid[17]; + char dev[13]; // 8 for 2^24, 1 for ':', 3 for 2^8, 1 for '\0' + char fsid[34]; // 2 * 16 for %llx, 1 for ':', 1 for '\0' struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); /* display header on line 1 */ if (v == &nn->nfs_volume_list) { - seq_puts(m, "NV SERVER PORT DEV FSID FSC\n"); + seq_puts(m, "NV SERVER PORT DEV FSID" + " FSC\n"); return 0; } /* display one transport per line on subsequent lines */ server = list_entry(v, struct nfs_server, master_link); clp = server->nfs_client; - snprintf(dev, 8, "%u:%u", + snprintf(dev, sizeof(dev), "%u:%u", MAJOR(server->s_dev), MINOR(server->s_dev)); - snprintf(fsid, 17, "%llx:%llx", + snprintf(fsid, sizeof(fsid), "%llx:%llx", (unsigned long long) server->fsid.major, (unsigned long long) server->fsid.minor); rcu_read_lock(); - seq_printf(m, "v%u %s %s %-7s %-17s %s\n", + seq_printf(m, "v%u %s %s %-12s %-33s %s\n", clp->rpc_ops->version, rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), @@ -1434,27 +1433,20 @@ void nfs_fs_proc_net_exit(struct net *net) */ int __init nfs_fs_proc_init(void) { - struct proc_dir_entry *p; - - proc_fs_nfs = proc_mkdir("fs/nfsfs", NULL); - if (!proc_fs_nfs) + if (!proc_mkdir("fs/nfsfs", NULL)) goto error_0; /* a file of servers with which we're dealing */ - p = proc_symlink("servers", proc_fs_nfs, "../../net/nfsfs/servers"); - if (!p) + if (!proc_symlink("fs/nfsfs/servers", NULL, "../../net/nfsfs/servers")) goto error_1; /* a file of volumes that we have mounted */ - p = proc_symlink("volumes", proc_fs_nfs, "../../net/nfsfs/volumes"); - if (!p) - goto error_2; - return 0; + if (!proc_symlink("fs/nfsfs/volumes", NULL, "../../net/nfsfs/volumes")) + goto error_1; -error_2: - remove_proc_entry("servers", proc_fs_nfs); + return 0; error_1: - remove_proc_entry("fs/nfsfs", NULL); + remove_proc_subtree("fs/nfsfs", NULL); error_0: return -ENOMEM; } @@ -1464,9 +1456,7 @@ error_0: */ void nfs_fs_proc_exit(void) { - remove_proc_entry("volumes", proc_fs_nfs); - remove_proc_entry("servers", proc_fs_nfs); - remove_proc_entry("fs/nfsfs", NULL); + remove_proc_subtree("fs/nfsfs", NULL); } #endif /* CONFIG_PROC_FS */ diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index b2c8b31b2..547308a5e 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1470,9 +1470,6 @@ static int nfs_finish_open(struct nfs_open_context *ctx, { int err; - if ((open_flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) - *opened |= FILE_CREATED; - err = finish_open(file, dentry, do_open, opened); if (err) goto out; @@ -1771,7 +1768,7 @@ EXPORT_SYMBOL_GPL(nfs_mkdir); static void nfs_dentry_handle_enoent(struct dentry *dentry) { - if (d_really_is_positive(dentry) && !d_unhashed(dentry)) + if (simple_positive(dentry)) d_delete(dentry); } diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 8b8d83a52..cc4fa1ed6 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -555,31 +555,22 @@ static int nfs_launder_page(struct page *page) return nfs_wb_page(inode, page); } -#ifdef CONFIG_NFS_SWAP static int nfs_swap_activate(struct swap_info_struct *sis, struct file *file, sector_t *span) { - int ret; struct rpc_clnt *clnt = NFS_CLIENT(file->f_mapping->host); *span = sis->pages; - rcu_read_lock(); - ret = xs_swapper(rcu_dereference(clnt->cl_xprt), 1); - rcu_read_unlock(); - - return ret; + return rpc_clnt_swap_activate(clnt); } static void nfs_swap_deactivate(struct file *file) { struct rpc_clnt *clnt = NFS_CLIENT(file->f_mapping->host); - rcu_read_lock(); - xs_swapper(rcu_dereference(clnt->cl_xprt), 0); - rcu_read_unlock(); + rpc_clnt_swap_deactivate(clnt); } -#endif const struct address_space_operations nfs_file_aops = { .readpage = nfs_readpage, @@ -596,10 +587,8 @@ const struct address_space_operations nfs_file_aops = { .launder_page = nfs_launder_page, .is_dirty_writeback = nfs_check_dirty_writeback, .error_remove_page = generic_error_remove_page, -#ifdef CONFIG_NFS_SWAP .swap_activate = nfs_swap_activate, .swap_deactivate = nfs_swap_deactivate, -#endif }; /* diff --git a/fs/nfs/filelayout/filelayout.c b/fs/nfs/filelayout/filelayout.c index a46bf6de9..b34f2e228 100644 --- a/fs/nfs/filelayout/filelayout.c +++ b/fs/nfs/filelayout/filelayout.c @@ -32,6 +32,7 @@ #include <linux/nfs_fs.h> #include <linux/nfs_page.h> #include <linux/module.h> +#include <linux/backing-dev.h> #include <linux/sunrpc/metrics.h> diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index 6f5f0f425..b3289d701 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -20,6 +20,7 @@ #include "../nfs4trace.h" #include "../iostat.h" #include "../nfs.h" +#include "../nfs42.h" #define NFSDBG_FACILITY NFSDBG_PNFS_LD @@ -182,17 +183,14 @@ static void _ff_layout_free_lseg(struct nfs4_ff_layout_segment *fls) static void ff_layout_sort_mirrors(struct nfs4_ff_layout_segment *fls) { - struct nfs4_ff_layout_mirror *tmp; int i, j; for (i = 0; i < fls->mirror_array_cnt - 1; i++) { for (j = i + 1; j < fls->mirror_array_cnt; j++) if (fls->mirror_array[i]->efficiency < - fls->mirror_array[j]->efficiency) { - tmp = fls->mirror_array[i]; - fls->mirror_array[i] = fls->mirror_array[j]; - fls->mirror_array[j] = tmp; - } + fls->mirror_array[j]->efficiency) + swap(fls->mirror_array[i], + fls->mirror_array[j]); } } @@ -274,6 +272,7 @@ ff_layout_alloc_lseg(struct pnfs_layout_hdr *lh, spin_lock_init(&fls->mirror_array[i]->lock); fls->mirror_array[i]->ds_count = ds_count; + fls->mirror_array[i]->lseg = &fls->generic_hdr; /* deviceid */ rc = decode_deviceid(&stream, &devid); @@ -344,6 +343,10 @@ ff_layout_alloc_lseg(struct pnfs_layout_hdr *lh, fls->mirror_array[i]->gid); } + p = xdr_inline_decode(&stream, 4); + if (p) + fls->flags = be32_to_cpup(p); + ff_layout_sort_mirrors(fls); rc = ff_layout_check_layout(lgr); if (rc) @@ -415,6 +418,146 @@ ff_layout_get_lseg_count(struct nfs4_ff_layout_segment *fls) return 1; } +static void +nfs4_ff_start_busy_timer(struct nfs4_ff_busy_timer *timer) +{ + /* first IO request? */ + if (atomic_inc_return(&timer->n_ops) == 1) { + timer->start_time = ktime_get(); + } +} + +static ktime_t +nfs4_ff_end_busy_timer(struct nfs4_ff_busy_timer *timer) +{ + ktime_t start, now; + + if (atomic_dec_return(&timer->n_ops) < 0) + WARN_ON_ONCE(1); + + now = ktime_get(); + start = timer->start_time; + timer->start_time = now; + return ktime_sub(now, start); +} + +static ktime_t +nfs4_ff_layout_calc_completion_time(struct rpc_task *task) +{ + return ktime_sub(ktime_get(), task->tk_start); +} + +static bool +nfs4_ff_layoutstat_start_io(struct nfs4_ff_layout_mirror *mirror, + struct nfs4_ff_layoutstat *layoutstat) +{ + static const ktime_t notime = {0}; + ktime_t now = ktime_get(); + + nfs4_ff_start_busy_timer(&layoutstat->busy_timer); + if (ktime_equal(mirror->start_time, notime)) + mirror->start_time = now; + if (ktime_equal(mirror->last_report_time, notime)) + mirror->last_report_time = now; + if (ktime_to_ms(ktime_sub(now, mirror->last_report_time)) >= + FF_LAYOUTSTATS_REPORT_INTERVAL) { + mirror->last_report_time = now; + return true; + } + + return false; +} + +static void +nfs4_ff_layout_stat_io_update_requested(struct nfs4_ff_layoutstat *layoutstat, + __u64 requested) +{ + struct nfs4_ff_io_stat *iostat = &layoutstat->io_stat; + + iostat->ops_requested++; + iostat->bytes_requested += requested; +} + +static void +nfs4_ff_layout_stat_io_update_completed(struct nfs4_ff_layoutstat *layoutstat, + __u64 requested, + __u64 completed, + ktime_t time_completed) +{ + struct nfs4_ff_io_stat *iostat = &layoutstat->io_stat; + ktime_t timer; + + iostat->ops_completed++; + iostat->bytes_completed += completed; + iostat->bytes_not_delivered += requested - completed; + + timer = nfs4_ff_end_busy_timer(&layoutstat->busy_timer); + iostat->total_busy_time = + ktime_add(iostat->total_busy_time, timer); + iostat->aggregate_completion_time = + ktime_add(iostat->aggregate_completion_time, time_completed); +} + +static void +nfs4_ff_layout_stat_io_start_read(struct nfs4_ff_layout_mirror *mirror, + __u64 requested) +{ + bool report; + + spin_lock(&mirror->lock); + report = nfs4_ff_layoutstat_start_io(mirror, &mirror->read_stat); + nfs4_ff_layout_stat_io_update_requested(&mirror->read_stat, requested); + spin_unlock(&mirror->lock); + + if (report) + pnfs_report_layoutstat(mirror->lseg->pls_layout->plh_inode); +} + +static void +nfs4_ff_layout_stat_io_end_read(struct rpc_task *task, + struct nfs4_ff_layout_mirror *mirror, + __u64 requested, + __u64 completed) +{ + spin_lock(&mirror->lock); + nfs4_ff_layout_stat_io_update_completed(&mirror->read_stat, + requested, completed, + nfs4_ff_layout_calc_completion_time(task)); + spin_unlock(&mirror->lock); +} + +static void +nfs4_ff_layout_stat_io_start_write(struct nfs4_ff_layout_mirror *mirror, + __u64 requested) +{ + bool report; + + spin_lock(&mirror->lock); + report = nfs4_ff_layoutstat_start_io(mirror , &mirror->write_stat); + nfs4_ff_layout_stat_io_update_requested(&mirror->write_stat, requested); + spin_unlock(&mirror->lock); + + if (report) + pnfs_report_layoutstat(mirror->lseg->pls_layout->plh_inode); +} + +static void +nfs4_ff_layout_stat_io_end_write(struct rpc_task *task, + struct nfs4_ff_layout_mirror *mirror, + __u64 requested, + __u64 completed, + enum nfs3_stable_how committed) +{ + if (committed == NFS_UNSTABLE) + requested = completed = 0; + + spin_lock(&mirror->lock); + nfs4_ff_layout_stat_io_update_completed(&mirror->write_stat, + requested, completed, + nfs4_ff_layout_calc_completion_time(task)); + spin_unlock(&mirror->lock); +} + static int ff_layout_alloc_commit_info(struct pnfs_layout_segment *lseg, struct nfs_commit_info *cinfo, @@ -879,6 +1022,12 @@ static int ff_layout_read_done_cb(struct rpc_task *task, return 0; } +static bool +ff_layout_need_layoutcommit(struct pnfs_layout_segment *lseg) +{ + return !(FF_LAYOUT_LSEG(lseg)->flags & FF_FLAGS_NO_LAYOUTCOMMIT); +} + /* * We reference the rpc_cred of the first WRITE that triggers the need for * a LAYOUTCOMMIT, and use it to send the layoutcommit compound. @@ -891,6 +1040,9 @@ static int ff_layout_read_done_cb(struct rpc_task *task, static void ff_layout_set_layoutcommit(struct nfs_pgio_header *hdr) { + if (!ff_layout_need_layoutcommit(hdr->lseg)) + return; + pnfs_set_layoutcommit(hdr->inode, hdr->lseg, hdr->mds_offset + hdr->res.count); dprintk("%s inode %lu pls_end_pos %lu\n", __func__, hdr->inode->i_ino, @@ -909,6 +1061,10 @@ ff_layout_reset_to_mds(struct pnfs_layout_segment *lseg, int idx) static int ff_layout_read_prepare_common(struct rpc_task *task, struct nfs_pgio_header *hdr) { + nfs4_ff_layout_stat_io_start_read( + FF_LAYOUT_COMP(hdr->lseg, hdr->pgio_mirror_idx), + hdr->args.count); + if (unlikely(test_bit(NFS_CONTEXT_BAD, &hdr->args.context->flags))) { rpc_exit(task, -EIO); return -EIO; @@ -962,15 +1118,15 @@ static void ff_layout_read_prepare_v4(struct rpc_task *task, void *data) { struct nfs_pgio_header *hdr = data; - if (ff_layout_read_prepare_common(task, hdr)) - return; - if (ff_layout_setup_sequence(hdr->ds_clp, &hdr->args.seq_args, &hdr->res.seq_res, task)) return; + if (ff_layout_read_prepare_common(task, hdr)) + return; + if (nfs4_set_rw_stateid(&hdr->args.stateid, hdr->args.context, hdr->args.lock_context, FMODE_READ) == -EIO) rpc_exit(task, -EIO); /* lost lock, terminate I/O */ @@ -982,6 +1138,10 @@ static void ff_layout_read_call_done(struct rpc_task *task, void *data) dprintk("--> %s task->tk_status %d\n", __func__, task->tk_status); + nfs4_ff_layout_stat_io_end_read(task, + FF_LAYOUT_COMP(hdr->lseg, hdr->pgio_mirror_idx), + hdr->args.count, hdr->res.count); + if (test_bit(NFS_IOHDR_REDO, &hdr->flags) && task->tk_status == 0) { nfs4_sequence_done(task, &hdr->res.seq_res); @@ -1074,7 +1234,8 @@ static int ff_layout_commit_done_cb(struct rpc_task *task, return -EAGAIN; } - if (data->verf.committed == NFS_UNSTABLE) + if (data->verf.committed == NFS_UNSTABLE + && ff_layout_need_layoutcommit(data->lseg)) pnfs_set_layoutcommit(data->inode, data->lseg, data->lwb); return 0; @@ -1083,6 +1244,10 @@ static int ff_layout_commit_done_cb(struct rpc_task *task, static int ff_layout_write_prepare_common(struct rpc_task *task, struct nfs_pgio_header *hdr) { + nfs4_ff_layout_stat_io_start_write( + FF_LAYOUT_COMP(hdr->lseg, hdr->pgio_mirror_idx), + hdr->args.count); + if (unlikely(test_bit(NFS_CONTEXT_BAD, &hdr->args.context->flags))) { rpc_exit(task, -EIO); return -EIO; @@ -1116,15 +1281,15 @@ static void ff_layout_write_prepare_v4(struct rpc_task *task, void *data) { struct nfs_pgio_header *hdr = data; - if (ff_layout_write_prepare_common(task, hdr)) - return; - if (ff_layout_setup_sequence(hdr->ds_clp, &hdr->args.seq_args, &hdr->res.seq_res, task)) return; + if (ff_layout_write_prepare_common(task, hdr)) + return; + if (nfs4_set_rw_stateid(&hdr->args.stateid, hdr->args.context, hdr->args.lock_context, FMODE_WRITE) == -EIO) rpc_exit(task, -EIO); /* lost lock, terminate I/O */ @@ -1134,6 +1299,11 @@ static void ff_layout_write_call_done(struct rpc_task *task, void *data) { struct nfs_pgio_header *hdr = data; + nfs4_ff_layout_stat_io_end_write(task, + FF_LAYOUT_COMP(hdr->lseg, hdr->pgio_mirror_idx), + hdr->args.count, hdr->res.count, + hdr->res.verf->committed); + if (test_bit(NFS_IOHDR_REDO, &hdr->flags) && task->tk_status == 0) { nfs4_sequence_done(task, &hdr->res.seq_res); @@ -1152,8 +1322,17 @@ static void ff_layout_write_count_stats(struct rpc_task *task, void *data) &NFS_CLIENT(hdr->inode)->cl_metrics[NFSPROC4_CLNT_WRITE]); } +static void ff_layout_commit_prepare_common(struct rpc_task *task, + struct nfs_commit_data *cdata) +{ + nfs4_ff_layout_stat_io_start_write( + FF_LAYOUT_COMP(cdata->lseg, cdata->ds_commit_index), + 0); +} + static void ff_layout_commit_prepare_v3(struct rpc_task *task, void *data) { + ff_layout_commit_prepare_common(task, data); rpc_call_start(task); } @@ -1161,10 +1340,30 @@ static void ff_layout_commit_prepare_v4(struct rpc_task *task, void *data) { struct nfs_commit_data *wdata = data; - ff_layout_setup_sequence(wdata->ds_clp, + if (ff_layout_setup_sequence(wdata->ds_clp, &wdata->args.seq_args, &wdata->res.seq_res, - task); + task)) + return; + ff_layout_commit_prepare_common(task, data); +} + +static void ff_layout_commit_done(struct rpc_task *task, void *data) +{ + struct nfs_commit_data *cdata = data; + struct nfs_page *req; + __u64 count = 0; + + if (task->tk_status == 0) { + list_for_each_entry(req, &cdata->pages, wb_list) + count += req->wb_bytes; + } + + nfs4_ff_layout_stat_io_end_write(task, + FF_LAYOUT_COMP(cdata->lseg, cdata->ds_commit_index), + count, count, NFS_FILE_SYNC); + + pnfs_generic_write_commit_done(task, data); } static void ff_layout_commit_count_stats(struct rpc_task *task, void *data) @@ -1205,14 +1404,14 @@ static const struct rpc_call_ops ff_layout_write_call_ops_v4 = { static const struct rpc_call_ops ff_layout_commit_call_ops_v3 = { .rpc_call_prepare = ff_layout_commit_prepare_v3, - .rpc_call_done = pnfs_generic_write_commit_done, + .rpc_call_done = ff_layout_commit_done, .rpc_count_stats = ff_layout_commit_count_stats, .rpc_release = pnfs_generic_commit_release, }; static const struct rpc_call_ops ff_layout_commit_call_ops_v4 = { .rpc_call_prepare = ff_layout_commit_prepare_v4, - .rpc_call_done = pnfs_generic_write_commit_done, + .rpc_call_done = ff_layout_commit_done, .rpc_count_stats = ff_layout_commit_count_stats, .rpc_release = pnfs_generic_commit_release, }; @@ -1256,7 +1455,6 @@ ff_layout_read_pagelist(struct nfs_pgio_header *hdr) fh = nfs4_ff_layout_select_ds_fh(lseg, idx); if (fh) hdr->args.fh = fh; - /* * Note that if we ever decide to split across DSes, * then we may need to handle dense-like offsets. @@ -1385,6 +1583,7 @@ static int ff_layout_initiate_commit(struct nfs_commit_data *data, int how) fh = select_ds_fh_from_commit(lseg, data->ds_commit_index); if (fh) data->args.fh = fh; + return nfs_initiate_commit(ds_clnt, data, ds->ds_clp->rpc_ops, vers == 3 ? &ff_layout_commit_call_ops_v3 : &ff_layout_commit_call_ops_v4, @@ -1488,6 +1687,247 @@ out: dprintk("%s: Return\n", __func__); } +static int +ff_layout_ntop4(const struct sockaddr *sap, char *buf, const size_t buflen) +{ + const struct sockaddr_in *sin = (struct sockaddr_in *)sap; + + return snprintf(buf, buflen, "%pI4", &sin->sin_addr); +} + +static size_t +ff_layout_ntop6_noscopeid(const struct sockaddr *sap, char *buf, + const int buflen) +{ + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; + const struct in6_addr *addr = &sin6->sin6_addr; + + /* + * RFC 4291, Section 2.2.2 + * + * Shorthanded ANY address + */ + if (ipv6_addr_any(addr)) + return snprintf(buf, buflen, "::"); + + /* + * RFC 4291, Section 2.2.2 + * + * Shorthanded loopback address + */ + if (ipv6_addr_loopback(addr)) + return snprintf(buf, buflen, "::1"); + + /* + * RFC 4291, Section 2.2.3 + * + * Special presentation address format for mapped v4 + * addresses. + */ + if (ipv6_addr_v4mapped(addr)) + return snprintf(buf, buflen, "::ffff:%pI4", + &addr->s6_addr32[3]); + + /* + * RFC 4291, Section 2.2.1 + */ + return snprintf(buf, buflen, "%pI6c", addr); +} + +/* Derived from rpc_sockaddr2uaddr */ +static void +ff_layout_encode_netaddr(struct xdr_stream *xdr, struct nfs4_pnfs_ds_addr *da) +{ + struct sockaddr *sap = (struct sockaddr *)&da->da_addr; + char portbuf[RPCBIND_MAXUADDRPLEN]; + char addrbuf[RPCBIND_MAXUADDRLEN]; + char *netid; + unsigned short port; + int len, netid_len; + __be32 *p; + + switch (sap->sa_family) { + case AF_INET: + if (ff_layout_ntop4(sap, addrbuf, sizeof(addrbuf)) == 0) + return; + port = ntohs(((struct sockaddr_in *)sap)->sin_port); + netid = "tcp"; + netid_len = 3; + break; + case AF_INET6: + if (ff_layout_ntop6_noscopeid(sap, addrbuf, sizeof(addrbuf)) == 0) + return; + port = ntohs(((struct sockaddr_in6 *)sap)->sin6_port); + netid = "tcp6"; + netid_len = 4; + break; + default: + /* we only support tcp and tcp6 */ + WARN_ON_ONCE(1); + return; + } + + snprintf(portbuf, sizeof(portbuf), ".%u.%u", port >> 8, port & 0xff); + len = strlcat(addrbuf, portbuf, sizeof(addrbuf)); + + p = xdr_reserve_space(xdr, 4 + netid_len); + xdr_encode_opaque(p, netid, netid_len); + + p = xdr_reserve_space(xdr, 4 + len); + xdr_encode_opaque(p, addrbuf, len); +} + +static void +ff_layout_encode_nfstime(struct xdr_stream *xdr, + ktime_t t) +{ + struct timespec64 ts; + __be32 *p; + + p = xdr_reserve_space(xdr, 12); + ts = ktime_to_timespec64(t); + p = xdr_encode_hyper(p, ts.tv_sec); + *p++ = cpu_to_be32(ts.tv_nsec); +} + +static void +ff_layout_encode_io_latency(struct xdr_stream *xdr, + struct nfs4_ff_io_stat *stat) +{ + __be32 *p; + + p = xdr_reserve_space(xdr, 5 * 8); + p = xdr_encode_hyper(p, stat->ops_requested); + p = xdr_encode_hyper(p, stat->bytes_requested); + p = xdr_encode_hyper(p, stat->ops_completed); + p = xdr_encode_hyper(p, stat->bytes_completed); + p = xdr_encode_hyper(p, stat->bytes_not_delivered); + ff_layout_encode_nfstime(xdr, stat->total_busy_time); + ff_layout_encode_nfstime(xdr, stat->aggregate_completion_time); +} + +static void +ff_layout_encode_layoutstats(struct xdr_stream *xdr, + struct nfs42_layoutstat_args *args, + struct nfs42_layoutstat_devinfo *devinfo) +{ + struct nfs4_ff_layout_mirror *mirror = devinfo->layout_private; + struct nfs4_pnfs_ds_addr *da; + struct nfs4_pnfs_ds *ds = mirror->mirror_ds->ds; + struct nfs_fh *fh = &mirror->fh_versions[0]; + __be32 *p, *start; + + da = list_first_entry(&ds->ds_addrs, struct nfs4_pnfs_ds_addr, da_node); + dprintk("%s: DS %s: encoding address %s\n", + __func__, ds->ds_remotestr, da->da_remotestr); + /* layoutupdate length */ + start = xdr_reserve_space(xdr, 4); + /* netaddr4 */ + ff_layout_encode_netaddr(xdr, da); + /* nfs_fh4 */ + p = xdr_reserve_space(xdr, 4 + fh->size); + xdr_encode_opaque(p, fh->data, fh->size); + /* ff_io_latency4 read */ + spin_lock(&mirror->lock); + ff_layout_encode_io_latency(xdr, &mirror->read_stat.io_stat); + /* ff_io_latency4 write */ + ff_layout_encode_io_latency(xdr, &mirror->write_stat.io_stat); + spin_unlock(&mirror->lock); + /* nfstime4 */ + ff_layout_encode_nfstime(xdr, ktime_sub(ktime_get(), mirror->start_time)); + /* bool */ + p = xdr_reserve_space(xdr, 4); + *p = cpu_to_be32(false); + + *start = cpu_to_be32((xdr->p - start - 1) * 4); +} + +static bool +ff_layout_mirror_prepare_stats(struct nfs42_layoutstat_args *args, + struct pnfs_layout_segment *pls, + int *dev_count, int dev_limit) +{ + struct nfs4_ff_layout_mirror *mirror; + struct nfs4_deviceid_node *dev; + struct nfs42_layoutstat_devinfo *devinfo; + int i; + + for (i = 0; i < FF_LAYOUT_MIRROR_COUNT(pls); i++) { + if (*dev_count >= dev_limit) + break; + mirror = FF_LAYOUT_COMP(pls, i); + if (!mirror || !mirror->mirror_ds) + continue; + dev = FF_LAYOUT_DEVID_NODE(pls, i); + devinfo = &args->devinfo[*dev_count]; + memcpy(&devinfo->dev_id, &dev->deviceid, NFS4_DEVICEID4_SIZE); + devinfo->offset = pls->pls_range.offset; + devinfo->length = pls->pls_range.length; + /* well, we don't really know if IO is continuous or not! */ + devinfo->read_count = mirror->read_stat.io_stat.bytes_completed; + devinfo->read_bytes = mirror->read_stat.io_stat.bytes_completed; + devinfo->write_count = mirror->write_stat.io_stat.bytes_completed; + devinfo->write_bytes = mirror->write_stat.io_stat.bytes_completed; + devinfo->layout_type = LAYOUT_FLEX_FILES; + devinfo->layoutstats_encode = ff_layout_encode_layoutstats; + devinfo->layout_private = mirror; + /* lseg refcount put in cleanup_layoutstats */ + pnfs_get_lseg(pls); + + ++(*dev_count); + } + + return *dev_count < dev_limit; +} + +static int +ff_layout_prepare_layoutstats(struct nfs42_layoutstat_args *args) +{ + struct pnfs_layout_segment *pls; + int dev_count = 0; + + spin_lock(&args->inode->i_lock); + list_for_each_entry(pls, &NFS_I(args->inode)->layout->plh_segs, pls_list) { + dev_count += FF_LAYOUT_MIRROR_COUNT(pls); + } + spin_unlock(&args->inode->i_lock); + /* For now, send at most PNFS_LAYOUTSTATS_MAXDEV statistics */ + if (dev_count > PNFS_LAYOUTSTATS_MAXDEV) { + dprintk("%s: truncating devinfo to limit (%d:%d)\n", + __func__, dev_count, PNFS_LAYOUTSTATS_MAXDEV); + dev_count = PNFS_LAYOUTSTATS_MAXDEV; + } + args->devinfo = kmalloc(dev_count * sizeof(*args->devinfo), GFP_KERNEL); + if (!args->devinfo) + return -ENOMEM; + + dev_count = 0; + spin_lock(&args->inode->i_lock); + list_for_each_entry(pls, &NFS_I(args->inode)->layout->plh_segs, pls_list) { + if (!ff_layout_mirror_prepare_stats(args, pls, &dev_count, + PNFS_LAYOUTSTATS_MAXDEV)) { + break; + } + } + spin_unlock(&args->inode->i_lock); + args->num_dev = dev_count; + + return 0; +} + +static void +ff_layout_cleanup_layoutstats(struct nfs42_layoutstat_data *data) +{ + struct nfs4_ff_layout_mirror *mirror; + int i; + + for (i = 0; i < data->args.num_dev; i++) { + mirror = data->args.devinfo[i].layout_private; + data->args.devinfo[i].layout_private = NULL; + pnfs_put_lseg(mirror->lseg); + } +} + static struct pnfs_layoutdriver_type flexfilelayout_type = { .id = LAYOUT_FLEX_FILES, .name = "LAYOUT_FLEX_FILES", @@ -1510,6 +1950,8 @@ static struct pnfs_layoutdriver_type flexfilelayout_type = { .alloc_deviceid_node = ff_layout_alloc_deviceid_node, .encode_layoutreturn = ff_layout_encode_layoutreturn, .sync = pnfs_nfs_generic_sync, + .prepare_layoutstats = ff_layout_prepare_layoutstats, + .cleanup_layoutstats = ff_layout_cleanup_layoutstats, }; static int __init nfs4flexfilelayout_init(void) diff --git a/fs/nfs/flexfilelayout/flexfilelayout.h b/fs/nfs/flexfilelayout/flexfilelayout.h index 070f20445..f92f9a0a8 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.h +++ b/fs/nfs/flexfilelayout/flexfilelayout.h @@ -9,12 +9,17 @@ #ifndef FS_NFS_NFS4FLEXFILELAYOUT_H #define FS_NFS_NFS4FLEXFILELAYOUT_H +#define FF_FLAGS_NO_LAYOUTCOMMIT 1 + #include "../pnfs.h" /* XXX: Let's filter out insanely large mirror count for now to avoid oom * due to network error etc. */ #define NFS4_FLEXFILE_LAYOUT_MAX_MIRROR_CNT 4096 +/* LAYOUTSTATS report interval in ms */ +#define FF_LAYOUTSTATS_REPORT_INTERVAL (60000L) + struct nfs4_ff_ds_version { u32 version; u32 minor_version; @@ -41,24 +46,48 @@ struct nfs4_ff_layout_ds_err { struct nfs4_deviceid deviceid; }; +struct nfs4_ff_io_stat { + __u64 ops_requested; + __u64 bytes_requested; + __u64 ops_completed; + __u64 bytes_completed; + __u64 bytes_not_delivered; + ktime_t total_busy_time; + ktime_t aggregate_completion_time; +}; + +struct nfs4_ff_busy_timer { + ktime_t start_time; + atomic_t n_ops; +}; + +struct nfs4_ff_layoutstat { + struct nfs4_ff_io_stat io_stat; + struct nfs4_ff_busy_timer busy_timer; +}; + struct nfs4_ff_layout_mirror { + struct pnfs_layout_segment *lseg; /* back pointer */ u32 ds_count; u32 efficiency; struct nfs4_ff_layout_ds *mirror_ds; u32 fh_versions_cnt; struct nfs_fh *fh_versions; nfs4_stateid stateid; - struct nfs4_string user_name; - struct nfs4_string group_name; u32 uid; u32 gid; struct rpc_cred *cred; spinlock_t lock; + struct nfs4_ff_layoutstat read_stat; + struct nfs4_ff_layoutstat write_stat; + ktime_t start_time; + ktime_t last_report_time; }; struct nfs4_ff_layout_segment { struct pnfs_layout_segment generic_hdr; u64 stripe_unit; + u32 flags; u32 mirror_array_cnt; struct nfs4_ff_layout_mirror **mirror_array; }; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 5d25b9d97..0adc7d245 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -442,8 +442,9 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, st nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR); if (fattr->valid & NFS_ATTR_FATTR_CHANGE) inode->i_version = fattr->change_attr; - else if (nfs_server_capable(inode, NFS_CAP_CHANGE_ATTR)) - nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR); + else + nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR + | NFS_INO_REVAL_PAGECACHE); if (fattr->valid & NFS_ATTR_FATTR_SIZE) inode->i_size = nfs_size_to_loff_t(fattr->size); else @@ -678,6 +679,8 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) if (!err) { generic_fillattr(inode, stat); stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode)); + if (S_ISDIR(inode->i_mode)) + stat->blksize = NFS_SERVER(inode)->dtsize; } out: trace_nfs_getattr_exit(inode, err); @@ -1689,7 +1692,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) nfs_force_lookup_revalidate(inode); inode->i_version = fattr->change_attr; } - } else if (server->caps & NFS_CAP_CHANGE_ATTR) + } else nfsi->cache_validity |= save_cache_validity; if (fattr->valid & NFS_ATTR_FATTR_MTIME) { @@ -2008,17 +2011,15 @@ static int __init init_nfs_fs(void) if (err) goto out1; -#ifdef CONFIG_PROC_FS rpc_proc_register(&init_net, &nfs_rpcstat); -#endif - if ((err = register_nfs_fs()) != 0) + + err = register_nfs_fs(); + if (err) goto out0; return 0; out0: -#ifdef CONFIG_PROC_FS rpc_proc_unregister(&init_net, "nfs"); -#endif nfs_destroy_directcache(); out1: nfs_destroy_writepagecache(); @@ -2049,9 +2050,7 @@ static void __exit exit_nfs_fs(void) nfs_destroy_nfspagecache(); nfs_fscache_unregister(); unregister_pernet_subsys(&nfs_net_ops); -#ifdef CONFIG_PROC_FS rpc_proc_unregister(&init_net, "nfs"); -#endif unregister_nfs_fs(); nfs_fs_proc_exit(); nfsiod_stop(); diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 9e6475bc5..9b372b845 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -296,6 +296,22 @@ extern struct rpc_procinfo nfs4_procedures[]; #ifdef CONFIG_NFS_V4_SECURITY_LABEL extern struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags); +static inline struct nfs4_label * +nfs4_label_copy(struct nfs4_label *dst, struct nfs4_label *src) +{ + if (!dst || !src) + return NULL; + + if (src->len > NFS4_MAXLABELLEN) + return NULL; + + dst->lfs = src->lfs; + dst->pi = src->pi; + dst->len = src->len; + memcpy(dst->label, src->label, src->len); + + return dst; +} static inline void nfs4_label_free(struct nfs4_label *label) { if (label) { @@ -316,6 +332,11 @@ static inline void nfs4_label_free(void *label) {} static inline void nfs_zap_label_cache_locked(struct nfs_inode *nfsi) { } +static inline struct nfs4_label * +nfs4_label_copy(struct nfs4_label *dst, struct nfs4_label *src) +{ + return NULL; +} #endif /* CONFIG_NFS_V4_SECURITY_LABEL */ /* proc.c */ @@ -607,7 +628,7 @@ void nfs_mark_page_unstable(struct page *page) struct inode *inode = page_file_mapping(page)->host; inc_zone_page_state(page, NR_UNSTABLE_NFS); - inc_bdi_stat(inode_to_bdi(inode), BDI_RECLAIMABLE); + inc_wb_stat(&inode_to_bdi(inode)->wb, WB_RECLAIMABLE); __mark_inode_dirty(inode, I_DIRTY_DATASYNC); } diff --git a/fs/nfs/nfs42.h b/fs/nfs/nfs42.h index 7afb8947d..ff66ae700 100644 --- a/fs/nfs/nfs42.h +++ b/fs/nfs/nfs42.h @@ -5,11 +5,18 @@ #ifndef __LINUX_FS_NFS_NFS4_2_H #define __LINUX_FS_NFS_NFS4_2_H +/* + * FIXME: four LAYOUTSTATS calls per compound at most! Do we need to support + * more? Need to consider not to pre-alloc too much for a compound. + */ +#define PNFS_LAYOUTSTATS_MAXDEV (4) + /* nfs4.2proc.c */ int nfs42_proc_allocate(struct file *, loff_t, loff_t); int nfs42_proc_deallocate(struct file *, loff_t, loff_t); loff_t nfs42_proc_llseek(struct file *, loff_t, int); - +int nfs42_proc_layoutstats_generic(struct nfs_server *, + struct nfs42_layoutstat_data *); /* nfs4.2xdr.h */ extern struct rpc_procinfo nfs4_2_procedures[]; diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c index 3a9e75235..d731bbf97 100644 --- a/fs/nfs/nfs42proc.c +++ b/fs/nfs/nfs42proc.c @@ -10,6 +10,11 @@ #include <linux/nfs_fs.h> #include "nfs4_fs.h" #include "nfs42.h" +#include "iostat.h" +#include "pnfs.h" +#include "internal.h" + +#define NFSDBG_FACILITY NFSDBG_PNFS static int nfs42_set_rw_stateid(nfs4_stateid *dst, struct file *file, fmode_t fmode) @@ -130,7 +135,7 @@ int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len) return err; } -loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence) +static loff_t _nfs42_proc_llseek(struct file *filep, loff_t offset, int whence) { struct inode *inode = file_inode(filep); struct nfs42_seek_args args = { @@ -165,3 +170,102 @@ loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence) 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 = { }; + int err; + + do { + err = _nfs42_proc_llseek(filep, offset, whence); + if (err == -ENOTSUPP) + return -EOPNOTSUPP; + err = nfs4_handle_exception(server, err, &exception); + } while (exception.retry); + + return err; +} + + +static void +nfs42_layoutstat_prepare(struct rpc_task *task, void *calldata) +{ + struct nfs42_layoutstat_data *data = calldata; + struct nfs_server *server = NFS_SERVER(data->args.inode); + + 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; + + if (!nfs4_sequence_done(task, &data->res.seq_res)) + return; + + switch (task->tk_status) { + case 0: + break; + case -ENOTSUPP: + case -EOPNOTSUPP: + NFS_SERVER(data->inode)->caps &= ~NFS_CAP_LAYOUTSTATS; + default: + 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); + return 0; +} diff --git a/fs/nfs/nfs42xdr.c b/fs/nfs/nfs42xdr.c index 1a25b2724..a6bd27da6 100644 --- a/fs/nfs/nfs42xdr.c +++ b/fs/nfs/nfs42xdr.c @@ -4,6 +4,8 @@ #ifndef __LINUX_FS_NFS_NFS4_2XDR_H #define __LINUX_FS_NFS_NFS4_2XDR_H +#include "nfs42.h" + #define encode_fallocate_maxsz (encode_stateid_maxsz + \ 2 /* offset */ + \ 2 /* length */) @@ -22,6 +24,16 @@ 1 /* whence */ + \ 2 /* offset */ + \ 2 /* length */) +#define encode_io_info_maxsz 4 +#define encode_layoutstats_maxsz (op_decode_hdr_maxsz + \ + 2 /* offset */ + \ + 2 /* length */ + \ + encode_stateid_maxsz + \ + encode_io_info_maxsz + \ + encode_io_info_maxsz + \ + 1 /* opaque devaddr4 length */ + \ + XDR_QUADLEN(PNFS_LAYOUTSTATS_MAXSIZE)) +#define decode_layoutstats_maxsz (op_decode_hdr_maxsz) #define NFS4_enc_allocate_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ @@ -45,6 +57,14 @@ #define NFS4_dec_seek_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ decode_seek_maxsz) +#define NFS4_enc_layoutstats_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ + encode_putfh_maxsz + \ + PNFS_LAYOUTSTATS_MAXDEV * encode_layoutstats_maxsz) +#define NFS4_dec_layoutstats_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ + decode_putfh_maxsz + \ + PNFS_LAYOUTSTATS_MAXDEV * decode_layoutstats_maxsz) static void encode_fallocate(struct xdr_stream *xdr, @@ -81,6 +101,33 @@ static void encode_seek(struct xdr_stream *xdr, encode_uint32(xdr, args->sa_what); } +static void encode_layoutstats(struct xdr_stream *xdr, + struct nfs42_layoutstat_args *args, + struct nfs42_layoutstat_devinfo *devinfo, + struct compound_hdr *hdr) +{ + __be32 *p; + + encode_op_hdr(xdr, OP_LAYOUTSTATS, decode_layoutstats_maxsz, hdr); + p = reserve_space(xdr, 8 + 8); + p = xdr_encode_hyper(p, devinfo->offset); + p = xdr_encode_hyper(p, devinfo->length); + encode_nfs4_stateid(xdr, &args->stateid); + p = reserve_space(xdr, 4*8 + NFS4_DEVICEID4_SIZE + 4); + p = xdr_encode_hyper(p, devinfo->read_count); + p = xdr_encode_hyper(p, devinfo->read_bytes); + p = xdr_encode_hyper(p, devinfo->write_count); + p = xdr_encode_hyper(p, devinfo->write_bytes); + p = xdr_encode_opaque_fixed(p, devinfo->dev_id.data, + NFS4_DEVICEID4_SIZE); + /* Encode layoutupdate4 */ + *p++ = cpu_to_be32(devinfo->layout_type); + if (devinfo->layoutstats_encode != NULL) + devinfo->layoutstats_encode(xdr, args, devinfo); + else + encode_uint32(xdr, 0); +} + /* * Encode ALLOCATE request */ @@ -137,6 +184,28 @@ static void nfs4_xdr_enc_seek(struct rpc_rqst *req, encode_nops(&hdr); } +/* + * Encode LAYOUTSTATS request + */ +static void nfs4_xdr_enc_layoutstats(struct rpc_rqst *req, + struct xdr_stream *xdr, + struct nfs42_layoutstat_args *args) +{ + int i; + + struct compound_hdr hdr = { + .minorversion = nfs4_xdr_minorversion(&args->seq_args), + }; + + encode_compound_hdr(xdr, req, &hdr); + encode_sequence(xdr, &args->seq_args, &hdr); + encode_putfh(xdr, args->fh, &hdr); + WARN_ON(args->num_dev > PNFS_LAYOUTSTATS_MAXDEV); + for (i = 0; i < args->num_dev; i++) + encode_layoutstats(xdr, args, &args->devinfo[i], &hdr); + encode_nops(&hdr); +} + static int decode_allocate(struct xdr_stream *xdr, struct nfs42_falloc_res *res) { return decode_op_hdr(xdr, OP_ALLOCATE); @@ -169,6 +238,12 @@ out_overflow: return -EIO; } +static int decode_layoutstats(struct xdr_stream *xdr, + struct nfs42_layoutstat_res *res) +{ + return decode_op_hdr(xdr, OP_LAYOUTSTATS); +} + /* * Decode ALLOCATE request */ @@ -246,4 +321,35 @@ static int nfs4_xdr_dec_seek(struct rpc_rqst *rqstp, out: return status; } + +/* + * Decode LAYOUTSTATS request + */ +static int nfs4_xdr_dec_layoutstats(struct rpc_rqst *rqstp, + struct xdr_stream *xdr, + struct nfs42_layoutstat_res *res) +{ + struct compound_hdr hdr; + int status, i; + + status = decode_compound_hdr(xdr, &hdr); + if (status) + goto out; + status = decode_sequence(xdr, &res->seq_res, rqstp); + if (status) + goto out; + status = decode_putfh(xdr); + if (status) + goto out; + WARN_ON(res->num_dev > PNFS_LAYOUTSTATS_MAXDEV); + for (i = 0; i < res->num_dev; i++) { + status = decode_layoutstats(xdr, res); + if (status) + goto out; + } +out: + res->rpc_status = status; + return status; +} + #endif /* __LINUX_FS_NFS_NFS4_2XDR_H */ diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index fdef424b0..ea3bee919 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -233,6 +233,7 @@ extern int nfs4_handle_exception(struct nfs_server *, int, struct nfs4_exception extern int nfs4_call_sync(struct rpc_clnt *, struct nfs_server *, struct rpc_message *, struct nfs4_sequence_args *, struct nfs4_sequence_res *, int); +extern void nfs4_init_sequence(struct nfs4_sequence_args *, struct nfs4_sequence_res *, int); extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struct rpc_cred *, struct nfs4_setclientid_res *); extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct nfs4_setclientid_res *arg, struct rpc_cred *); extern int nfs4_proc_get_rootfh(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *, bool); diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index e42be52a8..3aa6a9ba5 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -676,7 +676,6 @@ found: break; } - /* No matching nfs_client found. */ spin_unlock(&nn->nfs_client_lock); dprintk("NFS: <-- %s status = %d\n", __func__, status); nfs_put_client(prev); diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c index f58c17b3b..dcd39d4e2 100644 --- a/fs/nfs/nfs4file.c +++ b/fs/nfs/nfs4file.c @@ -41,6 +41,10 @@ nfs4_file_open(struct inode *inode, struct file *filp) dprintk("NFS: open file(%pd2)\n", dentry); + err = nfs_check_flags(openflags); + if (err) + return err; + if ((openflags & O_ACCMODE) == 3) openflags--; diff --git a/fs/nfs/nfs4getroot.c b/fs/nfs/nfs4getroot.c index c0b3a16b4..039b3eb6d 100644 --- a/fs/nfs/nfs4getroot.c +++ b/fs/nfs/nfs4getroot.c @@ -35,13 +35,6 @@ int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh, bool auth_p goto out; } - if (fsinfo.fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) { - printk(KERN_ERR "nfs4_get_rootfh:" - " getroot obtained referral\n"); - ret = -EREMOTE; - goto out; - } - memcpy(&server->fsid, &fsinfo.fattr->fsid, sizeof(server->fsid)); out: nfs_free_fattr(fsinfo.fattr); diff --git a/fs/nfs/nfs4idmap.c b/fs/nfs/nfs4idmap.c index 2e1737c40..535dfc69c 100644 --- a/fs/nfs/nfs4idmap.c +++ b/fs/nfs/nfs4idmap.c @@ -494,12 +494,7 @@ nfs_idmap_delete(struct nfs_client *clp) int nfs_idmap_init(void) { - int ret; - ret = nfs_idmap_init_keyring(); - if (ret != 0) - goto out; -out: - return ret; + return nfs_idmap_init_keyring(); } void nfs_idmap_quit(void) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d3f205126..3acb1eb72 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -356,6 +356,9 @@ int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_ case 0: return 0; case -NFS4ERR_OPENMODE: + case -NFS4ERR_DELEG_REVOKED: + case -NFS4ERR_ADMIN_REVOKED: + case -NFS4ERR_BAD_STATEID: if (inode && nfs4_have_delegation(inode, FMODE_READ)) { nfs4_inode_return_delegation(inode); exception->retry = 1; @@ -367,15 +370,6 @@ int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_ if (ret < 0) break; goto wait_on_recovery; - case -NFS4ERR_DELEG_REVOKED: - case -NFS4ERR_ADMIN_REVOKED: - case -NFS4ERR_BAD_STATEID: - if (state == NULL) - break; - ret = nfs4_schedule_stateid_recovery(server, state); - if (ret < 0) - break; - goto wait_on_recovery; case -NFS4ERR_EXPIRED: if (state != NULL) { ret = nfs4_schedule_stateid_recovery(server, state); @@ -473,7 +467,10 @@ static void do_renew_lease(struct nfs_client *clp, unsigned long timestamp) static void renew_lease(const struct nfs_server *server, unsigned long timestamp) { - do_renew_lease(server->nfs_client, timestamp); + struct nfs_client *clp = server->nfs_client; + + if (!nfs4_has_session(clp)) + do_renew_lease(clp, timestamp); } struct nfs4_call_sync_data { @@ -482,8 +479,8 @@ struct nfs4_call_sync_data { struct nfs4_sequence_res *seq_res; }; -static void nfs4_init_sequence(struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res, int cache_reply) +void nfs4_init_sequence(struct nfs4_sequence_args *args, + struct nfs4_sequence_res *res, int cache_reply) { args->sa_slot = NULL; args->sa_cache_this = cache_reply; @@ -622,8 +619,7 @@ int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) clp = session->clp; do_renew_lease(clp, res->sr_timestamp); /* Check sequence flags */ - if (res->sr_status_flags != 0) - nfs4_schedule_lease_recovery(clp); + nfs41_handle_sequence_flag_errors(clp, res->sr_status_flags); nfs41_update_target_slotid(slot->table, slot, res); break; case 1: @@ -916,6 +912,7 @@ struct nfs4_opendata { struct nfs_open_confirmres c_res; struct nfs4_string owner_name; struct nfs4_string group_name; + struct nfs4_label *a_label; struct nfs_fattr f_attr; struct nfs4_label *f_label; struct dentry *dir; @@ -1019,6 +1016,10 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry, if (IS_ERR(p->f_label)) goto err_free_p; + p->a_label = nfs4_label_alloc(server, gfp_mask); + if (IS_ERR(p->a_label)) + goto err_free_f; + alloc_seqid = server->nfs_client->cl_mvops->alloc_seqid; p->o_arg.seqid = alloc_seqid(&sp->so_seqid, gfp_mask); if (IS_ERR(p->o_arg.seqid)) @@ -1047,7 +1048,7 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry, p->o_arg.server = server; p->o_arg.bitmask = nfs4_bitmask(server, label); p->o_arg.open_bitmap = &nfs4_fattr_bitmap[0]; - p->o_arg.label = label; + p->o_arg.label = nfs4_label_copy(p->a_label, label); p->o_arg.claim = nfs4_map_atomic_open_claim(server, claim); switch (p->o_arg.claim) { case NFS4_OPEN_CLAIM_NULL: @@ -1080,6 +1081,8 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry, return p; err_free_label: + nfs4_label_free(p->a_label); +err_free_f: nfs4_label_free(p->f_label); err_free_p: kfree(p); @@ -1099,6 +1102,7 @@ static void nfs4_opendata_free(struct kref *kref) nfs4_put_open_state(p->state); nfs4_put_state_owner(p->owner); + nfs4_label_free(p->a_label); nfs4_label_free(p->f_label); dput(p->dir); @@ -1556,6 +1560,13 @@ static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmod struct nfs4_state *newstate; int ret; + if ((opendata->o_arg.claim == NFS4_OPEN_CLAIM_DELEGATE_CUR || + opendata->o_arg.claim == NFS4_OPEN_CLAIM_DELEG_CUR_FH) && + (opendata->o_arg.u.delegation_type & fmode) != fmode) + /* This mode can't have been delegated, so we must have + * a valid open_stateid to cover it - not need to reclaim. + */ + return 0; opendata->o_arg.open_flags = 0; opendata->o_arg.fmode = fmode; opendata->o_arg.share_access = nfs4_map_atomic_open_share( @@ -1687,6 +1698,7 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct "%d.\n", __func__, err); case 0: case -ENOENT: + case -EAGAIN: case -ESTALE: break; case -NFS4ERR_BADSESSION: @@ -3358,6 +3370,8 @@ static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir, goto out; case -NFS4ERR_MOVED: err = nfs4_get_referral(client, dir, name, fattr, fhandle); + if (err == -NFS4ERR_MOVED) + err = nfs4_handle_exception(NFS_SERVER(dir), err, &exception); goto out; case -NFS4ERR_WRONGSEC: err = -EPERM; @@ -4958,49 +4972,128 @@ static void nfs4_init_boot_verifier(const struct nfs_client *clp, memcpy(bootverf->data, verf, sizeof(bootverf->data)); } -static unsigned int -nfs4_init_nonuniform_client_string(struct nfs_client *clp, - char *buf, size_t len) +static int +nfs4_init_nonuniform_client_string(struct nfs_client *clp) { - unsigned int result; + int result; + size_t len; + char *str; + bool retried = false; if (clp->cl_owner_id != NULL) - return strlcpy(buf, clp->cl_owner_id, len); + return 0; +retry: + rcu_read_lock(); + len = 10 + strlen(clp->cl_ipaddr) + 1 + + strlen(rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)) + + 1 + + strlen(rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_PROTO)) + + 1; + rcu_read_unlock(); + + if (len > NFS4_OPAQUE_LIMIT + 1) + return -EINVAL; + + /* + * Since this string is allocated at mount time, and held until the + * nfs_client is destroyed, we can use GFP_KERNEL here w/o worrying + * about a memory-reclaim deadlock. + */ + str = kmalloc(len, GFP_KERNEL); + if (!str) + return -ENOMEM; rcu_read_lock(); - result = scnprintf(buf, len, "Linux NFSv4.0 %s/%s %s", - clp->cl_ipaddr, - rpc_peeraddr2str(clp->cl_rpcclient, - RPC_DISPLAY_ADDR), - rpc_peeraddr2str(clp->cl_rpcclient, - RPC_DISPLAY_PROTO)); + result = scnprintf(str, len, "Linux NFSv4.0 %s/%s %s", + clp->cl_ipaddr, + rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR), + rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_PROTO)); rcu_read_unlock(); - clp->cl_owner_id = kstrdup(buf, GFP_KERNEL); - return result; + + /* Did something change? */ + if (result >= len) { + kfree(str); + if (retried) + return -EINVAL; + retried = true; + goto retry; + } + clp->cl_owner_id = str; + return 0; } -static unsigned int -nfs4_init_uniform_client_string(struct nfs_client *clp, - char *buf, size_t len) +static int +nfs4_init_uniquifier_client_string(struct nfs_client *clp) +{ + int result; + size_t len; + char *str; + + len = 10 + 10 + 1 + 10 + 1 + + strlen(nfs4_client_id_uniquifier) + 1 + + strlen(clp->cl_rpcclient->cl_nodename) + 1; + + if (len > NFS4_OPAQUE_LIMIT + 1) + return -EINVAL; + + /* + * Since this string is allocated at mount time, and held until the + * nfs_client is destroyed, we can use GFP_KERNEL here w/o worrying + * about a memory-reclaim deadlock. + */ + str = kmalloc(len, GFP_KERNEL); + if (!str) + return -ENOMEM; + + result = scnprintf(str, len, "Linux NFSv%u.%u %s/%s", + clp->rpc_ops->version, clp->cl_minorversion, + nfs4_client_id_uniquifier, + clp->cl_rpcclient->cl_nodename); + if (result >= len) { + kfree(str); + return -EINVAL; + } + clp->cl_owner_id = str; + return 0; +} + +static int +nfs4_init_uniform_client_string(struct nfs_client *clp) { - const char *nodename = clp->cl_rpcclient->cl_nodename; - unsigned int result; + int result; + size_t len; + char *str; if (clp->cl_owner_id != NULL) - return strlcpy(buf, clp->cl_owner_id, len); + return 0; if (nfs4_client_id_uniquifier[0] != '\0') - result = scnprintf(buf, len, "Linux NFSv%u.%u %s/%s", - clp->rpc_ops->version, - clp->cl_minorversion, - nfs4_client_id_uniquifier, - nodename); - else - result = scnprintf(buf, len, "Linux NFSv%u.%u %s", - clp->rpc_ops->version, clp->cl_minorversion, - nodename); - clp->cl_owner_id = kstrdup(buf, GFP_KERNEL); - return result; + return nfs4_init_uniquifier_client_string(clp); + + len = 10 + 10 + 1 + 10 + 1 + + strlen(clp->cl_rpcclient->cl_nodename) + 1; + + if (len > NFS4_OPAQUE_LIMIT + 1) + return -EINVAL; + + /* + * Since this string is allocated at mount time, and held until the + * nfs_client is destroyed, we can use GFP_KERNEL here w/o worrying + * about a memory-reclaim deadlock. + */ + str = kmalloc(len, GFP_KERNEL); + if (!str) + return -ENOMEM; + + result = scnprintf(str, len, "Linux NFSv%u.%u %s", + clp->rpc_ops->version, clp->cl_minorversion, + clp->cl_rpcclient->cl_nodename); + if (result >= len) { + kfree(str); + return -EINVAL; + } + clp->cl_owner_id = str; + return 0; } /* @@ -5047,7 +5140,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, struct nfs4_setclientid setclientid = { .sc_verifier = &sc_verifier, .sc_prog = program, - .sc_cb_ident = clp->cl_cb_ident, + .sc_clnt = clp, }; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID], @@ -5067,16 +5160,15 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, /* nfs_client_id4 */ nfs4_init_boot_verifier(clp, &sc_verifier); + if (test_bit(NFS_CS_MIGRATION, &clp->cl_flags)) - setclientid.sc_name_len = - nfs4_init_uniform_client_string(clp, - setclientid.sc_name, - sizeof(setclientid.sc_name)); + status = nfs4_init_uniform_client_string(clp); else - setclientid.sc_name_len = - nfs4_init_nonuniform_client_string(clp, - setclientid.sc_name, - sizeof(setclientid.sc_name)); + status = nfs4_init_nonuniform_client_string(clp); + + if (status) + goto out; + /* cb_client4 */ setclientid.sc_netid_len = nfs4_init_callback_netid(clp, @@ -5086,9 +5178,9 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, sizeof(setclientid.sc_uaddr), "%s.%u.%u", clp->cl_ipaddr, port >> 8, port & 255); - dprintk("NFS call setclientid auth=%s, '%.*s'\n", + dprintk("NFS call setclientid auth=%s, '%s'\n", clp->cl_rpcclient->cl_auth->au_ops->au_name, - setclientid.sc_name_len, setclientid.sc_name); + clp->cl_owner_id); task = rpc_run_task(&task_setup_data); if (IS_ERR(task)) { status = PTR_ERR(task); @@ -5360,15 +5452,15 @@ static int nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock * return err; } -static int do_vfs_lock(struct file *file, struct file_lock *fl) +static int do_vfs_lock(struct inode *inode, struct file_lock *fl) { int res = 0; switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) { case FL_POSIX: - res = posix_lock_file_wait(file, fl); + res = posix_lock_inode_wait(inode, fl); break; case FL_FLOCK: - res = flock_lock_file_wait(file, fl); + res = flock_lock_inode_wait(inode, fl); break; default: BUG(); @@ -5428,7 +5520,7 @@ static void nfs4_locku_done(struct rpc_task *task, void *data) switch (task->tk_status) { case 0: renew_lease(calldata->server, calldata->timestamp); - do_vfs_lock(calldata->fl.fl_file, &calldata->fl); + do_vfs_lock(calldata->lsp->ls_state->inode, &calldata->fl); if (nfs4_update_lock_stateid(calldata->lsp, &calldata->res.stateid)) break; @@ -5536,7 +5628,7 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock * mutex_lock(&sp->so_delegreturn_mutex); /* Exclude nfs4_reclaim_open_stateid() - note nesting! */ down_read(&nfsi->rwsem); - if (do_vfs_lock(request->fl_file, request) == -ENOENT) { + if (do_vfs_lock(inode, request) == -ENOENT) { up_read(&nfsi->rwsem); mutex_unlock(&sp->so_delegreturn_mutex); goto out; @@ -5677,7 +5769,7 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata) data->timestamp); if (data->arg.new_lock) { data->fl.fl_flags &= ~(FL_SLEEP | FL_ACCESS); - if (do_vfs_lock(data->fl.fl_file, &data->fl) < 0) { + if (do_vfs_lock(lsp->ls_state->inode, &data->fl) < 0) { rpc_restart_call_prepare(task); break; } @@ -5919,7 +6011,7 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock if (status != 0) goto out; request->fl_flags |= FL_ACCESS; - status = do_vfs_lock(request->fl_file, request); + status = do_vfs_lock(state->inode, request); if (status < 0) goto out; down_read(&nfsi->rwsem); @@ -5927,7 +6019,7 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock /* Yes: cache locks! */ /* ...but avoid races with delegation recall... */ request->fl_flags = fl_flags & ~FL_SLEEP; - status = do_vfs_lock(request->fl_file, request); + status = do_vfs_lock(state->inode, request); up_read(&nfsi->rwsem); goto out; } @@ -6849,11 +6941,14 @@ static int _nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred, }; nfs4_init_boot_verifier(clp, &verifier); - args.id_len = nfs4_init_uniform_client_string(clp, args.id, - sizeof(args.id)); - dprintk("NFS call exchange_id auth=%s, '%.*s'\n", + + status = nfs4_init_uniform_client_string(clp); + if (status) + goto out; + + dprintk("NFS call exchange_id auth=%s, '%s'\n", clp->cl_rpcclient->cl_auth->au_ops->au_name, - args.id_len, args.id); + clp->cl_owner_id); res.server_owner = kzalloc(sizeof(struct nfs41_server_owner), GFP_NOFS); @@ -6888,7 +6983,7 @@ static int _nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred, /* unsupported! */ WARN_ON_ONCE(1); status = -EINVAL; - goto out_server_scope; + goto out_impl_id; } status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT); @@ -6916,6 +7011,7 @@ static int _nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred, /* use the most recent implementation id */ kfree(clp->cl_implid); clp->cl_implid = res.impl_id; + res.impl_id = NULL; if (clp->cl_serverscope != NULL && !nfs41_same_server_scope(clp->cl_serverscope, @@ -6929,15 +7025,16 @@ static int _nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred, if (clp->cl_serverscope == NULL) { clp->cl_serverscope = res.server_scope; - goto out; + res.server_scope = NULL; } - } else - kfree(res.impl_id); + } -out_server_owner: - kfree(res.server_owner); +out_impl_id: + kfree(res.impl_id); out_server_scope: kfree(res.server_scope); +out_server_owner: + kfree(res.server_owner); out: if (clp->cl_implid != NULL) dprintk("NFS reply exchange_id: Server Implementation ID: " @@ -7487,13 +7584,8 @@ static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred) goto out; } ret = rpc_wait_for_completion_task(task); - if (!ret) { - struct nfs4_sequence_res *res = task->tk_msg.rpc_resp; - - if (task->tk_status == 0) - nfs41_handle_sequence_flag_errors(clp, res->sr_status_flags); + if (!ret) ret = task->tk_status; - } rpc_put_task(task); out: dprintk("<-- %s status=%d\n", __func__, ret); @@ -7881,16 +7973,17 @@ static void nfs4_layoutreturn_release(void *calldata) { struct nfs4_layoutreturn *lrp = calldata; struct pnfs_layout_hdr *lo = lrp->args.layout; + LIST_HEAD(freeme); dprintk("--> %s\n", __func__); spin_lock(&lo->plh_inode->i_lock); if (lrp->res.lrs_present) pnfs_set_layout_stateid(lo, &lrp->res.stateid, true); + pnfs_mark_matching_lsegs_invalid(lo, &freeme, &lrp->args.range); pnfs_clear_layoutreturn_waitbit(lo); - clear_bit(NFS_LAYOUT_RETURN_BEFORE_CLOSE, &lo->plh_flags); - rpc_wake_up(&NFS_SERVER(lo->plh_inode)->roc_rpcwaitq); lo->plh_block_lgets--; spin_unlock(&lo->plh_inode->i_lock); + pnfs_free_lseg_list(&freeme); pnfs_put_layout_hdr(lrp->args.layout); nfs_iput_and_deactive(lrp->inode); kfree(calldata); @@ -8064,9 +8157,8 @@ nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data, bool sync) struct rpc_task *task; int status = 0; - dprintk("NFS: %4d initiating layoutcommit call. sync %d " - "lbw: %llu inode %lu\n", - data->task.tk_pid, sync, + dprintk("NFS: initiating layoutcommit call. sync %d " + "lbw: %llu inode %lu\n", sync, data->args.lastbytewritten, data->args.inode->i_ino); @@ -8505,7 +8597,6 @@ static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = { .minor_version = 0, .init_caps = NFS_CAP_READDIRPLUS | NFS_CAP_ATOMIC_OPEN - | NFS_CAP_CHANGE_ATTR | NFS_CAP_POSIX_LOCK, .init_client = nfs40_init_client, .shutdown_client = nfs40_shutdown_client, @@ -8531,7 +8622,6 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = { .minor_version = 1, .init_caps = NFS_CAP_READDIRPLUS | NFS_CAP_ATOMIC_OPEN - | NFS_CAP_CHANGE_ATTR | NFS_CAP_POSIX_LOCK | NFS_CAP_STATEID_NFSV41 | NFS_CAP_ATOMIC_OPEN_V1, @@ -8554,13 +8644,13 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = { .minor_version = 2, .init_caps = NFS_CAP_READDIRPLUS | NFS_CAP_ATOMIC_OPEN - | NFS_CAP_CHANGE_ATTR | NFS_CAP_POSIX_LOCK | NFS_CAP_STATEID_NFSV41 | NFS_CAP_ATOMIC_OPEN_V1 | NFS_CAP_ALLOCATE | NFS_CAP_DEALLOCATE - | NFS_CAP_SEEK, + | NFS_CAP_SEEK + | NFS_CAP_LAYOUTSTATS, .init_client = nfs41_init_client, .shutdown_client = nfs41_shutdown_client, .match_stateid = nfs41_match_stateid, diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index ddef1dc80..f2e2ad894 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -309,7 +309,6 @@ int nfs41_init_clientid(struct nfs_client *clp, struct rpc_cred *cred) if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state)) goto do_confirm; - nfs4_begin_drain_session(clp); status = nfs4_proc_exchange_id(clp, cred); if (status != 0) goto out; @@ -1832,6 +1831,7 @@ static int nfs4_establish_lease(struct nfs_client *clp) clp->cl_mvops->reboot_recovery_ops; int status; + nfs4_begin_drain_session(clp); cred = nfs4_get_clid_cred(clp); if (cred == NULL) return -ENOENT; @@ -2191,25 +2191,35 @@ static void nfs41_handle_server_reboot(struct nfs_client *clp) } } -static void nfs41_handle_state_revoked(struct nfs_client *clp) +static void nfs41_handle_all_state_revoked(struct nfs_client *clp) { nfs4_reset_all_state(clp); dprintk("%s: state revoked on server %s\n", __func__, clp->cl_hostname); } +static void nfs41_handle_some_state_revoked(struct nfs_client *clp) +{ + nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce); + nfs4_schedule_state_manager(clp); + + dprintk("%s: state revoked on server %s\n", __func__, clp->cl_hostname); +} + static void nfs41_handle_recallable_state_revoked(struct nfs_client *clp) { - /* This will need to handle layouts too */ - nfs_expire_all_delegations(clp); + /* FIXME: For now, we destroy all layouts. */ + pnfs_destroy_all_layouts(clp); + /* FIXME: For now, we test all delegations+open state+locks. */ + nfs41_handle_some_state_revoked(clp); dprintk("%s: Recallable state revoked on server %s!\n", __func__, clp->cl_hostname); } static void nfs41_handle_backchannel_fault(struct nfs_client *clp) { - nfs_expire_all_delegations(clp); - if (test_and_set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state) == 0) - nfs4_schedule_state_manager(clp); + set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); + nfs4_schedule_state_manager(clp); + dprintk("%s: server %s declared a backchannel fault\n", __func__, clp->cl_hostname); } @@ -2231,10 +2241,11 @@ void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags) if (flags & SEQ4_STATUS_RESTART_RECLAIM_NEEDED) nfs41_handle_server_reboot(clp); - if (flags & (SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED | - SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED | + if (flags & (SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED)) + nfs41_handle_all_state_revoked(clp); + if (flags & (SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED | SEQ4_STATUS_ADMIN_STATE_REVOKED)) - nfs41_handle_state_revoked(clp); + nfs41_handle_some_state_revoked(clp); if (flags & SEQ4_STATUS_LEASE_MOVED) nfs4_schedule_lease_moved_recovery(clp); if (flags & SEQ4_STATUS_RECALLABLE_STATE_REVOKED) diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 0aea97841..558cd65db 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -139,7 +139,8 @@ static int nfs4_stat_to_errno(int); #define encode_setclientid_maxsz \ (op_encode_hdr_maxsz + \ XDR_QUADLEN(NFS4_VERIFIER_SIZE) + \ - XDR_QUADLEN(NFS4_SETCLIENTID_NAMELEN) + \ + /* client name */ \ + 1 + XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + \ 1 /* sc_prog */ + \ 1 + XDR_QUADLEN(RPCBIND_MAXNETIDLEN) + \ 1 + XDR_QUADLEN(RPCBIND_MAXUADDRLEN) + \ @@ -288,7 +289,8 @@ static int nfs4_stat_to_errno(int); #define encode_exchange_id_maxsz (op_encode_hdr_maxsz + \ encode_verifier_maxsz + \ 1 /* co_ownerid.len */ + \ - XDR_QUADLEN(NFS4_EXCHANGE_ID_LEN) + \ + /* eia_clientowner */ \ + 1 + XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + \ 1 /* flags */ + \ 1 /* spa_how */ + \ /* max is SP4_MACH_CRED (for now) */ + \ @@ -1667,13 +1669,14 @@ static void encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclie encode_op_hdr(xdr, OP_SETCLIENTID, decode_setclientid_maxsz, hdr); encode_nfs4_verifier(xdr, setclientid->sc_verifier); - encode_string(xdr, setclientid->sc_name_len, setclientid->sc_name); + encode_string(xdr, strlen(setclientid->sc_clnt->cl_owner_id), + setclientid->sc_clnt->cl_owner_id); p = reserve_space(xdr, 4); *p = cpu_to_be32(setclientid->sc_prog); encode_string(xdr, setclientid->sc_netid_len, setclientid->sc_netid); encode_string(xdr, setclientid->sc_uaddr_len, setclientid->sc_uaddr); p = reserve_space(xdr, 4); - *p = cpu_to_be32(setclientid->sc_cb_ident); + *p = cpu_to_be32(setclientid->sc_clnt->cl_cb_ident); } static void encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs4_setclientid_res *arg, struct compound_hdr *hdr) @@ -1747,7 +1750,8 @@ static void encode_exchange_id(struct xdr_stream *xdr, encode_op_hdr(xdr, OP_EXCHANGE_ID, decode_exchange_id_maxsz, hdr); encode_nfs4_verifier(xdr, args->verifier); - encode_string(xdr, args->id_len, args->id); + encode_string(xdr, strlen(args->client->cl_owner_id), + args->client->cl_owner_id); encode_uint32(xdr, args->flags); encode_uint32(xdr, args->state_protect.how); @@ -7427,6 +7431,7 @@ struct rpc_procinfo nfs4_procedures[] = { PROC(SEEK, enc_seek, dec_seek), PROC(ALLOCATE, enc_allocate, dec_allocate), PROC(DEALLOCATE, enc_deallocate, dec_deallocate), + PROC(LAYOUTSTATS, enc_layoutstats, dec_layoutstats), #endif /* CONFIG_NFS_V4_2 */ }; diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index 7b4552678..4984bbe55 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -636,9 +636,8 @@ int nfs_initiate_pgio(struct rpc_clnt *clnt, struct nfs_pgio_header *hdr, hdr->rw_ops->rw_initiate(hdr, &msg, rpc_ops, &task_setup_data, how); - dprintk("NFS: %5u initiated pgio call " + dprintk("NFS: initiated pgio call " "(req %s/%llu, %u bytes @ offset %llu)\n", - hdr->task.tk_pid, hdr->inode->i_sb->s_id, (unsigned long long)NFS_FILEID(hdr->inode), hdr->args.count, @@ -690,8 +689,6 @@ static int nfs_pgio_error(struct nfs_pageio_descriptor *desc, static void nfs_pgio_release(void *calldata) { struct nfs_pgio_header *hdr = calldata; - if (hdr->rw_ops->rw_release) - hdr->rw_ops->rw_release(hdr); nfs_pgio_data_destroy(hdr); hdr->completion_ops->completion(hdr); } @@ -711,7 +708,9 @@ static void nfs_pageio_mirror_init(struct nfs_pgio_mirror *mirror, * nfs_pageio_init - initialise a page io descriptor * @desc: pointer to descriptor * @inode: pointer to inode - * @doio: pointer to io function + * @pg_ops: pointer to pageio operations + * @compl_ops: pointer to pageio completion operations + * @rw_ops: pointer to nfs read/write operations * @bsize: io block size * @io_flags: extra parameters for the io function */ @@ -1101,8 +1100,6 @@ static int nfs_do_recoalesce(struct nfs_pageio_descriptor *desc) mirror->pg_base = 0; mirror->pg_recoalesce = 0; - desc->pg_moreio = 0; - while (!list_empty(&head)) { struct nfs_page *req; @@ -1189,6 +1186,7 @@ int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc, * nfs_pageio_complete_mirror - Complete I/O on the current mirror of an * nfs_pageio_descriptor * @desc: pointer to io descriptor + * @mirror_idx: pointer to mirror index */ static void nfs_pageio_complete_mirror(struct nfs_pageio_descriptor *desc, u32 mirror_idx) diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index d47c18868..70bf706b1 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -35,6 +35,7 @@ #include "iostat.h" #include "nfs4trace.h" #include "delegation.h" +#include "nfs42.h" #define NFSDBG_FACILITY NFSDBG_PNFS #define PNFS_LAYOUTGET_RETRY_TIMEOUT (120*HZ) @@ -351,7 +352,7 @@ pnfs_layout_need_return(struct pnfs_layout_hdr *lo, { struct pnfs_layout_segment *s; - if (!test_bit(NFS_LSEG_LAYOUTRETURN, &lseg->pls_flags)) + if (!test_and_clear_bit(NFS_LSEG_LAYOUTRETURN, &lseg->pls_flags)) return false; list_for_each_entry(s, &lo->plh_segs, pls_list) @@ -361,6 +362,18 @@ pnfs_layout_need_return(struct pnfs_layout_hdr *lo, return true; } +static bool +pnfs_prepare_layoutreturn(struct pnfs_layout_hdr *lo) +{ + if (test_and_set_bit(NFS_LAYOUT_RETURN, &lo->plh_flags)) + return false; + lo->plh_return_iomode = 0; + lo->plh_block_lgets++; + pnfs_get_layout_hdr(lo); + clear_bit(NFS_LAYOUT_RETURN_BEFORE_CLOSE, &lo->plh_flags); + return true; +} + static void pnfs_layoutreturn_before_put_lseg(struct pnfs_layout_segment *lseg, struct pnfs_layout_hdr *lo, struct inode *inode) { @@ -371,17 +384,16 @@ static void pnfs_layoutreturn_before_put_lseg(struct pnfs_layout_segment *lseg, if (pnfs_layout_need_return(lo, lseg)) { nfs4_stateid stateid; enum pnfs_iomode iomode; + bool send; stateid = lo->plh_stateid; iomode = lo->plh_return_iomode; - /* decreased in pnfs_send_layoutreturn() */ - lo->plh_block_lgets++; - lo->plh_return_iomode = 0; + send = pnfs_prepare_layoutreturn(lo); spin_unlock(&inode->i_lock); - pnfs_get_layout_hdr(lo); - - /* Send an async layoutreturn so we dont deadlock */ - pnfs_send_layoutreturn(lo, stateid, iomode, false); + if (send) { + /* Send an async layoutreturn so we dont deadlock */ + pnfs_send_layoutreturn(lo, stateid, iomode, false); + } } else spin_unlock(&inode->i_lock); } @@ -410,6 +422,10 @@ pnfs_put_lseg(struct pnfs_layout_segment *lseg) pnfs_layoutreturn_before_put_lseg(lseg, lo, inode); if (atomic_dec_and_lock(&lseg->pls_refcount, &inode->i_lock)) { + if (test_bit(NFS_LSEG_VALID, &lseg->pls_flags)) { + spin_unlock(&inode->i_lock); + return; + } pnfs_get_layout_hdr(lo); pnfs_layout_remove_lseg(lo, lseg); spin_unlock(&inode->i_lock); @@ -450,6 +466,8 @@ pnfs_put_lseg_locked(struct pnfs_layout_segment *lseg) test_bit(NFS_LSEG_VALID, &lseg->pls_flags)); if (atomic_dec_and_test(&lseg->pls_refcount)) { struct pnfs_layout_hdr *lo = lseg->pls_layout; + if (test_bit(NFS_LSEG_VALID, &lseg->pls_flags)) + return; pnfs_get_layout_hdr(lo); pnfs_layout_remove_lseg(lo, lseg); pnfs_free_lseg_async(lseg); @@ -923,6 +941,7 @@ void pnfs_clear_layoutreturn_waitbit(struct pnfs_layout_hdr *lo) clear_bit_unlock(NFS_LAYOUT_RETURN, &lo->plh_flags); smp_mb__after_atomic(); wake_up_bit(&lo->plh_flags, NFS_LAYOUT_RETURN); + rpc_wake_up(&NFS_SERVER(lo->plh_inode)->roc_rpcwaitq); } static int @@ -977,6 +996,7 @@ _pnfs_return_layout(struct inode *ino) LIST_HEAD(tmp_list); nfs4_stateid stateid; int status = 0, empty; + bool send; dprintk("NFS: %s for inode %lu\n", __func__, ino->i_ino); @@ -1006,17 +1026,18 @@ _pnfs_return_layout(struct inode *ino) /* Don't send a LAYOUTRETURN if list was initially empty */ if (empty) { spin_unlock(&ino->i_lock); - pnfs_put_layout_hdr(lo); dprintk("NFS: %s no layout segments to return\n", __func__); - goto out; + goto out_put_layout_hdr; } set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags); - lo->plh_block_lgets++; + send = pnfs_prepare_layoutreturn(lo); spin_unlock(&ino->i_lock); pnfs_free_lseg_list(&tmp_list); - - status = pnfs_send_layoutreturn(lo, stateid, IOMODE_ANY, true); + if (send) + status = pnfs_send_layoutreturn(lo, stateid, IOMODE_ANY, true); +out_put_layout_hdr: + pnfs_put_layout_hdr(lo); out: dprintk("<-- %s status: %d\n", __func__, status); return status; @@ -1096,13 +1117,9 @@ bool pnfs_roc(struct inode *ino) out_noroc: if (lo) { stateid = lo->plh_stateid; - layoutreturn = - test_and_clear_bit(NFS_LAYOUT_RETURN_BEFORE_CLOSE, - &lo->plh_flags); - if (layoutreturn) { - lo->plh_block_lgets++; - pnfs_get_layout_hdr(lo); - } + if (test_and_clear_bit(NFS_LAYOUT_RETURN_BEFORE_CLOSE, + &lo->plh_flags)) + layoutreturn = pnfs_prepare_layoutreturn(lo); } spin_unlock(&ino->i_lock); if (layoutreturn) { @@ -1145,15 +1162,18 @@ bool pnfs_roc_drain(struct inode *ino, u32 *barrier, struct rpc_task *task) struct pnfs_layout_segment *lseg; nfs4_stateid stateid; u32 current_seqid; - bool found = false, layoutreturn = false; + bool layoutreturn = false; spin_lock(&ino->i_lock); - list_for_each_entry(lseg, &nfsi->layout->plh_segs, pls_list) - if (test_bit(NFS_LSEG_ROC, &lseg->pls_flags)) { - rpc_sleep_on(&NFS_SERVER(ino)->roc_rpcwaitq, task, NULL); - found = true; - goto out; - } + list_for_each_entry(lseg, &nfsi->layout->plh_segs, pls_list) { + if (!test_bit(NFS_LSEG_ROC, &lseg->pls_flags)) + continue; + if (test_bit(NFS_LSEG_VALID, &lseg->pls_flags)) + continue; + rpc_sleep_on(&NFS_SERVER(ino)->roc_rpcwaitq, task, NULL); + spin_unlock(&ino->i_lock); + return true; + } lo = nfsi->layout; current_seqid = be32_to_cpu(lo->plh_stateid.seqid); @@ -1161,23 +1181,19 @@ bool pnfs_roc_drain(struct inode *ino, u32 *barrier, struct rpc_task *task) * a barrier, we choose the worst-case barrier. */ *barrier = current_seqid + atomic_read(&lo->plh_outstanding); -out: - if (!found) { - stateid = lo->plh_stateid; - layoutreturn = - test_and_clear_bit(NFS_LAYOUT_RETURN_BEFORE_CLOSE, - &lo->plh_flags); - if (layoutreturn) { - lo->plh_block_lgets++; - pnfs_get_layout_hdr(lo); - } - } + stateid = lo->plh_stateid; + if (test_and_clear_bit(NFS_LAYOUT_RETURN_BEFORE_CLOSE, + &lo->plh_flags)) + layoutreturn = pnfs_prepare_layoutreturn(lo); + if (test_bit(NFS_LAYOUT_RETURN, &lo->plh_flags)) + rpc_sleep_on(&NFS_SERVER(ino)->roc_rpcwaitq, task, NULL); + spin_unlock(&ino->i_lock); if (layoutreturn) { - rpc_sleep_on(&NFS_SERVER(ino)->roc_rpcwaitq, task, NULL); pnfs_send_layoutreturn(lo, stateid, IOMODE_ANY, false); + return true; } - return found; + return false; } /* @@ -1694,7 +1710,6 @@ void pnfs_error_mark_layout_for_return(struct inode *inode, spin_lock(&inode->i_lock); /* set failure bit so that pnfs path will be retried later */ pnfs_layout_set_fail_bit(lo, iomode); - set_bit(NFS_LAYOUT_RETURN, &lo->plh_flags); if (lo->plh_return_iomode == 0) lo->plh_return_iomode = range.iomode; else if (lo->plh_return_iomode != range.iomode) @@ -2206,13 +2221,12 @@ pnfs_layoutcommit_inode(struct inode *inode, bool sync) if (ld->prepare_layoutcommit) { status = ld->prepare_layoutcommit(&data->args); if (status) { + put_rpccred(data->cred); spin_lock(&inode->i_lock); set_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags); if (end_pos > nfsi->layout->plh_lwb) nfsi->layout->plh_lwb = end_pos; - spin_unlock(&inode->i_lock); - put_rpccred(data->cred); - goto clear_layoutcommitting; + goto out_unlock; } } @@ -2250,3 +2264,63 @@ struct nfs4_threshold *pnfs_mdsthreshold_alloc(void) } return thp; } + +#if IS_ENABLED(CONFIG_NFS_V4_2) +int +pnfs_report_layoutstat(struct inode *inode) +{ + struct pnfs_layoutdriver_type *ld = NFS_SERVER(inode)->pnfs_curr_ld; + struct nfs_server *server = NFS_SERVER(inode); + struct nfs_inode *nfsi = NFS_I(inode); + struct nfs42_layoutstat_data *data; + struct pnfs_layout_hdr *hdr; + int status = 0; + + if (!pnfs_enabled_sb(server) || !ld->prepare_layoutstats) + goto out; + + if (!nfs_server_capable(inode, NFS_CAP_LAYOUTSTATS)) + goto out; + + if (test_and_set_bit(NFS_INO_LAYOUTSTATS, &nfsi->flags)) + goto out; + + spin_lock(&inode->i_lock); + if (!NFS_I(inode)->layout) { + spin_unlock(&inode->i_lock); + goto out; + } + hdr = NFS_I(inode)->layout; + pnfs_get_layout_hdr(hdr); + spin_unlock(&inode->i_lock); + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto out_put; + } + + data->args.fh = NFS_FH(inode); + data->args.inode = inode; + nfs4_stateid_copy(&data->args.stateid, &hdr->plh_stateid); + status = ld->prepare_layoutstats(&data->args); + if (status) + goto out_free; + + status = nfs42_proc_layoutstats_generic(NFS_SERVER(inode), data); + +out: + dprintk("%s returns %d\n", __func__, status); + return status; + +out_free: + kfree(data); +out_put: + pnfs_put_layout_hdr(hdr); + smp_mb__before_atomic(); + clear_bit(NFS_INO_LAYOUTSTATS, &nfsi->flags); + smp_mb__after_atomic(); + goto out; +} +EXPORT_SYMBOL_GPL(pnfs_report_layoutstat); +#endif diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 1e6308f82..3e6ab7bfb 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -178,6 +178,8 @@ struct pnfs_layoutdriver_type { void (*encode_layoutcommit) (struct pnfs_layout_hdr *lo, struct xdr_stream *xdr, const struct nfs4_layoutcommit_args *args); + int (*prepare_layoutstats) (struct nfs42_layoutstat_args *args); + void (*cleanup_layoutstats) (struct nfs42_layoutstat_data *data); }; struct pnfs_layout_hdr { @@ -290,7 +292,6 @@ int pnfs_write_done_resend_to_mds(struct nfs_pgio_header *); struct nfs4_threshold *pnfs_mdsthreshold_alloc(void); void pnfs_error_mark_layout_for_return(struct inode *inode, struct pnfs_layout_segment *lseg); - /* nfs4_deviceid_flags */ enum { NFS_DEVICEID_INVALID = 0, /* set when MDS clientid recalled */ @@ -689,4 +690,14 @@ static inline void nfs4_pnfs_v3_ds_connect_unload(void) #endif /* CONFIG_NFS_V4_1 */ +#if IS_ENABLED(CONFIG_NFS_V4_2) +int pnfs_report_layoutstat(struct inode *inode); +#else +static inline int +pnfs_report_layoutstat(struct inode *inode) +{ + return 0; +} +#endif + #endif /* FS_NFS_PNFS_H */ diff --git a/fs/nfs/super.c b/fs/nfs/super.c index f175b833b..aa62004f1 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2847,7 +2847,7 @@ static int param_set_portnr(const char *val, const struct kernel_param *kp) *((unsigned int *)kp->arg) = num; return 0; } -static struct kernel_param_ops param_ops_portnr = { +static const struct kernel_param_ops param_ops_portnr = { .set = param_set_portnr, .get = param_get_uint, }; diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index 2d5620065..b6de433da 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -20,7 +20,6 @@ #include <linux/stat.h> #include <linux/mm.h> #include <linux/string.h> -#include <linux/namei.h> /* Symlink caching in the page cache is even more simplistic * and straight-forward than readdir caching. @@ -43,7 +42,7 @@ error: return -EIO; } -static void *nfs_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *nfs_follow_link(struct dentry *dentry, void **cookie) { struct inode *inode = d_inode(dentry); struct page *page; @@ -51,19 +50,13 @@ static void *nfs_follow_link(struct dentry *dentry, struct nameidata *nd) err = ERR_PTR(nfs_revalidate_mapping(inode, inode->i_mapping)); if (err) - goto read_failed; + return err; page = read_cache_page(&inode->i_data, 0, (filler_t *)nfs_symlink_filler, inode); - if (IS_ERR(page)) { - err = page; - goto read_failed; - } - nd_set_link(nd, kmap(page)); - return page; - -read_failed: - nd_set_link(nd, err); - return NULL; + if (IS_ERR(page)) + return ERR_CAST(page); + *cookie = page; + return kmap(page); } /* diff --git a/fs/nfs/write.c b/fs/nfs/write.c index daf355642..75a35a1af 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -853,7 +853,8 @@ static void nfs_clear_page_commit(struct page *page) { dec_zone_page_state(page, NR_UNSTABLE_NFS); - dec_bdi_stat(inode_to_bdi(page_file_mapping(page)->host), BDI_RECLAIMABLE); + dec_wb_stat(&inode_to_bdi(page_file_mapping(page)->host)->wb, + WB_RECLAIMABLE); } /* Called holding inode (/cinfo) lock */ @@ -1348,11 +1349,6 @@ void nfs_commit_prepare(struct rpc_task *task, void *calldata) NFS_PROTO(data->inode)->commit_rpc_prepare(task, data); } -static void nfs_writeback_release_common(struct nfs_pgio_header *hdr) -{ - /* do nothing! */ -} - /* * Special version of should_remove_suid() that ignores capabilities. */ @@ -1383,24 +1379,27 @@ static void nfs_writeback_check_extend(struct nfs_pgio_header *hdr, { struct nfs_pgio_args *argp = &hdr->args; struct nfs_pgio_res *resp = &hdr->res; + u64 size = argp->offset + resp->count; if (!(fattr->valid & NFS_ATTR_FATTR_SIZE)) + fattr->size = size; + if (nfs_size_to_loff_t(fattr->size) < i_size_read(hdr->inode)) { + fattr->valid &= ~NFS_ATTR_FATTR_SIZE; return; - if (argp->offset + resp->count != fattr->size) - return; - if (nfs_size_to_loff_t(fattr->size) < i_size_read(hdr->inode)) + } + if (size != fattr->size) return; /* Set attribute barrier */ nfs_fattr_set_barrier(fattr); + /* ...and update size */ + fattr->valid |= NFS_ATTR_FATTR_SIZE; } void nfs_writeback_update_inode(struct nfs_pgio_header *hdr) { - struct nfs_fattr *fattr = hdr->res.fattr; + struct nfs_fattr *fattr = &hdr->fattr; struct inode *inode = hdr->inode; - if (fattr == NULL) - return; spin_lock(&inode->i_lock); nfs_writeback_check_extend(hdr, fattr); nfs_post_op_update_inode_force_wcc_locked(inode, fattr); @@ -1556,7 +1555,7 @@ int nfs_initiate_commit(struct rpc_clnt *clnt, struct nfs_commit_data *data, /* Set up the initial task struct. */ nfs_ops->commit_setup(data, &msg); - dprintk("NFS: %5u initiated commit call\n", data->task.tk_pid); + dprintk("NFS: initiated commit call\n"); nfs4_state_protect(NFS_SERVER(data->inode)->nfs_client, NFS_SP4_MACH_CRED_COMMIT, &task_setup_data.rpc_client, &msg); @@ -2013,7 +2012,6 @@ static const struct nfs_rw_ops nfs_rw_write_ops = { .rw_mode = FMODE_WRITE, .rw_alloc_header = nfs_writehdr_alloc, .rw_free_header = nfs_writehdr_free, - .rw_release = nfs_writeback_release_common, .rw_done = nfs_writeback_done, .rw_result = nfs_writeback_result, .rw_initiate = nfs_initiate_write, diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index e4b2b4322..f6e7cbaba 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c @@ -805,7 +805,7 @@ encode_entry_baggage(struct nfsd3_readdirres *cd, __be32 *p, const char *name, static __be32 compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp, - const char *name, int namlen) + const char *name, int namlen, u64 ino) { struct svc_export *exp; struct dentry *dparent, *dchild; @@ -830,19 +830,21 @@ compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp, goto out; if (d_really_is_negative(dchild)) goto out; + if (dchild->d_inode->i_ino != ino) + goto out; rv = fh_compose(fhp, exp, dchild, &cd->fh); out: dput(dchild); return rv; } -static __be32 *encode_entryplus_baggage(struct nfsd3_readdirres *cd, __be32 *p, const char *name, int namlen) +static __be32 *encode_entryplus_baggage(struct nfsd3_readdirres *cd, __be32 *p, const char *name, int namlen, u64 ino) { struct svc_fh *fh = &cd->scratch; __be32 err; fh_init(fh, NFS3_FHSIZE); - err = compose_entry_fh(cd, fh, name, namlen); + err = compose_entry_fh(cd, fh, name, namlen, ino); if (err) { *p++ = 0; *p++ = 0; @@ -927,7 +929,7 @@ encode_entry(struct readdir_cd *ccd, const char *name, int namlen, p = encode_entry_baggage(cd, p, name, namlen, ino); if (plus) - p = encode_entryplus_baggage(cd, p, name, namlen); + p = encode_entryplus_baggage(cd, p, name, namlen, ino); num_entry_words = p - cd->buffer; } else if (*(page+1) != NULL) { /* temporarily encode entry into next page, then move back to @@ -941,7 +943,7 @@ encode_entry(struct readdir_cd *ccd, const char *name, int namlen, p1 = encode_entry_baggage(cd, p1, name, namlen, ino); if (plus) - p1 = encode_entryplus_baggage(cd, p1, name, namlen); + p1 = encode_entryplus_baggage(cd, p1, name, namlen, ino); /* determine entry word length and lengths to go in pages */ num_entry_words = p1 - tmp; diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c index 67242bf7c..eb5accf1b 100644 --- a/fs/nfsd/nfs4acl.c +++ b/fs/nfsd/nfs4acl.c @@ -52,10 +52,6 @@ #define NFS4_ANYONE_MODE (NFS4_ACE_READ_ATTRIBUTES | NFS4_ACE_READ_ACL | NFS4_ACE_SYNCHRONIZE) #define NFS4_OWNER_MODE (NFS4_ACE_WRITE_ATTRIBUTES | NFS4_ACE_WRITE_ACL) -/* We don't support these bits; insist they be neither allowed nor denied */ -#define NFS4_MASK_UNSUPP (NFS4_ACE_DELETE | NFS4_ACE_WRITE_OWNER \ - | NFS4_ACE_READ_NAMED_ATTRS | NFS4_ACE_WRITE_NAMED_ATTRS) - /* flags used to simulate posix default ACLs */ #define NFS4_INHERITANCE_FLAGS (NFS4_ACE_FILE_INHERIT_ACE \ | NFS4_ACE_DIRECTORY_INHERIT_ACE) @@ -64,9 +60,6 @@ | NFS4_ACE_INHERIT_ONLY_ACE \ | NFS4_ACE_IDENTIFIER_GROUP) -#define MASK_EQUAL(mask1, mask2) \ - ( ((mask1) & NFS4_ACE_MASK_ALL) == ((mask2) & NFS4_ACE_MASK_ALL) ) - static u32 mask_from_posix(unsigned short perm, unsigned int flags) { @@ -126,11 +119,6 @@ low_mode_from_nfs4(u32 perm, unsigned short *mode, unsigned int flags) *mode |= ACL_EXECUTE; } -struct ace_container { - struct nfs4_ace *ace; - struct list_head ace_l; -}; - static short ace2type(struct nfs4_ace *); static void _posix_to_nfsv4_one(struct posix_acl *, struct nfs4_acl *, unsigned int); @@ -384,7 +372,6 @@ pace_gt(struct posix_acl_entry *pace1, struct posix_acl_entry *pace2) static void sort_pacl_range(struct posix_acl *pacl, int start, int end) { int sorted = 0, i; - struct posix_acl_entry tmp; /* We just do a bubble sort; easy to do in place, and we're not * expecting acl's to be long enough to justify anything more. */ @@ -394,9 +381,8 @@ sort_pacl_range(struct posix_acl *pacl, int start, int end) { if (pace_gt(&pacl->a_entries[i], &pacl->a_entries[i+1])) { sorted = 0; - tmp = pacl->a_entries[i]; - pacl->a_entries[i] = pacl->a_entries[i+1]; - pacl->a_entries[i+1] = tmp; + swap(pacl->a_entries[i], + pacl->a_entries[i + 1]); } } } diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 5694cfb7a..a49201835 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -455,6 +455,7 @@ static int decode_cb_sequence4res(struct xdr_stream *xdr, if (unlikely(status || cb->cb_status)) return status; + cb->cb_update_seq_nr = true; return decode_cb_sequence4resok(xdr, cb); } @@ -875,6 +876,8 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata) u32 minorversion = clp->cl_minorversion; cb->cb_minorversion = minorversion; + cb->cb_update_seq_nr = false; + cb->cb_status = 0; if (minorversion) { if (!nfsd41_cb_get_slot(clp, task)) return; @@ -891,9 +894,16 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata) clp->cl_minorversion); if (clp->cl_minorversion) { - /* No need for lock, access serialized in nfsd4_cb_prepare */ - if (!task->tk_status) + /* + * No need for lock, access serialized in nfsd4_cb_prepare + * + * RFC5661 20.9.3 + * If CB_SEQUENCE returns an error, then the state of the slot + * (sequence ID, cached reply) MUST NOT change. + */ + if (cb->cb_update_seq_nr) ++clp->cl_cb_session->se_cb_seq_nr; + clear_bit(0, &clp->cl_cb_slot_busy); rpc_wake_up_next(&clp->cl_cb_waitq); dprintk("%s: freed slot, new seqid=%d\n", __func__, @@ -1090,6 +1100,7 @@ void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp, cb->cb_ops = ops; INIT_WORK(&cb->cb_work, nfsd4_run_cb_work); cb->cb_status = 0; + cb->cb_update_seq_nr = false; cb->cb_need_restart = false; } diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c index 6904213a4..ebf90e487 100644 --- a/fs/nfsd/nfs4layouts.c +++ b/fs/nfsd/nfs4layouts.c @@ -212,6 +212,7 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate, BUG_ON(!ls->ls_file); if (nfsd4_layout_setlease(ls)) { + fput(ls->ls_file); put_nfs4_file(fp); kmem_cache_free(nfs4_layout_stateid_cache, ls); return NULL; diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 864e2003e..90cfda753 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -760,8 +760,6 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, { __be32 status; - /* no need to check permission - this will be done in nfsd_read() */ - read->rd_filp = NULL; if (read->rd_offset >= OFFSET_MAX) return nfserr_inval; @@ -778,9 +776,9 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags); /* check stateid */ - if ((status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), - cstate, &read->rd_stateid, - RD_STATE, &read->rd_filp))) { + status = nfs4_preprocess_stateid_op(rqstp, cstate, &read->rd_stateid, + RD_STATE, &read->rd_filp, &read->rd_tmp_file); + if (status) { dprintk("NFSD: nfsd4_read: couldn't process stateid!\n"); goto out; } @@ -924,8 +922,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, int err; if (setattr->sa_iattr.ia_valid & ATTR_SIZE) { - status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), cstate, - &setattr->sa_stateid, WR_STATE, NULL); + status = nfs4_preprocess_stateid_op(rqstp, cstate, + &setattr->sa_stateid, WR_STATE, NULL, NULL); if (status) { dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n"); return status; @@ -986,13 +984,11 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, unsigned long cnt; int nvecs; - /* no need to check permission - this will be done in nfsd_write() */ - if (write->wr_offset >= OFFSET_MAX) return nfserr_inval; - status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), - cstate, stateid, WR_STATE, &filp); + status = nfs4_preprocess_stateid_op(rqstp, cstate, stateid, WR_STATE, + &filp, NULL); if (status) { dprintk("NFSD: nfsd4_write: couldn't process stateid!\n"); return status; @@ -1005,11 +1001,10 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nvecs = fill_in_write_vector(rqstp->rq_vec, write); WARN_ON_ONCE(nvecs > ARRAY_SIZE(rqstp->rq_vec)); - status = nfsd_write(rqstp, &cstate->current_fh, filp, - write->wr_offset, rqstp->rq_vec, nvecs, - &cnt, &write->wr_how_written); - if (filp) - fput(filp); + status = nfsd_vfs_write(rqstp, &cstate->current_fh, filp, + write->wr_offset, rqstp->rq_vec, nvecs, &cnt, + &write->wr_how_written); + fput(filp); write->wr_bytes_written = cnt; @@ -1023,15 +1018,13 @@ nfsd4_fallocate(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, __be32 status = nfserr_notsupp; struct file *file; - status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), cstate, + status = nfs4_preprocess_stateid_op(rqstp, cstate, &fallocate->falloc_stateid, - WR_STATE, &file); + WR_STATE, &file, NULL); if (status != nfs_ok) { dprintk("NFSD: nfsd4_fallocate: couldn't process stateid!\n"); return status; } - if (!file) - return nfserr_bad_stateid; status = nfsd4_vfs_fallocate(rqstp, &cstate->current_fh, file, fallocate->falloc_offset, @@ -1064,15 +1057,13 @@ nfsd4_seek(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, __be32 status; struct file *file; - status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), cstate, + status = nfs4_preprocess_stateid_op(rqstp, cstate, &seek->seek_stateid, - RD_STATE, &file); + RD_STATE, &file, NULL); if (status) { dprintk("NFSD: nfsd4_seek: couldn't process stateid!\n"); return status; } - if (!file) - return nfserr_bad_stateid; switch (seek->seek_whence) { case NFS4_CONTENT_DATA: @@ -1732,10 +1723,6 @@ encode_op: be32_to_cpu(status)); nfsd4_cstate_clear_replay(cstate); - /* XXX Ugh, we need to get rid of this kind of special case: */ - if (op->opnum == OP_READ && op->u.read.rd_filp) - fput(op->u.read.rd_filp); - nfsd4_increment_op_stats(op->opnum); } diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 6e13504f7..95202719a 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3861,7 +3861,7 @@ static __be32 nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *cur_fh, struct nfs4_ol_stateid *stp, struct nfsd4_open *open) { __be32 status; - unsigned char old_deny_bmap; + unsigned char old_deny_bmap = stp->st_deny_bmap; if (!test_access(open->op_share_access, stp)) return nfs4_get_vfs_file(rqstp, fp, cur_fh, stp, open); @@ -3870,7 +3870,6 @@ nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *c spin_lock(&fp->fi_lock); status = nfs4_file_check_deny(fp, open->op_share_deny); if (status == nfs_ok) { - old_deny_bmap = stp->st_deny_bmap; set_deny(open->op_share_deny, stp); fp->fi_share_deny |= (open->op_share_deny & NFS4_SHARE_DENY_BOTH); @@ -4577,6 +4576,9 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate, static struct file * nfs4_find_file(struct nfs4_stid *s, int flags) { + if (!s) + return NULL; + switch (s->sc_type) { case NFS4_DELEG_STID: if (WARN_ON_ONCE(!s->sc_file->fi_deleg_file)) @@ -4605,27 +4607,63 @@ nfs4_check_olstateid(struct svc_fh *fhp, struct nfs4_ol_stateid *ols, int flags) return nfs4_check_openmode(ols, flags); } +static __be32 +nfs4_check_file(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfs4_stid *s, + struct file **filpp, bool *tmp_file, int flags) +{ + int acc = (flags & RD_STATE) ? NFSD_MAY_READ : NFSD_MAY_WRITE; + struct file *file; + __be32 status; + + file = nfs4_find_file(s, flags); + if (file) { + status = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry, + acc | NFSD_MAY_OWNER_OVERRIDE); + if (status) { + fput(file); + return status; + } + + *filpp = file; + } else { + status = nfsd_open(rqstp, fhp, S_IFREG, acc, filpp); + if (status) + return status; + + if (tmp_file) + *tmp_file = true; + } + + return 0; +} + /* * Checks for stateid operations */ __be32 -nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate, - stateid_t *stateid, int flags, struct file **filpp) +nfs4_preprocess_stateid_op(struct svc_rqst *rqstp, + struct nfsd4_compound_state *cstate, stateid_t *stateid, + int flags, struct file **filpp, bool *tmp_file) { struct svc_fh *fhp = &cstate->current_fh; struct inode *ino = d_inode(fhp->fh_dentry); + struct net *net = SVC_NET(rqstp); struct nfsd_net *nn = net_generic(net, nfsd_net_id); - struct nfs4_stid *s; + struct nfs4_stid *s = NULL; __be32 status; if (filpp) *filpp = NULL; + if (tmp_file) + *tmp_file = false; if (grace_disallows_io(net, ino)) return nfserr_grace; - if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) - return check_special_stateids(net, fhp, stateid, flags); + if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) { + status = check_special_stateids(net, fhp, stateid, flags); + goto done; + } status = nfsd4_lookup_stateid(cstate, stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, @@ -4653,13 +4691,12 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate, goto out; status = nfs4_check_fh(fhp, s); - if (!status && filpp) { - *filpp = nfs4_find_file(s, flags); - if (!*filpp) - status = nfserr_serverfault; - } +done: + if (!status && filpp) + status = nfs4_check_file(rqstp, fhp, s, filpp, tmp_file, flags); out: - nfs4_put_stid(s); + if (s) + nfs4_put_stid(s); return status; } @@ -5512,7 +5549,7 @@ static __be32 nfsd_test_lock(struct svc_rqst *rqstp, struct svc_fh *fhp, struct __be32 err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_READ, &file); if (!err) { err = nfserrno(vfs_test_lock(file, lock)); - nfsd_close(file); + fput(file); } return err; } diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index d4d84451e..75e0563c0 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -33,6 +33,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include <linux/file.h> #include <linux/slab.h> #include <linux/namei.h> #include <linux/statfs.h> @@ -2229,7 +2230,6 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp, u32 rdattr_err = 0; __be32 status; int err; - int aclsupport = 0; struct nfs4_acl *acl = NULL; void *context = NULL; int contextlen; @@ -2275,19 +2275,15 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp, goto out; fhp = tempfh; } - if (bmval0 & (FATTR4_WORD0_ACL | FATTR4_WORD0_ACLSUPPORT - | FATTR4_WORD0_SUPPORTED_ATTRS)) { + if (bmval0 & FATTR4_WORD0_ACL) { err = nfsd4_get_nfs4_acl(rqstp, dentry, &acl); - aclsupport = (err == 0); - if (bmval0 & FATTR4_WORD0_ACL) { - if (err == -EOPNOTSUPP) - bmval0 &= ~FATTR4_WORD0_ACL; - else if (err == -EINVAL) { - status = nfserr_attrnotsupp; - goto out; - } else if (err != 0) - goto out_nfserr; - } + if (err == -EOPNOTSUPP) + bmval0 &= ~FATTR4_WORD0_ACL; + else if (err == -EINVAL) { + status = nfserr_attrnotsupp; + goto out; + } else if (err != 0) + goto out_nfserr; } #ifdef CONFIG_NFSD_V4_SECURITY_LABEL @@ -2339,7 +2335,7 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp, u32 word1 = nfsd_suppattrs1(minorversion); u32 word2 = nfsd_suppattrs2(minorversion); - if (!aclsupport) + if (!IS_POSIXACL(dentry->d_inode)) word0 &= ~FATTR4_WORD0_ACL; if (!contextsupport) word2 &= ~FATTR4_WORD2_SECURITY_LABEL; @@ -2487,7 +2483,7 @@ out_acl: p = xdr_reserve_space(xdr, 4); if (!p) goto out_resource; - *p++ = cpu_to_be32(aclsupport ? + *p++ = cpu_to_be32(IS_POSIXACL(dentry->d_inode) ? ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0); } if (bmval0 & FATTR4_WORD0_CANSETTIME) { @@ -3423,52 +3419,51 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr, unsigned long maxcount; struct xdr_stream *xdr = &resp->xdr; struct file *file = read->rd_filp; - struct svc_fh *fhp = read->rd_fhp; int starting_len = xdr->buf->len; - struct raparms *ra; + struct raparms *ra = NULL; __be32 *p; - __be32 err; if (nfserr) - return nfserr; + goto out; p = xdr_reserve_space(xdr, 8); /* eof flag and byte count */ if (!p) { WARN_ON_ONCE(test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags)); - return nfserr_resource; + nfserr = nfserr_resource; + goto out; } - if (resp->xdr.buf->page_len && test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags)) { + if (resp->xdr.buf->page_len && + test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags)) { WARN_ON_ONCE(1); - return nfserr_resource; + nfserr = nfserr_resource; + goto out; } xdr_commit_encode(xdr); maxcount = svc_max_payload(resp->rqstp); - maxcount = min_t(unsigned long, maxcount, (xdr->buf->buflen - xdr->buf->len)); + maxcount = min_t(unsigned long, maxcount, + (xdr->buf->buflen - xdr->buf->len)); maxcount = min_t(unsigned long, maxcount, read->rd_length); - if (read->rd_filp) - err = nfsd_permission(resp->rqstp, fhp->fh_export, - fhp->fh_dentry, - NFSD_MAY_READ|NFSD_MAY_OWNER_OVERRIDE); - else - err = nfsd_get_tmp_read_open(resp->rqstp, read->rd_fhp, - &file, &ra); - if (err) - goto err_truncate; + if (read->rd_tmp_file) + ra = nfsd_init_raparms(file); - if (file->f_op->splice_read && test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags)) - err = nfsd4_encode_splice_read(resp, read, file, maxcount); + if (file->f_op->splice_read && + test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags)) + nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount); else - err = nfsd4_encode_readv(resp, read, file, maxcount); + nfserr = nfsd4_encode_readv(resp, read, file, maxcount); - if (!read->rd_filp) - nfsd_put_tmp_read_open(file, ra); + if (ra) + nfsd_put_raparams(file, ra); -err_truncate: - if (err) + if (nfserr) xdr_truncate_encode(xdr, starting_len); - return err; + +out: + if (file) + fput(file); + return nfserr; } static __be32 diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index aecbcd34d..4cd78ef4c 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -59,13 +59,61 @@ static __be32 nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp, struct nfsd_attrstat *resp) { + struct iattr *iap = &argp->attrs; + struct svc_fh *fhp; __be32 nfserr; + dprintk("nfsd: SETATTR %s, valid=%x, size=%ld\n", SVCFH_fmt(&argp->fh), argp->attrs.ia_valid, (long) argp->attrs.ia_size); - fh_copy(&resp->fh, &argp->fh); - nfserr = nfsd_setattr(rqstp, &resp->fh, &argp->attrs,0, (time_t)0); + fhp = fh_copy(&resp->fh, &argp->fh); + + /* + * NFSv2 does not differentiate between "set-[ac]time-to-now" + * which only requires access, and "set-[ac]time-to-X" which + * requires ownership. + * So if it looks like it might be "set both to the same time which + * is close to now", and if inode_change_ok fails, then we + * convert to "set to now" instead of "set to explicit time" + * + * We only call inode_change_ok as the last test as technically + * it is not an interface that we should be using. + */ +#define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET) +#define MAX_TOUCH_TIME_ERROR (30*60) + if ((iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET && + iap->ia_mtime.tv_sec == iap->ia_atime.tv_sec) { + /* + * Looks probable. + * + * Now just make sure time is in the right ballpark. + * Solaris, at least, doesn't seem to care what the time + * request is. We require it be within 30 minutes of now. + */ + time_t delta = iap->ia_atime.tv_sec - get_seconds(); + struct inode *inode; + + nfserr = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP); + if (nfserr) + goto done; + inode = d_inode(fhp->fh_dentry); + + if (delta < 0) + delta = -delta; + if (delta < MAX_TOUCH_TIME_ERROR && + inode_change_ok(inode, iap) != 0) { + /* + * Turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME. + * This will cause notify_change to set these times + * to "now" + */ + iap->ia_valid &= ~BOTH_TIME_SET; + } + } + + nfserr = nfsd_setattr(rqstp, fhp, iap, 0, (time_t)0); +done: return nfsd_return_attrs(nfserr, resp); } diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index dbc4f85a5..4874ce515 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -68,6 +68,7 @@ struct nfsd4_callback { struct nfsd4_callback_ops *cb_ops; struct work_struct cb_work; int cb_status; + bool cb_update_seq_nr; bool cb_need_restart; }; @@ -582,9 +583,9 @@ enum nfsd4_cb_op { struct nfsd4_compound_state; struct nfsd_net; -extern __be32 nfs4_preprocess_stateid_op(struct net *net, - struct nfsd4_compound_state *cstate, - stateid_t *stateid, int flags, struct file **filp); +extern __be32 nfs4_preprocess_stateid_op(struct svc_rqst *rqstp, + struct nfsd4_compound_state *cstate, stateid_t *stateid, + int flags, struct file **filp, bool *tmp_file); __be32 nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate, stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s, struct nfsd_net *nn); diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 84d770be0..b5e077a6e 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -302,42 +302,6 @@ commit_metadata(struct svc_fh *fhp) static void nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap) { - /* - * NFSv2 does not differentiate between "set-[ac]time-to-now" - * which only requires access, and "set-[ac]time-to-X" which - * requires ownership. - * So if it looks like it might be "set both to the same time which - * is close to now", and if inode_change_ok fails, then we - * convert to "set to now" instead of "set to explicit time" - * - * We only call inode_change_ok as the last test as technically - * it is not an interface that we should be using. - */ -#define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET) -#define MAX_TOUCH_TIME_ERROR (30*60) - if ((iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET && - iap->ia_mtime.tv_sec == iap->ia_atime.tv_sec) { - /* - * Looks probable. - * - * Now just make sure time is in the right ballpark. - * Solaris, at least, doesn't seem to care what the time - * request is. We require it be within 30 minutes of now. - */ - time_t delta = iap->ia_atime.tv_sec - get_seconds(); - if (delta < 0) - delta = -delta; - if (delta < MAX_TOUCH_TIME_ERROR && - inode_change_ok(inode, iap) != 0) { - /* - * Turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME. - * This will cause notify_change to set these times - * to "now" - */ - iap->ia_valid &= ~BOTH_TIME_SET; - } - } - /* sanitize the mode change */ if (iap->ia_valid & ATTR_MODE) { iap->ia_mode &= S_IALLUGO; @@ -538,16 +502,11 @@ __be32 nfsd4_vfs_fallocate(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, loff_t offset, loff_t len, int flags) { - __be32 err; int error; if (!S_ISREG(file_inode(file)->i_mode)) return nfserr_inval; - err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry, NFSD_MAY_WRITE); - if (err) - return err; - error = vfs_fallocate(file, flags, offset, len); if (!error) error = commit_metadata(fhp); @@ -744,7 +703,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, host_err = ima_file_check(file, may_flags, 0); if (host_err) { - nfsd_close(file); + fput(file); goto out_nfserr; } @@ -761,23 +720,12 @@ out: return err; } -/* - * Close a file. - */ -void -nfsd_close(struct file *filp) -{ - fput(filp); -} - -/* - * Obtain the readahead parameters for the file - * specified by (dev, ino). - */ - -static inline struct raparms * -nfsd_get_raparms(dev_t dev, ino_t ino) +struct raparms * +nfsd_init_raparms(struct file *file) { + struct inode *inode = file_inode(file); + dev_t dev = inode->i_sb->s_dev; + ino_t ino = inode->i_ino; struct raparms *ra, **rap, **frap = NULL; int depth = 0; unsigned int hash; @@ -814,9 +762,23 @@ found: ra->p_count++; nfsdstats.ra_depth[depth*10/nfsdstats.ra_size]++; spin_unlock(&rab->pb_lock); + + if (ra->p_set) + file->f_ra = ra->p_ra; return ra; } +void nfsd_put_raparams(struct file *file, struct raparms *ra) +{ + struct raparm_hbucket *rab = &raparm_hash[ra->p_hindex]; + + spin_lock(&rab->pb_lock); + ra->p_ra = file->f_ra; + ra->p_set = 1; + ra->p_count--; + spin_unlock(&rab->pb_lock); +} + /* * Grab and keep cached pages associated with a file in the svc_rqst * so that they can be passed to the network sendmsg/sendpage routines @@ -945,7 +907,7 @@ static int wait_for_concurrent_writes(struct file *file) return err; } -static __be32 +__be32 nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, loff_t offset, struct kvec *vec, int vlen, unsigned long *cnt, int *stablep) @@ -1009,40 +971,6 @@ out_nfserr: return err; } -__be32 nfsd_get_tmp_read_open(struct svc_rqst *rqstp, struct svc_fh *fhp, - struct file **file, struct raparms **ra) -{ - struct inode *inode; - __be32 err; - - err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_READ, file); - if (err) - return err; - - inode = file_inode(*file); - - /* Get readahead parameters */ - *ra = nfsd_get_raparms(inode->i_sb->s_dev, inode->i_ino); - - if (*ra && (*ra)->p_set) - (*file)->f_ra = (*ra)->p_ra; - return nfs_ok; -} - -void nfsd_put_tmp_read_open(struct file *file, struct raparms *ra) -{ - /* Write back readahead params */ - if (ra) { - struct raparm_hbucket *rab = &raparm_hash[ra->p_hindex]; - spin_lock(&rab->pb_lock); - ra->p_ra = file->f_ra; - ra->p_set = 1; - ra->p_count--; - spin_unlock(&rab->pb_lock); - } - nfsd_close(file); -} - /* * Read data from a file. count must contain the requested read count * on entry. On return, *count contains the number of bytes actually read. @@ -1055,13 +983,15 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct raparms *ra; __be32 err; - err = nfsd_get_tmp_read_open(rqstp, fhp, &file, &ra); + err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_READ, &file); if (err) return err; + ra = nfsd_init_raparms(file); err = nfsd_vfs_read(rqstp, file, offset, vec, vlen, count); - - nfsd_put_tmp_read_open(file, ra); + if (ra) + nfsd_put_raparams(file, ra); + fput(file); return err; } @@ -1093,7 +1023,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, if (cnt) err = nfsd_vfs_write(rqstp, fhp, file, offset, vec, vlen, cnt, stablep); - nfsd_close(file); + fput(file); } out: return err; @@ -1138,7 +1068,7 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, err = nfserr_notsupp; } - nfsd_close(file); + fput(file); out: return err; } @@ -1977,7 +1907,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp, if (err == nfserr_eof || err == nfserr_toosmall) err = nfs_ok; /* can still be found in ->err */ out_close: - nfsd_close(file); + fput(file); out: return err; } diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h index 2050cb016..5be875e3e 100644 --- a/fs/nfsd/vfs.h +++ b/fs/nfsd/vfs.h @@ -71,11 +71,7 @@ __be32 nfsd_commit(struct svc_rqst *, struct svc_fh *, #endif /* CONFIG_NFSD_V3 */ __be32 nfsd_open(struct svc_rqst *, struct svc_fh *, umode_t, int, struct file **); -void nfsd_close(struct file *); struct raparms; -__be32 nfsd_get_tmp_read_open(struct svc_rqst *, struct svc_fh *, - struct file **, struct raparms **); -void nfsd_put_tmp_read_open(struct file *, struct raparms *); __be32 nfsd_splice_read(struct svc_rqst *, struct file *, loff_t, unsigned long *); __be32 nfsd_readv(struct file *, loff_t, struct kvec *, int, @@ -84,6 +80,10 @@ __be32 nfsd_read(struct svc_rqst *, struct svc_fh *, loff_t, struct kvec *, int, unsigned long *); __be32 nfsd_write(struct svc_rqst *, struct svc_fh *,struct file *, loff_t, struct kvec *,int, unsigned long *, int *); +__be32 nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, + struct file *file, loff_t offset, + struct kvec *vec, int vlen, unsigned long *cnt, + int *stablep); __be32 nfsd_readlink(struct svc_rqst *, struct svc_fh *, char *, int *); __be32 nfsd_symlink(struct svc_rqst *, struct svc_fh *, @@ -104,6 +104,9 @@ __be32 nfsd_statfs(struct svc_rqst *, struct svc_fh *, __be32 nfsd_permission(struct svc_rqst *, struct svc_export *, struct dentry *, int); +struct raparms *nfsd_init_raparms(struct file *file); +void nfsd_put_raparams(struct file *file, struct raparms *ra); + static inline int fh_want_write(struct svc_fh *fh) { int ret = mnt_want_write(fh->fh_export->ex_path.mnt); diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 2f8c092be..9f991007a 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -273,6 +273,7 @@ struct nfsd4_read { u32 rd_length; /* request */ int rd_vlen; struct file *rd_filp; + bool rd_tmp_file; struct svc_rqst *rd_rqstp; /* response */ struct svc_fh * rd_fhp; /* response */ diff --git a/fs/nilfs2/dir.c b/fs/nilfs2/dir.c index 0ee0bed36..6b8b92b19 100644 --- a/fs/nilfs2/dir.c +++ b/fs/nilfs2/dir.c @@ -61,11 +61,6 @@ static inline void nilfs_put_page(struct page *page) page_cache_release(page); } -static inline unsigned long dir_pages(struct inode *inode) -{ - return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT; -} - /* * Return the offset into page `page_nr' of the last valid * byte in that page, plus one. diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 258d9fe25..4a73d6dff 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -307,31 +307,13 @@ static int nilfs_write_end(struct file *file, struct address_space *mapping, static ssize_t nilfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter, loff_t offset) { - struct file *file = iocb->ki_filp; - struct address_space *mapping = file->f_mapping; - struct inode *inode = file->f_mapping->host; - size_t count = iov_iter_count(iter); - ssize_t size; + struct inode *inode = file_inode(iocb->ki_filp); if (iov_iter_rw(iter) == WRITE) return 0; /* Needs synchronization with the cleaner */ - size = blockdev_direct_IO(iocb, inode, iter, offset, nilfs_get_block); - - /* - * In case of error extending write may have instantiated a few - * blocks outside i_size. Trim these off again. - */ - if (unlikely(iov_iter_rw(iter) == WRITE && size < 0)) { - loff_t isize = i_size_read(inode); - loff_t end = offset + count; - - if (end > isize) - nilfs_write_failed(mapping, end); - } - - return size; + return blockdev_direct_IO(iocb, inode, iter, offset, nilfs_get_block); } const struct address_space_operations nilfs_aops = { diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c index 9a20e513d..aba43811d 100644 --- a/fs/nilfs2/ioctl.c +++ b/fs/nilfs2/ioctl.c @@ -1369,7 +1369,6 @@ long nilfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case NILFS_IOCTL_SYNC: case NILFS_IOCTL_RESIZE: case NILFS_IOCTL_SET_ALLOC_RANGE: - case FITRIM: break; default: return -ENOIOCTLCMD; diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c index 22180836e..37dd6b05b 100644 --- a/fs/nilfs2/namei.c +++ b/fs/nilfs2/namei.c @@ -496,8 +496,7 @@ static struct dentry *nilfs_fh_to_dentry(struct super_block *sb, struct fid *fh, { struct nilfs_fid *fid = (struct nilfs_fid *)fh; - if ((fh_len != NILFS_FID_SIZE_NON_CONNECTABLE && - fh_len != NILFS_FID_SIZE_CONNECTABLE) || + if (fh_len < NILFS_FID_SIZE_NON_CONNECTABLE || (fh_type != FILEID_NILFS_WITH_PARENT && fh_type != FILEID_NILFS_WITHOUT_PARENT)) return NULL; @@ -510,7 +509,7 @@ static struct dentry *nilfs_fh_to_parent(struct super_block *sb, struct fid *fh, { struct nilfs_fid *fid = (struct nilfs_fid *)fh; - if (fh_len != NILFS_FID_SIZE_CONNECTABLE || + if (fh_len < NILFS_FID_SIZE_CONNECTABLE || fh_type != FILEID_NILFS_WITH_PARENT) return NULL; diff --git a/fs/nilfs2/segbuf.c b/fs/nilfs2/segbuf.c index dc3a9efda..42468e5ab 100644 --- a/fs/nilfs2/segbuf.c +++ b/fs/nilfs2/segbuf.c @@ -343,11 +343,6 @@ static void nilfs_end_bio_write(struct bio *bio, int err) const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); struct nilfs_segment_buffer *segbuf = bio->bi_private; - if (err == -EOPNOTSUPP) { - set_bit(BIO_EOPNOTSUPP, &bio->bi_flags); - /* to be detected by nilfs_segbuf_submit_bio() */ - } - if (!uptodate) atomic_inc(&segbuf->sb_err); @@ -374,15 +369,8 @@ static int nilfs_segbuf_submit_bio(struct nilfs_segment_buffer *segbuf, bio->bi_end_io = nilfs_end_bio_write; bio->bi_private = segbuf; - bio_get(bio); submit_bio(mode, bio); segbuf->sb_nbio++; - if (bio_flagged(bio, BIO_EOPNOTSUPP)) { - bio_put(bio); - err = -EOPNOTSUPP; - goto failed; - } - bio_put(bio); wi->bio = NULL; wi->rest_blocks -= wi->end - wi->start; diff --git a/fs/notify/group.c b/fs/notify/group.c index 06ca6bc4a..d16b62cb2 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -22,7 +22,6 @@ #include <linux/srcu.h> #include <linux/rculist.h> #include <linux/wait.h> -#include <linux/module.h> #include <linux/fsnotify_backend.h> #include "fsnotify.h" @@ -73,7 +72,6 @@ void fsnotify_get_group(struct fsnotify_group *group) { atomic_inc(&group->refcnt); } -EXPORT_SYMBOL(fsnotify_get_group); /* * Drop a reference to a group. Free it if it's through. @@ -83,7 +81,6 @@ void fsnotify_put_group(struct fsnotify_group *group) if (atomic_dec_and_test(&group->refcnt)) fsnotify_final_destroy_group(group); } -EXPORT_SYMBOL(fsnotify_put_group); /* * Create a new fsnotify_group and hold a reference for the group returned. @@ -112,7 +109,6 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) return group; } -EXPORT_SYMBOL(fsnotify_alloc_group); int fsnotify_fasync(int fd, struct file *file, int on) { diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 450648697..5b1e2a497 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -26,7 +26,7 @@ #include <linux/fs.h> /* struct inode */ #include <linux/fsnotify_backend.h> #include <linux/idr.h> -#include <linux/init.h> /* module_init */ +#include <linux/init.h> /* fs_initcall */ #include <linux/inotify.h> #include <linux/kernel.h> /* roundup() */ #include <linux/namei.h> /* LOOKUP_FOLLOW */ @@ -812,4 +812,4 @@ static int __init inotify_user_setup(void) return 0; } -module_init(inotify_user_setup); +fs_initcall(inotify_user_setup); diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 39a838ad2..39ddcaf09 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -109,7 +109,6 @@ void fsnotify_put_mark(struct fsnotify_mark *mark) mark->free_mark(mark); } } -EXPORT_SYMBOL(fsnotify_put_mark); /* Calculate mask of events for a list of marks */ u32 fsnotify_recalc_mask(struct hlist_head *head) @@ -203,7 +202,6 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark, fsnotify_destroy_mark_locked(mark, group); mutex_unlock(&group->mark_mutex); } -EXPORT_SYMBOL(fsnotify_destroy_mark); /* * Destroy all marks in the given list. The marks must be already detached from @@ -378,7 +376,6 @@ err: return ret; } -EXPORT_SYMBOL(fsnotify_add_mark); int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, int allow_dups) @@ -478,7 +475,6 @@ void fsnotify_init_mark(struct fsnotify_mark *mark, atomic_set(&mark->refcnt, 1); mark->free_mark = free_mark; } -EXPORT_SYMBOL(fsnotify_init_mark); static int fsnotify_mark_destroy(void *ignored) { diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index 7bb487e66..262561fea 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -382,7 +382,7 @@ static ssize_t ntfs_prepare_file_for_write(struct kiocb *iocb, base_ni = ni; if (NInoAttr(ni)) base_ni = ni->ext.base_ntfs_ino; - err = file_remove_suid(file); + err = file_remove_privs(file); if (unlikely(err)) goto out; /* @@ -525,7 +525,8 @@ static inline int __ntfs_grab_cache_pages(struct address_space *mapping, } } err = add_to_page_cache_lru(*cached_page, mapping, - index, GFP_KERNEL); + index, + GFP_KERNEL & mapping_gfp_mask(mapping)); if (unlikely(err)) { if (err == -EEXIST) continue; diff --git a/fs/ntfs/inode.h b/fs/ntfs/inode.h index 76b6cfb57..b3c3469de 100644 --- a/fs/ntfs/inode.h +++ b/fs/ntfs/inode.h @@ -239,7 +239,7 @@ typedef struct { */ static inline ntfs_inode *NTFS_I(struct inode *inode) { - return (ntfs_inode *)list_entry(inode, big_ntfs_inode, vfs_inode); + return (ntfs_inode *)container_of(inode, big_ntfs_inode, vfs_inode); } static inline struct inode *VFS_I(ntfs_inode *ni) diff --git a/fs/ntfs/malloc.h b/fs/ntfs/malloc.h index a44b14cbc..ab172e5f5 100644 --- a/fs/ntfs/malloc.h +++ b/fs/ntfs/malloc.h @@ -85,12 +85,7 @@ static inline void *ntfs_malloc_nofs_nofail(unsigned long size) static inline void ntfs_free(void *addr) { - if (!is_vmalloc_addr(addr)) { - kfree(addr); - /* free_page((unsigned long)addr); */ - return; - } - vfree(addr); + kvfree(addr); } #endif /* _LINUX_NTFS_MALLOC_H */ diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c index 0f35b80d1..443abecf0 100644 --- a/fs/ntfs/namei.c +++ b/fs/ntfs/namei.c @@ -35,7 +35,7 @@ * ntfs_lookup - find the inode represented by a dentry in a directory inode * @dir_ino: directory inode in which to look for the inode * @dent: dentry representing the inode to look for - * @nd: lookup nameidata + * @flags: lookup flags * * In short, ntfs_lookup() looks for the inode represented by the dentry @dent * in the directory inode @dir_ino and if found attaches the inode to the diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index 2d7f76e52..5997c00a1 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -2925,7 +2925,8 @@ static int __ocfs2_rotate_tree_left(handle_t *handle, struct ocfs2_path *right_path = NULL; struct super_block *sb = ocfs2_metadata_cache_get_super(et->et_ci); - BUG_ON(!ocfs2_is_empty_extent(&(path_leaf_el(path)->l_recs[0]))); + if (!ocfs2_is_empty_extent(&(path_leaf_el(path)->l_recs[0]))) + return 0; *empty_extent_path = NULL; @@ -4311,13 +4312,13 @@ out: return ret; } -static enum ocfs2_contig_type -ocfs2_figure_merge_contig_type(struct ocfs2_extent_tree *et, +static int ocfs2_figure_merge_contig_type(struct ocfs2_extent_tree *et, struct ocfs2_path *path, struct ocfs2_extent_list *el, int index, - struct ocfs2_extent_rec *split_rec) + struct ocfs2_extent_rec *split_rec, + struct ocfs2_merge_ctxt *ctxt) { - int status; + int status = 0; enum ocfs2_contig_type ret = CONTIG_NONE; u32 left_cpos, right_cpos; struct ocfs2_extent_rec *rec = NULL; @@ -4336,8 +4337,11 @@ ocfs2_figure_merge_contig_type(struct ocfs2_extent_tree *et, if (left_cpos != 0) { left_path = ocfs2_new_path_from_path(path); - if (!left_path) + if (!left_path) { + status = -ENOMEM; + mlog_errno(status); goto exit; + } status = ocfs2_find_path(et->et_ci, left_path, left_cpos); @@ -4392,8 +4396,11 @@ ocfs2_figure_merge_contig_type(struct ocfs2_extent_tree *et, goto free_left_path; right_path = ocfs2_new_path_from_path(path); - if (!right_path) + if (!right_path) { + status = -ENOMEM; + mlog_errno(status); goto free_left_path; + } status = ocfs2_find_path(et->et_ci, right_path, right_cpos); if (status) @@ -4433,7 +4440,10 @@ free_right_path: free_left_path: ocfs2_free_path(left_path); exit: - return ret; + if (status == 0) + ctxt->c_contig_type = ret; + + return status; } static void ocfs2_figure_contig_type(struct ocfs2_extent_tree *et, @@ -5039,9 +5049,14 @@ int ocfs2_split_extent(handle_t *handle, goto out; } - ctxt.c_contig_type = ocfs2_figure_merge_contig_type(et, path, el, - split_index, - split_rec); + ret = ocfs2_figure_merge_contig_type(et, path, el, + split_index, + split_rec, + &ctxt); + if (ret) { + mlog_errno(ret); + goto out; + } /* * The core merge / split code wants to know how much room is diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 9ea701270..0f5fd9db8 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -523,7 +523,7 @@ static int ocfs2_direct_IO_get_blocks(struct inode *inode, sector_t iblock, unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits; unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; unsigned long len = bh_result->b_size; - unsigned int clusters_to_alloc = 0; + unsigned int clusters_to_alloc = 0, contig_clusters = 0; cpos = ocfs2_blocks_to_clusters(inode->i_sb, iblock); @@ -560,8 +560,10 @@ static int ocfs2_direct_IO_get_blocks(struct inode *inode, sector_t iblock, /* fill hole, allocate blocks can't be larger than the size * of the hole */ clusters_to_alloc = ocfs2_clusters_for_bytes(inode->i_sb, len); - if (clusters_to_alloc > contig_blocks) - clusters_to_alloc = contig_blocks; + contig_clusters = ocfs2_clusters_for_blocks(inode->i_sb, + contig_blocks); + if (clusters_to_alloc > contig_clusters) + clusters_to_alloc = contig_clusters; /* allocate extent and insert them into the extent tree */ ret = ocfs2_extend_allocation(inode, cpos, @@ -619,9 +621,6 @@ static void ocfs2_dio_end_io(struct kiocb *iocb, /* this io's submitter should not have unlocked this before we could */ BUG_ON(!ocfs2_iocb_is_rw_locked(iocb)); - if (ocfs2_iocb_is_sem_locked(iocb)) - ocfs2_iocb_clear_sem_locked(iocb); - if (ocfs2_iocb_is_unaligned_aio(iocb)) { ocfs2_iocb_clear_unaligned_aio(iocb); @@ -925,13 +924,23 @@ clean_orphan: int update_isize = written > 0 ? 1 : 0; loff_t end = update_isize ? offset + written : 0; - tmp_ret = ocfs2_del_inode_from_orphan(osb, inode, + tmp_ret = ocfs2_inode_lock(inode, &di_bh, 1); + if (tmp_ret < 0) { + ret = tmp_ret; + mlog_errno(ret); + goto out; + } + + tmp_ret = ocfs2_del_inode_from_orphan(osb, inode, di_bh, update_isize, end); if (tmp_ret < 0) { ret = tmp_ret; + mlog_errno(ret); goto out; } + ocfs2_inode_unlock(inode, 1); + tmp_ret = jbd2_journal_force_commit(journal); if (tmp_ret < 0) { ret = tmp_ret; diff --git a/fs/ocfs2/aops.h b/fs/ocfs2/aops.h index dd59599b0..24e496d6b 100644 --- a/fs/ocfs2/aops.h +++ b/fs/ocfs2/aops.h @@ -79,7 +79,6 @@ static inline void ocfs2_iocb_set_rw_locked(struct kiocb *iocb, int level) enum ocfs2_iocb_lock_bits { OCFS2_IOCB_RW_LOCK = 0, OCFS2_IOCB_RW_LOCK_LEVEL, - OCFS2_IOCB_SEM, OCFS2_IOCB_UNALIGNED_IO, OCFS2_IOCB_NUM_LOCKS }; @@ -88,12 +87,6 @@ enum ocfs2_iocb_lock_bits { clear_bit(OCFS2_IOCB_RW_LOCK, (unsigned long *)&iocb->private) #define ocfs2_iocb_rw_locked_level(iocb) \ test_bit(OCFS2_IOCB_RW_LOCK_LEVEL, (unsigned long *)&iocb->private) -#define ocfs2_iocb_set_sem_locked(iocb) \ - set_bit(OCFS2_IOCB_SEM, (unsigned long *)&iocb->private) -#define ocfs2_iocb_clear_sem_locked(iocb) \ - clear_bit(OCFS2_IOCB_SEM, (unsigned long *)&iocb->private) -#define ocfs2_iocb_is_sem_locked(iocb) \ - test_bit(OCFS2_IOCB_SEM, (unsigned long *)&iocb->private) #define ocfs2_iocb_set_unaligned_aio(iocb) \ set_bit(OCFS2_IOCB_UNALIGNED_IO, (unsigned long *)&iocb->private) diff --git a/fs/ocfs2/cluster/masklog.c b/fs/ocfs2/cluster/masklog.c index af7598bff..dfe162f5f 100644 --- a/fs/ocfs2/cluster/masklog.c +++ b/fs/ocfs2/cluster/masklog.c @@ -64,6 +64,40 @@ static ssize_t mlog_mask_store(u64 mask, const char *buf, size_t count) return count; } +void __mlog_printk(const u64 *mask, const char *func, int line, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + const char *level; + const char *prefix = ""; + + if (!__mlog_test_u64(*mask, mlog_and_bits) || + __mlog_test_u64(*mask, mlog_not_bits)) + return; + + if (*mask & ML_ERROR) { + level = KERN_ERR; + prefix = "ERROR: "; + } else if (*mask & ML_NOTICE) { + level = KERN_NOTICE; + } else { + level = KERN_INFO; + } + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + printk("%s(%s,%u,%u):%s:%d %s%pV", + level, current->comm, task_pid_nr(current), + raw_smp_processor_id(), func, line, prefix, &vaf); + + va_end(args); +} +EXPORT_SYMBOL_GPL(__mlog_printk); + struct mlog_attribute { struct attribute attr; u64 mask; diff --git a/fs/ocfs2/cluster/masklog.h b/fs/ocfs2/cluster/masklog.h index 7fdc25a4d..308ea0eb3 100644 --- a/fs/ocfs2/cluster/masklog.h +++ b/fs/ocfs2/cluster/masklog.h @@ -162,38 +162,20 @@ extern struct mlog_bits mlog_and_bits, mlog_not_bits; #endif -/* - * smp_processor_id() "helpfully" screams when called outside preemptible - * regions in current kernels. sles doesn't have the variants that don't - * scream. just do this instead of trying to guess which we're building - * against.. *sigh*. - */ -#define __mlog_cpu_guess ({ \ - unsigned long _cpu = get_cpu(); \ - put_cpu(); \ - _cpu; \ -}) +__printf(4, 5) +void __mlog_printk(const u64 *m, const char *func, int line, + const char *fmt, ...); -/* In the following two macros, the whitespace after the ',' just - * before ##args is intentional. Otherwise, gcc 2.95 will eat the - * previous token if args expands to nothing. +/* + * Testing before the __mlog_printk call lets the compiler eliminate the + * call completely when (m & ML_ALLOWED_BITS) is 0. */ -#define __mlog_printk(level, fmt, args...) \ - printk(level "(%s,%u,%lu):%s:%d " fmt, current->comm, \ - task_pid_nr(current), __mlog_cpu_guess, \ - __PRETTY_FUNCTION__, __LINE__ , ##args) - -#define mlog(mask, fmt, args...) do { \ - u64 __m = MLOG_MASK_PREFIX | (mask); \ - if ((__m & ML_ALLOWED_BITS) && \ - __mlog_test_u64(__m, mlog_and_bits) && \ - !__mlog_test_u64(__m, mlog_not_bits)) { \ - if (__m & ML_ERROR) \ - __mlog_printk(KERN_ERR, "ERROR: "fmt , ##args); \ - else if (__m & ML_NOTICE) \ - __mlog_printk(KERN_NOTICE, fmt , ##args); \ - else __mlog_printk(KERN_INFO, fmt , ##args); \ - } \ +#define mlog(mask, fmt, ...) \ +do { \ + u64 _m = MLOG_MASK_PREFIX | (mask); \ + if (_m & ML_ALLOWED_BITS) \ + __mlog_printk(&_m, __func__, __LINE__, fmt, \ + ##__VA_ARGS__); \ } while (0) #define mlog_errno(st) ({ \ diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c index 56c403a56..2d0acd667 100644 --- a/fs/ocfs2/cluster/tcp.c +++ b/fs/ocfs2/cluster/tcp.c @@ -2204,7 +2204,7 @@ out: kfree(o2net_hand); kfree(o2net_keep_req); kfree(o2net_keep_resp); - + o2net_debugfs_exit(); o2quo_exit(); return -ENOMEM; } diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index ccd4dcfc3..02878a83f 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -1617,7 +1617,7 @@ int __ocfs2_add_entry(handle_t *handle, struct ocfs2_dir_entry *de, *de1; struct ocfs2_dinode *di = (struct ocfs2_dinode *)parent_fe_bh->b_data; struct super_block *sb = dir->i_sb; - int retval, status; + int retval; unsigned int size = sb->s_blocksize; struct buffer_head *insert_bh = lookup->dl_leaf_bh; char *data_start = insert_bh->b_data; @@ -1695,25 +1695,25 @@ int __ocfs2_add_entry(handle_t *handle, } if (insert_bh == parent_fe_bh) - status = ocfs2_journal_access_di(handle, + retval = ocfs2_journal_access_di(handle, INODE_CACHE(dir), insert_bh, OCFS2_JOURNAL_ACCESS_WRITE); else { - status = ocfs2_journal_access_db(handle, + retval = ocfs2_journal_access_db(handle, INODE_CACHE(dir), insert_bh, OCFS2_JOURNAL_ACCESS_WRITE); - if (ocfs2_dir_indexed(dir)) { - status = ocfs2_dx_dir_insert(dir, + if (!retval && ocfs2_dir_indexed(dir)) + retval = ocfs2_dx_dir_insert(dir, handle, lookup); - if (status) { - mlog_errno(status); - goto bail; - } - } + } + + if (retval) { + mlog_errno(retval); + goto bail; } /* By now the buffer is marked for journaling */ @@ -3543,13 +3543,10 @@ static void dx_leaf_sort_swap(void *a, void *b, int size) { struct ocfs2_dx_entry *entry1 = a; struct ocfs2_dx_entry *entry2 = b; - struct ocfs2_dx_entry tmp; BUG_ON(size != sizeof(*entry1)); - tmp = *entry1; - *entry1 = *entry2; - *entry2 = tmp; + swap(*entry1, *entry2); } static int ocfs2_dx_leaf_same_major(struct ocfs2_dx_leaf *dx_leaf) diff --git a/fs/ocfs2/dlm/dlmcommon.h b/fs/ocfs2/dlm/dlmcommon.h index fae17c640..e88ccf8c8 100644 --- a/fs/ocfs2/dlm/dlmcommon.h +++ b/fs/ocfs2/dlm/dlmcommon.h @@ -1014,7 +1014,6 @@ void dlm_move_lockres_to_recovery_list(struct dlm_ctxt *dlm, /* will exit holding res->spinlock, but may drop in function */ void __dlm_wait_on_lockres_flags(struct dlm_lock_resource *res, int flags); -void __dlm_wait_on_lockres_flags_set(struct dlm_lock_resource *res, int flags); /* will exit holding res->spinlock, but may drop in function */ static inline void __dlm_wait_on_lockres(struct dlm_lock_resource *res) diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index d8b670cbd..719f7f4c7 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -37,6 +37,7 @@ #include <linux/falloc.h> #include <linux/quotaops.h> #include <linux/blkdev.h> +#include <linux/backing-dev.h> #include <cluster/masklog.h> @@ -2250,7 +2251,7 @@ out: static ssize_t ocfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from) { - int direct_io, appending, rw_level, have_alloc_sem = 0; + int direct_io, appending, rw_level; int can_do_direct, has_refcount = 0; ssize_t written = 0; ssize_t ret; @@ -2279,16 +2280,7 @@ static ssize_t ocfs2_file_write_iter(struct kiocb *iocb, mutex_lock(&inode->i_mutex); - ocfs2_iocb_clear_sem_locked(iocb); - relock: - /* to match setattr's i_mutex -> rw_lock ordering */ - if (direct_io) { - have_alloc_sem = 1; - /* communicate with ocfs2_dio_end_io */ - ocfs2_iocb_set_sem_locked(iocb); - } - /* * Concurrent O_DIRECT writes are allowed with * mount_option "coherency=buffered". @@ -2298,7 +2290,7 @@ relock: ret = ocfs2_rw_lock(inode, rw_level); if (ret < 0) { mlog_errno(ret); - goto out_sems; + goto out_mutex; } /* @@ -2347,7 +2339,6 @@ relock: if (direct_io && !can_do_direct) { ocfs2_rw_unlock(inode, rw_level); - have_alloc_sem = 0; rw_level = -1; direct_io = 0; @@ -2416,7 +2407,6 @@ no_sync: */ if ((ret == -EIOCBQUEUED) || (!ocfs2_iocb_is_rw_locked(iocb))) { rw_level = -1; - have_alloc_sem = 0; unaligned_dio = 0; } @@ -2429,10 +2419,7 @@ out: if (rw_level != -1) ocfs2_rw_unlock(inode, rw_level); -out_sems: - if (have_alloc_sem) - ocfs2_iocb_clear_sem_locked(iocb); - +out_mutex: mutex_unlock(&inode->i_mutex); if (written) @@ -2473,7 +2460,7 @@ bail: static ssize_t ocfs2_file_read_iter(struct kiocb *iocb, struct iov_iter *to) { - int ret = 0, rw_level = -1, have_alloc_sem = 0, lock_level = 0; + int ret = 0, rw_level = -1, lock_level = 0; struct file *filp = iocb->ki_filp; struct inode *inode = file_inode(filp); @@ -2490,16 +2477,11 @@ static ssize_t ocfs2_file_read_iter(struct kiocb *iocb, goto bail; } - ocfs2_iocb_clear_sem_locked(iocb); - /* * buffered reads protect themselves in ->readpage(). O_DIRECT reads * need locks to protect pending reads from racing with truncate. */ if (iocb->ki_flags & IOCB_DIRECT) { - have_alloc_sem = 1; - ocfs2_iocb_set_sem_locked(iocb); - ret = ocfs2_rw_lock(inode, 0); if (ret < 0) { mlog_errno(ret); @@ -2535,13 +2517,9 @@ static ssize_t ocfs2_file_read_iter(struct kiocb *iocb, /* see ocfs2_file_write_iter */ if (ret == -EIOCBQUEUED || !ocfs2_iocb_is_rw_locked(iocb)) { rw_level = -1; - have_alloc_sem = 0; } bail: - if (have_alloc_sem) - ocfs2_iocb_clear_sem_locked(iocb); - if (rw_level != -1) ocfs2_rw_unlock(inode, rw_level); diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c index 53e6c40ed..3cb097ccc 100644 --- a/fs/ocfs2/ioctl.c +++ b/fs/ocfs2/ioctl.c @@ -980,7 +980,6 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg) case OCFS2_IOC_GROUP_EXTEND: case OCFS2_IOC_GROUP_ADD: case OCFS2_IOC_GROUP_ADD64: - case FITRIM: break; case OCFS2_IOC_REFLINK: if (copy_from_user(&args, argp, sizeof(args))) diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index ff5319282..7c099f703 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -108,7 +108,7 @@ struct ocfs2_replay_map { unsigned char rm_replay_slots[0]; }; -void ocfs2_replay_map_set_state(struct ocfs2_super *osb, int state) +static void ocfs2_replay_map_set_state(struct ocfs2_super *osb, int state) { if (!osb->replay_map) return; @@ -153,7 +153,7 @@ int ocfs2_compute_replay_slots(struct ocfs2_super *osb) return 0; } -void ocfs2_queue_replay_slots(struct ocfs2_super *osb, +static void ocfs2_queue_replay_slots(struct ocfs2_super *osb, enum ocfs2_orphan_reco_type orphan_reco_type) { struct ocfs2_replay_map *replay_map = osb->replay_map; @@ -173,7 +173,7 @@ void ocfs2_queue_replay_slots(struct ocfs2_super *osb, replay_map->rm_state = REPLAY_DONE; } -void ocfs2_free_replay_slots(struct ocfs2_super *osb) +static void ocfs2_free_replay_slots(struct ocfs2_super *osb) { struct ocfs2_replay_map *replay_map = osb->replay_map; @@ -571,9 +571,7 @@ static void ocfs2_abort_trigger(struct jbd2_buffer_trigger_type *triggers, (unsigned long)bh, (unsigned long long)bh->b_blocknr); - /* We aren't guaranteed to have the superblock here - but if we - * don't, it'll just crash. */ - ocfs2_error(bh->b_assoc_map->host->i_sb, + ocfs2_error(bh->b_bdev->bd_super, "JBD2 has aborted our journal, ocfs2 cannot continue\n"); } @@ -775,7 +773,20 @@ void ocfs2_journal_dirty(handle_t *handle, struct buffer_head *bh) trace_ocfs2_journal_dirty((unsigned long long)bh->b_blocknr); status = jbd2_journal_dirty_metadata(handle, bh); - BUG_ON(status); + if (status) { + mlog_errno(status); + if (!is_handle_aborted(handle)) { + journal_t *journal = handle->h_transaction->t_journal; + struct super_block *sb = bh->b_bdev->bd_super; + + mlog(ML_ERROR, "jbd2_journal_dirty_metadata failed. " + "Aborting transaction and journal.\n"); + handle->h_err = status; + jbd2_journal_abort_handle(handle); + jbd2_journal_abort(journal, status); + ocfs2_abort(sb, "Journal already aborted.\n"); + } + } } #define OCFS2_DEFAULT_COMMIT_INTERVAL (HZ * JBD2_DEFAULT_MAX_COMMIT_AGE) @@ -1884,7 +1895,7 @@ static inline unsigned long ocfs2_orphan_scan_timeout(void) * hasn't happened. The node queues a scan and increments the * sequence number in the LVB. */ -void ocfs2_queue_orphan_scan(struct ocfs2_super *osb) +static void ocfs2_queue_orphan_scan(struct ocfs2_super *osb) { struct ocfs2_orphan_scan *os; int status, i; @@ -1933,7 +1944,7 @@ out: } /* Worker task that gets fired every ORPHAN_SCAN_SCHEDULE_TIMEOUT millsec */ -void ocfs2_orphan_scan_work(struct work_struct *work) +static void ocfs2_orphan_scan_work(struct work_struct *work) { struct ocfs2_orphan_scan *os; struct ocfs2_super *osb; @@ -2137,6 +2148,8 @@ static int ocfs2_recover_orphans(struct ocfs2_super *osb, struct inode *inode = NULL; struct inode *iter; struct ocfs2_inode_info *oi; + struct buffer_head *di_bh = NULL; + struct ocfs2_dinode *di = NULL; trace_ocfs2_recover_orphans(slot); @@ -2157,16 +2170,22 @@ static int ocfs2_recover_orphans(struct ocfs2_super *osb, iter = oi->ip_next_orphan; oi->ip_next_orphan = NULL; + ret = ocfs2_rw_lock(inode, 1); + if (ret < 0) { + mlog_errno(ret); + goto next; + } /* * We need to take and drop the inode lock to * force read inode from disk. */ - ret = ocfs2_inode_lock(inode, NULL, 0); + ret = ocfs2_inode_lock(inode, &di_bh, 1); if (ret) { mlog_errno(ret); - goto next; + goto unlock_rw; } - ocfs2_inode_unlock(inode, 0); + + di = (struct ocfs2_dinode *)di_bh->b_data; if (inode->i_nlink == 0) { spin_lock(&oi->ip_lock); @@ -2174,43 +2193,30 @@ static int ocfs2_recover_orphans(struct ocfs2_super *osb, * ocfs2_delete_inode. */ oi->ip_flags |= OCFS2_INODE_MAYBE_ORPHANED; spin_unlock(&oi->ip_lock); - } else if (orphan_reco_type == ORPHAN_NEED_TRUNCATE) { - struct buffer_head *di_bh = NULL; - - ret = ocfs2_rw_lock(inode, 1); - if (ret) { - mlog_errno(ret); - goto next; - } - - ret = ocfs2_inode_lock(inode, &di_bh, 1); - if (ret < 0) { - ocfs2_rw_unlock(inode, 1); - mlog_errno(ret); - goto next; - } - + } else if ((orphan_reco_type == ORPHAN_NEED_TRUNCATE) && + (di->i_flags & cpu_to_le32(OCFS2_DIO_ORPHANED_FL))) { ret = ocfs2_truncate_file(inode, di_bh, i_size_read(inode)); - ocfs2_inode_unlock(inode, 1); - ocfs2_rw_unlock(inode, 1); - brelse(di_bh); if (ret < 0) { if (ret != -ENOSPC) mlog_errno(ret); - goto next; + goto unlock_inode; } - ret = ocfs2_del_inode_from_orphan(osb, inode, 0, 0); + ret = ocfs2_del_inode_from_orphan(osb, inode, di_bh, 0, 0); if (ret) mlog_errno(ret); wake_up(&OCFS2_I(inode)->append_dio_wq); } /* else if ORPHAN_NO_NEED_TRUNCATE, do nothing */ - +unlock_inode: + ocfs2_inode_unlock(inode, 1); +unlock_rw: + ocfs2_rw_unlock(inode, 1); next: iput(inode); - + brelse(di_bh); + di_bh = NULL; inode = iter; } diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index 176fe6afd..6e6abb93f 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -1116,8 +1116,6 @@ static int ocfs2_double_lock(struct ocfs2_super *osb, int inode1_is_ancestor, inode2_is_ancestor; struct ocfs2_inode_info *oi1 = OCFS2_I(inode1); struct ocfs2_inode_info *oi2 = OCFS2_I(inode2); - struct buffer_head **tmpbh; - struct inode *tmpinode; trace_ocfs2_double_lock((unsigned long long)oi1->ip_blkno, (unsigned long long)oi2->ip_blkno); @@ -1148,13 +1146,8 @@ static int ocfs2_double_lock(struct ocfs2_super *osb, (oi1->ip_blkno < oi2->ip_blkno && inode2_is_ancestor == 0)) { /* switch id1 and id2 around */ - tmpbh = bh2; - bh2 = bh1; - bh1 = tmpbh; - - tmpinode = inode2; - inode2 = inode1; - inode1 = tmpinode; + swap(bh2, bh1); + swap(inode2, inode1); } /* lock id2 */ status = ocfs2_inode_lock_nested(inode2, bh2, 1, @@ -2670,30 +2663,22 @@ bail: } int ocfs2_del_inode_from_orphan(struct ocfs2_super *osb, - struct inode *inode, int update_isize, - loff_t end) + struct inode *inode, struct buffer_head *di_bh, + int update_isize, loff_t end) { struct inode *orphan_dir_inode = NULL; struct buffer_head *orphan_dir_bh = NULL; - struct buffer_head *di_bh = NULL; - struct ocfs2_dinode *di = NULL; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; handle_t *handle = NULL; int status = 0; - status = ocfs2_inode_lock(inode, &di_bh, 1); - if (status < 0) { - mlog_errno(status); - goto bail; - } - di = (struct ocfs2_dinode *) di_bh->b_data; - orphan_dir_inode = ocfs2_get_system_file_inode(osb, ORPHAN_DIR_SYSTEM_INODE, le16_to_cpu(di->i_dio_orphaned_slot)); if (!orphan_dir_inode) { status = -ENOENT; mlog_errno(status); - goto bail_unlock_inode; + goto bail; } mutex_lock(&orphan_dir_inode->i_mutex); @@ -2702,7 +2687,7 @@ int ocfs2_del_inode_from_orphan(struct ocfs2_super *osb, mutex_unlock(&orphan_dir_inode->i_mutex); iput(orphan_dir_inode); mlog_errno(status); - goto bail_unlock_inode; + goto bail; } handle = ocfs2_start_trans(osb, @@ -2749,10 +2734,6 @@ bail_unlock_orphan: brelse(orphan_dir_bh); iput(orphan_dir_inode); -bail_unlock_inode: - ocfs2_inode_unlock(inode, 1); - brelse(di_bh); - bail: return status; } diff --git a/fs/ocfs2/namei.h b/fs/ocfs2/namei.h index 5ddecce17..e173329eb 100644 --- a/fs/ocfs2/namei.h +++ b/fs/ocfs2/namei.h @@ -42,8 +42,8 @@ int ocfs2_create_inode_in_orphan(struct inode *dir, int ocfs2_add_inode_to_orphan(struct ocfs2_super *osb, struct inode *inode); int ocfs2_del_inode_from_orphan(struct ocfs2_super *osb, - struct inode *inode, int update_isize, - loff_t end); + struct inode *inode, struct buffer_head *di_bh, + int update_isize, loff_t end); int ocfs2_mv_orphaned_inode_to_new(struct inode *dir, struct inode *new_inode, struct dentry *new_dentry); diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index 460c6c37e..690ddc601 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -717,6 +717,16 @@ static inline u64 ocfs2_clusters_to_blocks(struct super_block *sb, return (u64)clusters << c_to_b_bits; } +static inline u32 ocfs2_clusters_for_blocks(struct super_block *sb, + u64 blocks) +{ + int b_to_c_bits = OCFS2_SB(sb)->s_clustersize_bits - + sb->s_blocksize_bits; + + blocks += (1 << b_to_c_bits) - 1; + return (u32)(blocks >> b_to_c_bits); +} + static inline u32 ocfs2_blocks_to_clusters(struct super_block *sb, u64 blocks) { diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index d8c6af101..b69dd14c0 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -1406,11 +1406,9 @@ static int cmp_refcount_rec_by_cpos(const void *a, const void *b) static void swap_refcount_rec(void *a, void *b, int size) { - struct ocfs2_refcount_rec *l = a, *r = b, tmp; + struct ocfs2_refcount_rec *l = a, *r = b; - tmp = *l; - *l = *r; - *r = tmp; + swap(*l, *r); } /* diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index d03bfbf3d..889f3796a 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -7271,7 +7271,7 @@ static int ocfs2_xattr_security_set(struct dentry *dentry, const char *name, name, value, size, flags); } -int ocfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array, +static int ocfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array, void *fs_info) { const struct xattr *xattr; @@ -54,8 +54,10 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, newattrs.ia_valid |= ATTR_FILE; } - /* Remove suid/sgid on truncate too */ - ret = should_remove_suid(dentry); + /* Remove suid, sgid, and file capabilities on truncate too */ + ret = dentry_needs_remove_privs(dentry); + if (ret < 0) + return ret; if (ret) newattrs.ia_valid |= ret | ATTR_FORCE; @@ -65,7 +67,6 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, mutex_unlock(&dentry->d_inode->i_mutex); return ret; } -EXPORT_SYMBOL(do_truncate); long vfs_truncate(struct path *path, loff_t length) { @@ -371,7 +372,7 @@ retry: if (res) goto out; - inode = path.dentry->d_inode; + inode = d_backing_inode(path.dentry); if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) { /* @@ -680,21 +681,20 @@ int open_check_o_direct(struct file *f) } return 0; } -EXPORT_SYMBOL(open_check_o_direct); static int do_dentry_open(struct file *f, + struct inode *inode, int (*open)(struct inode *, struct file *), const struct cred *cred) { static const struct file_operations empty_fops = {}; - struct inode *inode; int error; f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE; path_get(&f->f_path); - inode = f->f_inode = f->f_path.dentry->d_inode; + f->f_inode = inode; f->f_mapping = inode->i_mapping; if (unlikely(f->f_flags & O_PATH)) { @@ -798,7 +798,8 @@ int finish_open(struct file *file, struct dentry *dentry, BUG_ON(*opened & FILE_OPENED); /* once it's opened, it's opened */ file->f_path.dentry = dentry; - error = do_dentry_open(file, open, current_cred()); + error = do_dentry_open(file, d_backing_inode(dentry), open, + current_cred()); if (!error) *opened |= FILE_OPENED; @@ -827,6 +828,34 @@ int finish_no_open(struct file *file, struct dentry *dentry) } EXPORT_SYMBOL(finish_no_open); +char *file_path(struct file *filp, char *buf, int buflen) +{ + return d_path(&filp->f_path, buf, buflen); +} +EXPORT_SYMBOL(file_path); + +/** + * vfs_open - open the file at the given path + * @path: path to open + * @file: newly allocated file with f_flag initialized + * @cred: credentials to use + */ +int vfs_open(const struct path *path, struct file *file, + const struct cred *cred) +{ + struct dentry *dentry = path->dentry; + struct inode *inode = dentry->d_inode; + + file->f_path = *path; + if (dentry->d_flags & DCACHE_OP_SELECT_INODE) { + inode = dentry->d_op->d_select_inode(dentry, file->f_flags); + if (IS_ERR(inode)) + return PTR_ERR(inode); + } + + return do_dentry_open(file, inode, NULL, cred); +} + struct file *dentry_open(const struct path *path, int flags, const struct cred *cred) { @@ -858,26 +887,6 @@ struct file *dentry_open(const struct path *path, int flags, } EXPORT_SYMBOL(dentry_open); -/** - * vfs_open - open the file at the given path - * @path: path to open - * @filp: newly allocated file with f_flag initialized - * @cred: credentials to use - */ -int vfs_open(const struct path *path, struct file *filp, - const struct cred *cred) -{ - struct inode *inode = path->dentry->d_inode; - - if (inode->i_op->dentry_open) - return inode->i_op->dentry_open(path->dentry, filp, cred); - else { - filp->f_path = *path; - return do_dentry_open(filp, NULL, cred); - } -} -EXPORT_SYMBOL(vfs_open); - static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op) { int lookup_flags = 0; diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 04f124884..d9da5a4e9 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -140,11 +140,12 @@ struct ovl_link_data { void *cookie; }; -static void *ovl_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *ovl_follow_link(struct dentry *dentry, void **cookie) { - void *ret; struct dentry *realdentry; struct inode *realinode; + struct ovl_link_data *data = NULL; + const char *ret; realdentry = ovl_dentry_real(dentry); realinode = realdentry->d_inode; @@ -152,28 +153,28 @@ static void *ovl_follow_link(struct dentry *dentry, struct nameidata *nd) if (WARN_ON(!realinode->i_op->follow_link)) return ERR_PTR(-EPERM); - ret = realinode->i_op->follow_link(realdentry, nd); - if (IS_ERR(ret)) - return ret; - if (realinode->i_op->put_link) { - struct ovl_link_data *data; - data = kmalloc(sizeof(struct ovl_link_data), GFP_KERNEL); - if (!data) { - realinode->i_op->put_link(realdentry, nd, ret); + if (!data) return ERR_PTR(-ENOMEM); - } data->realdentry = realdentry; - data->cookie = ret; + } - return data; - } else { - return NULL; + ret = realinode->i_op->follow_link(realdentry, cookie); + if (IS_ERR_OR_NULL(ret)) { + kfree(data); + return ret; } + + if (data) + data->cookie = *cookie; + + *cookie = data; + + return ret; } -static void ovl_put_link(struct dentry *dentry, struct nameidata *nd, void *c) +static void ovl_put_link(struct inode *unused, void *c) { struct inode *realinode; struct ovl_link_data *data = c; @@ -182,7 +183,7 @@ static void ovl_put_link(struct dentry *dentry, struct nameidata *nd, void *c) return; realinode = data->realdentry->d_inode; - realinode->i_op->put_link(data->realdentry, nd, data->cookie); + realinode->i_op->put_link(realinode, data->cookie); kfree(data); } @@ -336,37 +337,33 @@ static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type, return true; } -static int ovl_dentry_open(struct dentry *dentry, struct file *file, - const struct cred *cred) +struct inode *ovl_d_select_inode(struct dentry *dentry, unsigned file_flags) { int err; struct path realpath; enum ovl_path_type type; - bool want_write = false; + + if (d_is_dir(dentry)) + return d_backing_inode(dentry); type = ovl_path_real(dentry, &realpath); - if (ovl_open_need_copy_up(file->f_flags, type, realpath.dentry)) { - want_write = true; + if (ovl_open_need_copy_up(file_flags, type, realpath.dentry)) { err = ovl_want_write(dentry); if (err) - goto out; + return ERR_PTR(err); - if (file->f_flags & O_TRUNC) + if (file_flags & O_TRUNC) err = ovl_copy_up_last(dentry, NULL, true); else err = ovl_copy_up(dentry); + ovl_drop_write(dentry); if (err) - goto out_drop_write; + return ERR_PTR(err); ovl_path_upper(dentry, &realpath); } - err = vfs_open(&realpath, file, cred); -out_drop_write: - if (want_write) - ovl_drop_write(dentry); -out: - return err; + return d_backing_inode(realpath.dentry); } static const struct inode_operations ovl_file_inode_operations = { @@ -377,7 +374,6 @@ static const struct inode_operations ovl_file_inode_operations = { .getxattr = ovl_getxattr, .listxattr = ovl_listxattr, .removexattr = ovl_removexattr, - .dentry_open = ovl_dentry_open, }; static const struct inode_operations ovl_symlink_inode_operations = { diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 17ac5afc9..ea5a40b06 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -173,6 +173,7 @@ ssize_t ovl_getxattr(struct dentry *dentry, const char *name, void *value, size_t size); ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size); int ovl_removexattr(struct dentry *dentry, const char *name); +struct inode *ovl_d_select_inode(struct dentry *dentry, unsigned file_flags); struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, struct ovl_entry *oe); diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index bf8537c7f..7466ff339 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -273,8 +273,56 @@ static void ovl_dentry_release(struct dentry *dentry) } } +static int ovl_dentry_revalidate(struct dentry *dentry, unsigned int flags) +{ + struct ovl_entry *oe = dentry->d_fsdata; + unsigned int i; + int ret = 1; + + for (i = 0; i < oe->numlower; i++) { + struct dentry *d = oe->lowerstack[i].dentry; + + if (d->d_flags & DCACHE_OP_REVALIDATE) { + ret = d->d_op->d_revalidate(d, flags); + if (ret < 0) + return ret; + if (!ret) { + if (!(flags & LOOKUP_RCU)) + d_invalidate(d); + return -ESTALE; + } + } + } + return 1; +} + +static int ovl_dentry_weak_revalidate(struct dentry *dentry, unsigned int flags) +{ + struct ovl_entry *oe = dentry->d_fsdata; + unsigned int i; + int ret = 1; + + for (i = 0; i < oe->numlower; i++) { + struct dentry *d = oe->lowerstack[i].dentry; + + if (d->d_flags & DCACHE_OP_WEAK_REVALIDATE) { + ret = d->d_op->d_weak_revalidate(d, flags); + if (ret <= 0) + break; + } + } + return ret; +} + static const struct dentry_operations ovl_dentry_operations = { .d_release = ovl_dentry_release, + .d_select_inode = ovl_d_select_inode, +}; + +static const struct dentry_operations ovl_reval_dentry_operations = { + .d_release = ovl_dentry_release, + .d_revalidate = ovl_dentry_revalidate, + .d_weak_revalidate = ovl_dentry_weak_revalidate, }; static struct ovl_entry *ovl_alloc_entry(unsigned int numlower) @@ -288,6 +336,20 @@ static struct ovl_entry *ovl_alloc_entry(unsigned int numlower) return oe; } +static bool ovl_dentry_remote(struct dentry *dentry) +{ + return dentry->d_flags & + (DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE); +} + +static bool ovl_dentry_weird(struct dentry *dentry) +{ + return dentry->d_flags & (DCACHE_NEED_AUTOMOUNT | + DCACHE_MANAGE_TRANSIT | + DCACHE_OP_HASH | + DCACHE_OP_COMPARE); +} + static inline struct dentry *ovl_lookup_real(struct dentry *dir, struct qstr *name) { @@ -303,6 +365,10 @@ static inline struct dentry *ovl_lookup_real(struct dentry *dir, } else if (!dentry->d_inode) { dput(dentry); dentry = NULL; + } else if (ovl_dentry_weird(dentry)) { + dput(dentry); + /* Don't support traversing automounts and other weirdness */ + dentry = ERR_PTR(-EREMOTE); } return dentry; } @@ -350,6 +416,11 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, goto out; if (this) { + if (unlikely(ovl_dentry_remote(this))) { + dput(this); + err = -EREMOTE; + goto out; + } if (ovl_is_whiteout(this)) { dput(this); this = NULL; @@ -694,25 +765,6 @@ static void ovl_unescape(char *s) } } -static bool ovl_is_allowed_fs_type(struct dentry *root) -{ - const struct dentry_operations *dop = root->d_op; - - /* - * We don't support: - * - automount filesystems - * - filesystems with revalidate (FIXME for lower layer) - * - filesystems with case insensitive names - */ - if (dop && - (dop->d_manage || dop->d_automount || - dop->d_revalidate || dop->d_weak_revalidate || - dop->d_compare || dop->d_hash)) { - return false; - } - return true; -} - static int ovl_mount_dir_noesc(const char *name, struct path *path) { int err = -EINVAL; @@ -727,7 +779,7 @@ static int ovl_mount_dir_noesc(const char *name, struct path *path) goto out; } err = -EINVAL; - if (!ovl_is_allowed_fs_type(path->dentry)) { + if (ovl_dentry_weird(path->dentry)) { pr_err("overlayfs: filesystem on '%s' not supported\n", name); goto out_put; } @@ -751,13 +803,21 @@ static int ovl_mount_dir(const char *name, struct path *path) if (tmp) { ovl_unescape(tmp); err = ovl_mount_dir_noesc(tmp, path); + + if (!err) + if (ovl_dentry_remote(path->dentry)) { + pr_err("overlayfs: filesystem on '%s' not supported as upperdir\n", + tmp); + path_put(path); + err = -EINVAL; + } kfree(tmp); } return err; } static int ovl_lower_dir(const char *name, struct path *path, long *namelen, - int *stack_depth) + int *stack_depth, bool *remote) { int err; struct kstatfs statfs; @@ -774,6 +834,9 @@ static int ovl_lower_dir(const char *name, struct path *path, long *namelen, *namelen = max(*namelen, statfs.f_namelen); *stack_depth = max(*stack_depth, path->mnt->mnt_sb->s_stack_depth); + if (ovl_dentry_remote(path->dentry)) + *remote = true; + return 0; out_put: @@ -827,6 +890,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) unsigned int numlower; unsigned int stacklen = 0; unsigned int i; + bool remote = false; int err; err = -ENOMEM; @@ -900,7 +964,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) lower = lowertmp; for (numlower = 0; numlower < stacklen; numlower++) { err = ovl_lower_dir(lower, &stack[numlower], - &ufs->lower_namelen, &sb->s_stack_depth); + &ufs->lower_namelen, &sb->s_stack_depth, + &remote); if (err) goto out_put_lowerpath; @@ -958,7 +1023,10 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) if (!ufs->upper_mnt) sb->s_flags |= MS_RDONLY; - sb->s_d_op = &ovl_dentry_operations; + if (remote) + sb->s_d_op = &ovl_reval_dentry_operations; + else + sb->s_d_op = &ovl_dentry_operations; err = -ENOMEM; oe = ovl_alloc_entry(numlower); diff --git a/fs/posix_acl.c b/fs/posix_acl.c index 84bb65b83..4fb17ded7 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -547,51 +547,45 @@ posix_acl_create(struct inode *dir, umode_t *mode, struct posix_acl **default_acl, struct posix_acl **acl) { struct posix_acl *p; + struct posix_acl *clone; int ret; + *acl = NULL; + *default_acl = NULL; + if (S_ISLNK(*mode) || !IS_POSIXACL(dir)) - goto no_acl; + return 0; p = get_acl(dir, ACL_TYPE_DEFAULT); - if (IS_ERR(p)) { - if (p == ERR_PTR(-EOPNOTSUPP)) - goto apply_umask; - return PTR_ERR(p); + if (!p || p == ERR_PTR(-EOPNOTSUPP)) { + *mode &= ~current_umask(); + return 0; } + if (IS_ERR(p)) + return PTR_ERR(p); - if (!p) - goto apply_umask; - - *acl = posix_acl_clone(p, GFP_NOFS); - if (!*acl) + clone = posix_acl_clone(p, GFP_NOFS); + if (!clone) goto no_mem; - ret = posix_acl_create_masq(*acl, mode); + ret = posix_acl_create_masq(clone, mode); if (ret < 0) goto no_mem_clone; - if (ret == 0) { - posix_acl_release(*acl); - *acl = NULL; - } + if (ret == 0) + posix_acl_release(clone); + else + *acl = clone; - if (!S_ISDIR(*mode)) { + if (!S_ISDIR(*mode)) posix_acl_release(p); - *default_acl = NULL; - } else { + else *default_acl = p; - } - return 0; -apply_umask: - *mode &= ~current_umask(); -no_acl: - *default_acl = NULL; - *acl = NULL; return 0; no_mem_clone: - posix_acl_release(*acl); + posix_acl_release(clone); no_mem: posix_acl_release(p); return -ENOMEM; diff --git a/fs/proc/Kconfig b/fs/proc/Kconfig index 2183fcf41..1ade1206b 100644 --- a/fs/proc/Kconfig +++ b/fs/proc/Kconfig @@ -71,3 +71,13 @@ config PROC_PAGE_MONITOR /proc/pid/smaps, /proc/pid/clear_refs, /proc/pid/pagemap, /proc/kpagecount, and /proc/kpageflags. Disabling these interfaces will reduce the size of the kernel by approximately 4kb. + +config PROC_CHILDREN + bool "Include /proc/<pid>/task/<tid>/children file" + default n + help + Provides a fast way to retrieve first level children pids of a task. See + <file:Documentation/filesystems/proc.txt> for more information. + + Say Y if you are running any user-space software which takes benefit from + this interface. For example, rkt is such a piece of software. diff --git a/fs/proc/array.c b/fs/proc/array.c index fd02a9ebf..ce065cf31 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -126,6 +126,14 @@ static inline const char *get_task_state(struct task_struct *tsk) { unsigned int state = (tsk->state | tsk->exit_state) & TASK_REPORT; + /* + * Parked tasks do not run; they sit in __kthread_parkme(). + * Without this check, we would report them as running, which is + * clearly wrong, so we report them as sleeping instead. + */ + if (tsk->state == TASK_PARKED) + state = TASK_INTERRUPTIBLE; + BUILD_BUG_ON(1 + ilog2(TASK_REPORT) != ARRAY_SIZE(task_state_array)-1); return task_state_array[fls(state)]; @@ -569,7 +577,7 @@ int proc_pid_statm(struct seq_file *m, struct pid_namespace *ns, return 0; } -#ifdef CONFIG_CHECKPOINT_RESTORE +#ifdef CONFIG_PROC_CHILDREN static struct pid * get_children_pid(struct inode *inode, struct pid *pid_prev, loff_t pos) { @@ -692,4 +700,4 @@ const struct file_operations proc_tid_children_operations = { .llseek = seq_lseek, .release = children_seq_release, }; -#endif /* CONFIG_CHECKPOINT_RESTORE */ +#endif /* CONFIG_PROC_CHILDREN */ diff --git a/fs/proc/base.c b/fs/proc/base.c index c439a9dcc..aa50d1ac2 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -196,18 +196,210 @@ static int proc_root_link(struct dentry *dentry, struct path *path) return result; } -static int proc_pid_cmdline(struct seq_file *m, struct pid_namespace *ns, - struct pid *pid, struct task_struct *task) +static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf, + size_t _count, loff_t *pos) { + struct task_struct *tsk; + struct mm_struct *mm; + char *page; + unsigned long count = _count; + unsigned long arg_start, arg_end, env_start, env_end; + unsigned long len1, len2, len; + unsigned long p; + char c; + ssize_t rv; + + BUG_ON(*pos < 0); + + tsk = get_proc_task(file_inode(file)); + if (!tsk) + return -ESRCH; + mm = get_task_mm(tsk); + put_task_struct(tsk); + if (!mm) + return 0; + /* Check if process spawned far enough to have cmdline. */ + if (!mm->env_end) { + rv = 0; + goto out_mmput; + } + + page = (char *)__get_free_page(GFP_TEMPORARY); + if (!page) { + rv = -ENOMEM; + goto out_mmput; + } + + down_read(&mm->mmap_sem); + arg_start = mm->arg_start; + arg_end = mm->arg_end; + env_start = mm->env_start; + env_end = mm->env_end; + up_read(&mm->mmap_sem); + + BUG_ON(arg_start > arg_end); + BUG_ON(env_start > env_end); + + len1 = arg_end - arg_start; + len2 = env_end - env_start; + + /* Empty ARGV. */ + if (len1 == 0) { + rv = 0; + goto out_free_page; + } /* - * Rely on struct seq_operations::show() being called once - * per internal buffer allocation. See single_open(), traverse(). + * Inherently racy -- command line shares address space + * with code and data. */ - BUG_ON(m->size < PAGE_SIZE); - m->count += get_cmdline(task, m->buf, PAGE_SIZE); - return 0; + rv = access_remote_vm(mm, arg_end - 1, &c, 1, 0); + if (rv <= 0) + goto out_free_page; + + rv = 0; + + if (c == '\0') { + /* Command line (set of strings) occupies whole ARGV. */ + if (len1 <= *pos) + goto out_free_page; + + p = arg_start + *pos; + len = len1 - *pos; + while (count > 0 && len > 0) { + unsigned int _count; + int nr_read; + + _count = min3(count, len, PAGE_SIZE); + nr_read = access_remote_vm(mm, p, page, _count, 0); + if (nr_read < 0) + rv = nr_read; + if (nr_read <= 0) + goto out_free_page; + + if (copy_to_user(buf, page, nr_read)) { + rv = -EFAULT; + goto out_free_page; + } + + p += nr_read; + len -= nr_read; + buf += nr_read; + count -= nr_read; + rv += nr_read; + } + } else { + /* + * Command line (1 string) occupies ARGV and maybe + * extends into ENVP. + */ + if (len1 + len2 <= *pos) + goto skip_argv_envp; + if (len1 <= *pos) + goto skip_argv; + + p = arg_start + *pos; + len = len1 - *pos; + while (count > 0 && len > 0) { + unsigned int _count, l; + int nr_read; + bool final; + + _count = min3(count, len, PAGE_SIZE); + nr_read = access_remote_vm(mm, p, page, _count, 0); + if (nr_read < 0) + rv = nr_read; + if (nr_read <= 0) + goto out_free_page; + + /* + * Command line can be shorter than whole ARGV + * even if last "marker" byte says it is not. + */ + final = false; + l = strnlen(page, nr_read); + if (l < nr_read) { + nr_read = l; + final = true; + } + + if (copy_to_user(buf, page, nr_read)) { + rv = -EFAULT; + goto out_free_page; + } + + p += nr_read; + len -= nr_read; + buf += nr_read; + count -= nr_read; + rv += nr_read; + + if (final) + goto out_free_page; + } +skip_argv: + /* + * Command line (1 string) occupies ARGV and + * extends into ENVP. + */ + if (len1 <= *pos) { + p = env_start + *pos - len1; + len = len1 + len2 - *pos; + } else { + p = env_start; + len = len2; + } + while (count > 0 && len > 0) { + unsigned int _count, l; + int nr_read; + bool final; + + _count = min3(count, len, PAGE_SIZE); + nr_read = access_remote_vm(mm, p, page, _count, 0); + if (nr_read < 0) + rv = nr_read; + if (nr_read <= 0) + goto out_free_page; + + /* Find EOS. */ + final = false; + l = strnlen(page, nr_read); + if (l < nr_read) { + nr_read = l; + final = true; + } + + if (copy_to_user(buf, page, nr_read)) { + rv = -EFAULT; + goto out_free_page; + } + + p += nr_read; + len -= nr_read; + buf += nr_read; + count -= nr_read; + rv += nr_read; + + if (final) + goto out_free_page; + } +skip_argv_envp: + ; + } + +out_free_page: + free_page((unsigned long)page); +out_mmput: + mmput(mm); + if (rv > 0) + *pos += rv; + return rv; } +static const struct file_operations proc_pid_cmdline_ops = { + .read = proc_pid_cmdline_read, + .llseek = generic_file_llseek, +}; + static int proc_pid_auxv(struct seq_file *m, struct pid_namespace *ns, struct pid *pid, struct task_struct *task) { @@ -304,15 +496,18 @@ static int proc_pid_stack(struct seq_file *m, struct pid_namespace *ns, } #endif -#ifdef CONFIG_SCHEDSTATS +#ifdef CONFIG_SCHED_INFO /* * Provides /proc/PID/schedstat */ static int proc_pid_schedstat(struct seq_file *m, struct pid_namespace *ns, struct pid *pid, struct task_struct *task) { - seq_printf(m, "%llu %llu %lu\n", - (unsigned long long)tsk_seruntime(task), + if (unlikely(!sched_info_on())) + seq_printf(m, "0 0 0\n"); + else + seq_printf(m, "%llu %llu %lu\n", + (unsigned long long)task->se.sum_exec_runtime, (unsigned long long)task->sched_info.run_delay, task->sched_info.pcount); @@ -1380,7 +1575,7 @@ static int proc_exe_link(struct dentry *dentry, struct path *exe_path) return -ENOENT; } -static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *proc_pid_follow_link(struct dentry *dentry, void **cookie) { struct inode *inode = d_inode(dentry); struct path path; @@ -1394,7 +1589,7 @@ static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd) if (error) goto out; - nd_jump_link(nd, &path); + nd_jump_link(&path); return NULL; out: return ERR_PTR(error); @@ -1744,7 +1939,7 @@ static int proc_map_files_get_link(struct dentry *dentry, struct path *path) down_read(&mm->mmap_sem); vma = find_exact_vma(mm, vm_start, vm_end); if (vma && vma->vm_file) { - *path = vma_pr_or_file(vma)->f_path; + *path = vma->vm_file->f_path; path_get(path); rc = 0; } @@ -2572,7 +2767,7 @@ static const struct pid_entry tgid_base_stuff[] = { #ifdef CONFIG_HAVE_ARCH_TRACEHOOK ONE("syscall", S_IRUSR, proc_pid_syscall), #endif - ONE("cmdline", S_IRUGO, proc_pid_cmdline), + REG("cmdline", S_IRUGO, proc_pid_cmdline_ops), ONE("stat", S_IRUGO, proc_tgid_stat), ONE("statm", S_IRUGO, proc_pid_statm), REG("maps", S_IRUGO, proc_pid_maps_operations), @@ -2600,7 +2795,7 @@ static const struct pid_entry tgid_base_stuff[] = { #ifdef CONFIG_STACKTRACE ONE("stack", S_IRUSR, proc_pid_stack), #endif -#ifdef CONFIG_SCHEDSTATS +#ifdef CONFIG_SCHED_INFO ONE("schedstat", S_IRUGO, proc_pid_schedstat), #endif #ifdef CONFIG_LATENCYTOP @@ -2918,11 +3113,11 @@ static const struct pid_entry tid_base_stuff[] = { #ifdef CONFIG_HAVE_ARCH_TRACEHOOK ONE("syscall", S_IRUSR, proc_pid_syscall), #endif - ONE("cmdline", S_IRUGO, proc_pid_cmdline), + REG("cmdline", S_IRUGO, proc_pid_cmdline_ops), ONE("stat", S_IRUGO, proc_tid_stat), ONE("statm", S_IRUGO, proc_pid_statm), REG("maps", S_IRUGO, proc_tid_maps_operations), -#ifdef CONFIG_CHECKPOINT_RESTORE +#ifdef CONFIG_PROC_CHILDREN REG("children", S_IRUGO, proc_tid_children_operations), #endif #ifdef CONFIG_NUMA @@ -2948,7 +3143,7 @@ static const struct pid_entry tid_base_stuff[] = { #ifdef CONFIG_STACKTRACE ONE("stack", S_IRUSR, proc_pid_stack), #endif -#ifdef CONFIG_SCHEDSTATS +#ifdef CONFIG_SCHED_INFO ONE("schedstat", S_IRUGO, proc_pid_schedstat), #endif #ifdef CONFIG_LATENCYTOP diff --git a/fs/proc/inode.c b/fs/proc/inode.c index e3eb55246..bd95b9fde 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -23,7 +23,6 @@ #include <linux/slab.h> #include <linux/mount.h> #include <linux/magic.h> -#include <linux/namei.h> #include <asm/uaccess.h> @@ -394,16 +393,16 @@ static const struct file_operations proc_reg_file_ops_no_compat = { }; #endif -static void *proc_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *proc_follow_link(struct dentry *dentry, void **cookie) { struct proc_dir_entry *pde = PDE(d_inode(dentry)); if (unlikely(!use_pde(pde))) return ERR_PTR(-EINVAL); - nd_set_link(nd, pde->data); - return pde; + *cookie = pde; + return pde->data; } -static void proc_put_link(struct dentry *dentry, struct nameidata *nd, void *p) +static void proc_put_link(struct inode *unused, void *p) { unuse_pde(p); } diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c index 91a4e6426..92e6726f6 100644 --- a/fs/proc/kcore.c +++ b/fs/proc/kcore.c @@ -92,7 +92,7 @@ static size_t get_kcore_size(int *nphdr, size_t *elf_buflen) roundup(sizeof(CORE_STR), 4)) + roundup(sizeof(struct elf_prstatus), 4) + roundup(sizeof(struct elf_prpsinfo), 4) + - roundup(sizeof(struct task_struct), 4); + roundup(arch_task_struct_size, 4); *elf_buflen = PAGE_ALIGN(*elf_buflen); return size + *elf_buflen; } @@ -415,7 +415,7 @@ static void elf_kcore_store_hdr(char *bufp, int nphdr, int dataoff) /* set up the task structure */ notes[2].name = CORE_STR; notes[2].type = NT_TASKSTRUCT; - notes[2].datasz = sizeof(struct task_struct); + notes[2].datasz = arch_task_struct_size; notes[2].data = current; nhdr->p_filesz += notesize(¬es[2]); diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c index e512642db..f6e8354b8 100644 --- a/fs/proc/namespaces.c +++ b/fs/proc/namespaces.c @@ -30,7 +30,7 @@ static const struct proc_ns_operations *ns_entries[] = { &mntns_operations, }; -static void *proc_ns_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *proc_ns_follow_link(struct dentry *dentry, void **cookie) { struct inode *inode = d_inode(dentry); const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops; @@ -45,7 +45,7 @@ static void *proc_ns_follow_link(struct dentry *dentry, struct nameidata *nd) if (ptrace_may_access(task, PTRACE_MODE_READ)) { error = ns_get_path(&ns_path, task, ns_ops); if (!error) - nd_jump_link(nd, &ns_path); + nd_jump_link(&ns_path); } put_task_struct(task); return error; diff --git a/fs/proc/nommu.c b/fs/proc/nommu.c index 139718132..f8595e8b5 100644 --- a/fs/proc/nommu.c +++ b/fs/proc/nommu.c @@ -45,10 +45,7 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region) file = region->vm_file; if (file) { - struct inode *inode; - - file = vmr_pr_or_file(region); - inode = file_inode(file); + struct inode *inode = file_inode(region->vm_file); dev = inode->i_sb->s_dev; ino = inode->i_ino; } @@ -67,7 +64,7 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region) if (file) { seq_pad(m, ' '); - seq_path(m, &file->f_path, ""); + seq_file_path(m, file, ""); } seq_putc(m, '\n'); diff --git a/fs/proc/self.c b/fs/proc/self.c index 6195b4a7c..113b8d061 100644 --- a/fs/proc/self.c +++ b/fs/proc/self.c @@ -1,5 +1,4 @@ #include <linux/sched.h> -#include <linux/namei.h> #include <linux/slab.h> #include <linux/pid_namespace.h> #include "internal.h" @@ -19,21 +18,20 @@ static int proc_self_readlink(struct dentry *dentry, char __user *buffer, return readlink_copy(buffer, buflen, tmp); } -static void *proc_self_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *proc_self_follow_link(struct dentry *dentry, void **cookie) { struct pid_namespace *ns = dentry->d_sb->s_fs_info; pid_t tgid = task_tgid_nr_ns(current, ns); - char *name = ERR_PTR(-ENOENT); - if (tgid) { - /* 11 for max length of signed int in decimal + NULL term */ - name = kmalloc(12, GFP_KERNEL); - if (!name) - name = ERR_PTR(-ENOMEM); - else - sprintf(name, "%d", tgid); - } - nd_set_link(nd, name); - return NULL; + char *name; + + if (!tgid) + return ERR_PTR(-ENOENT); + /* 11 for max length of signed int in decimal + NULL term */ + name = kmalloc(12, GFP_KERNEL); + if (!name) + return ERR_PTR(-ENOMEM); + sprintf(name, "%d", tgid); + return *cookie = name; } static const struct inode_operations proc_self_inode_operations = { diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 9afa35d04..ca1e09188 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -279,10 +279,7 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) const char *name = NULL; if (file) { - struct inode *inode; - - file = vma_pr_or_file(vma); - inode = file_inode(file); + struct inode *inode = file_inode(vma->vm_file); dev = inode->i_sb->s_dev; ino = inode->i_ino; pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; @@ -313,7 +310,7 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) */ if (file) { seq_pad(m, ' '); - seq_path(m, &file->f_path, "\n"); + seq_file_path(m, file, "\n"); goto done; } @@ -1482,7 +1479,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid) struct proc_maps_private *proc_priv = &numa_priv->proc_maps; struct vm_area_struct *vma = v; struct numa_maps *md = &numa_priv->md; - struct file *file = vma_pr_or_file(vma); + struct file *file = vma->vm_file; struct mm_struct *mm = vma->vm_mm; struct mm_walk walk = { .hugetlb_entry = gather_hugetlb_stats, @@ -1512,7 +1509,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid) if (file) { seq_puts(m, " file="); - seq_path(m, &file->f_path, "\n\t= "); + seq_file_path(m, file, "\n\t= "); } else if (vma->vm_start <= mm->brk && vma->vm_end >= mm->start_brk) { seq_puts(m, " heap"); } else { diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c index 174020784..e0d64c92e 100644 --- a/fs/proc/task_nommu.c +++ b/fs/proc/task_nommu.c @@ -160,10 +160,7 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma, file = vma->vm_file; if (file) { - struct inode *inode; - - file = vma_pr_or_file(vma); - inode = file_inode(file); + struct inode *inode = file_inode(vma->vm_file); dev = inode->i_sb->s_dev; ino = inode->i_ino; pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT; @@ -183,7 +180,7 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma, if (file) { seq_pad(m, ' '); - seq_path(m, &file->f_path, ""); + seq_file_path(m, file, ""); } else if (mm) { pid_t tid = pid_of_stack(priv, vma, is_pid); diff --git a/fs/proc/thread_self.c b/fs/proc/thread_self.c index a8371993b..947b0f4fd 100644 --- a/fs/proc/thread_self.c +++ b/fs/proc/thread_self.c @@ -1,5 +1,4 @@ #include <linux/sched.h> -#include <linux/namei.h> #include <linux/slab.h> #include <linux/pid_namespace.h> #include "internal.h" @@ -20,21 +19,20 @@ static int proc_thread_self_readlink(struct dentry *dentry, char __user *buffer, return readlink_copy(buffer, buflen, tmp); } -static void *proc_thread_self_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *proc_thread_self_follow_link(struct dentry *dentry, void **cookie) { struct pid_namespace *ns = dentry->d_sb->s_fs_info; pid_t tgid = task_tgid_nr_ns(current, ns); pid_t pid = task_pid_nr_ns(current, ns); - char *name = ERR_PTR(-ENOENT); - if (pid) { - name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF, GFP_KERNEL); - if (!name) - name = ERR_PTR(-ENOMEM); - else - sprintf(name, "%d/task/%d", tgid, pid); - } - nd_set_link(nd, name); - return NULL; + char *name; + + if (!pid) + return ERR_PTR(-ENOENT); + name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF, GFP_KERNEL); + if (!name) + return ERR_PTR(-ENOMEM); + sprintf(name, "%d/task/%d", tgid, pid); + return *cookie = name; } static const struct inode_operations proc_thread_self_inode_operations = { diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c index 8db932da4..8ebd9a334 100644 --- a/fs/proc_namespace.c +++ b/fs/proc_namespace.c @@ -17,7 +17,8 @@ static unsigned mounts_poll(struct file *file, poll_table *wait) { - struct proc_mounts *p = proc_mounts(file->private_data); + struct seq_file *m = file->private_data; + struct proc_mounts *p = m->private; struct mnt_namespace *ns = p->ns; unsigned res = POLLIN | POLLRDNORM; int event; @@ -25,8 +26,8 @@ static unsigned mounts_poll(struct file *file, poll_table *wait) poll_wait(file, &p->ns->poll, wait); event = ACCESS_ONCE(ns->event); - if (p->m.poll_event != event) { - p->m.poll_event = event; + if (m->poll_event != event) { + m->poll_event = event; res |= POLLERR | POLLPRI; } @@ -92,7 +93,7 @@ static void show_type(struct seq_file *m, struct super_block *sb) static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt) { - struct proc_mounts *p = proc_mounts(m); + struct proc_mounts *p = m->private; struct mount *r = real_mount(mnt); int err = 0; struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; @@ -126,7 +127,7 @@ out: static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt) { - struct proc_mounts *p = proc_mounts(m); + struct proc_mounts *p = m->private; struct mount *r = real_mount(mnt); struct super_block *sb = mnt->mnt_sb; struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; @@ -186,7 +187,7 @@ out: static int show_vfsstat(struct seq_file *m, struct vfsmount *mnt) { - struct proc_mounts *p = proc_mounts(m); + struct proc_mounts *p = m->private; struct mount *r = real_mount(mnt); struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; struct super_block *sb = mnt_path.dentry->d_sb; @@ -236,6 +237,7 @@ static int mounts_open_common(struct inode *inode, struct file *file, struct mnt_namespace *ns = NULL; struct path root; struct proc_mounts *p; + struct seq_file *m; int ret = -EINVAL; if (!task) @@ -260,26 +262,21 @@ static int mounts_open_common(struct inode *inode, struct file *file, task_unlock(task); put_task_struct(task); - ret = -ENOMEM; - p = kmalloc(sizeof(struct proc_mounts), GFP_KERNEL); - if (!p) + ret = seq_open_private(file, &mounts_op, sizeof(struct proc_mounts)); + if (ret) goto err_put_path; - file->private_data = &p->m; - ret = seq_open(file, &mounts_op); - if (ret) - goto err_free; + m = file->private_data; + m->poll_event = ns->event; + p = m->private; p->ns = ns; p->root = root; - p->m.poll_event = ns->event; p->show = show; p->cached_event = ~0ULL; return 0; - err_free: - kfree(p); err_put_path: path_put(&root); err_put_ns: @@ -290,10 +287,11 @@ static int mounts_open_common(struct inode *inode, struct file *file, static int mounts_release(struct inode *inode, struct file *file) { - struct proc_mounts *p = proc_mounts(file->private_data); + struct seq_file *m = file->private_data; + struct proc_mounts *p = m->private; path_put(&p->root); put_mnt_ns(p->ns); - return seq_release(inode, file); + return seq_release_private(inode, file); } static int mounts_open(struct inode *inode, struct file *file) diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index c4c9a10c5..791743dee 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -299,7 +299,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, bool compressed; size_t total_len; - if (big_oops_buf) { + if (big_oops_buf && is_locked) { dst = big_oops_buf; hsize = sprintf(dst, "%s#%d Part%u\n", why, oopscount, part); @@ -456,6 +456,12 @@ int pstore_register(struct pstore_info *psi) add_timer(&pstore_timer); } + /* + * Update the module parameter backend, so it is visible + * through /sys/module/pstore/parameters/backend + */ + backend = psi->name; + pr_info("Registered %s as persistent store backend\n", psi->name); return 0; diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index 44a549bee..6c26c4daa 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -186,12 +186,34 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, ssize_t size; ssize_t ecc_notice_size; struct ramoops_context *cxt = psi->data; - struct persistent_ram_zone *prz; - int header_length; + struct persistent_ram_zone *prz = NULL; + int header_length = 0; + + /* Ramoops headers provide time stamps for PSTORE_TYPE_DMESG, but + * PSTORE_TYPE_CONSOLE and PSTORE_TYPE_FTRACE don't currently have + * valid time stamps, so it is initialized to zero. + */ + time->tv_sec = 0; + time->tv_nsec = 0; + *compressed = false; + + /* Find the next valid persistent_ram_zone for DMESG */ + while (cxt->dump_read_cnt < cxt->max_dump_cnt && !prz) { + prz = ramoops_get_next_prz(cxt->przs, &cxt->dump_read_cnt, + cxt->max_dump_cnt, id, type, + PSTORE_TYPE_DMESG, 1); + if (!prz_ok(prz)) + continue; + header_length = ramoops_read_kmsg_hdr(persistent_ram_old(prz), + time, compressed); + /* Clear and skip this DMESG record if it has no valid header */ + if (!header_length) { + persistent_ram_free_old(prz); + persistent_ram_zap(prz); + prz = NULL; + } + } - prz = ramoops_get_next_prz(cxt->przs, &cxt->dump_read_cnt, - cxt->max_dump_cnt, id, type, - PSTORE_TYPE_DMESG, 1); if (!prz_ok(prz)) prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt, 1, id, type, PSTORE_TYPE_CONSOLE, 0); @@ -204,13 +226,7 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, if (!prz_ok(prz)) return 0; - if (!persistent_ram_old(prz)) - return 0; - - size = persistent_ram_old_size(prz); - header_length = ramoops_read_kmsg_hdr(persistent_ram_old(prz), time, - compressed); - size -= header_length; + size = persistent_ram_old_size(prz) - header_length; /* ECC correction notice */ ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0); @@ -394,18 +410,16 @@ static int ramoops_init_przs(struct device *dev, struct ramoops_context *cxt, } for (i = 0; i < cxt->max_dump_cnt; i++) { - size_t sz = cxt->record_size; - - cxt->przs[i] = persistent_ram_new(*paddr, sz, 0, + cxt->przs[i] = persistent_ram_new(*paddr, cxt->record_size, 0, &cxt->ecc_info, cxt->memtype); if (IS_ERR(cxt->przs[i])) { err = PTR_ERR(cxt->przs[i]); dev_err(dev, "failed to request mem region (0x%zx@0x%llx): %d\n", - sz, (unsigned long long)*paddr, err); + cxt->record_size, (unsigned long long)*paddr, err); goto fail_prz; } - *paddr += sz; + *paddr += cxt->record_size; } return 0; @@ -608,7 +622,7 @@ static void ramoops_register_dummy(void) dummy_data->mem_size = mem_size; dummy_data->mem_address = mem_address; - dummy_data->mem_type = 0; + dummy_data->mem_type = mem_type; dummy_data->record_size = record_size; dummy_data->console_size = ramoops_console_size; dummy_data->ftrace_size = ramoops_ftrace_size; diff --git a/fs/qnx6/dir.c b/fs/qnx6/dir.c index 8d64bb536..e1f37278c 100644 --- a/fs/qnx6/dir.c +++ b/fs/qnx6/dir.c @@ -32,11 +32,6 @@ static struct page *qnx6_get_page(struct inode *dir, unsigned long n) return page; } -static inline unsigned long dir_pages(struct inode *inode) -{ - return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT; -} - static unsigned last_entry(struct inode *inode, unsigned long page_nr) { unsigned long last_byte = inode->i_size; diff --git a/fs/read_write.c b/fs/read_write.c index 8ace6ec15..819ef3faf 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -494,30 +494,6 @@ ssize_t __vfs_write(struct file *file, const char __user *p, size_t count, } EXPORT_SYMBOL(__vfs_write); -vfs_readf_t vfs_readf(struct file *file) -{ - const struct file_operations *fop = file->f_op; - - if (fop->read) - return fop->read; - if (fop->read_iter) - return new_sync_read; - return ERR_PTR(-ENOSYS); -} -EXPORT_SYMBOL(vfs_readf); - -vfs_writef_t vfs_writef(struct file *file) -{ - const struct file_operations *fop = file->f_op; - - if (fop->write) - return fop->write; - if (fop->write_iter) - return new_sync_write; - return ERR_PTR(-ENOSYS); -} -EXPORT_SYMBOL(vfs_writef); - ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos) { mm_segment_t old_fs; diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index 0111ad046..0e4cf7281 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -21,6 +21,7 @@ #include "xattr.h" #include <linux/init.h> #include <linux/blkdev.h> +#include <linux/backing-dev.h> #include <linux/buffer_head.h> #include <linux/exportfs.h> #include <linux/quotaops.h> @@ -588,8 +589,7 @@ static struct kmem_cache *reiserfs_inode_cachep; static struct inode *reiserfs_alloc_inode(struct super_block *sb) { struct reiserfs_inode_info *ei; - ei = (struct reiserfs_inode_info *) - kmem_cache_alloc(reiserfs_inode_cachep, GFP_KERNEL); + ei = kmem_cache_alloc(reiserfs_inode_cachep, GFP_KERNEL); if (!ei) return NULL; atomic_set(&ei->openers, 0); diff --git a/fs/select.c b/fs/select.c index f684c750e..015547330 100644 --- a/fs/select.c +++ b/fs/select.c @@ -189,7 +189,7 @@ static int __pollwake(wait_queue_t *wait, unsigned mode, int sync, void *key) * doesn't imply write barrier and the users expect write * barrier semantics on wakeup functions. The following * smp_wmb() is equivalent to smp_wmb() in try_to_wake_up() - * and is paired with set_mb() in poll_schedule_timeout. + * and is paired with smp_store_mb() in poll_schedule_timeout. */ smp_wmb(); pwq->triggered = 1; @@ -244,7 +244,7 @@ int poll_schedule_timeout(struct poll_wqueues *pwq, int state, /* * Prepare for the next iteration. * - * The following set_mb() serves two purposes. First, it's + * The following smp_store_mb() serves two purposes. First, it's * the counterpart rmb of the wmb in pollwake() such that data * written before wake up is always visible after wake up. * Second, the full barrier guarantees that triggered clearing @@ -252,7 +252,7 @@ int poll_schedule_timeout(struct poll_wqueues *pwq, int state, * this problem doesn't exist for the first iteration as * add_wait_queue() has full barrier semantics. */ - set_mb(pwq->triggered, 0); + smp_store_mb(pwq->triggered, 0); return rc; } diff --git a/fs/seq_file.c b/fs/seq_file.c index 555f82155..ce9e39fd5 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -48,18 +48,21 @@ static void *seq_buf_alloc(unsigned long size) * ERR_PTR(error). In the end of sequence they return %NULL. ->show() * returns 0 in case of success and negative number in case of error. * Returning SEQ_SKIP means "discard this element and move on". + * Note: seq_open() will allocate a struct seq_file and store its + * pointer in @file->private_data. This pointer should not be modified. */ int seq_open(struct file *file, const struct seq_operations *op) { - struct seq_file *p = file->private_data; + struct seq_file *p; + + WARN_ON(file->private_data); + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + + file->private_data = p; - if (!p) { - p = kmalloc(sizeof(*p), GFP_KERNEL); - if (!p) - return -ENOMEM; - file->private_data = p; - } - memset(p, 0, sizeof(*p)); mutex_init(&p->lock); p->op = op; #ifdef CONFIG_USER_NS @@ -487,6 +490,20 @@ int seq_path(struct seq_file *m, const struct path *path, const char *esc) } EXPORT_SYMBOL(seq_path); +/** + * seq_file_path - seq_file interface to print a pathname of a file + * @m: the seq_file handle + * @file: the struct file to print + * @esc: set of characters to escape in the output + * + * return the absolute path to the file. + */ +int seq_file_path(struct seq_file *m, struct file *file, const char *esc) +{ + return seq_path(m, &file->f_path, esc); +} +EXPORT_SYMBOL(seq_file_path); + /* * Same as seq_path, but relative to supplied root. */ @@ -538,6 +555,7 @@ int seq_dentry(struct seq_file *m, struct dentry *dentry, const char *esc) return res; } +EXPORT_SYMBOL(seq_dentry); static void *single_start(struct seq_file *p, loff_t *pos) { diff --git a/fs/splice.c b/fs/splice.c index bfb3324fc..5fc1e50a7 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -261,6 +261,7 @@ ssize_t splice_to_pipe(struct pipe_inode_info *pipe, return ret; } +EXPORT_SYMBOL_GPL(splice_to_pipe); void spd_release_page(struct splice_pipe_desc *spd, unsigned int i) { @@ -359,7 +360,7 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, break; error = add_to_page_cache_lru(page, mapping, index, - GFP_KERNEL); + GFP_KERNEL & mapping_gfp_mask(mapping)); if (unlikely(error)) { page_cache_release(page); if (error == -EEXIST) @@ -1101,8 +1102,8 @@ EXPORT_SYMBOL(generic_splice_sendpage); /* * Attempt to initiate a splice from pipe to file. */ -long do_splice_from(struct pipe_inode_info *pipe, struct file *out, - loff_t *ppos, size_t len, unsigned int flags) +static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags) { ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); @@ -1114,14 +1115,13 @@ long do_splice_from(struct pipe_inode_info *pipe, struct file *out, return splice_write(pipe, out, ppos, len, flags); } -EXPORT_SYMBOL(do_splice_from); /* * Attempt to initiate a splice from a file to a pipe. */ -long do_splice_to(struct file *in, loff_t *ppos, - struct pipe_inode_info *pipe, size_t len, - unsigned int flags) +static long do_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) { ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); @@ -1141,7 +1141,6 @@ long do_splice_to(struct file *in, loff_t *ppos, return splice_read(in, ppos, pipe, len, flags); } -EXPORT_SYMBOL(do_splice_to); /** * splice_direct_to_actor - splices data directly between two non-pipes diff --git a/fs/squashfs/squashfs_fs_i.h b/fs/squashfs/squashfs_fs_i.h index 73588e770..d09fcd6fb 100644 --- a/fs/squashfs/squashfs_fs_i.h +++ b/fs/squashfs/squashfs_fs_i.h @@ -49,6 +49,6 @@ struct squashfs_inode_info { static inline struct squashfs_inode_info *squashfs_i(struct inode *inode) { - return list_entry(inode, struct squashfs_inode_info, vfs_inode); + return container_of(inode, struct squashfs_inode_info, vfs_inode); } #endif diff --git a/fs/super.c b/fs/super.c index 6fb221e8d..b61372354 100644 --- a/fs/super.c +++ b/fs/super.c @@ -36,7 +36,7 @@ #include "internal.h" -LIST_HEAD(super_blocks); +static LIST_HEAD(super_blocks); static DEFINE_SPINLOCK(sb_lock); static char *sb_writers_name[SB_FREEZE_LEVELS] = { @@ -842,7 +842,7 @@ int get_anon_bdev(dev_t *p) else if (error) return -EAGAIN; - if (dev == (1 << MINORBITS)) { + if (dev >= (1 << MINORBITS)) { spin_lock(&unnamed_dev_lock); ida_remove(&unnamed_dev_ida, dev); if (unnamed_dev_start > dev) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 7c2867b44..6c95628ea 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -90,7 +90,7 @@ static ssize_t sysfs_kf_bin_read(struct kernfs_open_file *of, char *buf, return 0; if (size) { - if (pos > size) + if (pos >= size) return 0; if (pos + count > size) count = size - pos; diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index b400c0437..39a019936 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -135,7 +135,7 @@ static int internal_create_group(struct kobject *kobj, int update, * This function creates a group for the first time. It will explicitly * warn and error if any of the attribute files being created already exist. * - * Returns 0 on success or error. + * Returns 0 on success or error code on failure. */ int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) @@ -155,7 +155,7 @@ EXPORT_SYMBOL_GPL(sysfs_create_group); * It will explicitly warn and error if any of the attribute files being * created already exist. * - * Returns 0 on success or error code from sysfs_create_group on error. + * Returns 0 on success or error code from sysfs_create_group on failure. */ int sysfs_create_groups(struct kobject *kobj, const struct attribute_group **groups) @@ -193,7 +193,7 @@ EXPORT_SYMBOL_GPL(sysfs_create_groups); * The primary use for this function is to call it after making a change * that affects group visibility. * - * Returns 0 on success or error. + * Returns 0 on success or error code on failure. */ int sysfs_update_group(struct kobject *kobj, const struct attribute_group *grp) diff --git a/fs/sysv/Makefile b/fs/sysv/Makefile index 3591f9d7a..7a75e70a4 100644 --- a/fs/sysv/Makefile +++ b/fs/sysv/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_SYSV_FS) += sysv.o sysv-objs := ialloc.o balloc.o inode.o itree.o file.o dir.o \ - namei.o super.o symlink.o + namei.o super.o diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c index 8f3555f00..63c1bcb22 100644 --- a/fs/sysv/dir.c +++ b/fs/sysv/dir.c @@ -33,11 +33,6 @@ static inline void dir_put_page(struct page *page) page_cache_release(page); } -static inline unsigned long dir_pages(struct inode *inode) -{ - return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT; -} - static int dir_commit_chunk(struct page *page, loff_t pos, unsigned len) { struct address_space *mapping = page->mapping; diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index 88956309c..590ad9206 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -166,8 +166,9 @@ void sysv_set_inode(struct inode *inode, dev_t rdev) inode->i_op = &sysv_symlink_inode_operations; inode->i_mapping->a_ops = &sysv_aops; } else { - inode->i_op = &sysv_fast_symlink_inode_operations; - nd_terminate_link(SYSV_I(inode)->i_data, inode->i_size, + inode->i_op = &simple_symlink_inode_operations; + inode->i_link = (char *)SYSV_I(inode)->i_data; + nd_terminate_link(inode->i_link, inode->i_size, sizeof(SYSV_I(inode)->i_data) - 1); } } else diff --git a/fs/sysv/symlink.c b/fs/sysv/symlink.c deleted file mode 100644 index d3fa0d703..000000000 --- a/fs/sysv/symlink.c +++ /dev/null @@ -1,20 +0,0 @@ -/* - * linux/fs/sysv/symlink.c - * - * Handling of System V filesystem fast symlinks extensions. - * Aug 2001, Christoph Hellwig (hch@infradead.org) - */ - -#include "sysv.h" -#include <linux/namei.h> - -static void *sysv_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - nd_set_link(nd, (char *)SYSV_I(d_inode(dentry))->i_data); - return NULL; -} - -const struct inode_operations sysv_fast_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = sysv_follow_link, -}; diff --git a/fs/sysv/sysv.h b/fs/sysv/sysv.h index 69d488986..6c212288a 100644 --- a/fs/sysv/sysv.h +++ b/fs/sysv/sysv.h @@ -73,7 +73,7 @@ struct sysv_inode_info { static inline struct sysv_inode_info *SYSV_I(struct inode *inode) { - return list_entry(inode, struct sysv_inode_info, vfs_inode); + return container_of(inode, struct sysv_inode_info, vfs_inode); } static inline struct sysv_sb_info *SYSV_SB(struct super_block *sb) @@ -161,7 +161,6 @@ extern ino_t sysv_inode_by_name(struct dentry *); extern const struct inode_operations sysv_file_inode_operations; extern const struct inode_operations sysv_dir_inode_operations; -extern const struct inode_operations sysv_fast_symlink_inode_operations; extern const struct file_operations sysv_file_operations; extern const struct file_operations sysv_dir_operations; extern const struct address_space_operations sysv_aops; diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c index a43df11a1..cbc8d5d27 100644 --- a/fs/tracefs/inode.c +++ b/fs/tracefs/inode.c @@ -496,16 +496,11 @@ struct dentry *tracefs_create_instance_dir(const char *name, struct dentry *pare return dentry; } -static inline int tracefs_positive(struct dentry *dentry) -{ - return dentry->d_inode && !d_unhashed(dentry); -} - static int __tracefs_remove(struct dentry *dentry, struct dentry *parent) { int ret = 0; - if (tracefs_positive(dentry)) { + if (simple_positive(dentry)) { if (dentry->d_inode) { dget(dentry); switch (dentry->d_inode->i_mode & S_IFMT) { @@ -582,7 +577,7 @@ void tracefs_remove_recursive(struct dentry *dentry) */ spin_lock(&parent->d_lock); list_for_each_entry(child, &parent->d_subdirs, d_child) { - if (!tracefs_positive(child)) + if (!simple_positive(child)) continue; /* perhaps simple_empty(child) makes more sense */ @@ -603,7 +598,7 @@ void tracefs_remove_recursive(struct dentry *dentry) * from d_subdirs. When releasing the parent->d_lock we can * no longer trust that the next pointer is valid. * Restart the loop. We'll skip this one with the - * tracefs_positive() check. + * simple_positive() check. */ goto loop; } diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c index 27060fc85..5c27c66c2 100644 --- a/fs/ubifs/dir.c +++ b/fs/ubifs/dir.c @@ -889,6 +889,7 @@ static int ubifs_symlink(struct inode *dir, struct dentry *dentry, memcpy(ui->data, symname, len); ((char *)ui->data)[len] = '\0'; + inode->i_link = ui->data; /* * The terminating zero byte is not written to the flash media and it * is put just to make later in-memory string processing simpler. Thus, diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 35efc103c..a3dfe2ae7 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -51,7 +51,6 @@ #include "ubifs.h" #include <linux/mount.h> -#include <linux/namei.h> #include <linux/slab.h> static int read_block(struct inode *inode, void *addr, unsigned int block, @@ -1300,14 +1299,6 @@ static void ubifs_invalidatepage(struct page *page, unsigned int offset, ClearPageChecked(page); } -static void *ubifs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct ubifs_inode *ui = ubifs_inode(d_inode(dentry)); - - nd_set_link(nd, ui->data); - return NULL; -} - int ubifs_fsync(struct file *file, loff_t start, loff_t end, int datasync) { struct inode *inode = file->f_mapping->host; @@ -1570,7 +1561,7 @@ const struct inode_operations ubifs_file_inode_operations = { const struct inode_operations ubifs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = ubifs_follow_link, + .follow_link = simple_follow_link, .setattr = ubifs_setattr, .getattr = ubifs_getattr, .setxattr = ubifs_setxattr, diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 75e6f04bb..9547a2786 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -195,6 +195,7 @@ struct inode *ubifs_iget(struct super_block *sb, unsigned long inum) } memcpy(ui->data, ino->data, ui->data_len); ((char *)ui->data)[ui->data_len] = '\0'; + inode->i_link = ui->data; break; case S_IFBLK: case S_IFCHR: @@ -2245,7 +2246,9 @@ static int __init ubifs_init(void) if (!ubifs_inode_slab) return -ENOMEM; - register_shrinker(&ubifs_shrinker_info); + err = register_shrinker(&ubifs_shrinker_info); + if (err) + goto out_slab; err = ubifs_compressors_init(); if (err) @@ -2269,6 +2272,7 @@ out_compr: ubifs_compressors_exit(); out_shrinker: unregister_shrinker(&ubifs_shrinker_info); +out_slab: kmem_cache_destroy(ubifs_inode_slab); return err; } diff --git a/fs/udf/dir.c b/fs/udf/dir.c index 541a12b57..541d9c650 100644 --- a/fs/udf/dir.c +++ b/fs/udf/dir.c @@ -168,7 +168,7 @@ static int udf_readdir(struct file *file, struct dir_context *ctx) } flen = udf_get_filename(sb, nameptr, lfi, fname, UDF_NAME_LEN); - if (!flen) + if (flen < 0) continue; tloc = lelb_to_cpu(cfi.icb.extLocation); diff --git a/fs/udf/file.c b/fs/udf/file.c index 7a95b8fed..bddf3d071 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -152,8 +152,6 @@ out: mutex_unlock(&inode->i_mutex); if (retval > 0) { - ssize_t err; - mark_inode_dirty(inode); err = generic_write_sync(file, iocb->ki_pos - retval, retval); if (err < 0) diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 6afac3d56..8d0b3ade0 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -1652,17 +1652,9 @@ static int udf_update_inode(struct inode *inode, int do_sync) iinfo->i_ext.i_data, inode->i_sb->s_blocksize - sizeof(struct unallocSpaceEntry)); use->descTag.tagIdent = cpu_to_le16(TAG_IDENT_USE); - use->descTag.tagLocation = - cpu_to_le32(iinfo->i_location.logicalBlockNum); - crclen = sizeof(struct unallocSpaceEntry) + - iinfo->i_lenAlloc - sizeof(struct tag); - use->descTag.descCRCLength = cpu_to_le16(crclen); - use->descTag.descCRC = cpu_to_le16(crc_itu_t(0, (char *)use + - sizeof(struct tag), - crclen)); - use->descTag.tagChecksum = udf_tag_checksum(&use->descTag); + crclen = sizeof(struct unallocSpaceEntry); - goto out; + goto finish; } if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_UID_FORGET)) @@ -1782,6 +1774,8 @@ static int udf_update_inode(struct inode *inode, int do_sync) efe->descTag.tagIdent = cpu_to_le16(TAG_IDENT_EFE); crclen = sizeof(struct extendedFileEntry); } + +finish: if (iinfo->i_strat4096) { fe->icbTag.strategyType = cpu_to_le16(4096); fe->icbTag.strategyParameter = cpu_to_le16(1); @@ -1791,7 +1785,9 @@ static int udf_update_inode(struct inode *inode, int do_sync) fe->icbTag.numEntries = cpu_to_le16(1); } - if (S_ISDIR(inode->i_mode)) + if (iinfo->i_use) + fe->icbTag.fileType = ICBTAG_FILE_TYPE_USE; + else if (S_ISDIR(inode->i_mode)) fe->icbTag.fileType = ICBTAG_FILE_TYPE_DIRECTORY; else if (S_ISREG(inode->i_mode)) fe->icbTag.fileType = ICBTAG_FILE_TYPE_REGULAR; @@ -1828,7 +1824,6 @@ static int udf_update_inode(struct inode *inode, int do_sync) crclen)); fe->descTag.tagChecksum = udf_tag_checksum(&fe->descTag); -out: set_buffer_uptodate(bh); unlock_buffer(bh); diff --git a/fs/udf/namei.c b/fs/udf/namei.c index 5c03f0dfb..c97b5a8d1 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -138,6 +138,25 @@ int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi, return 0; } +/** + * udf_find_entry - find entry in given directory. + * + * @dir: directory inode to search in + * @child: qstr of the name + * @fibh: buffer head / inode with file identifier descriptor we found + * @cfi: found file identifier descriptor with given name + * + * This function searches in the directory @dir for a file name @child. When + * found, @fibh points to the buffer head(s) (bh is NULL for in ICB + * directories) containing the file identifier descriptor (FID). In that case + * the function returns pointer to the FID in the buffer or inode - but note + * that FID may be split among two buffers (blocks) so accessing it via that + * pointer isn't easily possible. This pointer can be used only as an iterator + * for other directory manipulation functions. For inspection of the FID @cfi + * can be used - the found FID is copied there. + * + * Returns pointer to FID, NULL when nothing found, or error code. + */ static struct fileIdentDesc *udf_find_entry(struct inode *dir, const struct qstr *child, struct udf_fileident_bh *fibh, @@ -167,8 +186,11 @@ static struct fileIdentDesc *udf_find_entry(struct inode *dir, fibh->soffset = fibh->eoffset = f_pos & (sb->s_blocksize - 1); if (dinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { if (inode_bmap(dir, f_pos >> sb->s_blocksize_bits, &epos, - &eloc, &elen, &offset) != (EXT_RECORDED_ALLOCATED >> 30)) + &eloc, &elen, &offset) != (EXT_RECORDED_ALLOCATED >> 30)) { + fi = ERR_PTR(-EIO); goto out_err; + } + block = udf_get_lb_pblock(sb, &eloc, offset); if ((++offset << sb->s_blocksize_bits) < elen) { if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) @@ -179,19 +201,25 @@ static struct fileIdentDesc *udf_find_entry(struct inode *dir, offset = 0; fibh->sbh = fibh->ebh = udf_tread(sb, block); - if (!fibh->sbh) + if (!fibh->sbh) { + fi = ERR_PTR(-EIO); goto out_err; + } } fname = kmalloc(UDF_NAME_LEN, GFP_NOFS); - if (!fname) + if (!fname) { + fi = ERR_PTR(-ENOMEM); goto out_err; + } while (f_pos < size) { fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &epos, &eloc, &elen, &offset); - if (!fi) + if (!fi) { + fi = ERR_PTR(-EIO); goto out_err; + } liu = le16_to_cpu(cfi->lengthOfImpUse); lfi = cfi->lengthFileIdent; @@ -234,12 +262,17 @@ static struct fileIdentDesc *udf_find_entry(struct inode *dir, continue; flen = udf_get_filename(sb, nameptr, lfi, fname, UDF_NAME_LEN); - if (flen && udf_match(flen, fname, child->len, child->name)) + if (flen < 0) { + fi = ERR_PTR(flen); + goto out_err; + } + + if (udf_match(flen, fname, child->len, child->name)) goto out_ok; } -out_err: fi = NULL; +out_err: if (fibh->sbh != fibh->ebh) brelse(fibh->ebh); brelse(fibh->sbh); @@ -256,6 +289,7 @@ static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry, struct inode *inode = NULL; struct fileIdentDesc cfi; struct udf_fileident_bh fibh; + struct fileIdentDesc *fi; if (dentry->d_name.len > UDF_NAME_LEN - 2) return ERR_PTR(-ENAMETOOLONG); @@ -275,7 +309,11 @@ static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry, } else #endif /* UDF_RECOVERY */ - if (udf_find_entry(dir, &dentry->d_name, &fibh, &cfi)) { + fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi); + if (IS_ERR(fi)) + return ERR_CAST(fi); + + if (fi) { struct kernel_lb_addr loc; if (fibh.sbh != fibh.ebh) @@ -774,8 +812,11 @@ static int udf_rmdir(struct inode *dir, struct dentry *dentry) retval = -ENOENT; fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi); - if (!fi) + if (IS_ERR_OR_NULL(fi)) { + if (fi) + retval = PTR_ERR(fi); goto out; + } retval = -EIO; tloc = lelb_to_cpu(cfi.icb.extLocation); @@ -817,8 +858,12 @@ static int udf_unlink(struct inode *dir, struct dentry *dentry) retval = -ENOENT; fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi); - if (!fi) + + if (IS_ERR_OR_NULL(fi)) { + if (fi) + retval = PTR_ERR(fi); goto out; + } retval = -EIO; tloc = lelb_to_cpu(cfi.icb.extLocation); @@ -1049,24 +1094,30 @@ static int udf_rename(struct inode *old_dir, struct dentry *old_dentry, struct udf_inode_info *old_iinfo = UDF_I(old_inode); ofi = udf_find_entry(old_dir, &old_dentry->d_name, &ofibh, &ocfi); - if (ofi) { - if (ofibh.sbh != ofibh.ebh) - brelse(ofibh.ebh); - brelse(ofibh.sbh); + if (IS_ERR(ofi)) { + retval = PTR_ERR(ofi); + goto end_rename; } + + if (ofibh.sbh != ofibh.ebh) + brelse(ofibh.ebh); + + brelse(ofibh.sbh); tloc = lelb_to_cpu(ocfi.icb.extLocation); if (!ofi || udf_get_lb_pblock(old_dir->i_sb, &tloc, 0) != old_inode->i_ino) goto end_rename; nfi = udf_find_entry(new_dir, &new_dentry->d_name, &nfibh, &ncfi); - if (nfi) { - if (!new_inode) { - if (nfibh.sbh != nfibh.ebh) - brelse(nfibh.ebh); - brelse(nfibh.sbh); - nfi = NULL; - } + if (IS_ERR(nfi)) { + retval = PTR_ERR(nfi); + goto end_rename; + } + if (nfi && !new_inode) { + if (nfibh.sbh != nfibh.ebh) + brelse(nfibh.ebh); + brelse(nfibh.sbh); + nfi = NULL; } if (S_ISDIR(old_inode->i_mode)) { int offset = udf_ext0_offset(old_inode); @@ -1221,7 +1272,7 @@ static struct dentry *udf_nfs_get_inode(struct super_block *sb, u32 block, static struct dentry *udf_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { - if ((fh_len != 3 && fh_len != 5) || + if (fh_len < 3 || (fh_type != FILEID_UDF_WITH_PARENT && fh_type != FILEID_UDF_WITHOUT_PARENT)) return NULL; @@ -1233,7 +1284,7 @@ static struct dentry *udf_fh_to_dentry(struct super_block *sb, static struct dentry *udf_fh_to_parent(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { - if (fh_len != 5 || fh_type != FILEID_UDF_WITH_PARENT) + if (fh_len < 5 || fh_type != FILEID_UDF_WITH_PARENT) return NULL; return udf_nfs_get_inode(sb, fid->udf.parent_block, diff --git a/fs/udf/super.c b/fs/udf/super.c index 6299f3419..b96f190bc 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -927,17 +927,23 @@ static int udf_load_pvoldesc(struct super_block *sb, sector_t block) #endif } - if (!udf_build_ustr(instr, pvoldesc->volIdent, 32)) - if (udf_CS0toUTF8(outstr, instr)) { - strncpy(UDF_SB(sb)->s_volume_ident, outstr->u_name, - outstr->u_len > 31 ? 31 : outstr->u_len); - udf_debug("volIdent[] = '%s'\n", - UDF_SB(sb)->s_volume_ident); - } + if (!udf_build_ustr(instr, pvoldesc->volIdent, 32)) { + ret = udf_CS0toUTF8(outstr, instr); + if (ret < 0) + goto out_bh; + + strncpy(UDF_SB(sb)->s_volume_ident, outstr->u_name, + outstr->u_len > 31 ? 31 : outstr->u_len); + udf_debug("volIdent[] = '%s'\n", UDF_SB(sb)->s_volume_ident); + } - if (!udf_build_ustr(instr, pvoldesc->volSetIdent, 128)) - if (udf_CS0toUTF8(outstr, instr)) - udf_debug("volSetIdent[] = '%s'\n", outstr->u_name); + if (!udf_build_ustr(instr, pvoldesc->volSetIdent, 128)) { + ret = udf_CS0toUTF8(outstr, instr); + if (ret < 0) + goto out_bh; + + udf_debug("volSetIdent[] = '%s'\n", outstr->u_name); + } ret = 0; out_bh: diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c index 8dfbc4025..862535b3b 100644 --- a/fs/udf/symlink.c +++ b/fs/udf/symlink.c @@ -82,6 +82,9 @@ static int udf_pc_to_char(struct super_block *sb, unsigned char *from, comp_len = udf_get_filename(sb, pc->componentIdent, pc->lengthComponentIdent, p, tolen); + if (comp_len < 0) + return comp_len; + p += comp_len; tolen -= comp_len; if (tolen == 0) diff --git a/fs/udf/udf_i.h b/fs/udf/udf_i.h index b5cd8ed2a..b1b9a63d8 100644 --- a/fs/udf/udf_i.h +++ b/fs/udf/udf_i.h @@ -56,7 +56,7 @@ struct udf_inode_info { static inline struct udf_inode_info *UDF_I(struct inode *inode) { - return list_entry(inode, struct udf_inode_info, vfs_inode); + return container_of(inode, struct udf_inode_info, vfs_inode); } #endif /* _UDF_I_H) */ diff --git a/fs/udf/unicode.c b/fs/udf/unicode.c index b84fee372..ab478e62b 100644 --- a/fs/udf/unicode.c +++ b/fs/udf/unicode.c @@ -68,21 +68,16 @@ int udf_build_ustr(struct ustr *dest, dstring *ptr, int size) /* * udf_build_ustr_exact */ -static int udf_build_ustr_exact(struct ustr *dest, dstring *ptr, int exactsize) +static void udf_build_ustr_exact(struct ustr *dest, dstring *ptr, int exactsize) { - if ((!dest) || (!ptr) || (!exactsize)) - return -1; - memset(dest, 0, sizeof(struct ustr)); dest->u_cmpID = ptr[0]; dest->u_len = exactsize - 1; memcpy(dest->u_name, ptr + 1, exactsize - 1); - - return 0; } /* - * udf_ocu_to_utf8 + * udf_CS0toUTF8 * * PURPOSE * Convert OSTA Compressed Unicode to the UTF-8 equivalent. @@ -94,7 +89,7 @@ static int udf_build_ustr_exact(struct ustr *dest, dstring *ptr, int exactsize) * both of type "struct ustr *" * * POST-CONDITIONS - * <return> Zero on success. + * <return> >= 0 on success. * * HISTORY * November 12, 1997 - Andrew E. Mileski @@ -117,7 +112,7 @@ int udf_CS0toUTF8(struct ustr *utf_o, const struct ustr *ocu_i) memset(utf_o, 0, sizeof(struct ustr)); pr_err("unknown compression code (%d) stri=%s\n", cmp_id, ocu_i->u_name); - return 0; + return -EINVAL; } ocu = ocu_i->u_name; @@ -154,7 +149,7 @@ int udf_CS0toUTF8(struct ustr *utf_o, const struct ustr *ocu_i) /* * - * udf_utf8_to_ocu + * udf_UTF8toCS0 * * PURPOSE * Convert UTF-8 to the OSTA Compressed Unicode equivalent. @@ -270,7 +265,7 @@ static int udf_CS0toNLS(struct nls_table *nls, struct ustr *utf_o, memset(utf_o, 0, sizeof(struct ustr)); pr_err("unknown compression code (%d) stri=%s\n", cmp_id, ocu_i->u_name); - return 0; + return -EINVAL; } ocu = ocu_i->u_name; @@ -338,43 +333,51 @@ int udf_get_filename(struct super_block *sb, uint8_t *sname, int slen, uint8_t *dname, int dlen) { struct ustr *filename, *unifilename; - int len = 0; + int ret; + + if (!slen) + return -EIO; filename = kmalloc(sizeof(struct ustr), GFP_NOFS); if (!filename) - return 0; + return -ENOMEM; unifilename = kmalloc(sizeof(struct ustr), GFP_NOFS); - if (!unifilename) + if (!unifilename) { + ret = -ENOMEM; goto out1; + } - if (udf_build_ustr_exact(unifilename, sname, slen)) - goto out2; - + udf_build_ustr_exact(unifilename, sname, slen); if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) { - if (!udf_CS0toUTF8(filename, unifilename)) { + ret = udf_CS0toUTF8(filename, unifilename); + if (ret < 0) { udf_debug("Failed in udf_get_filename: sname = %s\n", sname); goto out2; } } else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) { - if (!udf_CS0toNLS(UDF_SB(sb)->s_nls_map, filename, - unifilename)) { + ret = udf_CS0toNLS(UDF_SB(sb)->s_nls_map, filename, + unifilename); + if (ret < 0) { udf_debug("Failed in udf_get_filename: sname = %s\n", sname); goto out2; } } else - goto out2; + BUG(); - len = udf_translate_to_linux(dname, dlen, + ret = udf_translate_to_linux(dname, dlen, filename->u_name, filename->u_len, unifilename->u_name, unifilename->u_len); + /* Zero length filename isn't valid... */ + if (ret == 0) + ret = -EINVAL; out2: kfree(unifilename); out1: kfree(filename); - return len; + return ret; } int udf_put_filename(struct super_block *sb, const uint8_t *sname, diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c index 1bfe8cabf..74f2e8028 100644 --- a/fs/ufs/dir.c +++ b/fs/ufs/dir.c @@ -65,11 +65,6 @@ static inline void ufs_put_page(struct page *page) page_cache_release(page); } -static inline unsigned long ufs_dir_pages(struct inode *inode) -{ - return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT; -} - ino_t ufs_inode_by_name(struct inode *dir, const struct qstr *qstr) { ino_t res = 0; @@ -87,7 +82,8 @@ ino_t ufs_inode_by_name(struct inode *dir, const struct qstr *qstr) /* Releases the page */ void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de, - struct page *page, struct inode *inode) + struct page *page, struct inode *inode, + bool update_times) { loff_t pos = page_offset(page) + (char *) de - (char *) page_address(page); @@ -103,7 +99,8 @@ void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de, err = ufs_commit_chunk(page, pos, len); ufs_put_page(page); - dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; + if (update_times) + dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(dir); } @@ -256,7 +253,7 @@ struct ufs_dir_entry *ufs_find_entry(struct inode *dir, const struct qstr *qstr, int namelen = qstr->len; unsigned reclen = UFS_DIR_REC_LEN(namelen); unsigned long start, n; - unsigned long npages = ufs_dir_pages(dir); + unsigned long npages = dir_pages(dir); struct page *page = NULL; struct ufs_inode_info *ui = UFS_I(dir); struct ufs_dir_entry *de; @@ -320,7 +317,7 @@ int ufs_add_link(struct dentry *dentry, struct inode *inode) unsigned short rec_len, name_len; struct page *page = NULL; struct ufs_dir_entry *de; - unsigned long npages = ufs_dir_pages(dir); + unsigned long npages = dir_pages(dir); unsigned long n; char *kaddr; loff_t pos; @@ -437,7 +434,7 @@ ufs_readdir(struct file *file, struct dir_context *ctx) struct super_block *sb = inode->i_sb; unsigned int offset = pos & ~PAGE_CACHE_MASK; unsigned long n = pos >> PAGE_CACHE_SHIFT; - unsigned long npages = ufs_dir_pages(inode); + unsigned long npages = dir_pages(inode); unsigned chunk_mask = ~(UFS_SB(sb)->s_uspi->s_dirblksize - 1); int need_revalidate = file->f_version != inode->i_version; unsigned flags = UFS_SB(sb)->s_flags; @@ -608,7 +605,7 @@ int ufs_empty_dir(struct inode * inode) { struct super_block *sb = inode->i_sb; struct page *page = NULL; - unsigned long i, npages = ufs_dir_pages(inode); + unsigned long i, npages = dir_pages(inode); for (i = 0; i < npages; i++) { char *kaddr; diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 2d93ab07d..f913a6924 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -572,9 +572,10 @@ static void ufs_set_inode_ops(struct inode *inode) inode->i_fop = &ufs_dir_operations; inode->i_mapping->a_ops = &ufs_aops; } else if (S_ISLNK(inode->i_mode)) { - if (!inode->i_blocks) + if (!inode->i_blocks) { inode->i_op = &ufs_fast_symlink_inode_operations; - else { + inode->i_link = (char *)UFS_I(inode)->i_u1.i_symlink; + } else { inode->i_op = &ufs_symlink_inode_operations; inode->i_mapping->a_ops = &ufs_aops; } diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c index 60ee32249..479665543 100644 --- a/fs/ufs/namei.c +++ b/fs/ufs/namei.c @@ -56,11 +56,9 @@ static struct dentry *ufs_lookup(struct inode * dir, struct dentry *dentry, unsi if (dentry->d_name.len > UFS_MAXNAMLEN) return ERR_PTR(-ENAMETOOLONG); - lock_ufs(dir->i_sb); ino = ufs_inode_by_name(dir, &dentry->d_name); if (ino) inode = ufs_iget(dir->i_sb, ino); - unlock_ufs(dir->i_sb); return d_splice_alias(inode, dentry); } @@ -76,24 +74,16 @@ static int ufs_create (struct inode * dir, struct dentry * dentry, umode_t mode, bool excl) { struct inode *inode; - int err; - - UFSD("BEGIN\n"); inode = ufs_new_inode(dir, mode); - err = PTR_ERR(inode); + if (IS_ERR(inode)) + return PTR_ERR(inode); - if (!IS_ERR(inode)) { - inode->i_op = &ufs_file_inode_operations; - inode->i_fop = &ufs_file_operations; - inode->i_mapping->a_ops = &ufs_aops; - mark_inode_dirty(inode); - lock_ufs(dir->i_sb); - err = ufs_add_nondir(dentry, inode); - unlock_ufs(dir->i_sb); - } - UFSD("END: err=%d\n", err); - return err; + inode->i_op = &ufs_file_inode_operations; + inode->i_fop = &ufs_file_operations; + inode->i_mapping->a_ops = &ufs_aops; + mark_inode_dirty(inode); + return ufs_add_nondir(dentry, inode); } static int ufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev) @@ -110,9 +100,7 @@ static int ufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev init_special_inode(inode, mode, rdev); ufs_set_inode_dev(inode->i_sb, UFS_I(inode), rdev); mark_inode_dirty(inode); - lock_ufs(dir->i_sb); err = ufs_add_nondir(dentry, inode); - unlock_ufs(dir->i_sb); } return err; } @@ -121,18 +109,17 @@ static int ufs_symlink (struct inode * dir, struct dentry * dentry, const char * symname) { struct super_block * sb = dir->i_sb; - int err = -ENAMETOOLONG; + int err; unsigned l = strlen(symname)+1; struct inode * inode; if (l > sb->s_blocksize) - goto out_notlocked; + return -ENAMETOOLONG; - lock_ufs(dir->i_sb); inode = ufs_new_inode(dir, S_IFLNK | S_IRWXUGO); err = PTR_ERR(inode); if (IS_ERR(inode)) - goto out; + return err; if (l > UFS_SB(sb)->s_uspi->s_maxsymlinklen) { /* slow symlink */ @@ -144,22 +131,19 @@ static int ufs_symlink (struct inode * dir, struct dentry * dentry, } else { /* fast symlink */ inode->i_op = &ufs_fast_symlink_inode_operations; - memcpy(UFS_I(inode)->i_u1.i_symlink, symname, l); + inode->i_link = (char *)UFS_I(inode)->i_u1.i_symlink; + memcpy(inode->i_link, symname, l); inode->i_size = l-1; } mark_inode_dirty(inode); - err = ufs_add_nondir(dentry, inode); -out: - unlock_ufs(dir->i_sb); -out_notlocked: - return err; + return ufs_add_nondir(dentry, inode); out_fail: inode_dec_link_count(inode); unlock_new_inode(inode); iput(inode); - goto out; + return err; } static int ufs_link (struct dentry * old_dentry, struct inode * dir, @@ -168,8 +152,6 @@ static int ufs_link (struct dentry * old_dentry, struct inode * dir, struct inode *inode = d_inode(old_dentry); int error; - lock_ufs(dir->i_sb); - inode->i_ctime = CURRENT_TIME_SEC; inode_inc_link_count(inode); ihold(inode); @@ -180,7 +162,6 @@ static int ufs_link (struct dentry * old_dentry, struct inode * dir, iput(inode); } else d_instantiate(dentry, inode); - unlock_ufs(dir->i_sb); return error; } @@ -189,7 +170,6 @@ static int ufs_mkdir(struct inode * dir, struct dentry * dentry, umode_t mode) struct inode * inode; int err; - lock_ufs(dir->i_sb); inode_inc_link_count(dir); inode = ufs_new_inode(dir, S_IFDIR|mode); @@ -210,12 +190,10 @@ static int ufs_mkdir(struct inode * dir, struct dentry * dentry, umode_t mode) err = ufs_add_link(dentry, inode); if (err) goto out_fail; - unlock_ufs(dir->i_sb); unlock_new_inode(inode); d_instantiate(dentry, inode); -out: - return err; + return 0; out_fail: inode_dec_link_count(inode); @@ -224,8 +202,7 @@ out_fail: iput (inode); out_dir: inode_dec_link_count(dir); - unlock_ufs(dir->i_sb); - goto out; + return err; } static int ufs_unlink(struct inode *dir, struct dentry *dentry) @@ -255,7 +232,6 @@ static int ufs_rmdir (struct inode * dir, struct dentry *dentry) struct inode * inode = d_inode(dentry); int err= -ENOTEMPTY; - lock_ufs(dir->i_sb); if (ufs_empty_dir (inode)) { err = ufs_unlink(dir, dentry); if (!err) { @@ -264,7 +240,6 @@ static int ufs_rmdir (struct inode * dir, struct dentry *dentry) inode_dec_link_count(dir); } } - unlock_ufs(dir->i_sb); return err; } @@ -302,7 +277,7 @@ static int ufs_rename(struct inode *old_dir, struct dentry *old_dentry, new_de = ufs_find_entry(new_dir, &new_dentry->d_name, &new_page); if (!new_de) goto out_dir; - ufs_set_link(new_dir, new_de, new_page, old_inode); + ufs_set_link(new_dir, new_de, new_page, old_inode, 1); new_inode->i_ctime = CURRENT_TIME_SEC; if (dir_de) drop_nlink(new_inode); @@ -325,7 +300,12 @@ static int ufs_rename(struct inode *old_dir, struct dentry *old_dentry, mark_inode_dirty(old_inode); if (dir_de) { - ufs_set_link(old_inode, dir_de, dir_page, new_dir); + if (old_dir != new_dir) + ufs_set_link(old_inode, dir_de, dir_page, new_dir, 0); + else { + kunmap(dir_page); + page_cache_release(dir_page); + } inode_dec_link_count(old_dir); } return 0; diff --git a/fs/ufs/super.c b/fs/ufs/super.c index dc33f9416..250579a80 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -80,6 +80,7 @@ #include <linux/stat.h> #include <linux/string.h> #include <linux/blkdev.h> +#include <linux/backing-dev.h> #include <linux/init.h> #include <linux/parser.h> #include <linux/buffer_head.h> diff --git a/fs/ufs/symlink.c b/fs/ufs/symlink.c index 5b537e2fd..874480bb4 100644 --- a/fs/ufs/symlink.c +++ b/fs/ufs/symlink.c @@ -25,23 +25,12 @@ * ext2 symlink handling code */ -#include <linux/fs.h> -#include <linux/namei.h> - #include "ufs_fs.h" #include "ufs.h" - -static void *ufs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct ufs_inode_info *p = UFS_I(d_inode(dentry)); - nd_set_link(nd, (char*)p->i_u1.i_symlink); - return NULL; -} - const struct inode_operations ufs_fast_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = ufs_follow_link, + .follow_link = simple_follow_link, .setattr = ufs_setattr, }; diff --git a/fs/ufs/ufs.h b/fs/ufs/ufs.h index cf6368d42..2e31ea2e3 100644 --- a/fs/ufs/ufs.h +++ b/fs/ufs/ufs.h @@ -106,7 +106,7 @@ extern int ufs_delete_entry(struct inode *, struct ufs_dir_entry *, struct page extern int ufs_empty_dir (struct inode *); extern struct ufs_dir_entry *ufs_dotdot(struct inode *, struct page **); extern void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de, - struct page *page, struct inode *inode); + struct page *page, struct inode *inode, bool update_times); /* file.c */ extern const struct inode_operations ufs_file_inode_operations; diff --git a/fs/xattr.c b/fs/xattr.c index 6bb630399..072fee125 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -207,7 +207,6 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value, *xattr_value = value; return error; } -EXPORT_SYMBOL(vfs_getxattr_alloc); /* Compare an extended attribute value with the given value */ int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name, @@ -299,18 +298,18 @@ vfs_removexattr(struct dentry *dentry, const char *name) mutex_lock(&inode->i_mutex); error = security_inode_removexattr(dentry, name); - if (error) { - mutex_unlock(&inode->i_mutex); - return error; - } + if (error) + goto out; error = inode->i_op->removexattr(dentry, name); - mutex_unlock(&inode->i_mutex); if (!error) { fsnotify_xattr(dentry); evm_inode_post_removexattr(dentry, name); } + +out: + mutex_unlock(&inode->i_mutex); return error; } EXPORT_SYMBOL_GPL(vfs_removexattr); diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c index 516162be1..f9e9ffe6f 100644 --- a/fs/xfs/libxfs/xfs_alloc.c +++ b/fs/xfs/libxfs/xfs_alloc.c @@ -149,13 +149,27 @@ xfs_alloc_compute_aligned( { xfs_agblock_t bno; xfs_extlen_t len; + xfs_extlen_t diff; /* Trim busy sections out of found extent */ xfs_extent_busy_trim(args, foundbno, foundlen, &bno, &len); + /* + * If we have a largish extent that happens to start before min_agbno, + * see if we can shift it into range... + */ + if (bno < args->min_agbno && bno + len > args->min_agbno) { + diff = args->min_agbno - bno; + if (len > diff) { + bno += diff; + len -= diff; + } + } + if (args->alignment > 1 && len >= args->minlen) { xfs_agblock_t aligned_bno = roundup(bno, args->alignment); - xfs_extlen_t diff = aligned_bno - bno; + + diff = aligned_bno - bno; *resbno = aligned_bno; *reslen = diff >= len ? 0 : len - diff; @@ -795,9 +809,13 @@ xfs_alloc_find_best_extent( * The good extent is closer than this one. */ if (!dir) { + if (*sbnoa > args->max_agbno) + goto out_use_good; if (*sbnoa >= args->agbno + gdiff) goto out_use_good; } else { + if (*sbnoa < args->min_agbno) + goto out_use_good; if (*sbnoa <= args->agbno - gdiff) goto out_use_good; } @@ -884,6 +902,17 @@ xfs_alloc_ag_vextent_near( dofirst = prandom_u32() & 1; #endif + /* handle unitialized agbno range so caller doesn't have to */ + if (!args->min_agbno && !args->max_agbno) + args->max_agbno = args->mp->m_sb.sb_agblocks - 1; + ASSERT(args->min_agbno <= args->max_agbno); + + /* clamp agbno to the range if it's outside */ + if (args->agbno < args->min_agbno) + args->agbno = args->min_agbno; + if (args->agbno > args->max_agbno) + args->agbno = args->max_agbno; + restart: bno_cur_lt = NULL; bno_cur_gt = NULL; @@ -976,6 +1005,8 @@ restart: <bnoa, <lena); if (ltlena < args->minlen) continue; + if (ltbnoa < args->min_agbno || ltbnoa > args->max_agbno) + continue; args->len = XFS_EXTLEN_MIN(ltlena, args->maxlen); xfs_alloc_fix_len(args); ASSERT(args->len >= args->minlen); @@ -1096,11 +1127,11 @@ restart: XFS_WANT_CORRUPTED_GOTO(args->mp, i == 1, error0); xfs_alloc_compute_aligned(args, ltbno, ltlen, <bnoa, <lena); - if (ltlena >= args->minlen) + if (ltlena >= args->minlen && ltbnoa >= args->min_agbno) break; if ((error = xfs_btree_decrement(bno_cur_lt, 0, &i))) goto error0; - if (!i) { + if (!i || ltbnoa < args->min_agbno) { xfs_btree_del_cursor(bno_cur_lt, XFS_BTREE_NOERROR); bno_cur_lt = NULL; @@ -1112,11 +1143,11 @@ restart: XFS_WANT_CORRUPTED_GOTO(args->mp, i == 1, error0); xfs_alloc_compute_aligned(args, gtbno, gtlen, >bnoa, >lena); - if (gtlena >= args->minlen) + if (gtlena >= args->minlen && gtbnoa <= args->max_agbno) break; if ((error = xfs_btree_increment(bno_cur_gt, 0, &i))) goto error0; - if (!i) { + if (!i || gtbnoa > args->max_agbno) { xfs_btree_del_cursor(bno_cur_gt, XFS_BTREE_NOERROR); bno_cur_gt = NULL; @@ -1216,6 +1247,7 @@ restart: ASSERT(ltnew >= ltbno); ASSERT(ltnew + rlen <= ltbnoa + ltlena); ASSERT(ltnew + rlen <= be32_to_cpu(XFS_BUF_TO_AGF(args->agbp)->agf_length)); + ASSERT(ltnew >= args->min_agbno && ltnew <= args->max_agbno); args->agbno = ltnew; if ((error = xfs_alloc_fixup_trees(cnt_cur, bno_cur_lt, ltbno, ltlen, @@ -1825,11 +1857,11 @@ xfs_alloc_compute_maxlevels( xfs_extlen_t xfs_alloc_longest_free_extent( struct xfs_mount *mp, - struct xfs_perag *pag) + struct xfs_perag *pag, + xfs_extlen_t need) { - xfs_extlen_t need, delta = 0; + xfs_extlen_t delta = 0; - need = XFS_MIN_FREELIST_PAG(pag, mp); if (need > pag->pagf_flcount) delta = need - pag->pagf_flcount; @@ -1838,131 +1870,150 @@ xfs_alloc_longest_free_extent( return pag->pagf_flcount > 0 || pag->pagf_longest > 0; } +unsigned int +xfs_alloc_min_freelist( + struct xfs_mount *mp, + struct xfs_perag *pag) +{ + unsigned int min_free; + + /* space needed by-bno freespace btree */ + min_free = min_t(unsigned int, pag->pagf_levels[XFS_BTNUM_BNOi] + 1, + mp->m_ag_maxlevels); + /* space needed by-size freespace btree */ + min_free += min_t(unsigned int, pag->pagf_levels[XFS_BTNUM_CNTi] + 1, + mp->m_ag_maxlevels); + + return min_free; +} + +/* + * Check if the operation we are fixing up the freelist for should go ahead or + * not. If we are freeing blocks, we always allow it, otherwise the allocation + * is dependent on whether the size and shape of free space available will + * permit the requested allocation to take place. + */ +static bool +xfs_alloc_space_available( + struct xfs_alloc_arg *args, + xfs_extlen_t min_free, + int flags) +{ + struct xfs_perag *pag = args->pag; + xfs_extlen_t longest; + int available; + + if (flags & XFS_ALLOC_FLAG_FREEING) + return true; + + /* do we have enough contiguous free space for the allocation? */ + longest = xfs_alloc_longest_free_extent(args->mp, pag, min_free); + if ((args->minlen + args->alignment + args->minalignslop - 1) > longest) + return false; + + /* do have enough free space remaining for the allocation? */ + available = (int)(pag->pagf_freeblks + pag->pagf_flcount - + min_free - args->total); + if (available < (int)args->minleft) + return false; + + return true; +} + /* * Decide whether to use this allocation group for this allocation. * If so, fix up the btree freelist's size. */ STATIC int /* error */ xfs_alloc_fix_freelist( - xfs_alloc_arg_t *args, /* allocation argument structure */ - int flags) /* XFS_ALLOC_FLAG_... */ + struct xfs_alloc_arg *args, /* allocation argument structure */ + int flags) /* XFS_ALLOC_FLAG_... */ { - xfs_buf_t *agbp; /* agf buffer pointer */ - xfs_agf_t *agf; /* a.g. freespace structure pointer */ - xfs_buf_t *agflbp;/* agfl buffer pointer */ - xfs_agblock_t bno; /* freelist block */ - xfs_extlen_t delta; /* new blocks needed in freelist */ - int error; /* error result code */ - xfs_extlen_t longest;/* longest extent in allocation group */ - xfs_mount_t *mp; /* file system mount point structure */ - xfs_extlen_t need; /* total blocks needed in freelist */ - xfs_perag_t *pag; /* per-ag information structure */ - xfs_alloc_arg_t targs; /* local allocation arguments */ - xfs_trans_t *tp; /* transaction pointer */ - - mp = args->mp; + struct xfs_mount *mp = args->mp; + struct xfs_perag *pag = args->pag; + struct xfs_trans *tp = args->tp; + struct xfs_buf *agbp = NULL; + struct xfs_buf *agflbp = NULL; + struct xfs_alloc_arg targs; /* local allocation arguments */ + xfs_agblock_t bno; /* freelist block */ + xfs_extlen_t need; /* total blocks needed in freelist */ + int error; - pag = args->pag; - tp = args->tp; if (!pag->pagf_init) { - if ((error = xfs_alloc_read_agf(mp, tp, args->agno, flags, - &agbp))) - return error; + error = xfs_alloc_read_agf(mp, tp, args->agno, flags, &agbp); + if (error) + goto out_no_agbp; if (!pag->pagf_init) { ASSERT(flags & XFS_ALLOC_FLAG_TRYLOCK); ASSERT(!(flags & XFS_ALLOC_FLAG_FREEING)); - args->agbp = NULL; - return 0; + goto out_agbp_relse; } - } else - agbp = NULL; + } /* - * If this is a metadata preferred pag and we are user data - * then try somewhere else if we are not being asked to - * try harder at this point + * If this is a metadata preferred pag and we are user data then try + * somewhere else if we are not being asked to try harder at this + * point */ if (pag->pagf_metadata && args->userdata && (flags & XFS_ALLOC_FLAG_TRYLOCK)) { ASSERT(!(flags & XFS_ALLOC_FLAG_FREEING)); - args->agbp = NULL; - return 0; + goto out_agbp_relse; } - if (!(flags & XFS_ALLOC_FLAG_FREEING)) { - /* - * If it looks like there isn't a long enough extent, or enough - * total blocks, reject it. - */ - need = XFS_MIN_FREELIST_PAG(pag, mp); - longest = xfs_alloc_longest_free_extent(mp, pag); - if ((args->minlen + args->alignment + args->minalignslop - 1) > - longest || - ((int)(pag->pagf_freeblks + pag->pagf_flcount - - need - args->total) < (int)args->minleft)) { - if (agbp) - xfs_trans_brelse(tp, agbp); - args->agbp = NULL; - return 0; - } - } + need = xfs_alloc_min_freelist(mp, pag); + if (!xfs_alloc_space_available(args, need, flags)) + goto out_agbp_relse; /* * Get the a.g. freespace buffer. * Can fail if we're not blocking on locks, and it's held. */ - if (agbp == NULL) { - if ((error = xfs_alloc_read_agf(mp, tp, args->agno, flags, - &agbp))) - return error; - if (agbp == NULL) { + if (!agbp) { + error = xfs_alloc_read_agf(mp, tp, args->agno, flags, &agbp); + if (error) + goto out_no_agbp; + if (!agbp) { ASSERT(flags & XFS_ALLOC_FLAG_TRYLOCK); ASSERT(!(flags & XFS_ALLOC_FLAG_FREEING)); - args->agbp = NULL; - return 0; - } - } - /* - * Figure out how many blocks we should have in the freelist. - */ - agf = XFS_BUF_TO_AGF(agbp); - need = XFS_MIN_FREELIST(agf, mp); - /* - * If there isn't enough total or single-extent, reject it. - */ - if (!(flags & XFS_ALLOC_FLAG_FREEING)) { - delta = need > be32_to_cpu(agf->agf_flcount) ? - (need - be32_to_cpu(agf->agf_flcount)) : 0; - longest = be32_to_cpu(agf->agf_longest); - longest = (longest > delta) ? (longest - delta) : - (be32_to_cpu(agf->agf_flcount) > 0 || longest > 0); - if ((args->minlen + args->alignment + args->minalignslop - 1) > - longest || - ((int)(be32_to_cpu(agf->agf_freeblks) + - be32_to_cpu(agf->agf_flcount) - need - args->total) < - (int)args->minleft)) { - xfs_trans_brelse(tp, agbp); - args->agbp = NULL; - return 0; + goto out_no_agbp; } } + + /* If there isn't enough total space or single-extent, reject it. */ + need = xfs_alloc_min_freelist(mp, pag); + if (!xfs_alloc_space_available(args, need, flags)) + goto out_agbp_relse; + /* * Make the freelist shorter if it's too long. + * + * Note that from this point onwards, we will always release the agf and + * agfl buffers on error. This handles the case where we error out and + * the buffers are clean or may not have been joined to the transaction + * and hence need to be released manually. If they have been joined to + * the transaction, then xfs_trans_brelse() will handle them + * appropriately based on the recursion count and dirty state of the + * buffer. + * + * XXX (dgc): When we have lots of free space, does this buy us + * anything other than extra overhead when we need to put more blocks + * back on the free list? Maybe we should only do this when space is + * getting low or the AGFL is more than half full? */ - while (be32_to_cpu(agf->agf_flcount) > need) { - xfs_buf_t *bp; + while (pag->pagf_flcount > need) { + struct xfs_buf *bp; error = xfs_alloc_get_freelist(tp, agbp, &bno, 0); if (error) - return error; - if ((error = xfs_free_ag_extent(tp, agbp, args->agno, bno, 1, 1))) - return error; + goto out_agbp_relse; + error = xfs_free_ag_extent(tp, agbp, args->agno, bno, 1, 1); + if (error) + goto out_agbp_relse; bp = xfs_btree_get_bufs(mp, tp, args->agno, bno, 0); xfs_trans_binval(tp, bp); } - /* - * Initialize the args structure. - */ + memset(&targs, 0, sizeof(targs)); targs.tp = tp; targs.mp = mp; @@ -1971,21 +2022,20 @@ xfs_alloc_fix_freelist( targs.alignment = targs.minlen = targs.prod = targs.isfl = 1; targs.type = XFS_ALLOCTYPE_THIS_AG; targs.pag = pag; - if ((error = xfs_alloc_read_agfl(mp, tp, targs.agno, &agflbp))) - return error; - /* - * Make the freelist longer if it's too short. - */ - while (be32_to_cpu(agf->agf_flcount) < need) { + error = xfs_alloc_read_agfl(mp, tp, targs.agno, &agflbp); + if (error) + goto out_agbp_relse; + + /* Make the freelist longer if it's too short. */ + while (pag->pagf_flcount < need) { targs.agbno = 0; - targs.maxlen = need - be32_to_cpu(agf->agf_flcount); - /* - * Allocate as many blocks as possible at once. - */ - if ((error = xfs_alloc_ag_vextent(&targs))) { - xfs_trans_brelse(tp, agflbp); - return error; - } + targs.maxlen = need - pag->pagf_flcount; + + /* Allocate as many blocks as possible at once. */ + error = xfs_alloc_ag_vextent(&targs); + if (error) + goto out_agflbp_relse; + /* * Stop if we run out. Won't happen if callers are obeying * the restrictions correctly. Can happen for free calls @@ -1994,9 +2044,7 @@ xfs_alloc_fix_freelist( if (targs.agbno == NULLAGBLOCK) { if (flags & XFS_ALLOC_FLAG_FREEING) break; - xfs_trans_brelse(tp, agflbp); - args->agbp = NULL; - return 0; + goto out_agflbp_relse; } /* * Put each allocated block on the list. @@ -2005,12 +2053,21 @@ xfs_alloc_fix_freelist( error = xfs_alloc_put_freelist(tp, agbp, agflbp, bno, 0); if (error) - return error; + goto out_agflbp_relse; } } xfs_trans_brelse(tp, agflbp); args->agbp = agbp; return 0; + +out_agflbp_relse: + xfs_trans_brelse(tp, agflbp); +out_agbp_relse: + if (agbp) + xfs_trans_brelse(tp, agbp); +out_no_agbp: + args->agbp = NULL; + return error; } /* diff --git a/fs/xfs/libxfs/xfs_alloc.h b/fs/xfs/libxfs/xfs_alloc.h index d1b4b6a5c..ca1c81683 100644 --- a/fs/xfs/libxfs/xfs_alloc.h +++ b/fs/xfs/libxfs/xfs_alloc.h @@ -112,6 +112,8 @@ typedef struct xfs_alloc_arg { xfs_extlen_t total; /* total blocks needed in xaction */ xfs_extlen_t alignment; /* align answer to multiple of this */ xfs_extlen_t minalignslop; /* slop for minlen+alignment calcs */ + xfs_agblock_t min_agbno; /* set an agbno range for NEAR allocs */ + xfs_agblock_t max_agbno; /* ... */ xfs_extlen_t len; /* output: actual size of extent */ xfs_alloctype_t type; /* allocation type XFS_ALLOCTYPE_... */ xfs_alloctype_t otype; /* original allocation type */ @@ -128,11 +130,9 @@ typedef struct xfs_alloc_arg { #define XFS_ALLOC_USERDATA 1 /* allocation is for user data*/ #define XFS_ALLOC_INITIAL_USER_DATA 2 /* special case start of file */ -/* - * Find the length of the longest extent in an AG. - */ -xfs_extlen_t -xfs_alloc_longest_free_extent(struct xfs_mount *mp, +xfs_extlen_t xfs_alloc_longest_free_extent(struct xfs_mount *mp, + struct xfs_perag *pag, xfs_extlen_t need); +unsigned int xfs_alloc_min_freelist(struct xfs_mount *mp, struct xfs_perag *pag); /* diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 0a472fbe0..3349c9a1e 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -266,7 +266,7 @@ xfs_attr_set( tres.tr_logflags = XFS_TRANS_PERM_LOG_RES; error = xfs_trans_reserve(args.trans, &tres, args.total, 0); if (error) { - xfs_trans_cancel(args.trans, 0); + xfs_trans_cancel(args.trans); return error; } xfs_ilock(dp, XFS_ILOCK_EXCL); @@ -276,7 +276,7 @@ xfs_attr_set( XFS_QMOPT_RES_REGBLKS); if (error) { xfs_iunlock(dp, XFS_ILOCK_EXCL); - xfs_trans_cancel(args.trans, XFS_TRANS_RELEASE_LOG_RES); + xfs_trans_cancel(args.trans); return error; } @@ -320,8 +320,7 @@ xfs_attr_set( xfs_trans_ichgtime(args.trans, dp, XFS_ICHGTIME_CHG); } - err2 = xfs_trans_commit(args.trans, - XFS_TRANS_RELEASE_LOG_RES); + err2 = xfs_trans_commit(args.trans); xfs_iunlock(dp, XFS_ILOCK_EXCL); return error ? error : err2; @@ -383,16 +382,14 @@ xfs_attr_set( * Commit the last in the sequence of transactions. */ xfs_trans_log_inode(args.trans, dp, XFS_ILOG_CORE); - error = xfs_trans_commit(args.trans, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(args.trans); xfs_iunlock(dp, XFS_ILOCK_EXCL); return error; out: - if (args.trans) { - xfs_trans_cancel(args.trans, - XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT); - } + if (args.trans) + xfs_trans_cancel(args.trans); xfs_iunlock(dp, XFS_ILOCK_EXCL); return error; } @@ -462,7 +459,7 @@ xfs_attr_remove( error = xfs_trans_reserve(args.trans, &M_RES(mp)->tr_attrrm, XFS_ATTRRM_SPACE_RES(mp), 0); if (error) { - xfs_trans_cancel(args.trans, 0); + xfs_trans_cancel(args.trans); return error; } @@ -501,16 +498,14 @@ xfs_attr_remove( * Commit the last in the sequence of transactions. */ xfs_trans_log_inode(args.trans, dp, XFS_ILOG_CORE); - error = xfs_trans_commit(args.trans, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(args.trans); xfs_iunlock(dp, XFS_ILOCK_EXCL); return error; out: - if (args.trans) { - xfs_trans_cancel(args.trans, - XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT); - } + if (args.trans) + xfs_trans_cancel(args.trans); xfs_iunlock(dp, XFS_ILOCK_EXCL); return error; } diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index f1026e86d..63e05b663 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c @@ -1112,7 +1112,6 @@ xfs_bmap_add_attrfork( int committed; /* xaction was committed */ int logflags; /* logging flags */ int error; /* error return value */ - int cancel_flags = 0; ASSERT(XFS_IFORK_Q(ip) == 0); @@ -1124,17 +1123,15 @@ xfs_bmap_add_attrfork( tp->t_flags |= XFS_TRANS_RESERVE; error = xfs_trans_reserve(tp, &M_RES(mp)->tr_addafork, blks, 0); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); return error; } - cancel_flags = XFS_TRANS_RELEASE_LOG_RES; xfs_ilock(ip, XFS_ILOCK_EXCL); error = xfs_trans_reserve_quota_nblks(tp, ip, blks, 0, rsvd ? XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_FORCE_RES : XFS_QMOPT_RES_REGBLKS); if (error) goto trans_cancel; - cancel_flags |= XFS_TRANS_ABORT; if (XFS_IFORK_Q(ip)) goto trans_cancel; if (ip->i_d.di_aformat != XFS_DINODE_FMT_EXTENTS) { @@ -1218,14 +1215,14 @@ xfs_bmap_add_attrfork( error = xfs_bmap_finish(&tp, &flist, &committed); if (error) goto bmap_cancel; - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); xfs_iunlock(ip, XFS_ILOCK_EXCL); return error; bmap_cancel: xfs_bmap_cancel(&flist); trans_cancel: - xfs_trans_cancel(tp, cancel_flags); + xfs_trans_cancel(tp); xfs_iunlock(ip, XFS_ILOCK_EXCL); return error; } @@ -3521,7 +3518,8 @@ xfs_bmap_longest_free_extent( } } - longest = xfs_alloc_longest_free_extent(mp, pag); + longest = xfs_alloc_longest_free_extent(mp, pag, + xfs_alloc_min_freelist(mp, pag)); if (*blen < longest) *blen = longest; @@ -4424,7 +4422,15 @@ xfs_bmapi_convert_unwritten( error = xfs_bmap_add_extent_unwritten_real(bma->tp, bma->ip, &bma->idx, &bma->cur, mval, bma->firstblock, bma->flist, &tmp_logflags); - bma->logflags |= tmp_logflags; + /* + * Log the inode core unconditionally in the unwritten extent conversion + * path because the conversion might not have done so (e.g., if the + * extent count hasn't changed). We need to make sure the inode is dirty + * in the transaction for the sake of fsync(), even if nothing has + * changed, because fsync() will not force the log for this transaction + * unless it sees the inode pinned. + */ + bma->logflags |= tmp_logflags | XFS_ILOG_CORE; if (error) return error; @@ -5918,7 +5924,7 @@ xfs_bmap_split_extent( error = xfs_trans_reserve(tp, &M_RES(mp)->tr_write, XFS_DIOSTRAT_SPACE_RES(mp, 0), 0); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); return error; } @@ -5936,10 +5942,9 @@ xfs_bmap_split_extent( if (error) goto out; - return xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); - + return xfs_trans_commit(tp); out: - xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT); + xfs_trans_cancel(tp); return error; } diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h index 4daaa6623..a0ae57205 100644 --- a/fs/xfs/libxfs/xfs_format.h +++ b/fs/xfs/libxfs/xfs_format.h @@ -170,7 +170,7 @@ typedef struct xfs_sb { __uint32_t sb_features_log_incompat; __uint32_t sb_crc; /* superblock crc */ - __uint32_t sb_pad; + xfs_extlen_t sb_spino_align; /* sparse inode chunk alignment */ xfs_ino_t sb_pquotino; /* project quota inode */ xfs_lsn_t sb_lsn; /* last write sequence */ @@ -256,7 +256,7 @@ typedef struct xfs_dsb { __be32 sb_features_log_incompat; __le32 sb_crc; /* superblock crc */ - __be32 sb_pad; + __be32 sb_spino_align; /* sparse inode chunk alignment */ __be64 sb_pquotino; /* project quota inode */ __be64 sb_lsn; /* last write sequence */ @@ -457,8 +457,10 @@ xfs_sb_has_ro_compat_feature( } #define XFS_SB_FEAT_INCOMPAT_FTYPE (1 << 0) /* filetype in dirent */ +#define XFS_SB_FEAT_INCOMPAT_SPINODES (1 << 1) /* sparse inode chunks */ #define XFS_SB_FEAT_INCOMPAT_ALL \ - (XFS_SB_FEAT_INCOMPAT_FTYPE) + (XFS_SB_FEAT_INCOMPAT_FTYPE| \ + XFS_SB_FEAT_INCOMPAT_SPINODES) #define XFS_SB_FEAT_INCOMPAT_UNKNOWN ~XFS_SB_FEAT_INCOMPAT_ALL static inline bool @@ -506,6 +508,12 @@ static inline int xfs_sb_version_hasfinobt(xfs_sb_t *sbp) (sbp->sb_features_ro_compat & XFS_SB_FEAT_RO_COMPAT_FINOBT); } +static inline bool xfs_sb_version_hassparseinodes(struct xfs_sb *sbp) +{ + return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5 && + xfs_sb_has_incompat_feature(sbp, XFS_SB_FEAT_INCOMPAT_SPINODES); +} + /* * end of superblock version macros */ @@ -758,19 +766,6 @@ typedef struct xfs_agfl { #define XFS_AGFL_CRC_OFF offsetof(struct xfs_agfl, agfl_crc) - -#define XFS_AG_MAXLEVELS(mp) ((mp)->m_ag_maxlevels) -#define XFS_MIN_FREELIST_RAW(bl,cl,mp) \ - (MIN(bl + 1, XFS_AG_MAXLEVELS(mp)) + MIN(cl + 1, XFS_AG_MAXLEVELS(mp))) -#define XFS_MIN_FREELIST(a,mp) \ - (XFS_MIN_FREELIST_RAW( \ - be32_to_cpu((a)->agf_levels[XFS_BTNUM_BNOi]), \ - be32_to_cpu((a)->agf_levels[XFS_BTNUM_CNTi]), mp)) -#define XFS_MIN_FREELIST_PAG(pag,mp) \ - (XFS_MIN_FREELIST_RAW( \ - (unsigned int)(pag)->pagf_levels[XFS_BTNUM_BNOi], \ - (unsigned int)(pag)->pagf_levels[XFS_BTNUM_CNTi], mp)) - #define XFS_AGB_TO_FSB(mp,agno,agbno) \ (((xfs_fsblock_t)(agno) << (mp)->m_sb.sb_agblklog) | (agbno)) #define XFS_FSB_TO_AGNO(mp,fsbno) \ @@ -1216,26 +1211,54 @@ typedef __uint64_t xfs_inofree_t; #define XFS_INOBT_ALL_FREE ((xfs_inofree_t)-1) #define XFS_INOBT_MASK(i) ((xfs_inofree_t)1 << (i)) +#define XFS_INOBT_HOLEMASK_FULL 0 /* holemask for full chunk */ +#define XFS_INOBT_HOLEMASK_BITS (NBBY * sizeof(__uint16_t)) +#define XFS_INODES_PER_HOLEMASK_BIT \ + (XFS_INODES_PER_CHUNK / (NBBY * sizeof(__uint16_t))) + static inline xfs_inofree_t xfs_inobt_maskn(int i, int n) { return ((n >= XFS_INODES_PER_CHUNK ? 0 : XFS_INOBT_MASK(n)) - 1) << i; } /* - * Data record structure + * The on-disk inode record structure has two formats. The original "full" + * format uses a 4-byte freecount. The "sparse" format uses a 1-byte freecount + * and replaces the 3 high-order freecount bytes wth the holemask and inode + * count. + * + * The holemask of the sparse record format allows an inode chunk to have holes + * that refer to blocks not owned by the inode record. This facilitates inode + * allocation in the event of severe free space fragmentation. */ typedef struct xfs_inobt_rec { __be32 ir_startino; /* starting inode number */ - __be32 ir_freecount; /* count of free inodes (set bits) */ + union { + struct { + __be32 ir_freecount; /* count of free inodes */ + } f; + struct { + __be16 ir_holemask;/* hole mask for sparse chunks */ + __u8 ir_count; /* total inode count */ + __u8 ir_freecount; /* count of free inodes */ + } sp; + } ir_u; __be64 ir_free; /* free inode mask */ } xfs_inobt_rec_t; typedef struct xfs_inobt_rec_incore { xfs_agino_t ir_startino; /* starting inode number */ - __int32_t ir_freecount; /* count of free inodes (set bits) */ + __uint16_t ir_holemask; /* hole mask for sparse chunks */ + __uint8_t ir_count; /* total inode count */ + __uint8_t ir_freecount; /* count of free inodes (set bits) */ xfs_inofree_t ir_free; /* free inode mask */ } xfs_inobt_rec_incore_t; +static inline bool xfs_inobt_issparse(uint16_t holemask) +{ + /* non-zero holemask represents a sparse rec. */ + return holemask; +} /* * Key structure @@ -1453,8 +1476,8 @@ struct xfs_acl { sizeof(struct xfs_acl_entry) * XFS_ACL_MAX_ENTRIES((mp))) /* On-disk XFS extended attribute names */ -#define SGI_ACL_FILE (unsigned char *)"SGI_ACL_FILE" -#define SGI_ACL_DEFAULT (unsigned char *)"SGI_ACL_DEFAULT" +#define SGI_ACL_FILE "SGI_ACL_FILE" +#define SGI_ACL_DEFAULT "SGI_ACL_DEFAULT" #define SGI_ACL_FILE_SIZE (sizeof(SGI_ACL_FILE)-1) #define SGI_ACL_DEFAULT_SIZE (sizeof(SGI_ACL_DEFAULT)-1) diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h index 18dc721ca..89689c6a4 100644 --- a/fs/xfs/libxfs/xfs_fs.h +++ b/fs/xfs/libxfs/xfs_fs.h @@ -239,6 +239,7 @@ typedef struct xfs_fsop_resblks { #define XFS_FSOP_GEOM_FLAGS_V5SB 0x8000 /* version 5 superblock */ #define XFS_FSOP_GEOM_FLAGS_FTYPE 0x10000 /* inode directory types */ #define XFS_FSOP_GEOM_FLAGS_FINOBT 0x20000 /* free inode btree */ +#define XFS_FSOP_GEOM_FLAGS_SPINODES 0x40000 /* sparse inode chunks */ /* * Minimum and maximum sizes need for growth checks. diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c index 1c9e75521..66efc7024 100644 --- a/fs/xfs/libxfs/xfs_ialloc.c +++ b/fs/xfs/libxfs/xfs_ialloc.c @@ -65,6 +65,8 @@ xfs_inobt_lookup( int *stat) /* success/failure */ { cur->bc_rec.i.ir_startino = ino; + cur->bc_rec.i.ir_holemask = 0; + cur->bc_rec.i.ir_count = 0; cur->bc_rec.i.ir_freecount = 0; cur->bc_rec.i.ir_free = 0; return xfs_btree_lookup(cur, dir, stat); @@ -82,7 +84,14 @@ xfs_inobt_update( union xfs_btree_rec rec; rec.inobt.ir_startino = cpu_to_be32(irec->ir_startino); - rec.inobt.ir_freecount = cpu_to_be32(irec->ir_freecount); + if (xfs_sb_version_hassparseinodes(&cur->bc_mp->m_sb)) { + rec.inobt.ir_u.sp.ir_holemask = cpu_to_be16(irec->ir_holemask); + rec.inobt.ir_u.sp.ir_count = irec->ir_count; + rec.inobt.ir_u.sp.ir_freecount = irec->ir_freecount; + } else { + /* ir_holemask/ir_count not supported on-disk */ + rec.inobt.ir_u.f.ir_freecount = cpu_to_be32(irec->ir_freecount); + } rec.inobt.ir_free = cpu_to_be64(irec->ir_free); return xfs_btree_update(cur, &rec); } @@ -100,12 +109,27 @@ xfs_inobt_get_rec( int error; error = xfs_btree_get_rec(cur, &rec, stat); - if (!error && *stat == 1) { - irec->ir_startino = be32_to_cpu(rec->inobt.ir_startino); - irec->ir_freecount = be32_to_cpu(rec->inobt.ir_freecount); - irec->ir_free = be64_to_cpu(rec->inobt.ir_free); + if (error || *stat == 0) + return error; + + irec->ir_startino = be32_to_cpu(rec->inobt.ir_startino); + if (xfs_sb_version_hassparseinodes(&cur->bc_mp->m_sb)) { + irec->ir_holemask = be16_to_cpu(rec->inobt.ir_u.sp.ir_holemask); + irec->ir_count = rec->inobt.ir_u.sp.ir_count; + irec->ir_freecount = rec->inobt.ir_u.sp.ir_freecount; + } else { + /* + * ir_holemask/ir_count not supported on-disk. Fill in hardcoded + * values for full inode chunks. + */ + irec->ir_holemask = XFS_INOBT_HOLEMASK_FULL; + irec->ir_count = XFS_INODES_PER_CHUNK; + irec->ir_freecount = + be32_to_cpu(rec->inobt.ir_u.f.ir_freecount); } - return error; + irec->ir_free = be64_to_cpu(rec->inobt.ir_free); + + return 0; } /* @@ -114,10 +138,14 @@ xfs_inobt_get_rec( STATIC int xfs_inobt_insert_rec( struct xfs_btree_cur *cur, + __uint16_t holemask, + __uint8_t count, __int32_t freecount, xfs_inofree_t free, int *stat) { + cur->bc_rec.i.ir_holemask = holemask; + cur->bc_rec.i.ir_count = count; cur->bc_rec.i.ir_freecount = freecount; cur->bc_rec.i.ir_free = free; return xfs_btree_insert(cur, stat); @@ -154,7 +182,9 @@ xfs_inobt_insert( } ASSERT(i == 0); - error = xfs_inobt_insert_rec(cur, XFS_INODES_PER_CHUNK, + error = xfs_inobt_insert_rec(cur, XFS_INOBT_HOLEMASK_FULL, + XFS_INODES_PER_CHUNK, + XFS_INODES_PER_CHUNK, XFS_INOBT_ALL_FREE, &i); if (error) { xfs_btree_del_cursor(cur, XFS_BTREE_ERROR); @@ -220,6 +250,7 @@ xfs_ialloc_inode_init( struct xfs_mount *mp, struct xfs_trans *tp, struct list_head *buffer_list, + int icount, xfs_agnumber_t agno, xfs_agblock_t agbno, xfs_agblock_t length, @@ -275,7 +306,7 @@ xfs_ialloc_inode_init( * they track in the AIL as if they were physically logged. */ if (tp) - xfs_icreate_log(tp, agno, agbno, mp->m_ialloc_inos, + xfs_icreate_log(tp, agno, agbno, icount, mp->m_sb.sb_inodesize, length, gen); } else version = 2; @@ -347,6 +378,214 @@ xfs_ialloc_inode_init( } /* + * Align startino and allocmask for a recently allocated sparse chunk such that + * they are fit for insertion (or merge) into the on-disk inode btrees. + * + * Background: + * + * When enabled, sparse inode support increases the inode alignment from cluster + * size to inode chunk size. This means that the minimum range between two + * non-adjacent inode records in the inobt is large enough for a full inode + * record. This allows for cluster sized, cluster aligned block allocation + * without need to worry about whether the resulting inode record overlaps with + * another record in the tree. Without this basic rule, we would have to deal + * with the consequences of overlap by potentially undoing recent allocations in + * the inode allocation codepath. + * + * Because of this alignment rule (which is enforced on mount), there are two + * inobt possibilities for newly allocated sparse chunks. One is that the + * aligned inode record for the chunk covers a range of inodes not already + * covered in the inobt (i.e., it is safe to insert a new sparse record). The + * other is that a record already exists at the aligned startino that considers + * the newly allocated range as sparse. In the latter case, record content is + * merged in hope that sparse inode chunks fill to full chunks over time. + */ +STATIC void +xfs_align_sparse_ino( + struct xfs_mount *mp, + xfs_agino_t *startino, + uint16_t *allocmask) +{ + xfs_agblock_t agbno; + xfs_agblock_t mod; + int offset; + + agbno = XFS_AGINO_TO_AGBNO(mp, *startino); + mod = agbno % mp->m_sb.sb_inoalignmt; + if (!mod) + return; + + /* calculate the inode offset and align startino */ + offset = mod << mp->m_sb.sb_inopblog; + *startino -= offset; + + /* + * Since startino has been aligned down, left shift allocmask such that + * it continues to represent the same physical inodes relative to the + * new startino. + */ + *allocmask <<= offset / XFS_INODES_PER_HOLEMASK_BIT; +} + +/* + * Determine whether the source inode record can merge into the target. Both + * records must be sparse, the inode ranges must match and there must be no + * allocation overlap between the records. + */ +STATIC bool +__xfs_inobt_can_merge( + struct xfs_inobt_rec_incore *trec, /* tgt record */ + struct xfs_inobt_rec_incore *srec) /* src record */ +{ + uint64_t talloc; + uint64_t salloc; + + /* records must cover the same inode range */ + if (trec->ir_startino != srec->ir_startino) + return false; + + /* both records must be sparse */ + if (!xfs_inobt_issparse(trec->ir_holemask) || + !xfs_inobt_issparse(srec->ir_holemask)) + return false; + + /* both records must track some inodes */ + if (!trec->ir_count || !srec->ir_count) + return false; + + /* can't exceed capacity of a full record */ + if (trec->ir_count + srec->ir_count > XFS_INODES_PER_CHUNK) + return false; + + /* verify there is no allocation overlap */ + talloc = xfs_inobt_irec_to_allocmask(trec); + salloc = xfs_inobt_irec_to_allocmask(srec); + if (talloc & salloc) + return false; + + return true; +} + +/* + * Merge the source inode record into the target. The caller must call + * __xfs_inobt_can_merge() to ensure the merge is valid. + */ +STATIC void +__xfs_inobt_rec_merge( + struct xfs_inobt_rec_incore *trec, /* target */ + struct xfs_inobt_rec_incore *srec) /* src */ +{ + ASSERT(trec->ir_startino == srec->ir_startino); + + /* combine the counts */ + trec->ir_count += srec->ir_count; + trec->ir_freecount += srec->ir_freecount; + + /* + * Merge the holemask and free mask. For both fields, 0 bits refer to + * allocated inodes. We combine the allocated ranges with bitwise AND. + */ + trec->ir_holemask &= srec->ir_holemask; + trec->ir_free &= srec->ir_free; +} + +/* + * Insert a new sparse inode chunk into the associated inode btree. The inode + * record for the sparse chunk is pre-aligned to a startino that should match + * any pre-existing sparse inode record in the tree. This allows sparse chunks + * to fill over time. + * + * This function supports two modes of handling preexisting records depending on + * the merge flag. If merge is true, the provided record is merged with the + * existing record and updated in place. The merged record is returned in nrec. + * If merge is false, an existing record is replaced with the provided record. + * If no preexisting record exists, the provided record is always inserted. + * + * It is considered corruption if a merge is requested and not possible. Given + * the sparse inode alignment constraints, this should never happen. + */ +STATIC int +xfs_inobt_insert_sprec( + struct xfs_mount *mp, + struct xfs_trans *tp, + struct xfs_buf *agbp, + int btnum, + struct xfs_inobt_rec_incore *nrec, /* in/out: new/merged rec. */ + bool merge) /* merge or replace */ +{ + struct xfs_btree_cur *cur; + struct xfs_agi *agi = XFS_BUF_TO_AGI(agbp); + xfs_agnumber_t agno = be32_to_cpu(agi->agi_seqno); + int error; + int i; + struct xfs_inobt_rec_incore rec; + + cur = xfs_inobt_init_cursor(mp, tp, agbp, agno, btnum); + + /* the new record is pre-aligned so we know where to look */ + error = xfs_inobt_lookup(cur, nrec->ir_startino, XFS_LOOKUP_EQ, &i); + if (error) + goto error; + /* if nothing there, insert a new record and return */ + if (i == 0) { + error = xfs_inobt_insert_rec(cur, nrec->ir_holemask, + nrec->ir_count, nrec->ir_freecount, + nrec->ir_free, &i); + if (error) + goto error; + XFS_WANT_CORRUPTED_GOTO(mp, i == 1, error); + + goto out; + } + + /* + * A record exists at this startino. Merge or replace the record + * depending on what we've been asked to do. + */ + if (merge) { + error = xfs_inobt_get_rec(cur, &rec, &i); + if (error) + goto error; + XFS_WANT_CORRUPTED_GOTO(mp, i == 1, error); + XFS_WANT_CORRUPTED_GOTO(mp, + rec.ir_startino == nrec->ir_startino, + error); + + /* + * This should never fail. If we have coexisting records that + * cannot merge, something is seriously wrong. + */ + XFS_WANT_CORRUPTED_GOTO(mp, __xfs_inobt_can_merge(nrec, &rec), + error); + + trace_xfs_irec_merge_pre(mp, agno, rec.ir_startino, + rec.ir_holemask, nrec->ir_startino, + nrec->ir_holemask); + + /* merge to nrec to output the updated record */ + __xfs_inobt_rec_merge(nrec, &rec); + + trace_xfs_irec_merge_post(mp, agno, nrec->ir_startino, + nrec->ir_holemask); + + error = xfs_inobt_rec_check_count(mp, nrec); + if (error) + goto error; + } + + error = xfs_inobt_update(cur, nrec); + if (error) + goto error; + +out: + xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR); + return 0; +error: + xfs_btree_del_cursor(cur, XFS_BTREE_ERROR); + return error; +} + +/* * Allocate new inodes in the allocation group specified by agbp. * Return 0 for success, else error code. */ @@ -364,11 +603,22 @@ xfs_ialloc_ag_alloc( xfs_agino_t newlen; /* new number of inodes */ int isaligned = 0; /* inode allocation at stripe unit */ /* boundary */ + uint16_t allocmask = (uint16_t) -1; /* init. to full chunk */ + struct xfs_inobt_rec_incore rec; struct xfs_perag *pag; + int do_sparse = 0; memset(&args, 0, sizeof(args)); args.tp = tp; args.mp = tp->t_mountp; + args.fsbno = NULLFSBLOCK; + +#ifdef DEBUG + /* randomly do sparse inode allocations */ + if (xfs_sb_version_hassparseinodes(&tp->t_mountp->m_sb) && + args.mp->m_ialloc_min_blks < args.mp->m_ialloc_blks) + do_sparse = prandom_u32() & 1; +#endif /* * Locking will ensure that we don't have two callers in here @@ -390,6 +640,8 @@ xfs_ialloc_ag_alloc( agno = be32_to_cpu(agi->agi_seqno); args.agbno = XFS_AGINO_TO_AGBNO(args.mp, newino) + args.mp->m_ialloc_blks; + if (do_sparse) + goto sparse_alloc; if (likely(newino != NULLAGINO && (args.agbno < be32_to_cpu(agi->agi_length)))) { args.fsbno = XFS_AGB_TO_FSB(args.mp, agno, args.agbno); @@ -428,8 +680,7 @@ xfs_ialloc_ag_alloc( * subsequent requests. */ args.minalignslop = 0; - } else - args.fsbno = NULLFSBLOCK; + } if (unlikely(args.fsbno == NULLFSBLOCK)) { /* @@ -480,6 +731,47 @@ xfs_ialloc_ag_alloc( return error; } + /* + * Finally, try a sparse allocation if the filesystem supports it and + * the sparse allocation length is smaller than a full chunk. + */ + if (xfs_sb_version_hassparseinodes(&args.mp->m_sb) && + args.mp->m_ialloc_min_blks < args.mp->m_ialloc_blks && + args.fsbno == NULLFSBLOCK) { +sparse_alloc: + args.type = XFS_ALLOCTYPE_NEAR_BNO; + args.agbno = be32_to_cpu(agi->agi_root); + args.fsbno = XFS_AGB_TO_FSB(args.mp, agno, args.agbno); + args.alignment = args.mp->m_sb.sb_spino_align; + args.prod = 1; + + args.minlen = args.mp->m_ialloc_min_blks; + args.maxlen = args.minlen; + + /* + * The inode record will be aligned to full chunk size. We must + * prevent sparse allocation from AG boundaries that result in + * invalid inode records, such as records that start at agbno 0 + * or extend beyond the AG. + * + * Set min agbno to the first aligned, non-zero agbno and max to + * the last aligned agbno that is at least one full chunk from + * the end of the AG. + */ + args.min_agbno = args.mp->m_sb.sb_inoalignmt; + args.max_agbno = round_down(args.mp->m_sb.sb_agblocks, + args.mp->m_sb.sb_inoalignmt) - + args.mp->m_ialloc_blks; + + error = xfs_alloc_vextent(&args); + if (error) + return error; + + newlen = args.len << args.mp->m_sb.sb_inopblog; + ASSERT(newlen <= XFS_INODES_PER_CHUNK); + allocmask = (1 << (newlen / XFS_INODES_PER_HOLEMASK_BIT)) - 1; + } + if (args.fsbno == NULLFSBLOCK) { *alloc = 0; return 0; @@ -495,8 +787,8 @@ xfs_ialloc_ag_alloc( * rather than a linear progression to prevent the next generation * number from being easily guessable. */ - error = xfs_ialloc_inode_init(args.mp, tp, NULL, agno, args.agbno, - args.len, prandom_u32()); + error = xfs_ialloc_inode_init(args.mp, tp, NULL, newlen, agno, + args.agbno, args.len, prandom_u32()); if (error) return error; @@ -504,6 +796,73 @@ xfs_ialloc_ag_alloc( * Convert the results. */ newino = XFS_OFFBNO_TO_AGINO(args.mp, args.agbno, 0); + + if (xfs_inobt_issparse(~allocmask)) { + /* + * We've allocated a sparse chunk. Align the startino and mask. + */ + xfs_align_sparse_ino(args.mp, &newino, &allocmask); + + rec.ir_startino = newino; + rec.ir_holemask = ~allocmask; + rec.ir_count = newlen; + rec.ir_freecount = newlen; + rec.ir_free = XFS_INOBT_ALL_FREE; + + /* + * Insert the sparse record into the inobt and allow for a merge + * if necessary. If a merge does occur, rec is updated to the + * merged record. + */ + error = xfs_inobt_insert_sprec(args.mp, tp, agbp, XFS_BTNUM_INO, + &rec, true); + if (error == -EFSCORRUPTED) { + xfs_alert(args.mp, + "invalid sparse inode record: ino 0x%llx holemask 0x%x count %u", + XFS_AGINO_TO_INO(args.mp, agno, + rec.ir_startino), + rec.ir_holemask, rec.ir_count); + xfs_force_shutdown(args.mp, SHUTDOWN_CORRUPT_INCORE); + } + if (error) + return error; + + /* + * We can't merge the part we've just allocated as for the inobt + * due to finobt semantics. The original record may or may not + * exist independent of whether physical inodes exist in this + * sparse chunk. + * + * We must update the finobt record based on the inobt record. + * rec contains the fully merged and up to date inobt record + * from the previous call. Set merge false to replace any + * existing record with this one. + */ + if (xfs_sb_version_hasfinobt(&args.mp->m_sb)) { + error = xfs_inobt_insert_sprec(args.mp, tp, agbp, + XFS_BTNUM_FINO, &rec, + false); + if (error) + return error; + } + } else { + /* full chunk - insert new records to both btrees */ + error = xfs_inobt_insert(args.mp, tp, agbp, newino, newlen, + XFS_BTNUM_INO); + if (error) + return error; + + if (xfs_sb_version_hasfinobt(&args.mp->m_sb)) { + error = xfs_inobt_insert(args.mp, tp, agbp, newino, + newlen, XFS_BTNUM_FINO); + if (error) + return error; + } + } + + /* + * Update AGI counts and newino. + */ be32_add_cpu(&agi->agi_count, newlen); be32_add_cpu(&agi->agi_freecount, newlen); pag = xfs_perag_get(args.mp, agno); @@ -512,20 +871,6 @@ xfs_ialloc_ag_alloc( agi->agi_newino = cpu_to_be32(newino); /* - * Insert records describing the new inode chunk into the btrees. - */ - error = xfs_inobt_insert(args.mp, tp, agbp, newino, newlen, - XFS_BTNUM_INO); - if (error) - return error; - - if (xfs_sb_version_hasfinobt(&args.mp->m_sb)) { - error = xfs_inobt_insert(args.mp, tp, agbp, newino, newlen, - XFS_BTNUM_FINO); - if (error) - return error; - } - /* * Log allocation group header fields */ xfs_ialloc_log_agi(tp, agbp, @@ -645,7 +990,7 @@ xfs_ialloc_ag_select( * if we fail allocation due to alignment issues then it is most * likely a real ENOSPC condition. */ - ineed = mp->m_ialloc_blks; + ineed = mp->m_ialloc_min_blks; if (flags && ineed > 1) ineed += xfs_ialloc_cluster_alignment(mp); longest = pag->pagf_longest; @@ -732,6 +1077,27 @@ xfs_ialloc_get_rec( } /* + * Return the offset of the first free inode in the record. If the inode chunk + * is sparsely allocated, we convert the record holemask to inode granularity + * and mask off the unallocated regions from the inode free mask. + */ +STATIC int +xfs_inobt_first_free_inode( + struct xfs_inobt_rec_incore *rec) +{ + xfs_inofree_t realfree; + + /* if there are no holes, return the first available offset */ + if (!xfs_inobt_issparse(rec->ir_holemask)) + return xfs_lowbit64(rec->ir_free); + + realfree = xfs_inobt_irec_to_allocmask(rec); + realfree &= rec->ir_free; + + return xfs_lowbit64(realfree); +} + +/* * Allocate an inode using the inobt-only algorithm. */ STATIC int @@ -961,7 +1327,7 @@ newino: } alloc_inode: - offset = xfs_lowbit64(rec.ir_free); + offset = xfs_inobt_first_free_inode(&rec); ASSERT(offset >= 0); ASSERT(offset < XFS_INODES_PER_CHUNK); ASSERT((XFS_AGINO_TO_OFFSET(mp, rec.ir_startino) % @@ -1210,7 +1576,7 @@ xfs_dialloc_ag( if (error) goto error_cur; - offset = xfs_lowbit64(rec.ir_free); + offset = xfs_inobt_first_free_inode(&rec); ASSERT(offset >= 0); ASSERT(offset < XFS_INODES_PER_CHUNK); ASSERT((XFS_AGINO_TO_OFFSET(mp, rec.ir_startino) % @@ -1439,6 +1805,83 @@ out_error: return error; } +/* + * Free the blocks of an inode chunk. We must consider that the inode chunk + * might be sparse and only free the regions that are allocated as part of the + * chunk. + */ +STATIC void +xfs_difree_inode_chunk( + struct xfs_mount *mp, + xfs_agnumber_t agno, + struct xfs_inobt_rec_incore *rec, + struct xfs_bmap_free *flist) +{ + xfs_agblock_t sagbno = XFS_AGINO_TO_AGBNO(mp, rec->ir_startino); + int startidx, endidx; + int nextbit; + xfs_agblock_t agbno; + int contigblk; + DECLARE_BITMAP(holemask, XFS_INOBT_HOLEMASK_BITS); + + if (!xfs_inobt_issparse(rec->ir_holemask)) { + /* not sparse, calculate extent info directly */ + xfs_bmap_add_free(XFS_AGB_TO_FSB(mp, agno, + XFS_AGINO_TO_AGBNO(mp, rec->ir_startino)), + mp->m_ialloc_blks, flist, mp); + return; + } + + /* holemask is only 16-bits (fits in an unsigned long) */ + ASSERT(sizeof(rec->ir_holemask) <= sizeof(holemask[0])); + holemask[0] = rec->ir_holemask; + + /* + * Find contiguous ranges of zeroes (i.e., allocated regions) in the + * holemask and convert the start/end index of each range to an extent. + * We start with the start and end index both pointing at the first 0 in + * the mask. + */ + startidx = endidx = find_first_zero_bit(holemask, + XFS_INOBT_HOLEMASK_BITS); + nextbit = startidx + 1; + while (startidx < XFS_INOBT_HOLEMASK_BITS) { + nextbit = find_next_zero_bit(holemask, XFS_INOBT_HOLEMASK_BITS, + nextbit); + /* + * If the next zero bit is contiguous, update the end index of + * the current range and continue. + */ + if (nextbit != XFS_INOBT_HOLEMASK_BITS && + nextbit == endidx + 1) { + endidx = nextbit; + goto next; + } + + /* + * nextbit is not contiguous with the current end index. Convert + * the current start/end to an extent and add it to the free + * list. + */ + agbno = sagbno + (startidx * XFS_INODES_PER_HOLEMASK_BIT) / + mp->m_sb.sb_inopblock; + contigblk = ((endidx - startidx + 1) * + XFS_INODES_PER_HOLEMASK_BIT) / + mp->m_sb.sb_inopblock; + + ASSERT(agbno % mp->m_sb.sb_spino_align == 0); + ASSERT(contigblk % mp->m_sb.sb_spino_align == 0); + xfs_bmap_add_free(XFS_AGB_TO_FSB(mp, agno, agbno), contigblk, + flist, mp); + + /* reset range to current bit and carry on... */ + startidx = endidx = nextbit; + +next: + nextbit++; + } +} + STATIC int xfs_difree_inobt( struct xfs_mount *mp, @@ -1446,8 +1889,7 @@ xfs_difree_inobt( struct xfs_buf *agbp, xfs_agino_t agino, struct xfs_bmap_free *flist, - int *deleted, - xfs_ino_t *first_ino, + struct xfs_icluster *xic, struct xfs_inobt_rec_incore *orec) { struct xfs_agi *agi = XFS_BUF_TO_AGI(agbp); @@ -1501,20 +1943,23 @@ xfs_difree_inobt( rec.ir_freecount++; /* - * When an inode cluster is free, it becomes eligible for removal + * When an inode chunk is free, it becomes eligible for removal. Don't + * remove the chunk if the block size is large enough for multiple inode + * chunks (that might not be free). */ if (!(mp->m_flags & XFS_MOUNT_IKEEP) && - (rec.ir_freecount == mp->m_ialloc_inos)) { - - *deleted = 1; - *first_ino = XFS_AGINO_TO_INO(mp, agno, rec.ir_startino); + rec.ir_free == XFS_INOBT_ALL_FREE && + mp->m_sb.sb_inopblock <= XFS_INODES_PER_CHUNK) { + xic->deleted = 1; + xic->first_ino = XFS_AGINO_TO_INO(mp, agno, rec.ir_startino); + xic->alloc = xfs_inobt_irec_to_allocmask(&rec); /* * Remove the inode cluster from the AGI B+Tree, adjust the * AGI and Superblock inode counts, and mark the disk space * to be freed when the transaction is committed. */ - ilen = mp->m_ialloc_inos; + ilen = rec.ir_freecount; be32_add_cpu(&agi->agi_count, -ilen); be32_add_cpu(&agi->agi_freecount, -(ilen - 1)); xfs_ialloc_log_agi(tp, agbp, XFS_AGI_COUNT | XFS_AGI_FREECOUNT); @@ -1530,11 +1975,9 @@ xfs_difree_inobt( goto error0; } - xfs_bmap_add_free(XFS_AGB_TO_FSB(mp, agno, - XFS_AGINO_TO_AGBNO(mp, rec.ir_startino)), - mp->m_ialloc_blks, flist, mp); + xfs_difree_inode_chunk(mp, agno, &rec, flist); } else { - *deleted = 0; + xic->deleted = 0; error = xfs_inobt_update(cur, &rec); if (error) { @@ -1599,7 +2042,9 @@ xfs_difree_finobt( */ XFS_WANT_CORRUPTED_GOTO(mp, ibtrec->ir_freecount == 1, error); - error = xfs_inobt_insert_rec(cur, ibtrec->ir_freecount, + error = xfs_inobt_insert_rec(cur, ibtrec->ir_holemask, + ibtrec->ir_count, + ibtrec->ir_freecount, ibtrec->ir_free, &i); if (error) goto error; @@ -1634,8 +2079,13 @@ xfs_difree_finobt( * free inode. Hence, if all of the inodes are free and we aren't * keeping inode chunks permanently on disk, remove the record. * Otherwise, update the record with the new information. + * + * Note that we currently can't free chunks when the block size is large + * enough for multiple chunks. Leave the finobt record to remain in sync + * with the inobt. */ - if (rec.ir_freecount == mp->m_ialloc_inos && + if (rec.ir_free == XFS_INOBT_ALL_FREE && + mp->m_sb.sb_inopblock <= XFS_INODES_PER_CHUNK && !(mp->m_flags & XFS_MOUNT_IKEEP)) { error = xfs_btree_delete(cur, &i); if (error) @@ -1671,8 +2121,7 @@ xfs_difree( struct xfs_trans *tp, /* transaction pointer */ xfs_ino_t inode, /* inode to be freed */ struct xfs_bmap_free *flist, /* extents to free */ - int *deleted,/* set if inode cluster was deleted */ - xfs_ino_t *first_ino)/* first inode in deleted cluster */ + struct xfs_icluster *xic) /* cluster info if deleted */ { /* REFERENCED */ xfs_agblock_t agbno; /* block number containing inode */ @@ -1723,8 +2172,7 @@ xfs_difree( /* * Fix up the inode allocation btree. */ - error = xfs_difree_inobt(mp, tp, agbp, agino, flist, deleted, first_ino, - &rec); + error = xfs_difree_inobt(mp, tp, agbp, agino, flist, xic, &rec); if (error) goto error0; diff --git a/fs/xfs/libxfs/xfs_ialloc.h b/fs/xfs/libxfs/xfs_ialloc.h index 100007d56..6e450df29 100644 --- a/fs/xfs/libxfs/xfs_ialloc.h +++ b/fs/xfs/libxfs/xfs_ialloc.h @@ -28,6 +28,13 @@ struct xfs_btree_cur; /* Move inodes in clusters of this size */ #define XFS_INODE_BIG_CLUSTER_SIZE 8192 +struct xfs_icluster { + bool deleted; /* record is deleted */ + xfs_ino_t first_ino; /* first inode number */ + uint64_t alloc; /* inode phys. allocation bitmap for + * sparse chunks */ +}; + /* Calculate and return the number of filesystem blocks per inode cluster */ static inline int xfs_icluster_size_fsb( @@ -44,8 +51,7 @@ xfs_icluster_size_fsb( static inline struct xfs_dinode * xfs_make_iptr(struct xfs_mount *mp, struct xfs_buf *b, int o) { - return (struct xfs_dinode *) - (xfs_buf_offset(b, o << (mp)->m_sb.sb_inodelog)); + return xfs_buf_offset(b, o << (mp)->m_sb.sb_inodelog); } /* @@ -90,8 +96,7 @@ xfs_difree( struct xfs_trans *tp, /* transaction pointer */ xfs_ino_t inode, /* inode to be freed */ struct xfs_bmap_free *flist, /* extents to free */ - int *deleted, /* set if inode cluster was deleted */ - xfs_ino_t *first_ino); /* first inode in deleted cluster */ + struct xfs_icluster *ifree); /* cluster info if deleted */ /* * Return the location of the inode in imap, for mapping it into a buffer. @@ -156,7 +161,7 @@ int xfs_inobt_get_rec(struct xfs_btree_cur *cur, * Inode chunk initialisation routine */ int xfs_ialloc_inode_init(struct xfs_mount *mp, struct xfs_trans *tp, - struct list_head *buffer_list, + struct list_head *buffer_list, int icount, xfs_agnumber_t agno, xfs_agblock_t agbno, xfs_agblock_t length, unsigned int gen); diff --git a/fs/xfs/libxfs/xfs_ialloc_btree.c b/fs/xfs/libxfs/xfs_ialloc_btree.c index 964c465ca..674ad8f76 100644 --- a/fs/xfs/libxfs/xfs_ialloc_btree.c +++ b/fs/xfs/libxfs/xfs_ialloc_btree.c @@ -167,7 +167,16 @@ xfs_inobt_init_rec_from_cur( union xfs_btree_rec *rec) { rec->inobt.ir_startino = cpu_to_be32(cur->bc_rec.i.ir_startino); - rec->inobt.ir_freecount = cpu_to_be32(cur->bc_rec.i.ir_freecount); + if (xfs_sb_version_hassparseinodes(&cur->bc_mp->m_sb)) { + rec->inobt.ir_u.sp.ir_holemask = + cpu_to_be16(cur->bc_rec.i.ir_holemask); + rec->inobt.ir_u.sp.ir_count = cur->bc_rec.i.ir_count; + rec->inobt.ir_u.sp.ir_freecount = cur->bc_rec.i.ir_freecount; + } else { + /* ir_holemask/ir_count not supported on-disk */ + rec->inobt.ir_u.f.ir_freecount = + cpu_to_be32(cur->bc_rec.i.ir_freecount); + } rec->inobt.ir_free = cpu_to_be64(cur->bc_rec.i.ir_free); } @@ -418,3 +427,85 @@ xfs_inobt_maxrecs( return blocklen / sizeof(xfs_inobt_rec_t); return blocklen / (sizeof(xfs_inobt_key_t) + sizeof(xfs_inobt_ptr_t)); } + +/* + * Convert the inode record holemask to an inode allocation bitmap. The inode + * allocation bitmap is inode granularity and specifies whether an inode is + * physically allocated on disk (not whether the inode is considered allocated + * or free by the fs). + * + * A bit value of 1 means the inode is allocated, a value of 0 means it is free. + */ +uint64_t +xfs_inobt_irec_to_allocmask( + struct xfs_inobt_rec_incore *rec) +{ + uint64_t bitmap = 0; + uint64_t inodespbit; + int nextbit; + uint allocbitmap; + + /* + * The holemask has 16-bits for a 64 inode record. Therefore each + * holemask bit represents multiple inodes. Create a mask of bits to set + * in the allocmask for each holemask bit. + */ + inodespbit = (1 << XFS_INODES_PER_HOLEMASK_BIT) - 1; + + /* + * Allocated inodes are represented by 0 bits in holemask. Invert the 0 + * bits to 1 and convert to a uint so we can use xfs_next_bit(). Mask + * anything beyond the 16 holemask bits since this casts to a larger + * type. + */ + allocbitmap = ~rec->ir_holemask & ((1 << XFS_INOBT_HOLEMASK_BITS) - 1); + + /* + * allocbitmap is the inverted holemask so every set bit represents + * allocated inodes. To expand from 16-bit holemask granularity to + * 64-bit (e.g., bit-per-inode), set inodespbit bits in the target + * bitmap for every holemask bit. + */ + nextbit = xfs_next_bit(&allocbitmap, 1, 0); + while (nextbit != -1) { + ASSERT(nextbit < (sizeof(rec->ir_holemask) * NBBY)); + + bitmap |= (inodespbit << + (nextbit * XFS_INODES_PER_HOLEMASK_BIT)); + + nextbit = xfs_next_bit(&allocbitmap, 1, nextbit + 1); + } + + return bitmap; +} + +#if defined(DEBUG) || defined(XFS_WARN) +/* + * Verify that an in-core inode record has a valid inode count. + */ +int +xfs_inobt_rec_check_count( + struct xfs_mount *mp, + struct xfs_inobt_rec_incore *rec) +{ + int inocount = 0; + int nextbit = 0; + uint64_t allocbmap; + int wordsz; + + wordsz = sizeof(allocbmap) / sizeof(unsigned int); + allocbmap = xfs_inobt_irec_to_allocmask(rec); + + nextbit = xfs_next_bit((uint *) &allocbmap, wordsz, nextbit); + while (nextbit != -1) { + inocount++; + nextbit = xfs_next_bit((uint *) &allocbmap, wordsz, + nextbit + 1); + } + + if (inocount != rec->ir_count) + return -EFSCORRUPTED; + + return 0; +} +#endif /* DEBUG */ diff --git a/fs/xfs/libxfs/xfs_ialloc_btree.h b/fs/xfs/libxfs/xfs_ialloc_btree.h index d7ebea72c..bd8845321 100644 --- a/fs/xfs/libxfs/xfs_ialloc_btree.h +++ b/fs/xfs/libxfs/xfs_ialloc_btree.h @@ -62,4 +62,14 @@ extern struct xfs_btree_cur *xfs_inobt_init_cursor(struct xfs_mount *, xfs_btnum_t); extern int xfs_inobt_maxrecs(struct xfs_mount *, int, int); +/* ir_holemask to inode allocation bitmap conversion */ +uint64_t xfs_inobt_irec_to_allocmask(struct xfs_inobt_rec_incore *); + +#if defined(DEBUG) || defined(XFS_WARN) +int xfs_inobt_rec_check_count(struct xfs_mount *, + struct xfs_inobt_rec_incore *); +#else +#define xfs_inobt_rec_check_count(mp, rec) 0 +#endif /* DEBUG */ + #endif /* __XFS_IALLOC_BTREE_H__ */ diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c index 002b6b3a1..6526e7696 100644 --- a/fs/xfs/libxfs/xfs_inode_buf.c +++ b/fs/xfs/libxfs/xfs_inode_buf.c @@ -46,8 +46,7 @@ xfs_inobp_check( j = mp->m_inode_cluster_size >> mp->m_sb.sb_inodelog; for (i = 0; i < j; i++) { - dip = (xfs_dinode_t *)xfs_buf_offset(bp, - i * mp->m_sb.sb_inodesize); + dip = xfs_buf_offset(bp, i * mp->m_sb.sb_inodesize); if (!dip->di_next_unlinked) { xfs_alert(mp, "Detected bogus zero next_unlinked field in inode %d buffer 0x%llx.", @@ -86,8 +85,7 @@ xfs_inode_buf_verify( int di_ok; xfs_dinode_t *dip; - dip = (struct xfs_dinode *)xfs_buf_offset(bp, - (i << mp->m_sb.sb_inodelog)); + dip = xfs_buf_offset(bp, (i << mp->m_sb.sb_inodelog)); di_ok = dip->di_magic == cpu_to_be16(XFS_DINODE_MAGIC) && XFS_DINODE_GOOD_VERSION(dip->di_version); if (unlikely(XFS_TEST_ERROR(!di_ok, mp, @@ -186,7 +184,7 @@ xfs_imap_to_bp( } *bpp = bp; - *dipp = (struct xfs_dinode *)xfs_buf_offset(bp, imap->im_boffset); + *dipp = xfs_buf_offset(bp, imap->im_boffset); return 0; } diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c index dc4bfc5d8..df9851c46 100644 --- a/fs/xfs/libxfs/xfs_sb.c +++ b/fs/xfs/libxfs/xfs_sb.c @@ -174,6 +174,27 @@ xfs_mount_validate_sb( return -EFSCORRUPTED; } + /* + * Full inode chunks must be aligned to inode chunk size when + * sparse inodes are enabled to support the sparse chunk + * allocation algorithm and prevent overlapping inode records. + */ + if (xfs_sb_version_hassparseinodes(sbp)) { + uint32_t align; + + xfs_alert(mp, + "EXPERIMENTAL sparse inode feature enabled. Use at your own risk!"); + + align = XFS_INODES_PER_CHUNK * sbp->sb_inodesize + >> sbp->sb_blocklog; + if (sbp->sb_inoalignmt != align) { + xfs_warn(mp, +"Inode block alignment (%u) must match chunk size (%u) for sparse inodes.", + sbp->sb_inoalignmt, align); + return -EINVAL; + } + } + if (unlikely( sbp->sb_logstart == 0 && mp->m_logdev_targp == mp->m_ddev_targp)) { xfs_warn(mp, @@ -374,7 +395,7 @@ __xfs_sb_from_disk( be32_to_cpu(from->sb_features_log_incompat); /* crc is only used on disk, not in memory; just init to 0 here. */ to->sb_crc = 0; - to->sb_pad = 0; + to->sb_spino_align = be32_to_cpu(from->sb_spino_align); to->sb_pquotino = be64_to_cpu(from->sb_pquotino); to->sb_lsn = be64_to_cpu(from->sb_lsn); /* Convert on-disk flags to in-memory flags? */ @@ -516,7 +537,7 @@ xfs_sb_to_disk( cpu_to_be32(from->sb_features_incompat); to->sb_features_log_incompat = cpu_to_be32(from->sb_features_log_incompat); - to->sb_pad = 0; + to->sb_spino_align = cpu_to_be32(from->sb_spino_align); to->sb_lsn = cpu_to_be64(from->sb_lsn); } } @@ -689,6 +710,11 @@ xfs_sb_mount_common( mp->m_ialloc_inos = (int)MAX((__uint16_t)XFS_INODES_PER_CHUNK, sbp->sb_inopblock); mp->m_ialloc_blks = mp->m_ialloc_inos >> sbp->sb_inopblog; + + if (sbp->sb_spino_align) + mp->m_ialloc_min_blks = sbp->sb_spino_align; + else + mp->m_ialloc_min_blks = mp->m_ialloc_blks; } /* @@ -792,12 +818,12 @@ xfs_sync_sb( tp = _xfs_trans_alloc(mp, XFS_TRANS_SB_CHANGE, KM_SLEEP); error = xfs_trans_reserve(tp, &M_RES(mp)->tr_sb, 0, 0); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); return error; } xfs_log_sb(tp); if (wait) xfs_trans_set_sync(tp); - return xfs_trans_commit(tp, 0); + return xfs_trans_commit(tp); } diff --git a/fs/xfs/libxfs/xfs_shared.h b/fs/xfs/libxfs/xfs_shared.h index 8dda4b321..5be529707 100644 --- a/fs/xfs/libxfs/xfs_shared.h +++ b/fs/xfs/libxfs/xfs_shared.h @@ -182,12 +182,6 @@ int xfs_log_calc_minimum_size(struct xfs_mount *); #define XFS_TRANS_FREEZE_PROT 0x40 /* Transaction has elevated writer count in superblock */ /* - * Values for call flags parameter. - */ -#define XFS_TRANS_RELEASE_LOG_RES 0x4 -#define XFS_TRANS_ABORT 0x8 - -/* * Field values for xfs_trans_mod_sb. */ #define XFS_TRANS_SB_ICOUNT 0x00000001 diff --git a/fs/xfs/libxfs/xfs_trans_resv.h b/fs/xfs/libxfs/xfs_trans_resv.h index 2d5bdfce6..797815012 100644 --- a/fs/xfs/libxfs/xfs_trans_resv.h +++ b/fs/xfs/libxfs/xfs_trans_resv.h @@ -73,9 +73,9 @@ struct xfs_trans_resv { * 2 trees * (2 blocks/level * max depth - 1) * block size */ #define XFS_ALLOCFREE_LOG_RES(mp,nx) \ - ((nx) * (2 * XFS_FSB_TO_B((mp), 2 * XFS_AG_MAXLEVELS(mp) - 1))) + ((nx) * (2 * XFS_FSB_TO_B((mp), 2 * (mp)->m_ag_maxlevels - 1))) #define XFS_ALLOCFREE_LOG_COUNT(mp,nx) \ - ((nx) * (2 * (2 * XFS_AG_MAXLEVELS(mp) - 1))) + ((nx) * (2 * (2 * (mp)->m_ag_maxlevels - 1))) /* * Per-directory log reservation for any directory change. diff --git a/fs/xfs/libxfs/xfs_trans_space.h b/fs/xfs/libxfs/xfs_trans_space.h index bf9c45793..41e0428d8 100644 --- a/fs/xfs/libxfs/xfs_trans_space.h +++ b/fs/xfs/libxfs/xfs_trans_space.h @@ -67,7 +67,7 @@ #define XFS_DIOSTRAT_SPACE_RES(mp, v) \ (XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK) + (v)) #define XFS_GROWFS_SPACE_RES(mp) \ - (2 * XFS_AG_MAXLEVELS(mp)) + (2 * (mp)->m_ag_maxlevels) #define XFS_GROWFSRT_SPACE_RES(mp,b) \ ((b) + XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK)) #define XFS_LINK_SPACE_RES(mp,nl) \ diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index a56960dd1..3859f5e27 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -109,7 +109,7 @@ xfs_setfilesize_trans_alloc( error = xfs_trans_reserve(tp, &M_RES(mp)->tr_fsyncts, 0, 0); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); return error; } @@ -145,7 +145,7 @@ xfs_setfilesize( isize = xfs_new_eof(ip, offset + size); if (!isize) { xfs_iunlock(ip, XFS_ILOCK_EXCL); - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); return 0; } @@ -155,7 +155,7 @@ xfs_setfilesize( xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); - return xfs_trans_commit(tp, 0); + return xfs_trans_commit(tp); } STATIC int @@ -356,7 +356,6 @@ xfs_end_bio( { xfs_ioend_t *ioend = bio->bi_private; - ASSERT(atomic_read(&bio->bi_cnt) >= 1); ioend->io_error = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : error; /* Toss bio and pass work off to an xfsdatad thread */ @@ -1349,7 +1348,7 @@ __xfs_get_blocks( sector_t iblock, struct buffer_head *bh_result, int create, - int direct) + bool direct) { struct xfs_inode *ip = XFS_I(inode); struct xfs_mount *mp = ip->i_mount; @@ -1414,6 +1413,7 @@ __xfs_get_blocks( if (error) return error; new = 1; + } else { /* * Delalloc reservations do not require a transaction, @@ -1508,49 +1508,29 @@ xfs_get_blocks( struct buffer_head *bh_result, int create) { - return __xfs_get_blocks(inode, iblock, bh_result, create, 0); + return __xfs_get_blocks(inode, iblock, bh_result, create, false); } -STATIC int +int xfs_get_blocks_direct( struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { - return __xfs_get_blocks(inode, iblock, bh_result, create, 1); + return __xfs_get_blocks(inode, iblock, bh_result, create, true); } -/* - * Complete a direct I/O write request. - * - * The ioend structure is passed from __xfs_get_blocks() to tell us what to do. - * If no ioend exists (i.e. @private == NULL) then the write IO is an overwrite - * wholly within the EOF and so there is nothing for us to do. Note that in this - * case the completion can be called in interrupt context, whereas if we have an - * ioend we will always be called in task context (i.e. from a workqueue). - */ -STATIC void -xfs_end_io_direct_write( - struct kiocb *iocb, +static void +__xfs_end_io_direct_write( + struct inode *inode, + struct xfs_ioend *ioend, loff_t offset, - ssize_t size, - void *private) + ssize_t size) { - struct inode *inode = file_inode(iocb->ki_filp); - struct xfs_inode *ip = XFS_I(inode); - struct xfs_mount *mp = ip->i_mount; - struct xfs_ioend *ioend = private; - - trace_xfs_gbmap_direct_endio(ip, offset, size, - ioend ? ioend->io_type : 0, NULL); + struct xfs_mount *mp = XFS_I(inode)->i_mount; - if (!ioend) { - ASSERT(offset + size <= i_size_read(inode)); - return; - } - - if (XFS_FORCED_SHUTDOWN(mp)) + if (XFS_FORCED_SHUTDOWN(mp) || ioend->io_error) goto out_end_io; /* @@ -1587,10 +1567,10 @@ xfs_end_io_direct_write( * here can result in EOF moving backwards and Bad Things Happen when * that occurs. */ - spin_lock(&ip->i_flags_lock); + spin_lock(&XFS_I(inode)->i_flags_lock); if (offset + size > i_size_read(inode)) i_size_write(inode, offset + size); - spin_unlock(&ip->i_flags_lock); + spin_unlock(&XFS_I(inode)->i_flags_lock); /* * If we are doing an append IO that needs to update the EOF on disk, @@ -1607,6 +1587,98 @@ out_end_io: return; } +/* + * Complete a direct I/O write request. + * + * The ioend structure is passed from __xfs_get_blocks() to tell us what to do. + * If no ioend exists (i.e. @private == NULL) then the write IO is an overwrite + * wholly within the EOF and so there is nothing for us to do. Note that in this + * case the completion can be called in interrupt context, whereas if we have an + * ioend we will always be called in task context (i.e. from a workqueue). + */ +STATIC void +xfs_end_io_direct_write( + struct kiocb *iocb, + loff_t offset, + ssize_t size, + void *private) +{ + struct inode *inode = file_inode(iocb->ki_filp); + struct xfs_ioend *ioend = private; + + trace_xfs_gbmap_direct_endio(XFS_I(inode), offset, size, + ioend ? ioend->io_type : 0, NULL); + + if (!ioend) { + ASSERT(offset + size <= i_size_read(inode)); + return; + } + + __xfs_end_io_direct_write(inode, ioend, offset, size); +} + +/* + * For DAX we need a mapping buffer callback for unwritten extent conversion + * when page faults allocate blocks and then zero them. Note that in this + * case the mapping indicated by the ioend may extend beyond EOF. We most + * definitely do not want to extend EOF here, so we trim back the ioend size to + * EOF. + */ +#ifdef CONFIG_FS_DAX +void +xfs_end_io_dax_write( + struct buffer_head *bh, + int uptodate) +{ + struct xfs_ioend *ioend = bh->b_private; + struct inode *inode = ioend->io_inode; + ssize_t size = ioend->io_size; + + ASSERT(IS_DAX(ioend->io_inode)); + + /* if there was an error zeroing, then don't convert it */ + if (!uptodate) + ioend->io_error = -EIO; + + /* + * Trim update to EOF, so we don't extend EOF during unwritten extent + * conversion of partial EOF blocks. + */ + spin_lock(&XFS_I(inode)->i_flags_lock); + if (ioend->io_offset + size > i_size_read(inode)) + size = i_size_read(inode) - ioend->io_offset; + spin_unlock(&XFS_I(inode)->i_flags_lock); + + __xfs_end_io_direct_write(inode, ioend, ioend->io_offset, size); + +} +#else +void xfs_end_io_dax_write(struct buffer_head *bh, int uptodate) { } +#endif + +static inline ssize_t +xfs_vm_do_dio( + struct inode *inode, + struct kiocb *iocb, + struct iov_iter *iter, + loff_t offset, + void (*endio)(struct kiocb *iocb, + loff_t offset, + ssize_t size, + void *private), + int flags) +{ + struct block_device *bdev; + + if (IS_DAX(inode)) + return dax_do_io(iocb, inode, iter, offset, + xfs_get_blocks_direct, endio, 0); + + bdev = xfs_find_bdev_for_inode(inode); + return __blockdev_direct_IO(iocb, inode, bdev, iter, offset, + xfs_get_blocks_direct, endio, NULL, flags); +} + STATIC ssize_t xfs_vm_direct_IO( struct kiocb *iocb, @@ -1614,16 +1686,11 @@ xfs_vm_direct_IO( loff_t offset) { struct inode *inode = iocb->ki_filp->f_mapping->host; - struct block_device *bdev = xfs_find_bdev_for_inode(inode); - if (iov_iter_rw(iter) == WRITE) { - return __blockdev_direct_IO(iocb, inode, bdev, iter, offset, - xfs_get_blocks_direct, - xfs_end_io_direct_write, NULL, - DIO_ASYNC_EXTEND); - } - return __blockdev_direct_IO(iocb, inode, bdev, iter, offset, - xfs_get_blocks_direct, NULL, NULL, 0); + if (iov_iter_rw(iter) == WRITE) + return xfs_vm_do_dio(inode, iocb, iter, offset, + xfs_end_io_direct_write, DIO_ASYNC_EXTEND); + return xfs_vm_do_dio(inode, iocb, iter, offset, NULL, 0); } /* @@ -1874,6 +1941,7 @@ xfs_vm_set_page_dirty( loff_t end_offset; loff_t offset; int newly_dirty; + struct mem_cgroup *memcg; if (unlikely(!mapping)) return !TestSetPageDirty(page); @@ -1893,6 +1961,11 @@ xfs_vm_set_page_dirty( offset += 1 << inode->i_blkbits; } while (bh != head); } + /* + * Use mem_group_begin_page_stat() to keep PageDirty synchronized with + * per-memcg dirty page counters. + */ + memcg = mem_cgroup_begin_page_stat(page); newly_dirty = !TestSetPageDirty(page); spin_unlock(&mapping->private_lock); @@ -1903,13 +1976,15 @@ xfs_vm_set_page_dirty( spin_lock_irqsave(&mapping->tree_lock, flags); if (page->mapping) { /* Race with truncate? */ WARN_ON_ONCE(!PageUptodate(page)); - account_page_dirtied(page, mapping); + account_page_dirtied(page, mapping, memcg); radix_tree_tag_set(&mapping->page_tree, page_index(page), PAGECACHE_TAG_DIRTY); } spin_unlock_irqrestore(&mapping->tree_lock, flags); - __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); } + mem_cgroup_end_page_stat(memcg); + if (newly_dirty) + __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); return newly_dirty; } diff --git a/fs/xfs/xfs_aops.h b/fs/xfs/xfs_aops.h index ac644e013..86afd1ac7 100644 --- a/fs/xfs/xfs_aops.h +++ b/fs/xfs/xfs_aops.h @@ -53,7 +53,12 @@ typedef struct xfs_ioend { } xfs_ioend_t; extern const struct address_space_operations xfs_address_space_operations; -extern int xfs_get_blocks(struct inode *, sector_t, struct buffer_head *, int); + +int xfs_get_blocks(struct inode *inode, sector_t offset, + struct buffer_head *map_bh, int create); +int xfs_get_blocks_direct(struct inode *inode, sector_t offset, + struct buffer_head *map_bh, int create); +void xfs_end_io_dax_write(struct buffer_head *bh, int uptodate); extern void xfs_count_page_state(struct page *, int *, int *); diff --git a/fs/xfs/xfs_attr_inactive.c b/fs/xfs/xfs_attr_inactive.c index 73e75a87a..2bb959ada 100644 --- a/fs/xfs/xfs_attr_inactive.c +++ b/fs/xfs/xfs_attr_inactive.c @@ -394,7 +394,6 @@ xfs_attr_inactive( { struct xfs_trans *trans; struct xfs_mount *mp; - int cancel_flags = 0; int lock_mode = XFS_ILOCK_SHARED; int error = 0; @@ -423,7 +422,6 @@ xfs_attr_inactive( goto out_cancel; lock_mode = XFS_ILOCK_EXCL; - cancel_flags = XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT; xfs_ilock(dp, lock_mode); if (!XFS_IFORK_Q(dp)) @@ -455,12 +453,12 @@ xfs_attr_inactive( /* Reset the attribute fork - this also destroys the in-core fork */ xfs_attr_fork_remove(dp, trans); - error = xfs_trans_commit(trans, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(trans); xfs_iunlock(dp, lock_mode); return error; out_cancel: - xfs_trans_cancel(trans, cancel_flags); + xfs_trans_cancel(trans); out_destroy_fork: /* kill the in-core attr fork before we drop the inode lock */ if (dp->i_afp) diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c index a52bbd3ab..0f34886cf 100644 --- a/fs/xfs/xfs_bmap_util.c +++ b/fs/xfs/xfs_bmap_util.c @@ -75,28 +75,20 @@ xfs_bmap_finish( xfs_efi_log_item_t *efi; /* extent free intention */ int error; /* error return value */ xfs_bmap_free_item_t *free; /* free extent item */ - struct xfs_trans_res tres; /* new log reservation */ xfs_mount_t *mp; /* filesystem mount structure */ xfs_bmap_free_item_t *next; /* next item on free list */ - xfs_trans_t *ntp; /* new transaction pointer */ ASSERT((*tp)->t_flags & XFS_TRANS_PERM_LOG_RES); if (flist->xbf_count == 0) { *committed = 0; return 0; } - ntp = *tp; - efi = xfs_trans_get_efi(ntp, flist->xbf_count); + efi = xfs_trans_get_efi(*tp, flist->xbf_count); for (free = flist->xbf_first; free; free = free->xbfi_next) - xfs_trans_log_efi_extent(ntp, efi, free->xbfi_startblock, + xfs_trans_log_efi_extent(*tp, efi, free->xbfi_startblock, free->xbfi_blockcount); - tres.tr_logres = ntp->t_log_res; - tres.tr_logcount = ntp->t_log_count; - tres.tr_logflags = XFS_TRANS_PERM_LOG_RES; - ntp = xfs_trans_dup(*tp); - error = xfs_trans_commit(*tp, 0); - *tp = ntp; + error = xfs_trans_roll(tp, NULL); *committed = 1; /* * We have a new transaction, so we should return committed=1, @@ -105,19 +97,10 @@ xfs_bmap_finish( if (error) return error; - /* - * transaction commit worked ok so we can drop the extra ticket - * reference that we gained in xfs_trans_dup() - */ - xfs_log_ticket_put(ntp->t_ticket); - - error = xfs_trans_reserve(ntp, &tres, 0, 0); - if (error) - return error; - efd = xfs_trans_get_efd(ntp, efi, flist->xbf_count); + efd = xfs_trans_get_efd(*tp, efi, flist->xbf_count); for (free = flist->xbf_first; free != NULL; free = next) { next = free->xbfi_next; - if ((error = xfs_free_extent(ntp, free->xbfi_startblock, + if ((error = xfs_free_extent(*tp, free->xbfi_startblock, free->xbfi_blockcount))) { /* * The bmap free list will be cleaned up at a @@ -127,7 +110,7 @@ xfs_bmap_finish( * happens, since this transaction may not be * dirty yet. */ - mp = ntp->t_mountp; + mp = (*tp)->t_mountp; if (!XFS_FORCED_SHUTDOWN(mp)) xfs_force_shutdown(mp, (error == -EFSCORRUPTED) ? @@ -135,7 +118,7 @@ xfs_bmap_finish( SHUTDOWN_META_IO_ERROR); return error; } - xfs_trans_log_efd_extent(ntp, efd, free->xbfi_startblock, + xfs_trans_log_efd_extent(*tp, efd, free->xbfi_startblock, free->xbfi_blockcount); xfs_bmap_del_free(flist, NULL, free); } @@ -878,7 +861,7 @@ xfs_free_eofblocks( if (need_iolock) { if (!xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL)) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); return -EAGAIN; } } @@ -886,7 +869,7 @@ xfs_free_eofblocks( error = xfs_trans_reserve(tp, &M_RES(mp)->tr_itruncate, 0, 0); if (error) { ASSERT(XFS_FORCED_SHUTDOWN(mp)); - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); if (need_iolock) xfs_iunlock(ip, XFS_IOLOCK_EXCL); return error; @@ -908,12 +891,9 @@ xfs_free_eofblocks( * If we get an error at this point we simply don't * bother truncating the file. */ - xfs_trans_cancel(tp, - (XFS_TRANS_RELEASE_LOG_RES | - XFS_TRANS_ABORT)); + xfs_trans_cancel(tp); } else { - error = xfs_trans_commit(tp, - XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); if (!error) xfs_inode_clear_eofblocks_tag(ip); } @@ -1026,7 +1006,7 @@ xfs_alloc_file_space( * Free the transaction structure. */ ASSERT(error == -ENOSPC || XFS_FORCED_SHUTDOWN(mp)); - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); break; } xfs_ilock(ip, XFS_ILOCK_EXCL); @@ -1053,7 +1033,7 @@ xfs_alloc_file_space( goto error0; } - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); xfs_iunlock(ip, XFS_ILOCK_EXCL); if (error) { break; @@ -1077,7 +1057,7 @@ error0: /* Cancel bmap, unlock inode, unreserve quota blocks, cancel trans */ xfs_trans_unreserve_quota_nblks(tp, ip, (long)qblocks, 0, quota_flag); error1: /* Just cancel transaction */ - xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT); + xfs_trans_cancel(tp); xfs_iunlock(ip, XFS_ILOCK_EXCL); return error; } @@ -1133,14 +1113,29 @@ xfs_zero_remaining_bytes( break; ASSERT(imap.br_blockcount >= 1); ASSERT(imap.br_startoff == offset_fsb); + ASSERT(imap.br_startblock != DELAYSTARTBLOCK); + + if (imap.br_startblock == HOLESTARTBLOCK || + imap.br_state == XFS_EXT_UNWRITTEN) { + /* skip the entire extent */ + lastoffset = XFS_FSB_TO_B(mp, imap.br_startoff + + imap.br_blockcount) - 1; + continue; + } + lastoffset = XFS_FSB_TO_B(mp, imap.br_startoff + 1) - 1; if (lastoffset > endoff) lastoffset = endoff; - if (imap.br_startblock == HOLESTARTBLOCK) - continue; - ASSERT(imap.br_startblock != DELAYSTARTBLOCK); - if (imap.br_state == XFS_EXT_UNWRITTEN) + + /* DAX can just zero the backing device directly */ + if (IS_DAX(VFS_I(ip))) { + error = dax_zero_page_range(VFS_I(ip), offset, + lastoffset - offset + 1, + xfs_get_blocks_direct); + if (error) + return error; continue; + } error = xfs_buf_read_uncached(XFS_IS_REALTIME_INODE(ip) ? mp->m_rtdev_targp : mp->m_ddev_targp, @@ -1289,7 +1284,7 @@ xfs_free_file_space( * Free the transaction structure. */ ASSERT(error == -ENOSPC || XFS_FORCED_SHUTDOWN(mp)); - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); break; } xfs_ilock(ip, XFS_ILOCK_EXCL); @@ -1320,7 +1315,7 @@ xfs_free_file_space( goto error0; } - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); xfs_iunlock(ip, XFS_ILOCK_EXCL); } @@ -1330,7 +1325,7 @@ xfs_free_file_space( error0: xfs_bmap_cancel(&free_list); error1: - xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT); + xfs_trans_cancel(tp); xfs_iunlock(ip, XFS_ILOCK_EXCL); goto out; } @@ -1462,7 +1457,7 @@ xfs_shift_file_space( error = xfs_trans_reserve(tp, &M_RES(mp)->tr_write, XFS_DIOSTRAT_SPACE_RES(mp, 0), 0); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); break; } @@ -1492,13 +1487,13 @@ xfs_shift_file_space( if (error) goto out; - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); } return error; out: - xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT); + xfs_trans_cancel(tp); return error; } @@ -1718,7 +1713,7 @@ xfs_swap_extents( tp = xfs_trans_alloc(mp, XFS_TRANS_SWAPEXT); error = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); goto out_unlock; } @@ -1901,7 +1896,7 @@ xfs_swap_extents( if (mp->m_flags & XFS_MOUNT_WSYNC) xfs_trans_set_sync(tp); - error = xfs_trans_commit(tp, 0); + error = xfs_trans_commit(tp); trace_xfs_swap_extent_after(ip, 0); trace_xfs_swap_extent_after(tip, 1); @@ -1915,6 +1910,6 @@ out_unlock: goto out; out_trans_cancel: - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); goto out; } diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 1790b00be..a4b7d92e9 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -1419,9 +1419,9 @@ xfs_buf_submit_wait( return error; } -xfs_caddr_t +void * xfs_buf_offset( - xfs_buf_t *bp, + struct xfs_buf *bp, size_t offset) { struct page *page; @@ -1431,7 +1431,7 @@ xfs_buf_offset( offset += bp->b_offset; page = bp->b_pages[offset >> PAGE_SHIFT]; - return (xfs_caddr_t)page_address(page) + (offset & (PAGE_SIZE-1)); + return page_address(page) + (offset & (PAGE_SIZE-1)); } /* diff --git a/fs/xfs/xfs_buf.h b/fs/xfs/xfs_buf.h index 75ff5d5a7..331c1ccf8 100644 --- a/fs/xfs/xfs_buf.h +++ b/fs/xfs/xfs_buf.h @@ -299,7 +299,7 @@ extern void xfs_buf_iomove(xfs_buf_t *, size_t, size_t, void *, xfs_buf_iomove((bp), (off), (len), NULL, XBRW_ZERO) /* Buffer Utility Routines */ -extern xfs_caddr_t xfs_buf_offset(xfs_buf_t *, size_t); +extern void *xfs_buf_offset(struct xfs_buf *, size_t); /* Delayed Write Buffer Routines */ extern bool xfs_buf_delwri_queue(struct xfs_buf *, struct list_head *); diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 02c01bbbc..4143dc75d 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -568,8 +568,6 @@ xfs_qm_dqread( struct xfs_buf *bp; struct xfs_trans *tp = NULL; int error; - int cancelflags = 0; - dqp = kmem_zone_zalloc(xfs_qm_dqzone, KM_SLEEP); @@ -617,7 +615,6 @@ xfs_qm_dqread( XFS_QM_DQALLOC_SPACE_RES(mp), 0); if (error) goto error1; - cancelflags = XFS_TRANS_RELEASE_LOG_RES; } /* @@ -632,7 +629,6 @@ xfs_qm_dqread( * allocate (ENOENT). */ trace_xfs_dqread_fail(dqp); - cancelflags |= XFS_TRANS_ABORT; goto error1; } @@ -670,7 +666,7 @@ xfs_qm_dqread( xfs_trans_brelse(tp, bp); if (tp) { - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); if (error) goto error0; } @@ -680,7 +676,7 @@ xfs_qm_dqread( error1: if (tp) - xfs_trans_cancel(tp, cancelflags); + xfs_trans_cancel(tp); error0: xfs_qm_dqdestroy(dqp); *O_dqpp = NULL; diff --git a/fs/xfs/xfs_error.c b/fs/xfs/xfs_error.c index 338e50bbf..74d0e5966 100644 --- a/fs/xfs/xfs_error.c +++ b/fs/xfs/xfs_error.c @@ -127,7 +127,7 @@ xfs_error_report( struct xfs_mount *mp, const char *filename, int linenum, - inst_t *ra) + void *ra) { if (level <= xfs_error_level) { xfs_alert_tag(mp, XFS_PTAG_ERROR_REPORT, @@ -146,7 +146,7 @@ xfs_corruption_error( void *p, const char *filename, int linenum, - inst_t *ra) + void *ra) { if (level <= xfs_error_level) xfs_hex_dump(p, 64); diff --git a/fs/xfs/xfs_error.h b/fs/xfs/xfs_error.h index c0394ed12..4ed3042a0 100644 --- a/fs/xfs/xfs_error.h +++ b/fs/xfs/xfs_error.h @@ -21,10 +21,10 @@ struct xfs_mount; extern void xfs_error_report(const char *tag, int level, struct xfs_mount *mp, - const char *filename, int linenum, inst_t *ra); + const char *filename, int linenum, void *ra); extern void xfs_corruption_error(const char *tag, int level, struct xfs_mount *mp, void *p, const char *filename, - int linenum, inst_t *ra); + int linenum, void *ra); extern void xfs_verifier_error(struct xfs_buf *bp); #define XFS_ERROR_REPORT(e, lvl, mp) \ diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c index cb7fe64cd..adc8f8fdd 100644 --- a/fs/xfs/xfs_extfree_item.c +++ b/fs/xfs/xfs_extfree_item.c @@ -239,7 +239,7 @@ xfs_efi_init( xfs_log_item_init(mp, &efip->efi_item, XFS_LI_EFI, &xfs_efi_item_ops); efip->efi_format.efi_nextents = nextents; - efip->efi_format.efi_id = (__psint_t)(void*)efip; + efip->efi_format.efi_id = (uintptr_t)(void *)efip; atomic_set(&efip->efi_next_extent, 0); atomic_set(&efip->efi_refcount, 2); diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 3b7591224..db4acc1c3 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -41,6 +41,7 @@ #include <linux/dcache.h> #include <linux/falloc.h> #include <linux/pagevec.h> +#include <linux/backing-dev.h> static const struct vm_operations_struct xfs_file_vm_ops; @@ -79,14 +80,15 @@ xfs_rw_ilock_demote( } /* - * xfs_iozero + * xfs_iozero clears the specified range supplied via the page cache (except in + * the DAX case). Writes through the page cache will allocate blocks over holes, + * though the callers usually map the holes first and avoid them. If a block is + * not completely zeroed, then it will be read from disk before being partially + * zeroed. * - * xfs_iozero clears the specified range of buffer supplied, - * and marks all the affected blocks as valid and modified. If - * an affected block is not allocated, it will be allocated. If - * an affected block is not completely overwritten, and is not - * valid before the operation, it will be read from disk before - * being partially zeroed. + * In the DAX case, we can just directly write to the underlying pages. This + * will not allocate blocks, but will avoid holes and unwritten extents and so + * not do unnecessary work. */ int xfs_iozero( @@ -96,7 +98,8 @@ xfs_iozero( { struct page *page; struct address_space *mapping; - int status; + int status = 0; + mapping = VFS_I(ip)->i_mapping; do { @@ -108,20 +111,27 @@ xfs_iozero( if (bytes > count) bytes = count; - status = pagecache_write_begin(NULL, mapping, pos, bytes, - AOP_FLAG_UNINTERRUPTIBLE, - &page, &fsdata); - if (status) - break; + if (IS_DAX(VFS_I(ip))) { + status = dax_zero_page_range(VFS_I(ip), pos, bytes, + xfs_get_blocks_direct); + if (status) + break; + } else { + status = pagecache_write_begin(NULL, mapping, pos, bytes, + AOP_FLAG_UNINTERRUPTIBLE, + &page, &fsdata); + if (status) + break; - zero_user(page, offset, bytes); + zero_user(page, offset, bytes); - status = pagecache_write_end(NULL, mapping, pos, bytes, bytes, - page, fsdata); - WARN_ON(status <= 0); /* can't return less than zero! */ + status = pagecache_write_end(NULL, mapping, pos, bytes, + bytes, page, fsdata); + WARN_ON(status <= 0); /* can't return less than zero! */ + status = 0; + } pos += bytes; count -= bytes; - status = 0; } while (count); return status; @@ -138,7 +148,7 @@ xfs_update_prealloc_flags( tp = xfs_trans_alloc(ip->i_mount, XFS_TRANS_WRITEID); error = xfs_trans_reserve(tp, &M_RES(ip->i_mount)->tr_writeid, 0, 0); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); return error; } @@ -160,7 +170,7 @@ xfs_update_prealloc_flags( xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); if (flags & XFS_PREALLOC_SYNC) xfs_trans_set_sync(tp); - return xfs_trans_commit(tp, 0); + return xfs_trans_commit(tp); } /* @@ -284,7 +294,7 @@ xfs_file_read_iter( if (file->f_mode & FMODE_NOCMTIME) ioflags |= XFS_IO_INVIS; - if (unlikely(ioflags & XFS_IO_ISDIRECT)) { + if ((ioflags & XFS_IO_ISDIRECT) && !IS_DAX(inode)) { xfs_buftarg_t *target = XFS_IS_REALTIME_INODE(ip) ? mp->m_rtdev_targp : mp->m_ddev_targp; @@ -378,7 +388,11 @@ xfs_file_splice_read( trace_xfs_file_splice_read(ip, count, *ppos, ioflags); - ret = generic_file_splice_read(infilp, ppos, pipe, count, flags); + /* for dax, we need to avoid the page cache */ + if (IS_DAX(VFS_I(ip))) + ret = default_file_splice_read(infilp, ppos, pipe, count, flags); + else + ret = generic_file_splice_read(infilp, ppos, pipe, count, flags); if (ret > 0) XFS_STATS_ADD(xs_read_bytes, ret); @@ -563,6 +577,13 @@ restart: if (error) return error; + /* For changing security info in file_remove_privs() we need i_mutex */ + if (*iolock == XFS_IOLOCK_SHARED && !IS_NOSEC(inode)) { + xfs_rw_iunlock(ip, *iolock); + *iolock = XFS_IOLOCK_EXCL; + xfs_rw_ilock(ip, *iolock); + goto restart; + } /* * If the offset is beyond the size of the file, we need to zero any * blocks that fall between the existing EOF and the start of this @@ -623,7 +644,9 @@ restart: * setgid bits if the process is not being run by root. This keeps * people from modifying setuid and setgid binaries. */ - return file_remove_suid(file); + if (!IS_NOSEC(inode)) + return file_remove_privs(file); + return 0; } /* @@ -672,7 +695,7 @@ xfs_file_dio_aio_write( mp->m_rtdev_targp : mp->m_ddev_targp; /* DIO must be aligned to device logical sector size */ - if ((pos | count) & target->bt_logical_sectormask) + if (!IS_DAX(inode) && ((pos | count) & target->bt_logical_sectormask)) return -EINVAL; /* "unaligned" here means not aligned to a filesystem block */ @@ -758,8 +781,11 @@ xfs_file_dio_aio_write( out: xfs_rw_iunlock(ip, iolock); - /* No fallback to buffered IO on errors for XFS. */ - ASSERT(ret < 0 || ret == count); + /* + * No fallback to buffered IO on errors for XFS. DAX can result in + * partial writes, but direct IO will either complete fully or fail. + */ + ASSERT(ret < 0 || ret == count || IS_DAX(VFS_I(ip))); return ret; } @@ -842,7 +868,7 @@ xfs_file_write_iter( if (XFS_FORCED_SHUTDOWN(ip->i_mount)) return -EIO; - if (unlikely(iocb->ki_flags & IOCB_DIRECT)) + if ((iocb->ki_flags & IOCB_DIRECT) || IS_DAX(inode)) ret = xfs_file_dio_aio_write(iocb, from); else ret = xfs_file_buffered_aio_write(iocb, from); @@ -1063,17 +1089,6 @@ xfs_file_readdir( return xfs_readdir(ip, ctx, bufsize); } -STATIC int -xfs_file_mmap( - struct file *filp, - struct vm_area_struct *vma) -{ - vma->vm_ops = &xfs_file_vm_ops; - - file_accessed(filp); - return 0; -} - /* * This type is designed to indicate the type of offset we would like * to search from page cache for xfs_seek_hole_data(). @@ -1454,48 +1469,92 @@ xfs_file_llseek( * ordering of: * * mmap_sem (MM) - * i_mmap_lock (XFS - truncate serialisation) - * page_lock (MM) - * i_lock (XFS - extent map serialisation) + * sb_start_pagefault(vfs, freeze) + * i_mmap_lock (XFS - truncate serialisation) + * page_lock (MM) + * i_lock (XFS - extent map serialisation) + */ + +/* + * mmap()d file has taken write protection fault and is being made writable. We + * can set the page state up correctly for a writable page, which means we can + * do correct delalloc accounting (ENOSPC checking!) and unwritten extent + * mapping. */ STATIC int -xfs_filemap_fault( +xfs_filemap_page_mkwrite( struct vm_area_struct *vma, struct vm_fault *vmf) { - struct xfs_inode *ip = XFS_I(vma->vm_file->f_mapping->host); - int error; + struct inode *inode = file_inode(vma->vm_file); + int ret; - trace_xfs_filemap_fault(ip); + trace_xfs_filemap_page_mkwrite(XFS_I(inode)); - xfs_ilock(ip, XFS_MMAPLOCK_SHARED); - error = filemap_fault(vma, vmf); - xfs_iunlock(ip, XFS_MMAPLOCK_SHARED); + sb_start_pagefault(inode->i_sb); + file_update_time(vma->vm_file); + xfs_ilock(XFS_I(inode), XFS_MMAPLOCK_SHARED); - return error; + if (IS_DAX(inode)) { + ret = __dax_mkwrite(vma, vmf, xfs_get_blocks_direct, + xfs_end_io_dax_write); + } else { + ret = __block_page_mkwrite(vma, vmf, xfs_get_blocks); + ret = block_page_mkwrite_return(ret); + } + + xfs_iunlock(XFS_I(inode), XFS_MMAPLOCK_SHARED); + sb_end_pagefault(inode->i_sb); + + return ret; } -/* - * mmap()d file has taken write protection fault and is being made writable. We - * can set the page state up correctly for a writable page, which means we can - * do correct delalloc accounting (ENOSPC checking!) and unwritten extent - * mapping. - */ STATIC int -xfs_filemap_page_mkwrite( +xfs_filemap_fault( struct vm_area_struct *vma, struct vm_fault *vmf) { - struct xfs_inode *ip = XFS_I(vma->vm_file->f_mapping->host); - int error; + struct inode *inode = file_inode(vma->vm_file); + int ret; - trace_xfs_filemap_page_mkwrite(ip); + trace_xfs_filemap_fault(XFS_I(inode)); - xfs_ilock(ip, XFS_MMAPLOCK_SHARED); - error = block_page_mkwrite(vma, vmf, xfs_get_blocks); - xfs_iunlock(ip, XFS_MMAPLOCK_SHARED); + /* DAX can shortcut the normal fault path on write faults! */ + if ((vmf->flags & FAULT_FLAG_WRITE) && IS_DAX(inode)) + return xfs_filemap_page_mkwrite(vma, vmf); - return error; + xfs_ilock(XFS_I(inode), XFS_MMAPLOCK_SHARED); + if (IS_DAX(inode)) { + /* + * we do not want to trigger unwritten extent conversion on read + * faults - that is unnecessary overhead and would also require + * changes to xfs_get_blocks_direct() to map unwritten extent + * ioend for conversion on read-only mappings. + */ + ret = __dax_fault(vma, vmf, xfs_get_blocks_direct, NULL); + } else + ret = filemap_fault(vma, vmf); + xfs_iunlock(XFS_I(inode), XFS_MMAPLOCK_SHARED); + + return ret; +} + +static const struct vm_operations_struct xfs_file_vm_ops = { + .fault = xfs_filemap_fault, + .map_pages = filemap_map_pages, + .page_mkwrite = xfs_filemap_page_mkwrite, +}; + +STATIC int +xfs_file_mmap( + struct file *filp, + struct vm_area_struct *vma) +{ + file_accessed(filp); + vma->vm_ops = &xfs_file_vm_ops; + if (IS_DAX(file_inode(filp))) + vma->vm_flags |= VM_MIXEDMAP; + return 0; } const struct file_operations xfs_file_operations = { @@ -1526,9 +1585,3 @@ const struct file_operations xfs_dir_file_operations = { #endif .fsync = xfs_dir_fsync, }; - -static const struct vm_operations_struct xfs_file_vm_ops = { - .fault = xfs_filemap_fault, - .map_pages = filemap_map_pages, - .page_mkwrite = xfs_filemap_page_mkwrite, -}; diff --git a/fs/xfs/xfs_filestream.c b/fs/xfs/xfs_filestream.c index da82f1cb4..c4c130f9b 100644 --- a/fs/xfs/xfs_filestream.c +++ b/fs/xfs/xfs_filestream.c @@ -196,7 +196,8 @@ xfs_filestream_pick_ag( goto next_ag; } - longest = xfs_alloc_longest_free_extent(mp, pag); + longest = xfs_alloc_longest_free_extent(mp, pag, + xfs_alloc_min_freelist(mp, pag)); if (((minlen && longest >= minlen) || (!minlen && pag->pagf_freeblks >= minfree)) && (!pag->pagf_metadata || !(flags & XFS_PICK_USERDATA) || diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c index cb7e8a29d..9b3438a76 100644 --- a/fs/xfs/xfs_fsops.c +++ b/fs/xfs/xfs_fsops.c @@ -101,7 +101,9 @@ xfs_fs_geometry( (xfs_sb_version_hasftype(&mp->m_sb) ? XFS_FSOP_GEOM_FLAGS_FTYPE : 0) | (xfs_sb_version_hasfinobt(&mp->m_sb) ? - XFS_FSOP_GEOM_FLAGS_FINOBT : 0); + XFS_FSOP_GEOM_FLAGS_FINOBT : 0) | + (xfs_sb_version_hassparseinodes(&mp->m_sb) ? + XFS_FSOP_GEOM_FLAGS_SPINODES : 0); geo->logsectsize = xfs_sb_version_hassector(&mp->m_sb) ? mp->m_sb.sb_logsectsize : BBSIZE; geo->rtsectsize = mp->m_sb.sb_blocksize; @@ -201,7 +203,7 @@ xfs_growfs_data_private( error = xfs_trans_reserve(tp, &M_RES(mp)->tr_growdata, XFS_GROWFS_SPACE_RES(mp), 0); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); return error; } @@ -489,7 +491,7 @@ xfs_growfs_data_private( if (dpct) xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct); xfs_trans_set_sync(tp); - error = xfs_trans_commit(tp, 0); + error = xfs_trans_commit(tp); if (error) return error; @@ -557,7 +559,7 @@ xfs_growfs_data_private( return saved_error ? saved_error : error; error0: - xfs_trans_cancel(tp, XFS_TRANS_ABORT); + xfs_trans_cancel(tp); return error; } diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 539a85fdd..3da9f4da4 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -905,7 +905,6 @@ xfs_dir_ialloc( { xfs_trans_t *tp; - xfs_trans_t *ntp; xfs_inode_t *ip; xfs_buf_t *ialloc_context = NULL; int code; @@ -954,8 +953,6 @@ xfs_dir_ialloc( * to succeed the second time. */ if (ialloc_context) { - struct xfs_trans_res tres; - /* * Normally, xfs_trans_commit releases all the locks. * We call bhold to hang on to the ialloc_context across @@ -964,12 +961,6 @@ xfs_dir_ialloc( * allocation group. */ xfs_trans_bhold(tp, ialloc_context); - /* - * Save the log reservation so we can use - * them in the next transaction. - */ - tres.tr_logres = xfs_trans_get_log_res(tp); - tres.tr_logcount = xfs_trans_get_log_count(tp); /* * We want the quota changes to be associated with the next @@ -985,35 +976,9 @@ xfs_dir_ialloc( tp->t_flags &= ~(XFS_TRANS_DQ_DIRTY); } - ntp = xfs_trans_dup(tp); - code = xfs_trans_commit(tp, 0); - tp = ntp; - if (committed != NULL) { + code = xfs_trans_roll(&tp, 0); + if (committed != NULL) *committed = 1; - } - /* - * If we get an error during the commit processing, - * release the buffer that is still held and return - * to the caller. - */ - if (code) { - xfs_buf_relse(ialloc_context); - if (dqinfo) { - tp->t_dqinfo = dqinfo; - xfs_trans_free_dqinfo(tp); - } - *tpp = ntp; - *ipp = NULL; - return code; - } - - /* - * transaction commit worked ok so we can drop the extra ticket - * reference that we gained in xfs_trans_dup() - */ - xfs_log_ticket_put(tp->t_ticket); - tres.tr_logflags = XFS_TRANS_PERM_LOG_RES; - code = xfs_trans_reserve(tp, &tres, 0, 0); /* * Re-attach the quota info that we detached from prev trx. @@ -1025,7 +990,7 @@ xfs_dir_ialloc( if (code) { xfs_buf_relse(ialloc_context); - *tpp = ntp; + *tpp = tp; *ipp = NULL; return code; } @@ -1127,7 +1092,6 @@ xfs_create( xfs_bmap_free_t free_list; xfs_fsblock_t first_block; bool unlock_dp_on_error = false; - uint cancel_flags; int committed; prid_t prid; struct xfs_dquot *udqp = NULL; @@ -1164,8 +1128,6 @@ xfs_create( tp = xfs_trans_alloc(mp, XFS_TRANS_CREATE); } - cancel_flags = XFS_TRANS_RELEASE_LOG_RES; - /* * Initially assume that the file does not exist and * reserve the resources for that case. If that is not @@ -1183,10 +1145,9 @@ xfs_create( resblks = 0; error = xfs_trans_reserve(tp, tres, 0, 0); } - if (error) { - cancel_flags = 0; + if (error) goto out_trans_cancel; - } + xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT); unlock_dp_on_error = true; @@ -1217,7 +1178,7 @@ xfs_create( if (error) { if (error == -ENOSPC) goto out_trans_cancel; - goto out_trans_abort; + goto out_trans_cancel; } /* @@ -1235,7 +1196,7 @@ xfs_create( resblks - XFS_IALLOC_SPACE_RES(mp) : 0); if (error) { ASSERT(error != -ENOSPC); - goto out_trans_abort; + goto out_trans_cancel; } xfs_trans_ichgtime(tp, dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); @@ -1269,7 +1230,7 @@ xfs_create( if (error) goto out_bmap_cancel; - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); if (error) goto out_release_inode; @@ -1282,10 +1243,8 @@ xfs_create( out_bmap_cancel: xfs_bmap_cancel(&free_list); - out_trans_abort: - cancel_flags |= XFS_TRANS_ABORT; out_trans_cancel: - xfs_trans_cancel(tp, cancel_flags); + xfs_trans_cancel(tp); out_release_inode: /* * Wait until after the current transaction is aborted to finish the @@ -1317,7 +1276,6 @@ xfs_create_tmpfile( struct xfs_inode *ip = NULL; struct xfs_trans *tp = NULL; int error; - uint cancel_flags = XFS_TRANS_RELEASE_LOG_RES; prid_t prid; struct xfs_dquot *udqp = NULL; struct xfs_dquot *gdqp = NULL; @@ -1350,10 +1308,8 @@ xfs_create_tmpfile( resblks = 0; error = xfs_trans_reserve(tp, tres, 0, 0); } - if (error) { - cancel_flags = 0; + if (error) goto out_trans_cancel; - } error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp, pdqp, resblks, 1, 0); @@ -1365,7 +1321,7 @@ xfs_create_tmpfile( if (error) { if (error == -ENOSPC) goto out_trans_cancel; - goto out_trans_abort; + goto out_trans_cancel; } if (mp->m_flags & XFS_MOUNT_WSYNC) @@ -1381,9 +1337,9 @@ xfs_create_tmpfile( ip->i_d.di_nlink--; error = xfs_iunlink(tp, ip); if (error) - goto out_trans_abort; + goto out_trans_cancel; - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); if (error) goto out_release_inode; @@ -1394,10 +1350,8 @@ xfs_create_tmpfile( *ipp = ip; return 0; - out_trans_abort: - cancel_flags |= XFS_TRANS_ABORT; out_trans_cancel: - xfs_trans_cancel(tp, cancel_flags); + xfs_trans_cancel(tp); out_release_inode: /* * Wait until after the current transaction is aborted to finish the @@ -1427,7 +1381,6 @@ xfs_link( int error; xfs_bmap_free_t free_list; xfs_fsblock_t first_block; - int cancel_flags; int committed; int resblks; @@ -1447,17 +1400,14 @@ xfs_link( goto std_return; tp = xfs_trans_alloc(mp, XFS_TRANS_LINK); - cancel_flags = XFS_TRANS_RELEASE_LOG_RES; resblks = XFS_LINK_SPACE_RES(mp, target_name->len); error = xfs_trans_reserve(tp, &M_RES(mp)->tr_link, resblks, 0); if (error == -ENOSPC) { resblks = 0; error = xfs_trans_reserve(tp, &M_RES(mp)->tr_link, 0, 0); } - if (error) { - cancel_flags = 0; + if (error) goto error_return; - } xfs_lock_two_inodes(sip, tdp, XFS_ILOCK_EXCL); @@ -1486,19 +1436,19 @@ xfs_link( if (sip->i_d.di_nlink == 0) { error = xfs_iunlink_remove(tp, sip); if (error) - goto abort_return; + goto error_return; } error = xfs_dir_createname(tp, tdp, target_name, sip->i_ino, &first_block, &free_list, resblks); if (error) - goto abort_return; + goto error_return; xfs_trans_ichgtime(tp, tdp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); xfs_trans_log_inode(tp, tdp, XFS_ILOG_CORE); error = xfs_bumplink(tp, sip); if (error) - goto abort_return; + goto error_return; /* * If this is a synchronous mount, make sure that the @@ -1512,15 +1462,13 @@ xfs_link( error = xfs_bmap_finish (&tp, &free_list, &committed); if (error) { xfs_bmap_cancel(&free_list); - goto abort_return; + goto error_return; } - return xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + return xfs_trans_commit(tp); - abort_return: - cancel_flags |= XFS_TRANS_ABORT; error_return: - xfs_trans_cancel(tp, cancel_flags); + xfs_trans_cancel(tp); std_return: return error; } @@ -1555,7 +1503,6 @@ xfs_itruncate_extents( { struct xfs_mount *mp = ip->i_mount; struct xfs_trans *tp = *tpp; - struct xfs_trans *ntp; xfs_bmap_free_t free_list; xfs_fsblock_t first_block; xfs_fileoff_t first_unmap_block; @@ -1613,29 +1560,7 @@ xfs_itruncate_extents( if (error) goto out_bmap_cancel; - if (committed) { - /* - * Mark the inode dirty so it will be logged and - * moved forward in the log as part of every commit. - */ - xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); - } - - ntp = xfs_trans_dup(tp); - error = xfs_trans_commit(tp, 0); - tp = ntp; - - xfs_trans_ijoin(tp, ip, 0); - - if (error) - goto out; - - /* - * Transaction commit worked ok so we can drop the extra ticket - * reference that we gained in xfs_trans_dup() - */ - xfs_log_ticket_put(tp->t_ticket); - error = xfs_trans_reserve(tp, &M_RES(mp)->tr_itruncate, 0, 0); + error = xfs_trans_roll(&tp, ip); if (error) goto out; } @@ -1756,7 +1681,7 @@ xfs_inactive_truncate( error = xfs_trans_reserve(tp, &M_RES(mp)->tr_itruncate, 0, 0); if (error) { ASSERT(XFS_FORCED_SHUTDOWN(mp)); - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); return error; } @@ -1777,7 +1702,7 @@ xfs_inactive_truncate( ASSERT(ip->i_d.di_nextents == 0); - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); if (error) goto error_unlock; @@ -1785,7 +1710,7 @@ xfs_inactive_truncate( return 0; error_trans_cancel: - xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT); + xfs_trans_cancel(tp); error_unlock: xfs_iunlock(ip, XFS_ILOCK_EXCL); return error; @@ -1835,7 +1760,7 @@ xfs_inactive_ifree( } else { ASSERT(XFS_FORCED_SHUTDOWN(mp)); } - xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES); + xfs_trans_cancel(tp); return error; } @@ -1855,7 +1780,7 @@ xfs_inactive_ifree( __func__, error); xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR); } - xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT); + xfs_trans_cancel(tp); xfs_iunlock(ip, XFS_ILOCK_EXCL); return error; } @@ -1874,7 +1799,7 @@ xfs_inactive_ifree( if (error) xfs_notice(mp, "%s: xfs_bmap_finish returned error %d", __func__, error); - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); if (error) xfs_notice(mp, "%s: xfs_trans_commit returned error %d", __func__, error); @@ -2235,28 +2160,42 @@ xfs_iunlink_remove( */ STATIC int xfs_ifree_cluster( - xfs_inode_t *free_ip, - xfs_trans_t *tp, - xfs_ino_t inum) + xfs_inode_t *free_ip, + xfs_trans_t *tp, + struct xfs_icluster *xic) { xfs_mount_t *mp = free_ip->i_mount; int blks_per_cluster; int inodes_per_cluster; int nbufs; int i, j; + int ioffset; xfs_daddr_t blkno; xfs_buf_t *bp; xfs_inode_t *ip; xfs_inode_log_item_t *iip; xfs_log_item_t *lip; struct xfs_perag *pag; + xfs_ino_t inum; + inum = xic->first_ino; pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, inum)); blks_per_cluster = xfs_icluster_size_fsb(mp); inodes_per_cluster = blks_per_cluster << mp->m_sb.sb_inopblog; nbufs = mp->m_ialloc_blks / blks_per_cluster; for (j = 0; j < nbufs; j++, inum += inodes_per_cluster) { + /* + * The allocation bitmap tells us which inodes of the chunk were + * physically allocated. Skip the cluster if an inode falls into + * a sparse region. + */ + ioffset = inum - xic->first_ino; + if ((xic->alloc & XFS_INOBT_MASK(ioffset)) == 0) { + ASSERT(do_mod(ioffset, inodes_per_cluster) == 0); + continue; + } + blkno = XFS_AGB_TO_DADDR(mp, XFS_INO_TO_AGNO(mp, inum), XFS_INO_TO_AGBNO(mp, inum)); @@ -2414,8 +2353,7 @@ xfs_ifree( xfs_bmap_free_t *flist) { int error; - int delete; - xfs_ino_t first_ino; + struct xfs_icluster xic = { 0 }; ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); ASSERT(ip->i_d.di_nlink == 0); @@ -2431,7 +2369,7 @@ xfs_ifree( if (error) return error; - error = xfs_difree(tp, ip->i_ino, flist, &delete, &first_ino); + error = xfs_difree(tp, ip->i_ino, flist, &xic); if (error) return error; @@ -2448,8 +2386,8 @@ xfs_ifree( ip->i_d.di_gen++; xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); - if (delete) - error = xfs_ifree_cluster(ip, tp, first_ino); + if (xic.deleted) + error = xfs_ifree_cluster(ip, tp, &xic); return error; } @@ -2536,7 +2474,6 @@ xfs_remove( int error = 0; xfs_bmap_free_t free_list; xfs_fsblock_t first_block; - int cancel_flags; int committed; uint resblks; @@ -2557,7 +2494,6 @@ xfs_remove( tp = xfs_trans_alloc(mp, XFS_TRANS_RMDIR); else tp = xfs_trans_alloc(mp, XFS_TRANS_REMOVE); - cancel_flags = XFS_TRANS_RELEASE_LOG_RES; /* * We try to get the real space reservation first, @@ -2576,7 +2512,6 @@ xfs_remove( } if (error) { ASSERT(error != -ENOSPC); - cancel_flags = 0; goto out_trans_cancel; } @@ -2588,7 +2523,6 @@ xfs_remove( /* * If we're removing a directory perform some additional validation. */ - cancel_flags |= XFS_TRANS_ABORT; if (is_dir) { ASSERT(ip->i_d.di_nlink >= 2); if (ip->i_d.di_nlink != 2) { @@ -2644,7 +2578,7 @@ xfs_remove( if (error) goto out_bmap_cancel; - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); if (error) goto std_return; @@ -2656,7 +2590,7 @@ xfs_remove( out_bmap_cancel: xfs_bmap_cancel(&free_list); out_trans_cancel: - xfs_trans_cancel(tp, cancel_flags); + xfs_trans_cancel(tp); std_return: return error; } @@ -2730,11 +2664,11 @@ xfs_finish_rename( error = xfs_bmap_finish(&tp, free_list, &committed); if (error) { xfs_bmap_cancel(free_list); - xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT); + xfs_trans_cancel(tp); return error; } - return xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + return xfs_trans_commit(tp); } /* @@ -2855,7 +2789,7 @@ xfs_cross_rename( out_trans_abort: xfs_bmap_cancel(free_list); - xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT); + xfs_trans_cancel(tp); return error; } @@ -2915,7 +2849,6 @@ xfs_rename( int num_inodes = __XFS_SORT_INODES; bool new_parent = (src_dp != target_dp); bool src_is_directory = S_ISDIR(src_ip->i_d.di_mode); - int cancel_flags = 0; int spaceres; int error; @@ -2951,7 +2884,6 @@ xfs_rename( } if (error) goto out_trans_cancel; - cancel_flags = XFS_TRANS_RELEASE_LOG_RES; /* * Attach the dquots to the inodes @@ -3022,10 +2954,8 @@ xfs_rename( error = xfs_dir_createname(tp, target_dp, target_name, src_ip->i_ino, &first_block, &free_list, spaceres); - if (error == -ENOSPC) - goto out_bmap_cancel; if (error) - goto out_trans_abort; + goto out_bmap_cancel; xfs_trans_ichgtime(tp, target_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); @@ -3033,7 +2963,7 @@ xfs_rename( if (new_parent && src_is_directory) { error = xfs_bumplink(tp, target_dp); if (error) - goto out_trans_abort; + goto out_bmap_cancel; } } else { /* target_ip != NULL */ /* @@ -3065,7 +2995,7 @@ xfs_rename( src_ip->i_ino, &first_block, &free_list, spaceres); if (error) - goto out_trans_abort; + goto out_bmap_cancel; xfs_trans_ichgtime(tp, target_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); @@ -3076,7 +3006,7 @@ xfs_rename( */ error = xfs_droplink(tp, target_ip); if (error) - goto out_trans_abort; + goto out_bmap_cancel; if (src_is_directory) { /* @@ -3084,7 +3014,7 @@ xfs_rename( */ error = xfs_droplink(tp, target_ip); if (error) - goto out_trans_abort; + goto out_bmap_cancel; } } /* target_ip != NULL */ @@ -3101,7 +3031,7 @@ xfs_rename( &first_block, &free_list, spaceres); ASSERT(error != -EEXIST); if (error) - goto out_trans_abort; + goto out_bmap_cancel; } /* @@ -3127,7 +3057,7 @@ xfs_rename( */ error = xfs_droplink(tp, src_dp); if (error) - goto out_trans_abort; + goto out_bmap_cancel; } /* @@ -3142,7 +3072,7 @@ xfs_rename( error = xfs_dir_removename(tp, src_dp, src_name, src_ip->i_ino, &first_block, &free_list, spaceres); if (error) - goto out_trans_abort; + goto out_bmap_cancel; /* * For whiteouts, we need to bump the link count on the whiteout inode. @@ -3156,10 +3086,10 @@ xfs_rename( ASSERT(VFS_I(wip)->i_nlink == 0 && wip->i_d.di_nlink == 0); error = xfs_bumplink(tp, wip); if (error) - goto out_trans_abort; + goto out_bmap_cancel; error = xfs_iunlink_remove(tp, wip); if (error) - goto out_trans_abort; + goto out_bmap_cancel; xfs_trans_log_inode(tp, wip, XFS_ILOG_CORE); /* @@ -3180,12 +3110,10 @@ xfs_rename( IRELE(wip); return error; -out_trans_abort: - cancel_flags |= XFS_TRANS_ABORT; out_bmap_cancel: xfs_bmap_cancel(&free_list); out_trans_cancel: - xfs_trans_cancel(tp, cancel_flags); + xfs_trans_cancel(tp); if (wip) IRELE(wip); return error; @@ -3464,7 +3392,7 @@ xfs_iflush_int( ASSERT(ip->i_d.di_version > 1); /* set *dip = inode's place in the buffer */ - dip = (xfs_dinode_t *)xfs_buf_offset(bp, ip->i_imap.im_boffset); + dip = xfs_buf_offset(bp, ip->i_imap.im_boffset); if (XFS_TEST_ERROR(dip->di_magic != cpu_to_be16(XFS_DINODE_MAGIC), mp, XFS_ERRTAG_IFLUSH_1, XFS_RANDOM_IFLUSH_1)) { diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index 87f67c6b6..ea7d85af5 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c @@ -336,7 +336,7 @@ xfs_set_dmattrs( tp = xfs_trans_alloc(mp, XFS_TRANS_SET_DMATTRS); error = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); return error; } xfs_ilock(ip, XFS_ILOCK_EXCL); @@ -346,7 +346,7 @@ xfs_set_dmattrs( ip->i_d.di_dmstate = state; xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); - error = xfs_trans_commit(tp, 0); + error = xfs_trans_commit(tp); return error; } @@ -1076,7 +1076,7 @@ xfs_ioctl_setattr_get_trans( return tp; out_cancel: - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); return ERR_PTR(error); } @@ -1253,7 +1253,7 @@ xfs_ioctl_setattr( else ip->i_d.di_extsize = 0; - code = xfs_trans_commit(tp, 0); + code = xfs_trans_commit(tp); /* * Release any dquot(s) the inode had kept before chown. @@ -1265,7 +1265,7 @@ xfs_ioctl_setattr( return code; error_trans_cancel: - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); error_free_dquots: xfs_qm_dqrele(udqp); xfs_qm_dqrele(pdqp); @@ -1338,11 +1338,11 @@ xfs_ioc_setxflags( error = xfs_ioctl_setattr_xflags(tp, ip, &fa); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); goto out_drop_write; } - error = xfs_trans_commit(tp, 0); + error = xfs_trans_commit(tp); out_drop_write: mnt_drop_write_file(filp); return error; diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index 38e633bad..1f8603317 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -183,7 +183,7 @@ xfs_iomap_write_direct( * Check for running out of space, note: need lock to return */ if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); return error; } @@ -213,7 +213,7 @@ xfs_iomap_write_direct( error = xfs_bmap_finish(&tp, &free_list, &committed); if (error) goto out_bmap_cancel; - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); if (error) goto out_unlock; @@ -236,7 +236,7 @@ out_bmap_cancel: xfs_bmap_cancel(&free_list); xfs_trans_unreserve_quota_nblks(tp, ip, (long)qblocks, 0, quota_flag); out_trans_cancel: - xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT); + xfs_trans_cancel(tp); goto out_unlock; } @@ -690,7 +690,7 @@ xfs_iomap_write_allocate( error = xfs_trans_reserve(tp, &M_RES(mp)->tr_write, nres, 0); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); return error; } xfs_ilock(ip, XFS_ILOCK_EXCL); @@ -760,7 +760,7 @@ xfs_iomap_write_allocate( if (error) goto trans_cancel; - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); if (error) goto error0; @@ -791,7 +791,7 @@ xfs_iomap_write_allocate( trans_cancel: xfs_bmap_cancel(&free_list); - xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT); + xfs_trans_cancel(tp); error0: xfs_iunlock(ip, XFS_ILOCK_EXCL); return error; @@ -853,7 +853,7 @@ xfs_iomap_write_unwritten( error = xfs_trans_reserve(tp, &M_RES(mp)->tr_write, resblks, 0); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); return error; } @@ -890,7 +890,7 @@ xfs_iomap_write_unwritten( if (error) goto error_on_bmapi_transaction; - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); xfs_iunlock(ip, XFS_ILOCK_EXCL); if (error) return error; @@ -914,7 +914,7 @@ xfs_iomap_write_unwritten( error_on_bmapi_transaction: xfs_bmap_cancel(&free_list); - xfs_trans_cancel(tp, (XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT)); + xfs_trans_cancel(tp); xfs_iunlock(ip, XFS_ILOCK_EXCL); return error; } diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index f4cd7204e..766b23f86 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -41,7 +41,6 @@ #include <linux/capability.h> #include <linux/xattr.h> -#include <linux/namei.h> #include <linux/posix_acl.h> #include <linux/security.h> #include <linux/fiemap.h> @@ -414,10 +413,10 @@ xfs_vn_rename( * we need to be very careful about how much stack we use. * uio is kmalloced for this reason... */ -STATIC void * +STATIC const char * xfs_vn_follow_link( struct dentry *dentry, - struct nameidata *nd) + void **cookie) { char *link; int error = -ENOMEM; @@ -430,14 +429,12 @@ xfs_vn_follow_link( if (unlikely(error)) goto out_kfree; - nd_set_link(nd, link); - return NULL; + return *cookie = link; out_kfree: kfree(link); out_err: - nd_set_link(nd, ERR_PTR(error)); - return NULL; + return ERR_PTR(error); } STATIC int @@ -702,7 +699,7 @@ xfs_setattr_nonsize( if (mp->m_flags & XFS_MOUNT_WSYNC) xfs_trans_set_sync(tp); - error = xfs_trans_commit(tp, 0); + error = xfs_trans_commit(tp); xfs_iunlock(ip, XFS_ILOCK_EXCL); @@ -733,7 +730,7 @@ xfs_setattr_nonsize( return 0; out_trans_cancel: - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); xfs_iunlock(ip, XFS_ILOCK_EXCL); out_dqrele: xfs_qm_dqrele(udqp); @@ -755,7 +752,6 @@ xfs_setattr_size( struct xfs_trans *tp; int error; uint lock_flags = 0; - uint commit_flags = 0; bool did_zeroing = false; trace_xfs_setattr(ip); @@ -851,7 +847,11 @@ xfs_setattr_size( * to hope that the caller sees ENOMEM and retries the truncate * operation. */ - error = block_truncate_page(inode->i_mapping, newsize, xfs_get_blocks); + if (IS_DAX(inode)) + error = dax_truncate_page(inode, newsize, xfs_get_blocks_direct); + else + error = block_truncate_page(inode->i_mapping, newsize, + xfs_get_blocks); if (error) return error; truncate_setsize(inode, newsize); @@ -861,7 +861,6 @@ xfs_setattr_size( if (error) goto out_trans_cancel; - commit_flags = XFS_TRANS_RELEASE_LOG_RES; lock_flags |= XFS_ILOCK_EXCL; xfs_ilock(ip, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, ip, 0); @@ -901,7 +900,7 @@ xfs_setattr_size( if (newsize <= oldsize) { error = xfs_itruncate_extents(&tp, ip, XFS_DATA_FORK, newsize); if (error) - goto out_trans_abort; + goto out_trans_cancel; /* * Truncated "down", so we're removing references to old data @@ -928,16 +927,14 @@ xfs_setattr_size( if (mp->m_flags & XFS_MOUNT_WSYNC) xfs_trans_set_sync(tp); - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); out_unlock: if (lock_flags) xfs_iunlock(ip, lock_flags); return error; -out_trans_abort: - commit_flags |= XFS_TRANS_ABORT; out_trans_cancel: - xfs_trans_cancel(tp, commit_flags); + xfs_trans_cancel(tp); goto out_unlock; } @@ -984,7 +981,7 @@ xfs_vn_update_time( tp = xfs_trans_alloc(mp, XFS_TRANS_FSYNC_TS); error = xfs_trans_reserve(tp, &M_RES(mp)->tr_fsyncts, 0, 0); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); return error; } @@ -1006,7 +1003,7 @@ xfs_vn_update_time( } xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); xfs_trans_log_inode(tp, ip, XFS_ILOG_TIMESTAMP); - return xfs_trans_commit(tp, 0); + return xfs_trans_commit(tp); } #define XFS_FIEMAP_FLAGS (FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR) @@ -1191,22 +1188,22 @@ xfs_diflags_to_iflags( struct inode *inode, struct xfs_inode *ip) { - if (ip->i_d.di_flags & XFS_DIFLAG_IMMUTABLE) + uint16_t flags = ip->i_d.di_flags; + + inode->i_flags &= ~(S_IMMUTABLE | S_APPEND | S_SYNC | + S_NOATIME | S_DAX); + + if (flags & XFS_DIFLAG_IMMUTABLE) inode->i_flags |= S_IMMUTABLE; - else - inode->i_flags &= ~S_IMMUTABLE; - if (ip->i_d.di_flags & XFS_DIFLAG_APPEND) + if (flags & XFS_DIFLAG_APPEND) inode->i_flags |= S_APPEND; - else - inode->i_flags &= ~S_APPEND; - if (ip->i_d.di_flags & XFS_DIFLAG_SYNC) + if (flags & XFS_DIFLAG_SYNC) inode->i_flags |= S_SYNC; - else - inode->i_flags &= ~S_SYNC; - if (ip->i_d.di_flags & XFS_DIFLAG_NOATIME) + if (flags & XFS_DIFLAG_NOATIME) inode->i_flags |= S_NOATIME; - else - inode->i_flags &= ~S_NOATIME; + /* XXX: Also needs an on-disk per inode flag! */ + if (ip->i_mount->m_flags & XFS_MOUNT_DAX) + inode->i_flags |= S_DAX; } /* diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c index 80429891d..f41b0c3fd 100644 --- a/fs/xfs/xfs_itable.c +++ b/fs/xfs/xfs_itable.c @@ -252,7 +252,7 @@ xfs_bulkstat_grab_ichunk( } irec->ir_free |= xfs_inobt_maskn(0, idx); - *icount = XFS_INODES_PER_CHUNK - irec->ir_freecount; + *icount = irec->ir_count - irec->ir_freecount; } return 0; @@ -415,6 +415,8 @@ xfs_bulkstat( goto del_cursor; if (icount) { irbp->ir_startino = r.ir_startino; + irbp->ir_holemask = r.ir_holemask; + irbp->ir_count = r.ir_count; irbp->ir_freecount = r.ir_freecount; irbp->ir_free = r.ir_free; irbp++; @@ -447,13 +449,15 @@ xfs_bulkstat( * If this chunk has any allocated inodes, save it. * Also start read-ahead now for this chunk. */ - if (r.ir_freecount < XFS_INODES_PER_CHUNK) { + if (r.ir_freecount < r.ir_count) { xfs_bulkstat_ichunk_ra(mp, agno, &r); irbp->ir_startino = r.ir_startino; + irbp->ir_holemask = r.ir_holemask; + irbp->ir_count = r.ir_count; irbp->ir_freecount = r.ir_freecount; irbp->ir_free = r.ir_free; irbp++; - icount += XFS_INODES_PER_CHUNK - r.ir_freecount; + icount += r.ir_count - r.ir_freecount; } error = xfs_btree_increment(cur, 0, &stat); if (error || stat == 0) { @@ -599,8 +603,7 @@ xfs_inumbers( agino = r.ir_startino + XFS_INODES_PER_CHUNK - 1; buffer[bufidx].xi_startino = XFS_AGINO_TO_INO(mp, agno, r.ir_startino); - buffer[bufidx].xi_alloccount = - XFS_INODES_PER_CHUNK - r.ir_freecount; + buffer[bufidx].xi_alloccount = r.ir_count - r.ir_freecount; buffer[bufidx].xi_allocmask = ~r.ir_free; if (++bufidx == bcount) { long written; diff --git a/fs/xfs/xfs_linux.h b/fs/xfs/xfs_linux.h index 7c7842c85..85f883dd6 100644 --- a/fs/xfs/xfs_linux.h +++ b/fs/xfs/xfs_linux.h @@ -32,26 +32,12 @@ typedef unsigned int __uint32_t; typedef signed long long int __int64_t; typedef unsigned long long int __uint64_t; -typedef __uint32_t inst_t; /* an instruction */ - typedef __s64 xfs_off_t; /* <file offset> type */ typedef unsigned long long xfs_ino_t; /* <inode> type */ typedef __s64 xfs_daddr_t; /* <disk address> type */ -typedef char * xfs_caddr_t; /* <core address> type */ typedef __u32 xfs_dev_t; typedef __u32 xfs_nlink_t; -/* __psint_t is the same size as a pointer */ -#if (BITS_PER_LONG == 32) -typedef __int32_t __psint_t; -typedef __uint32_t __psunsigned_t; -#elif (BITS_PER_LONG == 64) -typedef __int64_t __psint_t; -typedef __uint64_t __psunsigned_t; -#else -#error BITS_PER_LONG must be 32 or 64 -#endif - #include "xfs_types.h" #include "kmem.h" diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index bcc7cfabb..08d4fe46f 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -109,7 +109,7 @@ xlog_ungrant_log_space( STATIC void xlog_verify_dest_ptr( struct xlog *log, - char *ptr); + void *ptr); STATIC void xlog_verify_grant_tail( struct xlog *log); @@ -513,7 +513,7 @@ xfs_log_done( struct xfs_mount *mp, struct xlog_ticket *ticket, struct xlog_in_core **iclog, - uint flags) + bool regrant) { struct xlog *log = mp->m_log; xfs_lsn_t lsn = 0; @@ -526,14 +526,11 @@ xfs_log_done( (((ticket->t_flags & XLOG_TIC_INITED) == 0) && (xlog_commit_record(log, ticket, iclog, &lsn)))) { lsn = (xfs_lsn_t) -1; - if (ticket->t_flags & XLOG_TIC_PERM_RESERV) { - flags |= XFS_LOG_REL_PERM_RESERV; - } + regrant = false; } - if ((ticket->t_flags & XLOG_TIC_PERM_RESERV) == 0 || - (flags & XFS_LOG_REL_PERM_RESERV)) { + if (!regrant) { trace_xfs_log_done_nonperm(log, ticket); /* @@ -541,7 +538,6 @@ xfs_log_done( * request has been made to release a permanent reservation. */ xlog_ungrant_log_space(log, ticket); - xfs_log_ticket_put(ticket); } else { trace_xfs_log_done_perm(log, ticket); @@ -553,6 +549,7 @@ xfs_log_done( ticket->t_flags |= XLOG_TIC_INITED; } + xfs_log_ticket_put(ticket); return lsn; } @@ -1447,7 +1444,7 @@ xlog_alloc_log( iclog->ic_bp = bp; iclog->ic_data = bp->b_addr; #ifdef DEBUG - log->l_iclog_bak[i] = (xfs_caddr_t)&(iclog->ic_header); + log->l_iclog_bak[i] = &iclog->ic_header; #endif head = &iclog->ic_header; memset(head, 0, sizeof(xlog_rec_header_t)); @@ -1602,7 +1599,7 @@ xlog_pack_data( int i, j, k; int size = iclog->ic_offset + roundoff; __be32 cycle_lsn; - xfs_caddr_t dp; + char *dp; cycle_lsn = CYCLE_LSN_DISK(iclog->ic_header.h_lsn); @@ -3664,7 +3661,7 @@ xlog_ticket_alloc( void xlog_verify_dest_ptr( struct xlog *log, - char *ptr) + void *ptr) { int i; int good_ptr = 0; @@ -3767,9 +3764,8 @@ xlog_verify_iclog( xlog_op_header_t *ophead; xlog_in_core_t *icptr; xlog_in_core_2_t *xhdr; - xfs_caddr_t ptr; - xfs_caddr_t base_ptr; - __psint_t field_offset; + void *base_ptr, *ptr, *p; + ptrdiff_t field_offset; __uint8_t clientid; int len, i, j, k, op_len; int idx; @@ -3788,9 +3784,9 @@ xlog_verify_iclog( if (iclog->ic_header.h_magicno != cpu_to_be32(XLOG_HEADER_MAGIC_NUM)) xfs_emerg(log->l_mp, "%s: invalid magic num", __func__); - ptr = (xfs_caddr_t) &iclog->ic_header; - for (ptr += BBSIZE; ptr < ((xfs_caddr_t)&iclog->ic_header) + count; - ptr += BBSIZE) { + base_ptr = ptr = &iclog->ic_header; + p = &iclog->ic_header; + for (ptr += BBSIZE; ptr < base_ptr + count; ptr += BBSIZE) { if (*(__be32 *)ptr == cpu_to_be32(XLOG_HEADER_MAGIC_NUM)) xfs_emerg(log->l_mp, "%s: unexpected magic num", __func__); @@ -3798,20 +3794,19 @@ xlog_verify_iclog( /* check fields */ len = be32_to_cpu(iclog->ic_header.h_num_logops); - ptr = iclog->ic_datap; - base_ptr = ptr; - ophead = (xlog_op_header_t *)ptr; + base_ptr = ptr = iclog->ic_datap; + ophead = ptr; xhdr = iclog->ic_data; for (i = 0; i < len; i++) { - ophead = (xlog_op_header_t *)ptr; + ophead = ptr; /* clientid is only 1 byte */ - field_offset = (__psint_t) - ((xfs_caddr_t)&(ophead->oh_clientid) - base_ptr); + p = &ophead->oh_clientid; + field_offset = p - base_ptr; if (!syncing || (field_offset & 0x1ff)) { clientid = ophead->oh_clientid; } else { - idx = BTOBBT((xfs_caddr_t)&(ophead->oh_clientid) - iclog->ic_datap); + idx = BTOBBT((char *)&ophead->oh_clientid - iclog->ic_datap); if (idx >= (XLOG_HEADER_CYCLE_SIZE / BBSIZE)) { j = idx / (XLOG_HEADER_CYCLE_SIZE / BBSIZE); k = idx % (XLOG_HEADER_CYCLE_SIZE / BBSIZE); @@ -3829,13 +3824,13 @@ xlog_verify_iclog( (unsigned long)field_offset); /* check length */ - field_offset = (__psint_t) - ((xfs_caddr_t)&(ophead->oh_len) - base_ptr); + p = &ophead->oh_len; + field_offset = p - base_ptr; if (!syncing || (field_offset & 0x1ff)) { op_len = be32_to_cpu(ophead->oh_len); } else { - idx = BTOBBT((__psint_t)&ophead->oh_len - - (__psint_t)iclog->ic_datap); + idx = BTOBBT((uintptr_t)&ophead->oh_len - + (uintptr_t)iclog->ic_datap); if (idx >= (XLOG_HEADER_CYCLE_SIZE / BBSIZE)) { j = idx / (XLOG_HEADER_CYCLE_SIZE / BBSIZE); k = idx % (XLOG_HEADER_CYCLE_SIZE / BBSIZE); diff --git a/fs/xfs/xfs_log.h b/fs/xfs/xfs_log.h index 84e0deb95..fa27aaec7 100644 --- a/fs/xfs/xfs_log.h +++ b/fs/xfs/xfs_log.h @@ -111,15 +111,6 @@ static inline xfs_lsn_t _lsn_cmp(xfs_lsn_t lsn1, xfs_lsn_t lsn2) #define XFS_LSN_CMP(x,y) _lsn_cmp(x,y) /* - * Macros, structures, prototypes for interface to the log manager. - */ - -/* - * Flags to xfs_log_done() - */ -#define XFS_LOG_REL_PERM_RESERV 0x1 - -/* * Flags to xfs_log_force() * * XFS_LOG_SYNC: Synchronous force in-core log to disk @@ -138,7 +129,7 @@ struct xfs_log_callback; xfs_lsn_t xfs_log_done(struct xfs_mount *mp, struct xlog_ticket *ticket, struct xlog_in_core **iclog, - uint flags); + bool regrant); int _xfs_log_force(struct xfs_mount *mp, uint flags, int *log_forced); @@ -183,7 +174,7 @@ struct xlog_ticket *xfs_log_ticket_get(struct xlog_ticket *ticket); void xfs_log_ticket_put(struct xlog_ticket *ticket); void xfs_log_commit_cil(struct xfs_mount *mp, struct xfs_trans *tp, - xfs_lsn_t *commit_lsn, int flags); + xfs_lsn_t *commit_lsn, bool regrant); bool xfs_log_item_in_current_chkpt(struct xfs_log_item *lip); void xfs_log_work_queue(struct xfs_mount *mp); diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c index 45cc0ce18..abc2ccbff 100644 --- a/fs/xfs/xfs_log_cil.c +++ b/fs/xfs/xfs_log_cil.c @@ -624,7 +624,7 @@ restart: spin_unlock(&cil->xc_push_lock); /* xfs_log_done always frees the ticket on error. */ - commit_lsn = xfs_log_done(log->l_mp, tic, &commit_iclog, 0); + commit_lsn = xfs_log_done(log->l_mp, tic, &commit_iclog, false); if (commit_lsn == -1) goto out_abort; @@ -773,14 +773,10 @@ xfs_log_commit_cil( struct xfs_mount *mp, struct xfs_trans *tp, xfs_lsn_t *commit_lsn, - int flags) + bool regrant) { struct xlog *log = mp->m_log; struct xfs_cil *cil = log->l_cilp; - int log_flags = 0; - - if (flags & XFS_TRANS_RELEASE_LOG_RES) - log_flags = XFS_LOG_REL_PERM_RESERV; /* lock out background commit */ down_read(&cil->xc_ctx_lock); @@ -795,7 +791,7 @@ xfs_log_commit_cil( if (commit_lsn) *commit_lsn = tp->t_commit_lsn; - xfs_log_done(mp, tp->t_ticket, NULL, log_flags); + xfs_log_done(mp, tp->t_ticket, NULL, regrant); xfs_trans_unreserve_and_mod_sb(tp); /* @@ -809,7 +805,7 @@ xfs_log_commit_cil( * the log items. This affects (at least) processing of stale buffers, * inodes and EFIs. */ - xfs_trans_free_items(tp, tp->t_commit_lsn, 0); + xfs_trans_free_items(tp, tp->t_commit_lsn, false); xlog_cil_push_background(log); diff --git a/fs/xfs/xfs_log_priv.h b/fs/xfs/xfs_log_priv.h index db7cbdeb2..1c87c8abf 100644 --- a/fs/xfs/xfs_log_priv.h +++ b/fs/xfs/xfs_log_priv.h @@ -409,7 +409,7 @@ struct xlog { /* The following field are used for debugging; need to hold icloglock */ #ifdef DEBUG - char *l_iclog_bak[XLOG_MAX_ICLOGS]; + void *l_iclog_bak[XLOG_MAX_ICLOGS]; #endif }; diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index a5d03396d..480ebba84 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -147,7 +147,7 @@ xlog_put_bp( * Return the address of the start of the given block number's data * in a log buffer. The buffer covers a log sector-aligned region. */ -STATIC xfs_caddr_t +STATIC char * xlog_align( struct xlog *log, xfs_daddr_t blk_no, @@ -203,7 +203,7 @@ xlog_bread( xfs_daddr_t blk_no, int nbblks, struct xfs_buf *bp, - xfs_caddr_t *offset) + char **offset) { int error; @@ -225,9 +225,9 @@ xlog_bread_offset( xfs_daddr_t blk_no, /* block to read from */ int nbblks, /* blocks to read */ struct xfs_buf *bp, - xfs_caddr_t offset) + char *offset) { - xfs_caddr_t orig_offset = bp->b_addr; + char *orig_offset = bp->b_addr; int orig_len = BBTOB(bp->b_length); int error, error2; @@ -396,7 +396,7 @@ xlog_find_cycle_start( xfs_daddr_t *last_blk, uint cycle) { - xfs_caddr_t offset; + char *offset; xfs_daddr_t mid_blk; xfs_daddr_t end_blk; uint mid_cycle; @@ -443,7 +443,7 @@ xlog_find_verify_cycle( uint cycle; xfs_buf_t *bp; xfs_daddr_t bufblks; - xfs_caddr_t buf = NULL; + char *buf = NULL; int error = 0; /* @@ -509,7 +509,7 @@ xlog_find_verify_log_record( { xfs_daddr_t i; xfs_buf_t *bp; - xfs_caddr_t offset = NULL; + char *offset = NULL; xlog_rec_header_t *head = NULL; int error = 0; int smallmem = 0; @@ -616,7 +616,7 @@ xlog_find_head( xfs_daddr_t *return_head_blk) { xfs_buf_t *bp; - xfs_caddr_t offset; + char *offset; xfs_daddr_t new_blk, first_blk, start_blk, last_blk, head_blk; int num_scan_bblks; uint first_half_cycle, last_half_cycle; @@ -891,7 +891,7 @@ xlog_find_tail( { xlog_rec_header_t *rhead; xlog_op_header_t *op_head; - xfs_caddr_t offset = NULL; + char *offset = NULL; xfs_buf_t *bp; int error, i, found; xfs_daddr_t umount_data_blk; @@ -1099,7 +1099,7 @@ xlog_find_zeroed( xfs_daddr_t *blk_no) { xfs_buf_t *bp; - xfs_caddr_t offset; + char *offset; uint first_cycle, last_cycle; xfs_daddr_t new_blk, last_blk, start_blk; xfs_daddr_t num_scan_bblks; @@ -1199,7 +1199,7 @@ bp_err: STATIC void xlog_add_record( struct xlog *log, - xfs_caddr_t buf, + char *buf, int cycle, int block, int tail_cycle, @@ -1227,7 +1227,7 @@ xlog_write_log_records( int tail_cycle, int tail_block) { - xfs_caddr_t offset; + char *offset; xfs_buf_t *bp; int balign, ealign; int sectbb = log->l_sectBBsize; @@ -1789,8 +1789,7 @@ xlog_recover_do_inode_buffer( return -EFSCORRUPTED; } - buffer_nextp = (xfs_agino_t *)xfs_buf_offset(bp, - next_unlinked_offset); + buffer_nextp = xfs_buf_offset(bp, next_unlinked_offset); *buffer_nextp = *logged_nextp; /* @@ -1798,7 +1797,7 @@ xlog_recover_do_inode_buffer( * have to leave the inode in a consistent state for whoever * reads it next.... */ - xfs_dinode_calc_crc(mp, (struct xfs_dinode *) + xfs_dinode_calc_crc(mp, xfs_buf_offset(bp, i * mp->m_sb.sb_inodesize)); } @@ -2508,8 +2507,8 @@ xlog_recover_inode_pass2( xfs_buf_t *bp; xfs_dinode_t *dip; int len; - xfs_caddr_t src; - xfs_caddr_t dest; + char *src; + char *dest; int error; int attr_index; uint fields; @@ -2551,7 +2550,7 @@ xlog_recover_inode_pass2( goto out_release; } ASSERT(in_f->ilf_fields & XFS_ILOG_CORE); - dip = (xfs_dinode_t *)xfs_buf_offset(bp, in_f->ilf_boffset); + dip = xfs_buf_offset(bp, in_f->ilf_boffset); /* * Make sure the place we're flushing out to really looks @@ -2890,7 +2889,7 @@ xlog_recover_dquot_pass2( return error; ASSERT(bp); - ddq = (xfs_disk_dquot_t *)xfs_buf_offset(bp, dq_f->qlf_boffset); + ddq = xfs_buf_offset(bp, dq_f->qlf_boffset); /* * If the dquot has an LSN in it, recover the dquot only if it's less @@ -3073,12 +3072,22 @@ xlog_recover_do_icreate_pass2( return -EINVAL; } - /* existing allocation is fixed value */ - ASSERT(count == mp->m_ialloc_inos); - ASSERT(length == mp->m_ialloc_blks); - if (count != mp->m_ialloc_inos || - length != mp->m_ialloc_blks) { - xfs_warn(log->l_mp, "xlog_recover_do_icreate_trans: bad count 2"); + /* + * The inode chunk is either full or sparse and we only support + * m_ialloc_min_blks sized sparse allocations at this time. + */ + if (length != mp->m_ialloc_blks && + length != mp->m_ialloc_min_blks) { + xfs_warn(log->l_mp, + "%s: unsupported chunk length", __FUNCTION__); + return -EINVAL; + } + + /* verify inode count is consistent with extent length */ + if ((count >> mp->m_sb.sb_inopblog) != length) { + xfs_warn(log->l_mp, + "%s: inconsistent inode count and chunk length", + __FUNCTION__); return -EINVAL; } @@ -3096,8 +3105,8 @@ xlog_recover_do_icreate_pass2( XFS_AGB_TO_DADDR(mp, agno, agbno), length, 0)) return 0; - xfs_ialloc_inode_init(mp, NULL, buffer_list, agno, agbno, length, - be32_to_cpu(icl->icl_gen)); + xfs_ialloc_inode_init(mp, NULL, buffer_list, count, agno, agbno, length, + be32_to_cpu(icl->icl_gen)); return 0; } @@ -3369,17 +3378,17 @@ STATIC int xlog_recover_add_to_cont_trans( struct xlog *log, struct xlog_recover *trans, - xfs_caddr_t dp, + char *dp, int len) { xlog_recover_item_t *item; - xfs_caddr_t ptr, old_ptr; + char *ptr, *old_ptr; int old_len; if (list_empty(&trans->r_itemq)) { /* finish copying rest of trans header */ xlog_recover_add_item(&trans->r_itemq); - ptr = (xfs_caddr_t) &trans->r_theader + + ptr = (char *)&trans->r_theader + sizeof(xfs_trans_header_t) - len; memcpy(ptr, dp, len); return 0; @@ -3415,12 +3424,12 @@ STATIC int xlog_recover_add_to_trans( struct xlog *log, struct xlog_recover *trans, - xfs_caddr_t dp, + char *dp, int len) { xfs_inode_log_format_t *in_f; /* any will do */ xlog_recover_item_t *item; - xfs_caddr_t ptr; + char *ptr; if (!len) return 0; @@ -3509,7 +3518,7 @@ STATIC int xlog_recovery_process_trans( struct xlog *log, struct xlog_recover *trans, - xfs_caddr_t dp, + char *dp, unsigned int len, unsigned int flags, int pass) @@ -3616,8 +3625,8 @@ xlog_recover_process_ophdr( struct hlist_head rhash[], struct xlog_rec_header *rhead, struct xlog_op_header *ohead, - xfs_caddr_t dp, - xfs_caddr_t end, + char *dp, + char *end, int pass) { struct xlog_recover *trans; @@ -3666,11 +3675,11 @@ xlog_recover_process_data( struct xlog *log, struct hlist_head rhash[], struct xlog_rec_header *rhead, - xfs_caddr_t dp, + char *dp, int pass) { struct xlog_op_header *ohead; - xfs_caddr_t end; + char *end; int num_logops; int error; @@ -3756,11 +3765,11 @@ xlog_recover_process_efi( } set_bit(XFS_EFI_RECOVERED, &efip->efi_flags); - error = xfs_trans_commit(tp, 0); + error = xfs_trans_commit(tp); return error; abort_error: - xfs_trans_cancel(tp, XFS_TRANS_ABORT); + xfs_trans_cancel(tp); return error; } @@ -3862,13 +3871,13 @@ xlog_recover_clear_agi_bucket( xfs_trans_log_buf(tp, agibp, offset, (offset + sizeof(xfs_agino_t) - 1)); - error = xfs_trans_commit(tp, 0); + error = xfs_trans_commit(tp); if (error) goto out_error; return; out_abort: - xfs_trans_cancel(tp, XFS_TRANS_ABORT); + xfs_trans_cancel(tp); out_error: xfs_warn(mp, "%s: failed to clear agi %d. Continuing.", __func__, agno); return; @@ -4015,7 +4024,7 @@ xlog_recover_process_iunlinks( STATIC int xlog_unpack_data_crc( struct xlog_rec_header *rhead, - xfs_caddr_t dp, + char *dp, struct xlog *log) { __le32 crc; @@ -4045,7 +4054,7 @@ xlog_unpack_data_crc( STATIC int xlog_unpack_data( struct xlog_rec_header *rhead, - xfs_caddr_t dp, + char *dp, struct xlog *log) { int i, j, k; @@ -4127,7 +4136,7 @@ xlog_do_recovery_pass( { xlog_rec_header_t *rhead; xfs_daddr_t blk_no; - xfs_caddr_t offset; + char *offset; xfs_buf_t *hbp, *dbp; int error = 0, h_size; int bblks, split_bblks; diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index 6f23fbdfb..461e791ef 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -725,6 +725,22 @@ xfs_mountfs( } /* + * If enabled, sparse inode chunk alignment is expected to match the + * cluster size. Full inode chunk alignment must match the chunk size, + * but that is checked on sb read verification... + */ + if (xfs_sb_version_hassparseinodes(&mp->m_sb) && + mp->m_sb.sb_spino_align != + XFS_B_TO_FSBT(mp, mp->m_inode_cluster_size)) { + xfs_warn(mp, + "Sparse inode block alignment (%u) must match cluster size (%llu).", + mp->m_sb.sb_spino_align, + XFS_B_TO_FSBT(mp, mp->m_inode_cluster_size)); + error = -EINVAL; + goto out_remove_uuid; + } + + /* * Set inode alignment fields */ xfs_set_inoalignment(mp); diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index 8c995a2cc..7999e91cd 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -101,6 +101,8 @@ typedef struct xfs_mount { __uint64_t m_flags; /* global mount flags */ int m_ialloc_inos; /* inodes in inode allocation */ int m_ialloc_blks; /* blocks in inode allocation */ + int m_ialloc_min_blks;/* min blocks in sparse inode + * allocation */ int m_inoalign_mask;/* mask sb_inoalignmt if used */ uint m_qflags; /* quota status flags */ struct xfs_trans_resv m_resv; /* precomputed res values */ @@ -179,6 +181,8 @@ typedef struct xfs_mount { allocator */ #define XFS_MOUNT_NOATTR2 (1ULL << 25) /* disable use of attr2 format */ +#define XFS_MOUNT_DAX (1ULL << 62) /* TEST ONLY! */ + /* * Default minimum read and write sizes. diff --git a/fs/xfs/xfs_pnfs.c b/fs/xfs/xfs_pnfs.c index 981a657ec..ab4a6066f 100644 --- a/fs/xfs/xfs_pnfs.c +++ b/fs/xfs/xfs_pnfs.c @@ -306,7 +306,7 @@ xfs_fs_commit_blocks( tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE); error = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); goto out_drop_iolock; } @@ -321,7 +321,7 @@ xfs_fs_commit_blocks( } xfs_trans_set_sync(tp); - error = xfs_trans_commit(tp, 0); + error = xfs_trans_commit(tp); out_drop_iolock: xfs_iunlock(ip, XFS_IOLOCK_EXCL); diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 5538468c7..eac9549ef 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -756,7 +756,7 @@ xfs_qm_qino_alloc( error = xfs_trans_reserve(tp, &M_RES(mp)->tr_create, XFS_QM_QINOCREATE_SPACE_RES(mp), 0); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); return error; } @@ -764,8 +764,7 @@ xfs_qm_qino_alloc( error = xfs_dir_ialloc(&tp, NULL, S_IFREG, 1, 0, 0, 1, ip, &committed); if (error) { - xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | - XFS_TRANS_ABORT); + xfs_trans_cancel(tp); return error; } } @@ -796,7 +795,7 @@ xfs_qm_qino_alloc( spin_unlock(&mp->m_sb_lock); xfs_log_sb(tp); - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); if (error) { ASSERT(XFS_FORCED_SHUTDOWN(mp)); xfs_alert(mp, "%s failed (error %d)!", __func__, error); diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index 9a25c9275..3640c6e89 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -239,7 +239,7 @@ xfs_qm_scall_trunc_qfile( tp = xfs_trans_alloc(mp, XFS_TRANS_TRUNCATE_FILE); error = xfs_trans_reserve(tp, &M_RES(mp)->tr_itruncate, 0, 0); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); xfs_iunlock(ip, XFS_IOLOCK_EXCL); goto out_put; } @@ -252,15 +252,14 @@ xfs_qm_scall_trunc_qfile( error = xfs_itruncate_extents(&tp, ip, XFS_DATA_FORK, 0); if (error) { - xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | - XFS_TRANS_ABORT); + xfs_trans_cancel(tp); goto out_unlock; } ASSERT(ip->i_d.di_nextents == 0); xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); out_unlock: xfs_iunlock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); @@ -437,7 +436,7 @@ xfs_qm_scall_setqlim( tp = xfs_trans_alloc(mp, XFS_TRANS_QM_SETQLIM); error = xfs_trans_reserve(tp, &M_RES(mp)->tr_qm_setqlim, 0, 0); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); goto out_rele; } @@ -548,7 +547,7 @@ xfs_qm_scall_setqlim( dqp->dq_flags |= XFS_DQ_DIRTY; xfs_trans_log_dquot(tp, dqp); - error = xfs_trans_commit(tp, 0); + error = xfs_trans_commit(tp); out_rele: xfs_qm_dqrele(dqp); @@ -571,7 +570,7 @@ xfs_qm_log_quotaoff_end( error = xfs_trans_reserve(tp, &M_RES(mp)->tr_qm_equotaoff, 0, 0); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); return error; } @@ -585,8 +584,7 @@ xfs_qm_log_quotaoff_end( * We don't care about quotoff's performance. */ xfs_trans_set_sync(tp); - error = xfs_trans_commit(tp, 0); - return error; + return xfs_trans_commit(tp); } @@ -605,7 +603,7 @@ xfs_qm_log_quotaoff( tp = xfs_trans_alloc(mp, XFS_TRANS_QM_QUOTAOFF); error = xfs_trans_reserve(tp, &M_RES(mp)->tr_qm_quotaoff, 0, 0); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); goto out; } @@ -624,7 +622,7 @@ xfs_qm_log_quotaoff( * We don't care about quotoff's performance. */ xfs_trans_set_sync(tp); - error = xfs_trans_commit(tp, 0); + error = xfs_trans_commit(tp); if (error) goto out; diff --git a/fs/xfs/xfs_quota.h b/fs/xfs/xfs_quota.h index 5376dd406..ce6506ada 100644 --- a/fs/xfs/xfs_quota.h +++ b/fs/xfs/xfs_quota.h @@ -55,7 +55,6 @@ struct xfs_trans; typedef struct xfs_dqtrx { struct xfs_dquot *qt_dquot; /* the dquot this refers to */ ulong qt_blk_res; /* blks reserved on a dquot */ - ulong qt_blk_res_used; /* blks used from the reservation */ ulong qt_ino_res; /* inode reserved on a dquot */ ulong qt_ino_res_used; /* inodes used from the reservation */ long qt_bcount_delta; /* dquot blk count changes */ diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c index f2079b691..f4e8c06ee 100644 --- a/fs/xfs/xfs_rtalloc.c +++ b/fs/xfs/xfs_rtalloc.c @@ -780,7 +780,6 @@ xfs_growfs_rt_alloc( * Allocate space to the file, as necessary. */ while (oblocks < nblocks) { - int cancelflags = 0; xfs_trans_t *tp; tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFSRT_ALLOC); @@ -792,7 +791,6 @@ xfs_growfs_rt_alloc( resblks, 0); if (error) goto error_cancel; - cancelflags = XFS_TRANS_RELEASE_LOG_RES; /* * Lock the inode. */ @@ -804,7 +802,6 @@ xfs_growfs_rt_alloc( * Allocate blocks to the bitmap file. */ nmap = 1; - cancelflags |= XFS_TRANS_ABORT; error = xfs_bmapi_write(tp, ip, oblocks, nblocks - oblocks, XFS_BMAPI_METADATA, &firstblock, resblks, &map, &nmap, &flist); @@ -818,14 +815,13 @@ xfs_growfs_rt_alloc( error = xfs_bmap_finish(&tp, &flist, &committed); if (error) goto error_cancel; - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); if (error) goto error; /* * Now we need to clear the allocated blocks. * Do this one block per transaction, to keep it simple. */ - cancelflags = 0; for (bno = map.br_startoff, fsbno = map.br_startblock; bno < map.br_startoff + map.br_blockcount; bno++, fsbno++) { @@ -851,7 +847,7 @@ xfs_growfs_rt_alloc( if (bp == NULL) { error = -EIO; error_cancel: - xfs_trans_cancel(tp, cancelflags); + xfs_trans_cancel(tp); goto error; } memset(bp->b_addr, 0, mp->m_sb.sb_blocksize); @@ -859,7 +855,7 @@ error_cancel: /* * Commit the transaction. */ - error = xfs_trans_commit(tp, 0); + error = xfs_trans_commit(tp); if (error) goto error; } @@ -973,7 +969,6 @@ xfs_growfs_rt( bmbno < nrbmblocks; bmbno++) { xfs_trans_t *tp; - int cancelflags = 0; *nmp = *mp; nsbp = &nmp->m_sb; @@ -1015,7 +1010,6 @@ xfs_growfs_rt( mp->m_rbmip->i_d.di_size = nsbp->sb_rbmblocks * nsbp->sb_blocksize; xfs_trans_log_inode(tp, mp->m_rbmip, XFS_ILOG_CORE); - cancelflags |= XFS_TRANS_ABORT; /* * Get the summary inode into the transaction. */ @@ -1062,7 +1056,7 @@ xfs_growfs_rt( nsbp->sb_rextents - sbp->sb_rextents, &bp, &sumbno); if (error) { error_cancel: - xfs_trans_cancel(tp, cancelflags); + xfs_trans_cancel(tp); break; } /* @@ -1076,7 +1070,7 @@ error_cancel: mp->m_rsumlevels = nrsumlevels; mp->m_rsumsize = nrsumsize; - error = xfs_trans_commit(tp, 0); + error = xfs_trans_commit(tp); if (error) break; } diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index 858e1e62b..1fb16562c 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -112,6 +112,8 @@ static struct xfs_kobj xfs_dbg_kobj; /* global debug sysfs attrs */ #define MNTOPT_DISCARD "discard" /* Discard unused blocks */ #define MNTOPT_NODISCARD "nodiscard" /* Do not discard unused blocks */ +#define MNTOPT_DAX "dax" /* Enable direct access to bdev pages */ + /* * Table driven mount option parser. * @@ -363,6 +365,10 @@ xfs_parseargs( mp->m_flags |= XFS_MOUNT_DISCARD; } else if (!strcmp(this_char, MNTOPT_NODISCARD)) { mp->m_flags &= ~XFS_MOUNT_DISCARD; +#ifdef CONFIG_FS_DAX + } else if (!strcmp(this_char, MNTOPT_DAX)) { + mp->m_flags |= XFS_MOUNT_DAX; +#endif } else { xfs_warn(mp, "unknown mount option [%s].", this_char); return -EINVAL; @@ -452,8 +458,8 @@ done: } struct proc_xfs_info { - int flag; - char *str; + uint64_t flag; + char *str; }; STATIC int @@ -474,6 +480,7 @@ xfs_showargs( { XFS_MOUNT_GRPID, "," MNTOPT_GRPID }, { XFS_MOUNT_DISCARD, "," MNTOPT_DISCARD }, { XFS_MOUNT_SMALL_INUMS, "," MNTOPT_32BITINODE }, + { XFS_MOUNT_DAX, "," MNTOPT_DAX }, { 0, NULL } }; static struct proc_xfs_info xfs_info_unset[] = { @@ -1507,6 +1514,20 @@ xfs_fs_fill_super( if (XFS_SB_VERSION_NUM(&mp->m_sb) == XFS_SB_VERSION_5) sb->s_flags |= MS_I_VERSION; + if (mp->m_flags & XFS_MOUNT_DAX) { + xfs_warn(mp, + "DAX enabled. Warning: EXPERIMENTAL, use at your own risk"); + if (sb->s_blocksize != PAGE_SIZE) { + xfs_alert(mp, + "Filesystem block size invalid for DAX Turning DAX off."); + mp->m_flags &= ~XFS_MOUNT_DAX; + } else if (!sb->s_bdev->bd_disk->fops->direct_access) { + xfs_alert(mp, + "Block device does not support DAX Turning DAX off."); + mp->m_flags &= ~XFS_MOUNT_DAX; + } + } + error = xfs_mountfs(mp); if (error) goto out_filestream_unmount; diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c index 40c076523..4be27b021 100644 --- a/fs/xfs/xfs_symlink.c +++ b/fs/xfs/xfs_symlink.c @@ -178,7 +178,6 @@ xfs_symlink( struct xfs_bmap_free free_list; xfs_fsblock_t first_block; bool unlock_dp_on_error = false; - uint cancel_flags; int committed; xfs_fileoff_t first_fsb; xfs_filblks_t fs_blocks; @@ -224,7 +223,6 @@ xfs_symlink( return error; tp = xfs_trans_alloc(mp, XFS_TRANS_SYMLINK); - cancel_flags = XFS_TRANS_RELEASE_LOG_RES; /* * The symlink will fit into the inode data fork? * There can't be any attributes so we get the whole variable part. @@ -239,10 +237,8 @@ xfs_symlink( resblks = 0; error = xfs_trans_reserve(tp, &M_RES(mp)->tr_symlink, 0, 0); } - if (error) { - cancel_flags = 0; + if (error) goto out_trans_cancel; - } xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT); unlock_dp_on_error = true; @@ -394,7 +390,7 @@ xfs_symlink( if (error) goto out_bmap_cancel; - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); if (error) goto out_release_inode; @@ -407,9 +403,8 @@ xfs_symlink( out_bmap_cancel: xfs_bmap_cancel(&free_list); - cancel_flags |= XFS_TRANS_ABORT; out_trans_cancel: - xfs_trans_cancel(tp, cancel_flags); + xfs_trans_cancel(tp); out_release_inode: /* * Wait until after the current transaction is aborted to finish the @@ -464,7 +459,7 @@ xfs_inactive_symlink_rmt( tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE); error = xfs_trans_reserve(tp, &M_RES(mp)->tr_itruncate, 0, 0); if (error) { - xfs_trans_cancel(tp, 0); + xfs_trans_cancel(tp); return error; } @@ -533,7 +528,7 @@ xfs_inactive_symlink_rmt( /* * Commit the transaction containing extent freeing and EFDs. */ - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + error = xfs_trans_commit(tp); if (error) { ASSERT(XFS_FORCED_SHUTDOWN(mp)); goto error_unlock; @@ -552,7 +547,7 @@ xfs_inactive_symlink_rmt( error_bmap_cancel: xfs_bmap_cancel(&free_list); error_trans_cancel: - xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT); + xfs_trans_cancel(tp); error_unlock: xfs_iunlock(ip, XFS_ILOCK_EXCL); return error; diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 615781bf4..8d916d33d 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -738,6 +738,53 @@ TRACE_EVENT(xfs_iomap_prealloc_size, __entry->blocks, __entry->shift, __entry->writeio_blocks) ) +TRACE_EVENT(xfs_irec_merge_pre, + TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, xfs_agino_t agino, + uint16_t holemask, xfs_agino_t nagino, uint16_t nholemask), + TP_ARGS(mp, agno, agino, holemask, nagino, nholemask), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(xfs_agnumber_t, agno) + __field(xfs_agino_t, agino) + __field(uint16_t, holemask) + __field(xfs_agino_t, nagino) + __field(uint16_t, nholemask) + ), + TP_fast_assign( + __entry->dev = mp->m_super->s_dev; + __entry->agno = agno; + __entry->agino = agino; + __entry->holemask = holemask; + __entry->nagino = nagino; + __entry->nholemask = holemask; + ), + TP_printk("dev %d:%d agno %d inobt (%u:0x%x) new (%u:0x%x)", + MAJOR(__entry->dev), MINOR(__entry->dev), __entry->agno, + __entry->agino, __entry->holemask, __entry->nagino, + __entry->nholemask) +) + +TRACE_EVENT(xfs_irec_merge_post, + TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, xfs_agino_t agino, + uint16_t holemask), + TP_ARGS(mp, agno, agino, holemask), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(xfs_agnumber_t, agno) + __field(xfs_agino_t, agino) + __field(uint16_t, holemask) + ), + TP_fast_assign( + __entry->dev = mp->m_super->s_dev; + __entry->agno = agno; + __entry->agino = agino; + __entry->holemask = holemask; + ), + TP_printk("dev %d:%d agno %d inobt (%u:0x%x)", MAJOR(__entry->dev), + MINOR(__entry->dev), __entry->agno, __entry->agino, + __entry->holemask) +) + #define DEFINE_IREF_EVENT(name) \ DEFINE_EVENT(xfs_iref_class, name, \ TP_PROTO(struct xfs_inode *ip, unsigned long caller_ip), \ diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index 220ef2c90..0582a2710 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -113,7 +113,7 @@ xfs_trans_free( * blocks. Locks and log items, however, are no inherited. They must * be added to the new transaction explicitly. */ -xfs_trans_t * +STATIC xfs_trans_t * xfs_trans_dup( xfs_trans_t *tp) { @@ -251,14 +251,7 @@ xfs_trans_reserve( */ undo_log: if (resp->tr_logres > 0) { - int log_flags; - - if (resp->tr_logflags & XFS_TRANS_PERM_LOG_RES) { - log_flags = XFS_LOG_REL_PERM_RESERV; - } else { - log_flags = 0; - } - xfs_log_done(tp->t_mountp, tp->t_ticket, NULL, log_flags); + xfs_log_done(tp->t_mountp, tp->t_ticket, NULL, false); tp->t_ticket = NULL; tp->t_log_res = 0; tp->t_flags &= ~XFS_TRANS_PERM_LOG_RES; @@ -744,7 +737,7 @@ void xfs_trans_free_items( struct xfs_trans *tp, xfs_lsn_t commit_lsn, - int flags) + bool abort) { struct xfs_log_item_desc *lidp, *next; @@ -755,7 +748,7 @@ xfs_trans_free_items( if (commit_lsn != NULLCOMMITLSN) lip->li_ops->iop_committing(lip, commit_lsn); - if (flags & XFS_TRANS_ABORT) + if (abort) lip->li_flags |= XFS_LI_ABORTED; lip->li_ops->iop_unlock(lip); @@ -892,27 +885,17 @@ xfs_trans_committed_bulk( * have already been unlocked as if the commit had succeeded. * Do not reference the transaction structure after this call. */ -int -xfs_trans_commit( +static int +__xfs_trans_commit( struct xfs_trans *tp, - uint flags) + bool regrant) { struct xfs_mount *mp = tp->t_mountp; xfs_lsn_t commit_lsn = -1; int error = 0; - int log_flags = 0; int sync = tp->t_flags & XFS_TRANS_SYNC; /* - * Determine whether this commit is releasing a permanent - * log reservation or not. - */ - if (flags & XFS_TRANS_RELEASE_LOG_RES) { - ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES); - log_flags = XFS_LOG_REL_PERM_RESERV; - } - - /* * If there is nothing to be logged by the transaction, * then unlock all of the items associated with the * transaction and free the transaction structure. @@ -936,7 +919,7 @@ xfs_trans_commit( xfs_trans_apply_sb_deltas(tp); xfs_trans_apply_dquot_deltas(tp); - xfs_log_commit_cil(mp, tp, &commit_lsn, flags); + xfs_log_commit_cil(mp, tp, &commit_lsn, regrant); current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS); xfs_trans_free(tp); @@ -964,18 +947,25 @@ out_unreserve: */ xfs_trans_unreserve_and_mod_dquots(tp); if (tp->t_ticket) { - commit_lsn = xfs_log_done(mp, tp->t_ticket, NULL, log_flags); + commit_lsn = xfs_log_done(mp, tp->t_ticket, NULL, regrant); if (commit_lsn == -1 && !error) error = -EIO; } current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS); - xfs_trans_free_items(tp, NULLCOMMITLSN, error ? XFS_TRANS_ABORT : 0); + xfs_trans_free_items(tp, NULLCOMMITLSN, !!error); xfs_trans_free(tp); XFS_STATS_INC(xs_trans_empty); return error; } +int +xfs_trans_commit( + struct xfs_trans *tp) +{ + return __xfs_trans_commit(tp, false); +} + /* * Unlock all of the transaction's items and free the transaction. * The transaction must not have modified any of its items, because @@ -986,29 +976,22 @@ out_unreserve: */ void xfs_trans_cancel( - xfs_trans_t *tp, - int flags) + struct xfs_trans *tp) { - int log_flags; - xfs_mount_t *mp = tp->t_mountp; + struct xfs_mount *mp = tp->t_mountp; + bool dirty = (tp->t_flags & XFS_TRANS_DIRTY); /* - * See if the caller is being too lazy to figure out if - * the transaction really needs an abort. - */ - if ((flags & XFS_TRANS_ABORT) && !(tp->t_flags & XFS_TRANS_DIRTY)) - flags &= ~XFS_TRANS_ABORT; - /* * See if the caller is relying on us to shut down the * filesystem. This happens in paths where we detect * corruption and decide to give up. */ - if ((tp->t_flags & XFS_TRANS_DIRTY) && !XFS_FORCED_SHUTDOWN(mp)) { + if (dirty && !XFS_FORCED_SHUTDOWN(mp)) { XFS_ERROR_REPORT("xfs_trans_cancel", XFS_ERRLEVEL_LOW, mp); xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); } #ifdef DEBUG - if (!(flags & XFS_TRANS_ABORT) && !XFS_FORCED_SHUTDOWN(mp)) { + if (!dirty && !XFS_FORCED_SHUTDOWN(mp)) { struct xfs_log_item_desc *lidp; list_for_each_entry(lidp, &tp->t_items, lid_trans) @@ -1018,27 +1001,20 @@ xfs_trans_cancel( xfs_trans_unreserve_and_mod_sb(tp); xfs_trans_unreserve_and_mod_dquots(tp); - if (tp->t_ticket) { - if (flags & XFS_TRANS_RELEASE_LOG_RES) { - ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES); - log_flags = XFS_LOG_REL_PERM_RESERV; - } else { - log_flags = 0; - } - xfs_log_done(mp, tp->t_ticket, NULL, log_flags); - } + if (tp->t_ticket) + xfs_log_done(mp, tp->t_ticket, NULL, false); /* mark this thread as no longer being in a transaction */ current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS); - xfs_trans_free_items(tp, NULLCOMMITLSN, flags); + xfs_trans_free_items(tp, NULLCOMMITLSN, dirty); xfs_trans_free(tp); } /* * Roll from one trans in the sequence of PERMANENT transactions to * the next: permanent transactions are only flushed out when - * committed with XFS_TRANS_RELEASE_LOG_RES, but we still want as soon + * committed with xfs_trans_commit(), but we still want as soon * as possible to let chunks of it go to the log. So we commit the * chunk we've been working on and get a new transaction to continue. */ @@ -1055,7 +1031,8 @@ xfs_trans_roll( * Ensure that the inode is always logged. */ trans = *tpp; - xfs_trans_log_inode(trans, dp, XFS_ILOG_CORE); + if (dp) + xfs_trans_log_inode(trans, dp, XFS_ILOG_CORE); /* * Copy the critical parameters from one trans to the next. @@ -1071,20 +1048,13 @@ xfs_trans_roll( * is in progress. The caller takes the responsibility to cancel * the duplicate transaction that gets returned. */ - error = xfs_trans_commit(trans, 0); + error = __xfs_trans_commit(trans, true); if (error) return error; trans = *tpp; /* - * transaction commit worked ok so we can drop the extra ticket - * reference that we gained in xfs_trans_dup() - */ - xfs_log_ticket_put(trans->t_ticket); - - - /* * Reserve space in the log for th next transaction. * This also pushes items in the "AIL", the list of logged items, * out to disk if they are taking up space at the tail of the log @@ -1100,6 +1070,7 @@ xfs_trans_roll( if (error) return error; - xfs_trans_ijoin(trans, dp, 0); + if (dp) + xfs_trans_ijoin(trans, dp, 0); return 0; } diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h index b5bc1ab3c..3b21b4e5e 100644 --- a/fs/xfs/xfs_trans.h +++ b/fs/xfs/xfs_trans.h @@ -133,8 +133,6 @@ typedef struct xfs_trans { * XFS transaction mechanism exported interfaces that are * actually macros. */ -#define xfs_trans_get_log_res(tp) ((tp)->t_log_res) -#define xfs_trans_get_log_count(tp) ((tp)->t_log_count) #define xfs_trans_get_block_res(tp) ((tp)->t_blk_res) #define xfs_trans_set_sync(tp) ((tp)->t_flags |= XFS_TRANS_SYNC) @@ -153,7 +151,6 @@ typedef struct xfs_trans { */ xfs_trans_t *xfs_trans_alloc(struct xfs_mount *, uint); xfs_trans_t *_xfs_trans_alloc(struct xfs_mount *, uint, xfs_km_flags_t); -xfs_trans_t *xfs_trans_dup(xfs_trans_t *); int xfs_trans_reserve(struct xfs_trans *, struct xfs_trans_res *, uint, uint); void xfs_trans_mod_sb(xfs_trans_t *, uint, int64_t); @@ -228,9 +225,9 @@ void xfs_trans_log_efd_extent(xfs_trans_t *, struct xfs_efd_log_item *, xfs_fsblock_t, xfs_extlen_t); -int xfs_trans_commit(xfs_trans_t *, uint flags); +int xfs_trans_commit(struct xfs_trans *); int xfs_trans_roll(struct xfs_trans **, struct xfs_inode *); -void xfs_trans_cancel(xfs_trans_t *, int); +void xfs_trans_cancel(xfs_trans_t *); int xfs_trans_ail_init(struct xfs_mount *); void xfs_trans_ail_destroy(struct xfs_mount *); diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c index 573aefb5a..1098cf490 100644 --- a/fs/xfs/xfs_trans_ail.c +++ b/fs/xfs/xfs_trans_ail.c @@ -159,7 +159,7 @@ xfs_trans_ail_cursor_next( { struct xfs_log_item *lip = cur->item; - if ((__psint_t)lip & 1) + if ((uintptr_t)lip & 1) lip = xfs_ail_min(ailp); if (lip) cur->item = xfs_ail_next(ailp, lip); @@ -196,7 +196,7 @@ xfs_trans_ail_cursor_clear( list_for_each_entry(cur, &ailp->xa_cursors, list) { if (cur->item == lip) cur->item = (struct xfs_log_item *) - ((__psint_t)cur->item | 1); + ((uintptr_t)cur->item | 1); } } @@ -287,7 +287,7 @@ xfs_ail_splice( * find the place in the AIL where the items belong. */ lip = cur ? cur->item : NULL; - if (!lip || (__psint_t) lip & 1) + if (!lip || (uintptr_t)lip & 1) lip = __xfs_trans_ail_cursor_last(ailp, lsn); /* diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index 76a16df55..ce78534a0 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -90,8 +90,9 @@ xfs_trans_dup_dqinfo( xfs_trans_t *ntp) { xfs_dqtrx_t *oq, *nq; - int i,j; + int i, j; xfs_dqtrx_t *oqa, *nqa; + ulong blk_res_used; if (!otp->t_dqinfo) return; @@ -102,18 +103,23 @@ xfs_trans_dup_dqinfo( * Because the quota blk reservation is carried forward, * it is also necessary to carry forward the DQ_DIRTY flag. */ - if(otp->t_flags & XFS_TRANS_DQ_DIRTY) + if (otp->t_flags & XFS_TRANS_DQ_DIRTY) ntp->t_flags |= XFS_TRANS_DQ_DIRTY; for (j = 0; j < XFS_QM_TRANS_DQTYPES; j++) { oqa = otp->t_dqinfo->dqs[j]; nqa = ntp->t_dqinfo->dqs[j]; for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) { + blk_res_used = 0; + if (oqa[i].qt_dquot == NULL) break; oq = &oqa[i]; nq = &nqa[i]; + if (oq->qt_blk_res && oq->qt_bcount_delta > 0) + blk_res_used = oq->qt_bcount_delta; + nq->qt_dquot = oq->qt_dquot; nq->qt_bcount_delta = nq->qt_icount_delta = 0; nq->qt_rtbcount_delta = 0; @@ -121,8 +127,8 @@ xfs_trans_dup_dqinfo( /* * Transfer whatever is left of the reservations. */ - nq->qt_blk_res = oq->qt_blk_res - oq->qt_blk_res_used; - oq->qt_blk_res = oq->qt_blk_res_used; + nq->qt_blk_res = oq->qt_blk_res - blk_res_used; + oq->qt_blk_res = blk_res_used; nq->qt_rtblk_res = oq->qt_rtblk_res - oq->qt_rtblk_res_used; @@ -239,10 +245,6 @@ xfs_trans_mod_dquot( * disk blocks used. */ case XFS_TRANS_DQ_BCOUNT: - if (qtrx->qt_blk_res && delta > 0) { - qtrx->qt_blk_res_used += (ulong)delta; - ASSERT(qtrx->qt_blk_res >= qtrx->qt_blk_res_used); - } qtrx->qt_bcount_delta += delta; break; @@ -423,15 +425,19 @@ xfs_trans_apply_dquot_deltas( * reservation that a transaction structure knows of. */ if (qtrx->qt_blk_res != 0) { - if (qtrx->qt_blk_res != qtrx->qt_blk_res_used) { - if (qtrx->qt_blk_res > - qtrx->qt_blk_res_used) + ulong blk_res_used = 0; + + if (qtrx->qt_bcount_delta > 0) + blk_res_used = qtrx->qt_bcount_delta; + + if (qtrx->qt_blk_res != blk_res_used) { + if (qtrx->qt_blk_res > blk_res_used) dqp->q_res_bcount -= (xfs_qcnt_t) (qtrx->qt_blk_res - - qtrx->qt_blk_res_used); + blk_res_used); else dqp->q_res_bcount -= (xfs_qcnt_t) - (qtrx->qt_blk_res_used - + (blk_res_used - qtrx->qt_blk_res); } } else { diff --git a/fs/xfs/xfs_trans_priv.h b/fs/xfs/xfs_trans_priv.h index bd1281862..1b7362945 100644 --- a/fs/xfs/xfs_trans_priv.h +++ b/fs/xfs/xfs_trans_priv.h @@ -30,7 +30,7 @@ void xfs_trans_init(struct xfs_mount *); void xfs_trans_add_item(struct xfs_trans *, struct xfs_log_item *); void xfs_trans_del_item(struct xfs_log_item *); void xfs_trans_free_items(struct xfs_trans *tp, xfs_lsn_t commit_lsn, - int flags); + bool abort); void xfs_trans_unreserve_and_mod_sb(struct xfs_trans *tp); void xfs_trans_committed_bulk(struct xfs_ail *ailp, struct xfs_log_vec *lv, |