2019-04-16 17:51:39

by Al Viro

[permalink] [raw]
Subject: [RFC][PATCHSET] sorting out RCU-delayed stuff in ->destroy_inode()

We have a lot of boilerplate in ->destroy_inode()
instances, and several filesystems got the things wrong
in that area. The patchset below attempts to deal with that.

New method (void ->free_inode(inode)) is introduced,
and RCU-delayed parts of ->destroy_inode() are moved there.
The change is backwards-compatible - unmodified filesystem
will behave as it used to. Rules:
->destroy_inode ->free_inode
f g f(), rcu-delayed g()
f NULL f()
NULL g rcu-delayed g()
NULL NULL rcu-delayed free_inode_nonrcu()
IOW, NULL/NULL acts as NULL/free_inode_nonrcu.

For a lot of filesystems ->destroy_inode() used to consist
only of call_rcu(foo_i_callback, &inode->i_rcu). Those simply get
rid of ->destroy_inode() and have the callback (with saner prototype)
become their ->free_inode().

Filesystems with NULL ->destroy_inode() are simply left as-is
and so are the filesystems that don't have RCU-delayed call (pipefs,
xfs, btrfs-tests).

Filesystems that have both synchronous work and RCU-delayed
call of a callback are more interesting. In any case, the callback
can be converted to ->free_inode(). Sometimes that's all we can
reasonably do there - the rest is left in ->destroy_inode() and that's
it. However, for some of those we can do more:
* some of the synchronous stuff can just as well live in
RCU callback; such can be moved to ->free_inode().
* some of the synchronous stuff is a better fit for ->evict_inode();
e.g. the code that's undoing something done after the ->alloc_inode() or
sanity checks on the inode state.
I've done that in the obvious cases; the few non-obvious are up to
fs maintainers - they can be done as followups at any point.

The series lives in vfs.git#work.icache; patchbomb in followups. Overview:

* a couple of missed fixes for ->i_link freed to early; -stable fodder:
securityfs: fix use-after-free on symlink traversal
apparmorfs: fix use-after-free on symlink traversal
* infrastructure:
new inode method: ->free_inode()
* simple conversions (->destroy_inode() consisting only of call_rcu())
spufs: switch to ->free_inode()
erofs: switch to ->free_inode()
9p: switch to ->free_inode()
adfs: switch to ->free_inode()
affs: switch to ->free_inode()
befs: switch to ->free_inode()
bfs: switch to ->free_inode()
bdev: switch to ->free_inode()
cifs: switch to ->free_inode()
debugfs: switch to ->free_inode()
efs: switch to ->free_inode()
ext2: switch to ->free_inode()
f2fs: switch to ->free_inode()
fat: switch to ->free_inode()
freevxfs: switch to ->free_inode()
gfs2: switch to ->free_inode()
hfs: switch to ->free_inode()
hfsplus: switch to ->free_inode()
hostfs: switch to ->free_inode()
hpfs: switch to ->free_inode()
isofs: switch to ->free_inode()
jffs2: switch to ->free_inode()
minix: switch to ->free_inode()
nfs{,4}: switch to ->free_inode()
nilfs2: switch to ->free_inode()
dlmfs: switch to ->free_inode()
ocfs2: switch to ->free_inode()
openpromfs: switch to ->free_inode()
procfs: switch to ->free_inode()
qnx4: switch to ->free_inode()
qnx6: switch to ->free_inode()
reiserfs: convert to ->free_inode()
romfs: convert to ->free_inode()
squashfs: switch to ->free_inode()
ubifs: switch to ->free_inode()
udf: switch to ->free_inode()
sysv: switch to ->free_inode()
coda: switch to ->free_inode()
ufs: switch to ->free_inode()
mqueue: switch to ->free_inode()
bpf: switch to ->free_inode()
rpcpipe: switch to ->free_inode()
apparmor: switch to ->free_inode()
securityfs: switch to ->free_inode()
ntfs: switch to ->free_inode()
* cases where ->destroy_inode() contains both synchronous and delayed
parts; fuse, jfs have their ->destroy_inode() dissolved and
I'd like an ACK from their maintainers:
dax: make use of ->free_inode()
afs: switch to use of ->free_inode()
btrfs: use ->free_inode()
ceph: use ->free_inode()
ecryptfs: make use of ->free_inode()
ext4: make use of ->free_inode()
fuse: switch to ->free_inode()
jfs: switch to ->free_inode()
overlayfs: make use of ->free_inode()
hugetlb: make use of ->free_inode()
shmem: make use of ->free_inode()
orangefs: make use of ->free_inode()
* sockets: sockfs is a case where everything can be moved to ->free_inode();
we are RCU-delaying the freeing of socket_wq anyway, so we might as well
combine that with freeing the socket_alloc itself. That allows to get
rid of separate allocations for those, which simplifies the things nicely.
We obviously need an ACK from networking folks on the last pair of commits.
sockfs: switch to ->free_inode()
coallocate socket->wq with socket itself

I have *not* included an update of vfs.txt into that branch, since
there's a big patchset converting it to a different format. I have
a tentative variant of documentation on the tail-end of inode lifecycle,
but it still needs more work; I want to sort out the situation with
writeback for "don't retain inodes in icache" case first...

Diffstat:
Documentation/filesystems/Locking | 2 ++
Documentation/filesystems/porting | 17 ++++++++++
arch/powerpc/platforms/cell/spufs/inode.c | 10 ++----
drivers/dax/super.c | 7 ++--
drivers/net/tap.c | 5 ++-
drivers/net/tun.c | 8 ++---
drivers/staging/erofs/super.c | 10 ++----
fs/9p/v9fs_vfs.h | 2 +-
fs/9p/vfs_inode.c | 10 ++----
fs/9p/vfs_super.c | 4 +--
fs/adfs/super.c | 10 ++----
fs/affs/super.c | 10 ++----
fs/afs/super.c | 9 +++---
fs/aio.c | 4 +--
fs/befs/linuxvfs.c | 12 ++-----
fs/bfs/inode.c | 10 ++----
fs/block_dev.c | 14 ++------
fs/btrfs/ctree.h | 1 +
fs/btrfs/inode.c | 7 ++--
fs/btrfs/super.c | 1 +
fs/ceph/inode.c | 5 +--
fs/ceph/super.c | 1 +
fs/ceph/super.h | 1 +
fs/cifs/cifsfs.c | 12 ++-----
fs/coda/inode.c | 10 ++----
fs/debugfs/inode.c | 10 ++----
fs/ecryptfs/super.c | 5 ++-
fs/efs/super.c | 10 ++----
fs/ext2/super.c | 10 ++----
fs/ext4/super.c | 5 ++-
fs/f2fs/super.c | 10 ++----
fs/fat/inode.c | 10 ++----
fs/freevxfs/vxfs_super.c | 11 ++-----
fs/fuse/inode.c | 24 ++++++--------
fs/gfs2/super.c | 12 ++-----
fs/hfs/super.c | 10 ++----
fs/hfsplus/super.c | 13 ++------
fs/hostfs/hostfs_kern.c | 10 ++----
fs/hpfs/super.c | 10 ++----
fs/hugetlbfs/inode.c | 5 ++-
fs/inode.c | 54 ++++++++++++++++++-------------
fs/isofs/inode.c | 10 ++----
fs/jffs2/super.c | 10 ++----
fs/jfs/inode.c | 13 ++++++++
fs/jfs/super.c | 24 ++------------
fs/minix/inode.c | 10 ++----
fs/nfs/inode.c | 10 ++----
fs/nfs/internal.h | 2 +-
fs/nfs/nfs4super.c | 2 +-
fs/nfs/super.c | 2 +-
fs/nilfs2/nilfs.h | 2 --
fs/nilfs2/super.c | 11 ++-----
fs/ntfs/inode.c | 17 +++-------
fs/ntfs/inode.h | 2 +-
fs/ntfs/super.c | 2 +-
fs/ocfs2/dlmfs/dlmfs.c | 10 ++----
fs/ocfs2/super.c | 12 ++-----
fs/openpromfs/inode.c | 10 ++----
fs/orangefs/super.c | 9 ++----
fs/overlayfs/super.c | 13 ++++----
fs/proc/inode.c | 10 ++----
fs/qnx4/inode.c | 12 ++-----
fs/qnx6/inode.c | 12 ++-----
fs/reiserfs/super.c | 10 ++----
fs/romfs/super.c | 11 ++-----
fs/squashfs/super.c | 11 ++-----
fs/sysv/inode.c | 10 ++----
fs/ubifs/super.c | 10 ++----
fs/udf/super.c | 10 ++----
fs/ufs/super.c | 10 ++----
include/linux/fs.h | 1 +
include/linux/if_tap.h | 1 -
include/linux/net.h | 4 +--
include/net/sock.h | 4 +--
ipc/mqueue.c | 10 ++----
kernel/bpf/inode.c | 10 ++----
lib/iov_iter.c | 4 +++
mm/shmem.c | 5 ++-
net/core/sock.c | 2 +-
net/socket.c | 23 ++++---------
net/sunrpc/rpc_pipe.c | 11 ++-----
security/apparmor/apparmorfs.c | 7 ++--
security/inode.c | 7 ++--
83 files changed, 241 insertions(+), 516 deletions(-)


2019-04-16 17:54:36

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 08/62] affs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/affs/super.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/affs/super.c b/fs/affs/super.c
index d1ad11a8a4a5..d58217f0baaa 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -111,17 +111,11 @@ static struct inode *affs_alloc_inode(struct super_block *sb)
return &i->vfs_inode;
}

-static void affs_i_callback(struct rcu_head *head)
+static void affs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(affs_inode_cachep, AFFS_I(inode));
}

-static void affs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, affs_i_callback);
-}
-
static void init_once(void *foo)
{
struct affs_inode_info *ei = (struct affs_inode_info *) foo;
@@ -155,7 +149,7 @@ static void destroy_inodecache(void)

static const struct super_operations affs_sops = {
.alloc_inode = affs_alloc_inode,
- .destroy_inode = affs_destroy_inode,
+ .free_inode = affs_free_inode,
.write_inode = affs_write_inode,
.evict_inode = affs_evict_inode,
.put_super = affs_put_super,
--
2.11.0

2019-04-16 17:54:38

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 07/62] adfs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/adfs/super.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/adfs/super.c b/fs/adfs/super.c
index 7e099a7a4eb1..2a83655c408f 100644
--- a/fs/adfs/super.c
+++ b/fs/adfs/super.c
@@ -248,17 +248,11 @@ static struct inode *adfs_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}

-static void adfs_i_callback(struct rcu_head *head)
+static void adfs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(adfs_inode_cachep, ADFS_I(inode));
}

-static void adfs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, adfs_i_callback);
-}
-
static void init_once(void *foo)
{
struct adfs_inode_info *ei = (struct adfs_inode_info *) foo;
@@ -290,7 +284,7 @@ static void destroy_inodecache(void)

static const struct super_operations adfs_sops = {
.alloc_inode = adfs_alloc_inode,
- .destroy_inode = adfs_destroy_inode,
+ .free_inode = adfs_free_inode,
.drop_inode = generic_delete_inode,
.write_inode = adfs_write_inode,
.put_super = adfs_put_super,
--
2.11.0

2019-04-16 17:54:47

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 04/62] spufs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
arch/powerpc/platforms/cell/spufs/inode.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c
index db329d4bf1c3..c1a75216050a 100644
--- a/arch/powerpc/platforms/cell/spufs/inode.c
+++ b/arch/powerpc/platforms/cell/spufs/inode.c
@@ -71,17 +71,11 @@ spufs_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}

-static void spufs_i_callback(struct rcu_head *head)
+static void spufs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(spufs_inode_cache, SPUFS_I(inode));
}

-static void spufs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, spufs_i_callback);
-}
-
static void
spufs_init_once(void *p)
{
@@ -739,7 +733,7 @@ spufs_fill_super(struct super_block *sb, void *data, int silent)
struct spufs_sb_info *info;
static const struct super_operations s_ops = {
.alloc_inode = spufs_alloc_inode,
- .destroy_inode = spufs_destroy_inode,
+ .free_inode = spufs_free_inode,
.statfs = simple_statfs,
.evict_inode = spufs_evict_inode,
.show_options = spufs_show_options,
--
2.11.0

2019-04-16 17:54:56

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 32/62] procfs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/proc/inode.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index fc7e38def174..5f8d215b3fd0 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -72,17 +72,11 @@ static struct inode *proc_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}

-static void proc_i_callback(struct rcu_head *head)
+static void proc_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(proc_inode_cachep, PROC_I(inode));
}

-static void proc_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, proc_i_callback);
-}
-
static void init_once(void *foo)
{
struct proc_inode *ei = (struct proc_inode *) foo;
@@ -123,7 +117,7 @@ static int proc_show_options(struct seq_file *seq, struct dentry *root)

const struct super_operations proc_sops = {
.alloc_inode = proc_alloc_inode,
- .destroy_inode = proc_destroy_inode,
+ .free_inode = proc_free_inode,
.drop_inode = generic_delete_inode,
.evict_inode = proc_evict_inode,
.statfs = simple_statfs,
--
2.11.0

2019-04-16 17:55:19

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 54/62] ext4: make use of ->free_inode()

From: Al Viro <[email protected]>

the rest of this ->destroy_inode() instance could probably be folded
into ext4_evict_inode()

Signed-off-by: Al Viro <[email protected]>
---
fs/ext4/super.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 6ed4eb81e674..981f702848e7 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1107,9 +1107,8 @@ static int ext4_drop_inode(struct inode *inode)
return drop;
}

-static void ext4_i_callback(struct rcu_head *head)
+static void ext4_free_in_core_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(ext4_inode_cachep, EXT4_I(inode));
}

@@ -1124,7 +1123,6 @@ static void ext4_destroy_inode(struct inode *inode)
true);
dump_stack();
}
- call_rcu(&inode->i_rcu, ext4_i_callback);
}

static void init_once(void *foo)
@@ -1402,6 +1400,7 @@ static const struct quotactl_ops ext4_qctl_operations = {

static const struct super_operations ext4_sops = {
.alloc_inode = ext4_alloc_inode,
+ .free_inode = ext4_free_in_core_inode,
.destroy_inode = ext4_destroy_inode,
.write_inode = ext4_write_inode,
.dirty_inode = ext4_dirty_inode,
--
2.11.0

2019-04-16 17:55:25

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 52/62] ceph: use ->free_inode()

From: Al Viro <[email protected]>

a lot of non-delayed work in this case; all of that is left in
->destroy_inode()

Signed-off-by: Al Viro <[email protected]>
---
fs/ceph/inode.c | 5 +----
fs/ceph/super.c | 1 +
fs/ceph/super.h | 1 +
3 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 2d61ddda9bf5..dc0a36d0adf8 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -519,9 +519,8 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
return &ci->vfs_inode;
}

-static void ceph_i_callback(struct rcu_head *head)
+void ceph_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
struct ceph_inode_info *ci = ceph_inode(inode);

kfree(ci->i_symlink);
@@ -581,8 +580,6 @@ void ceph_destroy_inode(struct inode *inode)
ceph_buffer_put(ci->i_xattrs.prealloc_blob);

ceph_put_string(rcu_dereference_raw(ci->i_layout.pool_ns));
-
- call_rcu(&inode->i_rcu, ceph_i_callback);
}

int ceph_drop_inode(struct inode *inode)
diff --git a/fs/ceph/super.c b/fs/ceph/super.c
index 6d5bb2f74612..285edda4fc3b 100644
--- a/fs/ceph/super.c
+++ b/fs/ceph/super.c
@@ -848,6 +848,7 @@ static void ceph_umount_begin(struct super_block *sb)
static const struct super_operations ceph_super_ops = {
.alloc_inode = ceph_alloc_inode,
.destroy_inode = ceph_destroy_inode,
+ .free_inode = ceph_free_inode,
.write_inode = ceph_write_inode,
.drop_inode = ceph_drop_inode,
.sync_fs = ceph_sync_fs,
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index 16c03188578e..c5b4a05905c0 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -874,6 +874,7 @@ extern const struct inode_operations ceph_file_iops;

extern struct inode *ceph_alloc_inode(struct super_block *sb);
extern void ceph_destroy_inode(struct inode *inode);
+extern void ceph_free_inode(struct inode *inode);
extern int ceph_drop_inode(struct inode *inode);

extern struct inode *ceph_get_inode(struct super_block *sb,
--
2.11.0

2019-04-16 17:55:29

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 53/62] ecryptfs: make use of ->free_inode()

From: Al Viro <[email protected]>

no idea if crypto destruction could be moved there as well

Signed-off-by: Al Viro <[email protected]>
---
fs/ecryptfs/super.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/fs/ecryptfs/super.c b/fs/ecryptfs/super.c
index 85411ceb0508..c3e511f2b6c0 100644
--- a/fs/ecryptfs/super.c
+++ b/fs/ecryptfs/super.c
@@ -67,9 +67,8 @@ static struct inode *ecryptfs_alloc_inode(struct super_block *sb)
return inode;
}

-static void ecryptfs_i_callback(struct rcu_head *head)
+static void ecryptfs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
struct ecryptfs_inode_info *inode_info;
inode_info = ecryptfs_inode_to_private(inode);

@@ -92,7 +91,6 @@ static void ecryptfs_destroy_inode(struct inode *inode)
inode_info = ecryptfs_inode_to_private(inode);
BUG_ON(inode_info->lower_file);
ecryptfs_destroy_crypt_stat(&inode_info->crypt_stat);
- call_rcu(&inode->i_rcu, ecryptfs_i_callback);
}

