Whamcloud - gitweb
LU-9121 lnet: Add the userspace Marshalling API
[fs/lustre-release.git] / lnet / utils / lnetconfig / liblnetconfig_udsp.c
1 /*
2  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
3  *
4  * Copyright (c) 2011, 2017, Intel Corporation.
5  *
6  * Copyright (c) 2018-2020 Data Direct Networks.
7  *
8  *   This file is part of Lustre, https://wiki.whamcloud.com/
9  *
10  *   Portals is free software; you can redistribute it and/or
11  *   modify it under the terms of version 2 of the GNU General Public
12  *   License as published by the Free Software Foundation.
13  *
14  *   Portals is distributed in the hope that it will be useful,
15  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *   GNU General Public License for more details.
18  *
19  *   You should have received a copy of the GNU General Public License
20  *   version 2 along with this program; If not, see
21  *   http://www.gnu.org/licenses/gpl-2.0.html
22  *
23  * Author: Sonia Sharma
24  */
25 /*
26  * Copyright (c) 2020, Whamcloud.
27  *
28  */
29
30 #include <errno.h>
31 #include <limits.h>
32 #include <byteswap.h>
33 #include <netdb.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <sys/ioctl.h>
39 #include <libcfs/util/ioctl.h>
40 #include <linux/lnet/lnetctl.h>
41 #include "liblnd.h"
42 #include <sys/types.h>
43 #include <fcntl.h>
44 #include <ctype.h>
45 #include "liblnetconfig.h"
46
47 static inline bool
48 lnet_udsp_criteria_present(struct lnet_ud_nid_descr *descr)
49 {
50         return descr->ud_net_id.udn_net_type != 0;
51 }
52
53 struct lnet_udsp *lnet_udsp_alloc(void)
54 {
55         struct lnet_udsp *udsp;
56
57         udsp = calloc(1, sizeof(*udsp));
58
59         if (!udsp)
60                 return NULL;
61
62         INIT_LIST_HEAD(&udsp->udsp_on_list);
63         INIT_LIST_HEAD(&udsp->udsp_src.ud_addr_range);
64         INIT_LIST_HEAD(&udsp->udsp_src.ud_net_id.udn_net_num_range);
65         INIT_LIST_HEAD(&udsp->udsp_dst.ud_addr_range);
66         INIT_LIST_HEAD(&udsp->udsp_dst.ud_net_id.udn_net_num_range);
67         INIT_LIST_HEAD(&udsp->udsp_rte.ud_addr_range);
68         INIT_LIST_HEAD(&udsp->udsp_rte.ud_net_id.udn_net_num_range);
69
70         return udsp;
71 }
72
73 static void
74 lnet_udsp_nid_descr_free(struct lnet_ud_nid_descr *nid_descr, bool blk)
75 {
76         struct list_head *net_range = &nid_descr->ud_net_id.udn_net_num_range;
77
78         if (!lnet_udsp_criteria_present(nid_descr))
79                 return;
80
81         /* memory management is a bit tricky here. When we allocate the
82          * memory to store the NID descriptor we allocate a large buffer
83          * for all the data, so we need to free the entire buffer at
84          * once. If the net is present the net_range->next points to that
85          * buffer otherwise if the ud_addr_range is present then it's the
86          * ud_addr_range.next
87          */
88         if (blk) {
89                 if (!list_empty(net_range))
90                         free(net_range->next);
91                 else if (!list_empty(&nid_descr->ud_addr_range))
92                         free(nid_descr->ud_addr_range.next);
93         } else {
94                 cfs_expr_list_free_list(net_range);
95                 cfs_expr_list_free_list(&nid_descr->ud_addr_range);
96         }
97 }
98
99 void
100 lnet_udsp_free(struct lnet_udsp *udsp, bool blk)
101 {
102         lnet_udsp_nid_descr_free(&udsp->udsp_src, blk);
103         lnet_udsp_nid_descr_free(&udsp->udsp_dst, blk);
104         lnet_udsp_nid_descr_free(&udsp->udsp_rte, blk);
105
106         free(udsp);
107 }
108
109 static void
110 copy_range_info(void __user **bulk, void **buf, struct list_head *list,
111                 int count)
112 {
113         struct lnet_range_expr *range_expr;
114         struct cfs_range_expr *range;
115         struct cfs_expr_list *exprs;
116         int range_count = count;
117         int i;
118
119         if (range_count == 0)
120                 return;
121
122         if (range_count == -1) {
123                 struct lnet_expressions *e;
124
125                 e = *bulk;
126                 range_count = e->le_count;
127                 *bulk += sizeof(*e);
128         }
129
130         exprs = *buf;
131         INIT_LIST_HEAD(&exprs->el_link);
132         INIT_LIST_HEAD(&exprs->el_exprs);
133         list_add_tail(&exprs->el_link, list);
134         *buf += sizeof(*exprs);
135
136         for (i = 0; i < range_count; i++) {
137                 range_expr = *bulk;
138                 range = *buf;
139                 INIT_LIST_HEAD(&range->re_link);
140                 range->re_lo = range_expr->re_lo;
141                 range->re_hi = range_expr->re_hi;
142                 range->re_stride = range_expr->re_stride;
143                 list_add_tail(&range->re_link, &exprs->el_exprs);
144                 *bulk += sizeof(*range_expr);
145                 *buf += sizeof(*range);
146         }
147 }
148
149 static int
150 copy_ioc_udsp_descr(struct lnet_ud_nid_descr *nid_descr, char *type,
151                     void **bulk, __u32 *bulk_size)
152 {
153         struct lnet_ioctl_udsp_descr *ioc_nid = *bulk;
154         struct lnet_expressions *exprs;
155         __u32 descr_type;
156         int expr_count = 0;
157         int range_count = 0;
158         int i;
159         __u32 size;
160         int remaining_size = *bulk_size;
161         void *tmp = *bulk;
162         __u32 alloc_size;
163         void *buf;
164         size_t range_expr_s = sizeof(struct lnet_range_expr);
165         size_t lnet_exprs_s = sizeof(struct lnet_expressions);
166
167         /* criteria not present, skip over the static part of the
168          * bulk, which is included for each NID descriptor
169          */
170         if (ioc_nid->iud_net.ud_net_type == 0) {
171                 remaining_size -= sizeof(*ioc_nid);
172                 if (remaining_size < 0)
173                         return -EINVAL;
174                 *bulk += sizeof(*ioc_nid);
175                 *bulk_size = remaining_size;
176                 return 0;
177         }
178
179         descr_type = ioc_nid->iud_src_hdr.ud_descr_type;
180         if (descr_type != *(__u32 *)type)
181                 return -EINVAL;
182
183         /* calculate the total size to verify we have enough buffer.
184          * Start of by finding how many ranges there are for the net
185          * expression.
186          */
187         range_count = ioc_nid->iud_net.ud_net_num_expr.le_count;
188         size = sizeof(*ioc_nid) + (range_count * range_expr_s);
189         remaining_size -= size;
190         if (remaining_size < 0)
191                 return -EINVAL;
192
193         /* the number of expressions for the NID. IE 4 for IP, 1 for GNI */
194         expr_count = ioc_nid->iud_src_hdr.ud_descr_count;
195         /* point tmp to the beginning of the NID expressions */
196         tmp += size;
197         for (i = 0; i < expr_count; i++) {
198                 /* get the number of ranges per expression */
199                 exprs = tmp;
200                 range_count += exprs->le_count;
201                 size = (range_expr_s * exprs->le_count) + lnet_exprs_s;
202                 remaining_size -= size;
203                 if (remaining_size < 0)
204                         return -EINVAL;
205                 tmp += size;
206         }
207
208         *bulk_size = remaining_size;
209
210         /* copy over the net type */
211         nid_descr->ud_net_id.udn_net_type = ioc_nid->iud_net.ud_net_type;
212
213         /* allocate the total memory required to copy this NID descriptor */
214         alloc_size = (sizeof(struct cfs_expr_list) * (expr_count + 1)) +
215                      (sizeof(struct cfs_range_expr) * (range_count));
216         buf = calloc(alloc_size, 1);
217         if (!buf)
218                 return -ENOMEM;
219
220         /* copy over the net number range */
221         range_count = ioc_nid->iud_net.ud_net_num_expr.le_count;
222         *bulk += sizeof(*ioc_nid);
223         copy_range_info(bulk, &buf, &nid_descr->ud_net_id.udn_net_num_range,
224                         range_count);
225
226         /* copy over the NID descriptor */
227         for (i = 0; i < expr_count; i++)
228                 copy_range_info(bulk, &buf, &nid_descr->ud_addr_range, -1);
229
230         return 0;
231 }
232
233 struct lnet_udsp *
234 lnet_udsp_demarshal(void *bulk, __u32 bulk_size)
235 {
236         struct lnet_ioctl_udsp *ioc_udsp;
237         struct lnet_udsp *udsp;
238         int rc = -ENOMEM;
239
240         if (bulk_size < sizeof(*ioc_udsp))
241                 return NULL;
242
243         udsp = lnet_udsp_alloc();
244         if (!udsp)
245                 return NULL;
246
247         ioc_udsp = bulk;
248
249         udsp->udsp_action_type = ioc_udsp->iou_action_type;
250         udsp->udsp_action.udsp_priority = ioc_udsp->iou_action.priority;
251         udsp->udsp_idx = ioc_udsp->iou_idx;
252
253         bulk = ioc_udsp->iou_bulk;
254         bulk_size -= sizeof(*ioc_udsp);
255
256         if (bulk_size != ioc_udsp->iou_bulk_size)
257                 goto failed;
258
259         rc = copy_ioc_udsp_descr(&udsp->udsp_src, "SRC", &bulk, &bulk_size);
260         if (rc < 0)
261                 goto failed;
262
263         rc = copy_ioc_udsp_descr(&udsp->udsp_dst, "DST", &bulk, &bulk_size);
264         if (rc < 0)
265                 goto failed;
266
267         rc = copy_ioc_udsp_descr(&udsp->udsp_rte, "RTE", &bulk, &bulk_size);
268         if (rc < 0)
269                 goto failed;
270
271         return udsp;
272
273 failed:
274         lnet_udsp_free(udsp, true);
275         return NULL;
276 }
277
278 static inline int
279 lnet_get_list_len(struct list_head *list)
280 {
281         struct list_head *l;
282         int count = 0;
283
284         list_for_each(l, list)
285                 count++;
286
287         return count;
288 }
289
290 static size_t
291 lnet_size_marshaled_nid_descr(struct lnet_ud_nid_descr *descr)
292 {
293         struct cfs_expr_list *expr;
294         int expr_count = 0;
295         int range_count = 0;
296         size_t size = sizeof(struct lnet_ioctl_udsp_descr);
297
298         if (!lnet_udsp_criteria_present(descr))
299                 return size;
300
301         if (!list_empty(&descr->ud_net_id.udn_net_num_range)) {
302                 expr = list_entry(descr->ud_net_id.udn_net_num_range.next,
303                                   struct cfs_expr_list, el_link);
304                 range_count = lnet_get_list_len(&expr->el_exprs);
305         }
306
307         /* count the number of cfs_range_expr in the address expressions */
308         list_for_each_entry(expr, &descr->ud_addr_range, el_link) {
309                 expr_count++;
310                 range_count += lnet_get_list_len(&expr->el_exprs);
311         }
312
313         size += (sizeof(struct lnet_expressions) * expr_count);
314         size += (sizeof(struct lnet_range_expr) * range_count);
315
316         return size;
317 }
318
319 size_t
320 lnet_get_udsp_size(struct lnet_udsp *udsp)
321 {
322         size_t size = sizeof(struct lnet_ioctl_udsp);
323
324         size += lnet_size_marshaled_nid_descr(&udsp->udsp_src);
325         size += lnet_size_marshaled_nid_descr(&udsp->udsp_dst);
326         size += lnet_size_marshaled_nid_descr(&udsp->udsp_rte);
327
328         return size;
329 }
330
331 static void
332 copy_exprs(struct cfs_expr_list *expr, void __user **bulk,
333            __s32 *bulk_size)
334 {
335         struct cfs_range_expr *range;
336         struct lnet_range_expr range_expr;
337
338         /* copy over the net range expressions to the bulk */
339         list_for_each_entry(range, &expr->el_exprs, re_link) {
340                 range_expr.re_lo = range->re_lo;
341                 range_expr.re_hi = range->re_hi;
342                 range_expr.re_stride = range->re_stride;
343                 memcpy(*bulk, &range_expr, sizeof(range_expr));
344                 *bulk += sizeof(range_expr);
345                 *bulk_size -= sizeof(range_expr);
346         }
347 }
348
349 static int
350 copy_nid_range(struct lnet_ud_nid_descr *nid_descr, char *type,
351                 void __user **bulk, __s32 *bulk_size)
352 {
353         struct lnet_ioctl_udsp_descr ioc_udsp_descr = { { 0 } };
354         struct cfs_expr_list *expr;
355         struct lnet_expressions ioc_expr;
356         int expr_count;
357         int net_expr_count = 0;
358
359         ioc_udsp_descr.iud_src_hdr.ud_descr_type = *(__u32 *)type;
360
361         /* if criteria not present, copy over the static part of the NID
362          * descriptor
363          */
364         if (!lnet_udsp_criteria_present(nid_descr)) {
365                 memcpy(*bulk, &ioc_udsp_descr,
366                         sizeof(ioc_udsp_descr));
367                 *bulk += sizeof(ioc_udsp_descr);
368                 *bulk_size -= sizeof(ioc_udsp_descr);
369                 return 0;
370         }
371
372         expr_count = lnet_get_list_len(&nid_descr->ud_addr_range);
373
374         /* copy the net information */
375         if (!list_empty(&nid_descr->ud_net_id.udn_net_num_range)) {
376                 expr = list_entry(nid_descr->ud_net_id.udn_net_num_range.next,
377                                   struct cfs_expr_list, el_link);
378                 net_expr_count = lnet_get_list_len(&expr->el_exprs);
379         } else {
380                 net_expr_count = 0;
381         }
382
383         /* set the total expression count */
384         ioc_udsp_descr.iud_src_hdr.ud_descr_count = expr_count;
385         ioc_udsp_descr.iud_net.ud_net_type =
386                 nid_descr->ud_net_id.udn_net_type;
387         ioc_udsp_descr.iud_net.ud_net_num_expr.le_count = net_expr_count;
388
389         /* copy over the header info to the bulk */
390         memcpy(*bulk, &ioc_udsp_descr, sizeof(ioc_udsp_descr));
391         *bulk += sizeof(ioc_udsp_descr);
392         *bulk_size -= sizeof(ioc_udsp_descr);
393
394         /* copy over the net num expression if it exists */
395         if (net_expr_count)
396                 copy_exprs(expr, bulk, bulk_size);
397
398         /* copy the address range */
399         list_for_each_entry(expr, &nid_descr->ud_addr_range, el_link) {
400                 ioc_expr.le_count = lnet_get_list_len(&expr->el_exprs);
401                 memcpy(*bulk, &ioc_expr, sizeof(ioc_expr));
402                 *bulk += sizeof(ioc_expr);
403                 *bulk_size -= sizeof(ioc_expr);
404
405                 copy_exprs(expr, bulk, bulk_size);
406         }
407
408         return 0;
409 }
410
411 int
412 lnet_udsp_marshal(struct lnet_udsp *udsp, void *bulk,
413                   __s32 bulk_size)
414 {
415         struct lnet_ioctl_udsp *ioc_udsp;
416         int rc = -ENOMEM;
417
418         /* make sure user space allocated enough buffer to marshal the
419          * udsp
420          */
421         if (bulk_size < lnet_get_udsp_size(udsp))
422                 return -EINVAL;
423
424         ioc_udsp = bulk;
425
426         ioc_udsp->iou_idx = udsp->udsp_idx;
427         ioc_udsp->iou_action_type = udsp->udsp_action_type;
428         ioc_udsp->iou_action.priority = udsp->udsp_action.udsp_priority;
429
430         bulk += sizeof(*ioc_udsp);
431         bulk_size -= sizeof(*ioc_udsp);
432
433         rc = copy_nid_range(&udsp->udsp_src, "SRC", &bulk, &bulk_size);
434         if (rc != 0)
435                 return rc;
436
437         rc = copy_nid_range(&udsp->udsp_dst, "DST", &bulk, &bulk_size);
438         if (rc != 0)
439                 return rc;
440
441         rc = copy_nid_range(&udsp->udsp_rte, "RTE", &bulk, &bulk_size);
442
443         return rc;
444 }
445