1 /*
2  * virnodedeviceobj.c: node device object handling
3  *                     (derived from node_device_conf.c)
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library.  If not, see
17  * <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <config.h>
21 
22 #include "datatypes.h"
23 #include "node_device_conf.h"
24 
25 #include "viralloc.h"
26 #include "virnodedeviceobj.h"
27 #include "virerror.h"
28 #include "virhash.h"
29 #include "virlog.h"
30 #include "virstring.h"
31 
32 #define VIR_FROM_THIS VIR_FROM_NODEDEV
33 
34 VIR_LOG_INIT("conf.virnodedeviceobj");
35 
36 struct _virNodeDeviceObj {
37     virObjectLockable parent;
38 
39     virNodeDeviceDef *def;            /* device definition */
40     bool skipUpdateCaps;                /* whether to skip checking host caps,
41                                            used by testdriver */
42     bool active;
43     bool persistent;
44     bool autostart;
45 };
46 
47 struct _virNodeDeviceObjList {
48     virObjectRWLockable parent;
49 
50     /* name string -> virNodeDeviceObj mapping
51      * for O(1), lockless lookup-by-name */
52     GHashTable *objs;
53 
54 };
55 
56 
57 static virClass *virNodeDeviceObjClass;
58 static virClass *virNodeDeviceObjListClass;
59 static void virNodeDeviceObjDispose(void *opaque);
60 static void virNodeDeviceObjListDispose(void *opaque);
61 static bool virNodeDeviceObjHasCap(const virNodeDeviceObj *obj, int type);
62 
63 static int
virNodeDeviceObjOnceInit(void)64 virNodeDeviceObjOnceInit(void)
65 {
66     if (!VIR_CLASS_NEW(virNodeDeviceObj, virClassForObjectLockable()))
67         return -1;
68 
69     if (!VIR_CLASS_NEW(virNodeDeviceObjList, virClassForObjectRWLockable()))
70         return -1;
71 
72     return 0;
73 }
74 
75 VIR_ONCE_GLOBAL_INIT(virNodeDeviceObj);
76 
77 
78 static void
virNodeDeviceObjDispose(void * opaque)79 virNodeDeviceObjDispose(void *opaque)
80 {
81     virNodeDeviceObj *obj = opaque;
82 
83     virNodeDeviceDefFree(obj->def);
84 }
85 
86 
87 static virNodeDeviceObj *
virNodeDeviceObjNew(void)88 virNodeDeviceObjNew(void)
89 {
90     virNodeDeviceObj *obj;
91 
92     if (virNodeDeviceObjInitialize() < 0)
93         return NULL;
94 
95     if (!(obj = virObjectLockableNew(virNodeDeviceObjClass)))
96         return NULL;
97 
98     virObjectLock(obj);
99 
100     return obj;
101 }
102 
103 
104 void
virNodeDeviceObjEndAPI(virNodeDeviceObj ** obj)105 virNodeDeviceObjEndAPI(virNodeDeviceObj **obj)
106 {
107     if (!*obj)
108         return;
109 
110     virObjectUnlock(*obj);
111     virObjectUnref(*obj);
112     *obj = NULL;
113 }
114 
115 
116 virNodeDeviceDef *
virNodeDeviceObjGetDef(virNodeDeviceObj * obj)117 virNodeDeviceObjGetDef(virNodeDeviceObj *obj)
118 {
119     return obj->def;
120 }
121 
122 
123 static bool
virNodeDeviceObjHasCapStr(const virNodeDeviceObj * obj,const char * cap)124 virNodeDeviceObjHasCapStr(const virNodeDeviceObj *obj,
125                           const char *cap)
126 {
127     int type;
128 
129     if ((type = virNodeDevCapTypeFromString(cap)) < 0)
130         return false;
131 
132     return virNodeDeviceObjHasCap(obj, type);
133 }
134 
135 
136 /* virNodeDeviceFindFCCapDef:
137  * @obj: Pointer to current device
138  *
139  * Search the device object 'caps' array for fc_host capability.
140  *
141  * Returns:
142  * Pointer to the caps or NULL if not found
143  */
144 static virNodeDevCapsDef *
virNodeDeviceFindFCCapDef(const virNodeDeviceObj * obj)145 virNodeDeviceFindFCCapDef(const virNodeDeviceObj *obj)
146 {
147     virNodeDevCapsDef *caps = obj->def->caps;
148 
149     while (caps) {
150         if (caps->data.type == VIR_NODE_DEV_CAP_SCSI_HOST &&
151             (caps->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST))
152             break;
153 
154         caps = caps->next;
155     }
156     return caps;
157 }
158 
159 
160 /* virNodeDeviceFindVPORTCapDef:
161  * @obj: Pointer to current device
162  *
163  * Search the device object 'caps' array for vport_ops capability.
164  *
165  * Returns:
166  * Pointer to the caps or NULL if not found
167  */
168 static virNodeDevCapsDef *
virNodeDeviceFindVPORTCapDef(const virNodeDeviceObj * obj)169 virNodeDeviceFindVPORTCapDef(const virNodeDeviceObj *obj)
170 {
171     virNodeDevCapsDef *caps = obj->def->caps;
172 
173     while (caps) {
174         if (caps->data.type == VIR_NODE_DEV_CAP_SCSI_HOST &&
175             (caps->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS))
176             break;
177 
178         caps = caps->next;
179     }
180     return caps;
181 }
182 
183 
184 static virNodeDeviceObj *
virNodeDeviceObjListSearch(virNodeDeviceObjList * devs,virHashSearcher callback,const void * data)185 virNodeDeviceObjListSearch(virNodeDeviceObjList *devs,
186                            virHashSearcher callback,
187                            const void *data)
188 {
189     virNodeDeviceObj *obj;
190 
191     virObjectRWLockRead(devs);
192     obj = virHashSearch(devs->objs, callback, data, NULL);
193     virObjectRef(obj);
194     virObjectRWUnlock(devs);
195 
196     if (obj)
197         virObjectLock(obj);
198 
199     return obj;
200 }
201 
202 
203 static int
virNodeDeviceObjListFindBySysfsPathCallback(const void * payload,const char * name G_GNUC_UNUSED,const void * opaque)204 virNodeDeviceObjListFindBySysfsPathCallback(const void *payload,
205                                             const char *name G_GNUC_UNUSED,
206                                             const void *opaque)
207 {
208     virNodeDeviceObj *obj = (virNodeDeviceObj *) payload;
209     const char *sysfs_path = opaque;
210     int want = 0;
211 
212     virObjectLock(obj);
213     if (obj->def->sysfs_path &&
214         STREQ_NULLABLE(obj->def->sysfs_path, sysfs_path))
215         want = 1;
216     virObjectUnlock(obj);
217     return want;
218 }
219 
220 
221 virNodeDeviceObj *
virNodeDeviceObjListFindBySysfsPath(virNodeDeviceObjList * devs,const char * sysfs_path)222 virNodeDeviceObjListFindBySysfsPath(virNodeDeviceObjList *devs,
223                                     const char *sysfs_path)
224 {
225     return virNodeDeviceObjListSearch(devs,
226                                       virNodeDeviceObjListFindBySysfsPathCallback,
227                                       sysfs_path);
228 }
229 
230 
231 static virNodeDeviceObj *
virNodeDeviceObjListFindByNameLocked(virNodeDeviceObjList * devs,const char * name)232 virNodeDeviceObjListFindByNameLocked(virNodeDeviceObjList *devs,
233                                      const char *name)
234 {
235     return virObjectRef(virHashLookup(devs->objs, name));
236 }
237 
238 
239 virNodeDeviceObj *
virNodeDeviceObjListFindByName(virNodeDeviceObjList * devs,const char * name)240 virNodeDeviceObjListFindByName(virNodeDeviceObjList *devs,
241                                const char *name)
242 {
243     virNodeDeviceObj *obj;
244 
245     virObjectRWLockRead(devs);
246     obj = virNodeDeviceObjListFindByNameLocked(devs, name);
247     virObjectRWUnlock(devs);
248     if (obj)
249         virObjectLock(obj);
250 
251     return obj;
252 }
253 
254 
255 struct virNodeDeviceObjListFindByWWNsData {
256     const char *parent_wwnn;
257     const char *parent_wwpn;
258 };
259 
260 static int
virNodeDeviceObjListFindByWWNsCallback(const void * payload,const char * name G_GNUC_UNUSED,const void * opaque)261 virNodeDeviceObjListFindByWWNsCallback(const void *payload,
262                                        const char *name G_GNUC_UNUSED,
263                                        const void *opaque)
264 {
265     virNodeDeviceObj *obj = (virNodeDeviceObj *) payload;
266     struct virNodeDeviceObjListFindByWWNsData *data =
267         (struct virNodeDeviceObjListFindByWWNsData *) opaque;
268     virNodeDevCapsDef *cap;
269     int want = 0;
270 
271     virObjectLock(obj);
272     if ((cap = virNodeDeviceFindFCCapDef(obj)) &&
273         STREQ_NULLABLE(cap->data.scsi_host.wwnn, data->parent_wwnn) &&
274         STREQ_NULLABLE(cap->data.scsi_host.wwpn, data->parent_wwpn) &&
275         virNodeDeviceFindVPORTCapDef(obj))
276         want = 1;
277     virObjectUnlock(obj);
278     return want;
279 }
280 
281 
282 static virNodeDeviceObj *
virNodeDeviceObjListFindByWWNs(virNodeDeviceObjList * devs,const char * parent_wwnn,const char * parent_wwpn)283 virNodeDeviceObjListFindByWWNs(virNodeDeviceObjList *devs,
284                                const char *parent_wwnn,
285                                const char *parent_wwpn)
286 {
287     struct virNodeDeviceObjListFindByWWNsData data = {
288         .parent_wwnn = parent_wwnn, .parent_wwpn = parent_wwpn };
289 
290     return virNodeDeviceObjListSearch(devs,
291                                       virNodeDeviceObjListFindByWWNsCallback,
292                                       &data);
293 }
294 
295 
296 static int
virNodeDeviceObjListFindByFabricWWNCallback(const void * payload,const char * name G_GNUC_UNUSED,const void * opaque)297 virNodeDeviceObjListFindByFabricWWNCallback(const void *payload,
298                                             const char *name G_GNUC_UNUSED,
299                                             const void *opaque)
300 {
301     virNodeDeviceObj *obj = (virNodeDeviceObj *) payload;
302     const char *matchstr = opaque;
303     virNodeDevCapsDef *cap;
304     int want = 0;
305 
306     virObjectLock(obj);
307     if ((cap = virNodeDeviceFindFCCapDef(obj)) &&
308         STREQ_NULLABLE(cap->data.scsi_host.fabric_wwn, matchstr) &&
309         virNodeDeviceFindVPORTCapDef(obj))
310         want = 1;
311     virObjectUnlock(obj);
312     return want;
313 }
314 
315 
316 static virNodeDeviceObj *
virNodeDeviceObjListFindByFabricWWN(virNodeDeviceObjList * devs,const char * parent_fabric_wwn)317 virNodeDeviceObjListFindByFabricWWN(virNodeDeviceObjList *devs,
318                                     const char *parent_fabric_wwn)
319 {
320     return virNodeDeviceObjListSearch(devs,
321                                       virNodeDeviceObjListFindByFabricWWNCallback,
322                                       parent_fabric_wwn);
323 }
324 
325 
326 static int
virNodeDeviceObjListFindByCapCallback(const void * payload,const char * name G_GNUC_UNUSED,const void * opaque)327 virNodeDeviceObjListFindByCapCallback(const void *payload,
328                                       const char *name G_GNUC_UNUSED,
329                                       const void *opaque)
330 {
331     virNodeDeviceObj *obj = (virNodeDeviceObj *) payload;
332     const char *matchstr = opaque;
333     int want = 0;
334 
335     virObjectLock(obj);
336     if (virNodeDeviceObjHasCapStr(obj, matchstr))
337         want = 1;
338     virObjectUnlock(obj);
339     return want;
340 }
341 
342 
343 static virNodeDeviceObj *
virNodeDeviceObjListFindByCap(virNodeDeviceObjList * devs,const char * cap)344 virNodeDeviceObjListFindByCap(virNodeDeviceObjList *devs,
345                               const char *cap)
346 {
347     return virNodeDeviceObjListSearch(devs,
348                                       virNodeDeviceObjListFindByCapCallback,
349                                       cap);
350 }
351 
352 
353 struct virNodeDeviceObjListFindSCSIHostByWWNsData {
354     const char *wwnn;
355     const char *wwpn;
356 };
357 
358 static int
virNodeDeviceObjListFindSCSIHostByWWNsCallback(const void * payload,const char * name G_GNUC_UNUSED,const void * opaque)359 virNodeDeviceObjListFindSCSIHostByWWNsCallback(const void *payload,
360                                                const char *name G_GNUC_UNUSED,
361                                                const void *opaque)
362 {
363     virNodeDeviceObj *obj = (virNodeDeviceObj *) payload;
364     struct virNodeDeviceObjListFindSCSIHostByWWNsData *data =
365         (struct virNodeDeviceObjListFindSCSIHostByWWNsData *) opaque;
366     virNodeDevCapsDef *cap;
367     int want = 0;
368 
369     virObjectLock(obj);
370     cap = obj->def->caps;
371 
372     while (cap) {
373         if (cap->data.type == VIR_NODE_DEV_CAP_SCSI_HOST) {
374             virNodeDeviceGetSCSIHostCaps(&cap->data.scsi_host);
375             if (cap->data.scsi_host.flags &
376                 VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) {
377                 if (STREQ(cap->data.scsi_host.wwnn, data->wwnn) &&
378                     STREQ(cap->data.scsi_host.wwpn, data->wwpn)) {
379                     want = 1;
380                     break;
381                 }
382             }
383         }
384         cap = cap->next;
385      }
386 
387     virObjectUnlock(obj);
388     return want;
389 }
390 
391 
392 virNodeDeviceObj *
virNodeDeviceObjListFindSCSIHostByWWNs(virNodeDeviceObjList * devs,const char * wwnn,const char * wwpn)393 virNodeDeviceObjListFindSCSIHostByWWNs(virNodeDeviceObjList *devs,
394                                        const char *wwnn,
395                                        const char *wwpn)
396 {
397     struct virNodeDeviceObjListFindSCSIHostByWWNsData data = {
398         .wwnn = wwnn, .wwpn = wwpn };
399 
400     return virNodeDeviceObjListSearch(devs,
401                                       virNodeDeviceObjListFindSCSIHostByWWNsCallback,
402                                       &data);
403 }
404 
405 
406 typedef struct _FindMediatedDeviceData FindMediatedDeviceData;
407 struct _FindMediatedDeviceData {
408     const char *uuid;
409     const char *parent_addr;
410 };
411 
412 
413 static int
virNodeDeviceObjListFindMediatedDeviceByUUIDCallback(const void * payload,const char * name G_GNUC_UNUSED,const void * opaque)414 virNodeDeviceObjListFindMediatedDeviceByUUIDCallback(const void *payload,
415                                                      const char *name G_GNUC_UNUSED,
416                                                      const void *opaque)
417 {
418     virNodeDeviceObj *obj = (virNodeDeviceObj *) payload;
419     const FindMediatedDeviceData* data = opaque;
420     virNodeDevCapsDef *cap;
421     int want = 0;
422 
423     virObjectLock(obj);
424 
425     for (cap = obj->def->caps; cap != NULL; cap = cap->next) {
426         if (cap->data.type == VIR_NODE_DEV_CAP_MDEV) {
427             if (STREQ(cap->data.mdev.uuid, data->uuid) &&
428                 STREQ(cap->data.mdev.parent_addr, data->parent_addr)) {
429                 want = 1;
430                 break;
431             }
432         }
433      }
434 
435     virObjectUnlock(obj);
436     return want;
437 }
438 
439 
440 virNodeDeviceObj *
virNodeDeviceObjListFindMediatedDeviceByUUID(virNodeDeviceObjList * devs,const char * uuid,const char * parent_addr)441 virNodeDeviceObjListFindMediatedDeviceByUUID(virNodeDeviceObjList *devs,
442                                              const char *uuid,
443                                              const char *parent_addr)
444 {
445     const FindMediatedDeviceData data = {uuid, parent_addr};
446     return virNodeDeviceObjListSearch(devs,
447                                       virNodeDeviceObjListFindMediatedDeviceByUUIDCallback,
448                                       &data);
449 }
450 
451 static void
virNodeDeviceObjListDispose(void * obj)452 virNodeDeviceObjListDispose(void *obj)
453 {
454     virNodeDeviceObjList *devs = obj;
455 
456     virHashFree(devs->objs);
457 }
458 
459 
460 virNodeDeviceObjList *
virNodeDeviceObjListNew(void)461 virNodeDeviceObjListNew(void)
462 {
463     virNodeDeviceObjList *devs;
464 
465     if (virNodeDeviceObjInitialize() < 0)
466         return NULL;
467 
468     if (!(devs = virObjectRWLockableNew(virNodeDeviceObjListClass)))
469         return NULL;
470 
471     devs->objs = virHashNew(virObjectFreeHashData);
472 
473     return devs;
474 }
475 
476 
477 void
virNodeDeviceObjListFree(virNodeDeviceObjList * devs)478 virNodeDeviceObjListFree(virNodeDeviceObjList *devs)
479 {
480     virObjectUnref(devs);
481 }
482 
483 
484 virNodeDeviceObj *
virNodeDeviceObjListAssignDef(virNodeDeviceObjList * devs,virNodeDeviceDef * def)485 virNodeDeviceObjListAssignDef(virNodeDeviceObjList *devs,
486                               virNodeDeviceDef *def)
487 {
488     virNodeDeviceObj *obj;
489 
490     virObjectRWLockWrite(devs);
491 
492     if ((obj = virNodeDeviceObjListFindByNameLocked(devs, def->name))) {
493         virObjectLock(obj);
494         virNodeDeviceDefFree(obj->def);
495         obj->def = def;
496     } else {
497         if (!(obj = virNodeDeviceObjNew()))
498             goto cleanup;
499 
500         if (virHashAddEntry(devs->objs, def->name, obj) < 0) {
501             virNodeDeviceObjEndAPI(&obj);
502             goto cleanup;
503         }
504 
505         obj->def = def;
506         virObjectRef(obj);
507     }
508 
509  cleanup:
510     virObjectRWUnlock(devs);
511     return obj;
512 }
513 
514 
515 void
virNodeDeviceObjListRemove(virNodeDeviceObjList * devs,virNodeDeviceObj * obj)516 virNodeDeviceObjListRemove(virNodeDeviceObjList *devs,
517                            virNodeDeviceObj *obj)
518 {
519     if (!obj)
520         return;
521 
522     virObjectRef(obj);
523     virObjectUnlock(obj);
524     virObjectRWLockWrite(devs);
525     virObjectLock(obj);
526     virNodeDeviceObjListRemoveLocked(devs, obj);
527     virNodeDeviceObjEndAPI(&obj);
528     virObjectRWUnlock(devs);
529 }
530 
531 
532 /* The caller must hold lock on 'devs' */
533 void
virNodeDeviceObjListRemoveLocked(virNodeDeviceObjList * devs,virNodeDeviceObj * dev)534 virNodeDeviceObjListRemoveLocked(virNodeDeviceObjList *devs,
535                                  virNodeDeviceObj *dev)
536 {
537     virHashRemoveEntry(devs->objs, dev->def->name);
538 }
539 
540 
541 /*
542  * Return the NPIV dev's parent device name
543  */
544 /* virNodeDeviceFindFCParentHost:
545  * @obj: Pointer to node device object
546  *
547  * Search the capabilities for the device to find the FC capabilities
548  * in order to set the parent_host value.
549  *
550  * Returns:
551  *   parent_host value on success (>= 0), -1 otherwise.
552  */
553 static int
virNodeDeviceFindFCParentHost(virNodeDeviceObj * obj)554 virNodeDeviceFindFCParentHost(virNodeDeviceObj *obj)
555 {
556     virNodeDevCapsDef *cap = virNodeDeviceFindVPORTCapDef(obj);
557 
558     if (!cap) {
559         virReportError(VIR_ERR_INTERNAL_ERROR,
560                        _("Parent device %s is not capable "
561                          "of vport operations"),
562                        obj->def->name);
563         return -1;
564     }
565 
566     return cap->data.scsi_host.host;
567 }
568 
569 
570 static int
virNodeDeviceObjListGetParentHostByParent(virNodeDeviceObjList * devs,const char * dev_name,const char * parent_name)571 virNodeDeviceObjListGetParentHostByParent(virNodeDeviceObjList *devs,
572                                           const char *dev_name,
573                                           const char *parent_name)
574 {
575     virNodeDeviceObj *obj = NULL;
576     int ret;
577 
578     if (!(obj = virNodeDeviceObjListFindByName(devs, parent_name))) {
579         virReportError(VIR_ERR_INTERNAL_ERROR,
580                        _("Could not find parent device for '%s'"),
581                        dev_name);
582         return -1;
583     }
584 
585     ret = virNodeDeviceFindFCParentHost(obj);
586 
587     virNodeDeviceObjEndAPI(&obj);
588 
589     return ret;
590 }
591 
592 
593 static int
virNodeDeviceObjListGetParentHostByWWNs(virNodeDeviceObjList * devs,const char * dev_name,const char * parent_wwnn,const char * parent_wwpn)594 virNodeDeviceObjListGetParentHostByWWNs(virNodeDeviceObjList *devs,
595                                         const char *dev_name,
596                                         const char *parent_wwnn,
597                                         const char *parent_wwpn)
598 {
599     virNodeDeviceObj *obj = NULL;
600     int ret;
601 
602     if (!(obj = virNodeDeviceObjListFindByWWNs(devs, parent_wwnn,
603                                                parent_wwpn))) {
604         virReportError(VIR_ERR_INTERNAL_ERROR,
605                        _("Could not find parent device for '%s'"),
606                        dev_name);
607         return -1;
608     }
609 
610     ret = virNodeDeviceFindFCParentHost(obj);
611 
612     virNodeDeviceObjEndAPI(&obj);
613 
614     return ret;
615 }
616 
617 
618 static int
virNodeDeviceObjListGetParentHostByFabricWWN(virNodeDeviceObjList * devs,const char * dev_name,const char * parent_fabric_wwn)619 virNodeDeviceObjListGetParentHostByFabricWWN(virNodeDeviceObjList *devs,
620                                              const char *dev_name,
621                                              const char *parent_fabric_wwn)
622 {
623     virNodeDeviceObj *obj = NULL;
624     int ret;
625 
626     if (!(obj = virNodeDeviceObjListFindByFabricWWN(devs, parent_fabric_wwn))) {
627         virReportError(VIR_ERR_INTERNAL_ERROR,
628                        _("Could not find parent device for '%s'"),
629                        dev_name);
630         return -1;
631     }
632 
633     ret = virNodeDeviceFindFCParentHost(obj);
634 
635     virNodeDeviceObjEndAPI(&obj);
636 
637     return ret;
638 }
639 
640 
641 static int
virNodeDeviceObjListFindVportParentHost(virNodeDeviceObjList * devs)642 virNodeDeviceObjListFindVportParentHost(virNodeDeviceObjList *devs)
643 {
644     virNodeDeviceObj *obj = NULL;
645     const char *cap = virNodeDevCapTypeToString(VIR_NODE_DEV_CAP_VPORTS);
646     int ret;
647 
648     if (!(obj = virNodeDeviceObjListFindByCap(devs, cap))) {
649         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
650                        _("Could not find any vport capable device"));
651         return -1;
652     }
653 
654     ret = virNodeDeviceFindFCParentHost(obj);
655 
656     virNodeDeviceObjEndAPI(&obj);
657 
658     return ret;
659 }
660 
661 
662 int
virNodeDeviceObjListGetParentHost(virNodeDeviceObjList * devs,virNodeDeviceDef * def)663 virNodeDeviceObjListGetParentHost(virNodeDeviceObjList *devs,
664                                   virNodeDeviceDef *def)
665 {
666     int parent_host = -1;
667 
668     if (def->parent) {
669         parent_host = virNodeDeviceObjListGetParentHostByParent(devs, def->name,
670                                                                 def->parent);
671     } else if (def->parent_wwnn && def->parent_wwpn) {
672         parent_host = virNodeDeviceObjListGetParentHostByWWNs(devs, def->name,
673                                                               def->parent_wwnn,
674                                                               def->parent_wwpn);
675     } else if (def->parent_fabric_wwn) {
676         parent_host =
677             virNodeDeviceObjListGetParentHostByFabricWWN(devs, def->name,
678                                                          def->parent_fabric_wwn);
679     } else {
680         /* Try to find a vport capable scsi_host when no parent supplied */
681         parent_host = virNodeDeviceObjListFindVportParentHost(devs);
682     }
683 
684     return parent_host;
685 }
686 
687 
688 static bool
virNodeDeviceObjHasCap(const virNodeDeviceObj * obj,int type)689 virNodeDeviceObjHasCap(const virNodeDeviceObj *obj,
690                        int type)
691 {
692     virNodeDevCapsDef *cap = NULL;
693 
694     for (cap = obj->def->caps; cap; cap = cap->next) {
695         if (type == cap->data.type)
696             return true;
697 
698         switch (cap->data.type) {
699         case VIR_NODE_DEV_CAP_PCI_DEV:
700             if (type == VIR_NODE_DEV_CAP_MDEV_TYPES &&
701                 (cap->data.pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_MDEV))
702                 return true;
703             if (type == VIR_NODE_DEV_CAP_VPD &&
704                 (cap->data.pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_VPD))
705                 return true;
706             break;
707 
708         case VIR_NODE_DEV_CAP_SCSI_HOST:
709             if (type == VIR_NODE_DEV_CAP_FC_HOST &&
710                 (cap->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST))
711                 return true;
712 
713             if (type == VIR_NODE_DEV_CAP_VPORTS &&
714                 (cap->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS))
715                 return true;
716             break;
717 
718         case VIR_NODE_DEV_CAP_CSS_DEV:
719             if (type == VIR_NODE_DEV_CAP_MDEV_TYPES &&
720                 (cap->data.ccw_dev.flags & VIR_NODE_DEV_CAP_FLAG_CSS_MDEV))
721                 return true;
722             break;
723 
724         case VIR_NODE_DEV_CAP_AP_MATRIX:
725             if (type == VIR_NODE_DEV_CAP_MDEV_TYPES &&
726                 (cap->data.ap_matrix.flags & VIR_NODE_DEV_CAP_FLAG_AP_MATRIX_MDEV))
727                 return true;
728             break;
729 
730         case VIR_NODE_DEV_CAP_SYSTEM:
731         case VIR_NODE_DEV_CAP_USB_DEV:
732         case VIR_NODE_DEV_CAP_USB_INTERFACE:
733         case VIR_NODE_DEV_CAP_NET:
734         case VIR_NODE_DEV_CAP_SCSI_TARGET:
735         case VIR_NODE_DEV_CAP_SCSI:
736         case VIR_NODE_DEV_CAP_STORAGE:
737         case VIR_NODE_DEV_CAP_FC_HOST:
738         case VIR_NODE_DEV_CAP_VPORTS:
739         case VIR_NODE_DEV_CAP_SCSI_GENERIC:
740         case VIR_NODE_DEV_CAP_DRM:
741         case VIR_NODE_DEV_CAP_MDEV_TYPES:
742         case VIR_NODE_DEV_CAP_MDEV:
743         case VIR_NODE_DEV_CAP_CCW_DEV:
744         case VIR_NODE_DEV_CAP_VDPA:
745         case VIR_NODE_DEV_CAP_AP_CARD:
746         case VIR_NODE_DEV_CAP_AP_QUEUE:
747         case VIR_NODE_DEV_CAP_VPD:
748         case VIR_NODE_DEV_CAP_LAST:
749             break;
750         }
751     }
752 
753     return false;
754 }
755 
756 
757 struct virNodeDeviceCountData {
758     virConnectPtr conn;
759     virNodeDeviceObjListFilter filter;
760     const char *matchstr;
761     int count;
762 };
763 
764 static int
virNodeDeviceObjListNumOfDevicesCallback(void * payload,const char * name G_GNUC_UNUSED,void * opaque)765 virNodeDeviceObjListNumOfDevicesCallback(void *payload,
766                                          const char *name G_GNUC_UNUSED,
767                                          void *opaque)
768 {
769     virNodeDeviceObj *obj = payload;
770     virNodeDeviceDef *def;
771     struct virNodeDeviceCountData *data = opaque;
772     virNodeDeviceObjListFilter filter = data->filter;
773 
774     virObjectLock(obj);
775     def = obj->def;
776     if ((!filter || filter(data->conn, def)) &&
777         (!data->matchstr || virNodeDeviceObjHasCapStr(obj, data->matchstr)))
778         data->count++;
779 
780     virObjectUnlock(obj);
781     return 0;
782 }
783 
784 
785 int
virNodeDeviceObjListNumOfDevices(virNodeDeviceObjList * devs,virConnectPtr conn,const char * cap,virNodeDeviceObjListFilter filter)786 virNodeDeviceObjListNumOfDevices(virNodeDeviceObjList *devs,
787                                  virConnectPtr conn,
788                                  const char *cap,
789                                  virNodeDeviceObjListFilter filter)
790 {
791     struct virNodeDeviceCountData data = {
792         .conn = conn, .filter = filter, .matchstr = cap, .count = 0 };
793 
794     virObjectRWLockRead(devs);
795     virHashForEach(devs->objs, virNodeDeviceObjListNumOfDevicesCallback, &data);
796     virObjectRWUnlock(devs);
797 
798     return data.count;
799 }
800 
801 
802 struct virNodeDeviceGetNamesData {
803     virConnectPtr conn;
804     virNodeDeviceObjListFilter filter;
805     const char *matchstr;
806     int nnames;
807     char **names;
808     int maxnames;
809     bool error;
810 };
811 
812 static int
virNodeDeviceObjListGetNamesCallback(void * payload,const char * name G_GNUC_UNUSED,void * opaque)813 virNodeDeviceObjListGetNamesCallback(void *payload,
814                                      const char *name G_GNUC_UNUSED,
815                                      void *opaque)
816 {
817     virNodeDeviceObj *obj = payload;
818     virNodeDeviceDef *def;
819     struct virNodeDeviceGetNamesData *data = opaque;
820     virNodeDeviceObjListFilter filter = data->filter;
821 
822     if (data->error)
823         return 0;
824 
825     if (data->nnames >= data->maxnames)
826         return 0;
827 
828     virObjectLock(obj);
829     def = obj->def;
830 
831     if ((!filter || filter(data->conn, def)) &&
832         (!data->matchstr || virNodeDeviceObjHasCapStr(obj, data->matchstr))) {
833         data->names[data->nnames] = g_strdup(def->name);
834         data->nnames++;
835      }
836 
837     virObjectUnlock(obj);
838     return 0;
839 }
840 
841 
842 int
virNodeDeviceObjListGetNames(virNodeDeviceObjList * devs,virConnectPtr conn,virNodeDeviceObjListFilter filter,const char * cap,char ** const names,int maxnames)843 virNodeDeviceObjListGetNames(virNodeDeviceObjList *devs,
844                              virConnectPtr conn,
845                              virNodeDeviceObjListFilter filter,
846                              const char *cap,
847                              char **const names,
848                              int maxnames)
849 {
850     struct virNodeDeviceGetNamesData data = {
851         .conn = conn, .filter = filter, .matchstr = cap, .names = names,
852         .nnames = 0, .maxnames = maxnames, .error = false };
853 
854     virObjectRWLockRead(devs);
855     virHashForEach(devs->objs, virNodeDeviceObjListGetNamesCallback, &data);
856     virObjectRWUnlock(devs);
857 
858     if (data.error)
859         goto error;
860 
861     return data.nnames;
862 
863  error:
864     while (--data.nnames)
865         VIR_FREE(data.names[data.nnames]);
866     return -1;
867 }
868 
869 
870 #define MATCH_CAP(FLAG) ((flags & (VIR_CONNECT_LIST_NODE_DEVICES_CAP_ ## FLAG)) && \
871                          virNodeDeviceObjHasCap(obj, VIR_NODE_DEV_CAP_ ## FLAG))
872 #define MATCH(FLAG) (flags & (FLAG))
873 
874 static bool
virNodeDeviceObjMatch(virNodeDeviceObj * obj,unsigned int flags)875 virNodeDeviceObjMatch(virNodeDeviceObj *obj,
876                       unsigned int flags)
877 {
878     /* Refresh the capabilities first, e.g. due to a driver change */
879     if (!obj->skipUpdateCaps &&
880         virNodeDeviceUpdateCaps(obj->def) < 0)
881         return false;
882 
883     /* filter by cap type */
884     if (flags & VIR_CONNECT_LIST_NODE_DEVICES_FILTERS_CAP) {
885         if (!(MATCH_CAP(SYSTEM)        ||
886               MATCH_CAP(PCI_DEV)       ||
887               MATCH_CAP(USB_DEV)       ||
888               MATCH_CAP(USB_INTERFACE) ||
889               MATCH_CAP(NET)           ||
890               MATCH_CAP(SCSI_HOST)     ||
891               MATCH_CAP(SCSI_TARGET)   ||
892               MATCH_CAP(SCSI)          ||
893               MATCH_CAP(STORAGE)       ||
894               MATCH_CAP(FC_HOST)       ||
895               MATCH_CAP(VPORTS)        ||
896               MATCH_CAP(SCSI_GENERIC)  ||
897               MATCH_CAP(DRM)           ||
898               MATCH_CAP(MDEV_TYPES)    ||
899               MATCH_CAP(MDEV)          ||
900               MATCH_CAP(CCW_DEV)       ||
901               MATCH_CAP(CSS_DEV)       ||
902               MATCH_CAP(VDPA)          ||
903               MATCH_CAP(AP_CARD)       ||
904               MATCH_CAP(AP_QUEUE)      ||
905               MATCH_CAP(AP_MATRIX)     ||
906               MATCH_CAP(VPD)))
907             return false;
908     }
909 
910     if (flags & (VIR_CONNECT_LIST_NODE_DEVICES_FILTERS_ACTIVE)) {
911         if (!((MATCH(VIR_CONNECT_LIST_NODE_DEVICES_ACTIVE) &&
912               virNodeDeviceObjIsActive(obj)) ||
913               (MATCH(VIR_CONNECT_LIST_NODE_DEVICES_INACTIVE) &&
914                !virNodeDeviceObjIsActive(obj))))
915             return false;
916     }
917 
918     return true;
919 }
920 #undef MATCH
921 #undef MATCH_CAP
922 
923 
924 typedef struct _virNodeDeviceObjListExportData virNodeDeviceObjListExportData;
925 struct _virNodeDeviceObjListExportData {
926     virConnectPtr conn;
927     virNodeDeviceObjListFilter filter;
928     unsigned int flags;
929     virNodeDevicePtr *devices;
930     int ndevices;
931     bool error;
932 };
933 
934 static int
virNodeDeviceObjListExportCallback(void * payload,const char * name G_GNUC_UNUSED,void * opaque)935 virNodeDeviceObjListExportCallback(void *payload,
936                                    const char *name G_GNUC_UNUSED,
937                                    void *opaque)
938 {
939     virNodeDeviceObj *obj = payload;
940     virNodeDeviceDef *def;
941     virNodeDeviceObjListExportData *data = opaque;
942     virNodeDevicePtr device = NULL;
943 
944     if (data->error)
945         return 0;
946 
947     virObjectLock(obj);
948     def = obj->def;
949 
950     if ((!data->filter || data->filter(data->conn, def)) &&
951         virNodeDeviceObjMatch(obj, data->flags)) {
952         if (data->devices) {
953             if (!(device = virGetNodeDevice(data->conn, def->name))) {
954                 virObjectUnref(device);
955                 data->error = true;
956                 goto cleanup;
957             }
958             device->parentName = g_strdup(def->parent);
959             data->devices[data->ndevices] = device;
960         }
961         data->ndevices++;
962     }
963 
964  cleanup:
965     virObjectUnlock(obj);
966     return 0;
967 }
968 
969 
970 int
virNodeDeviceObjListExport(virConnectPtr conn,virNodeDeviceObjList * devs,virNodeDevicePtr ** devices,virNodeDeviceObjListFilter filter,unsigned int flags)971 virNodeDeviceObjListExport(virConnectPtr conn,
972                            virNodeDeviceObjList *devs,
973                            virNodeDevicePtr **devices,
974                            virNodeDeviceObjListFilter filter,
975                            unsigned int flags)
976 {
977     virNodeDeviceObjListExportData data = {
978         .conn = conn, .filter = filter, .flags = flags,
979         .devices = NULL, .ndevices = 0, .error = false };
980 
981     virObjectRWLockRead(devs);
982     if (devices)
983         data.devices = g_new0(virNodeDevicePtr, virHashSize(devs->objs) + 1);
984 
985     virHashForEach(devs->objs, virNodeDeviceObjListExportCallback, &data);
986     virObjectRWUnlock(devs);
987 
988     if (data.error)
989         goto cleanup;
990 
991     if (data.devices) {
992         VIR_REALLOC_N(data.devices, data.ndevices + 1);
993         *devices = data.devices;
994      }
995 
996     return data.ndevices;
997 
998  cleanup:
999     virObjectListFree(data.devices);
1000     return -1;
1001 }
1002 
1003 
1004 void
virNodeDeviceObjSetSkipUpdateCaps(virNodeDeviceObj * obj,bool skipUpdateCaps)1005 virNodeDeviceObjSetSkipUpdateCaps(virNodeDeviceObj *obj,
1006                                   bool skipUpdateCaps)
1007 {
1008     obj->skipUpdateCaps = skipUpdateCaps;
1009 }
1010 
1011 
1012 bool
virNodeDeviceObjIsActive(virNodeDeviceObj * obj)1013 virNodeDeviceObjIsActive(virNodeDeviceObj *obj)
1014 {
1015     return obj->active;
1016 }
1017 
1018 
1019 void
virNodeDeviceObjSetActive(virNodeDeviceObj * obj,bool active)1020 virNodeDeviceObjSetActive(virNodeDeviceObj *obj,
1021                           bool active)
1022 {
1023     obj->active = active;
1024 }
1025 
1026 
1027 bool
virNodeDeviceObjIsPersistent(virNodeDeviceObj * obj)1028 virNodeDeviceObjIsPersistent(virNodeDeviceObj *obj)
1029 {
1030     return obj->persistent;
1031 }
1032 
1033 
1034 void
virNodeDeviceObjSetPersistent(virNodeDeviceObj * obj,bool persistent)1035 virNodeDeviceObjSetPersistent(virNodeDeviceObj *obj,
1036                               bool persistent)
1037 {
1038     obj->persistent = persistent;
1039 }
1040 
1041 
1042 bool
virNodeDeviceObjIsAutostart(virNodeDeviceObj * obj)1043 virNodeDeviceObjIsAutostart(virNodeDeviceObj *obj)
1044 {
1045     return obj->autostart;
1046 }
1047 
1048 
1049 void
virNodeDeviceObjSetAutostart(virNodeDeviceObj * obj,bool autostart)1050 virNodeDeviceObjSetAutostart(virNodeDeviceObj *obj,
1051                              bool autostart)
1052 {
1053     obj->autostart = autostart;
1054 }
1055 
1056 
1057 typedef struct _PredicateHelperData PredicateHelperData;
1058 struct _PredicateHelperData {
1059     virNodeDeviceObjListPredicate predicate;
1060     void *opaque;
1061 };
1062 
virNodeDeviceObjListRemoveHelper(void * key G_GNUC_UNUSED,void * value,void * opaque)1063 static int virNodeDeviceObjListRemoveHelper(void *key G_GNUC_UNUSED,
1064                                             void *value,
1065                                             void *opaque)
1066 {
1067     PredicateHelperData *data = opaque;
1068 
1069     return data->predicate(value, data->opaque);
1070 }
1071 
1072 
1073 /**
1074  * virNodeDeviceObjListForEachRemove
1075  * @devs: Pointer to object list
1076  * @callback: function to call for each device object
1077  * @opaque: Opaque data to use as argument to helper
1078  *
1079  * For each object in @devs, call the @callback helper using @opaque as
1080  * an argument. If @callback returns true, that item will be removed from the
1081  * object list.
1082  */
1083 void
virNodeDeviceObjListForEachRemove(virNodeDeviceObjList * devs,virNodeDeviceObjListPredicate callback,void * opaque)1084 virNodeDeviceObjListForEachRemove(virNodeDeviceObjList *devs,
1085                                   virNodeDeviceObjListPredicate callback,
1086                                   void *opaque)
1087 {
1088     PredicateHelperData data = {
1089         .predicate = callback,
1090         .opaque = opaque
1091     };
1092 
1093     virObjectRWLockWrite(devs);
1094     g_hash_table_foreach_remove(devs->objs,
1095                                 virNodeDeviceObjListRemoveHelper,
1096                                 &data);
1097     virObjectRWUnlock(devs);
1098 }
1099 
1100 
virNodeDeviceObjListFindHelper(const void * payload,const char * name G_GNUC_UNUSED,const void * opaque)1101 static int virNodeDeviceObjListFindHelper(const void *payload,
1102                                           const char *name G_GNUC_UNUSED,
1103                                           const void *opaque)
1104 {
1105     PredicateHelperData *data = (PredicateHelperData *) opaque;
1106     virNodeDeviceObj *obj = (virNodeDeviceObj *) payload;
1107 
1108     return data->predicate(obj, data->opaque);
1109 }
1110 
1111 
1112 /**
1113  * virNodeDeviceObjListFind
1114  * @devs: Pointer to object list
1115  * @predicate: function to test the device for a certain property
1116  * @opaque: Opaque data to use as argument to helper
1117  *
1118  * For each object in @devs, call the @predicate helper using @opaque as
1119  * an argument until it returns TRUE. The list may not be modified while
1120  * iterating.
1121  */
1122 virNodeDeviceObj *
virNodeDeviceObjListFind(virNodeDeviceObjList * devs,virNodeDeviceObjListPredicate predicate,void * opaque)1123 virNodeDeviceObjListFind(virNodeDeviceObjList *devs,
1124                          virNodeDeviceObjListPredicate predicate,
1125                          void *opaque)
1126 {
1127     PredicateHelperData data = {
1128         .predicate = predicate,
1129         .opaque = opaque
1130     };
1131 
1132     return virNodeDeviceObjListSearch(devs,
1133                                       virNodeDeviceObjListFindHelper,
1134                                       &data);
1135 }
1136