Commit 85f75dd7 authored by Lars Ellenberg's avatar Lars Ellenberg Committed by Philipp Reisner

drbd: introduce in-kernel "down" command

This greatly simplifies deconfiguration of whole resources.
Signed-off-by: default avatarPhilipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: default avatarLars Ellenberg <lars.ellenberg@linbit.com>
parent 3c5e5f6a
......@@ -2303,9 +2303,7 @@ struct drbd_tconn *drbd_new_tconn(const char *name)
void drbd_free_tconn(struct drbd_tconn *tconn)
{
mutex_lock(&drbd_cfg_mutex);
list_del(&tconn->all_tconn);
mutex_unlock(&drbd_cfg_mutex);
idr_destroy(&tconn->volumes);
free_cpumask_var(tconn->cpu_mask);
......
......@@ -49,6 +49,7 @@ int drbd_adm_delete_minor(struct sk_buff *skb, struct genl_info *info);
int drbd_adm_create_connection(struct sk_buff *skb, struct genl_info *info);
int drbd_adm_delete_connection(struct sk_buff *skb, struct genl_info *info);
int drbd_adm_down(struct sk_buff *skb, struct genl_info *info);
int drbd_adm_set_role(struct sk_buff *skb, struct genl_info *info);
int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info);
......@@ -1416,6 +1417,18 @@ int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info)
return 0;
}
static int adm_detach(struct drbd_conf *mdev)
{
enum drbd_ret_code retcode;
drbd_suspend_io(mdev); /* so no-one is stuck in drbd_al_begin_io */
retcode = drbd_request_state(mdev, NS(disk, D_DISKLESS));
wait_event(mdev->misc_wait,
mdev->state.disk != D_DISKLESS ||
!atomic_read(&mdev->local_cnt));
drbd_resume_io(mdev);
return retcode;
}
/* Detaching the disk is a process in multiple stages. First we need to lock
* out application IO, in-flight IO, IO stuck in drbd_al_begin_io.
* Then we transition to D_DISKLESS, and wait for put_ldev() to return all
......@@ -1423,7 +1436,6 @@ int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info)
* Only then we have finally detached. */
int drbd_adm_detach(struct sk_buff *skb, struct genl_info *info)
{
struct drbd_conf *mdev;
enum drbd_ret_code retcode;
retcode = drbd_adm_prepare(skb, info, DRBD_ADM_NEED_MINOR);
......@@ -1432,13 +1444,7 @@ int drbd_adm_detach(struct sk_buff *skb, struct genl_info *info)
if (retcode != NO_ERROR)
goto out;
mdev = adm_ctx.mdev;
drbd_suspend_io(mdev); /* so no-one is stuck in drbd_al_begin_io */
retcode = drbd_request_state(mdev, NS(disk, D_DISKLESS));
wait_event(mdev->misc_wait,
mdev->state.disk != D_DISKLESS ||
!atomic_read(&mdev->local_cnt));
drbd_resume_io(mdev);
retcode = adm_detach(adm_ctx.mdev);
out:
drbd_adm_finish(info, retcode);
return 0;
......@@ -1680,10 +1686,49 @@ int drbd_adm_connect(struct sk_buff *skb, struct genl_info *info)
return 0;
}
static enum drbd_state_rv conn_try_disconnect(struct drbd_tconn *tconn, bool force)
{
enum drbd_state_rv rv;
if (force) {
spin_lock_irq(&tconn->req_lock);
if (tconn->cstate >= C_WF_CONNECTION)
_conn_request_state(tconn, NS(conn, C_DISCONNECTING), CS_HARD);
spin_unlock_irq(&tconn->req_lock);
return SS_SUCCESS;
}
rv = conn_request_state(tconn, NS(conn, C_DISCONNECTING), 0);
switch (rv) {
case SS_NOTHING_TO_DO:
case SS_ALREADY_STANDALONE:
return SS_SUCCESS;
case SS_PRIMARY_NOP:
/* Our state checking code wants to see the peer outdated. */
rv = conn_request_state(tconn, NS2(conn, C_DISCONNECTING,
pdsk, D_OUTDATED), CS_VERBOSE);
break;
case SS_CW_FAILED_BY_PEER:
/* The peer probably wants to see us outdated. */
rv = conn_request_state(tconn, NS2(conn, C_DISCONNECTING,
disk, D_OUTDATED), 0);
if (rv == SS_IS_DISKLESS || rv == SS_LOWER_THAN_OUTDATED) {
conn_request_state(tconn, NS(conn, C_DISCONNECTING), CS_HARD);
rv = SS_SUCCESS;
}
break;
default:;
/* no special handling necessary */
}
return rv;
}
int drbd_adm_disconnect(struct sk_buff *skb, struct genl_info *info)
{
struct disconnect_parms parms;
struct drbd_tconn *tconn;
enum drbd_state_rv rv;
enum drbd_ret_code retcode;
int err;
......@@ -1704,35 +1749,8 @@ int drbd_adm_disconnect(struct sk_buff *skb, struct genl_info *info)
}
}
if (parms.force_disconnect) {
spin_lock_irq(&tconn->req_lock);
if (tconn->cstate >= C_WF_CONNECTION)
_conn_request_state(tconn, NS(conn, C_DISCONNECTING), CS_HARD);
spin_unlock_irq(&tconn->req_lock);
goto done;
}
retcode = conn_request_state(tconn, NS(conn, C_DISCONNECTING), 0);
if (retcode == SS_NOTHING_TO_DO)
goto done;
else if (retcode == SS_ALREADY_STANDALONE)
goto done;
else if (retcode == SS_PRIMARY_NOP) {
/* Our state checking code wants to see the peer outdated. */
retcode = conn_request_state(tconn, NS2(conn, C_DISCONNECTING,
pdsk, D_OUTDATED), CS_VERBOSE);
} else if (retcode == SS_CW_FAILED_BY_PEER) {
/* The peer probably wants to see us outdated. */
retcode = conn_request_state(tconn, NS2(conn, C_DISCONNECTING,
disk, D_OUTDATED), 0);
if (retcode == SS_IS_DISKLESS || retcode == SS_LOWER_THAN_OUTDATED) {
conn_request_state(tconn, NS(conn, C_DISCONNECTING), CS_HARD);
retcode = SS_SUCCESS;
}
}
if (retcode < SS_SUCCESS)
rv = conn_try_disconnect(tconn, parms.force_disconnect);
if (rv < SS_SUCCESS)
goto fail;
if (wait_event_interruptible(tconn->ping_wait,
......@@ -1743,7 +1761,6 @@ int drbd_adm_disconnect(struct sk_buff *skb, struct genl_info *info)
goto fail;
}
done:
retcode = NO_ERROR;
fail:
drbd_adm_finish(info, retcode);
......@@ -2644,9 +2661,21 @@ int drbd_adm_add_minor(struct sk_buff *skb, struct genl_info *info)
return 0;
}
static enum drbd_ret_code adm_delete_minor(struct drbd_conf *mdev)
{
if (mdev->state.disk == D_DISKLESS &&
/* no need to be mdev->state.conn == C_STANDALONE &&
* we may want to delete a minor from a live replication group.
*/
mdev->state.role == R_SECONDARY) {
drbd_delete_device(mdev_to_minor(mdev));
return NO_ERROR;
} else
return ERR_MINOR_CONFIGURED;
}
int drbd_adm_delete_minor(struct sk_buff *skb, struct genl_info *info)
{
struct drbd_conf *mdev;
enum drbd_ret_code retcode;
retcode = drbd_adm_prepare(skb, info, DRBD_ADM_NEED_MINOR);
......@@ -2655,19 +2684,89 @@ int drbd_adm_delete_minor(struct sk_buff *skb, struct genl_info *info)
if (retcode != NO_ERROR)
goto out;
mdev = adm_ctx.mdev;
if (mdev->state.disk == D_DISKLESS &&
/* no need to be mdev->state.conn == C_STANDALONE &&
* we may want to delete a minor from a live replication group.
*/
mdev->state.role == R_SECONDARY) {
drbd_delete_device(mdev_to_minor(mdev));
retcode = NO_ERROR;
mutex_lock(&drbd_cfg_mutex);
retcode = adm_delete_minor(adm_ctx.mdev);
mutex_unlock(&drbd_cfg_mutex);
/* if this was the last volume of this connection,
* this will terminate all threads */
if (retcode == NO_ERROR)
conn_reconfig_done(adm_ctx.tconn);
} else
retcode = ERR_MINOR_CONFIGURED;
out:
drbd_adm_finish(info, retcode);
return 0;
}
int drbd_adm_down(struct sk_buff *skb, struct genl_info *info)
{
enum drbd_ret_code retcode;
enum drbd_state_rv rv;
struct drbd_conf *mdev;
unsigned i;
retcode = drbd_adm_prepare(skb, info, 0);
if (!adm_ctx.reply_skb)
return retcode;
if (retcode != NO_ERROR)
goto out;
if (!adm_ctx.tconn) {
retcode = ERR_CONN_NOT_KNOWN;
goto out;
}
mutex_lock(&drbd_cfg_mutex);
/* demote */
idr_for_each_entry(&adm_ctx.tconn->volumes, mdev, i) {
retcode = drbd_set_role(mdev, R_SECONDARY, 0);
if (retcode < SS_SUCCESS) {
drbd_msg_put_info("failed to demote");
goto out_unlock;
}
}
/* disconnect */
rv = conn_try_disconnect(adm_ctx.tconn, 0);
if (rv < SS_SUCCESS) {
retcode = rv; /* enum type mismatch! */
drbd_msg_put_info("failed to disconnect");
goto out_unlock;
}
/* detach */
idr_for_each_entry(&adm_ctx.tconn->volumes, mdev, i) {
rv = adm_detach(mdev);
if (rv < SS_SUCCESS) {
retcode = rv; /* enum type mismatch! */
drbd_msg_put_info("failed to detach");
goto out_unlock;
}
}
/* delete volumes */
idr_for_each_entry(&adm_ctx.tconn->volumes, mdev, i) {
retcode = adm_delete_minor(mdev);
if (retcode != NO_ERROR) {
/* "can not happen" */
drbd_msg_put_info("failed to delete volume");
goto out_unlock;
}
}
/* stop all threads */
conn_reconfig_done(adm_ctx.tconn);
/* delete connection */
if (conn_lowest_minor(adm_ctx.tconn) < 0) {
drbd_free_tconn(adm_ctx.tconn);
retcode = NO_ERROR;
} else {
/* "can not happen" */
retcode = ERR_CONN_IN_USE;
drbd_msg_put_info("failed to delete connection");
goto out_unlock;
}
out_unlock:
mutex_unlock(&drbd_cfg_mutex);
out:
drbd_adm_finish(info, retcode);
return 0;
......@@ -2683,12 +2782,14 @@ int drbd_adm_delete_connection(struct sk_buff *skb, struct genl_info *info)
if (retcode != NO_ERROR)
goto out;
mutex_lock(&drbd_cfg_mutex);
if (conn_lowest_minor(adm_ctx.tconn) < 0) {
drbd_free_tconn(adm_ctx.tconn);
retcode = NO_ERROR;
} else {
retcode = ERR_CONN_IN_USE;
}
mutex_unlock(&drbd_cfg_mutex);
out:
drbd_adm_finish(info, retcode);
......
......@@ -347,3 +347,5 @@ GENL_op(DRBD_ADM_OUTDATE, 25, GENL_doit(drbd_adm_outdate),
GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, GENLA_F_REQUIRED))
GENL_op(DRBD_ADM_GET_TIMEOUT_TYPE, 26, GENL_doit(drbd_adm_get_timeout_type),
GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, GENLA_F_REQUIRED))
GENL_op(DRBD_ADM_DOWN, 27, GENL_doit(drbd_adm_down),
GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, GENLA_F_REQUIRED))
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment