Whamcloud - gitweb
LU-14945 lnet: don't use hops to determine the route state
[fs/lustre-release.git] / lnet / lnet / udsp.c
index 5442ff9..ffb2cde 100644 (file)
@@ -212,7 +212,8 @@ lnet_udsp_apply_rule_on_ni(struct udsp_info *udi)
        struct lnet_ud_nid_descr *ni_match = udi->udi_match;
        __u32 priority = (udi->udi_revert) ? -1 : udi->udi_priority;
 
-       rc = cfs_match_nid_net(ni->ni_nid,
+       rc = cfs_match_nid_net(
+               lnet_nid_to_nid4(&ni->ni_nid),
                ni_match->ud_net_id.udn_net_type,
                &ni_match->ud_net_id.udn_net_num_range,
                &ni_match->ud_addr_range);
@@ -220,7 +221,7 @@ lnet_udsp_apply_rule_on_ni(struct udsp_info *udi)
                return 0;
 
        CDEBUG(D_NET, "apply udsp on ni %s\n",
-              libcfs_nid2str(ni->ni_nid));
+              libcfs_nidstr(&ni->ni_nid));
 
        /* Detected match. Set NIDs priority */
        lnet_ni_set_sel_priority_locked(ni, priority);
@@ -247,14 +248,15 @@ lnet_udsp_apply_rte_list_on_net(struct lnet_net *net,
                list_for_each_entry(rnet, rn_list, lrn_list) {
                        list_for_each_entry(route, &rnet->lrn_routes, lr_list) {
                                /* look if gw nid on the same net matches */
-                               gw_prim_nid = route->lr_gateway->lp_primary_nid;
+                               gw_prim_nid = lnet_nid_to_nid4(
+                                       &route->lr_gateway->lp_primary_nid);
                                lpni = NULL;
                                while ((lpni = lnet_get_next_peer_ni_locked(route->lr_gateway,
                                                                            NULL,
                                                                            lpni)) != NULL) {
                                        if (!lnet_get_net_locked(lpni->lpni_peer_net->lpn_net_id))
                                                continue;
-                                       gw_nid = lpni->lpni_nid;
+                                       gw_nid = lnet_nid_to_nid4(&lpni->lpni_nid);
                                        rc = cfs_match_nid_net(gw_nid,
                                                rte_action->ud_net_id.udn_net_type,
                                                &rte_action->ud_net_id.udn_net_num_range,
@@ -423,7 +425,8 @@ lnet_udsp_apply_rte_list_on_lpni(struct lnet_peer_ni *lpni,
                rn_list = &the_lnet.ln_remote_nets_hash[i];
                list_for_each_entry(rnet, rn_list, lrn_list) {
                        list_for_each_entry(route, &rnet->lrn_routes, lr_list) {
-                               gw_nid = route->lr_gateway->lp_primary_nid;
+                               gw_nid = lnet_nid_to_nid4(
+                                       &route->lr_gateway->lp_primary_nid);
                                rc = cfs_match_nid_net(gw_nid,
                                        rte_action->ud_net_id.udn_net_type,
                                        &rte_action->ud_net_id.udn_net_num_range,
@@ -434,7 +437,7 @@ lnet_udsp_apply_rte_list_on_lpni(struct lnet_peer_ni *lpni,
                                if (!cleared || revert) {
                                        CDEBUG(D_NET, "%spref rtr nids from lpni %s\n",
                                               (revert) ? "revert " : "clear ",
-                                              libcfs_nid2str(lpni->lpni_nid));
+                                              libcfs_nidstr(&lpni->lpni_nid));
                                        lnet_peer_clr_pref_rtrs(lpni);
                                        cleared = true;
                                        if (revert) {
@@ -444,7 +447,7 @@ lnet_udsp_apply_rte_list_on_lpni(struct lnet_peer_ni *lpni,
                                }
                                CDEBUG(D_NET, "add gw nid %s as preferred for peer %s\n",
                                       libcfs_nid2str(gw_nid),
-                                      libcfs_nid2str(lpni->lpni_nid));
+                                      libcfs_nidstr(&lpni->lpni_nid));
                                /* match. Add to pref NIDs */
                                rc = lnet_peer_add_pref_rtr(lpni, gw_nid);
                                lnet_net_lock(LNET_LOCK_EX);
@@ -452,7 +455,7 @@ lnet_udsp_apply_rte_list_on_lpni(struct lnet_peer_ni *lpni,
                                if (rc && rc != -EEXIST) {
                                        CERROR("Failed to add %s to %s pref rtr list\n",
                                               libcfs_nid2str(gw_nid),
-                                              libcfs_nid2str(lpni->lpni_nid));
+                                              libcfs_nidstr(&lpni->lpni_nid));
                                        return rc;
                                }
                        }
@@ -476,7 +479,8 @@ lnet_udsp_apply_ni_list(struct lnet_peer_ni *lpni,
                if (LNET_NETTYP(net->net_id) != ni_action->ud_net_id.udn_net_type)
                        continue;
                list_for_each_entry(ni, &net->net_ni_list, ni_netlist) {
-                       rc = cfs_match_nid_net(ni->ni_nid,
+                       rc = cfs_match_nid_net(
+                               lnet_nid_to_nid4(&ni->ni_nid),
                                ni_action->ud_net_id.udn_net_type,
                                &ni_action->ud_net_id.udn_net_num_range,
                                &ni_action->ud_addr_range);
@@ -487,7 +491,7 @@ lnet_udsp_apply_ni_list(struct lnet_peer_ni *lpni,
                                lnet_peer_clr_pref_nids(lpni);
                                CDEBUG(D_NET, "%spref nids from lpni %s\n",
                                        (revert) ? "revert " : "clear ",
-                                       libcfs_nid2str(lpni->lpni_nid));
+                                       libcfs_nidstr(&lpni->lpni_nid));
                                cleared = true;
                                if (revert) {
                                        lnet_net_lock(LNET_LOCK_EX);
@@ -495,16 +499,17 @@ lnet_udsp_apply_ni_list(struct lnet_peer_ni *lpni,
                                }
                        }
                        CDEBUG(D_NET, "add nid %s as preferred for peer %s\n",
-                               libcfs_nid2str(ni->ni_nid),
-                               libcfs_nid2str(lpni->lpni_nid));
+                               libcfs_nidstr(&ni->ni_nid),
+                               libcfs_nidstr(&lpni->lpni_nid));
                        /* match. Add to pref NIDs */
-                       rc = lnet_peer_add_pref_nid(lpni, ni->ni_nid);
+                       rc = lnet_peer_add_pref_nid(
+                               lpni, lnet_nid_to_nid4(&ni->ni_nid));
                        lnet_net_lock(LNET_LOCK_EX);
                        /* success if EEXIST return */
                        if (rc && rc != -EEXIST) {
                                CERROR("Failed to add %s to %s pref nid list\n",
-                                       libcfs_nid2str(ni->ni_nid),
-                                       libcfs_nid2str(lpni->lpni_nid));
+                                       libcfs_nidstr(&ni->ni_nid),
+                                       libcfs_nidstr(&lpni->lpni_nid));
                                return rc;
                        }
                }
@@ -524,7 +529,7 @@ lnet_udsp_apply_rule_on_lpni(struct udsp_info *udi)
        bool local = udi->udi_local;
        enum lnet_udsp_action_type type = udi->udi_type;
 
-       rc = cfs_match_nid_net(lpni->lpni_nid,
+       rc = cfs_match_nid_net(lnet_nid_to_nid4(&lpni->lpni_nid),
                lp_match->ud_net_id.udn_net_type,
                &lp_match->ud_net_id.udn_net_num_range,
                &lp_match->ud_addr_range);
@@ -602,7 +607,7 @@ lnet_udsp_apply_rule_on_lpnis(struct udsp_info *udi)
                ptable = the_lnet.ln_peer_tables[cpt];
                list_for_each_entry(lp, &ptable->pt_peer_list, lp_peer_list) {
                        CDEBUG(D_NET, "udsp examining lp %s\n",
-                              libcfs_nid2str(lp->lp_primary_nid));
+                              libcfs_nidstr(&lp->lp_primary_nid));
                        list_for_each_entry(lpn,
                                            &lp->lp_peer_nets,
                                            lpn_peer_nets) {
@@ -622,7 +627,7 @@ lnet_udsp_apply_rule_on_lpnis(struct udsp_info *udi)
                                                    &lpn->lpn_peer_nis,
                                                    lpni_peer_nis) {
                                        CDEBUG(D_NET, "udsp examining lpni %s\n",
-                                              libcfs_nid2str(lpni->lpni_nid));
+                                              libcfs_nidstr(&lpni->lpni_nid));
                                        udi->udi_lpni = lpni;
                                        rc = lnet_udsp_apply_rule_on_lpni(udi);
                                        if (rc)
@@ -973,6 +978,94 @@ lnet_udsp_del_policy(int idx)
        return 0;
 }
 
+static void
+lnet_udsp_get_ni_info(struct lnet_ioctl_construct_udsp_info *info,
+                     struct lnet_ni *ni)
+{
+       struct lnet_nid_list *ne;
+       struct lnet_net *net = ni->ni_net;
+       int i = 0;
+
+       LASSERT(ni);
+
+       info->cud_nid_priority = ni->ni_sel_priority;
+       if (net) {
+               info->cud_net_priority = ni->ni_net->net_sel_priority;
+               list_for_each_entry(ne, &net->net_rtr_pref_nids, nl_list) {
+                       if (i < LNET_MAX_SHOW_NUM_NID)
+                               info->cud_pref_rtr_nid[i] = ne->nl_nid;
+                       else
+                               break;
+                       i++;
+               }
+       }
+}
+
+static void
+lnet_udsp_get_peer_info(struct lnet_ioctl_construct_udsp_info *info,
+                       struct lnet_peer_ni *lpni)
+{
+       struct lnet_nid_list *ne;
+       int i = 0;
+
+       /* peer tree structure needs to be in existence */
+       LASSERT(lpni && lpni->lpni_peer_net &&
+               lpni->lpni_peer_net->lpn_peer);
+
+       info->cud_nid_priority = lpni->lpni_sel_priority;
+       CDEBUG(D_NET, "lpni %s has %d pref nids\n",
+              libcfs_nidstr(&lpni->lpni_nid),
+              lpni->lpni_pref_nnids);
+       if (lpni->lpni_pref_nnids == 1) {
+               info->cud_pref_nid[0] = lpni->lpni_pref.nid;
+       } else if (lpni->lpni_pref_nnids > 1) {
+               struct list_head *list = &lpni->lpni_pref.nids;
+
+               list_for_each_entry(ne, list, nl_list) {
+                       if (i < LNET_MAX_SHOW_NUM_NID)
+                               info->cud_pref_nid[i] = ne->nl_nid;
+                       else
+                               break;
+                       i++;
+               }
+       }
+
+       i = 0;
+       list_for_each_entry(ne, &lpni->lpni_rtr_pref_nids, nl_list) {
+               if (i < LNET_MAX_SHOW_NUM_NID)
+                       info->cud_pref_rtr_nid[i] = ne->nl_nid;
+               else
+                       break;
+               i++;
+       }
+
+       info->cud_net_priority = lpni->lpni_peer_net->lpn_sel_priority;
+}
+
+void
+lnet_udsp_get_construct_info(struct lnet_ioctl_construct_udsp_info *info)
+{
+       struct lnet_ni *ni;
+       struct lnet_peer_ni *lpni;
+
+       lnet_net_lock(0);
+       if (!info->cud_peer) {
+               ni = lnet_nid2ni_locked(info->cud_nid, 0);
+               if (ni)
+                       lnet_udsp_get_ni_info(info, ni);
+       } else {
+               lpni = lnet_find_peer_ni_locked(info->cud_nid);
+               if (!lpni) {
+                       CDEBUG(D_NET, "nid %s is not found\n",
+                              libcfs_nid2str(info->cud_nid));
+               } else {
+                       lnet_udsp_get_peer_info(info, lpni);
+                       lnet_peer_ni_decref_locked(lpni);
+               }
+       }
+       lnet_net_unlock(0);
+}
+
 struct lnet_udsp *
 lnet_udsp_alloc(void)
 {
@@ -1043,3 +1136,417 @@ lnet_udsp_destroy(bool shutdown)
                lnet_udsp_free(udsp);
        }
 }
+
+static size_t
+lnet_size_marshaled_nid_descr(struct lnet_ud_nid_descr *descr)
+{
+       struct cfs_expr_list *expr;
+       int expr_count = 0;
+       int range_count = 0;
+       size_t size = sizeof(struct lnet_ioctl_udsp_descr);
+
+       if (!lnet_udsp_criteria_present(descr))
+               return size;
+
+       /* we always have one net expression */
+       if (!list_empty(&descr->ud_net_id.udn_net_num_range)) {
+               expr = list_first_entry(&descr->ud_net_id.udn_net_num_range,
+                                       struct cfs_expr_list, el_link);
+
+               /* count the number of cfs_range_expr in the net expression */
+               range_count = lnet_get_list_len(&expr->el_exprs);
+       }
+
+       /* count the number of cfs_range_expr in the address expressions */
+       list_for_each_entry(expr, &descr->ud_addr_range, el_link) {
+               expr_count++;
+               range_count += lnet_get_list_len(&expr->el_exprs);
+       }
+
+       size += (sizeof(struct lnet_expressions) * expr_count);
+       size += (sizeof(struct lnet_range_expr) * range_count);
+
+       return size;
+}
+
+size_t
+lnet_get_udsp_size(struct lnet_udsp *udsp)
+{
+       size_t size = sizeof(struct lnet_ioctl_udsp);
+
+       size += lnet_size_marshaled_nid_descr(&udsp->udsp_src);
+       size += lnet_size_marshaled_nid_descr(&udsp->udsp_dst);
+       size += lnet_size_marshaled_nid_descr(&udsp->udsp_rte);
+
+       CDEBUG(D_NET, "get udsp (%p) size: %d\n", udsp, (int)size);
+
+       return size;
+}
+
+static int
+copy_exprs(struct cfs_expr_list *expr, void __user **bulk,
+          __u32 *bulk_size)
+{
+       struct cfs_range_expr *range;
+       struct lnet_range_expr range_expr;
+
+       /* copy over the net range expressions to the bulk */
+       list_for_each_entry(range, &expr->el_exprs, re_link) {
+               range_expr.re_lo = range->re_lo;
+               range_expr.re_hi = range->re_hi;
+               range_expr.re_stride = range->re_stride;
+               CDEBUG(D_NET, "Copy Range %u:%u:%u\n",
+                      range_expr.re_lo, range_expr.re_hi,
+                      range_expr.re_stride);
+               if (copy_to_user(*bulk, &range_expr, sizeof(range_expr))) {
+                       CDEBUG(D_NET, "Failed to copy range_expr\n");
+                       return -EFAULT;
+               }
+               *bulk += sizeof(range_expr);
+               *bulk_size -= sizeof(range_expr);
+       }
+
+       return 0;
+}
+
+static int
+copy_nid_range(struct lnet_ud_nid_descr *nid_descr, char *type,
+               void __user **bulk, __u32 *bulk_size)
+{
+       struct lnet_ioctl_udsp_descr ioc_udsp_descr;
+       struct cfs_expr_list *expr;
+       struct lnet_expressions ioc_expr;
+       int expr_count;
+       int net_expr_count;
+       int rc;
+
+       memset(&ioc_udsp_descr, 0, sizeof(ioc_udsp_descr));
+       ioc_udsp_descr.iud_src_hdr.ud_descr_type = *(__u32 *)type;
+
+       /* if criteria not present, copy over the static part of the NID
+        * descriptor
+        */
+       if (!lnet_udsp_criteria_present(nid_descr)) {
+               CDEBUG(D_NET, "Descriptor %u:%u:%u:%u\n",
+                      ioc_udsp_descr.iud_src_hdr.ud_descr_type,
+                      ioc_udsp_descr.iud_src_hdr.ud_descr_count,
+                      ioc_udsp_descr.iud_net.ud_net_type,
+                      ioc_udsp_descr.iud_net.ud_net_num_expr.le_count);
+               if (copy_to_user(*bulk, &ioc_udsp_descr,
+                                sizeof(ioc_udsp_descr))) {
+                       CDEBUG(D_NET, "failed to copy ioc_udsp_descr\n");
+                       return -EFAULT;
+               }
+               *bulk += sizeof(ioc_udsp_descr);
+               *bulk_size -= sizeof(ioc_udsp_descr);
+               return 0;
+       }
+
+       expr_count = lnet_get_list_len(&nid_descr->ud_addr_range);
+
+       /* copy the net information */
+       if (!list_empty(&nid_descr->ud_net_id.udn_net_num_range)) {
+               expr = list_first_entry(&nid_descr->ud_net_id.udn_net_num_range,
+                                       struct cfs_expr_list, el_link);
+               net_expr_count = lnet_get_list_len(&expr->el_exprs);
+       } else {
+               net_expr_count = 0;
+       }
+
+       /* set the total expression count */
+       ioc_udsp_descr.iud_src_hdr.ud_descr_count = expr_count;
+       ioc_udsp_descr.iud_net.ud_net_type =
+               nid_descr->ud_net_id.udn_net_type;
+       ioc_udsp_descr.iud_net.ud_net_num_expr.le_count = net_expr_count;
+
+       CDEBUG(D_NET, "Descriptor %u:%u:%u:%u\n",
+               ioc_udsp_descr.iud_src_hdr.ud_descr_type,
+               ioc_udsp_descr.iud_src_hdr.ud_descr_count,
+               ioc_udsp_descr.iud_net.ud_net_type,
+               ioc_udsp_descr.iud_net.ud_net_num_expr.le_count);
+
+       /* copy over the header info to the bulk */
+       if (copy_to_user(*bulk, &ioc_udsp_descr, sizeof(ioc_udsp_descr))) {
+               CDEBUG(D_NET, "Failed to copy data\n");
+               return -EFAULT;
+       }
+       *bulk += sizeof(ioc_udsp_descr);
+       *bulk_size -= sizeof(ioc_udsp_descr);
+
+       /* copy over the net num expression if it exists */
+       if (net_expr_count) {
+               rc = copy_exprs(expr, bulk, bulk_size);
+               if (rc)
+                       return rc;
+       }
+
+       /* copy the address range */
+       list_for_each_entry(expr, &nid_descr->ud_addr_range, el_link) {
+               ioc_expr.le_count = lnet_get_list_len(&expr->el_exprs);
+               if (copy_to_user(*bulk, &ioc_expr, sizeof(ioc_expr))) {
+                       CDEBUG(D_NET, "failex to copy ioc_expr\n");
+                       return -EFAULT;
+               }
+               *bulk += sizeof(ioc_expr);
+               *bulk_size -= sizeof(ioc_expr);
+
+               rc = copy_exprs(expr, bulk, bulk_size);
+               if (rc)
+                       return rc;
+       }
+
+       return 0;
+}
+
+int
+lnet_udsp_marshal(struct lnet_udsp *udsp, struct lnet_ioctl_udsp *ioc_udsp)
+{
+       int rc = -ENOMEM;
+       void __user *bulk;
+       __u32 bulk_size;
+
+       if (!ioc_udsp)
+               return -EINVAL;
+
+       bulk = ioc_udsp->iou_bulk;
+       bulk_size = ioc_udsp->iou_hdr.ioc_len +
+         ioc_udsp->iou_bulk_size;
+
+       CDEBUG(D_NET, "marshal udsp (%p)\n", udsp);
+       CDEBUG(D_NET, "MEM -----> bulk: %p:0x%x\n", bulk, bulk_size);
+       /* make sure user space allocated enough buffer to marshal the
+        * udsp
+        */
+       if (bulk_size != lnet_get_udsp_size(udsp)) {
+               rc = -ENOSPC;
+               goto fail;
+       }
+
+       ioc_udsp->iou_idx = udsp->udsp_idx;
+       ioc_udsp->iou_action_type = udsp->udsp_action_type;
+       ioc_udsp->iou_action.priority = udsp->udsp_action.udsp_priority;
+
+       bulk_size -= sizeof(*ioc_udsp);
+
+       rc = copy_nid_range(&udsp->udsp_src, "SRC", &bulk, &bulk_size);
+       if (rc)
+               goto fail;
+
+       rc = copy_nid_range(&udsp->udsp_dst, "DST", &bulk, &bulk_size);
+       if (rc)
+               goto fail;
+
+       rc = copy_nid_range(&udsp->udsp_rte, "RTE", &bulk, &bulk_size);
+       if (rc)
+               goto fail;
+
+       CDEBUG(D_NET, "MEM <----- bulk: %p\n", bulk);
+
+       /* we should've consumed the entire buffer */
+       LASSERT(bulk_size == 0);
+       return 0;
+
+fail:
+       CERROR("Failed to marshal udsp: %d\n", rc);
+       return rc;
+}
+
+static void
+copy_range_info(void **bulk, void **buf, struct list_head *list,
+               int count)
+{
+       struct lnet_range_expr *range_expr;
+       struct cfs_range_expr *range;
+       struct cfs_expr_list *exprs;
+       int range_count = count;
+       int i;
+
+       if (range_count == 0)
+               return;
+
+       if (range_count == -1) {
+               struct lnet_expressions *e;
+
+               e = *bulk;
+               range_count = e->le_count;
+               *bulk += sizeof(*e);
+       }
+
+       exprs = *buf;
+       INIT_LIST_HEAD(&exprs->el_link);
+       INIT_LIST_HEAD(&exprs->el_exprs);
+       list_add_tail(&exprs->el_link, list);
+       *buf += sizeof(*exprs);
+
+       for (i = 0; i < range_count; i++) {
+               range_expr = *bulk;
+               range = *buf;
+               INIT_LIST_HEAD(&range->re_link);
+               range->re_lo = range_expr->re_lo;
+               range->re_hi = range_expr->re_hi;
+               range->re_stride = range_expr->re_stride;
+               CDEBUG(D_NET, "Copy Range %u:%u:%u\n",
+                      range->re_lo,
+                      range->re_hi,
+                      range->re_stride);
+               list_add_tail(&range->re_link, &exprs->el_exprs);
+               *bulk += sizeof(*range_expr);
+               *buf += sizeof(*range);
+       }
+}
+
+static int
+copy_ioc_udsp_descr(struct lnet_ud_nid_descr *nid_descr, char *type,
+                   void **bulk, __u32 *bulk_size)
+{
+       struct lnet_ioctl_udsp_descr *ioc_nid = *bulk;
+       struct lnet_expressions *exprs;
+       __u32 descr_type;
+       int expr_count = 0;
+       int range_count = 0;
+       int i;
+       __u32 size;
+       int remaining_size = *bulk_size;
+       void *tmp = *bulk;
+       __u32 alloc_size;
+       void *buf;
+       size_t range_expr_s = sizeof(struct lnet_range_expr);
+       size_t lnet_exprs_s = sizeof(struct lnet_expressions);
+
+       CDEBUG(D_NET, "%s: bulk = %p:%u\n", type, *bulk, *bulk_size);
+
+       /* criteria not present, skip over the static part of the
+        * bulk, which is included for each NID descriptor
+        */
+       if (ioc_nid->iud_net.ud_net_type == 0) {
+               remaining_size -= sizeof(*ioc_nid);
+               if (remaining_size < 0) {
+                       CERROR("Truncated userspace udsp buffer given\n");
+                       return -EINVAL;
+               }
+               *bulk += sizeof(*ioc_nid);
+               *bulk_size = remaining_size;
+               return 0;
+       }
+
+       descr_type = ioc_nid->iud_src_hdr.ud_descr_type;
+       if (descr_type != *(__u32 *)type) {
+               CERROR("Bad NID descriptor type. Expected %s, given %c%c%c\n",
+                       type, (__u8)descr_type, (__u8)(descr_type << 4),
+                       (__u8)(descr_type << 8));
+               return -EINVAL;
+       }
+
+       /* calculate the total size to verify we have enough buffer.
+        * Start of by finding how many ranges there are for the net
+        * expression.
+        */
+       range_count = ioc_nid->iud_net.ud_net_num_expr.le_count;
+       size = sizeof(*ioc_nid) + (range_count * range_expr_s);
+       remaining_size -= size;
+       if (remaining_size < 0) {
+               CERROR("Truncated userspace udsp buffer given\n");
+               return -EINVAL;
+       }
+
+       CDEBUG(D_NET, "Total net num ranges in %s: %d:%u\n", type,
+              range_count, size);
+       /* the number of expressions for the NID. IE 4 for IP, 1 for GNI */
+       expr_count = ioc_nid->iud_src_hdr.ud_descr_count;
+       CDEBUG(D_NET, "addr as %d exprs\n", expr_count);
+       /* point tmp to the beginning of the NID expressions */
+       tmp += size;
+       for (i = 0; i < expr_count; i++) {
+               /* get the number of ranges per expression */
+               exprs = tmp;
+               range_count += exprs->le_count;
+               size = (range_expr_s * exprs->le_count) + lnet_exprs_s;
+               remaining_size -= size;
+               CDEBUG(D_NET, "expr %d:%d:%u:%d:%d\n", i, exprs->le_count,
+                      size, remaining_size, range_count);
+               if (remaining_size < 0) {
+                       CERROR("Truncated userspace udsp buffer given\n");
+                       return -EINVAL;
+               }
+               tmp += size;
+       }
+
+       *bulk_size = remaining_size;
+
+       /* copy over the net type */
+       nid_descr->ud_net_id.udn_net_type = ioc_nid->iud_net.ud_net_type;
+
+       CDEBUG(D_NET, "%u\n", nid_descr->ud_net_id.udn_net_type);
+
+       /* allocate the total memory required to copy this NID descriptor */
+       alloc_size = (sizeof(struct cfs_expr_list) * (expr_count + 1)) +
+                    (sizeof(struct cfs_range_expr) * (range_count));
+       LIBCFS_ALLOC(buf, alloc_size);
+       if (!buf)
+               return -ENOMEM;
+
+       /* store the amount of memory allocated so we can free it later on */
+       nid_descr->ud_mem_size = alloc_size;
+
+       /* copy over the net number range */
+       range_count = ioc_nid->iud_net.ud_net_num_expr.le_count;
+       *bulk += sizeof(*ioc_nid);
+       CDEBUG(D_NET, "bulk = %p\n", *bulk);
+       copy_range_info(bulk, &buf, &nid_descr->ud_net_id.udn_net_num_range,
+                       range_count);
+       CDEBUG(D_NET, "bulk = %p\n", *bulk);
+
+       /* copy over the NID descriptor */
+       for (i = 0; i < expr_count; i++) {
+               copy_range_info(bulk, &buf, &nid_descr->ud_addr_range, -1);
+               CDEBUG(D_NET, "bulk = %p\n", *bulk);
+       }
+
+       return 0;
+}
+
+int
+lnet_udsp_demarshal_add(void *bulk, __u32 bulk_size)
+{
+       struct lnet_ioctl_udsp *ioc_udsp;
+       struct lnet_udsp *udsp;
+       int rc = -ENOMEM;
+       int idx;
+
+       if (bulk_size < sizeof(*ioc_udsp))
+               return -ENOSPC;
+
+       udsp = lnet_udsp_alloc();
+       if (!udsp)
+               return rc;
+
+       ioc_udsp = bulk;
+
+       udsp->udsp_action_type = ioc_udsp->iou_action_type;
+       udsp->udsp_action.udsp_priority = ioc_udsp->iou_action.priority;
+       idx = ioc_udsp->iou_idx;
+
+       CDEBUG(D_NET, "demarshal descr %u:%u:%d:%u\n", udsp->udsp_action_type,
+              udsp->udsp_action.udsp_priority, idx, bulk_size);
+
+       bulk += sizeof(*ioc_udsp);
+       bulk_size -= sizeof(*ioc_udsp);
+
+       rc = copy_ioc_udsp_descr(&udsp->udsp_src, "SRC", &bulk, &bulk_size);
+       if (rc < 0)
+               goto free_udsp;
+
+       rc = copy_ioc_udsp_descr(&udsp->udsp_dst, "DST", &bulk, &bulk_size);
+       if (rc < 0)
+               goto free_udsp;
+
+       rc = copy_ioc_udsp_descr(&udsp->udsp_rte, "RTE", &bulk, &bulk_size);
+       if (rc < 0)
+               goto free_udsp;
+
+       return lnet_udsp_add_policy(udsp, idx);
+
+free_udsp:
+       lnet_udsp_free(udsp);
+       return rc;
+}