FAILED: patch "[PATCH] mm,vmscan: Allow preallocating memory for" failed to apply to 3.18-stable tree
Subsystems (6)
[email protected]
maintainerThe patch below does not apply to the 3.18-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
Quoted 1 line
From
8e04944f0ea8b838399049bdcda920ab36ae3b04Mon Sep 17 00:00:00 2001
From: Tetsuo Handa <[email protected]>
Date: Wed, 4 Apr 2018 19:53:07 +0900
Subject: [PATCH] mm,vmscan: Allow preallocating memory for
register_shrinker().
syzbot is catching so many bugs triggered by commit 9ee332d99e4d5a97 ("sget(): handle failures of register_shrinker()"). That commit expected that calling kill_sb() from deactivate_locked_super() without successful fill_super() is safe, but the reality was different; some callers assign attributes which are needed for kill_sb() after sget() succeeds.
For example, [1] is a report where sb->s_mode (which seems to be either FMODE_READ | FMODE_EXCL | FMODE_WRITE or FMODE_READ | FMODE_EXCL) is not assigned unless sget() succeeds. But it does not worth complicate sget() so that register_shrinker() failure path can safely call kill_block_super() via kill_sb(). Making alloc_super() fail if memory allocation for register_shrinker() failed is much simpler. Let's avoid calling deactivate_locked_super() from sget_userns() by preallocating memory for the shrinker and making register_shrinker() in sget_userns() never fail.
[1] https://syzkaller.appspot.com/bug?id=588996a25a2587be2e3a54e8646728fb9cae44e7
fs/super.c +4 -5
@@ -167,6 +167,7 @@ static void destroy_unused_super(struct super_block *s)security_sb_free (s);put_user_ns (s->s_user_ns);kfree (s->s_subtype); +free_prealloced_shrinker (&s->s_shrink);/* no delays needed */ destroy_super_work (&s->destroy_work); }
@@ -252,6 +253,8 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags, s->s_shrink.count_objects = super_cache_count; s->s_shrink.batch =1024 ; s->s_shrink.flags =SHRINKER_NUMA_AWARE |SHRINKER_MEMCG_AWARE ; +if (prealloc_shrinker (&s->s_shrink)) + goto fail;return s; fail:
@@ -518,11 +521,7 @@ struct super_block *sget_userns(struct file_system_type *type,hlist_add_head (&s->s_instances, &type->fs_supers);spin_unlock (&sb_lock);get_filesystem (type); - err =register_shrinker (&s->s_shrink); -if (err) { -deactivate_locked_super (s); - s =ERR_PTR (err); - } +register_shrinker_prepared (&s->s_shrink);return s; }
include/linux/shrinker.h +5 -2
@@ -75,6 +75,9 @@ struct shrinker {#define SHRINKER_NUMA_AWARE (1 << 0)#define SHRINKER_MEMCG_AWARE (1 << 1) -extern int register_shrinker (struct shrinker *); -extern void unregister_shrinker (struct shrinker *); +extern int prealloc_shrinker (struct shrinker *shrinker); +extern void register_shrinker_prepared (struct shrinker *shrinker); +extern int register_shrinker (struct shrinker *shrinker); +extern void unregister_shrinker (struct shrinker *shrinker); +extern void free_prealloced_shrinker (struct shrinker *shrinker);#endif
mm/vmscan.c +20 -1
@@ -303,7 +303,7 @@ unsigned long lruvec_lru_size(struct lruvec *lruvec, enum lru_list lru, int zone/* * Add a shrinker callback to be called from the vm. */ -int register_shrinker (struct shrinker *shrinker) +int prealloc_shrinker (struct shrinker *shrinker) {size_t size =sizeof (*shrinker->nr_deferred);
@@ -313,10 +313,29 @@ int register_shrinker(struct shrinker *shrinker) shrinker->nr_deferred =kzalloc (size,GFP_KERNEL );if (!shrinker->nr_deferred)return -ENOMEM ; +return 0 ; +} + +void free_prealloced_shrinker (struct shrinker *shrinker) +{ +kfree (shrinker->nr_deferred); + shrinker->nr_deferred =NULL ; +} +void register_shrinker_prepared (struct shrinker *shrinker) +{down_write (&shrinker_rwsem);list_add_tail (&shrinker->list, &shrinker_list);up_write (&shrinker_rwsem); +} + +int register_shrinker (struct shrinker *shrinker) +{ +int err =prealloc_shrinker (shrinker); + +if (err) +return err; +register_shrinker_prepared (shrinker);return 0 ; }EXPORT_SYMBOL (register_shrinker);