]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
dtrace: modules provide called from rcu atomic section
authorTomas Jedlicka <tomas.jedlicka@oracle.com>
Fri, 28 Jul 2017 11:46:53 +0000 (07:46 -0400)
committerTomas Jedlicka <tomas.jedlicka@oracle.com>
Fri, 15 Sep 2017 22:28:15 +0000 (00:28 +0200)
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 <tomas.jedlicka@oracle.com>
Reviewed-by: Nick Alcock <nick.alcock@oracle.com>
Acked-by: Somasundaram Krishnasamy <somasundaram.krishnasamy@oracle.com>
dtrace/dtrace_dev.c
dtrace/dtrace_probe.c
dtrace/dtrace_util.c
dtrace/fbt_dev.c
include/dtrace/dtrace_impl.h
include/linux/dtrace_os.h
kernel/dtrace/dtrace_os.c
kernel/module.c

index fb0908c14dada90765989fbe1fb89c0fadfab5cc..6e918c2573e68ee08788e4904e7b356811dd1cb0 100644 (file)
@@ -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);
 
        /*
index 46f7db1294d75a01834d9b44e91cec7450017fe9..89a15b0c83630773d64cb63131a495becaf61e0b 100644 (file)
@@ -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);
 }
 
index a4c636a51bca1a361e450177b013a62757d1380a..c20d7d09e3ec498575d71bbccac3dcd25c1f9295 100644 (file)
@@ -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);
index 67d487e7cd6b80aaf0de67f052b66ac1b4ebf911..a160a3c4fe1bae38877a1e9efdbaec4bf12a6e5c 100644 (file)
@@ -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;
index 94430006db2be011afc8280e88ddc50ba3bc15a0..663e0a36ebd1167e17a7af4a876903c4aae0c4e5 100644 (file)
@@ -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()
index a9636cb711b766a96132548a49c90a88c34311cd..86b1223a2bff16f5f842193b33a2f7e929995dd9 100644 (file)
@@ -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);
 
index f32d6fd2d55d5a8eb7ccfea8498995906192e0a8..047fd5611fc713b5eed070322c309cbe0fee9b26 100644 (file)
@@ -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                                                    *)
 \*---------------------------------------------------------------------------*/
index d3e5e2f8ec15de487b8cbb670ad5a26be4da6427..7b9d678bba439407af70642773c732149f2b85ad 100644 (file)
@@ -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