/**
@@ -186,6 +184,7 @@ static int ecryptfs_show_options(struct seq_file *m, struct dentry *root)
const struct super_operations ecryptfs_sops = {
.alloc_inode = ecryptfs_alloc_inode,
.destroy_inode = ecryptfs_destroy_inode,
+ .free_inode = ecryptfs_free_inode,
.statfs = ecryptfs_statfs,
.remount_fs = NULL,
.evict_inode = ecryptfs_evict_inode,
--
2.11.0

2019-04-16 17:55:33

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 43/62] mqueue: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
ipc/mqueue.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index aea30530c472..ba44164ea1f9 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -419,17 +419,11 @@ static struct inode *mqueue_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}

-static void mqueue_i_callback(struct rcu_head *head)
+static void mqueue_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(mqueue_inode_cachep, MQUEUE_I(inode));
}

-static void mqueue_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, mqueue_i_callback);
-}
-
static void mqueue_evict_inode(struct inode *inode)
{
struct mqueue_inode_info *info;
@@ -1562,7 +1556,7 @@ static const struct file_operations mqueue_file_operations = {

static const struct super_operations mqueue_super_ops = {
.alloc_inode = mqueue_alloc_inode,
- .destroy_inode = mqueue_destroy_inode,
+ .free_inode = mqueue_free_inode,
.evict_inode = mqueue_evict_inode,
.statfs = simple_statfs,
};
--
2.11.0

2019-04-16 17:55:36

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 44/62] bpf: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
kernel/bpf/inode.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c
index 4a8f390a2b82..bc53e5b20ddc 100644
--- a/kernel/bpf/inode.c
+++ b/kernel/bpf/inode.c
@@ -566,9 +566,8 @@ static int bpf_show_options(struct seq_file *m, struct dentry *root)
return 0;
}

-static void bpf_destroy_inode_deferred(struct rcu_head *head)
+static void bpf_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
enum bpf_type type;

if (S_ISLNK(inode->i_mode))
@@ -578,16 +577,11 @@ static void bpf_destroy_inode_deferred(struct rcu_head *head)
free_inode_nonrcu(inode);
}

-static void bpf_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, bpf_destroy_inode_deferred);
-}
-
static const struct super_operations bpf_super_ops = {
.statfs = simple_statfs,
.drop_inode = generic_delete_inode,
.show_options = bpf_show_options,
- .destroy_inode = bpf_destroy_inode,
+ .free_inode = bpf_free_inode,
};

enum {
--
2.11.0

2019-04-16 17:55:41

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 39/62] udf: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/udf/super.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/udf/super.c b/fs/udf/super.c
index ffd8038ff728..f64691f2168a 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -161,17 +161,11 @@ static struct inode *udf_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}

-static void udf_i_callback(struct rcu_head *head)
+static void udf_free_in_core_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(udf_inode_cachep, UDF_I(inode));
}

-static void udf_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, udf_i_callback);
-}
-
static void init_once(void *foo)
{
struct udf_inode_info *ei = (struct udf_inode_info *)foo;
@@ -206,7 +200,7 @@ static void destroy_inodecache(void)
/* Superblock operations */
static const struct super_operations udf_sb_ops = {
.alloc_inode = udf_alloc_inode,
- .destroy_inode = udf_destroy_inode,
+ .free_inode = udf_free_in_core_inode,
.write_inode = udf_write_inode,
.evict_inode = udf_evict_inode,
.put_super = udf_put_super,
--
2.11.0

2019-04-16 17:55:45

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 31/62] openpromfs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/openpromfs/inode.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c
index 1b2d0d2fe2ee..46655e454c55 100644
--- a/fs/openpromfs/inode.c
+++ b/fs/openpromfs/inode.c
@@ -336,17 +336,11 @@ static struct inode *openprom_alloc_inode(struct super_block *sb)
return &oi->vfs_inode;
}

-static void openprom_i_callback(struct rcu_head *head)
+static void openprom_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(op_inode_cachep, OP_I(inode));
}

-static void openprom_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, openprom_i_callback);
-}
-
static struct inode *openprom_iget(struct super_block *sb, ino_t ino)
{
struct inode *inode;
@@ -375,7 +369,7 @@ static int openprom_remount(struct super_block *sb, int *flags, char *data)

static const struct super_operations openprom_sops = {
.alloc_inode = openprom_alloc_inode,
- .destroy_inode = openprom_destroy_inode,
+ .free_inode = openprom_free_inode,
.statfs = simple_statfs,
.remount_fs = openprom_remount,
};
--
2.11.0

2019-04-16 17:55:58

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 21/62] hfsplus: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/hfsplus/super.c | 13 +++----------
1 file changed, 3 insertions(+), 10 deletions(-)

diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index eb4535eba95d..0cc5feff76cd 100644
--- a/fs/hfsplus/super.c
+++ b/fs/hfsplus/super.c
@@ -18,7 +18,7 @@
#include <linux/nls.h>

static struct inode *hfsplus_alloc_inode(struct super_block *sb);
-static void hfsplus_destroy_inode(struct inode *inode);
+static void hfsplus_free_inode(struct inode *inode);

#include "hfsplus_fs.h"
#include "xattr.h"
@@ -361,7 +361,7 @@ static int hfsplus_remount(struct super_block *sb, int *flags, char *data)

static const struct super_operations hfsplus_sops = {
.alloc_inode = hfsplus_alloc_inode,
- .destroy_inode = hfsplus_destroy_inode,
+ .free_inode = hfsplus_free_inode,
.write_inode = hfsplus_write_inode,
.evict_inode = hfsplus_evict_inode,
.put_super = hfsplus_put_super,
@@ -628,18 +628,11 @@ static struct inode *hfsplus_alloc_inode(struct super_block *sb)
return i ? &i->vfs_inode : NULL;
}

-static void hfsplus_i_callback(struct rcu_head *head)
+static void hfsplus_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
-
kmem_cache_free(hfsplus_inode_cachep, HFSPLUS_I(inode));
}

-static void hfsplus_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, hfsplus_i_callback);
-}
-
#define HFSPLUS_INODE_SIZE sizeof(struct hfsplus_inode_info)

static struct dentry *hfsplus_mount(struct file_system_type *fs_type,
--
2.11.0

2019-04-16 17:56:12

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 58/62] hugetlb: make use of ->free_inode()

From: Al Viro <[email protected]>

moving synchronous parts of ->destroy_inode() to ->evict_inode() is
not possible here - they are balancing the stuff done in ->alloc_inode(),
not the things acquired while using it or sanity checks.

Signed-off-by: Al Viro <[email protected]>
---
fs/hugetlbfs/inode.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 9285dd4f4b1c..c74ef4426282 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -1051,9 +1051,8 @@ static struct inode *hugetlbfs_alloc_inode(struct super_block *sb)
return &p->vfs_inode;
}

-static void hugetlbfs_i_callback(struct rcu_head *head)
+static void hugetlbfs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(hugetlbfs_inode_cachep, HUGETLBFS_I(inode));
}

@@ -1061,7 +1060,6 @@ static void hugetlbfs_destroy_inode(struct inode *inode)
{
hugetlbfs_inc_free_inodes(HUGETLBFS_SB(inode->i_sb));
mpol_free_shared_policy(&HUGETLBFS_I(inode)->policy);
- call_rcu(&inode->i_rcu, hugetlbfs_i_callback);
}

static const struct address_space_operations hugetlbfs_aops = {
@@ -1108,6 +1106,7 @@ static const struct inode_operations hugetlbfs_inode_operations = {

static const struct super_operations hugetlbfs_ops = {
.alloc_inode = hugetlbfs_alloc_inode,
+ .free_inode = hugetlbfs_free_inode,
.destroy_inode = hugetlbfs_destroy_inode,
.evict_inode = hugetlbfs_evict_inode,
.statfs = hugetlbfs_statfs,
--
2.11.0

2019-04-16 17:56:18

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 56/62] jfs: switch to ->free_inode()

From: Al Viro <[email protected]>

synchronous part can be moved to ->evict_inode(), the rest -
->free_inode() fodder

Signed-off-by: Al Viro <[email protected]>
---
fs/jfs/inode.c | 13 +++++++++++++
fs/jfs/super.c | 24 +++---------------------
2 files changed, 16 insertions(+), 21 deletions(-)

diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c
index 805ae9e8944a..f2b92b292abe 100644
--- a/fs/jfs/inode.c
+++ b/fs/jfs/inode.c
@@ -31,6 +31,7 @@
#include "jfs_extent.h"
#include "jfs_unicode.h"
#include "jfs_debug.h"
+#include "jfs_dmap.h"


struct inode *jfs_iget(struct super_block *sb, unsigned long ino)
@@ -150,6 +151,8 @@ int jfs_write_inode(struct inode *inode, struct writeback_control *wbc)

void jfs_evict_inode(struct inode *inode)
{
+ struct jfs_inode_info *ji = JFS_IP(inode);
+
jfs_info("In jfs_evict_inode, inode = 0x%p", inode);

if (!inode->i_nlink && !is_bad_inode(inode)) {
@@ -173,6 +176,16 @@ void jfs_evict_inode(struct inode *inode)
}
clear_inode(inode);
dquot_drop(inode);
+
+ BUG_ON(!list_empty(&ji->anon_inode_list));
+
+ spin_lock_irq(&ji->ag_lock);
+ if (ji->active_ag != -1) {
+ struct bmap *bmap = JFS_SBI(inode->i_sb)->bmap;
+ atomic_dec(&bmap->db_active[ji->active_ag]);
+ ji->active_ag = -1;
+ }
+ spin_unlock_irq(&ji->ag_lock);
}

void jfs_dirty_inode(struct inode *inode, int flags)
diff --git a/fs/jfs/super.c b/fs/jfs/super.c
index 65d8fc87ab11..9454831bbd71 100644
--- a/fs/jfs/super.c
+++ b/fs/jfs/super.c
@@ -124,27 +124,9 @@ static struct inode *jfs_alloc_inode(struct super_block *sb)
return &jfs_inode->vfs_inode;
}

-static void jfs_i_callback(struct rcu_head *head)
+static void jfs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
- struct jfs_inode_info *ji = JFS_IP(inode);
- kmem_cache_free(jfs_inode_cachep, ji);
-}
-
-static void jfs_destroy_inode(struct inode *inode)
-{
- struct jfs_inode_info *ji = JFS_IP(inode);
-
- BUG_ON(!list_empty(&ji->anon_inode_list));
-
- spin_lock_irq(&ji->ag_lock);
- if (ji->active_ag != -1) {
- struct bmap *bmap = JFS_SBI(inode->i_sb)->bmap;
- atomic_dec(&bmap->db_active[ji->active_ag]);
- ji->active_ag = -1;
- }
- spin_unlock_irq(&ji->ag_lock);
- call_rcu(&inode->i_rcu, jfs_i_callback);
+ kmem_cache_free(jfs_inode_cachep, JFS_IP(inode));
}

static int jfs_statfs(struct dentry *dentry, struct kstatfs *buf)
@@ -912,7 +894,7 @@ static int jfs_quota_off(struct super_block *sb, int type)

static const struct super_operations jfs_super_operations = {
.alloc_inode = jfs_alloc_inode,
- .destroy_inode = jfs_destroy_inode,
+ .free_inode = jfs_free_inode,
.dirty_inode = jfs_dirty_inode,
.write_inode = jfs_write_inode,
.evict_inode = jfs_evict_inode,
--
2.11.0

2019-04-16 17:56:21

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 55/62] fuse: switch to ->free_inode()

From: Al Viro <[email protected]>

fuse_destroy_inode() is gone - sanity checks that need the stack
trace of the caller get moved into ->evict_inode(), the rest joins
the RCU-delayed part which becomes ->free_inode().

While we are at it, don't just pass the address of what happens
to be the first member of structure to kmem_cache_free() -
get_fuse_inode() is there for purpose and it gives the proper
container_of() use. No behaviour change, but verifying correctness
is easier that way.

Signed-off-by: Al Viro <[email protected]>
---
fs/fuse/inode.c | 24 ++++++++++--------------
1 file changed, 10 insertions(+), 14 deletions(-)

diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index ec5d9953dfb6..f485d09d14df 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -107,34 +107,30 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
return inode;
}

-static void fuse_i_callback(struct rcu_head *head)
-{
- struct inode *inode = container_of(head, struct inode, i_rcu);
- kmem_cache_free(fuse_inode_cachep, inode);
-}
-
-static void fuse_destroy_inode(struct inode *inode)
+static void fuse_free_inode(struct inode *inode)
{
struct fuse_inode *fi = get_fuse_inode(inode);
- if (S_ISREG(inode->i_mode) && !is_bad_inode(inode)) {
- WARN_ON(!list_empty(&fi->write_files));
- WARN_ON(!list_empty(&fi->queued_writes));
- }
+
mutex_destroy(&fi->mutex);
kfree(fi->forget);
- call_rcu(&inode->i_rcu, fuse_i_callback);
+ kmem_cache_free(fuse_inode_cachep, fi);
}

static void fuse_evict_inode(struct inode *inode)
{
+ struct fuse_inode *fi = get_fuse_inode(inode);
+
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
if (inode->i_sb->s_flags & SB_ACTIVE) {
struct fuse_conn *fc = get_fuse_conn(inode);
- struct fuse_inode *fi = get_fuse_inode(inode);
fuse_queue_forget(fc, fi->forget, fi->nodeid, fi->nlookup);
fi->forget = NULL;
}
+ if (S_ISREG(inode->i_mode) && !is_bad_inode(inode)) {
+ WARN_ON(!list_empty(&fi->write_files));
+ WARN_ON(!list_empty(&fi->queued_writes));
+ }
}

static int fuse_remount_fs(struct super_block *sb, int *flags, char *data)
@@ -814,7 +810,7 @@ static const struct export_operations fuse_export_operations = {

static const struct super_operations fuse_super_operations = {
.alloc_inode = fuse_alloc_inode,
- .destroy_inode = fuse_destroy_inode,
+ .free_inode = fuse_free_inode,
.evict_inode = fuse_evict_inode,
.write_inode = fuse_write_inode,
.drop_inode = generic_delete_inode,
--
2.11.0

2019-04-16 17:56:49

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 06/62] 9p: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/9p/v9fs_vfs.h | 2 +-
fs/9p/vfs_inode.c | 10 ++--------
fs/9p/vfs_super.c | 4 ++--
3 files changed, 5 insertions(+), 11 deletions(-)

diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h
index aaee1e6584e6..60cd4ba04afc 100644
--- a/fs/9p/v9fs_vfs.h
+++ b/fs/9p/v9fs_vfs.h
@@ -58,7 +58,7 @@ extern const struct file_operations v9fs_mmap_file_operations_dotl;
extern struct kmem_cache *v9fs_inode_cache;

struct inode *v9fs_alloc_inode(struct super_block *sb);
-void v9fs_destroy_inode(struct inode *inode);
+void v9fs_free_inode(struct inode *inode);
struct inode *v9fs_get_inode(struct super_block *sb, umode_t mode, dev_t);
int v9fs_init_inode(struct v9fs_session_info *v9ses,
struct inode *inode, umode_t mode, dev_t);
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index 72b779bc0942..24050e866e64 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -253,21 +253,15 @@ struct inode *v9fs_alloc_inode(struct super_block *sb)
}

/**
- * v9fs_destroy_inode - destroy an inode
+ * v9fs_free_inode - destroy an inode
*
*/

-static void v9fs_i_callback(struct rcu_head *head)
+void v9fs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(v9fs_inode_cache, V9FS_I(inode));
}

