]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
mm/zpool: prevent zbud/zsmalloc from unloading when used
authorDan Streetman <ddstreet@ieee.org>
Thu, 26 Jun 2014 00:42:46 +0000 (10:42 +1000)
committerStephen Rothwell <sfr@canb.auug.org.au>
Thu, 26 Jun 2014 00:42:46 +0000 (10:42 +1000)
Add try_module_get() to zpool_create_pool(), and module_put() to
zpool_destroy_pool().  Without module usage counting, the driver module(s)
could be unloaded while their pool(s) were active, resulting in an oops
when zpool tried to access them.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Cc: Seth Jennings <sjennings@variantweb.net>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Nitin Gupta <ngupta@vflare.org>
Cc: Weijie Yang <weijie.yang@samsung.com>
Reviewed-by: Bob Liu <bob.liu@oracle.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Rik van Riel <riel@redhat.com>
Cc: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
include/linux/zpool.h
mm/zbud.c
mm/zpool.c
mm/zsmalloc.c

index 81f0c380286f20437592f5f633711aadbe78a6b3..27b93730d102a2f4fca3ff96691a2b7cff102739 100644 (file)
@@ -176,6 +176,7 @@ u64 zpool_get_total_size(struct zpool *pool);
  */
 struct zpool_driver {
        char *type;
+       struct module *owner;
        struct list_head list;
 
        void *(*create)(gfp_t gfp, struct zpool_ops *ops);
@@ -203,6 +204,10 @@ void zpool_register_driver(struct zpool_driver *driver);
 /**
  * zpool_unregister_driver() - unregister a zpool implementation.
  * @driver:    driver to unregister.
+ *
+ * Module usage counting is used to prevent using a driver
+ * while/after unloading.  Please only call unregister from
+ * module exit function.
  */
 void zpool_unregister_driver(struct zpool_driver *driver);
 
index 645379e20f0a59c91fab9ee185693f9fa84b14f4..440bab7912ae9c072543cae4307d0dabf3d0b39f 100644 (file)
--- a/mm/zbud.c
+++ b/mm/zbud.c
@@ -184,6 +184,7 @@ u64 zbud_zpool_total_size(void *pool)
 
 static struct zpool_driver zbud_zpool_driver = {
        .type =         "zbud",
+       .owner =        THIS_MODULE,
        .create =       zbud_zpool_create,
        .destroy =      zbud_zpool_destroy,
        .malloc =       zbud_zpool_malloc,
index 578c379fb96492c2e221cda746870be5a332df12..119f340b09a519508218a43f25a2bcb06bb5ea28 100644 (file)
@@ -72,15 +72,24 @@ static struct zpool_driver *zpool_get_driver(char *type)
 {
        struct zpool_driver *driver;
 
-       assert_spin_locked(&drivers_lock);
+       spin_lock(&drivers_lock);
        list_for_each_entry(driver, &drivers_head, list) {
-               if (!strcmp(driver->type, type))
-                       return driver;
+               if (!strcmp(driver->type, type)) {
+                       bool got = try_module_get(driver->owner);
+                       spin_unlock(&drivers_lock);
+                       return got ? driver : NULL;
+               }
        }
 
+       spin_unlock(&drivers_lock);
        return NULL;
 }
 
+static void zpool_put_driver(struct zpool_driver *driver)
+{
+       module_put(driver->owner);
+}
+
 struct zpool *zpool_create_pool(char *type, gfp_t flags,
                        struct zpool_ops *ops)
 {
@@ -89,15 +98,11 @@ struct zpool *zpool_create_pool(char *type, gfp_t flags,
 
        pr_info("creating pool type %s\n", type);
 
-       spin_lock(&drivers_lock);
        driver = zpool_get_driver(type);
-       spin_unlock(&drivers_lock);
 
        if (!driver) {
                request_module(type);
-               spin_lock(&drivers_lock);
                driver = zpool_get_driver(type);
-               spin_unlock(&drivers_lock);
        }
 
        if (!driver) {
@@ -108,6 +113,7 @@ struct zpool *zpool_create_pool(char *type, gfp_t flags,
        zpool = kmalloc(sizeof(*zpool), GFP_KERNEL);
        if (!zpool) {
                pr_err("couldn't create zpool - out of memory\n");
+               zpool_put_driver(driver);
                return NULL;
        }
 
@@ -118,6 +124,7 @@ struct zpool *zpool_create_pool(char *type, gfp_t flags,
 
        if (!zpool->pool) {
                pr_err("couldn't create %s pool\n", type);
+               zpool_put_driver(driver);
                kfree(zpool);
                return NULL;
        }
@@ -139,6 +146,7 @@ void zpool_destroy_pool(struct zpool *zpool)
        list_del(&zpool->list);
        spin_unlock(&pools_lock);
        zpool->driver->destroy(zpool->pool);
+       zpool_put_driver(zpool->driver);
        kfree(zpool);
 }
 
index feba6449b66cd302434bdec39ea5004be4722f5e..ae3a28fd8fded625df566400449542fd71fbec5d 100644 (file)
@@ -303,6 +303,7 @@ u64 zs_zpool_total_size(void *pool)
 
 static struct zpool_driver zs_zpool_driver = {
        .type =         "zsmalloc",
+       .owner =        THIS_MODULE,
        .create =       zs_zpool_create,
        .destroy =      zs_zpool_destroy,
        .malloc =       zs_zpool_malloc,