drbd: Take a reference on tconn when finding a tconn by name
authorPhilipp Reisner <philipp.reisner@linbit.com>
Sun, 24 Apr 2011 08:53:19 +0000 (10:53 +0200)
committerPhilipp Reisner <philipp.reisner@linbit.com>
Thu, 8 Nov 2012 15:49:06 +0000 (16:49 +0100)
Rule #3 of kref.txt

Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
drivers/block/drbd/drbd_int.h
drivers/block/drbd/drbd_main.c
drivers/block/drbd/drbd_nl.c

index 3abf982..7797879 100644 (file)
@@ -1381,7 +1381,7 @@ extern void drbd_delete_device(struct drbd_conf *mdev);
 
 struct drbd_tconn *conn_create(const char *name);
 extern void conn_destroy(struct kref *kref);
-struct drbd_tconn *conn_by_name(const char *name);
+struct drbd_tconn *conn_get_by_name(const char *name);
 extern void conn_free_crypto(struct drbd_tconn *tconn);
 
 extern int proc_details;
index 11427f5..f0a0e17 100644 (file)
@@ -2362,7 +2362,7 @@ static void drbd_init_workqueue(struct drbd_work_queue* wq)
        INIT_LIST_HEAD(&wq->q);
 }
 
-struct drbd_tconn *conn_by_name(const char *name)
+struct drbd_tconn *conn_get_by_name(const char *name)
 {
        struct drbd_tconn *tconn;
 
@@ -2371,8 +2371,10 @@ struct drbd_tconn *conn_by_name(const char *name)
 
        down_read(&drbd_cfg_rwsem);
        list_for_each_entry(tconn, &drbd_tconns, all_tconn) {
-               if (!strcmp(tconn->name, name))
+               if (!strcmp(tconn->name, name)) {
+                       kref_get(&tconn->kref);
                        goto found;
+               }
        }
        tconn = NULL;
 found:
index 23c34ba..272c4a0 100644 (file)
@@ -195,7 +195,7 @@ static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info,
 
        adm_ctx.minor = d_in->minor;
        adm_ctx.mdev = minor_to_mdev(d_in->minor);
-       adm_ctx.tconn = conn_by_name(adm_ctx.conn_name);
+       adm_ctx.tconn = conn_get_by_name(adm_ctx.conn_name);
 
        if (!adm_ctx.mdev && (flags & DRBD_ADM_NEED_MINOR)) {
                drbd_msg_put_info("unknown minor");
@@ -223,8 +223,7 @@ static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info,
                drbd_msg_put_info("minor exists as different volume");
                return ERR_INVALID_REQUEST;
        }
-       if (adm_ctx.mdev && !adm_ctx.tconn)
-               adm_ctx.tconn = adm_ctx.mdev->tconn;
+
        return NO_ERROR;
 
 fail:
@@ -238,6 +237,11 @@ static int drbd_adm_finish(struct genl_info *info, int retcode)
        struct nlattr *nla;
        const char *conn_name = NULL;
 
+       if (adm_ctx.tconn) {
+               kref_put(&adm_ctx.tconn->kref, &conn_destroy);
+               adm_ctx.tconn = NULL;
+       }
+
        if (!adm_ctx.reply_skb)
                return -ENOMEM;
 
@@ -2748,10 +2752,13 @@ int drbd_adm_get_status_all(struct sk_buff *skb, struct netlink_callback *cb)
        if (!nla)
                return -EINVAL;
        conn_name = nla_data(nla);
-       tconn = conn_by_name(conn_name);
+       tconn = conn_get_by_name(conn_name);
+
        if (!tconn)
                return -ENODEV;
 
+       kref_put(&tconn->kref, &conn_destroy); /* get_one_status() (re)validates tconn by itself */
+
        /* prime iterators, and set "filter" mode mark:
         * only dump this tconn. */
        cb->args[0] = (long)tconn;