-void v9fs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, v9fs_i_callback);
-}
-
int v9fs_init_inode(struct v9fs_session_info *v9ses,
struct inode *inode, umode_t mode, dev_t rdev)
{
diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c
index d13d35cf69c0..67d1b965adcd 100644
--- a/fs/9p/vfs_super.c
+++ b/fs/9p/vfs_super.c
@@ -344,7 +344,7 @@ static int v9fs_write_inode_dotl(struct inode *inode,

static const struct super_operations v9fs_super_ops = {
.alloc_inode = v9fs_alloc_inode,
- .destroy_inode = v9fs_destroy_inode,
+ .free_inode = v9fs_free_inode,
.statfs = simple_statfs,
.evict_inode = v9fs_evict_inode,
.show_options = v9fs_show_options,
@@ -354,7 +354,7 @@ static const struct super_operations v9fs_super_ops = {

static const struct super_operations v9fs_super_ops_dotl = {
.alloc_inode = v9fs_alloc_inode,
- .destroy_inode = v9fs_destroy_inode,
+ .free_inode = v9fs_free_inode,
.statfs = v9fs_statfs,
.drop_inode = v9fs_drop_inode,
.evict_inode = v9fs_evict_inode,
--
2.11.0

2019-04-16 17:56:49

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 60/62] orangefs: make use of ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/orangefs/super.c | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/fs/orangefs/super.c b/fs/orangefs/super.c
index dfaee90d30bd..3784f7e8b603 100644
--- a/fs/orangefs/super.c
+++ b/fs/orangefs/super.c
@@ -124,11 +124,9 @@ static struct inode *orangefs_alloc_inode(struct super_block *sb)
return &orangefs_inode->vfs_inode;
}

-static void orangefs_i_callback(struct rcu_head *head)
+static void orangefs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
- struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
- kmem_cache_free(orangefs_inode_cache, orangefs_inode);
+ kmem_cache_free(orangefs_inode_cache, ORANGEFS_I(inode));
}

static void orangefs_destroy_inode(struct inode *inode)
@@ -138,8 +136,6 @@ static void orangefs_destroy_inode(struct inode *inode)
gossip_debug(GOSSIP_SUPER_DEBUG,
"%s: deallocated %p destroying inode %pU\n",
__func__, orangefs_inode, get_khandle_from_ino(inode));
-
- call_rcu(&inode->i_rcu, orangefs_i_callback);
}

/*
@@ -299,6 +295,7 @@ void fsid_key_table_finalize(void)

static const struct super_operations orangefs_s_ops = {
.alloc_inode = orangefs_alloc_inode,
+ .free_inode = orangefs_free_inode,
.destroy_inode = orangefs_destroy_inode,
.drop_inode = generic_delete_inode,
.statfs = orangefs_statfs,
--
2.11.0

2019-04-16 17:56:54

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 57/62] overlayfs: make use of ->free_inode()

From: Al Viro <[email protected]>

synchronous parts are left in ->destroy_inode()

Signed-off-by: Al Viro <[email protected]>
---
fs/overlayfs/super.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 0116735cc321..5ec4fc2f5d7e 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -190,11 +190,13 @@ static struct inode *ovl_alloc_inode(struct super_block *sb)
return &oi->vfs_inode;
}

-static void ovl_i_callback(struct rcu_head *head)
+static void ovl_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
+ struct ovl_inode *oi = OVL_I(inode);

- kmem_cache_free(ovl_inode_cachep, OVL_I(inode));
+ kfree(oi->redirect);
+ mutex_destroy(&oi->lock);
+ kmem_cache_free(ovl_inode_cachep, oi);
}

static void ovl_destroy_inode(struct inode *inode)
@@ -207,10 +209,6 @@ static void ovl_destroy_inode(struct inode *inode)
ovl_dir_cache_free(inode);
else
iput(oi->lowerdata);
- kfree(oi->redirect);
- mutex_destroy(&oi->lock);
-
- call_rcu(&inode->i_rcu, ovl_i_callback);
}

static void ovl_free_fs(struct ovl_fs *ofs)
@@ -377,6 +375,7 @@ static int ovl_remount(struct super_block *sb, int *flags, char *data)

static const struct super_operations ovl_super_operations = {
.alloc_inode = ovl_alloc_inode,
+ .free_inode = ovl_free_inode,
.destroy_inode = ovl_destroy_inode,
.drop_inode = generic_delete_inode,
.put_super = ovl_put_super,
--
2.11.0

2019-04-16 17:57:09

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 20/62] hfs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/hfs/super.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/hfs/super.c b/fs/hfs/super.c
index 173876782f73..c33324686d89 100644
--- a/fs/hfs/super.c
+++ b/fs/hfs/super.c
@@ -167,20 +167,14 @@ static struct inode *hfs_alloc_inode(struct super_block *sb)
return i ? &i->vfs_inode : NULL;
}

-static void hfs_i_callback(struct rcu_head *head)
+static void hfs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(hfs_inode_cachep, HFS_I(inode));
}

-static void hfs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, hfs_i_callback);
-}
-
static const struct super_operations hfs_super_operations = {
.alloc_inode = hfs_alloc_inode,
- .destroy_inode = hfs_destroy_inode,
+ .free_inode = hfs_free_inode,
.write_inode = hfs_write_inode,
.evict_inode = hfs_evict_inode,
.put_super = hfs_put_super,
--
2.11.0

2019-04-16 17:57:10

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 48/62] ntfs: switch to ->free_inode()

From: Al Viro <[email protected]>

move the synchronous stuff from ->destroy_inode() to ->evict_inode(),
turn the RCU-delayed part into ->free_inode()

Signed-off-by: Al Viro <[email protected]>
---
fs/ntfs/inode.c | 17 ++++-------------
fs/ntfs/inode.h | 2 +-
fs/ntfs/super.c | 2 +-
3 files changed, 6 insertions(+), 15 deletions(-)

diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index bd3221cbdd95..fb1a2b49a5da 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -332,23 +332,11 @@ struct inode *ntfs_alloc_big_inode(struct super_block *sb)
return NULL;
}

-static void ntfs_i_callback(struct rcu_head *head)
+void ntfs_free_big_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(ntfs_big_inode_cache, NTFS_I(inode));
}

-void ntfs_destroy_big_inode(struct inode *inode)
-{
- ntfs_inode *ni = NTFS_I(inode);
-
- ntfs_debug("Entering.");
- BUG_ON(ni->page);
- if (!atomic_dec_and_test(&ni->count))
- BUG();
- call_rcu(&inode->i_rcu, ntfs_i_callback);
-}
-
static inline ntfs_inode *ntfs_alloc_extent_inode(void)
{
ntfs_inode *ni;
@@ -2287,6 +2275,9 @@ void ntfs_evict_big_inode(struct inode *vi)
ni->ext.base_ntfs_ino = NULL;
}
}
+ BUG_ON(ni->page);
+ if (!atomic_dec_and_test(&ni->count))
+ BUG();
return;
}

diff --git a/fs/ntfs/inode.h b/fs/ntfs/inode.h
index b3c3469de6cb..58c8fd2948d3 100644
--- a/fs/ntfs/inode.h
+++ b/fs/ntfs/inode.h
@@ -278,7 +278,7 @@ extern struct inode *ntfs_index_iget(struct inode *base_vi, ntfschar *name,
u32 name_len);

extern struct inode *ntfs_alloc_big_inode(struct super_block *sb);
-extern void ntfs_destroy_big_inode(struct inode *inode);
+extern void ntfs_free_big_inode(struct inode *inode);
extern void ntfs_evict_big_inode(struct inode *vi);

extern void __ntfs_init_inode(struct super_block *sb, ntfs_inode *ni);
diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index bb7159f697f2..887ea8b3b000 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -2676,7 +2676,7 @@ static int ntfs_write_inode(struct inode *vi, struct writeback_control *wbc)
*/
static const struct super_operations ntfs_sops = {
.alloc_inode = ntfs_alloc_big_inode, /* VFS: Allocate new inode. */
- .destroy_inode = ntfs_destroy_big_inode, /* VFS: Deallocate inode. */
+ .free_inode = ntfs_free_big_inode, /* VFS: Deallocate inode. */
#ifdef NTFS_RW
.write_inode = ntfs_write_inode, /* VFS: Write dirty inode to
disk. */
--
2.11.0

2019-04-16 17:57:16

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 59/62] shmem: make use of ->free_inode()

From: Al Viro <[email protected]>

same situation as for hugetlbfs

Signed-off-by: Al Viro <[email protected]>
---
mm/shmem.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/mm/shmem.c b/mm/shmem.c
index b3db3779a30a..dbb7a6dadba7 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -3635,9 +3635,8 @@ static struct inode *shmem_alloc_inode(struct super_block *sb)
return &info->vfs_inode;
}

-static void shmem_destroy_callback(struct rcu_head *head)
+static void shmem_free_in_core_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
if (S_ISLNK(inode->i_mode))
kfree(inode->i_link);
kmem_cache_free(shmem_inode_cachep, SHMEM_I(inode));
@@ -3647,7 +3646,6 @@ static void shmem_destroy_inode(struct inode *inode)
{
if (S_ISREG(inode->i_mode))
mpol_free_shared_policy(&SHMEM_I(inode)->policy);
- call_rcu(&inode->i_rcu, shmem_destroy_callback);
}

static void shmem_init_inode(void *foo)
@@ -3738,6 +3736,7 @@ static const struct inode_operations shmem_special_inode_operations = {

static const struct super_operations shmem_ops = {
.alloc_inode = shmem_alloc_inode,
+ .free_inode = shmem_free_in_core_inode,
.destroy_inode = shmem_destroy_inode,
#ifdef CONFIG_TMPFS
.statfs = shmem_statfs,
--
2.11.0

2019-04-16 17:57:17

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 62/62] coallocate socket->wq with socket itself

From: Al Viro <[email protected]>

socket->wq is assign-once, set when we are initializing both
struct socket it's in and struct socket_wq it points to. As the
matter of fact, the only reason for separate allocation was the
ability to RCU-delay freeing of socket_wq. RCU-delaying the
freeing of socket itself gets rid of that need, so we can just
fold struct socket_wq into the end of struct socket and simplify
the life both for sock_alloc_inode() (one allocation instead of
two) and for tun/tap oddballs, where we used to embed struct socket
and struct socket_wq into the same structure (now - embedding just
the struct socket).

Note that reference to struct socket_wq in struct sock does remain
a reference - that's unchanged.

Signed-off-by: Al Viro <[email protected]>
---
drivers/net/tap.c | 5 ++---
drivers/net/tun.c | 8 +++-----
include/linux/if_tap.h | 1 -
include/linux/net.h | 4 ++--
include/net/sock.h | 4 ++--
net/core/sock.c | 2 +-
net/socket.c | 19 +++++--------------
7 files changed, 15 insertions(+), 28 deletions(-)

diff --git a/drivers/net/tap.c b/drivers/net/tap.c
index 2ea9b4976f4a..249bfd85b65c 100644
--- a/drivers/net/tap.c
+++ b/drivers/net/tap.c
@@ -519,8 +519,7 @@ static int tap_open(struct inode *inode, struct file *file)
goto err;
}

- RCU_INIT_POINTER(q->sock.wq, &q->wq);
- init_waitqueue_head(&q->wq.wait);
+ init_waitqueue_head(&q->sock.wq.wait);
q->sock.type = SOCK_RAW;
q->sock.state = SS_CONNECTED;
q->sock.file = file;
@@ -578,7 +577,7 @@ static __poll_t tap_poll(struct file *file, poll_table *wait)
goto out;

mask = 0;
- poll_wait(file, &q->wq.wait, wait);
+ poll_wait(file, &q->sock.wq.wait, wait);

if (!ptr_ring_empty(&q->ring))
mask |= EPOLLIN | EPOLLRDNORM;
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index e9ca1c088d0b..f404d1588e9c 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -169,7 +169,6 @@ struct tun_pcpu_stats {
struct tun_file {
struct sock sk;
struct socket socket;
- struct socket_wq wq;
struct tun_struct __rcu *tun;
struct fasync_struct *fasync;
/* only used for fasnyc */
@@ -2174,7 +2173,7 @@ static void *tun_ring_recv(struct tun_file *tfile, int noblock, int *err)
goto out;
}

- add_wait_queue(&tfile->wq.wait, &wait);
+ add_wait_queue(&tfile->socket.wq.wait, &wait);

while (1) {
set_current_state(TASK_INTERRUPTIBLE);
@@ -2194,7 +2193,7 @@ static void *tun_ring_recv(struct tun_file *tfile, int noblock, int *err)
}

__set_current_state(TASK_RUNNING);
- remove_wait_queue(&tfile->wq.wait, &wait);
+ remove_wait_queue(&tfile->socket.wq.wait, &wait);

out:
*err = error;
@@ -3417,8 +3416,7 @@ static int tun_chr_open(struct inode *inode, struct file * file)
tfile->flags = 0;
tfile->ifindex = 0;

- init_waitqueue_head(&tfile->wq.wait);
- RCU_INIT_POINTER(tfile->socket.wq, &tfile->wq);
+ init_waitqueue_head(&tfile->socket.wq.wait);

tfile->socket.file = file;
tfile->socket.ops = &tun_socket_ops;
diff --git a/include/linux/if_tap.h b/include/linux/if_tap.h
index 8e66866c11be..915a187cfabd 100644
--- a/include/linux/if_tap.h
+++ b/include/linux/if_tap.h
@@ -62,7 +62,6 @@ struct tap_dev {
struct tap_queue {
struct sock sk;
struct socket sock;
- struct socket_wq wq;
int vnet_hdr_sz;
struct tap_dev __rcu *tap;
struct file *file;
diff --git a/include/linux/net.h b/include/linux/net.h
index c606c72311d0..6979057c7c86 100644
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -120,11 +120,11 @@ struct socket {

unsigned long flags;

- struct socket_wq *wq;
-
struct file *file;
struct sock *sk;
const struct proto_ops *ops;
+
+ struct socket_wq wq;
};

struct vm_area_struct;
diff --git a/include/net/sock.h b/include/net/sock.h
index 8de5ee258b93..0e1975b6202f 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -1811,7 +1811,7 @@ static inline void sock_graft(struct sock *sk, struct socket *parent)
{
WARN_ON(parent->sk);
write_lock_bh(&sk->sk_callback_lock);
- rcu_assign_pointer(sk->sk_wq, parent->wq);
+ rcu_assign_pointer(sk->sk_wq, &parent->wq);
parent->sk = sk;
sk_set_socket(sk, parent);
sk->sk_uid = SOCK_INODE(parent)->i_uid;
@@ -2095,7 +2095,7 @@ static inline void sock_poll_wait(struct file *filp, struct socket *sock,
poll_table *p)
{
if (!poll_does_not_wait(p)) {
- poll_wait(filp, &sock->wq->wait, p);
+ poll_wait(filp, &sock->wq.wait, p);
/* We need to be sure we are in sync with the
* socket flags modification.
*
diff --git a/net/core/sock.c b/net/core/sock.c
index 782343bb925b..11af1ee7d542 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -2842,7 +2842,7 @@ void sock_init_data(struct socket *sock, struct sock *sk)

if (sock) {
sk->sk_type = sock->type;
- RCU_INIT_POINTER(sk->sk_wq, sock->wq);
+ RCU_INIT_POINTER(sk->sk_wq, &sock->wq);
sock->sk = sk;
sk->sk_uid = SOCK_INODE(sock)->i_uid;
} else {
diff --git a/net/socket.c b/net/socket.c
index 6953a049fb82..7d3d043fc56f 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -239,20 +239,13 @@ static struct kmem_cache *sock_inode_cachep __ro_after_init;
static struct inode *sock_alloc_inode(struct super_block *sb)
{
struct socket_alloc *ei;
- struct socket_wq *wq;

ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL);
if (!ei)
return NULL;
- wq = kmalloc(sizeof(*wq), GFP_KERNEL);
- if (!wq) {
- kmem_cache_free(sock_inode_cachep, ei);
- return NULL;
- }
- init_waitqueue_head(&wq->wait);
- wq->fasync_list = NULL;
- wq->flags = 0;
- ei->socket.wq = wq;
+ init_waitqueue_head(&ei->socket.wq.wait);
+ ei->socket.wq.fasync_list = NULL;
+ ei->socket.wq.flags = 0;

ei->socket.state = SS_UNCONNECTED;
ei->socket.flags = 0;
@@ -268,7 +261,6 @@ static void sock_free_inode(struct inode *inode)
struct socket_alloc *ei;

ei = container_of(inode, struct socket_alloc, vfs_inode);
- kfree(ei->socket.wq);
kmem_cache_free(sock_inode_cachep, ei);
}

@@ -604,7 +596,7 @@ static void __sock_release(struct socket *sock, struct inode *inode)
module_put(owner);
}

- if (sock->wq->fasync_list)
+ if (sock->wq.fasync_list)
pr_err("%s: fasync list not empty!\n", __func__);

if (!sock->file) {
@@ -1263,13 +1255,12 @@ static int sock_fasync(int fd, struct file *filp, int on)
{
struct socket *sock = filp->private_data;
struct sock *sk = sock->sk;
- struct socket_wq *wq;
+ struct socket_wq *wq = &sock->wq;

if (sk == NULL)
return -EINVAL;

lock_sock(sk);
- wq = sock->wq;
fasync_helper(fd, filp, on, &wq->fasync_list);

if (!wq->fasync_list)
--
2.11.0

2019-04-16 17:57:19

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 51/62] btrfs: use ->free_inode()

From: Al Viro <[email protected]>

a lot of stuff remains in ->destroy_inode()

Signed-off-by: Al Viro <[email protected]>
---
fs/btrfs/ctree.h | 1 +
fs/btrfs/inode.c | 7 ++-----
fs/btrfs/super.c | 1 +
3 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index b3642367a595..5260a9263d73 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -3267,6 +3267,7 @@ void btrfs_evict_inode(struct inode *inode);
int btrfs_write_inode(struct inode *inode, struct writeback_control *wbc);
struct inode *btrfs_alloc_inode(struct super_block *sb);
void btrfs_destroy_inode(struct inode *inode);
+void btrfs_free_inode(struct inode *inode);
int btrfs_drop_inode(struct inode *inode);
int __init btrfs_init_cachep(void);
void __cold btrfs_destroy_cachep(void);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 82fdda8ff5ab..aeb31c2dc14e 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -9206,9 +9206,8 @@ void btrfs_test_destroy_inode(struct inode *inode)
}
#endif

-static void btrfs_i_callback(struct rcu_head *head)
+void btrfs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode));
}

@@ -9234,7 +9233,7 @@ void btrfs_destroy_inode(struct inode *inode)
* created.
*/
if (!root)
- goto free;
+ return;

while (1) {
ordered = btrfs_lookup_first_ordered_extent(inode, (u64)-1);
@@ -9252,8 +9251,6 @@ void btrfs_destroy_inode(struct inode *inode)
btrfs_qgroup_check_reserved_leak(inode);
inode_tree_del(inode);
btrfs_drop_extent_cache(BTRFS_I(inode), 0, (u64)-1, 0);
-free:
- call_rcu(&inode->i_rcu, btrfs_i_callback);
}

int btrfs_drop_inode(struct inode *inode)
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 120e4340792a..236f812091a3 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -2298,6 +2298,7 @@ static const struct super_operations btrfs_super_ops = {
.show_devname = btrfs_show_devname,
.alloc_inode = btrfs_alloc_inode,
.destroy_inode = btrfs_destroy_inode,
+ .free_inode = btrfs_free_inode,
.statfs = btrfs_statfs,
.remount_fs = btrfs_remount,
.freeze_fs = btrfs_freeze,
--
2.11.0

2019-04-16 17:57:24

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 50/62] afs: switch to use of ->free_inode()

From: Al Viro <[email protected]>

debugging printks left in ->destroy_inode() and so's the
update of inode count; we could take the latter to RCU-delayed
part (would take only moving the check on module exit past
rcu_barrier() there), but debugging output ought to either
stay where it is or go into ->evict_inode()

Signed-off-by: Al Viro <[email protected]>
---
fs/afs/super.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/fs/afs/super.c b/fs/afs/super.c
index 5adf012b8e27..bab89763119b 100644
--- a/fs/afs/super.c
+++ b/fs/afs/super.c
@@ -33,6 +33,7 @@ static void afs_i_init_once(void *foo);
static void afs_kill_super(struct super_block *sb);
static struct inode *afs_alloc_inode(struct super_block *sb);
static void afs_destroy_inode(struct inode *inode);
+static void afs_free_inode(struct inode *inode);
static int afs_statfs(struct dentry *dentry, struct kstatfs *buf);
static int afs_show_devname(struct seq_file *m, struct dentry *root);
static int afs_show_options(struct seq_file *m, struct dentry *root);
@@ -56,6 +57,7 @@ static const struct super_operations afs_super_ops = {
.alloc_inode = afs_alloc_inode,
.drop_inode = afs_drop_inode,
.destroy_inode = afs_destroy_inode,
+ .free_inode = afs_free_inode,
.evict_inode = afs_evict_inode,
.show_devname = afs_show_devname,
.show_options = afs_show_options,
@@ -660,11 +662,9 @@ static struct inode *afs_alloc_inode(struct super_block *sb)
return &vnode->vfs_inode;
}

-static void afs_i_callback(struct rcu_head *head)
+static void afs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
- struct afs_vnode *vnode = AFS_FS_I(inode);
- kmem_cache_free(afs_inode_cachep, vnode);
+ kmem_cache_free(afs_inode_cachep, AFS_FS_I(inode));
}

/*
@@ -680,7 +680,6 @@ static void afs_destroy_inode(struct inode *inode)

ASSERTCMP(vnode->cb_interest, ==, NULL);

- call_rcu(&inode->i_rcu, afs_i_callback);
atomic_dec(&afs_count_active_inodes);
}

--
2.11.0

2019-04-16 17:57:50

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 61/62] sockfs: switch to ->free_inode()

From: Al Viro <[email protected]>

we do have an RCU-delayed part there already (freeing the wq),
so it's not like the pipe situation; moreover, it might be
worth considering coallocating wq with the rest of struct sock_alloc.
->sk_wq in struct sock would remain a pointer as it is, but
the object it normally points to would be coallocated with
struct socket...

Signed-off-by: Al Viro <[email protected]>
---
net/socket.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/net/socket.c b/net/socket.c
index 8255f5bda0aa..6953a049fb82 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -263,12 +263,12 @@ static struct inode *sock_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}

-static void sock_destroy_inode(struct inode *inode)
+static void sock_free_inode(struct inode *inode)
{
struct socket_alloc *ei;

ei = container_of(inode, struct socket_alloc, vfs_inode);
- kfree_rcu(ei->socket.wq, rcu);
+ kfree(ei->socket.wq);
kmem_cache_free(sock_inode_cachep, ei);
}

@@ -293,7 +293,7 @@ static void init_inodecache(void)

static const struct super_operations sockfs_ops = {
.alloc_inode = sock_alloc_inode,
- .destroy_inode = sock_destroy_inode,
+ .free_inode = sock_free_inode,
.statfs = simple_statfs,
};

--
2.11.0

2019-04-16 17:58:03

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 47/62] securityfs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
security/inode.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/security/inode.c b/security/inode.c
index 421dd72b5876..aacc4dabba7d 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -27,22 +27,16 @@
static struct vfsmount *mount;
static int mount_count;

-static void securityfs_i_callback(struct rcu_head *head)
+static void securityfs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
if (S_ISLNK(inode->i_mode))
kfree(inode->i_link);
free_inode_nonrcu(inode);
}

-static void securityfs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, securityfs_i_callback);
-}
-
static const struct super_operations securityfs_super_operations = {
.statfs = simple_statfs,
- .destroy_inode = securityfs_destroy_inode,
+ .free_inode = securityfs_free_inode,
};

static int fill_super(struct super_block *sb, void *data, int silent)
--
2.11.0

2019-04-16 17:58:26

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 40/62] sysv: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/sysv/inode.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c
index 273736f41be3..02b1d9d0c182 100644
--- a/fs/sysv/inode.c
+++ b/fs/sysv/inode.c
@@ -313,17 +313,11 @@ static struct inode *sysv_alloc_inode(struct super_block *sb)
return &si->vfs_inode;
}

-static void sysv_i_callback(struct rcu_head *head)
+static void sysv_free_in_core_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(sysv_inode_cachep, SYSV_I(inode));
}

-static void sysv_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, sysv_i_callback);
-}
-
static void init_once(void *p)
{
struct sysv_inode_info *si = (struct sysv_inode_info *)p;
@@ -333,7 +327,7 @@ static void init_once(void *p)

const struct super_operations sysv_sops = {
.alloc_inode = sysv_alloc_inode,
- .destroy_inode = sysv_destroy_inode,
+ .free_inode = sysv_free_in_core_inode,
.write_inode = sysv_write_inode,
.evict_inode = sysv_evict_inode,
.put_super = sysv_put_super,
--
2.11.0

2019-04-16 17:58:37

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 38/62] ubifs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/ubifs/super.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index 12628184772c..c2307c423638 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -272,19 +272,13 @@ static struct inode *ubifs_alloc_inode(struct super_block *sb)
return &ui->vfs_inode;
};

-static void ubifs_i_callback(struct rcu_head *head)
+static void ubifs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
struct ubifs_inode *ui = ubifs_inode(inode);
kfree(ui->data);
kmem_cache_free(ubifs_inode_slab, ui);
}

-static void ubifs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, ubifs_i_callback);
-}
-
/*
* Note, Linux write-back code calls this without 'i_mutex'.
*/
@@ -1977,7 +1971,7 @@ static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data)

const struct super_operations ubifs_super_operations = {
.alloc_inode = ubifs_alloc_inode,
- .destroy_inode = ubifs_destroy_inode,
+ .free_inode = ubifs_free_inode,
.put_super = ubifs_put_super,
.write_inode = ubifs_write_inode,
.evict_inode = ubifs_evict_inode,
--
2.11.0

2019-04-16 17:58:42

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 37/62] squashfs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/squashfs/super.c | 11 ++---------
1 file changed, 2 insertions(+), 9 deletions(-)

diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
index 40e657386fa5..767046d9f65d 100644
--- a/fs/squashfs/super.c
+++ b/fs/squashfs/super.c
@@ -473,18 +473,11 @@ static struct inode *squashfs_alloc_inode(struct super_block *sb)
}


-static void squashfs_i_callback(struct rcu_head *head)
+static void squashfs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(squashfs_inode_cachep, squashfs_i(inode));
}

-static void squashfs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, squashfs_i_callback);
-}
-
-
static struct file_system_type squashfs_fs_type = {
.owner = THIS_MODULE,
.name = "squashfs",
@@ -496,7 +489,7 @@ MODULE_ALIAS_FS("squashfs");

static const struct super_operations squashfs_super_ops = {
.alloc_inode = squashfs_alloc_inode,
- .destroy_inode = squashfs_destroy_inode,
+ .free_inode = squashfs_free_inode,
.statfs = squashfs_statfs,
.put_super = squashfs_put_super,
.remount_fs = squashfs_remount
--
2.11.0

2019-04-16 17:58:48

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 36/62] romfs: convert to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/romfs/super.c | 11 ++---------
1 file changed, 2 insertions(+), 9 deletions(-)

diff --git a/fs/romfs/super.c b/fs/romfs/super.c
index 6ccb51993a76..7d580f7c3f1d 100644
--- a/fs/romfs/super.c
+++ b/fs/romfs/super.c
@@ -381,18 +381,11 @@ static struct inode *romfs_alloc_inode(struct super_block *sb)
/*
* return a spent inode to the slab cache
*/
-static void romfs_i_callback(struct rcu_head *head)
+static void romfs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
-
kmem_cache_free(romfs_inode_cachep, ROMFS_I(inode));
}

