New upstream version 18.05
[deb_dpdk.git] / lib / librte_eal / common / eal_common_dev.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation.
3  * Copyright(c) 2014 6WIND S.A.
4  */
5
6 #include <stdio.h>
7 #include <string.h>
8 #include <inttypes.h>
9 #include <sys/queue.h>
10
11 #include <rte_compat.h>
12 #include <rte_bus.h>
13 #include <rte_dev.h>
14 #include <rte_devargs.h>
15 #include <rte_debug.h>
16 #include <rte_log.h>
17 #include <rte_spinlock.h>
18 #include <rte_malloc.h>
19
20 #include "eal_private.h"
21
22 /**
23  * The device event callback description.
24  *
25  * It contains callback address to be registered by user application,
26  * the pointer to the parameters for callback, and the device name.
27  */
28 struct dev_event_callback {
29         TAILQ_ENTRY(dev_event_callback) next; /**< Callbacks list */
30         rte_dev_event_cb_fn cb_fn;            /**< Callback address */
31         void *cb_arg;                         /**< Callback parameter */
32         char *dev_name;  /**< Callback device name, NULL is for all device */
33         uint32_t active;                      /**< Callback is executing */
34 };
35
36 /** @internal Structure to keep track of registered callbacks */
37 TAILQ_HEAD(dev_event_cb_list, dev_event_callback);
38
39 /* The device event callback list for all registered callbacks. */
40 static struct dev_event_cb_list dev_event_cbs;
41
42 /* spinlock for device callbacks */
43 static rte_spinlock_t dev_event_lock = RTE_SPINLOCK_INITIALIZER;
44
45 static int cmp_detached_dev_name(const struct rte_device *dev,
46         const void *_name)
47 {
48         const char *name = _name;
49
50         /* skip attached devices */
51         if (dev->driver != NULL)
52                 return 1;
53
54         return strcmp(dev->name, name);
55 }
56
57 static int cmp_dev_name(const struct rte_device *dev, const void *_name)
58 {
59         const char *name = _name;
60
61         return strcmp(dev->name, name);
62 }
63
64 int rte_eal_dev_attach(const char *name, const char *devargs)
65 {
66         struct rte_bus *bus;
67
68         if (name == NULL || devargs == NULL) {
69                 RTE_LOG(ERR, EAL, "Invalid device or arguments provided\n");
70                 return -EINVAL;
71         }
72
73         bus = rte_bus_find_by_device_name(name);
74         if (bus == NULL) {
75                 RTE_LOG(ERR, EAL, "Unable to find a bus for the device '%s'\n",
76                         name);
77                 return -EINVAL;
78         }
79         if (strcmp(bus->name, "pci") == 0 || strcmp(bus->name, "vdev") == 0)
80                 return rte_eal_hotplug_add(bus->name, name, devargs);
81
82         RTE_LOG(ERR, EAL,
83                 "Device attach is only supported for PCI and vdev devices.\n");
84
85         return -ENOTSUP;
86 }
87
88 int rte_eal_dev_detach(struct rte_device *dev)
89 {
90         struct rte_bus *bus;
91         int ret;
92
93         if (dev == NULL) {
94                 RTE_LOG(ERR, EAL, "Invalid device provided.\n");
95                 return -EINVAL;
96         }
97
98         bus = rte_bus_find_by_device(dev);
99         if (bus == NULL) {
100                 RTE_LOG(ERR, EAL, "Cannot find bus for device (%s)\n",
101                         dev->name);
102                 return -EINVAL;
103         }
104
105         if (bus->unplug == NULL) {
106                 RTE_LOG(ERR, EAL, "Bus function not supported\n");
107                 return -ENOTSUP;
108         }
109
110         ret = bus->unplug(dev);
111         if (ret)
112                 RTE_LOG(ERR, EAL, "Driver cannot detach the device (%s)\n",
113                         dev->name);
114         return ret;
115 }
116
117 int __rte_experimental rte_eal_hotplug_add(const char *busname, const char *devname,
118                         const char *devargs)
119 {
120         struct rte_bus *bus;
121         struct rte_device *dev;
122         struct rte_devargs *da;
123         int ret;
124
125         bus = rte_bus_find_by_name(busname);
126         if (bus == NULL) {
127                 RTE_LOG(ERR, EAL, "Cannot find bus (%s)\n", busname);
128                 return -ENOENT;
129         }
130
131         if (bus->plug == NULL) {
132                 RTE_LOG(ERR, EAL, "Function plug not supported by bus (%s)\n",
133                         bus->name);
134                 return -ENOTSUP;
135         }
136
137         da = calloc(1, sizeof(*da));
138         if (da == NULL)
139                 return -ENOMEM;
140
141         ret = rte_devargs_parse(da, "%s:%s,%s",
142                                     busname, devname, devargs);
143         if (ret)
144                 goto err_devarg;
145
146         ret = rte_devargs_insert(da);
147         if (ret)
148                 goto err_devarg;
149
150         ret = bus->scan();
151         if (ret)
152                 goto err_devarg;
153
154         dev = bus->find_device(NULL, cmp_detached_dev_name, devname);
155         if (dev == NULL) {
156                 RTE_LOG(ERR, EAL, "Cannot find unplugged device (%s)\n",
157                         devname);
158                 ret = -ENODEV;
159                 goto err_devarg;
160         }
161
162         ret = bus->plug(dev);
163         if (ret) {
164                 RTE_LOG(ERR, EAL, "Driver cannot attach the device (%s)\n",
165                         dev->name);
166                 goto err_devarg;
167         }
168         return 0;
169
170 err_devarg:
171         if (rte_devargs_remove(busname, devname)) {
172                 free(da->args);
173                 free(da);
174         }
175         return ret;
176 }
177
178 int __rte_experimental
179 rte_eal_hotplug_remove(const char *busname, const char *devname)
180 {
181         struct rte_bus *bus;
182         struct rte_device *dev;
183         int ret;
184
185         bus = rte_bus_find_by_name(busname);
186         if (bus == NULL) {
187                 RTE_LOG(ERR, EAL, "Cannot find bus (%s)\n", busname);
188                 return -ENOENT;
189         }
190
191         if (bus->unplug == NULL) {
192                 RTE_LOG(ERR, EAL, "Function unplug not supported by bus (%s)\n",
193                         bus->name);
194                 return -ENOTSUP;
195         }
196
197         dev = bus->find_device(NULL, cmp_dev_name, devname);
198         if (dev == NULL) {
199                 RTE_LOG(ERR, EAL, "Cannot find plugged device (%s)\n", devname);
200                 return -EINVAL;
201         }
202
203         ret = bus->unplug(dev);
204         if (ret)
205                 RTE_LOG(ERR, EAL, "Driver cannot detach the device (%s)\n",
206                         dev->name);
207         rte_devargs_remove(busname, devname);
208         return ret;
209 }
210
211 int __rte_experimental
212 rte_dev_event_callback_register(const char *device_name,
213                                 rte_dev_event_cb_fn cb_fn,
214                                 void *cb_arg)
215 {
216         struct dev_event_callback *event_cb;
217         int ret;
218
219         if (!cb_fn)
220                 return -EINVAL;
221
222         rte_spinlock_lock(&dev_event_lock);
223
224         if (TAILQ_EMPTY(&dev_event_cbs))
225                 TAILQ_INIT(&dev_event_cbs);
226
227         TAILQ_FOREACH(event_cb, &dev_event_cbs, next) {
228                 if (event_cb->cb_fn == cb_fn && event_cb->cb_arg == cb_arg) {
229                         if (device_name == NULL && event_cb->dev_name == NULL)
230                                 break;
231                         if (device_name == NULL || event_cb->dev_name == NULL)
232                                 continue;
233                         if (!strcmp(event_cb->dev_name, device_name))
234                                 break;
235                 }
236         }
237
238         /* create a new callback. */
239         if (event_cb == NULL) {
240                 event_cb = malloc(sizeof(struct dev_event_callback));
241                 if (event_cb != NULL) {
242                         event_cb->cb_fn = cb_fn;
243                         event_cb->cb_arg = cb_arg;
244                         event_cb->active = 0;
245                         if (!device_name) {
246                                 event_cb->dev_name = NULL;
247                         } else {
248                                 event_cb->dev_name = strdup(device_name);
249                                 if (event_cb->dev_name == NULL) {
250                                         ret = -ENOMEM;
251                                         goto error;
252                                 }
253                         }
254                         TAILQ_INSERT_TAIL(&dev_event_cbs, event_cb, next);
255                 } else {
256                         RTE_LOG(ERR, EAL,
257                                 "Failed to allocate memory for device "
258                                 "event callback.");
259                         ret = -ENOMEM;
260                         goto error;
261                 }
262         } else {
263                 RTE_LOG(ERR, EAL,
264                         "The callback is already exist, no need "
265                         "to register again.\n");
266                 ret = -EEXIST;
267         }
268
269         rte_spinlock_unlock(&dev_event_lock);
270         return 0;
271 error:
272         free(event_cb);
273         rte_spinlock_unlock(&dev_event_lock);
274         return ret;
275 }
276
277 int __rte_experimental
278 rte_dev_event_callback_unregister(const char *device_name,
279                                   rte_dev_event_cb_fn cb_fn,
280                                   void *cb_arg)
281 {
282         int ret = 0;
283         struct dev_event_callback *event_cb, *next;
284
285         if (!cb_fn)
286                 return -EINVAL;
287
288         rte_spinlock_lock(&dev_event_lock);
289         /*walk through the callbacks and remove all that match. */
290         for (event_cb = TAILQ_FIRST(&dev_event_cbs); event_cb != NULL;
291              event_cb = next) {
292
293                 next = TAILQ_NEXT(event_cb, next);
294
295                 if (device_name != NULL && event_cb->dev_name != NULL) {
296                         if (!strcmp(event_cb->dev_name, device_name)) {
297                                 if (event_cb->cb_fn != cb_fn ||
298                                     (cb_arg != (void *)-1 &&
299                                     event_cb->cb_arg != cb_arg))
300                                         continue;
301                         }
302                 } else if (device_name != NULL) {
303                         continue;
304                 }
305
306                 /*
307                  * if this callback is not executing right now,
308                  * then remove it.
309                  */
310                 if (event_cb->active == 0) {
311                         TAILQ_REMOVE(&dev_event_cbs, event_cb, next);
312                         free(event_cb);
313                         ret++;
314                 } else {
315                         continue;
316                 }
317         }
318         rte_spinlock_unlock(&dev_event_lock);
319         return ret;
320 }
321
322 void
323 dev_callback_process(char *device_name, enum rte_dev_event_type event)
324 {
325         struct dev_event_callback *cb_lst;
326
327         if (device_name == NULL)
328                 return;
329
330         rte_spinlock_lock(&dev_event_lock);
331
332         TAILQ_FOREACH(cb_lst, &dev_event_cbs, next) {
333                 if (cb_lst->dev_name) {
334                         if (strcmp(cb_lst->dev_name, device_name))
335                                 continue;
336                 }
337                 cb_lst->active = 1;
338                 rte_spinlock_unlock(&dev_event_lock);
339                 cb_lst->cb_fn(device_name, event,
340                                 cb_lst->cb_arg);
341                 rte_spinlock_lock(&dev_event_lock);
342                 cb_lst->active = 0;
343         }
344         rte_spinlock_unlock(&dev_event_lock);
345 }