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; /* <tconn, vnr> to mdev mapping */
enum drbd_conns cstate; /* Only C_STANDALONE to C_WF_REPORT_PARAMS */
unsigned susp:1; /* IO suspended by user */
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);
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;
}
return false;
}
+ kref_get(&thi->tconn->kref);
+
init_completion(&thi->stop);
thi->reset_cpu_mask = 1;
thi->t_state = RUNNING;
if (IS_ERR(nt)) {
conn_err(tconn, "Couldn't start thread\n");
+ kref_put(&tconn->kref, &conn_destroy);
module_put(THIS_MODULE);
return false;
}
/* 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();
put_disk(mdev->vdisk);
blk_cleanup_queue(mdev->rq_queue);
kfree(mdev);
+
+ kref_put(&tconn->kref, &conn_destroy);
}
static void drbd_cleanup(void)
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;
};
down_write(&drbd_cfg_rwsem);
+ kref_init(&tconn->kref);
list_add_tail(&tconn->all_tconn, &drbd_tconns);
up_write(&drbd_cfg_rwsem);
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);
if (!mdev)
return ERR_NOMEM;
+ kref_get(&tconn->kref);
mdev->tconn = tconn;
+
mdev->minor = minor;
mdev->vnr = vnr;
blk_cleanup_queue(q);
out_no_q:
kfree(mdev);
+ kref_put(&tconn->kref, &conn_destroy);
return err;
}
conn_try_outdate_peer(tconn);
+ kref_put(&tconn->kref, &conn_destroy);
return 0;
}
{
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
* 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) {
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);
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;
/* 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" */
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;
//conn_err(tconn, STATE_FMT, STATE_ARGS("nms", nms));
after_all_state_ch(tconn);
+ kref_put(&tconn->kref, &conn_destroy);
return 0;
}
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 {