diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index 0fb3fc32a993..3abf982ec559 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h @@ -828,6 +828,7 @@ enum { struct drbd_tconn { /* is a resource from the config file */ char *name; /* Resource name */ struct list_head all_tconn; /* linked on global drbd_tconns */ + struct kref kref; struct idr volumes; /* to mdev mapping */ enum drbd_conns cstate; /* Only C_STANDALONE to C_WF_REPORT_PARAMS */ unsigned susp:1; /* IO suspended by user */ @@ -1378,8 +1379,8 @@ extern int conn_lowest_minor(struct drbd_tconn *tconn); enum drbd_ret_code conn_new_minor(struct drbd_tconn *tconn, unsigned int minor, int vnr); extern void drbd_delete_device(struct drbd_conf *mdev); -struct drbd_tconn *drbd_new_tconn(const char *name); -extern void drbd_free_tconn(struct drbd_tconn *tconn); +struct drbd_tconn *conn_create(const char *name); +extern void conn_destroy(struct kref *kref); struct drbd_tconn *conn_by_name(const char *name); extern void conn_free_crypto(struct drbd_tconn *tconn); diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index b9c103f16ae7..11427f59c5af 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -509,6 +509,8 @@ static int drbd_thread_setup(void *arg) conn_info(tconn, "Terminating %s\n", current->comm); /* Release mod reference taken when thread was started */ + + kref_put(&tconn->kref, &conn_destroy); module_put(THIS_MODULE); return retval; } @@ -546,6 +548,8 @@ int drbd_thread_start(struct drbd_thread *thi) return false; } + kref_get(&thi->tconn->kref); + init_completion(&thi->stop); thi->reset_cpu_mask = 1; thi->t_state = RUNNING; @@ -558,6 +562,7 @@ int drbd_thread_start(struct drbd_thread *thi) if (IS_ERR(nt)) { conn_err(tconn, "Couldn't start thread\n"); + kref_put(&tconn->kref, &conn_destroy); module_put(THIS_MODULE); return false; } @@ -2237,6 +2242,8 @@ static void drbd_release_all_peer_reqs(struct drbd_conf *mdev) /* caution. no locking. */ void drbd_delete_device(struct drbd_conf *mdev) { + struct drbd_tconn *tconn = mdev->tconn; + idr_remove(&mdev->tconn->volumes, mdev->vnr); idr_remove(&minors, mdev_to_minor(mdev)); synchronize_rcu(); @@ -2272,6 +2279,8 @@ void drbd_delete_device(struct drbd_conf *mdev) put_disk(mdev->vdisk); blk_cleanup_queue(mdev->rq_queue); kfree(mdev); + + kref_put(&tconn->kref, &conn_destroy); } static void drbd_cleanup(void) @@ -2409,7 +2418,7 @@ void conn_free_crypto(struct drbd_tconn *tconn) tconn->int_dig_vv = NULL; } -struct drbd_tconn *drbd_new_tconn(const char *name) +struct drbd_tconn *conn_create(const char *name) { struct drbd_tconn *tconn; @@ -2455,6 +2464,7 @@ struct drbd_tconn *drbd_new_tconn(const char *name) }; down_write(&drbd_cfg_rwsem); + kref_init(&tconn->kref); list_add_tail(&tconn->all_tconn, &drbd_tconns); up_write(&drbd_cfg_rwsem); @@ -2471,9 +2481,10 @@ struct drbd_tconn *drbd_new_tconn(const char *name) return NULL; } -void drbd_free_tconn(struct drbd_tconn *tconn) +void conn_destroy(struct kref *kref) { - list_del(&tconn->all_tconn); + struct drbd_tconn *tconn = container_of(kref, struct drbd_tconn, kref); + idr_destroy(&tconn->volumes); free_cpumask_var(tconn->cpu_mask); @@ -2503,7 +2514,9 @@ enum drbd_ret_code conn_new_minor(struct drbd_tconn *tconn, unsigned int minor, if (!mdev) return ERR_NOMEM; + kref_get(&tconn->kref); mdev->tconn = tconn; + mdev->minor = minor; mdev->vnr = vnr; @@ -2605,6 +2618,7 @@ enum drbd_ret_code conn_new_minor(struct drbd_tconn *tconn, unsigned int minor, blk_cleanup_queue(q); out_no_q: kfree(mdev); + kref_put(&tconn->kref, &conn_destroy); return err; } diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index fff11ae79f15..23c34baa75a4 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -479,6 +479,7 @@ static int _try_outdate_peer_async(void *data) conn_try_outdate_peer(tconn); + kref_put(&tconn->kref, &conn_destroy); return 0; } @@ -486,9 +487,12 @@ void conn_try_outdate_peer_async(struct drbd_tconn *tconn) { struct task_struct *opa; + kref_get(&tconn->kref); opa = kthread_run(_try_outdate_peer_async, tconn, "drbd_async_h"); - if (IS_ERR(opa)) + if (IS_ERR(opa)) { conn_err(tconn, "out of mem, failed to invoke fence-peer helper\n"); + kref_put(&tconn->kref, &conn_destroy); + } } enum drbd_state_rv @@ -2627,7 +2631,7 @@ int get_one_status(struct sk_buff *skb, struct netlink_callback *cb) * on each iteration. */ - /* synchronize with drbd_new_tconn/drbd_free_tconn */ + /* synchronize with conn_create()/conn_destroy() */ down_read(&drbd_cfg_rwsem); /* revalidate iterator position */ list_for_each_entry(tmp, &drbd_tconns, all_tconn) { @@ -2932,7 +2936,7 @@ int drbd_adm_create_connection(struct sk_buff *skb, struct genl_info *info) goto out; } - if (!drbd_new_tconn(adm_ctx.conn_name)) + if (!conn_create(adm_ctx.conn_name)) retcode = ERR_NOMEM; out: drbd_adm_finish(info, retcode); @@ -3005,10 +3009,6 @@ int drbd_adm_delete_minor(struct sk_buff *skb, struct genl_info *info) down_write(&drbd_cfg_rwsem); retcode = adm_delete_minor(adm_ctx.mdev); up_write(&drbd_cfg_rwsem); - /* if this was the last volume of this connection, - * this will terminate all threads */ - if (retcode == NO_ERROR) - conn_reconfig_done(adm_ctx.tconn); out: drbd_adm_finish(info, retcode); return 0; @@ -3078,7 +3078,9 @@ int drbd_adm_down(struct sk_buff *skb, struct genl_info *info) /* delete connection */ if (conn_lowest_minor(adm_ctx.tconn) < 0) { - drbd_free_tconn(adm_ctx.tconn); + list_del(&adm_ctx.tconn->all_tconn); + kref_put(&adm_ctx.tconn->kref, &conn_destroy); + retcode = NO_ERROR; } else { /* "can not happen" */ @@ -3107,7 +3109,9 @@ int drbd_adm_delete_connection(struct sk_buff *skb, struct genl_info *info) down_write(&drbd_cfg_rwsem); if (conn_lowest_minor(adm_ctx.tconn) < 0) { - drbd_free_tconn(adm_ctx.tconn); + list_del(&adm_ctx.tconn->all_tconn); + kref_put(&adm_ctx.tconn->kref, &conn_destroy); + retcode = NO_ERROR; } else { retcode = ERR_CONN_IN_USE; diff --git a/drivers/block/drbd/drbd_state.c b/drivers/block/drbd/drbd_state.c index f20a4a3807eb..d7a330e0135f 100644 --- a/drivers/block/drbd/drbd_state.c +++ b/drivers/block/drbd/drbd_state.c @@ -1460,6 +1460,7 @@ static int w_after_conn_state_ch(struct drbd_work *w, int unused) //conn_err(tconn, STATE_FMT, STATE_ARGS("nms", nms)); after_all_state_ch(tconn); + kref_put(&tconn->kref, &conn_destroy); return 0; } @@ -1686,6 +1687,7 @@ _conn_request_state(struct drbd_tconn *tconn, union drbd_state mask, union drbd_ acscw->ns_max = ns_max; acscw->flags = flags; acscw->w.cb = w_after_conn_state_ch; + kref_get(&tconn->kref); acscw->w.tconn = tconn; drbd_queue_work(&tconn->data.work, &acscw->w); } else {