-static void romfs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, romfs_i_callback);
-}
-
/*
* get filesystem statistics
*/
@@ -439,7 +432,7 @@ static int romfs_remount(struct super_block *sb, int *flags, char *data)

static const struct super_operations romfs_super_ops = {
.alloc_inode = romfs_alloc_inode,
- .destroy_inode = romfs_destroy_inode,
+ .free_inode = romfs_free_inode,
.statfs = romfs_statfs,
.remount_fs = romfs_remount,
};
--
2.11.0

2019-04-16 17:58:50

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 33/62] qnx4: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/qnx4/inode.c | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c
index 3d46fe302fcb..48c70aa4a3ec 100644
--- a/fs/qnx4/inode.c
+++ b/fs/qnx4/inode.c
@@ -28,14 +28,14 @@
static const struct super_operations qnx4_sops;

static struct inode *qnx4_alloc_inode(struct super_block *sb);
-static void qnx4_destroy_inode(struct inode *inode);
+static void qnx4_free_inode(struct inode *inode);
static int qnx4_remount(struct super_block *sb, int *flags, char *data);
static int qnx4_statfs(struct dentry *, struct kstatfs *);

static const struct super_operations qnx4_sops =
{
.alloc_inode = qnx4_alloc_inode,
- .destroy_inode = qnx4_destroy_inode,
+ .free_inode = qnx4_free_inode,
.statfs = qnx4_statfs,
.remount_fs = qnx4_remount,
};
@@ -342,17 +342,11 @@ static struct inode *qnx4_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}

-static void qnx4_i_callback(struct rcu_head *head)
+static void qnx4_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(qnx4_inode_cachep, qnx4_i(inode));
}

-static void qnx4_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, qnx4_i_callback);
-}
-
static void init_once(void *foo)
{
struct qnx4_inode_info *ei = (struct qnx4_inode_info *) foo;
--
2.11.0

2019-04-16 17:58:52

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 35/62] reiserfs: convert to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/reiserfs/super.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c
index 1fc934d24459..ab028ea0e561 100644
--- a/fs/reiserfs/super.c
+++ b/fs/reiserfs/super.c
@@ -650,17 +650,11 @@ static struct inode *reiserfs_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}

-static void reiserfs_i_callback(struct rcu_head *head)
+static void reiserfs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(reiserfs_inode_cachep, REISERFS_I(inode));
}

-static void reiserfs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, reiserfs_i_callback);
-}
-
static void init_once(void *foo)
{
struct reiserfs_inode_info *ei = (struct reiserfs_inode_info *)foo;
@@ -815,7 +809,7 @@ static struct dquot **reiserfs_get_dquots(struct inode *inode)

static const struct super_operations reiserfs_sops = {
.alloc_inode = reiserfs_alloc_inode,
- .destroy_inode = reiserfs_destroy_inode,
+ .free_inode = reiserfs_free_inode,
.write_inode = reiserfs_write_inode,
.dirty_inode = reiserfs_dirty_inode,
.evict_inode = reiserfs_evict_inode,
--
2.11.0

2019-04-16 17:58:57

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 34/62] qnx6: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/qnx6/inode.c | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c
index 4aeb26bcb4d0..59cf45f6be49 100644
--- a/fs/qnx6/inode.c
+++ b/fs/qnx6/inode.c
@@ -29,14 +29,14 @@ static const struct super_operations qnx6_sops;

static void qnx6_put_super(struct super_block *sb);
static struct inode *qnx6_alloc_inode(struct super_block *sb);
-static void qnx6_destroy_inode(struct inode *inode);
+static void qnx6_free_inode(struct inode *inode);
static int qnx6_remount(struct super_block *sb, int *flags, char *data);
static int qnx6_statfs(struct dentry *dentry, struct kstatfs *buf);
static int qnx6_show_options(struct seq_file *seq, struct dentry *root);

static const struct super_operations qnx6_sops = {
.alloc_inode = qnx6_alloc_inode,
- .destroy_inode = qnx6_destroy_inode,
+ .free_inode = qnx6_free_inode,
.put_super = qnx6_put_super,
.statfs = qnx6_statfs,
.remount_fs = qnx6_remount,
@@ -602,17 +602,11 @@ static struct inode *qnx6_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}

-static void qnx6_i_callback(struct rcu_head *head)
+static void qnx6_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(qnx6_inode_cachep, QNX6_I(inode));
}

-static void qnx6_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, qnx6_i_callback);
-}
-
static void init_once(void *foo)
{
struct qnx6_inode_info *ei = (struct qnx6_inode_info *) foo;
--
2.11.0

2019-04-16 17:58:59

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 49/62] dax: make use of ->free_inode()

From: Al Viro <[email protected]>

we might want to drop ->destroy_inode() there - it's used only for
WARN_ON() now, and AFAICS that could be moved to ->evict_inode()
if we had one...

Signed-off-by: Al Viro <[email protected]>
---
drivers/dax/super.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/drivers/dax/super.c b/drivers/dax/super.c
index 0a339b85133e..bbd57ca0634a 100644
--- a/drivers/dax/super.c
+++ b/drivers/dax/super.c
@@ -412,11 +412,9 @@ static struct dax_device *to_dax_dev(struct inode *inode)
return container_of(inode, struct dax_device, inode);
}

-static void dax_i_callback(struct rcu_head *head)
+static void dax_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
struct dax_device *dax_dev = to_dax_dev(inode);
-
kfree(dax_dev->host);
dax_dev->host = NULL;
if (inode->i_rdev)
@@ -427,16 +425,15 @@ static void dax_i_callback(struct rcu_head *head)
static void dax_destroy_inode(struct inode *inode)
{
struct dax_device *dax_dev = to_dax_dev(inode);
-
WARN_ONCE(test_bit(DAXDEV_ALIVE, &dax_dev->flags),
"kill_dax() must be called before final iput()\n");
- call_rcu(&inode->i_rcu, dax_i_callback);
}

static const struct super_operations dax_sops = {
.statfs = simple_statfs,
.alloc_inode = dax_alloc_inode,
.destroy_inode = dax_destroy_inode,
+ .free_inode = dax_free_inode,
.drop_inode = generic_delete_inode,
};

--
2.11.0

2019-04-16 17:59:03

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 41/62] coda: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/coda/inode.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/coda/inode.c b/fs/coda/inode.c
index 97424cf206c0..23f6ebd08e80 100644
--- a/fs/coda/inode.c
+++ b/fs/coda/inode.c
@@ -54,17 +54,11 @@ static struct inode *coda_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}

-static void coda_i_callback(struct rcu_head *head)
+static void coda_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(coda_inode_cachep, ITOC(inode));
}

-static void coda_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, coda_i_callback);
-}
-
static void init_once(void *foo)
{
struct coda_inode_info *ei = (struct coda_inode_info *) foo;
@@ -104,7 +98,7 @@ static int coda_remount(struct super_block *sb, int *flags, char *data)
static const struct super_operations coda_super_operations =
{
.alloc_inode = coda_alloc_inode,
- .destroy_inode = coda_destroy_inode,
+ .free_inode = coda_free_inode,
.evict_inode = coda_evict_inode,
.put_super = coda_put_super,
.statfs = coda_statfs,
--
2.11.0

2019-04-16 17:59:03

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 05/62] erofs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
drivers/staging/erofs/super.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/drivers/staging/erofs/super.c b/drivers/staging/erofs/super.c
index 15c784fba879..700cbd460807 100644
--- a/drivers/staging/erofs/super.c
+++ b/drivers/staging/erofs/super.c
@@ -57,9 +57,8 @@ static struct inode *alloc_inode(struct super_block *sb)
return &vi->vfs_inode;
}

-static void i_callback(struct rcu_head *head)
+static void free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
struct erofs_vnode *vi = EROFS_V(inode);

/* be careful RCU symlink path (see ext4_inode_info->i_data)! */
@@ -71,11 +70,6 @@ static void i_callback(struct rcu_head *head)
kmem_cache_free(erofs_inode_cachep, vi);
}

-static void destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, i_callback);
-}
-
static int superblock_read(struct super_block *sb)
{
struct erofs_sb_info *sbi;
@@ -668,7 +662,7 @@ static int erofs_remount(struct super_block *sb, int *flags, char *data)
const struct super_operations erofs_sops = {
.put_super = erofs_put_super,
.alloc_inode = alloc_inode,
- .destroy_inode = destroy_inode,
+ .free_inode = free_inode,
.statfs = erofs_statfs,
.show_options = erofs_show_options,
.remount_fs = erofs_remount,
--
2.11.0

2019-04-16 17:59:07

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 28/62] nilfs2: switch to ->free_inode()

From: Al Viro <[email protected]>

kill an extern that went stale 9 years ago, while we are at it...

Signed-off-by: Al Viro <[email protected]>
---
fs/nilfs2/nilfs.h | 2 --
fs/nilfs2/super.c | 11 ++---------
2 files changed, 2 insertions(+), 11 deletions(-)

diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h
index a2f247b6a209..42395ba52da6 100644
--- a/fs/nilfs2/nilfs.h
+++ b/fs/nilfs2/nilfs.h
@@ -252,7 +252,6 @@ int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *, struct nilfs_argv *,
void nilfs_inode_add_blocks(struct inode *inode, int n);
void nilfs_inode_sub_blocks(struct inode *inode, int n);
extern struct inode *nilfs_new_inode(struct inode *, umode_t);
-extern void nilfs_free_inode(struct inode *);
extern int nilfs_get_block(struct inode *, sector_t, struct buffer_head *, int);
extern void nilfs_set_inode_flags(struct inode *);
extern int nilfs_read_inode_common(struct inode *, struct nilfs_inode *);
@@ -289,7 +288,6 @@ static inline int nilfs_mark_inode_dirty_sync(struct inode *inode)

/* super.c */
extern struct inode *nilfs_alloc_inode(struct super_block *);
-extern void nilfs_destroy_inode(struct inode *);

extern __printf(3, 4)
void __nilfs_msg(struct super_block *sb, const char *level,
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c
index 26290aa1023f..5729ee86da9a 100644
--- a/fs/nilfs2/super.c
+++ b/fs/nilfs2/super.c
@@ -155,21 +155,14 @@ struct inode *nilfs_alloc_inode(struct super_block *sb)
return &ii->vfs_inode;
}

-static void nilfs_i_callback(struct rcu_head *head)
+static void nilfs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
-
if (nilfs_is_metadata_file_inode(inode))
nilfs_mdt_destroy(inode);

kmem_cache_free(nilfs_inode_cachep, NILFS_I(inode));
}

-void nilfs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, nilfs_i_callback);
-}
-
static int nilfs_sync_super(struct super_block *sb, int flag)
{
struct the_nilfs *nilfs = sb->s_fs_info;
@@ -686,7 +679,7 @@ static int nilfs_show_options(struct seq_file *seq, struct dentry *dentry)

static const struct super_operations nilfs_sops = {
.alloc_inode = nilfs_alloc_inode,
- .destroy_inode = nilfs_destroy_inode,
+ .free_inode = nilfs_free_inode,
.dirty_inode = nilfs_dirty_inode,
.evict_inode = nilfs_evict_inode,
.put_super = nilfs_put_super,
--
2.11.0

2019-04-16 17:59:13

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 46/62] apparmor: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
security/apparmor/apparmorfs.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index b9298d2e8165..9ab5613fe07c 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -123,22 +123,16 @@ static int aafs_show_path(struct seq_file *seq, struct dentry *dentry)
return 0;
}

-static void aafs_i_callback(struct rcu_head *head)
+static void aafs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
if (S_ISLNK(inode->i_mode))
kfree(inode->i_link);
free_inode_nonrcu(inode);
}

-static void aafs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, aafs_i_callback);
-}
-
static const struct super_operations aafs_super_ops = {
.statfs = simple_statfs,
- .destroy_inode = aafs_destroy_inode,
+ .free_inode = aafs_free_inode,
.show_path = aafs_show_path,
};

--
2.11.0

2019-04-16 17:59:17

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 45/62] rpcpipe: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
net/sunrpc/rpc_pipe.c | 11 ++---------
1 file changed, 2 insertions(+), 9 deletions(-)

diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c
index 69663681bf9d..979d23646e33 100644
--- a/net/sunrpc/rpc_pipe.c
+++ b/net/sunrpc/rpc_pipe.c
@@ -202,18 +202,11 @@ rpc_alloc_inode(struct super_block *sb)
}

static void
-rpc_i_callback(struct rcu_head *head)
+rpc_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(rpc_inode_cachep, RPC_I(inode));
}

-static void
-rpc_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, rpc_i_callback);
-}
-
static int
rpc_pipe_open(struct inode *inode, struct file *filp)
{
@@ -1123,7 +1116,7 @@ void rpc_remove_cache_dir(struct dentry *dentry)
*/
static const struct super_operations s_ops = {
.alloc_inode = rpc_alloc_inode,
- .destroy_inode = rpc_destroy_inode,
+ .free_inode = rpc_free_inode,
.statfs = simple_statfs,
};

--
2.11.0

2019-04-16 17:59:24

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 27/62] nfs{,4}: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/nfs/inode.c | 10 ++--------
fs/nfs/internal.h | 2 +-
fs/nfs/nfs4super.c | 2 +-
fs/nfs/super.c | 2 +-
4 files changed, 5 insertions(+), 11 deletions(-)

diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 414a90d48493..f61af8307dc8 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -2055,17 +2055,11 @@ struct inode *nfs_alloc_inode(struct super_block *sb)
}
EXPORT_SYMBOL_GPL(nfs_alloc_inode);

-static void nfs_i_callback(struct rcu_head *head)
+void nfs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(nfs_inode_cachep, NFS_I(inode));
}
-
-void nfs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, nfs_i_callback);
-}
-EXPORT_SYMBOL_GPL(nfs_destroy_inode);
+EXPORT_SYMBOL_GPL(nfs_free_inode);

static inline void nfs4_init_once(struct nfs_inode *nfsi)
{
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index c7cf23ae6597..331a0504eaf8 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -381,7 +381,7 @@ int nfs_check_flags(int);
/* inode.c */
extern struct workqueue_struct *nfsiod_workqueue;
extern struct inode *nfs_alloc_inode(struct super_block *sb);
-extern void nfs_destroy_inode(struct inode *);
+extern void nfs_free_inode(struct inode *);
extern int nfs_write_inode(struct inode *, struct writeback_control *);
extern int nfs_drop_inode(struct inode *);
extern void nfs_clear_inode(struct inode *);
diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c
index 6fb7cb6b3f4b..689977e148cb 100644
--- a/fs/nfs/nfs4super.c
+++ b/fs/nfs/nfs4super.c
@@ -50,7 +50,7 @@ struct file_system_type nfs4_referral_fs_type = {

static const struct super_operations nfs4_sops = {
.alloc_inode = nfs_alloc_inode,
- .destroy_inode = nfs_destroy_inode,
+ .free_inode = nfs_free_inode,
.write_inode = nfs4_write_inode,
.drop_inode = nfs_drop_inode,
.statfs = nfs_statfs,
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 23790c7b2289..aec4e2c4b02f 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -309,7 +309,7 @@ struct file_system_type nfs_xdev_fs_type = {

const struct super_operations nfs_sops = {
.alloc_inode = nfs_alloc_inode,
- .destroy_inode = nfs_destroy_inode,
+ .free_inode = nfs_free_inode,
.write_inode = nfs_write_inode,
.drop_inode = nfs_drop_inode,
.statfs = nfs_statfs,
--
2.11.0

2019-04-16 17:59:31

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 42/62] ufs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/ufs/super.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/ufs/super.c b/fs/ufs/super.c
index a4e07e910f1b..84c0c5178cd2 100644
--- a/fs/ufs/super.c
+++ b/fs/ufs/super.c
@@ -1449,17 +1449,11 @@ static struct inode *ufs_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}

-static void ufs_i_callback(struct rcu_head *head)
+static void ufs_free_in_core_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(ufs_inode_cachep, UFS_I(inode));
}

-static void ufs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, ufs_i_callback);
-}
-
static void init_once(void *foo)
{
struct ufs_inode_info *ei = (struct ufs_inode_info *) foo;
@@ -1494,7 +1488,7 @@ static void destroy_inodecache(void)

static const struct super_operations ufs_super_ops = {
.alloc_inode = ufs_alloc_inode,
- .destroy_inode = ufs_destroy_inode,
+ .free_inode = ufs_free_in_core_inode,
.write_inode = ufs_write_inode,
.evict_inode = ufs_evict_inode,
.put_super = ufs_put_super,
--
2.11.0

2019-04-16 17:59:46

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 23/62] hpfs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/hpfs/super.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c
index f2c3ebcd309c..ed4264bca790 100644
--- a/fs/hpfs/super.c
+++ b/fs/hpfs/super.c
@@ -238,17 +238,11 @@ static struct inode *hpfs_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}

-static void hpfs_i_callback(struct rcu_head *head)
+static void hpfs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(hpfs_inode_cachep, hpfs_i(inode));
}

-static void hpfs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, hpfs_i_callback);
-}
-
static void init_once(void *foo)
{
struct hpfs_inode_info *ei = (struct hpfs_inode_info *) foo;
@@ -532,7 +526,7 @@ static int hpfs_show_options(struct seq_file *seq, struct dentry *root)
static const struct super_operations hpfs_sops =
{
.alloc_inode = hpfs_alloc_inode,
- .destroy_inode = hpfs_destroy_inode,
+ .free_inode = hpfs_free_inode,
.evict_inode = hpfs_evict_inode,
.put_super = hpfs_put_super,
.statfs = hpfs_statfs,
--
2.11.0

2019-04-16 17:59:47

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 24/62] isofs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/isofs/inode.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index 488a9e7f8f66..603b052a3c94 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -72,17 +72,11 @@ static struct inode *isofs_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}

-static void isofs_i_callback(struct rcu_head *head)
+static void isofs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(isofs_inode_cachep, ISOFS_I(inode));
}

-static void isofs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, isofs_i_callback);
-}
-
static void init_once(void *foo)
{
struct iso_inode_info *ei = foo;
@@ -122,7 +116,7 @@ static int isofs_remount(struct super_block *sb, int *flags, char *data)

static const struct super_operations isofs_sops = {
.alloc_inode = isofs_alloc_inode,
- .destroy_inode = isofs_destroy_inode,
+ .free_inode = isofs_free_inode,
.put_super = isofs_put_super,
.statfs = isofs_statfs,
.remount_fs = isofs_remount,
--
2.11.0

2019-04-16 18:00:19

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 13/62] debugfs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/debugfs/inode.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index f25daa207421..414fa4752047 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -163,24 +163,18 @@ static int debugfs_show_options(struct seq_file *m, struct dentry *root)
return 0;
}

-static void debugfs_i_callback(struct rcu_head *head)
+static void debugfs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
if (S_ISLNK(inode->i_mode))
kfree(inode->i_link);
free_inode_nonrcu(inode);
}

-static void debugfs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, debugfs_i_callback);
-}
-
static const struct super_operations debugfs_super_operations = {
.statfs = simple_statfs,
.remount_fs = debugfs_remount,
.show_options = debugfs_show_options,
- .destroy_inode = debugfs_destroy_inode,
+ .free_inode = debugfs_free_inode,
};

static void debugfs_release_dentry(struct dentry *dentry)
--
2.11.0

2019-04-16 18:00:29

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 17/62] fat: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/fat/inode.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 79bb0e73a65f..ba93d1373306 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -746,17 +746,11 @@ static struct inode *fat_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}

-static void fat_i_callback(struct rcu_head *head)
+static void fat_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(fat_inode_cachep, MSDOS_I(inode));
}

-static void fat_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, fat_i_callback);
-}
-
static void init_once(void *foo)
{
struct msdos_inode_info *ei = (struct msdos_inode_info *)foo;
@@ -920,7 +914,7 @@ EXPORT_SYMBOL_GPL(fat_sync_inode);
static int fat_show_options(struct seq_file *m, struct dentry *root);
static const struct super_operations fat_sops = {
.alloc_inode = fat_alloc_inode,
- .destroy_inode = fat_destroy_inode,
+ .free_inode = fat_free_inode,
.write_inode = fat_write_inode,
.evict_inode = fat_evict_inode,
.put_super = fat_put_super,
--
2.11.0

2019-04-16 18:00:29

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 29/62] dlmfs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/ocfs2/dlmfs/dlmfs.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c
index 8decbe95dcec..98885181e1fe 100644
--- a/fs/ocfs2/dlmfs/dlmfs.c
+++ b/fs/ocfs2/dlmfs/dlmfs.c
@@ -349,17 +349,11 @@ static struct inode *dlmfs_alloc_inode(struct super_block *sb)
return &ip->ip_vfs_inode;
}

-static void dlmfs_i_callback(struct rcu_head *head)
+static void dlmfs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(dlmfs_inode_cache, DLMFS_I(inode));
}

-static void dlmfs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, dlmfs_i_callback);
-}
-
static void dlmfs_evict_inode(struct inode *inode)
{
int status;
@@ -605,7 +599,7 @@ static const struct inode_operations dlmfs_root_inode_operations = {
static const struct super_operations dlmfs_ops = {
.statfs = simple_statfs,
.alloc_inode = dlmfs_alloc_inode,
- .destroy_inode = dlmfs_destroy_inode,
+ .free_inode = dlmfs_free_inode,
.evict_inode = dlmfs_evict_inode,
.drop_inode = generic_delete_inode,
};
--
2.11.0

2019-04-16 18:00:34

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 15/62] ext2: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/ext2/super.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index 0128010a0874..3988633789cb 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -192,17 +192,11 @@ static struct inode *ext2_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}

-static void ext2_i_callback(struct rcu_head *head)
+static void ext2_free_in_core_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(ext2_inode_cachep, EXT2_I(inode));
}

-static void ext2_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, ext2_i_callback);
-}
-
static void init_once(void *foo)
{
struct ext2_inode_info *ei = (struct ext2_inode_info *) foo;
@@ -351,7 +345,7 @@ static const struct quotactl_ops ext2_quotactl_ops = {

static const struct super_operations ext2_sops = {
.alloc_inode = ext2_alloc_inode,
- .destroy_inode = ext2_destroy_inode,
+ .free_inode = ext2_free_in_core_inode,
.write_inode = ext2_write_inode,
.evict_inode = ext2_evict_inode,
.put_super = ext2_put_super,
--
2.11.0

2019-04-16 18:00:42

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 16/62] f2fs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/f2fs/super.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index f2aaa2cc6b3e..9924eac76254 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -1000,17 +1000,11 @@ static void f2fs_dirty_inode(struct inode *inode, int flags)
f2fs_inode_dirtied(inode, false);
}

-static void f2fs_i_callback(struct rcu_head *head)
+static void f2fs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(f2fs_inode_cachep, F2FS_I(inode));
}

-static void f2fs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, f2fs_i_callback);
-}
-
static void destroy_percpu_info(struct f2fs_sb_info *sbi)
{
percpu_counter_destroy(&sbi->alloc_valid_block_count);
@@ -2166,8 +2160,8 @@ void f2fs_quota_off_umount(struct super_block *sb)

static const struct super_operations f2fs_sops = {
.alloc_inode = f2fs_alloc_inode,
+ .free_inode = f2fs_free_inode,
.drop_inode = f2fs_drop_inode,
- .destroy_inode = f2fs_destroy_inode,
.write_inode = f2fs_write_inode,
.dirty_inode = f2fs_dirty_inode,
.show_options = f2fs_show_options,
--
2.11.0

2019-04-16 18:00:44

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 25/62] jffs2: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/jffs2/super.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
index 05d892c79339..af4aa6599473 100644
--- a/fs/jffs2/super.c
+++ b/fs/jffs2/super.c
@@ -44,20 +44,14 @@ static struct inode *jffs2_alloc_inode(struct super_block *sb)
return &f->vfs_inode;
}

-static void jffs2_i_callback(struct rcu_head *head)
+static void jffs2_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);

kfree(f->target);
kmem_cache_free(jffs2_inode_cachep, f);
}

-static void jffs2_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, jffs2_i_callback);
-}
-
static void jffs2_i_init_once(void *foo)
{
struct jffs2_inode_info *f = foo;
@@ -258,7 +252,7 @@ static int jffs2_remount_fs(struct super_block *sb, int *flags, char *data)
static const struct super_operations jffs2_super_operations =
{
.alloc_inode = jffs2_alloc_inode,
- .destroy_inode =jffs2_destroy_inode,
+ .free_inode = jffs2_free_inode,
.put_super = jffs2_put_super,
.statfs = jffs2_statfs,
.remount_fs = jffs2_remount_fs,
--
2.11.0

2019-04-16 18:00:46

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 18/62] freevxfs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/freevxfs/vxfs_super.c | 11 ++---------
1 file changed, 2 insertions(+), 9 deletions(-)

diff --git a/fs/freevxfs/vxfs_super.c b/fs/freevxfs/vxfs_super.c
index 48b24bb50d02..a89f68c3cbed 100644
--- a/fs/freevxfs/vxfs_super.c
+++ b/fs/freevxfs/vxfs_super.c
@@ -131,21 +131,14 @@ static struct inode *vxfs_alloc_inode(struct super_block *sb)
return &vi->vfs_inode;
}

-static void vxfs_i_callback(struct rcu_head *head)
+static void vxfs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
-
kmem_cache_free(vxfs_inode_cachep, VXFS_INO(inode));
}

-static void vxfs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, vxfs_i_callback);
-}
-
static const struct super_operations vxfs_super_ops = {
.alloc_inode = vxfs_alloc_inode,
- .destroy_inode = vxfs_destroy_inode,
+ .free_inode = vxfs_free_inode,
.evict_inode = vxfs_evict_inode,
.put_super = vxfs_put_super,
.statfs = vxfs_statfs,
--
2.11.0

2019-04-16 18:00:53

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 30/62] ocfs2: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/ocfs2/super.c | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index 96ae7cedd487..7982a93e630f 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -134,7 +134,7 @@ static int ocfs2_get_sector(struct super_block *sb,
int block,
int sect_size);
static struct inode *ocfs2_alloc_inode(struct super_block *sb);
-static void ocfs2_destroy_inode(struct inode *inode);
+static void ocfs2_free_inode(struct inode *inode);
static int ocfs2_susp_quotas(struct ocfs2_super *osb, int unsuspend);
static int ocfs2_enable_quotas(struct ocfs2_super *osb);
static void ocfs2_disable_quotas(struct ocfs2_super *osb);
@@ -147,7 +147,7 @@ static struct dquot **ocfs2_get_dquots(struct inode *inode)
static const struct super_operations ocfs2_sops = {
.statfs = ocfs2_statfs,
.alloc_inode = ocfs2_alloc_inode,
- .destroy_inode = ocfs2_destroy_inode,
+ .free_inode = ocfs2_free_inode,
.drop_inode = ocfs2_drop_inode,
.evict_inode = ocfs2_evict_inode,
.sync_fs = ocfs2_sync_fs,
@@ -575,17 +575,11 @@ static struct inode *ocfs2_alloc_inode(struct super_block *sb)
return &oi->vfs_inode;
}

-static void ocfs2_i_callback(struct rcu_head *head)
+static void ocfs2_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(ocfs2_inode_cachep, OCFS2_I(inode));
}

-static void ocfs2_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, ocfs2_i_callback);
-}
-
static unsigned long long ocfs2_max_file_offset(unsigned int bbits,
unsigned int cbits)
{
--
2.11.0

2019-04-16 18:00:53

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 26/62] minix: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/minix/inode.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index 72e308c3e66b..101200761f61 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -68,17 +68,11 @@ static struct inode *minix_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}

-static void minix_i_callback(struct rcu_head *head)
+static void minix_free_in_core_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(minix_inode_cachep, minix_i(inode));
}

-static void minix_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, minix_i_callback);
-}
-
static void init_once(void *foo)
{
struct minix_inode_info *ei = (struct minix_inode_info *) foo;
@@ -110,7 +104,7 @@ static void destroy_inodecache(void)

static const struct super_operations minix_sops = {
.alloc_inode = minix_alloc_inode,
- .destroy_inode = minix_destroy_inode,
+ .free_inode = minix_free_in_core_inode,
.write_inode = minix_write_inode,
.evict_inode = minix_evict_inode,
.put_super = minix_put_super,
--
2.11.0

2019-04-16 18:01:03

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 09/62] befs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/befs/linuxvfs.c | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c
index 4700b4534439..e273850c95af 100644
--- a/fs/befs/linuxvfs.c
+++ b/fs/befs/linuxvfs.c
@@ -44,7 +44,7 @@ static struct dentry *befs_lookup(struct inode *, struct dentry *,
unsigned int);
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_free_inode(struct inode *inode);
static void befs_destroy_inodecache(void);
static int befs_symlink_readpage(struct file *, struct page *);
static int befs_utf2nls(struct super_block *sb, const char *in, int in_len,
@@ -64,7 +64,7 @@ static struct dentry *befs_get_parent(struct dentry *child);

static const struct super_operations befs_sops = {
.alloc_inode = befs_alloc_inode, /* allocate a new inode */
- .destroy_inode = befs_destroy_inode, /* deallocate an inode */
+ .free_inode = befs_free_inode, /* deallocate an inode */
.put_super = befs_put_super, /* uninit super */
.statfs = befs_statfs, /* statfs */
.remount_fs = befs_remount,
@@ -281,17 +281,11 @@ befs_alloc_inode(struct super_block *sb)
return &bi->vfs_inode;
}

-static void befs_i_callback(struct rcu_head *head)
+static void befs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(befs_inode_cachep, BEFS_I(inode));
}

-static void befs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, befs_i_callback);
-}
-
static void init_once(void *foo)
{
struct befs_inode_info *bi = (struct befs_inode_info *) foo;
--
2.11.0

2019-04-16 18:01:11

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 03/62] new inode method: ->free_inode()

From: Al Viro <[email protected]>

A lot of ->destroy_inode() instances end with call_rcu() of a callback
that does RCU-delayed part of freeing. Introduce a new method for
doing just that, with saner signature.

Rules:
->destroy_inode ->free_inode
f g immediate call of f(),
RCU-delayed call of g()
f NULL immediate call of f(),
no RCU-delayed calls
NULL g RCU-delayed call of g()
NULL NULL RCU-delayed default freeing

IOW, NULL ->free_inode gives the same behaviour as now.

Note that NULL, NULL is equivalent to NULL, free_inode_nonrcu; we could
mandate the latter form, but that would have very little benefit beyond
making rules a bit more symmetric. It would break backwards compatibility,
require extra boilerplate and expected semantics for (NULL, NULL) pair
would have no use whatsoever...

Signed-off-by: Al Viro <[email protected]>
---
Documentation/filesystems/Locking | 2 ++
Documentation/filesystems/porting | 17 ++++++++++++
fs/inode.c | 54 +++++++++++++++++++++++----------------
include/linux/fs.h | 1 +
4 files changed, 52 insertions(+), 22 deletions(-)

diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking
index efea228ccd8a..7b20c385cc02 100644
--- a/Documentation/filesystems/Locking
+++ b/Documentation/filesystems/Locking
@@ -118,6 +118,7 @@ set: exclusive
--------------------------- super_operations ---------------------------
prototypes:
struct inode *(*alloc_inode)(struct super_block *sb);
+ void (*free_inode)(struct inode *);
void (*destroy_inode)(struct inode *);
void (*dirty_inode) (struct inode *, int flags);
int (*write_inode) (struct inode *, struct writeback_control *wbc);
@@ -139,6 +140,7 @@ locking rules:
All may block [not true, see below]
s_umount
alloc_inode:
+free_inode: called from RCU callback
destroy_inode:
dirty_inode:
write_inode:
diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting
index cf43bc4dbf31..9d80f9e0855e 100644
--- a/Documentation/filesystems/porting
+++ b/Documentation/filesystems/porting
@@ -638,3 +638,20 @@ in your dentry operations instead.
inode to d_splice_alias() will also do the right thing (equivalent of
d_add(dentry, NULL); return NULL;), so that kind of special cases
also doesn't need a separate treatment.
+--
+[strongly recommended]
+ take the RCU-delayed parts of ->destroy_inode() into a new method -
+ ->free_inode(). If ->destroy_inode() becomes empty - all the better,
+ just get rid of it. Synchronous work (e.g. the stuff that can't
+ be done from an RCU callback, or any WARN_ON() where we want the
+ stack trace) *might* be movable to ->evict_inode(); however,
+ that goes only for the things that are not needed to balance something
+ done by ->alloc_inode(). IOW, if it's cleaning up the stuff that
+ might have accumulated over the life of in-core inode, ->evict_inode()
+ might be a fit.
+
+ Rules for inode destruction:
+ * if ->destroy_inode() is non-NULL, it gets called
+ * if ->free_inode() is non-NULL, it gets scheduled by call_rcu()
+ * combination of NULL ->destroy_inode and NULL ->free_inode is
+ treated as NULL/free_inode_nonrcu, to preserve the compatibility.
diff --git a/fs/inode.c b/fs/inode.c
index e9d97add2b36..fb45590d284e 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -202,12 +202,28 @@ int inode_init_always(struct super_block *sb, struct inode *inode)
}
EXPORT_SYMBOL(inode_init_always);

+void free_inode_nonrcu(struct inode *inode)
+{
+ kmem_cache_free(inode_cachep, inode);
+}
+EXPORT_SYMBOL(free_inode_nonrcu);
+
+static void i_callback(struct rcu_head *head)
+{
+ struct inode *inode = container_of(head, struct inode, i_rcu);
+ if (inode->i_sb->s_op->free_inode)
+ inode->i_sb->s_op->free_inode(inode);
+ else
+ free_inode_nonrcu(inode);
+}
+
static struct inode *alloc_inode(struct super_block *sb)
{
+ const struct super_operations *ops = sb->s_op;
struct inode *inode;

- if (sb->s_op->alloc_inode)
- inode = sb->s_op->alloc_inode(sb);
+ if (ops->alloc_inode)
+ inode = ops->alloc_inode(sb);
else
inode = kmem_cache_alloc(inode_cachep, GFP_KERNEL);

@@ -215,22 +231,18 @@ static struct inode *alloc_inode(struct super_block *sb)
return NULL;

if (unlikely(inode_init_always(sb, inode))) {
- if (inode->i_sb->s_op->destroy_inode)
- inode->i_sb->s_op->destroy_inode(inode);
- else
- kmem_cache_free(inode_cachep, inode);
+ if (ops->destroy_inode) {
+ ops->destroy_inode(inode);
+ if (!ops->free_inode)
+ return NULL;
+ }
+ i_callback(&inode->i_rcu);
return NULL;
}

return inode;
}

-void free_inode_nonrcu(struct inode *inode)
-{
- kmem_cache_free(inode_cachep, inode);
-}
-EXPORT_SYMBOL(free_inode_nonrcu);
-
void __destroy_inode(struct inode *inode)
{
BUG_ON(inode_has_buffers(inode));
@@ -253,20 +265,18 @@ void __destroy_inode(struct inode *inode)
}
EXPORT_SYMBOL(__destroy_inode);

-static void i_callback(struct rcu_head *head)
-{
- struct inode *inode = container_of(head, struct inode, i_rcu);
- kmem_cache_free(inode_cachep, inode);
-}
-
static void destroy_inode(struct inode *inode)
{
+ const struct super_operations *ops = inode->i_sb->s_op;
+
BUG_ON(!list_empty(&inode->i_lru));
__destroy_inode(inode);
- if (inode->i_sb->s_op->destroy_inode)
- inode->i_sb->s_op->destroy_inode(inode);
- else
- call_rcu(&inode->i_rcu, i_callback);
+ if (ops->destroy_inode) {
+ ops->destroy_inode(inode);
+ if (!ops->free_inode)
+ return;
+ }
+ call_rcu(&inode->i_rcu, i_callback);
}

