Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 345866
b: refs/heads/master
c: 85f75dd
h: refs/heads/master
v: v3
  • Loading branch information
Lars Ellenberg authored and Philipp Reisner committed Nov 3, 2012
1 parent c84fcd2 commit e9a9b69
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 54 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 3c5e5f6afd242ea5944197a9b54033c1461b793c
refs/heads/master: 85f75dd7630436b0aa46a6393099c0f23121f5f0
2 changes: 0 additions & 2 deletions trunk/drivers/block/drbd/drbd_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
203 changes: 152 additions & 51 deletions trunk/drivers/block/drbd/drbd_nl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -1416,14 +1417,25 @@ 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
* internal references as well.
* 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);
Expand All @@ -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;
Expand Down Expand Up @@ -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;

Expand All @@ -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,
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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;
/* if this was the last volume of this connection,
* this will terminate all threads */
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;
Expand All @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions trunk/include/linux/drbd_genl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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))

0 comments on commit e9a9b69

Please sign in to comment.