diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2019-04-10 14:43:44 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2019-05-01 22:37:39 -0400 |
commit | fdb0da89f4ba0c74d7d3b9e6f471e96a5766820b (patch) | |
tree | 3c82eb1fe04568315a76d03ec86617e206b12a41 /fs/inode.c | |
parent | ad7999cd701e4e058765d35cf5274ee16801e986 (diff) |
new inode method: ->free_inode()
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 <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/inode.c')
-rw-r--r-- | fs/inode.c | 56 |
1 files changed, 34 insertions, 22 deletions
diff --git a/fs/inode.c b/fs/inode.c index e9d97add2b36..627e1766503a 100644 --- a/fs/inode.c +++ b/fs/inode.c | |||
@@ -202,12 +202,28 @@ out: | |||
202 | } | 202 | } |
203 | EXPORT_SYMBOL(inode_init_always); | 203 | EXPORT_SYMBOL(inode_init_always); |
204 | 204 | ||
205 | void free_inode_nonrcu(struct inode *inode) | ||
206 | { | ||
207 | kmem_cache_free(inode_cachep, inode); | ||
208 | } | ||
209 | EXPORT_SYMBOL(free_inode_nonrcu); | ||
210 | |||
211 | static void i_callback(struct rcu_head *head) | ||
212 | { | ||
213 | struct inode *inode = container_of(head, struct inode, i_rcu); | ||
214 | if (inode->free_inode) | ||
215 | inode->free_inode(inode); | ||
216 | else | ||
217 | free_inode_nonrcu(inode); | ||
218 | } | ||
219 | |||
205 | static struct inode *alloc_inode(struct super_block *sb) | 220 | static struct inode *alloc_inode(struct super_block *sb) |
206 | { | 221 | { |
222 | const struct super_operations *ops = sb->s_op; | ||
207 | struct inode *inode; | 223 | struct inode *inode; |
208 | 224 | ||
209 | if (sb->s_op->alloc_inode) | 225 | if (ops->alloc_inode) |
210 | inode = sb->s_op->alloc_inode(sb); | 226 | inode = ops->alloc_inode(sb); |
211 | else | 227 | else |
212 | inode = kmem_cache_alloc(inode_cachep, GFP_KERNEL); | 228 | inode = kmem_cache_alloc(inode_cachep, GFP_KERNEL); |
213 | 229 | ||
@@ -215,22 +231,19 @@ static struct inode *alloc_inode(struct super_block *sb) | |||
215 | return NULL; | 231 | return NULL; |
216 | 232 | ||
217 | if (unlikely(inode_init_always(sb, inode))) { | 233 | if (unlikely(inode_init_always(sb, inode))) { |
218 | if (inode->i_sb->s_op->destroy_inode) | 234 | if (ops->destroy_inode) { |
219 | inode->i_sb->s_op->destroy_inode(inode); | 235 | ops->destroy_inode(inode); |
220 | else | 236 | if (!ops->free_inode) |
221 | kmem_cache_free(inode_cachep, inode); | 237 | return NULL; |
238 | } | ||
239 | inode->free_inode = ops->free_inode; | ||
240 | i_callback(&inode->i_rcu); | ||
222 | return NULL; | 241 | return NULL; |
223 | } | 242 | } |
224 | 243 | ||
225 | return inode; | 244 | return inode; |
226 | } | 245 | } |
227 | 246 | ||
228 | void free_inode_nonrcu(struct inode *inode) | ||
229 | { | ||
230 | kmem_cache_free(inode_cachep, inode); | ||
231 | } | ||
232 | EXPORT_SYMBOL(free_inode_nonrcu); | ||
233 | |||
234 | void __destroy_inode(struct inode *inode) | 247 | void __destroy_inode(struct inode *inode) |
235 | { | 248 | { |
236 | BUG_ON(inode_has_buffers(inode)); | 249 | BUG_ON(inode_has_buffers(inode)); |
@@ -253,20 +266,19 @@ void __destroy_inode(struct inode *inode) | |||
253 | } | 266 | } |
254 | EXPORT_SYMBOL(__destroy_inode); | 267 | EXPORT_SYMBOL(__destroy_inode); |
255 | 268 | ||
256 | static void i_callback(struct rcu_head *head) | ||
257 | { | ||
258 | struct inode *inode = container_of(head, struct inode, i_rcu); | ||
259 | kmem_cache_free(inode_cachep, inode); | ||
260 | } | ||
261 | |||
262 | static void destroy_inode(struct inode *inode) | 269 | static void destroy_inode(struct inode *inode) |
263 | { | 270 | { |
271 | const struct super_operations *ops = inode->i_sb->s_op; | ||
272 | |||
264 | BUG_ON(!list_empty(&inode->i_lru)); | 273 | BUG_ON(!list_empty(&inode->i_lru)); |
265 | __destroy_inode(inode); | 274 | __destroy_inode(inode); |
266 | if (inode->i_sb->s_op->destroy_inode) | 275 | if (ops->destroy_inode) { |
267 | inode->i_sb->s_op->destroy_inode(inode); | 276 | ops->destroy_inode(inode); |
268 | else | 277 | if (!ops->free_inode) |
269 | call_rcu(&inode->i_rcu, i_callback); | 278 | return; |
279 | } | ||
280 | inode->free_inode = ops->free_inode; | ||
281 | call_rcu(&inode->i_rcu, i_callback); | ||
270 | } | 282 | } |
271 | 283 | ||
272 | /** | 284 | /** |