Whamcloud - gitweb
LU-16035 kfilnd: Initial kfilnd implementation
[fs/lustre-release.git] / lnet / klnds / kfilnd / kfilnd_dom.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright 2022 Hewlett Packard Enterprise Development LP
24  */
25 /*
26  * This file is part of Lustre, http://www.lustre.org/
27  */
28 /*
29  * kfilnd domain and fabric implementation.
30  */
31 #include "kfilnd_dom.h"
32 #include "kfilnd_tn.h"
33
34 /* Global list of allocated KFI LND fabrics. */
35 static LIST_HEAD(fab_list);
36 static DEFINE_MUTEX(fab_list_lock);
37
38 /**
39  * kfilnd_dom_free() - Free a KFI LND domain.
40  * @dom: KFI LND domain to be freed.
41  */
42 static void kfilnd_dom_free(struct kref *kref)
43 {
44         struct kfilnd_dom *dom;
45
46         if (!kref)
47                 return;
48
49         dom = container_of(kref, struct kfilnd_dom, cnt);
50
51         mutex_lock(&dom->fab->dom_list_lock);
52         list_del(&dom->entry);
53         mutex_unlock(&dom->fab->dom_list_lock);
54
55         kfi_close(&dom->domain->fid);
56         LIBCFS_FREE(dom, sizeof(*dom));
57 }
58
59 /**
60  * kfilnd_dom_alloc() - Allocate a new KFI LND domain.
61  * @dom_info: KFI info structure used to allocate the KFI LND domain.
62  * @fab: KFI LND fabric used by the domain.
63  *
64  * A KFI LND domain (and the underlying KFI domain) provides access to a
65  * specific NIC on a fabric. The same KFI LND domain can be used to allocate
66  * different KFI LND devices.
67  *
68  * Return: On success, valid pointer. Else, negative errno pointer.
69  */
70 static struct kfilnd_dom *kfilnd_dom_alloc(struct kfi_info *dom_info,
71                                            struct kfilnd_fab *fab)
72 {
73         int rc;
74         struct kfilnd_dom *dom;
75
76         if (!dom_info || !fab) {
77                 rc = -EINVAL;
78                 goto err;
79         }
80
81         LIBCFS_ALLOC_GFP(dom, sizeof(*dom), GFP_KERNEL);
82         if (!dom) {
83                 rc = -ENOMEM;
84                 goto err;
85         }
86
87         INIT_LIST_HEAD(&dom->dev_list);
88         spin_lock_init(&dom->lock);
89         dom->fab = fab;
90         kref_init(&dom->cnt);
91
92         rc = kfi_domain(fab->fabric, dom_info, &dom->domain, dom);
93         if (rc) {
94                 CERROR("Failed to create KFI domain: rc=%d\n", rc);
95                 goto err_free_dom;
96         }
97
98         mutex_lock(&fab->dom_list_lock);
99         list_add_tail(&dom->entry, &fab->dom_list);
100         mutex_unlock(&fab->dom_list_lock);
101
102         return dom;
103
104 err_free_dom:
105         LIBCFS_FREE(dom, sizeof(*dom));
106 err:
107         return ERR_PTR(rc);
108 }
109
110 /**
111  * kfilnd_dom_reuse() - Attempt to reuse an already allocated domain.
112  * @node: Node string used to limit domains to.
113  * @service: Service string used to limit domains to.
114  * @hints: Hints used to allocate KFI info structures.
115  * @fab: Fabric used to limit domains to.
116  *
117  * Return: On success (matching domain is found), valid pointer is returned.
118  * Else, NULL.
119  */
120 struct kfilnd_dom *kfilnd_dom_reuse(const char *node, const char *service,
121                                     struct kfi_info *hints,
122                                     struct kfilnd_fab *fab)
123 {
124         struct kfilnd_dom *dom;
125         struct kfi_info *info;
126         int rc;
127
128         if (!node || !service || !hints || !fab)
129                 return NULL;
130
131         /* Update the hints domain attribute with an already allocated domain to
132          * see if domains can be reused.
133          */
134         hints->fabric_attr->fabric = fab->fabric;
135
136         mutex_lock(&fab->dom_list_lock);
137         list_for_each_entry(dom, &fab->dom_list, entry) {
138                 hints->domain_attr->domain = dom->domain;
139
140                 rc = kfi_getinfo(0, node, service, KFI_SOURCE, hints, &info);
141                 if (!rc) {
142                         kref_get(&dom->cnt);
143
144                         mutex_unlock(&fab->dom_list_lock);
145
146                         kfi_freeinfo(info);
147
148                         return dom;
149                 }
150         }
151         mutex_unlock(&fab->dom_list_lock);
152
153         hints->domain_attr->domain = NULL;
154
155         return NULL;
156 }
157
158 /**
159  * kfilnd_fab_free() - Free KFI LND fabric.
160  */
161 static void kfilnd_fab_free(struct kref *kref)
162 {
163         struct kfilnd_fab *fab;
164
165         if (!kref)
166                 return;
167
168         fab = container_of(kref, struct kfilnd_fab, cnt);
169
170         mutex_lock(&fab_list_lock);
171         list_del(&fab->entry);
172         mutex_unlock(&fab_list_lock);
173
174         kfi_close(&fab->fabric->fid);
175         LIBCFS_FREE(fab, sizeof(*fab));
176 }
177
178 /**
179  * kfilnd_fab_alloc() - Allocate a new KFI LND fabric.
180  * @attr: KFI fabric attributes used to allocate the underlying KFI fabric.
181  *
182  * A KFI LND fabric (and the underlying KFI fabric) providers access to NICs on
183  * the same fabric. The underlying KFI fabric should be shared between all NICs
184  * (KFI domains) on the same fabric.
185  *
186  * Return: On success, valid pointer. Else, negative errno pointer.
187  */
188 static struct kfilnd_fab *kfilnd_fab_alloc(struct kfi_fabric_attr *attr)
189 {
190         int rc;
191         struct kfilnd_fab *fab;
192
193         if (!attr) {
194                 rc = -EINVAL;
195                 goto err;
196         }
197
198         LIBCFS_ALLOC_GFP(fab, sizeof(*fab), GFP_KERNEL);
199         if (!fab) {
200                 rc = -ENOMEM;
201                 goto err;
202         }
203
204         INIT_LIST_HEAD(&fab->dom_list);
205         mutex_init(&fab->dom_list_lock);
206         kref_init(&fab->cnt);
207
208         rc = kfi_fabric(attr, &fab->fabric, fab);
209         if (rc) {
210                 CERROR("Failed to allocate KFI fabric: rc=%d\n", rc);
211                 goto err_free_fab;
212         }
213
214         mutex_lock(&fab_list_lock);
215         list_add_tail(&fab->entry, &fab_list);
216         mutex_unlock(&fab_list_lock);
217
218         return fab;
219
220 err_free_fab:
221         LIBCFS_FREE(fab, sizeof(*fab));
222 err:
223         return ERR_PTR(rc);
224 }
225
226 /**
227  * kfilnd_fab_reuse() - Attempt to reuse an already allocated fabric.
228  * @node: Node string used to limit fabrics to.
229  * @service: Service string used to limit fabrics to.
230  * @hints: Hints used to allocate KFI info structures.
231  *
232  * Return: On success (matching fabric is found), valid pointer is returned.
233  * Else, NULL.
234  */
235 struct kfilnd_fab *kfilnd_fab_reuse(const char *node, const char *service,
236                                     struct kfi_info *hints)
237 {
238         struct kfilnd_fab *fab;
239         struct kfi_info *info;
240         int rc;
241
242         if (!node || !service || !hints)
243                 return NULL;
244
245         /* Update the hints fabric attribute with an already allocated fabric to
246          * see if fabrics can be reused.
247          */
248         mutex_lock(&fab_list_lock);
249         list_for_each_entry(fab, &fab_list, entry) {
250                 hints->fabric_attr->fabric = fab->fabric;
251
252                 rc = kfi_getinfo(0, node, service, KFI_SOURCE, hints, &info);
253                 if (!rc) {
254                         kref_get(&fab->cnt);
255
256                         mutex_unlock(&fab_list_lock);
257
258                         kfi_freeinfo(info);
259
260                         return fab;
261                 }
262         }
263         mutex_unlock(&fab_list_lock);
264
265         hints->fabric_attr->fabric = NULL;
266
267         return NULL;
268 }
269
270 /**
271  * kfi_domain_put() - Put a KFI LND domain reference.
272  */
273 void kfilnd_dom_put(struct kfilnd_dom *dom)
274 {
275         struct kfilnd_fab *fab;
276
277         if (!dom)
278                 return;
279
280         fab = dom->fab;
281
282         kref_put(&dom->cnt, kfilnd_dom_free);
283
284         kref_put(&fab->cnt, kfilnd_fab_free);
285 }
286
287 /**
288  * kfilnd_dom_get() - Get a KFI LND domain.
289  * @ni: LNet NI used to define the KFI LND domain address.
290  * @node: Node string which can be passed into kfi_getinfo().
291  * @dev_info: KFI info structure which should be used to allocate a KFI LND
292  * device using this domain.
293  *
294  * On success, a KFI info structure is returned to the user in addition to a KFI
295  * LND domain. Callers should free the KFI info structure once done using it.
296  *
297  * Return: On success, dev_info is set to a valid KFI info structure and a valid
298  * KFI LND domain is returned. Else, negative errno pointer is returned.
299  */
300 struct kfilnd_dom *kfilnd_dom_get(struct lnet_ni *ni, const char *node,
301                                   struct kfi_info **dev_info)
302 {
303         int rc;
304         struct kfi_info *hints;
305         struct kfi_info *info;
306         struct kfi_info *hints_tmp;
307         struct kfi_info *info_tmp;
308         struct kfilnd_fab *fab;
309         struct kfilnd_dom *dom;
310         struct kfi_cxi_fabric_ops *fab_ops;
311         char *service;
312
313         if (!ni || !dev_info) {
314                 rc = -EINVAL;
315                 goto err;
316         }
317
318         service = kasprintf(GFP_KERNEL, "%u", ni->ni_nid.nid_num);
319         if (!service) {
320                 rc = -ENOMEM;
321                 goto err;
322         }
323
324         hints = kfi_allocinfo();
325         if (!hints) {
326                 rc = -ENOMEM;
327                 goto err_free_service;
328         }
329
330         hints->caps = KFI_MSG | KFI_RMA | KFI_SEND | KFI_RECV | KFI_READ |
331                 KFI_WRITE | KFI_REMOTE_READ | KFI_REMOTE_WRITE |
332                 KFI_MULTI_RECV | KFI_REMOTE_COMM | KFI_NAMED_RX_CTX |
333                 KFI_TAGGED | KFI_TAGGED_RMA | KFI_DIRECTED_RECV;
334         hints->fabric_attr->prov_version =
335                 KFI_VERSION(ni->ni_lnd_tunables.lnd_tun_u.lnd_kfi.lnd_prov_major_version,
336                             ni->ni_lnd_tunables.lnd_tun_u.lnd_kfi.lnd_prov_minor_version);
337         hints->domain_attr->mr_iov_limit = 256; /* 1 MiB LNet message */
338         hints->domain_attr->mr_key_size = sizeof(int);
339         hints->domain_attr->resource_mgmt = KFI_RM_DISABLED;
340         hints->ep_attr->max_msg_size = LNET_MAX_PAYLOAD;
341         hints->rx_attr->op_flags = KFI_COMPLETION | KFI_MULTI_RECV;
342         hints->rx_attr->iov_limit = 256; /* 1 MiB LNet message */
343         hints->tx_attr->op_flags = KFI_COMPLETION;
344         hints->tx_attr->iov_limit = 256; /* 1 MiB LNet message */
345         hints->tx_attr->rma_iov_limit = 256; /* 1 MiB LNet message */
346         hints->ep_attr->auth_key =
347                 (void *)&ni->ni_lnd_tunables.lnd_tun_u.lnd_kfi.lnd_auth_key;
348         hints->ep_attr->auth_key_size =
349                 sizeof(ni->ni_lnd_tunables.lnd_tun_u.lnd_kfi.lnd_auth_key);
350
351         /* Check if dynamic resource allocation is supported.
352          * Set dynamic resource alloc hints if it is.
353          *
354          * Need to check if op is supported since due to a bug can't
355          * simply set ctx_cnts greater than 1 (default value) if it isn't.
356          */
357         hints_tmp = kfi_dupinfo(hints);
358         if (hints_tmp) {
359                 rc = kfi_getinfo(0, node, service, KFI_SOURCE, hints_tmp,
360                                  &info_tmp);
361                 if (!rc) {
362                         fab = kfilnd_fab_alloc(info_tmp->fabric_attr);
363                         if (!IS_ERR(fab)) {
364                                 rc = kfi_open_ops(&fab->fabric->fid,
365                                         KFI_CXI_FAB_OPS_1, 0, (void **)&fab_ops,
366                                         NULL);
367                                 if (!rc) {
368                                         /* Set dynamic resource alloc hints */
369                                         hints->domain_attr->cq_cnt = ni->ni_ncpts * 2;
370                                         hints->domain_attr->tx_ctx_cnt = ni->ni_ncpts;
371                                         hints->domain_attr->rx_ctx_cnt = ni->ni_ncpts;
372                                         hints->rx_attr->size =
373                                                 ni->ni_net->net_tunables.lct_max_tx_credits +
374                                                 immediate_rx_buf_count;
375                                 }
376                                 kref_put(&fab->cnt, kfilnd_fab_free);
377                         }
378                         kfi_freeinfo(info_tmp);
379                 }
380                 kfi_freeinfo(hints_tmp);
381         }
382
383         /* Check to see if any KFI LND fabrics/domains can be reused. */
384         fab = kfilnd_fab_reuse(node, service, hints);
385         dom = kfilnd_dom_reuse(node, service, hints, fab);
386
387         if (fab)
388                 hints->fabric_attr->fabric = fab->fabric;
389         if (dom)
390                 hints->domain_attr->domain = dom->domain;
391
392         /* Allocate the official KFI info structure to be used for KFI LND
393          * device allocation.
394          */
395         rc = kfi_getinfo(0, node, service, KFI_SOURCE, hints, &info);
396
397         /* Authorization key information is now stored in the returned kfi_info
398          * structure. Since kfi_freeinfo() will try to free the auth_key pointer
399          * and this memory is owned as part of the LNet NI, need to zero this
400          * information in the hints to prevent LNet NI corruption.
401          */
402         hints->ep_attr->auth_key = NULL;
403         hints->ep_attr->auth_key_size = 0;
404
405         kfi_freeinfo(hints);
406         kfree(service);
407         node = NULL;
408         service = NULL;
409
410         if (rc)
411                 goto err_free_service;
412
413         /* Allocate a new KFI LND fabric and domain if necessary. */
414         if (!fab) {
415                 fab = kfilnd_fab_alloc(info->fabric_attr);
416                 if (IS_ERR(fab)) {
417                         rc = PTR_ERR(fab);
418                         goto err_free_info;
419                 }
420         }
421
422         if (!dom) {
423                 /* Enable dynamic resource allocation if operation supported */
424                 rc = kfi_open_ops(&fab->fabric->fid, KFI_CXI_FAB_OPS_1, 0,
425                                   (void **)&fab_ops, NULL);
426                 if (!rc) {
427                         rc = fab_ops->enable_dynamic_rsrc_alloc(&fab->fabric->fid, true);
428                         if (!rc)
429                                 CDEBUG(D_NET, "Enabled dynamic resource allocation for KFI domain\n");
430                 }
431                 dom = kfilnd_dom_alloc(info, fab);
432                 if (IS_ERR(dom)) {
433                         rc = PTR_ERR(dom);
434                         goto err_put_fab;
435                 }
436         }
437
438         *dev_info = info;
439
440         return dom;
441
442 err_put_fab:
443         kref_put(&fab->cnt, kfilnd_fab_free);
444 err_free_info:
445         kfi_freeinfo(info);
446 err_free_service:
447         kfree(service);
448 err:
449         return ERR_PTR(rc);
450 }