drbd: Implemented new commands to create/delete connections/minors

Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
This commit is contained in:
Philipp Reisner 2011-02-22 02:07:03 -05:00
parent 80883197da
commit 774b305518
5 changed files with 119 additions and 72 deletions

View file

@ -1258,7 +1258,6 @@ extern int drbd_bmio_clear_n_write(struct drbd_conf *mdev);
extern void drbd_go_diskless(struct drbd_conf *mdev);
extern void drbd_ldev_destroy(struct drbd_conf *mdev);
/* Meta data layout
We reserve a 128MB Block (4k aligned)
* either at the end of the backing device
@ -1476,8 +1475,9 @@ extern wait_queue_head_t drbd_pp_wait;
extern rwlock_t global_state_lock;
extern int conn_lowest_minor(struct drbd_tconn *tconn);
extern struct drbd_conf *drbd_new_device(unsigned int minor);
enum drbd_ret_code conn_new_minor(struct drbd_tconn *tconn, unsigned int minor, int vnr);
extern void drbd_free_mdev(struct drbd_conf *mdev);
extern void drbd_delete_device(unsigned int minor);
struct drbd_tconn *drbd_new_tconn(char *name);
extern void drbd_free_tconn(struct drbd_tconn *tconn);

View file

@ -614,13 +614,16 @@ char *drbd_task_to_thread_name(struct drbd_tconn *tconn, struct task_struct *tas
return thi ? thi->name : task->comm;
}
#ifdef CONFIG_SMP
int conn_lowest_minor(struct drbd_tconn *tconn)
{
int minor = 0;
idr_get_next(&tconn->volumes, &minor);
if (!idr_get_next(&tconn->volumes, &minor))
return -1;
return minor;
}
#ifdef CONFIG_SMP
/**
* drbd_calc_cpu_mask() - Generate CPU masks, spread over all CPUs
* @mdev: DRBD device.
@ -2078,15 +2081,16 @@ static void drbd_release_ee_lists(struct drbd_conf *mdev)
dev_err(DEV, "%d EEs in net list found!\n", rr);
}
/* caution. no locking.
* currently only used from module cleanup code. */
static void drbd_delete_device(unsigned int minor)
/* caution. no locking. */
void drbd_delete_device(unsigned int minor)
{
struct drbd_conf *mdev = minor_to_mdev(minor);
if (!mdev)
return;
idr_remove(&mdev->tconn->volumes, minor);
/* paranoia asserts */
D_ASSERT(mdev->open_cnt == 0);
D_ASSERT(list_empty(&mdev->tconn->data.work.q));
@ -2101,7 +2105,6 @@ static void drbd_delete_device(unsigned int minor)
bdput(mdev->this_bdev);
drbd_free_resources(mdev);
drbd_free_tconn(mdev->tconn);
drbd_release_ee_lists(mdev);
@ -2223,6 +2226,9 @@ struct drbd_tconn *drbd_new_tconn(char *name)
if (!tconn->name)
goto fail;
if (!zalloc_cpumask_var(&tconn->cpu_mask, GFP_KERNEL))
goto fail;
if (!tl_init(tconn))
goto fail;
@ -2252,6 +2258,7 @@ struct drbd_tconn *drbd_new_tconn(char *name)
fail:
tl_cleanup(tconn);
free_cpumask_var(tconn->cpu_mask);
kfree(tconn->name);
kfree(tconn);
@ -2265,6 +2272,7 @@ void drbd_free_tconn(struct drbd_tconn *tconn)
write_unlock_irq(&global_state_lock);
idr_destroy(&tconn->volumes);
free_cpumask_var(tconn->cpu_mask);
kfree(tconn->name);
kfree(tconn->int_dig_out);
kfree(tconn->int_dig_in);
@ -2272,32 +2280,31 @@ void drbd_free_tconn(struct drbd_tconn *tconn)
kfree(tconn);
}
struct drbd_conf *drbd_new_device(unsigned int minor)
enum drbd_ret_code conn_new_minor(struct drbd_tconn *tconn, unsigned int minor, int vnr)
{
struct drbd_conf *mdev;
struct gendisk *disk;
struct request_queue *q;
char conn_name[9]; /* drbd1234N */
int vnr;
int vnr_got = vnr;
mdev = minor_to_mdev(minor);
if (mdev)
return ERR_MINOR_EXISTS;
/* GFP_KERNEL, we are outside of all write-out paths */
mdev = kzalloc(sizeof(struct drbd_conf), GFP_KERNEL);
if (!mdev)
return NULL;
sprintf(conn_name, "drbd%d", minor);
mdev->tconn = drbd_new_tconn(conn_name);
if (!mdev->tconn)
goto out_no_tconn;
if (!idr_pre_get(&mdev->tconn->volumes, GFP_KERNEL))
goto out_no_cpumask;
if (idr_get_new(&mdev->tconn->volumes, mdev, &vnr))
goto out_no_cpumask;
if (vnr != 0) {
dev_err(DEV, "vnr = %d\n", vnr);
goto out_no_cpumask;
return ERR_NOMEM;
mdev->tconn = tconn;
if (!idr_pre_get(&tconn->volumes, GFP_KERNEL))
goto out_no_idr;
if (idr_get_new(&tconn->volumes, mdev, &vnr_got))
goto out_no_idr;
if (vnr_got != vnr) {
dev_err(DEV, "vnr_got (%d) != vnr (%d)\n", vnr_got, vnr);
goto out_no_q;
}
if (!zalloc_cpumask_var(&mdev->tconn->cpu_mask, GFP_KERNEL))
goto out_no_cpumask;
mdev->minor = minor;
@ -2354,7 +2361,10 @@ struct drbd_conf *drbd_new_device(unsigned int minor)
INIT_LIST_HEAD(&mdev->current_epoch->list);
mdev->epochs = 1;
return mdev;
minor_table[minor] = mdev;
add_disk(disk);
return NO_ERROR;
/* out_whatever_else:
kfree(mdev->current_epoch); */
@ -2367,12 +2377,10 @@ struct drbd_conf *drbd_new_device(unsigned int minor)
out_no_disk:
blk_cleanup_queue(q);
out_no_q:
free_cpumask_var(mdev->tconn->cpu_mask);
out_no_cpumask:
drbd_free_tconn(mdev->tconn);
out_no_tconn:
idr_remove(&tconn->volumes, vnr_got);
out_no_idr:
kfree(mdev);
return NULL;
return ERR_NOMEM;
}
/* counterpart of drbd_new_device.

View file

@ -443,40 +443,6 @@ drbd_set_role(struct drbd_conf *mdev, enum drbd_role new_role, int force)
return rv;
}
static struct drbd_conf *ensure_mdev(int minor, int create)
{
struct drbd_conf *mdev;
if (minor >= minor_count)
return NULL;
mdev = minor_to_mdev(minor);
if (!mdev && create) {
struct gendisk *disk = NULL;
mdev = drbd_new_device(minor);
spin_lock_irq(&drbd_pp_lock);
if (minor_table[minor] == NULL) {
minor_table[minor] = mdev;
disk = mdev->vdisk;
mdev = NULL;
} /* else: we lost the race */
spin_unlock_irq(&drbd_pp_lock);
if (disk) /* we won the race above */
/* in case we ever add a drbd_delete_device(),
* don't forget the del_gendisk! */
add_disk(disk);
else /* we lost the race above */
drbd_free_mdev(mdev);
mdev = minor_to_mdev(minor);
}
return mdev;
}
static int drbd_nl_primary(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
struct drbd_nl_cfg_reply *reply)
{
@ -1789,12 +1755,6 @@ static int drbd_nl_syncer_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *n
if (!expect(sc.al_extents <= DRBD_AL_EXTENTS_MAX))
sc.al_extents = DRBD_AL_EXTENTS_MAX;
/* to avoid spurious errors when configuring minors before configuring
* the minors they depend on: if necessary, first create the minor we
* depend on */
if (sc.after >= 0)
ensure_mdev(sc.after, 1);
/* most sanity checks done, try to assign the new sync-after
* dependency. need to hold the global lock in there,
* to avoid a race in the dependency loop check. */
@ -2184,13 +2144,73 @@ static int drbd_nl_new_c_uuid(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nl
return 0;
}
static int drbd_nl_new_conn(struct drbd_nl_cfg_req *nlp, struct drbd_nl_cfg_reply *reply)
{
struct new_connection args;
if (!new_connection_from_tags(nlp->tag_list, &args)) {
reply->ret_code = ERR_MANDATORY_TAG;
return 0;
}
reply->ret_code = NO_ERROR;
if (!drbd_new_tconn(args.name))
reply->ret_code = ERR_NOMEM;
return 0;
}
static int drbd_nl_new_minor(struct drbd_tconn *tconn,
struct drbd_nl_cfg_req *nlp, struct drbd_nl_cfg_reply *reply)
{
struct new_minor args;
args.vol_nr = 0;
args.minor = 0;
if (!new_minor_from_tags(nlp->tag_list, &args)) {
reply->ret_code = ERR_MANDATORY_TAG;
return 0;
}
reply->ret_code = conn_new_minor(tconn, args.minor, args.vol_nr);
return 0;
}
static int drbd_nl_del_minor(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
struct drbd_nl_cfg_reply *reply)
{
if (mdev->state.disk == D_DISKLESS &&
mdev->state.conn == C_STANDALONE &&
mdev->state.role == R_SECONDARY) {
drbd_delete_device(mdev_to_minor(mdev));
reply->ret_code = NO_ERROR;
} else {
reply->ret_code = ERR_MINOR_CONFIGURED;
}
return 0;
}
static int drbd_nl_del_conn(struct drbd_tconn *tconn,
struct drbd_nl_cfg_req *nlp, struct drbd_nl_cfg_reply *reply)
{
if (conn_lowest_minor(tconn) < 0) {
drbd_free_tconn(tconn);
reply->ret_code = NO_ERROR;
} else {
reply->ret_code = ERR_CONN_IN_USE;
}
return 0;
}
enum cn_handler_type {
CHT_MINOR,
CHT_CONN,
CHT_CTOR,
/* CHT_RES, later */
};
struct cn_handler_struct {
enum cn_handler_type type;
union {
@ -2235,6 +2255,10 @@ static struct cn_handler_struct cnd_table[] = {
sizeof(struct get_timeout_flag_tag_len_struct)},
[ P_start_ov ] = { CHT_MINOR, { &drbd_nl_start_ov }, 0 },
[ P_new_c_uuid ] = { CHT_MINOR, { &drbd_nl_new_c_uuid }, 0 },
[ P_new_connection ] = { CHT_CTOR, { .constructor = &drbd_nl_new_conn }, 0 },
[ P_new_minor ] = { CHT_CONN, { .conn_based = &drbd_nl_new_minor }, 0 },
[ P_del_minor ] = { CHT_MINOR, { &drbd_nl_del_minor }, 0 },
[ P_del_connection ] = { CHT_CONN, { .conn_based = &drbd_nl_del_conn }, 0 },
};
static void drbd_connector_callback(struct cn_msg *req, struct netlink_skb_parms *nsp)

View file

@ -156,6 +156,9 @@ enum drbd_ret_code {
ERR_PIC_AFTER_DEP = 156,
ERR_PIC_PEER_DEP = 157,
ERR_CONN_NOT_KNOWN = 158,
ERR_CONN_IN_USE = 159,
ERR_MINOR_CONFIGURED = 160,
ERR_MINOR_EXISTS = 161,
/* insert new ones above this line */
AFTER_LAST_ERR_CODE

View file

@ -152,6 +152,18 @@ NL_PACKET(new_c_uuid, 26,
NL_RESPONSE(return_code_only, 27)
#endif
NL_PACKET(new_connection, 28, /* CHT_CTOR */
NL_STRING( 85, T_MANDATORY, name, DRBD_NL_OBJ_NAME_LEN)
)
NL_PACKET(new_minor, 29, /* CHT_CONN */
NL_INTEGER( 86, T_MANDATORY, minor)
NL_INTEGER( 87, T_MANDATORY, vol_nr)
)
NL_PACKET(del_minor, 30, ) /* CHT_MINOR */
NL_PACKET(del_connection, 31, ) /* CHT_CONN */
#undef NL_PACKET
#undef NL_INTEGER
#undef NL_INT64