mirror of https://gitee.com/openkylin/linux.git
drbd: drbd_adm_get_status needs to show some more detail
We want to see existing connection objects, even if they do not currently have volumes attached. Change the .dumpit variant of drbd_adm_get_status to iterate not over minor devices, but over connections + volumes. Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com> Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
This commit is contained in:
parent
73d901b74f
commit
543cc10b4c
|
@ -171,6 +171,7 @@ drbd_insert_fault(struct drbd_conf *mdev, unsigned int type) {
|
||||||
extern struct ratelimit_state drbd_ratelimit_state;
|
extern struct ratelimit_state drbd_ratelimit_state;
|
||||||
extern struct idr minors;
|
extern struct idr minors;
|
||||||
extern struct list_head drbd_tconns;
|
extern struct list_head drbd_tconns;
|
||||||
|
extern struct mutex drbd_cfg_mutex;
|
||||||
|
|
||||||
/* on the wire */
|
/* on the wire */
|
||||||
enum drbd_packet {
|
enum drbd_packet {
|
||||||
|
@ -918,7 +919,7 @@ enum {
|
||||||
|
|
||||||
struct drbd_tconn { /* is a resource from the config file */
|
struct drbd_tconn { /* is a resource from the config file */
|
||||||
char *name; /* Resource name */
|
char *name; /* Resource name */
|
||||||
struct list_head all_tconn; /* List of all drbd_tconn, prot by global_state_lock */
|
struct list_head all_tconn; /* linked on global drbd_tconns */
|
||||||
struct idr volumes; /* <tconn, vnr> to mdev mapping */
|
struct idr volumes; /* <tconn, vnr> to mdev mapping */
|
||||||
enum drbd_conns cstate; /* Only C_STANDALONE to C_WF_REPORT_PARAMS */
|
enum drbd_conns cstate; /* Only C_STANDALONE to C_WF_REPORT_PARAMS */
|
||||||
struct mutex cstate_mutex; /* Protects graceful disconnects */
|
struct mutex cstate_mutex; /* Protects graceful disconnects */
|
||||||
|
|
|
@ -120,6 +120,7 @@ module_param_string(usermode_helper, usermode_helper, sizeof(usermode_helper), 0
|
||||||
*/
|
*/
|
||||||
struct idr minors;
|
struct idr minors;
|
||||||
struct list_head drbd_tconns; /* list of struct drbd_tconn */
|
struct list_head drbd_tconns; /* list of struct drbd_tconn */
|
||||||
|
DEFINE_MUTEX(drbd_cfg_mutex);
|
||||||
|
|
||||||
struct kmem_cache *drbd_request_cache;
|
struct kmem_cache *drbd_request_cache;
|
||||||
struct kmem_cache *drbd_ee_cache; /* peer requests */
|
struct kmem_cache *drbd_ee_cache; /* peer requests */
|
||||||
|
@ -2238,14 +2239,14 @@ struct drbd_tconn *conn_by_name(const char *name)
|
||||||
if (!name || !name[0])
|
if (!name || !name[0])
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
write_lock_irq(&global_state_lock);
|
mutex_lock(&drbd_cfg_mutex);
|
||||||
list_for_each_entry(tconn, &drbd_tconns, all_tconn) {
|
list_for_each_entry(tconn, &drbd_tconns, all_tconn) {
|
||||||
if (!strcmp(tconn->name, name))
|
if (!strcmp(tconn->name, name))
|
||||||
goto found;
|
goto found;
|
||||||
}
|
}
|
||||||
tconn = NULL;
|
tconn = NULL;
|
||||||
found:
|
found:
|
||||||
write_unlock_irq(&global_state_lock);
|
mutex_unlock(&drbd_cfg_mutex);
|
||||||
return tconn;
|
return tconn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2285,9 +2286,9 @@ struct drbd_tconn *drbd_new_tconn(const char *name)
|
||||||
drbd_thread_init(tconn, &tconn->worker, drbd_worker, "worker");
|
drbd_thread_init(tconn, &tconn->worker, drbd_worker, "worker");
|
||||||
drbd_thread_init(tconn, &tconn->asender, drbd_asender, "asender");
|
drbd_thread_init(tconn, &tconn->asender, drbd_asender, "asender");
|
||||||
|
|
||||||
write_lock_irq(&global_state_lock);
|
mutex_lock(&drbd_cfg_mutex);
|
||||||
list_add(&tconn->all_tconn, &drbd_tconns);
|
list_add_tail(&tconn->all_tconn, &drbd_tconns);
|
||||||
write_unlock_irq(&global_state_lock);
|
mutex_unlock(&drbd_cfg_mutex);
|
||||||
|
|
||||||
return tconn;
|
return tconn;
|
||||||
|
|
||||||
|
@ -2302,9 +2303,9 @@ struct drbd_tconn *drbd_new_tconn(const char *name)
|
||||||
|
|
||||||
void drbd_free_tconn(struct drbd_tconn *tconn)
|
void drbd_free_tconn(struct drbd_tconn *tconn)
|
||||||
{
|
{
|
||||||
write_lock_irq(&global_state_lock);
|
mutex_lock(&drbd_cfg_mutex);
|
||||||
list_del(&tconn->all_tconn);
|
list_del(&tconn->all_tconn);
|
||||||
write_unlock_irq(&global_state_lock);
|
mutex_unlock(&drbd_cfg_mutex);
|
||||||
idr_destroy(&tconn->volumes);
|
idr_destroy(&tconn->volumes);
|
||||||
|
|
||||||
free_cpumask_var(tconn->cpu_mask);
|
free_cpumask_var(tconn->cpu_mask);
|
||||||
|
|
|
@ -1544,6 +1544,10 @@ int drbd_adm_connect(struct sk_buff *skb, struct genl_info *info)
|
||||||
|
|
||||||
new_my_addr = (struct sockaddr *)&new_conf->my_addr;
|
new_my_addr = (struct sockaddr *)&new_conf->my_addr;
|
||||||
new_peer_addr = (struct sockaddr *)&new_conf->peer_addr;
|
new_peer_addr = (struct sockaddr *)&new_conf->peer_addr;
|
||||||
|
|
||||||
|
/* No need to take drbd_cfg_mutex here. All reconfiguration is
|
||||||
|
* strictly serialized on genl_lock(). We are protected against
|
||||||
|
* concurrent reconfiguration/addition/deletion */
|
||||||
list_for_each_entry(oconn, &drbd_tconns, all_tconn) {
|
list_for_each_entry(oconn, &drbd_tconns, all_tconn) {
|
||||||
if (oconn == tconn)
|
if (oconn == tconn)
|
||||||
continue;
|
continue;
|
||||||
|
@ -2187,6 +2191,24 @@ int drbd_adm_outdate(struct sk_buff *skb, struct genl_info *info)
|
||||||
return drbd_adm_simple_request_state(skb, info, NS(disk, D_OUTDATED));
|
return drbd_adm_simple_request_state(skb, info, NS(disk, D_OUTDATED));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int nla_put_drbd_cfg_context(struct sk_buff *skb, const char *conn_name, unsigned vnr)
|
||||||
|
{
|
||||||
|
struct nlattr *nla;
|
||||||
|
nla = nla_nest_start(skb, DRBD_NLA_CFG_CONTEXT);
|
||||||
|
if (!nla)
|
||||||
|
goto nla_put_failure;
|
||||||
|
if (vnr != VOLUME_UNSPECIFIED)
|
||||||
|
NLA_PUT_U32(skb, T_ctx_volume, vnr);
|
||||||
|
NLA_PUT_STRING(skb, T_ctx_conn_name, conn_name);
|
||||||
|
nla_nest_end(skb, nla);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
nla_put_failure:
|
||||||
|
if (nla)
|
||||||
|
nla_nest_cancel(skb, nla);
|
||||||
|
return -EMSGSIZE;
|
||||||
|
}
|
||||||
|
|
||||||
int nla_put_status_info(struct sk_buff *skb, struct drbd_conf *mdev,
|
int nla_put_status_info(struct sk_buff *skb, struct drbd_conf *mdev,
|
||||||
const struct sib_info *sib)
|
const struct sib_info *sib)
|
||||||
{
|
{
|
||||||
|
@ -2215,12 +2237,8 @@ int nla_put_status_info(struct sk_buff *skb, struct drbd_conf *mdev,
|
||||||
|
|
||||||
/* We need to add connection name and volume number information still.
|
/* We need to add connection name and volume number information still.
|
||||||
* Minor number is in drbd_genlmsghdr. */
|
* Minor number is in drbd_genlmsghdr. */
|
||||||
nla = nla_nest_start(skb, DRBD_NLA_CFG_CONTEXT);
|
if (nla_put_drbd_cfg_context(skb, mdev->tconn->name, mdev->vnr))
|
||||||
if (!nla)
|
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
NLA_PUT_U32(skb, T_ctx_volume, mdev->vnr);
|
|
||||||
NLA_PUT_STRING(skb, T_ctx_conn_name, mdev->tconn->name);
|
|
||||||
nla_nest_end(skb, nla);
|
|
||||||
|
|
||||||
if (got_ldev)
|
if (got_ldev)
|
||||||
if (disk_conf_to_skb(skb, &mdev->ldev->dc, exclude_sensitive))
|
if (disk_conf_to_skb(skb, &mdev->ldev->dc, exclude_sensitive))
|
||||||
|
@ -2307,41 +2325,100 @@ int drbd_adm_get_status_all(struct sk_buff *skb, struct netlink_callback *cb)
|
||||||
{
|
{
|
||||||
struct drbd_conf *mdev;
|
struct drbd_conf *mdev;
|
||||||
struct drbd_genlmsghdr *dh;
|
struct drbd_genlmsghdr *dh;
|
||||||
int minor = cb->args[0];
|
struct drbd_tconn *pos = (struct drbd_tconn*)cb->args[0];
|
||||||
|
struct drbd_tconn *tconn = NULL;
|
||||||
|
struct drbd_tconn *tmp;
|
||||||
|
unsigned volume = cb->args[1];
|
||||||
|
|
||||||
/* Open coded deferred single idr_for_each_entry iteration.
|
/* Open coded, deferred, iteration:
|
||||||
|
* list_for_each_entry_safe(tconn, tmp, &drbd_tconns, all_tconn) {
|
||||||
|
* idr_for_each_entry(&tconn->volumes, mdev, i) {
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* where tconn is cb->args[0];
|
||||||
|
* and i is cb->args[1];
|
||||||
|
*
|
||||||
* This may miss entries inserted after this dump started,
|
* This may miss entries inserted after this dump started,
|
||||||
* or entries deleted before they are reached.
|
* or entries deleted before they are reached.
|
||||||
* But we need to make sure the mdev won't disappear while
|
*
|
||||||
* we are looking at it. */
|
* We need to make sure the mdev won't disappear while
|
||||||
|
* we are looking at it, and revalidate our iterators
|
||||||
|
* on each iteration.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* synchronize with drbd_new_tconn/drbd_free_tconn */
|
||||||
|
mutex_lock(&drbd_cfg_mutex);
|
||||||
|
/* synchronize with drbd_delete_device */
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
mdev = idr_get_next(&minors, &minor);
|
next_tconn:
|
||||||
if (mdev) {
|
/* revalidate iterator position */
|
||||||
|
list_for_each_entry(tmp, &drbd_tconns, all_tconn) {
|
||||||
|
if (pos == NULL) {
|
||||||
|
/* first iteration */
|
||||||
|
pos = tmp;
|
||||||
|
tconn = pos;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (tmp == pos) {
|
||||||
|
tconn = pos;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tconn) {
|
||||||
|
mdev = idr_get_next(&tconn->volumes, &volume);
|
||||||
|
if (!mdev) {
|
||||||
|
/* No more volumes to dump on this tconn.
|
||||||
|
* Advance tconn iterator. */
|
||||||
|
pos = list_entry(tconn->all_tconn.next,
|
||||||
|
struct drbd_tconn, all_tconn);
|
||||||
|
/* But, did we dump any volume on this tconn yet? */
|
||||||
|
if (volume != 0) {
|
||||||
|
tconn = NULL;
|
||||||
|
volume = 0;
|
||||||
|
goto next_tconn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dh = genlmsg_put(skb, NETLINK_CB(cb->skb).pid,
|
dh = genlmsg_put(skb, NETLINK_CB(cb->skb).pid,
|
||||||
cb->nlh->nlmsg_seq, &drbd_genl_family,
|
cb->nlh->nlmsg_seq, &drbd_genl_family,
|
||||||
NLM_F_MULTI, DRBD_ADM_GET_STATUS);
|
NLM_F_MULTI, DRBD_ADM_GET_STATUS);
|
||||||
if (!dh)
|
if (!dh)
|
||||||
goto errout;
|
goto out;
|
||||||
|
|
||||||
D_ASSERT(mdev->minor == minor);
|
if (!mdev) {
|
||||||
|
/* this is a tconn without a single volume */
|
||||||
|
dh->minor = -1U;
|
||||||
|
dh->ret_code = NO_ERROR;
|
||||||
|
if (nla_put_drbd_cfg_context(skb, tconn->name, VOLUME_UNSPECIFIED))
|
||||||
|
genlmsg_cancel(skb, dh);
|
||||||
|
else
|
||||||
|
genlmsg_end(skb, dh);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
dh->minor = minor;
|
D_ASSERT(mdev->vnr == volume);
|
||||||
|
D_ASSERT(mdev->tconn == tconn);
|
||||||
|
|
||||||
|
dh->minor = mdev_to_minor(mdev);
|
||||||
dh->ret_code = NO_ERROR;
|
dh->ret_code = NO_ERROR;
|
||||||
|
|
||||||
if (nla_put_status_info(skb, mdev, NULL)) {
|
if (nla_put_status_info(skb, mdev, NULL)) {
|
||||||
genlmsg_cancel(skb, dh);
|
genlmsg_cancel(skb, dh);
|
||||||
goto errout;
|
goto out;
|
||||||
}
|
}
|
||||||
genlmsg_end(skb, dh);
|
genlmsg_end(skb, dh);
|
||||||
}
|
}
|
||||||
|
|
||||||
errout:
|
out:
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
/* where to start idr_get_next with the next iteration */
|
mutex_unlock(&drbd_cfg_mutex);
|
||||||
cb->args[0] = minor+1;
|
/* where to start the next iteration */
|
||||||
|
cb->args[0] = (long)pos;
|
||||||
|
cb->args[1] = (pos == tconn) ? volume + 1 : 0;
|
||||||
|
|
||||||
/* No more minors found: empty skb. Which will terminate the dump. */
|
/* No more tconns/volumes/minors found results in an empty skb.
|
||||||
|
* Which will terminate the dump. */
|
||||||
return skb->len;
|
return skb->len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue