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