1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(C) 2020 Broadcom.
3 * All rights reserved.
4 */
5
6 #include <dirent.h>
7 #include <stdbool.h>
8 #include <sys/queue.h>
9
10 #include <rte_malloc.h>
11 #include <rte_string_fns.h>
12
13 #include "bcmfs_device.h"
14 #include "bcmfs_logs.h"
15 #include "bcmfs_qp.h"
16 #include "bcmfs_vfio.h"
17 #include "bcmfs_sym_pmd.h"
18
19 struct bcmfs_device_attr {
20 const char name[BCMFS_MAX_PATH_LEN];
21 const char suffix[BCMFS_DEV_NAME_LEN];
22 const enum bcmfs_device_type type;
23 const uint32_t offset;
24 const uint32_t version;
25 };
26
27 /* BCMFS supported devices */
28 static struct bcmfs_device_attr dev_table[] = {
29 {
30 .name = "fs4",
31 .suffix = "crypto_mbox",
32 .type = BCMFS_SYM_FS4,
33 .offset = 0,
34 .version = BCMFS_SYM_FS4_VERSION
35 },
36 {
37 .name = "fs5",
38 .suffix = "mbox",
39 .type = BCMFS_SYM_FS5,
40 .offset = 0,
41 .version = BCMFS_SYM_FS5_VERSION
42 },
43 {
44 /* sentinel */
45 }
46 };
47
48 struct bcmfs_hw_queue_pair_ops_table bcmfs_hw_queue_pair_ops_table = {
49 .tl = RTE_SPINLOCK_INITIALIZER,
50 .num_ops = 0
51 };
52
bcmfs_hw_queue_pair_register_ops(const struct bcmfs_hw_queue_pair_ops * h)53 int bcmfs_hw_queue_pair_register_ops(const struct bcmfs_hw_queue_pair_ops *h)
54 {
55 struct bcmfs_hw_queue_pair_ops *ops;
56 int16_t ops_index;
57
58 rte_spinlock_lock(&bcmfs_hw_queue_pair_ops_table.tl);
59
60 if (h->enq_one_req == NULL || h->dequeue == NULL ||
61 h->ring_db == NULL || h->startq == NULL || h->stopq == NULL) {
62 rte_spinlock_unlock(&bcmfs_hw_queue_pair_ops_table.tl);
63 BCMFS_LOG(ERR,
64 "Missing callback while registering device ops");
65 return -EINVAL;
66 }
67
68 if (strlen(h->name) >= sizeof(ops->name) - 1) {
69 rte_spinlock_unlock(&bcmfs_hw_queue_pair_ops_table.tl);
70 BCMFS_LOG(ERR, "%s(): fs device_ops <%s>: name too long",
71 __func__, h->name);
72 return -EEXIST;
73 }
74
75 ops_index = bcmfs_hw_queue_pair_ops_table.num_ops++;
76 ops = &bcmfs_hw_queue_pair_ops_table.qp_ops[ops_index];
77 strlcpy(ops->name, h->name, sizeof(ops->name));
78 ops->enq_one_req = h->enq_one_req;
79 ops->dequeue = h->dequeue;
80 ops->ring_db = h->ring_db;
81 ops->startq = h->startq;
82 ops->stopq = h->stopq;
83
84 rte_spinlock_unlock(&bcmfs_hw_queue_pair_ops_table.tl);
85
86 return ops_index;
87 }
88
89 TAILQ_HEAD(fsdev_list, bcmfs_device);
90 static struct fsdev_list fsdev_list = TAILQ_HEAD_INITIALIZER(fsdev_list);
91
92 static struct bcmfs_device *
fsdev_allocate_one_dev(struct rte_vdev_device * vdev,char * dirpath,char * devname,enum bcmfs_device_type dev_type __rte_unused)93 fsdev_allocate_one_dev(struct rte_vdev_device *vdev,
94 char *dirpath,
95 char *devname,
96 enum bcmfs_device_type dev_type __rte_unused)
97 {
98 struct bcmfs_device *fsdev;
99 uint32_t i;
100
101 fsdev = rte_calloc(__func__, 1, sizeof(*fsdev), 0);
102 if (!fsdev)
103 return NULL;
104
105 if (strlen(dirpath) > sizeof(fsdev->dirname)) {
106 BCMFS_LOG(ERR, "dir path name is too long");
107 goto cleanup;
108 }
109
110 if (strlen(devname) > sizeof(fsdev->name)) {
111 BCMFS_LOG(ERR, "devname is too long");
112 goto cleanup;
113 }
114
115 /* check if registered ops name is present in directory path */
116 for (i = 0; i < bcmfs_hw_queue_pair_ops_table.num_ops; i++)
117 if (strstr(dirpath,
118 bcmfs_hw_queue_pair_ops_table.qp_ops[i].name))
119 fsdev->sym_hw_qp_ops =
120 &bcmfs_hw_queue_pair_ops_table.qp_ops[i];
121 if (!fsdev->sym_hw_qp_ops)
122 goto cleanup;
123
124 strcpy(fsdev->dirname, dirpath);
125 strcpy(fsdev->name, devname);
126
127 fsdev->vdev = vdev;
128
129 /* attach to VFIO */
130 if (bcmfs_attach_vfio(fsdev))
131 goto cleanup;
132
133 /* Maximum number of QPs supported */
134 fsdev->max_hw_qps = fsdev->mmap_size / BCMFS_HW_QUEUE_IO_ADDR_LEN;
135
136 TAILQ_INSERT_TAIL(&fsdev_list, fsdev, next);
137
138 return fsdev;
139
140 cleanup:
141 free(fsdev);
142
143 return NULL;
144 }
145
146 static struct bcmfs_device *
find_fsdev(struct rte_vdev_device * vdev)147 find_fsdev(struct rte_vdev_device *vdev)
148 {
149 struct bcmfs_device *fsdev;
150
151 TAILQ_FOREACH(fsdev, &fsdev_list, next)
152 if (fsdev->vdev == vdev)
153 return fsdev;
154
155 return NULL;
156 }
157
158 static void
fsdev_release(struct bcmfs_device * fsdev)159 fsdev_release(struct bcmfs_device *fsdev)
160 {
161 if (fsdev == NULL)
162 return;
163
164 TAILQ_REMOVE(&fsdev_list, fsdev, next);
165 free(fsdev);
166 }
167
168 static int
cmprator(const void * a,const void * b)169 cmprator(const void *a, const void *b)
170 {
171 return (*(const unsigned int *)a - *(const unsigned int *)b);
172 }
173
174 static int
fsdev_find_all_devs(const char * path,const char * search,uint32_t * devs)175 fsdev_find_all_devs(const char *path, const char *search,
176 uint32_t *devs)
177 {
178 DIR *dir;
179 struct dirent *entry;
180 int count = 0;
181 char addr[BCMFS_MAX_NODES][BCMFS_MAX_PATH_LEN];
182 int i;
183
184 dir = opendir(path);
185 if (dir == NULL) {
186 BCMFS_LOG(ERR, "Unable to open directory");
187 return 0;
188 }
189
190 while ((entry = readdir(dir)) != NULL) {
191 if (strstr(entry->d_name, search)) {
192 strlcpy(addr[count], entry->d_name,
193 BCMFS_MAX_PATH_LEN);
194 count++;
195 }
196 }
197
198 closedir(dir);
199
200 for (i = 0 ; i < count; i++)
201 devs[i] = (uint32_t)strtoul(addr[i], NULL, 16);
202 /* sort the devices based on IO addresses */
203 qsort(devs, count, sizeof(uint32_t), cmprator);
204
205 return count;
206 }
207
208 static bool
fsdev_find_sub_dir(char * path,const char * search,char * output)209 fsdev_find_sub_dir(char *path, const char *search, char *output)
210 {
211 DIR *dir;
212 struct dirent *entry;
213
214 dir = opendir(path);
215 if (dir == NULL) {
216 BCMFS_LOG(ERR, "Unable to open directory");
217 return -ENODEV;
218 }
219
220 while ((entry = readdir(dir)) != NULL) {
221 if (!strcmp(entry->d_name, search)) {
222 strlcpy(output, entry->d_name, BCMFS_MAX_PATH_LEN);
223 closedir(dir);
224 return true;
225 }
226 }
227
228 closedir(dir);
229
230 return false;
231 }
232
233
234 static int
bcmfs_vdev_probe(struct rte_vdev_device * vdev)235 bcmfs_vdev_probe(struct rte_vdev_device *vdev)
236 {
237 struct bcmfs_device *fsdev;
238 char top_dirpath[BCMFS_MAX_PATH_LEN];
239 char sub_dirpath[BCMFS_MAX_PATH_LEN];
240 char out_dirpath[BCMFS_MAX_PATH_LEN];
241 char out_dirname[BCMFS_MAX_PATH_LEN];
242 uint32_t fsdev_dev[BCMFS_MAX_NODES];
243 enum bcmfs_device_type dtype;
244 int err;
245 int i = 0;
246 int dev_idx;
247 int count = 0;
248 bool found = false;
249
250 sprintf(top_dirpath, "%s", SYSFS_BCM_PLTFORM_DEVICES);
251 while (strlen(dev_table[i].name)) {
252 found = fsdev_find_sub_dir(top_dirpath,
253 dev_table[i].name,
254 sub_dirpath);
255 if (found)
256 break;
257 i++;
258 }
259 if (!found) {
260 BCMFS_LOG(ERR, "No supported bcmfs dev found");
261 return -ENODEV;
262 }
263
264 dev_idx = i;
265 dtype = dev_table[i].type;
266
267 snprintf(out_dirpath, sizeof(out_dirpath), "%s/%s",
268 top_dirpath, sub_dirpath);
269 count = fsdev_find_all_devs(out_dirpath,
270 dev_table[dev_idx].suffix,
271 fsdev_dev);
272 if (!count) {
273 BCMFS_LOG(ERR, "No supported bcmfs dev found");
274 return -ENODEV;
275 }
276
277 i = 0;
278 while (count) {
279 /* format the device name present in the patch */
280 snprintf(out_dirname, sizeof(out_dirname), "%x.%s",
281 fsdev_dev[i], dev_table[dev_idx].suffix);
282 fsdev = fsdev_allocate_one_dev(vdev, out_dirpath,
283 out_dirname, dtype);
284 if (!fsdev) {
285 count--;
286 i++;
287 continue;
288 }
289 break;
290 }
291 if (fsdev == NULL) {
292 BCMFS_LOG(ERR, "All supported devs busy");
293 return -ENODEV;
294 }
295
296 err = bcmfs_sym_dev_create(fsdev);
297 if (err) {
298 BCMFS_LOG(WARNING,
299 "Failed to create BCMFS SYM PMD for device %s",
300 fsdev->name);
301 goto pmd_create_fail;
302 }
303
304 return 0;
305
306 pmd_create_fail:
307 fsdev_release(fsdev);
308
309 return err;
310 }
311
312 static int
bcmfs_vdev_remove(struct rte_vdev_device * vdev)313 bcmfs_vdev_remove(struct rte_vdev_device *vdev)
314 {
315 struct bcmfs_device *fsdev;
316
317 fsdev = find_fsdev(vdev);
318 if (fsdev == NULL)
319 return -ENODEV;
320
321 fsdev_release(fsdev);
322 return 0;
323 }
324
325 /* Register with vdev */
326 static struct rte_vdev_driver rte_bcmfs_pmd = {
327 .probe = bcmfs_vdev_probe,
328 .remove = bcmfs_vdev_remove
329 };
330
331 RTE_PMD_REGISTER_VDEV(bcmfs_pmd,
332 rte_bcmfs_pmd);
333