/**
diff --git a/include/linux/fs.h b/include/linux/fs.h
index dd28e7679089..2e9b9f87caca 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1903,6 +1903,7 @@ extern loff_t vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos,
struct super_operations {
struct inode *(*alloc_inode)(struct super_block *sb);
void (*destroy_inode)(struct inode *);
+ void (*free_inode)(struct inode *);

void (*dirty_inode) (struct inode *, int flags);
int (*write_inode) (struct inode *, struct writeback_control *wbc);
--
2.11.0

2019-04-16 18:01:12

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 22/62] hostfs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/hostfs/hostfs_kern.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index 444c7b170359..5a7eb0c79839 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -243,17 +243,11 @@ static void hostfs_evict_inode(struct inode *inode)
}
}

-static void hostfs_i_callback(struct rcu_head *head)
+static void hostfs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kfree(HOSTFS_I(inode));
}

-static void hostfs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, hostfs_i_callback);
-}
-
static int hostfs_show_options(struct seq_file *seq, struct dentry *root)
{
const char *root_path = root->d_sb->s_fs_info;
@@ -270,7 +264,7 @@ static int hostfs_show_options(struct seq_file *seq, struct dentry *root)

static const struct super_operations hostfs_sbops = {
.alloc_inode = hostfs_alloc_inode,
- .destroy_inode = hostfs_destroy_inode,
+ .free_inode = hostfs_free_inode,
.evict_inode = hostfs_evict_inode,
.statfs = hostfs_statfs,
.show_options = hostfs_show_options,
--
2.11.0

2019-04-16 18:01:15

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 02/62] apparmorfs: fix use-after-free on symlink traversal

From: Al Viro <[email protected]>

symlink body shouldn't be freed without an RCU delay. Switch apparmorfs
to ->destroy_inode() and use of call_rcu(); free both the inode and symlink
body in the callback.

Signed-off-by: Al Viro <[email protected]>
---
security/apparmor/apparmorfs.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index fefee040bf79..b9298d2e8165 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -123,17 +123,22 @@ static int aafs_show_path(struct seq_file *seq, struct dentry *dentry)
return 0;
}

-static void aafs_evict_inode(struct inode *inode)
+static void aafs_i_callback(struct rcu_head *head)
{
- truncate_inode_pages_final(&inode->i_data);
- clear_inode(inode);
+ struct inode *inode = container_of(head, struct inode, i_rcu);
if (S_ISLNK(inode->i_mode))
kfree(inode->i_link);
+ free_inode_nonrcu(inode);
+}
+
+static void aafs_destroy_inode(struct inode *inode)
+{
+ call_rcu(&inode->i_rcu, aafs_i_callback);
}

static const struct super_operations aafs_super_ops = {
.statfs = simple_statfs,
- .evict_inode = aafs_evict_inode,
+ .destroy_inode = aafs_destroy_inode,
.show_path = aafs_show_path,
};

--
2.11.0

2019-04-16 18:01:25

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 11/62] bdev: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/block_dev.c | 14 +++-----------
1 file changed, 3 insertions(+), 11 deletions(-)

diff --git a/fs/block_dev.c b/fs/block_dev.c
index 78d3257435c0..9d5fd05dd643 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -789,17 +789,9 @@ static struct inode *bdev_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}

-static void bdev_i_callback(struct rcu_head *head)
+static void bdev_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
- struct bdev_inode *bdi = BDEV_I(inode);
-
- kmem_cache_free(bdev_cachep, bdi);
-}
-
-static void bdev_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, bdev_i_callback);
+ kmem_cache_free(bdev_cachep, BDEV_I(inode));
}

static void init_once(void *foo)
@@ -839,7 +831,7 @@ static void bdev_evict_inode(struct inode *inode)
static const struct super_operations bdev_sops = {
.statfs = simple_statfs,
.alloc_inode = bdev_alloc_inode,
- .destroy_inode = bdev_destroy_inode,
+ .free_inode = bdev_free_inode,
.drop_inode = generic_delete_inode,
.evict_inode = bdev_evict_inode,
};
--
2.11.0

2019-04-16 18:01:31

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 12/62] cifs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/cifs/cifsfs.c | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index a05bf1d6e1d0..877174761efb 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -315,16 +315,10 @@ cifs_alloc_inode(struct super_block *sb)
return &cifs_inode->vfs_inode;
}

-static void cifs_i_callback(struct rcu_head *head)
-{
- struct inode *inode = container_of(head, struct inode, i_rcu);
- kmem_cache_free(cifs_inode_cachep, CIFS_I(inode));
-}
-
static void
-cifs_destroy_inode(struct inode *inode)
+cifs_free_inode(struct inode *inode)
{
- call_rcu(&inode->i_rcu, cifs_i_callback);
+ kmem_cache_free(cifs_inode_cachep, CIFS_I(inode));
}

static void
@@ -630,7 +624,7 @@ static int cifs_drop_inode(struct inode *inode)
static const struct super_operations cifs_super_ops = {
.statfs = cifs_statfs,
.alloc_inode = cifs_alloc_inode,
- .destroy_inode = cifs_destroy_inode,
+ .free_inode = cifs_free_inode,
.drop_inode = cifs_drop_inode,
.evict_inode = cifs_evict_inode,
/* .delete_inode = cifs_delete_inode, */ /* Do not need above
--
2.11.0

2019-04-16 18:01:32

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 10/62] bfs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/bfs/inode.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c
index d136b2aaafb3..dc0cd2aa3d65 100644
--- a/fs/bfs/inode.c
+++ b/fs/bfs/inode.c
@@ -245,17 +245,11 @@ static struct inode *bfs_alloc_inode(struct super_block *sb)
return &bi->vfs_inode;
}

-static void bfs_i_callback(struct rcu_head *head)
+static void bfs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(bfs_inode_cachep, BFS_I(inode));
}

-static void bfs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, bfs_i_callback);
-}
-
static void init_once(void *foo)
{
struct bfs_inode_info *bi = foo;
@@ -287,7 +281,7 @@ static void destroy_inodecache(void)

static const struct super_operations bfs_sops = {
.alloc_inode = bfs_alloc_inode,
- .destroy_inode = bfs_destroy_inode,
+ .free_inode = bfs_free_inode,
.write_inode = bfs_write_inode,
.evict_inode = bfs_evict_inode,
.put_super = bfs_put_super,
--
2.11.0

2019-04-16 18:01:34

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 14/62] efs: switch to ->free_inode()

From: Al Viro <[email protected]>

Signed-off-by: Al Viro <[email protected]>
---
fs/efs/super.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/fs/efs/super.c b/fs/efs/super.c
index 6ffb7ba1547a..867fc24dee20 100644
--- a/fs/efs/super.c
+++ b/fs/efs/super.c
@@ -74,17 +74,11 @@ static struct inode *efs_alloc_inode(struct super_block *sb)
return &ei->vfs_inode;
}

-static void efs_i_callback(struct rcu_head *head)
+static void efs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
kmem_cache_free(efs_inode_cachep, INODE_INFO(inode));
}

-static void efs_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, efs_i_callback);
-}
-
static void init_once(void *foo)
{
struct efs_inode_info *ei = (struct efs_inode_info *) foo;
@@ -122,7 +116,7 @@ static int efs_remount(struct super_block *sb, int *flags, char *data)

static const struct super_operations efs_superblock_operations = {
.alloc_inode = efs_alloc_inode,
- .destroy_inode = efs_destroy_inode,
+ .free_inode = efs_free_inode,
.statfs = efs_statfs,
.remount_fs = efs_remount,
};
--
2.11.0

2019-04-16 18:02:00

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 19/62] gfs2: switch to ->free_inode()

From: Al Viro <[email protected]>

... and use GFS2_I() to get the containing gfs2_inode by inode;
yes, we can feed the address of the first member of structure
to kmem_cache_free(), but let's do it in an obviously safe way.

Signed-off-by: Al Viro <[email protected]>
---
fs/gfs2/super.c | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index ca71163ff7cf..7b8d2306b3d3 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -1736,20 +1736,14 @@ static struct inode *gfs2_alloc_inode(struct super_block *sb)
return &ip->i_inode;
}

-static void gfs2_i_callback(struct rcu_head *head)
+static void gfs2_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
- kmem_cache_free(gfs2_inode_cachep, inode);
-}
-
-static void gfs2_destroy_inode(struct inode *inode)
-{
- call_rcu(&inode->i_rcu, gfs2_i_callback);
+ kmem_cache_free(gfs2_inode_cachep, GFS2_I(inode));
}

const struct super_operations gfs2_super_ops = {
.alloc_inode = gfs2_alloc_inode,
- .destroy_inode = gfs2_destroy_inode,
+ .free_inode = gfs2_free_inode,
.write_inode = gfs2_write_inode,
.dirty_inode = gfs2_dirty_inode,
.evict_inode = gfs2_evict_inode,
--
2.11.0

2019-04-16 18:02:23

by Al Viro

[permalink] [raw]
Subject: [RFC PATCH 01/62] securityfs: fix use-after-free on symlink traversal

From: Al Viro <[email protected]>

symlink body shouldn't be freed without an RCU delay. Switch securityfs
to ->destroy_inode() and use of call_rcu(); free both the inode and symlink
body in the callback.

Signed-off-by: Al Viro <[email protected]>
---
security/inode.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/security/inode.c b/security/inode.c
index b7772a9b315e..421dd72b5876 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -27,17 +27,22 @@
static struct vfsmount *mount;
static int mount_count;

-static void securityfs_evict_inode(struct inode *inode)
+static void securityfs_i_callback(struct rcu_head *head)
{
- truncate_inode_pages_final(&inode->i_data);
- clear_inode(inode);
+ struct inode *inode = container_of(head, struct inode, i_rcu);
if (S_ISLNK(inode->i_mode))
kfree(inode->i_link);
+ free_inode_nonrcu(inode);
+}
+
+static void securityfs_destroy_inode(struct inode *inode)
+{
+ call_rcu(&inode->i_rcu, securityfs_i_callback);
}

static const struct super_operations securityfs_super_operations = {
.statfs = simple_statfs,
- .evict_inode = securityfs_evict_inode,
+ .destroy_inode = securityfs_destroy_inode,
};

static int fill_super(struct super_block *sb, void *data, int silent)
--
2.11.0

2019-04-16 18:03:51

by Linus Torvalds

[permalink] [raw]
Subject: Re: [RFC][PATCHSET] sorting out RCU-delayed stuff in ->destroy_inode()

On Tue, Apr 16, 2019 at 10:49 AM Al Viro <[email protected]> wrote:
>
> 83 files changed, 241 insertions(+), 516 deletions(-)

I think this single line is pretty convincing on its own. Ignoring
docs and fs/inode.c, we have

80 files changed, 190 insertions(+), 494 deletions(-)

IOW, just over 300 lines of boiler plate code removed.

The additions are

- Ten more lines of actual code in fs/inode.c (and that's not
actually added complexity, it looks simpler if anything - most of it
is the new "i_callback()" helper function)

- 19 lines of doc updates.

So it absolutely looks fine to me.

I only skimmed through the actual filesystem (and one networking)
patches, but they looked like trivial conversions to a better
interface.

Linus

2019-04-16 18:08:21

by Alexei Starovoitov

[permalink] [raw]
Subject: Re: [RFC PATCH 44/62] bpf: switch to ->free_inode()

On Tue, Apr 16, 2019 at 10:55 AM Al Viro <[email protected]> wrote:
>
> From: Al Viro <[email protected]>
>
> Signed-off-by: Al Viro <[email protected]>

Acked-by: Alexei Starovoitov <[email protected]>

> ---
> kernel/bpf/inode.c | 10 ++--------
> 1 file changed, 2 insertions(+), 8 deletions(-)
>
> diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c
> index 4a8f390a2b82..bc53e5b20ddc 100644
> --- a/kernel/bpf/inode.c
> +++ b/kernel/bpf/inode.c
> @@ -566,9 +566,8 @@ static int bpf_show_options(struct seq_file *m, struct dentry *root)
> return 0;
> }
>
> -static void bpf_destroy_inode_deferred(struct rcu_head *head)
> +static void bpf_free_inode(struct inode *inode)
> {
> - struct inode *inode = container_of(head, struct inode, i_rcu);
> enum bpf_type type;
>
> if (S_ISLNK(inode->i_mode))
> @@ -578,16 +577,11 @@ static void bpf_destroy_inode_deferred(struct rcu_head *head)
> free_inode_nonrcu(inode);
> }
>
> -static void bpf_destroy_inode(struct inode *inode)
> -{
> - call_rcu(&inode->i_rcu, bpf_destroy_inode_deferred);
> -}
> -
> static const struct super_operations bpf_super_ops = {
> .statfs = simple_statfs,
> .drop_inode = generic_delete_inode,
> .show_options = bpf_show_options,
> - .destroy_inode = bpf_destroy_inode,
> + .free_inode = bpf_free_inode,
> };
>
> enum {
> --
> 2.11.0
>

2019-04-16 21:35:50

by Song Liu

[permalink] [raw]
Subject: Re: [RFC PATCH 44/62] bpf: switch to ->free_inode()

On Tue, Apr 16, 2019 at 11:07 AM Alexei Starovoitov
<[email protected]> wrote:
>
> On Tue, Apr 16, 2019 at 10:55 AM Al Viro <[email protected]> wrote:
> >
> > From: Al Viro <[email protected]>
> >
> > Signed-off-by: Al Viro <[email protected]>
>
> Acked-by: Alexei Starovoitov <[email protected]>

Acked-by: Song Liu <[email protected]>

>
> > ---
> > kernel/bpf/inode.c | 10 ++--------
> > 1 file changed, 2 insertions(+), 8 deletions(-)
> >
> > diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c
> > index 4a8f390a2b82..bc53e5b20ddc 100644
> > --- a/kernel/bpf/inode.c
> > +++ b/kernel/bpf/inode.c
> > @@ -566,9 +566,8 @@ static int bpf_show_options(struct seq_file *m, struct dentry *root)
> > return 0;
> > }
> >
> > -static void bpf_destroy_inode_deferred(struct rcu_head *head)
> > +static void bpf_free_inode(struct inode *inode)
> > {
> > - struct inode *inode = container_of(head, struct inode, i_rcu);
> > enum bpf_type type;
> >
> > if (S_ISLNK(inode->i_mode))
> > @@ -578,16 +577,11 @@ static void bpf_destroy_inode_deferred(struct rcu_head *head)
> > free_inode_nonrcu(inode);
> > }
> >
> > -static void bpf_destroy_inode(struct inode *inode)
> > -{
> > - call_rcu(&inode->i_rcu, bpf_destroy_inode_deferred);
> > -}
> > -
> > static const struct super_operations bpf_super_ops = {
> > .statfs = simple_statfs,
> > .drop_inode = generic_delete_inode,
> > .show_options = bpf_show_options,
> > - .destroy_inode = bpf_destroy_inode,
> > + .free_inode = bpf_free_inode,
> > };
> >
> > enum {
> > --
> > 2.11.0
> >

2019-04-17 15:55:21

by David Sterba

[permalink] [raw]
Subject: Re: [RFC][PATCHSET] sorting out RCU-delayed stuff in ->destroy_inode()

On Tue, Apr 16, 2019 at 06:49:00PM +0100, Al Viro wrote:
> We have a lot of boilerplate in ->destroy_inode()
> instances, and several filesystems got the things wrong
> in that area. The patchset below attempts to deal with that.
>
> New method (void ->free_inode(inode)) is introduced,
> and RCU-delayed parts of ->destroy_inode() are moved there.
> The change is backwards-compatible - unmodified filesystem
> will behave as it used to. Rules:
> ->destroy_inode ->free_inode
> f g f(), rcu-delayed g()
> f NULL f()
> NULL g rcu-delayed g()
> NULL NULL rcu-delayed free_inode_nonrcu()
> IOW, NULL/NULL acts as NULL/free_inode_nonrcu.
>
> For a lot of filesystems ->destroy_inode() used to consist
> only of call_rcu(foo_i_callback, &inode->i_rcu). Those simply get
> rid of ->destroy_inode() and have the callback (with saner prototype)
> become their ->free_inode().

The simplified API looks good to me. For btrfs and affs bits:

Acked-by: David Sterba <[email protected]>

2019-04-18 12:13:35

by Jan Kara

[permalink] [raw]
Subject: Re: [RFC PATCH 54/62] ext4: make use of ->free_inode()

On Tue 16-04-19 18:53:32, Al Viro wrote:
> From: Al Viro <[email protected]>
>
> the rest of this ->destroy_inode() instance could probably be folded
> into ext4_evict_inode()
>
> Signed-off-by: Al Viro <[email protected]>

Looks good to me. You can add:

Reviewed-by: Jan Kara <[email protected]>

You're right about the possibility of moving the check to
ext4_evict_inode() (probably ext4_clear_inode() would be the best). But we
can leave that for later.

Honza

> ---
> fs/ext4/super.c | 5 ++---
> 1 file changed, 2 insertions(+), 3 deletions(-)
>
> diff --git a/fs/ext4/super.c b/fs/ext4/super.c
> index 6ed4eb81e674..981f702848e7 100644
> --- a/fs/ext4/super.c
> +++ b/fs/ext4/super.c
> @@ -1107,9 +1107,8 @@ static int ext4_drop_inode(struct inode *inode)
> return drop;
> }
>
> -static void ext4_i_callback(struct rcu_head *head)
> +static void ext4_free_in_core_inode(struct inode *inode)
> {
> - struct inode *inode = container_of(head, struct inode, i_rcu);
> kmem_cache_free(ext4_inode_cachep, EXT4_I(inode));
> }
>
> @@ -1124,7 +1123,6 @@ static void ext4_destroy_inode(struct inode *inode)
> true);
> dump_stack();
> }
> - call_rcu(&inode->i_rcu, ext4_i_callback);
> }
>
> static void init_once(void *foo)
> @@ -1402,6 +1400,7 @@ static const struct quotactl_ops ext4_qctl_operations = {
>
> static const struct super_operations ext4_sops = {
> .alloc_inode = ext4_alloc_inode,
> + .free_inode = ext4_free_in_core_inode,
> .destroy_inode = ext4_destroy_inode,
> .write_inode = ext4_write_inode,
> .dirty_inode = ext4_dirty_inode,
> --
> 2.11.0
>
--
Jan Kara <[email protected]>
SUSE Labs, CR

2019-04-18 12:19:32

by Jan Kara

[permalink] [raw]
Subject: Re: [RFC PATCH 49/62] dax: make use of ->free_inode()

On Tue 16-04-19 18:53:27, Al Viro wrote:
> From: Al Viro <[email protected]>
>
> we might want to drop ->destroy_inode() there - it's used only for
> WARN_ON() now, and AFAICS that could be moved to ->evict_inode()
> if we had one...
>
> Signed-off-by: Al Viro <[email protected]>
> ---
> drivers/dax/super.c | 7 ++-----
> 1 file changed, 2 insertions(+), 5 deletions(-)

Added Dan to CC since I'm not sure how closely he follows fsdevel. The
patch looks good to me FWIW so feel free to add:

Reviewed-by: Jan Kara <[email protected]>

Honza


>
> diff --git a/drivers/dax/super.c b/drivers/dax/super.c
> index 0a339b85133e..bbd57ca0634a 100644
> --- a/drivers/dax/super.c
> +++ b/drivers/dax/super.c
> @@ -412,11 +412,9 @@ static struct dax_device *to_dax_dev(struct inode *inode)
> return container_of(inode, struct dax_device, inode);
> }
>
> -static void dax_i_callback(struct rcu_head *head)
> +static void dax_free_inode(struct inode *inode)
> {
> - struct inode *inode = container_of(head, struct inode, i_rcu);
> struct dax_device *dax_dev = to_dax_dev(inode);
> -
> kfree(dax_dev->host);
> dax_dev->host = NULL;
> if (inode->i_rdev)
> @@ -427,16 +425,15 @@ static void dax_i_callback(struct rcu_head *head)
> static void dax_destroy_inode(struct inode *inode)
> {
> struct dax_device *dax_dev = to_dax_dev(inode);
> -
> WARN_ONCE(test_bit(DAXDEV_ALIVE, &dax_dev->flags),
> "kill_dax() must be called before final iput()\n");
> - call_rcu(&inode->i_rcu, dax_i_callback);
> }
>
> static const struct super_operations dax_sops = {
> .statfs = simple_statfs,
> .alloc_inode = dax_alloc_inode,
> .destroy_inode = dax_destroy_inode,
> + .free_inode = dax_free_inode,
> .drop_inode = generic_delete_inode,
> };
>
> --
> 2.11.0
>
--
Jan Kara <[email protected]>
SUSE Labs, CR

2019-04-18 14:05:19

by Gao Xiang

[permalink] [raw]
Subject: Re: [RFC PATCH 05/62] erofs: switch to ->free_inode()



On 2019/4/17 1:52, Al Viro wrote:
> From: Al Viro <[email protected]>
>
> Signed-off-by: Al Viro <[email protected]>

It seems more clear than before :) Thanks for taking erofs into consideration as well,

Acked-by: Gao Xiang <[email protected]>

Thanks,
Gao Xiang

> ---
> drivers/staging/erofs/super.c | 10 ++--------
> 1 file changed, 2 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/staging/erofs/super.c b/drivers/staging/erofs/super.c
> index 15c784fba879..700cbd460807 100644
> --- a/drivers/staging/erofs/super.c
> +++ b/drivers/staging/erofs/super.c
> @@ -57,9 +57,8 @@ static struct inode *alloc_inode(struct super_block *sb)
> return &vi->vfs_inode;
> }
>
> -static void i_callback(struct rcu_head *head)
> +static void free_inode(struct inode *inode)
> {
> - struct inode *inode = container_of(head, struct inode, i_rcu);
> struct erofs_vnode *vi = EROFS_V(inode);
>
> /* be careful RCU symlink path (see ext4_inode_info->i_data)! */
> @@ -71,11 +70,6 @@ static void i_callback(struct rcu_head *head)
> kmem_cache_free(erofs_inode_cachep, vi);
> }
>
> -static void destroy_inode(struct inode *inode)
> -{
> - call_rcu(&inode->i_rcu, i_callback);
> -}
> -
> static int superblock_read(struct super_block *sb)
> {
> struct erofs_sb_info *sbi;
> @@ -668,7 +662,7 @@ static int erofs_remount(struct super_block *sb, int *flags, char *data)
> const struct super_operations erofs_sops = {
> .put_super = erofs_put_super,
> .alloc_inode = alloc_inode,
> - .destroy_inode = destroy_inode,
> + .free_inode = free_inode,
> .statfs = erofs_statfs,
> .show_options = erofs_show_options,
> .remount_fs = erofs_remount,
>

2019-04-18 17:01:44

by Dan Williams

[permalink] [raw]
Subject: Re: [RFC PATCH 49/62] dax: make use of ->free_inode()

On Thu, Apr 18, 2019 at 5:16 AM Jan Kara <[email protected]> wrote:
>
> On Tue 16-04-19 18:53:27, Al Viro wrote:
> > From: Al Viro <[email protected]>
> >
> > we might want to drop ->destroy_inode() there - it's used only for
> > WARN_ON() now, and AFAICS that could be moved to ->evict_inode()
> > if we had one...
> >
> > Signed-off-by: Al Viro <[email protected]>
> > ---
> > drivers/dax/super.c | 7 ++-----
> > 1 file changed, 2 insertions(+), 5 deletions(-)
>
> Added Dan to CC since I'm not sure how closely he follows fsdevel. The
> patch looks good to me FWIW so feel free to add:
>
> Reviewed-by: Jan Kara <[email protected]>

Thanks Jan.

Acked-by: Dan Williams <[email protected]>

Al, I assume you'll merge this with the other free_inode changes.

2019-04-20 02:56:59

by Chao Yu

[permalink] [raw]
Subject: Re: [RFC PATCH 16/62] f2fs: switch to ->free_inode()

On 2019/4/17 1:52, Al Viro wrote:
> From: Al Viro <[email protected]>
>
> Signed-off-by: Al Viro <[email protected]>

Acked-by: Chao Yu <[email protected]>

Thanks,

2019-04-23 01:08:23

by Mike Marshall

[permalink] [raw]
Subject: Re: [RFC PATCH 60/62] orangefs: make use of ->free_inode()

Hi Al...

I applied your "new inode method: ->free_inode()" and
"orangefs: make use of ->free_inode()" to our pagecache
branch (I hope to get it pulled in the next merge window).

I had to modify your "orangefs: make use of ->free_inode()" a
little, since Martin Brandenburg had already modified orangefs_i_callback
for the pagecache branch. I don't know for sure that my modifications
aren't nonsense :-) but I do know for sure that everything runs
with no xfstests regressions. I'll see what Martin thinks about
my changes...

orangefs_destroy_inode is pretty much a no-op now, so I guess
we'll get rid of it...

Acked-by: Mike Marshall <[email protected]>

Thanks...

-Mike


[root@vm1 linux]# git diff HEAD^ fs/orangefs/super.c
diff --git a/fs/orangefs/super.c b/fs/orangefs/super.c
index 8fa30c13b7ed..f82ac9373443 100644
--- a/fs/orangefs/super.c
+++ b/fs/orangefs/super.c
@@ -125,20 +125,18 @@ static struct inode *orangefs_alloc_inode(struct
super_block *sb)
return &orangefs_inode->vfs_inode;
}

-static void orangefs_i_callback(struct rcu_head *head)
+static void orangefs_free_inode(struct inode *inode)
{
- struct inode *inode = container_of(head, struct inode, i_rcu);
- struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
struct orangefs_cached_xattr *cx;
struct hlist_node *tmp;
int i;

- hash_for_each_safe(orangefs_inode->xattr_cache, i, tmp, cx, node) {
+ hash_for_each_safe(ORANGEFS_I(inode)->xattr_cache, i, tmp, cx, node) {
hlist_del(&cx->node);
kfree(cx);
}

- kmem_cache_free(orangefs_inode_cache, orangefs_inode);
+ kmem_cache_free(orangefs_inode_cache, ORANGEFS_I(inode));
}

static void orangefs_destroy_inode(struct inode *inode)
@@ -148,8 +146,6 @@ static void orangefs_destroy_inode(struct inode *inode)
gossip_debug(GOSSIP_SUPER_DEBUG,
"%s: deallocated %p destroying inode %pU\n",
__func__, orangefs_inode, get_khandle_from_ino(inode));
-
- call_rcu(&inode->i_rcu, orangefs_i_callback);
}

static int orangefs_write_inode(struct inode *inode,
@@ -316,6 +312,7 @@ void fsid_key_table_finalize(void)

static const struct super_operations orangefs_s_ops = {
.alloc_inode = orangefs_alloc_inode,
+ .free_inode = orangefs_free_inode,
.destroy_inode = orangefs_destroy_inode,
.write_inode = orangefs_write_inode,
.drop_inode = generic_delete_inode,

On Tue, Apr 16, 2019 at 1:55 PM Al Viro <[email protected]> wrote:
>
> From: Al Viro <[email protected]>
>
> Signed-off-by: Al Viro <[email protected]>
> ---
> fs/orangefs/super.c | 9 +++------
> 1 file changed, 3 insertions(+), 6 deletions(-)
>
> diff --git a/fs/orangefs/super.c b/fs/orangefs/super.c
> index dfaee90d30bd..3784f7e8b603 100644
> --- a/fs/orangefs/super.c
> +++ b/fs/orangefs/super.c
> @@ -124,11 +124,9 @@ static struct inode *orangefs_alloc_inode(struct super_block *sb)
> return &orangefs_inode->vfs_inode;
> }
>
> -static void orangefs_i_callback(struct rcu_head *head)
> +static void orangefs_free_inode(struct inode *inode)
> {
> - struct inode *inode = container_of(head, struct inode, i_rcu);
> - struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
> - kmem_cache_free(orangefs_inode_cache, orangefs_inode);
> + kmem_cache_free(orangefs_inode_cache, ORANGEFS_I(inode));
> }
>
> static void orangefs_destroy_inode(struct inode *inode)
> @@ -138,8 +136,6 @@ static void orangefs_destroy_inode(struct inode *inode)
> gossip_debug(GOSSIP_SUPER_DEBUG,
> "%s: deallocated %p destroying inode %pU\n",
> __func__, orangefs_inode, get_khandle_from_ino(inode));
> -
> - call_rcu(&inode->i_rcu, orangefs_i_callback);
> }
>
> /*
> @@ -299,6 +295,7 @@ void fsid_key_table_finalize(void)
>
> static const struct super_operations orangefs_s_ops = {
> .alloc_inode = orangefs_alloc_inode,
> + .free_inode = orangefs_free_inode,
> .destroy_inode = orangefs_destroy_inode,
> .drop_inode = generic_delete_inode,
> .statfs = orangefs_statfs,
> --
> 2.11.0
>

2019-04-23 01:24:58

by Linus Torvalds

[permalink] [raw]
Subject: Re: [RFC PATCH 60/62] orangefs: make use of ->free_inode()

On Mon, Apr 22, 2019 at 2:14 PM Mike Marshall <[email protected]> wrote:
>
> I applied your "new inode method: ->free_inode()" and
> "orangefs: make use of ->free_inode()" to our pagecache
> branch (I hope to get it pulled in the next merge window).

Actually, please don't.

Exactly because this needs that common vfs patch, I'd really prefer to
get it all through Al's tree, rather than have individual filesystems
apply their own copies of the common infrastructure commit, and then
apply their changes on top of that.

I can easily handle any trivial conflicts this causes, so that's not a
reason to have each filesystem do it either.

So if this is at the top of your tree, can you just "git reset" it
away and I'll get all the filesystems (and the common infrastructure
commit) all together from Al.

Linus

2019-04-23 03:29:17

by Al Viro

[permalink] [raw]
Subject: Re: [RFC PATCH 60/62] orangefs: make use of ->free_inode()

On Mon, Apr 22, 2019 at 02:56:57PM -0700, Linus Torvalds wrote:
> On Mon, Apr 22, 2019 at 2:14 PM Mike Marshall <[email protected]> wrote:
> >
> > I applied your "new inode method: ->free_inode()" and
> > "orangefs: make use of ->free_inode()" to our pagecache
> > branch (I hope to get it pulled in the next merge window).
>
> Actually, please don't.
>
> Exactly because this needs that common vfs patch, I'd really prefer to
> get it all through Al's tree, rather than have individual filesystems
> apply their own copies of the common infrastructure commit, and then
> apply their changes on top of that.
>
> I can easily handle any trivial conflicts this causes, so that's not a
> reason to have each filesystem do it either.
>
> So if this is at the top of your tree, can you just "git reset" it
> away and I'll get all the filesystems (and the common infrastructure
> commit) all together from Al.

What's more, seeing the changes in orangefs tree I would rather have
static void orangefs_free_inode(struct inode *inode)
{
struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
kmem_cache_free(orangefs_inode_cache, orangefs_inode);
}

in that series; not only less noise on merge, but with additional
uses of orangefs_inode in the body from orangefs tree changes
keeping the local variable clearly makes sense...

2019-04-23 03:54:01

by Mike Marshall

[permalink] [raw]
Subject: Re: [RFC PATCH 60/62] orangefs: make use of ->free_inode()

Hi Linus and Al...

I just wanted Al to know I tested his patch and acked it and that it
there would be
a conflict if our pagecache code got pulled... I wasn't suggesting that I
should get that one part of Al's patch pulled...

>> I can easily handle any trivial conflicts this causes...

Thanks :-)

-Mike

On Mon, Apr 22, 2019 at 7:10 PM Al Viro <[email protected]> wrote:
>
> On Mon, Apr 22, 2019 at 02:56:57PM -0700, Linus Torvalds wrote:
> > On Mon, Apr 22, 2019 at 2:14 PM Mike Marshall <[email protected]> wrote:
> > >
> > > I applied your "new inode method: ->free_inode()" and
> > > "orangefs: make use of ->free_inode()" to our pagecache
> > > branch (I hope to get it pulled in the next merge window).
> >
> > Actually, please don't.
> >
> > Exactly because this needs that common vfs patch, I'd really prefer to
> > get it all through Al's tree, rather than have individual filesystems
> > apply their own copies of the common infrastructure commit, and then
> > apply their changes on top of that.
> >
> > I can easily handle any trivial conflicts this causes, so that's not a
> > reason to have each filesystem do it either.
> >
> > So if this is at the top of your tree, can you just "git reset" it
> > away and I'll get all the filesystems (and the common infrastructure
> > commit) all together from Al.
>
> What's more, seeing the changes in orangefs tree I would rather have
> static void orangefs_free_inode(struct inode *inode)
> {
> struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
> kmem_cache_free(orangefs_inode_cache, orangefs_inode);
> }
>
> in that series; not only less noise on merge, but with additional
> uses of orangefs_inode in the body from orangefs tree changes
> keeping the local variable clearly makes sense...

2019-04-30 03:10:49

by Al Viro

[permalink] [raw]
Subject: Re: [RFC][PATCHSET] sorting out RCU-delayed stuff in ->destroy_inode()

On Tue, Apr 16, 2019 at 11:01:16AM -0700, Linus Torvalds wrote:
> On Tue, Apr 16, 2019 at 10:49 AM Al Viro <[email protected]> wrote:
> >
> > 83 files changed, 241 insertions(+), 516 deletions(-)
>
> I think this single line is pretty convincing on its own. Ignoring
> docs and fs/inode.c, we have
>
> 80 files changed, 190 insertions(+), 494 deletions(-)
>
> IOW, just over 300 lines of boiler plate code removed.
>
> The additions are
>
> - Ten more lines of actual code in fs/inode.c (and that's not
> actually added complexity, it looks simpler if anything - most of it
> is the new "i_callback()" helper function)
>
> - 19 lines of doc updates.
>
> So it absolutely looks fine to me.
>
> I only skimmed through the actual filesystem (and one networking)
> patches, but they looked like trivial conversions to a better
> interface.

... except that this callback can (and always could) get executed after
freeing struct super_block. So we can't just dereference ->i_sb->s_op
and expect to survive; the table ->s_op pointed to will still be there,
but ->i_sb might very well have been freed, with all its contents overwritten.
We need to copy the callback into struct inode itself, unfortunately.
The following incremental fixes it; I'm going to fold it into the first
commit in there.

diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting
index 9d80f9e0855e..b8d3ddd8b8db 100644
--- a/Documentation/filesystems/porting
+++ b/Documentation/filesystems/porting
@@ -655,3 +655,11 @@ in your dentry operations instead.
* if ->free_inode() is non-NULL, it gets scheduled by call_rcu()
* combination of NULL ->destroy_inode and NULL ->free_inode is
treated as NULL/free_inode_nonrcu, to preserve the compatibility.
+
+ Note that the callback (be it via ->free_inode() or explicit call_rcu()
+ in ->destroy_inode()) is *NOT* ordered wrt superblock destruction;
+ as the matter of fact, the superblock and all associated structures
+ might be already gone. The filesystem driver is guaranteed to be still
+ there, but that's it. Freeing memory in the callback is fine; doing
+ more than that is possible, but requires a lot of care and is best
+ avoided.
diff --git a/fs/inode.c b/fs/inode.c
index fb45590d284e..855dad43b11d 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -164,6 +164,7 @@ int inode_init_always(struct super_block *sb, struct inode *inode)
inode->i_wb_frn_avg_time = 0;
inode->i_wb_frn_history = 0;
#endif
+ inode->free_inode = sb->s_op->free_inode;

if (security_inode_alloc(inode))
goto out;
@@ -211,8 +212,8 @@ EXPORT_SYMBOL(free_inode_nonrcu);
static void i_callback(struct rcu_head *head)
{
struct inode *inode = container_of(head, struct inode, i_rcu);
- if (inode->i_sb->s_op->free_inode)
- inode->i_sb->s_op->free_inode(inode);
+ if (inode->free_inode)
+ inode->free_inode(inode);
else
free_inode_nonrcu(inode);
}
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 2e9b9f87caca..5ed6b39e588e 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -718,6 +718,7 @@ struct inode {
#endif

void *i_private; /* fs or device private pointer */
+ void (*free_inode)(struct inode *);
} __randomize_layout;

static inline unsigned int i_blocksize(const struct inode *node)

2019-04-30 04:03:12

by Al Viro

[permalink] [raw]
Subject: Re: [RFC][PATCHSET] sorting out RCU-delayed stuff in ->destroy_inode()

On Mon, Apr 29, 2019 at 08:37:29PM -0700, Linus Torvalds wrote:
> On Mon, Apr 29, 2019, 20:09 Al Viro <[email protected]> wrote:
>
> >
> > ... except that this callback can (and always could) get executed after
> > freeing struct super_block.
> >
>
> Ugh.
>
> That food looks nasty. Shouldn't the super block freeing wait for the
> filesystem to be all done instead? Do a rcu synchronization or something?
>
> Adding that pointer looks really wrong to me. I'd much rather delay the sb
> freeing. Is there some reason that can't be done that I'm missing?

Where would you put that synchronize_rcu()? Doing that before ->put_super()
is too early - inode references might be dropped in there. OTOH, doing
that after that point means that while struct super_block itself will be
there, any number of data structures hanging from it might be not.

So we are still very limited in what we can do inside ->free_inode()
instance *and* we get bunch of synchronize_rcu() for no good reason.

Note that for normal lockless accesses (lockless ->d_revalidate(), ->d_hash(),
etc.) we are just fine with having struct super_block freeing RCU-delayed
(along with any data structures we might need) - the superblock had
been seen at some point after we'd taken rcu_read_lock(), so its
freeing won't happen until we drop it. So we don't need synchronize_rcu()
for that.

Here the problem is that we are dealing with another RCU callback;
synchronize_rcu() would be needed for it, but it will only protect that
intermediate dereference of ->i_sb; any rcu-delayed stuff scheduled
from inside ->put_super() would not be ordered wrt ->free_inode().
And if we are doing that just for the sake of that one dereference,
we might as well do it before scheduling i_callback().

PS: we *are* guaranteed that module will still be there (unregister_filesystem()
does synchronize_rcu() and rcu_barrier() is done before kmem_cache_destroy()
in assorted exit_foo_fs()).

2019-04-30 04:21:31

by Andreas Dilger

[permalink] [raw]
Subject: Re: [RFC][PATCHSET] sorting out RCU-delayed stuff in ->destroy_inode()

On Apr 29, 2019, at 9:09 PM, Al Viro <[email protected]> wrote:
>
> On Tue, Apr 16, 2019 at 11:01:16AM -0700, Linus Torvalds wrote:
>>
>> I only skimmed through the actual filesystem (and one networking)
>> patches, but they looked like trivial conversions to a better
>> interface.
>
> ... except that this callback can (and always could) get executed after
> freeing struct super_block. So we can't just dereference ->i_sb->s_op
> and expect to survive; the table ->s_op pointed to will still be there,
> but ->i_sb might very well have been freed, with all its contents overwritten.
> We need to copy the callback into struct inode itself, unfortunately.
> The following incremental fixes it; I'm going to fold it into the first
> commit in there.
>
> diff --git a/fs/inode.c b/fs/inode.c
> index fb45590d284e..855dad43b11d 100644
> --- a/fs/inode.c
> +++ b/fs/inode.c
> @@ -164,6 +164,7 @@ int inode_init_always(struct super_block *sb, struct inode *inode)
> inode->i_wb_frn_avg_time = 0;
> inode->i_wb_frn_history = 0;
> #endif
> + inode->free_inode = sb->s_op->free_inode;
>
> if (security_inode_alloc(inode))
> goto out;
> @@ -211,8 +212,8 @@ EXPORT_SYMBOL(free_inode_nonrcu);
> static void i_callback(struct rcu_head *head)
> {
> struct inode *inode = container_of(head, struct inode, i_rcu);
> - if (inode->i_sb->s_op->free_inode)
> - inode->i_sb->s_op->free_inode(inode);
> + if (inode->free_inode)
> + inode->free_inode(inode);
> else
> free_inode_nonrcu(inode);
> }
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index 2e9b9f87caca..5ed6b39e588e 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -718,6 +718,7 @@ struct inode {
> #endif
>
> void *i_private; /* fs or device private pointer */
> + void (*free_inode)(struct inode *);

It seems like a waste to increase the size of every struct inode just to access
a static pointer. Is this the only place that ->free_inode() is called? Why
not move the ->free_inode() pointer into inode->i_fop->free_inode() so that it
is still directly accessible at this point.

Cheers, Andreas






Attachments:
signature.asc (890.00 B)
Message signed with OpenPGP

2019-04-30 04:29:20

by Al Viro

[permalink] [raw]
Subject: Re: [RFC][PATCHSET] sorting out RCU-delayed stuff in ->destroy_inode()

On Mon, Apr 29, 2019 at 10:18:04PM -0600, Andreas Dilger wrote:
> >
> > void *i_private; /* fs or device private pointer */
> > + void (*free_inode)(struct inode *);
>
> It seems like a waste to increase the size of every struct inode just to access
> a static pointer. Is this the only place that ->free_inode() is called? Why
> not move the ->free_inode() pointer into inode->i_fop->free_inode() so that it
> is still directly accessible at this point.

i_op, surely? In any case, increasing sizeof(struct inode) is not a problem -
if anything, I'd turn ->i_fop into an anon union with that. As in,

diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting
index 9d80f9e0855e..b8d3ddd8b8db 100644
--- a/Documentation/filesystems/porting
+++ b/Documentation/filesystems/porting
@@ -655,3 +655,11 @@ in your dentry operations instead.
* if ->free_inode() is non-NULL, it gets scheduled by call_rcu()
* combination of NULL ->destroy_inode and NULL ->free_inode is
treated as NULL/free_inode_nonrcu, to preserve the compatibility.
+
+ Note that the callback (be it via ->free_inode() or explicit call_rcu()
+ in ->destroy_inode()) is *NOT* ordered wrt superblock destruction;
+ as the matter of fact, the superblock and all associated structures
+ might be already gone. The filesystem driver is guaranteed to be still
+ there, but that's it. Freeing memory in the callback is fine; doing
+ more than that is possible, but requires a lot of care and is best
+ avoided.
diff --git a/fs/inode.c b/fs/inode.c
index fb45590d284e..627e1766503a 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -211,8 +211,8 @@ EXPORT_SYMBOL(free_inode_nonrcu);
static void i_callback(struct rcu_head *head)
{
struct inode *inode = container_of(head, struct inode, i_rcu);
- if (inode->i_sb->s_op->free_inode)
- inode->i_sb->s_op->free_inode(inode);
+ if (inode->free_inode)
+ inode->free_inode(inode);
else
free_inode_nonrcu(inode);
}
@@ -236,6 +236,7 @@ static struct inode *alloc_inode(struct super_block *sb)
if (!ops->free_inode)
return NULL;
}
+ inode->free_inode = ops->free_inode;
i_callback(&inode->i_rcu);
return NULL;
}
@@ -276,6 +277,7 @@ static void destroy_inode(struct inode *inode)
if (!ops->free_inode)
return;
}
+ inode->free_inode = ops->free_inode;
call_rcu(&inode->i_rcu, i_callback);
}

diff --git a/include/linux/fs.h b/include/linux/fs.h
index 2e9b9f87caca..92732286b748 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -694,7 +694,10 @@ struct inode {
#ifdef CONFIG_IMA
atomic_t i_readcount; /* struct files open RO */
#endif
- const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
+ union {
+ const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
+ void (*free_inode)(struct inode *);
+ };
struct file_lock_context *i_flctx;
struct address_space i_data;
struct list_head i_devices;

2019-04-30 05:27:33

by Andreas Dilger

[permalink] [raw]
Subject: Re: [RFC][PATCHSET] sorting out RCU-delayed stuff in ->destroy_inode()


> On Apr 29, 2019, at 10:26 PM, Al Viro <[email protected]> wrote:
>
> On Mon, Apr 29, 2019 at 10:18:04PM -0600, Andreas Dilger wrote:
>>>
>>> void *i_private; /* fs or device private pointer */
>>> + void (*free_inode)(struct inode *);
>>
>> It seems like a waste to increase the size of every struct inode just to access
>> a static pointer. Is this the only place that ->free_inode() is called? Why
>> not move the ->free_inode() pointer into inode->i_fop->free_inode() so that it
>> is still directly accessible at this point.
>
> i_op, surely?

Yes, i_op is what I was thinking.

> In any case, increasing sizeof(struct inode) is not a problem -

> if anything, I'd turn ->i_fop into an anon union with that. As in,
>
> diff --git a/fs/inode.c b/fs/inode.c
> index fb45590d284e..627e1766503a 100644
> --- a/fs/inode.c
> +++ b/fs/inode.c
> @@ -211,8 +211,8 @@ EXPORT_SYMBOL(free_inode_nonrcu);
> static void i_callback(struct rcu_head *head)
> {
> struct inode *inode = container_of(head, struct inode, i_rcu);
> - if (inode->i_sb->s_op->free_inode)
> - inode->i_sb->s_op->free_inode(inode);
> + if (inode->free_inode)
> + inode->free_inode(inode);
> else
> free_inode_nonrcu(inode);
> }
> @@ -236,6 +236,7 @@ static struct inode *alloc_inode(struct super_block *sb)
> if (!ops->free_inode)
> return NULL;
> }
> + inode->free_inode = ops->free_inode;
> i_callback(&inode->i_rcu);
> return NULL;
> }

> @@ -276,6 +277,7 @@ static void destroy_inode(struct inode *inode)
> if (!ops->free_inode)
> return;
> }
> + inode->free_inode = ops->free_inode;
> call_rcu(&inode->i_rcu, i_callback);
> }

This seems like kind of a hack. I guess your goal is to have ->free_inode
accessible regardless of whether the filesystem has installed its own ->i_op
methods or not, and i_fop is no longer used by this point.

That said, this seems better than increasing the size of struct inode.

> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index 2e9b9f87caca..92732286b748 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -694,7 +694,10 @@ struct inode {
> #ifdef CONFIG_IMA
> atomic_t i_readcount; /* struct files open RO */
> #endif
> - const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
> + union {
> + const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
> + void (*free_inode)(struct inode *);
> + };


Cheers, Andreas






Attachments:
signature.asc (890.00 B)
Message signed with OpenPGP

2019-05-01 02:00:25

by Al Viro

[permalink] [raw]
Subject: Re: [RFC][PATCHSET] sorting out RCU-delayed stuff in ->destroy_inode()

On Tue, Apr 30, 2019 at 05:00:43AM +0100, Al Viro wrote:

> Where would you put that synchronize_rcu()? Doing that before ->put_super()
> is too early - inode references might be dropped in there. OTOH, doing
> that after that point means that while struct super_block itself will be
> there, any number of data structures hanging from it might be not.
>
> So we are still very limited in what we can do inside ->free_inode()
> instance *and* we get bunch of synchronize_rcu() for no good reason.
>
> Note that for normal lockless accesses (lockless ->d_revalidate(), ->d_hash(),
> etc.) we are just fine with having struct super_block freeing RCU-delayed
> (along with any data structures we might need) - the superblock had
> been seen at some point after we'd taken rcu_read_lock(), so its
> freeing won't happen until we drop it. So we don't need synchronize_rcu()
> for that.
>
> Here the problem is that we are dealing with another RCU callback;
> synchronize_rcu() would be needed for it, but it will only protect that
> intermediate dereference of ->i_sb; any rcu-delayed stuff scheduled
> from inside ->put_super() would not be ordered wrt ->free_inode().
> And if we are doing that just for the sake of that one dereference,
> we might as well do it before scheduling i_callback().
>
> PS: we *are* guaranteed that module will still be there (unregister_filesystem()
> does synchronize_rcu() and rcu_barrier() is done before kmem_cache_destroy()
> in assorted exit_foo_fs()).

After playing with that for a while, I think that adding barriers on
superblock freeing (or shutdown) should wait, assuming we do them at
all.

Right now no ->free_inode() instances look at superblock or anything
associated with it; moreover, there's no good candidate code that
could be moved there and would benefit from such access. So we
don't have any material to see what could be useful to protect.

Access to ->i_sb->s_op->free_inode itself is the only exception and
moving that to before the rcu delay is both less invasive and a _lot_
more robust than playing with synchronize_rcu(). We can do that
without growing struct inode or storing it for long periods -
->i_fop is only accessed for struct inode with positive refcount,
so we can put that into anon union with the ->free_inode value,
setting it just before we schedule execution of i_callback()
(and before the direct call of the same in alloc_inode() failure
exit).

IMO the following is the sane incremental for the coming window purposes;
if we get a convincing case for ->free_inode() doing something that could
benefit from being ordered wrt parts of fs shutdown, we can always deal
with synchronize_rcu() later. Existing instances will be fine, and IMO
separating RCU-delayed parts of inode destruction from the rest is
worthwhile on its own.

Objections?

diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting
index 9d80f9e0855e..b8d3ddd8b8db 100644
--- a/Documentation/filesystems/porting
+++ b/Documentation/filesystems/porting
@@ -655,3 +655,11 @@ in your dentry operations instead.
* if ->free_inode() is non-NULL, it gets scheduled by call_rcu()
* combination of NULL ->destroy_inode and NULL ->free_inode is
treated as NULL/free_inode_nonrcu, to preserve the compatibility.
+
+ Note that the callback (be it via ->free_inode() or explicit call_rcu()
+ in ->destroy_inode()) is *NOT* ordered wrt superblock destruction;
+ as the matter of fact, the superblock and all associated structures
+ might be already gone. The filesystem driver is guaranteed to be still
+ there, but that's it. Freeing memory in the callback is fine; doing
+ more than that is possible, but requires a lot of care and is best
+ avoided.
diff --git a/fs/inode.c b/fs/inode.c
index fb45590d284e..627e1766503a 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -211,8 +211,8 @@ EXPORT_SYMBOL(free_inode_nonrcu);
static void i_callback(struct rcu_head *head)
{
struct inode *inode = container_of(head, struct inode, i_rcu);
- if (inode->i_sb->s_op->free_inode)
- inode->i_sb->s_op->free_inode(inode);
+ if (inode->free_inode)
+ inode->free_inode(inode);
else
free_inode_nonrcu(inode);
}
@@ -236,6 +236,7 @@ static struct inode *alloc_inode(struct super_block *sb)
if (!ops->free_inode)
return NULL;
}
+ inode->free_inode = ops->free_inode;
i_callback(&inode->i_rcu);
return NULL;
}
@@ -276,6 +277,7 @@ static void destroy_inode(struct inode *inode)
if (!ops->free_inode)
return;
}
+ inode->free_inode = ops->free_inode;
call_rcu(&inode->i_rcu, i_callback);
}

diff --git a/include/linux/fs.h b/include/linux/fs.h
index 2e9b9f87caca..92732286b748 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -694,7 +694,10 @@ struct inode {
#ifdef CONFIG_IMA
atomic_t i_readcount; /* struct files open RO */
#endif
- const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
+ union {
+ const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
+ void (*free_inode)(struct inode *);
+ };
struct file_lock_context *i_flctx;
struct address_space i_data;
struct list_head i_devices;