From: Tomas Jedlicka Date: Fri, 28 Jul 2017 11:46:53 +0000 (-0400) Subject: dtrace: modules provide called from rcu atomic section X-Git-Tag: v4.1.12-111.0.20170918_2215~156^2 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=653646c18333c734986db09affa7249c989375a3;p=users%2Fjedix%2Flinux-maple.git dtrace: modules provide called from rcu atomic section The per-module provide callback is called from within RCU read critical section. This results of running proivder code in atomic context which can cause troubles. Mainly due to sleeping allocations calls in this code path. Orabug: 26680982 Signed-off-by: Tomas Jedlicka Reviewed-by: Nick Alcock Acked-by: Somasundaram Krishnasamy --- diff --git a/dtrace/dtrace_dev.c b/dtrace/dtrace_dev.c index fb0908c14dada..6e918c2573e68 100644 --- a/dtrace/dtrace_dev.c +++ b/dtrace/dtrace_dev.c @@ -1126,8 +1126,18 @@ static struct miscdevice helper_dev = { static void module_add_pdata(void *dmy, struct module *mp) { - dtrace_module_t *pdata = kmem_cache_alloc(dtrace_pdata_cachep, - GFP_KERNEL | __GFP_ZERO); + dtrace_module_t *pdata; + + /* + * A module may be on its way out before DTrace sets up its module + * handling support. Do not try to provide anything for modules being + * removed during startup. + */ + if (mp->state == MODULE_STATE_GOING) + return; + + pdata = kmem_cache_alloc(dtrace_pdata_cachep, + GFP_KERNEL | __GFP_ZERO); pdata_init(pdata, mp); mp->pdata = pdata; @@ -1464,7 +1474,6 @@ int dtrace_dev_init(void) * a pdata object. Modules loaded after this one will get their pdata * object assigned using the module notifier hook. */ - module_add_pdata(NULL, dtrace_kmod); dtrace_for_each_module(module_add_pdata, NULL); /* @@ -1616,7 +1625,6 @@ void dtrace_dev_exit(void) * point had their pdata object cleaned up using the module notifier * hook. */ - module_del_pdata(NULL, dtrace_kmod); dtrace_for_each_module(module_del_pdata, NULL); /* diff --git a/dtrace/dtrace_probe.c b/dtrace/dtrace_probe.c index 46f7db1294d75..89a15b0c83630 100644 --- a/dtrace/dtrace_probe.c +++ b/dtrace/dtrace_probe.c @@ -163,7 +163,6 @@ void dtrace_probe_description(const dtrace_probe_t *prp, void dtrace_probe_provide(dtrace_probedesc_t *desc, dtrace_provider_t *prv) { - struct module *mod; int all = 0; if (prv == NULL) { @@ -173,48 +172,7 @@ void dtrace_probe_provide(dtrace_probedesc_t *desc, dtrace_provider_t *prv) do { prv->dtpv_pops.dtps_provide(prv->dtpv_arg, desc); - - /* - * In Linux, the kernel proper is not a module and therefore is - * not listed in the list of modules. Since the kernel proper - * can have probe points (and e.g. has a lot of SDT ones), we - * use a pseudo-kernel module to collect them so that there is - * no need for code duplication in handling such probe points. - */ - prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, dtrace_kmod); - - /* - * We need to explicitly make the call for the 'dtrace' module - * itself, because the following loop does not actually process - * the 'dtrace' module (it is used as a sentinel). - */ - prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, THIS_MODULE); - - rcu_read_lock(); - - list_for_each_entry(mod, &(THIS_MODULE->list), list) { - /* - * Skip over the modules list header, because it cannot - * validly be interpreted as a 'struct module'. It is - * a basic list_head structure. - */ -#ifdef MODULES_VADDR - if ((uintptr_t)mod < MODULES_VADDR || - (uintptr_t)mod >= MODULES_END) - continue; -#else - if ((uintptr_t)mod < VMALLOC_START || - (uintptr_t)mod >= VMALLOC_END) - continue; -#endif - - if (mod->state != MODULE_STATE_LIVE) - continue; - - prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, mod); - } - - rcu_read_unlock(); + dtrace_for_each_module(prv->dtpv_pops.dtps_provide_module, prv->dtpv_arg); } while (all && (prv = prv->dtpv_next) != NULL); } diff --git a/dtrace/dtrace_util.c b/dtrace/dtrace_util.c index a4c636a51bca1..c20d7d09e3ec4 100644 --- a/dtrace/dtrace_util.c +++ b/dtrace/dtrace_util.c @@ -167,43 +167,3 @@ void dtrace_cred2priv(const cred_t *cr, uint32_t *privp, kuid_t *uidp) #endif } -void dtrace_for_each_module(for_each_module_fn *fn, void *arg) -{ - struct module *mp; - - /* - * The Linux kernel does not export the modules list explicitly, nor - * is there an API to do so. However, when operating on a module - * that is in the modules list, we can traverse the entire list anyway. - * That's what we're doing here. - * - * The current module is the anchor and sentinel for the loop, so we - * need to call the worker function explicitly for that module. We - * must also identify and skip the list header because that is not a - * valid module at all. - */ - fn(arg, dtrace_kmod); - fn(arg, THIS_MODULE); - - rcu_read_lock(); - - list_for_each_entry_rcu(mp, &(THIS_MODULE->list), list) { -#ifdef MODULES_VADDR - if ((uintptr_t)mp < MODULES_VADDR || - (uintptr_t)mp >= MODULES_END) - continue; -#else - if ((uintptr_t)mp < VMALLOC_START || - (uintptr_t)mp >= VMALLOC_END) - continue; -#endif - - if (mp->state != MODULE_STATE_LIVE) - continue; - - fn(arg, mp); - } - - rcu_read_unlock(); -} -EXPORT_SYMBOL(dtrace_for_each_module); diff --git a/dtrace/fbt_dev.c b/dtrace/fbt_dev.c index 67d487e7cd6b8..a160a3c4fe1ba 100644 --- a/dtrace/fbt_dev.c +++ b/dtrace/fbt_dev.c @@ -110,9 +110,8 @@ void fbt_provide_module(void *arg, struct module *mp) /* * Do not try to instrument DTrace itself and its modules: - * - dtrace module - * - ctf module - * - all modules depending on dtrace + * - dtrace module + * - all modules depending on dtrace */ if (!strncmp(mp->name, "dtrace", 7)) return; diff --git a/include/dtrace/dtrace_impl.h b/include/dtrace/dtrace_impl.h index 94430006db2be..663e0a36ebd11 100644 --- a/include/dtrace/dtrace_impl.h +++ b/include/dtrace/dtrace_impl.h @@ -1122,9 +1122,6 @@ extern int dtrace_badattr(const dtrace_attribute_t *); extern int dtrace_badname(const char *); extern void dtrace_cred2priv(const cred_t *, uint32_t *, kuid_t *); -typedef void for_each_module_fn(void *, struct module *); -extern void dtrace_for_each_module(for_each_module_fn *fn, void *arg); - extern void ctf_forceload(void); #define dtrace_membar_producer() smp_wmb() diff --git a/include/linux/dtrace_os.h b/include/linux/dtrace_os.h index a9636cb711b76..86b1223a2bff1 100644 --- a/include/linux/dtrace_os.h +++ b/include/linux/dtrace_os.h @@ -42,6 +42,9 @@ typedef enum dtrace_vtime_state { extern dtrace_vtime_state_t dtrace_vtime_active; +typedef void for_each_module_fn(void *, struct module *); +extern void dtrace_for_each_module(for_each_module_fn *fn, void *arg); + extern void dtrace_update_time(struct timekeeper *); extern ktime_t dtrace_get_walltime(void); diff --git a/kernel/dtrace/dtrace_os.c b/kernel/dtrace/dtrace_os.c index f32d6fd2d55d5..047fd5611fc71 100644 --- a/kernel/dtrace/dtrace_os.c +++ b/kernel/dtrace/dtrace_os.c @@ -374,6 +374,46 @@ void dtrace_psinfo_free(struct task_struct *tsk) schedule_work(&psinfo_cleanup); } +/*---------------------------------------------------------------------------*\ +(* MODULE SUPPORT FUNCTIONS *) +\*---------------------------------------------------------------------------*/ +extern struct list_head *dtrace_modules; + +/* + * Iterate over all loaded kernel modules. This is requried until the linux + * kernel receives its own module iterator. + */ +void dtrace_for_each_module(for_each_module_fn func, void *arg) +{ + struct module *mp; + + if (func == NULL) + return; + + mutex_lock(&module_mutex); + + /* The dtrace fake module is not in the list. */ + func(arg, dtrace_kmod); + + list_for_each_entry(mp, dtrace_modules, list) { + +#ifdef MODULES_VADDR + if ((uintptr_t)mp < MODULES_VADDR || + (uintptr_t)mp >= MODULES_END) + continue; +#else + if ((uintptr_t)mp < VMALLOC_START || + (uintptr_t)mp >= VMALLOC_END) + continue; +#endif + + func(arg, mp); + } + + mutex_unlock(&module_mutex); +} +EXPORT_SYMBOL_GPL(dtrace_for_each_module); + /*---------------------------------------------------------------------------*\ (* TIME SUPPORT FUNCTIONS *) \*---------------------------------------------------------------------------*/ diff --git a/kernel/module.c b/kernel/module.c index d3e5e2f8ec15d..7b9d678bba439 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -105,6 +105,9 @@ static LIST_HEAD(modules); #ifdef CONFIG_KGDB_KDB struct list_head *kdb_modules = &modules; /* kdb needs the list of modules */ #endif /* CONFIG_KGDB_KDB */ +#ifdef CONFIG_DTRACE +struct list_head *dtrace_modules = &modules; +#endif /* CONFIG_DTRACE */ #ifdef CONFIG_MODULE_SIG #ifdef CONFIG_MODULE_SIG_FORCE