drbd: allow status dump request all volumes of a specific resource
authorLars Ellenberg <lars.ellenberg@linbit.com>
Mon, 18 Apr 2011 07:43:25 +0000 (09:43 +0200)
committerPhilipp Reisner <philipp.reisner@linbit.com>
Thu, 8 Nov 2012 15:49:04 +0000 (16:49 +0100)
We had drbd_adm_get_status (one single volume),
and drbd_adm_get_status_all (dump of all volumes of all resources).

This enhances the latter to be able to dump all volumes
of just one specific resource.

Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
drivers/block/drbd/drbd_nl.c

index f86e882..fff11ae 100644 (file)
@@ -2598,7 +2598,7 @@ out:
        return 0;
 }
 
-int drbd_adm_get_status_all(struct sk_buff *skb, struct netlink_callback *cb)
+int get_one_status(struct sk_buff *skb, struct netlink_callback *cb)
 {
        struct drbd_conf *mdev;
        struct drbd_genlmsghdr *dh;
@@ -2616,6 +2616,9 @@ int drbd_adm_get_status_all(struct sk_buff *skb, struct netlink_callback *cb)
         * where tconn is cb->args[0];
         * and i is cb->args[1];
         *
+        * cb->args[2] indicates if we shall loop over all resources,
+        * or just dump all volumes of a single resource.
+        *
         * This may miss entries inserted after this dump started,
         * or entries deleted before they are reached.
         *
@@ -2626,7 +2629,6 @@ int drbd_adm_get_status_all(struct sk_buff *skb, struct netlink_callback *cb)
 
        /* synchronize with drbd_new_tconn/drbd_free_tconn */
        down_read(&drbd_cfg_rwsem);
-next_tconn:
        /* revalidate iterator position */
        list_for_each_entry(tmp, &drbd_tconns, all_tconn) {
                if (pos == NULL) {
@@ -2641,16 +2643,22 @@ next_tconn:
                }
        }
        if (tconn) {
+next_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? */
+                       /* Did we dump any volume on this tconn yet? */
                        if (volume != 0) {
-                               tconn = NULL;
+                               /* If we reached the end of the list,
+                                * or only a single resource dump was requested,
+                                * we are done. */
+                               if (&pos->all_tconn == &drbd_tconns || cb->args[2])
+                                       goto out;
                                volume = 0;
+                               tconn = pos;
                                goto next_tconn;
                        }
                }
@@ -2696,6 +2704,60 @@ out:
         return skb->len;
 }
 
+/*
+ * Request status of all resources, or of all volumes within a single resource.
+ *
+ * This is a dump, as the answer may not fit in a single reply skb otherwise.
+ * Which means we cannot use the family->attrbuf or other such members, because
+ * dump is NOT protected by the genl_lock().  During dump, we only have access
+ * to the incoming skb, and need to opencode "parsing" of the nlattr payload.
+ *
+ * Once things are setup properly, we call into get_one_status().
+ */
+int drbd_adm_get_status_all(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       const unsigned hdrlen = GENL_HDRLEN + GENL_MAGIC_FAMILY_HDRSZ;
+       struct nlattr *nla;
+       const char *conn_name;
+       struct drbd_tconn *tconn;
+
+       /* Is this a followup call? */
+       if (cb->args[0]) {
+               /* ... of a single resource dump,
+                * and the resource iterator has been advanced already? */
+               if (cb->args[2] && cb->args[2] != cb->args[0])
+                       return 0; /* DONE. */
+               goto dump;
+       }
+
+       /* First call (from netlink_dump_start).  We need to figure out
+        * which resource(s) the user wants us to dump. */
+       nla = nla_find(nlmsg_attrdata(cb->nlh, hdrlen),
+                       nlmsg_attrlen(cb->nlh, hdrlen),
+                       DRBD_NLA_CFG_CONTEXT);
+
+       /* No explicit context given.  Dump all. */
+       if (!nla)
+               goto dump;
+       nla = nla_find_nested(nla, __nla_type(T_ctx_conn_name));
+       /* context given, but no name present? */
+       if (!nla)
+               return -EINVAL;
+       conn_name = nla_data(nla);
+       tconn = conn_by_name(conn_name);
+       if (!tconn)
+               return -ENODEV;
+
+       /* prime iterators, and set "filter" mode mark:
+        * only dump this tconn. */
+       cb->args[0] = (long)tconn;
+       /* cb->args[1] = 0; passed in this way. */
+       cb->args[2] = (long)tconn;
+
+dump:
+       return get_one_status(skb, cb);
+}
+
 int drbd_adm_get_timeout_type(struct sk_buff *skb, struct genl_info *info)
 {
        enum drbd_ret_code retcode;