2009-11-12 22:48:24 +01:00
|
|
|
/*
|
|
|
|
|
* node_device_udev.c: node device enumeration - libudev implementation
|
|
|
|
|
*
|
2015-05-06 16:40:39 -04:00
|
|
|
* Copyright (C) 2009-2015 Red Hat, Inc.
|
2009-11-12 22:48:24 +01:00
|
|
|
*
|
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2012-09-20 16:30:55 -06:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 18:06:23 +08:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2009-11-12 22:48:24 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
2020-07-09 16:16:57 -05:00
|
|
|
#include <gio/gio.h>
|
2009-11-12 22:48:24 +01:00
|
|
|
#include <libudev.h>
|
2009-11-12 23:22:00 +01:00
|
|
|
#include <pciaccess.h>
|
2009-11-12 22:48:24 +01:00
|
|
|
#include <scsi/scsi.h>
|
|
|
|
|
|
|
|
|
|
#include "node_device_conf.h"
|
2016-07-28 14:02:55 +02:00
|
|
|
#include "node_device_event.h"
|
2009-11-12 22:48:24 +01:00
|
|
|
#include "node_device_driver.h"
|
2015-05-06 16:40:39 -04:00
|
|
|
#include "node_device_udev.h"
|
|
|
|
|
#include "virerror.h"
|
2009-11-12 22:48:24 +01:00
|
|
|
#include "driver.h"
|
|
|
|
|
#include "datatypes.h"
|
2012-12-12 17:59:27 +00:00
|
|
|
#include "virlog.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2012-12-13 18:01:25 +00:00
|
|
|
#include "viruuid.h"
|
2012-12-04 12:04:07 +00:00
|
|
|
#include "virbuffer.h"
|
2013-05-09 14:59:04 -04:00
|
|
|
#include "virfile.h"
|
2012-12-13 14:52:25 +00:00
|
|
|
#include "virpci.h"
|
2019-05-23 11:34:08 +01:00
|
|
|
#include "virpidfile.h"
|
2013-04-03 12:36:23 +02:00
|
|
|
#include "virstring.h"
|
2014-06-05 17:36:31 +02:00
|
|
|
#include "virnetdev.h"
|
2017-03-06 17:20:00 +01:00
|
|
|
#include "virmdev.h"
|
2020-02-16 22:59:28 +01:00
|
|
|
#include "virutil.h"
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2019-05-23 11:34:08 +01:00
|
|
|
#include "configmake.h"
|
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
#define VIR_FROM_THIS VIR_FROM_NODEDEV
|
|
|
|
|
|
2014-02-28 12:16:17 +00:00
|
|
|
VIR_LOG_INIT("node_device.node_device_udev");
|
|
|
|
|
|
2011-02-07 17:25:06 +00:00
|
|
|
#ifndef TYPE_RAID
|
|
|
|
|
# define TYPE_RAID 12
|
|
|
|
|
#endif
|
|
|
|
|
|
2017-10-06 15:21:55 +02:00
|
|
|
typedef struct _udevEventData udevEventData;
|
|
|
|
|
struct _udevEventData {
|
|
|
|
|
virObjectLockable parent;
|
|
|
|
|
|
2010-01-26 02:58:37 +01:00
|
|
|
struct udev_monitor *udev_monitor;
|
|
|
|
|
int watch;
|
2017-06-28 14:34:41 +02:00
|
|
|
|
|
|
|
|
/* Thread data */
|
2021-04-12 16:25:13 +02:00
|
|
|
virThread *th;
|
2017-06-28 14:34:41 +02:00
|
|
|
virCond threadCond;
|
|
|
|
|
bool threadQuit;
|
|
|
|
|
bool dataReady;
|
2021-03-16 17:27:25 -05:00
|
|
|
|
|
|
|
|
/* init thread */
|
2021-04-12 16:25:13 +02:00
|
|
|
virThread *initThread;
|
2020-07-09 16:16:57 -05:00
|
|
|
|
|
|
|
|
GList *mdevctlMonitors;
|
|
|
|
|
virMutex mdevctlLock;
|
|
|
|
|
int mdevctlTimeout;
|
2010-01-26 02:58:37 +01:00
|
|
|
};
|
|
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
static virClass *udevEventDataClass;
|
2017-10-06 15:21:55 +02:00
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
udevEventDataDispose(void *obj)
|
|
|
|
|
{
|
|
|
|
|
struct udev *udev = NULL;
|
2021-03-11 08:16:13 +01:00
|
|
|
udevEventData *priv = obj;
|
2017-10-06 15:21:55 +02:00
|
|
|
|
|
|
|
|
if (priv->watch != -1)
|
|
|
|
|
virEventRemoveHandle(priv->watch);
|
|
|
|
|
|
|
|
|
|
if (!priv->udev_monitor)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
udev = udev_monitor_get_udev(priv->udev_monitor);
|
|
|
|
|
udev_monitor_unref(priv->udev_monitor);
|
|
|
|
|
udev_unref(udev);
|
2017-06-28 14:34:41 +02:00
|
|
|
|
2020-07-09 16:16:57 -05:00
|
|
|
virMutexLock(&priv->mdevctlLock);
|
|
|
|
|
g_list_free_full(priv->mdevctlMonitors, g_object_unref);
|
|
|
|
|
virMutexUnlock(&priv->mdevctlLock);
|
|
|
|
|
virMutexDestroy(&priv->mdevctlLock);
|
|
|
|
|
|
2017-06-28 14:34:41 +02:00
|
|
|
virCondDestroy(&priv->threadCond);
|
2017-10-06 15:21:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
udevEventDataOnceInit(void)
|
|
|
|
|
{
|
2018-04-17 17:42:33 +02:00
|
|
|
if (!VIR_CLASS_NEW(udevEventData, virClassForObjectLockable()))
|
2017-10-06 15:21:55 +02:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-20 12:23:29 -05:00
|
|
|
VIR_ONCE_GLOBAL_INIT(udevEventData);
|
2017-10-06 15:21:55 +02:00
|
|
|
|
2021-03-11 08:16:13 +01:00
|
|
|
static udevEventData *
|
2017-10-06 15:21:55 +02:00
|
|
|
udevEventDataNew(void)
|
|
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
udevEventData *ret = NULL;
|
2017-10-06 15:21:55 +02:00
|
|
|
|
|
|
|
|
if (udevEventDataInitialize() < 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
if (!(ret = virObjectLockableNew(udevEventDataClass)))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2017-06-28 14:34:41 +02:00
|
|
|
if (virCondInit(&ret->threadCond) < 0) {
|
|
|
|
|
virObjectUnref(ret);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-09 16:16:57 -05:00
|
|
|
if (virMutexInit(&ret->mdevctlLock) < 0) {
|
|
|
|
|
virObjectUnref(ret);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-06 15:21:55 +02:00
|
|
|
ret->watch = -1;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 16:36:35 +02:00
|
|
|
static bool
|
|
|
|
|
udevHasDeviceProperty(struct udev_device *dev,
|
|
|
|
|
const char *key)
|
|
|
|
|
{
|
|
|
|
|
if (udev_device_get_property_value(dev, key))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static const char *
|
|
|
|
|
udevGetDeviceProperty(struct udev_device *udev_device,
|
|
|
|
|
const char *property_key)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2016-06-03 15:25:22 +02:00
|
|
|
const char *ret = NULL;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 15:25:22 +02:00
|
|
|
ret = udev_device_get_property_value(udev_device, property_key);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2020-11-17 11:54:54 +00:00
|
|
|
VIR_DEBUG("Found property key '%s' value '%s' for device with sysname '%s' errno='%s'",
|
|
|
|
|
property_key, NULLSTR(ret), udev_device_get_sysname(udev_device),
|
|
|
|
|
ret ? "" : g_strerror(errno));
|
2009-11-12 22:48:24 +01:00
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-05-31 16:24:18 +02:00
|
|
|
static void
|
2017-05-12 11:49:18 -04:00
|
|
|
udevGetStringProperty(struct udev_device *udev_device,
|
|
|
|
|
const char *property_key,
|
|
|
|
|
char **value)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2019-10-20 13:49:46 +02:00
|
|
|
*value = g_strdup(udevGetDeviceProperty(udev_device, property_key));
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevGetIntProperty(struct udev_device *udev_device,
|
|
|
|
|
const char *property_key,
|
|
|
|
|
int *value,
|
|
|
|
|
int base)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2016-06-03 15:25:22 +02:00
|
|
|
const char *str = NULL;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 15:25:22 +02:00
|
|
|
str = udevGetDeviceProperty(udev_device, property_key);
|
2020-11-17 10:17:48 +00:00
|
|
|
if (!str) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
|
_("Missing udev property '%s' on '%s'"),
|
|
|
|
|
property_key, udev_device_get_sysname(udev_device));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2020-11-17 10:17:48 +00:00
|
|
|
if (virStrToLong_i(str, NULL, base, value) < 0) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
2020-11-17 10:17:48 +00:00
|
|
|
_("Failed to parse int '%s' from udev property '%s' on '%s'"),
|
|
|
|
|
str, property_key, udev_device_get_sysname(udev_device));
|
2016-06-03 16:10:21 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
2016-06-03 16:10:21 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevGetUintProperty(struct udev_device *udev_device,
|
|
|
|
|
const char *property_key,
|
|
|
|
|
unsigned int *value,
|
|
|
|
|
int base)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2016-06-03 15:25:22 +02:00
|
|
|
const char *str = NULL;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 15:25:22 +02:00
|
|
|
str = udevGetDeviceProperty(udev_device, property_key);
|
2020-11-17 10:17:48 +00:00
|
|
|
if (!str) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
|
_("Missing udev property '%s' on '%s'"),
|
|
|
|
|
property_key, udev_device_get_sysname(udev_device));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2020-11-17 10:17:48 +00:00
|
|
|
if (virStrToLong_ui(str, NULL, base, value) < 0) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
2020-11-17 10:17:48 +00:00
|
|
|
_("Failed to parse uint '%s' from udev property '%s' on '%s'"),
|
|
|
|
|
str, property_key, udev_device_get_sysname(udev_device));
|
2016-06-03 16:10:21 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
2016-06-03 16:10:21 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static const char *
|
|
|
|
|
udevGetDeviceSysfsAttr(struct udev_device *udev_device,
|
|
|
|
|
const char *attr_name)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2016-06-03 17:13:39 +02:00
|
|
|
const char *ret = NULL;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:13:39 +02:00
|
|
|
ret = udev_device_get_sysattr_value(udev_device, attr_name);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
|
|
|
|
VIR_DEBUG("Found sysfs attribute '%s' value '%s' "
|
2010-04-02 21:44:04 +02:00
|
|
|
"for device with sysname '%s'",
|
2016-06-03 17:13:39 +02:00
|
|
|
attr_name, NULLSTR(ret),
|
2009-11-12 22:48:24 +01:00
|
|
|
udev_device_get_sysname(udev_device));
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevGetStringSysfsAttr(struct udev_device *udev_device,
|
|
|
|
|
const char *attr_name,
|
|
|
|
|
char **value)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2019-10-20 13:49:46 +02:00
|
|
|
*value = g_strdup(udevGetDeviceSysfsAttr(udev_device, attr_name));
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:13:39 +02:00
|
|
|
virStringStripControlChars(*value);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:13:39 +02:00
|
|
|
if (*value != NULL && (STREQ(*value, "")))
|
|
|
|
|
VIR_FREE(*value);
|
2015-04-14 12:30:34 +02:00
|
|
|
|
2016-06-03 17:27:48 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevGetIntSysfsAttr(struct udev_device *udev_device,
|
|
|
|
|
const char *attr_name,
|
|
|
|
|
int *value,
|
|
|
|
|
int base)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2016-06-03 17:13:39 +02:00
|
|
|
const char *str = NULL;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:13:39 +02:00
|
|
|
str = udevGetDeviceSysfsAttr(udev_device, attr_name);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:13:39 +02:00
|
|
|
if (str && virStrToLong_i(str, NULL, base, value) < 0) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
|
_("Failed to convert '%s' to int"), str);
|
2016-06-03 17:39:39 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
2016-06-03 17:39:39 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevGetUintSysfsAttr(struct udev_device *udev_device,
|
|
|
|
|
const char *attr_name,
|
|
|
|
|
unsigned int *value,
|
|
|
|
|
int base)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2016-06-03 17:13:39 +02:00
|
|
|
const char *str = NULL;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:13:39 +02:00
|
|
|
str = udevGetDeviceSysfsAttr(udev_device, attr_name);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:13:39 +02:00
|
|
|
if (str && virStrToLong_ui(str, NULL, base, value) < 0) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
|
_("Failed to convert '%s' to unsigned int"), str);
|
2016-06-03 18:49:40 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
2016-06-03 18:49:40 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevGetUint64SysfsAttr(struct udev_device *udev_device,
|
|
|
|
|
const char *attr_name,
|
|
|
|
|
unsigned long long *value)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2016-06-03 17:13:39 +02:00
|
|
|
const char *str = NULL;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:13:39 +02:00
|
|
|
str = udevGetDeviceSysfsAttr(udev_device, attr_name);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:13:39 +02:00
|
|
|
if (str && virStrToLong_ull(str, NULL, 0, value) < 0) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
|
_("Failed to convert '%s' to unsigned long long"), str);
|
2016-06-03 18:54:43 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
2016-06-03 18:54:43 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-05-31 16:19:12 +02:00
|
|
|
static void
|
2017-05-12 11:49:18 -04:00
|
|
|
udevGenerateDeviceName(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def,
|
2017-05-12 11:49:18 -04:00
|
|
|
const char *s)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2020-11-06 12:02:50 -06:00
|
|
|
nodeDeviceGenerateName(def,
|
|
|
|
|
udev_device_get_subsystem(device),
|
|
|
|
|
udev_device_get_sysname(device), s);
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-26 11:21:16 +08:00
|
|
|
static virMutex pciaccessMutex = VIR_MUTEX_INITIALIZER;
|
2017-05-12 11:49:18 -04:00
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
udevTranslatePCIIds(unsigned int vendor,
|
|
|
|
|
unsigned int product,
|
|
|
|
|
char **vendor_string,
|
|
|
|
|
char **product_string)
|
2009-11-12 23:22:00 +01:00
|
|
|
{
|
|
|
|
|
struct pci_id_match m;
|
|
|
|
|
const char *vendor_name = NULL, *device_name = NULL;
|
|
|
|
|
|
|
|
|
|
m.vendor_id = vendor;
|
|
|
|
|
m.device_id = product;
|
|
|
|
|
m.subvendor_id = PCI_MATCH_ANY;
|
|
|
|
|
m.subdevice_id = PCI_MATCH_ANY;
|
|
|
|
|
m.device_class = 0;
|
|
|
|
|
m.device_class_mask = 0;
|
|
|
|
|
m.match_data = 0;
|
|
|
|
|
|
2021-03-26 11:21:16 +08:00
|
|
|
/* pci_get_strings returns void and unfortunately is not thread safe. */
|
|
|
|
|
virMutexLock(&pciaccessMutex);
|
2009-11-12 23:22:00 +01:00
|
|
|
pci_get_strings(&m,
|
|
|
|
|
&device_name,
|
2010-05-11 14:44:34 -04:00
|
|
|
&vendor_name,
|
2009-11-12 23:22:00 +01:00
|
|
|
NULL,
|
|
|
|
|
NULL);
|
2021-03-26 11:21:16 +08:00
|
|
|
virMutexUnlock(&pciaccessMutex);
|
2009-11-12 23:22:00 +01:00
|
|
|
|
2019-10-20 13:49:46 +02:00
|
|
|
*vendor_string = g_strdup(vendor_name);
|
|
|
|
|
*product_string = g_strdup(device_name);
|
2009-11-12 23:22:00 +01:00
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
return 0;
|
2009-11-12 23:22:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevProcessPCI(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapPCIDev *pci_dev = &def->caps->data.pci_dev;
|
|
|
|
|
virPCIEDeviceInfo *pci_express = NULL;
|
|
|
|
|
virPCIDevice *pciDev = NULL;
|
virpci.c: simplify virPCIDeviceNew() signature
The current virPCIDeviceNew() signature, receiving 4 uints in sequence
(domain, bus, slot, function), is not neat.
We already have a way to represent a PCI address in virPCIDeviceAddress
that is used in the code. Aside from the test files, most of
virPCIDeviceNew() callers have access to a virPCIDeviceAddress reference,
but then we need to retrieve the 4 required uints (addr.domain, addr.bus,
addr.slot, addr.function) to satisfy virPCIDeviceNew(). The result is
that we have extra verbosity/boilerplate to retrieve an information that
is already available in virPCIDeviceAddress.
A better way is presented by virNVMEDeviceNew(), where the caller just
supplies a virPCIDeviceAddress pointer and the function handles the
details internally.
This patch changes virPCIDeviceNew() to receive a virPCIDeviceAddress
pointer instead of 4 uints.
Reviewed-by: Laine Stump <laine@redhat.com>
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
2021-01-04 09:54:28 -03:00
|
|
|
virPCIDeviceAddress devAddr;
|
2015-05-11 13:50:37 -04:00
|
|
|
int ret = -1;
|
build: detect potentential uninitialized variables
Even with -Wuninitialized (which is part of autobuild.sh
--enable-compile-warnings=error), gcc does NOT catch this
use of an uninitialized variable:
{
if (cond)
goto error;
int a = 1;
error:
printf("%d", a);
}
which prints 0 (supposing the stack started life wiped) if
cond was true. Clang will catch it, but we don't use clang
as often. Using gcc -Wjump-misses-init catches it, but also
gives false positives:
{
if (cond)
goto error;
int a = 1;
return a;
error:
return 0;
}
Here, a was never used in the scope of the error block, so
declaring it after goto is technically fine (and clang agrees).
However, given that our HACKING already documents a preference
to C89 decl-before-statement, the false positive warning is
enough of a prod to comply with HACKING.
[Personally, I'd _really_ rather use C99 decl-after-statement
to minimize scope, but until gcc can efficiently and reliably
catch scoping and uninitialized usage bugs, I'll settle with
the compromise of enforcing a coding standard that happens to
reject false positives if it can also detect real bugs.]
* acinclude.m4 (LIBVIRT_COMPILE_WARNINGS): Add -Wjump-misses-init.
* src/util/util.c (__virExec): Adjust offenders.
* src/conf/domain_conf.c (virDomainTimerDefParseXML): Likewise.
* src/remote/remote_driver.c (doRemoteOpen): Likewise.
* src/phyp/phyp_driver.c (phypGetLparNAME, phypGetLparProfile)
(phypGetVIOSFreeSCSIAdapter, phypVolumeGetKey)
(phypGetStoragePoolDevice)
(phypVolumeGetPhysicalVolumeByStoragePool)
(phypVolumeGetPath): Likewise.
* src/vbox/vbox_tmpl.c (vboxNetworkUndefineDestroy)
(vboxNetworkCreate, vboxNetworkDumpXML)
(vboxNetworkDefineCreateXML): Likewise.
* src/xenapi/xenapi_driver.c (getCapsObject)
(xenapiDomainDumpXML): Likewise.
* src/xenapi/xenapi_utils.c (createVMRecordFromXml): Likewise.
* src/security/security_selinux.c (SELinuxGenNewContext):
Likewise.
* src/qemu/qemu_command.c (qemuBuildCommandLine): Likewise.
* src/qemu/qemu_hotplug.c (qemuDomainChangeEjectableMedia):
Likewise.
* src/qemu/qemu_process.c (qemuProcessWaitForMonitor): Likewise.
* src/qemu/qemu_monitor_text.c (qemuMonitorTextGetPtyPaths):
Likewise.
* src/qemu/qemu_driver.c (qemudDomainShutdown)
(qemudDomainBlockStats, qemudDomainMemoryPeek): Likewise.
* src/storage/storage_backend_iscsi.c
(virStorageBackendCreateIfaceIQN): Likewise.
* src/node_device/node_device_udev.c (udevProcessPCI): Likewise.
2011-04-01 09:41:45 -06:00
|
|
|
char *p;
|
2017-10-16 11:53:33 +02:00
|
|
|
bool privileged;
|
|
|
|
|
|
|
|
|
|
nodeDeviceLock();
|
|
|
|
|
privileged = driver->privileged;
|
|
|
|
|
nodeDeviceUnlock();
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2019-03-21 10:12:40 +03:00
|
|
|
pci_dev->klass = -1;
|
|
|
|
|
if (udevGetIntProperty(device, "PCI_CLASS", &pci_dev->klass, 16) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-01 17:46:53 +01:00
|
|
|
if ((p = strrchr(def->sysfs_path, '/')) == NULL ||
|
2017-03-02 10:59:25 -05:00
|
|
|
virStrToLong_ui(p + 1, &p, 16, &pci_dev->domain) < 0 || p == NULL ||
|
|
|
|
|
virStrToLong_ui(p + 1, &p, 16, &pci_dev->bus) < 0 || p == NULL ||
|
|
|
|
|
virStrToLong_ui(p + 1, &p, 16, &pci_dev->slot) < 0 || p == NULL ||
|
|
|
|
|
virStrToLong_ui(p + 1, &p, 16, &pci_dev->function) < 0) {
|
2016-06-03 14:54:03 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
|
_("failed to parse the PCI address from sysfs path: '%s'"),
|
2017-03-01 17:46:53 +01:00
|
|
|
def->sysfs_path);
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetUintSysfsAttr(device, "vendor", &pci_dev->vendor, 16) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetUintSysfsAttr(device, "device", &pci_dev->product, 16) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevTranslatePCIIds(pci_dev->vendor,
|
|
|
|
|
pci_dev->product,
|
|
|
|
|
&pci_dev->vendor_name,
|
|
|
|
|
&pci_dev->product_name) != 0) {
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 23:22:00 +01:00
|
|
|
}
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2021-05-31 16:19:12 +02:00
|
|
|
udevGenerateDeviceName(device, def, NULL);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:39:39 +02:00
|
|
|
/* The default value is -1, because it can't be 0
|
|
|
|
|
* as zero is valid node number. */
|
2017-03-02 10:59:25 -05:00
|
|
|
pci_dev->numa_node = -1;
|
2016-06-03 17:39:39 +02:00
|
|
|
if (udevGetIntSysfsAttr(device, "numa_node",
|
2017-03-02 10:59:25 -05:00
|
|
|
&pci_dev->numa_node, 10) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2014-05-07 18:07:12 +02:00
|
|
|
|
2018-01-12 13:14:26 +01:00
|
|
|
if (virNodeDeviceGetPCIDynamicCaps(def->sysfs_path, pci_dev) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2013-06-23 14:01:00 -04:00
|
|
|
|
virpci.c: simplify virPCIDeviceNew() signature
The current virPCIDeviceNew() signature, receiving 4 uints in sequence
(domain, bus, slot, function), is not neat.
We already have a way to represent a PCI address in virPCIDeviceAddress
that is used in the code. Aside from the test files, most of
virPCIDeviceNew() callers have access to a virPCIDeviceAddress reference,
but then we need to retrieve the 4 required uints (addr.domain, addr.bus,
addr.slot, addr.function) to satisfy virPCIDeviceNew(). The result is
that we have extra verbosity/boilerplate to retrieve an information that
is already available in virPCIDeviceAddress.
A better way is presented by virNVMEDeviceNew(), where the caller just
supplies a virPCIDeviceAddress pointer and the function handles the
details internally.
This patch changes virPCIDeviceNew() to receive a virPCIDeviceAddress
pointer instead of 4 uints.
Reviewed-by: Laine Stump <laine@redhat.com>
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
2021-01-04 09:54:28 -03:00
|
|
|
devAddr.domain = pci_dev->domain;
|
|
|
|
|
devAddr.bus = pci_dev->bus;
|
|
|
|
|
devAddr.slot = pci_dev->slot;
|
|
|
|
|
devAddr.function = pci_dev->function;
|
|
|
|
|
|
|
|
|
|
if (!(pciDev = virPCIDeviceNew(&devAddr)))
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2014-05-15 10:13:45 +02:00
|
|
|
|
2014-06-30 15:18:23 +02:00
|
|
|
/* We need to be root to read PCI device configs */
|
2017-10-16 11:53:33 +02:00
|
|
|
if (privileged) {
|
2017-03-02 10:59:25 -05:00
|
|
|
if (virPCIGetHeaderType(pciDev, &pci_dev->hdrType) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-03-15 12:22:03 +01:00
|
|
|
|
2016-03-15 13:13:48 +01:00
|
|
|
if (virPCIDeviceIsPCIExpress(pciDev) > 0) {
|
2020-09-23 22:04:41 +02:00
|
|
|
pci_express = g_new0(virPCIEDeviceInfo, 1);
|
2014-05-15 10:13:45 +02:00
|
|
|
|
2016-03-15 13:13:48 +01:00
|
|
|
if (virPCIDeviceHasPCIExpressLink(pciDev) > 0) {
|
2020-09-23 22:04:41 +02:00
|
|
|
pci_express->link_cap = g_new0(virPCIELink, 1);
|
|
|
|
|
pci_express->link_sta = g_new0(virPCIELink, 1);
|
2016-03-15 13:13:48 +01:00
|
|
|
|
|
|
|
|
if (virPCIDeviceGetLinkCapSta(pciDev,
|
|
|
|
|
&pci_express->link_cap->port,
|
|
|
|
|
&pci_express->link_cap->speed,
|
|
|
|
|
&pci_express->link_cap->width,
|
|
|
|
|
&pci_express->link_sta->speed,
|
|
|
|
|
&pci_express->link_sta->width) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-03-15 13:13:48 +01:00
|
|
|
|
|
|
|
|
pci_express->link_sta->port = -1; /* PCIe can't negotiate port. Yet :) */
|
|
|
|
|
}
|
2017-03-02 10:59:25 -05:00
|
|
|
pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCIE;
|
2021-03-24 10:32:58 +01:00
|
|
|
pci_dev->pci_express = g_steal_pointer(&pci_express);
|
2014-05-15 10:13:45 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
ret = 0;
|
|
|
|
|
|
2016-06-03 19:40:45 +02:00
|
|
|
cleanup:
|
2014-05-15 10:13:45 +02:00
|
|
|
virPCIDeviceFree(pciDev);
|
2014-07-22 22:38:30 -06:00
|
|
|
virPCIEDeviceInfoFree(pci_express);
|
2009-11-12 22:48:24 +01:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
drmGetMinorType(int minor)
|
2017-02-15 01:04:12 +04:00
|
|
|
{
|
|
|
|
|
int type = minor >> 6;
|
|
|
|
|
|
|
|
|
|
if (minor < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case VIR_NODE_DEV_DRM_PRIMARY:
|
|
|
|
|
case VIR_NODE_DEV_DRM_CONTROL:
|
|
|
|
|
case VIR_NODE_DEV_DRM_RENDER:
|
|
|
|
|
return type;
|
|
|
|
|
default:
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
udevProcessDRMDevice(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2017-02-15 01:04:12 +04:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapDRM *drm = &def->caps->data.drm;
|
2017-02-15 01:04:12 +04:00
|
|
|
int minor;
|
|
|
|
|
|
2021-05-31 16:19:12 +02:00
|
|
|
udevGenerateDeviceName(device, def, NULL);
|
2017-02-15 01:04:12 +04:00
|
|
|
|
|
|
|
|
if (udevGetIntProperty(device, "MINOR", &minor, 10) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if ((minor = drmGetMinorType(minor)) == -1)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
drm->type = minor;
|
2017-02-15 01:04:12 +04:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
udevProcessUSBDevice(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapUSBDev *usb_dev = &def->caps->data.usb_dev;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetUintProperty(device, "BUSNUM", &usb_dev->bus, 10) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetUintProperty(device, "DEVNUM", &usb_dev->device, 10) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetUintProperty(device, "ID_VENDOR_ID", &usb_dev->vendor, 16) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2021-05-31 16:24:18 +02:00
|
|
|
udevGetStringProperty(device,
|
|
|
|
|
"ID_VENDOR_FROM_DATABASE",
|
|
|
|
|
&usb_dev->vendor_name);
|
2016-06-03 15:54:19 +02:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (!usb_dev->vendor_name &&
|
2016-06-03 17:27:48 +02:00
|
|
|
udevGetStringSysfsAttr(device, "manufacturer",
|
2017-03-02 10:59:25 -05:00
|
|
|
&usb_dev->vendor_name) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetUintProperty(device, "ID_MODEL_ID", &usb_dev->product, 16) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2021-05-31 16:24:18 +02:00
|
|
|
udevGetStringProperty(device,
|
|
|
|
|
"ID_MODEL_FROM_DATABASE",
|
|
|
|
|
&usb_dev->product_name);
|
2016-06-03 15:54:19 +02:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (!usb_dev->product_name &&
|
2016-06-03 17:27:48 +02:00
|
|
|
udevGetStringSysfsAttr(device, "product",
|
2017-03-02 10:59:25 -05:00
|
|
|
&usb_dev->product_name) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2021-05-31 16:19:12 +02:00
|
|
|
udevGenerateDeviceName(device, def, NULL);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevProcessUSBInterface(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapUSBIf *usb_if = &def->caps->data.usb_if;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 18:49:40 +02:00
|
|
|
if (udevGetUintSysfsAttr(device, "bInterfaceNumber",
|
2017-03-02 10:59:25 -05:00
|
|
|
&usb_if->number, 16) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 18:49:40 +02:00
|
|
|
if (udevGetUintSysfsAttr(device, "bInterfaceClass",
|
2019-03-12 11:08:00 +03:00
|
|
|
&usb_if->klass, 16) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 18:49:40 +02:00
|
|
|
if (udevGetUintSysfsAttr(device, "bInterfaceSubClass",
|
2017-03-02 10:59:25 -05:00
|
|
|
&usb_if->subclass, 16) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 18:49:40 +02:00
|
|
|
if (udevGetUintSysfsAttr(device, "bInterfaceProtocol",
|
2017-03-02 10:59:25 -05:00
|
|
|
&usb_if->protocol, 16) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2021-05-31 16:19:12 +02:00
|
|
|
udevGenerateDeviceName(device, def, NULL);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevProcessNetworkInterface(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2010-05-27 10:44:02 -04:00
|
|
|
const char *devtype = udev_device_get_devtype(device);
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapNet *net = &def->caps->data.net;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2010-05-27 10:44:02 -04:00
|
|
|
if (devtype && STREQ(devtype, "wlan")) {
|
2017-03-02 10:59:25 -05:00
|
|
|
net->subtype = VIR_NODE_DEV_CAP_NET_80211;
|
2010-05-27 10:44:02 -04:00
|
|
|
} else {
|
2017-03-02 10:59:25 -05:00
|
|
|
net->subtype = VIR_NODE_DEV_CAP_NET_80203;
|
2010-05-27 10:44:02 -04:00
|
|
|
}
|
|
|
|
|
|
2021-05-31 16:24:18 +02:00
|
|
|
udevGetStringProperty(device, "INTERFACE", &net->ifname);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:27:48 +02:00
|
|
|
if (udevGetStringSysfsAttr(device, "address",
|
2017-03-02 10:59:25 -05:00
|
|
|
&net->address) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetUintSysfsAttr(device, "addr_len", &net->address_len, 0) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2021-05-31 16:19:12 +02:00
|
|
|
udevGenerateDeviceName(device, def, net->address);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (virNetDevGetLinkInfo(net->ifname, &net->lnk) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2014-06-05 17:36:31 +02:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (virNetDevGetFeatures(net->ifname, &net->features) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
2019-10-14 14:45:33 +02:00
|
|
|
udevProcessSCSIHost(struct udev_device *device G_GNUC_UNUSED,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapSCSIHost *scsi_host = &def->caps->data.scsi_host;
|
2019-12-23 15:47:21 +00:00
|
|
|
g_autofree char *filename = NULL;
|
2016-06-03 14:41:28 +02:00
|
|
|
char *str;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2019-12-23 15:47:21 +00:00
|
|
|
filename = g_path_get_basename(def->sysfs_path);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 14:54:03 +02:00
|
|
|
if (!(str = STRSKIP(filename, "host")) ||
|
2017-03-02 10:59:25 -05:00
|
|
|
virStrToLong_ui(str, NULL, 0, &scsi_host->host) < 0) {
|
2016-06-03 14:54:03 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
|
_("failed to parse SCSI host '%s'"),
|
|
|
|
|
filename);
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
2018-01-12 16:23:30 +01:00
|
|
|
virNodeDeviceGetSCSIHostCaps(&def->caps->data.scsi_host);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2021-05-31 16:19:12 +02:00
|
|
|
udevGenerateDeviceName(device, def, NULL);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevProcessSCSITarget(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2009-11-12 23:03:47 +01:00
|
|
|
{
|
|
|
|
|
const char *sysname = NULL;
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapSCSITarget *scsi_target = &def->caps->data.scsi_target;
|
2009-11-12 23:03:47 +01:00
|
|
|
|
|
|
|
|
sysname = udev_device_get_sysname(device);
|
|
|
|
|
|
2019-10-20 13:49:46 +02:00
|
|
|
scsi_target->name = g_strdup(sysname);
|
2009-11-12 23:03:47 +01:00
|
|
|
|
2018-01-12 13:14:26 +01:00
|
|
|
virNodeDeviceGetSCSITargetCaps(def->sysfs_path, &def->caps->data.scsi_target);
|
2017-05-22 08:38:25 +02:00
|
|
|
|
2021-05-31 16:19:12 +02:00
|
|
|
udevGenerateDeviceName(device, def, NULL);
|
2009-11-12 23:03:47 +01:00
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
return 0;
|
2009-11-12 23:03:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
udevGetSCSIType(virNodeDeviceDef *def G_GNUC_UNUSED,
|
2017-05-12 11:49:18 -04:00
|
|
|
unsigned int type,
|
|
|
|
|
char **typestring)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
|
|
|
|
int ret = 0;
|
|
|
|
|
int foundtype = 1;
|
|
|
|
|
|
|
|
|
|
*typestring = NULL;
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case TYPE_DISK:
|
2019-10-18 13:27:03 +02:00
|
|
|
*typestring = g_strdup("disk");
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
|
|
|
|
case TYPE_TAPE:
|
2019-10-18 13:27:03 +02:00
|
|
|
*typestring = g_strdup("tape");
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
|
|
|
|
case TYPE_PROCESSOR:
|
2019-10-18 13:27:03 +02:00
|
|
|
*typestring = g_strdup("processor");
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
|
|
|
|
case TYPE_WORM:
|
2019-10-18 13:27:03 +02:00
|
|
|
*typestring = g_strdup("worm");
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
|
|
|
|
case TYPE_ROM:
|
2019-10-18 13:27:03 +02:00
|
|
|
*typestring = g_strdup("cdrom");
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
|
|
|
|
case TYPE_SCANNER:
|
2019-10-18 13:27:03 +02:00
|
|
|
*typestring = g_strdup("scanner");
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
|
|
|
|
case TYPE_MOD:
|
2019-10-18 13:27:03 +02:00
|
|
|
*typestring = g_strdup("mod");
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
|
|
|
|
case TYPE_MEDIUM_CHANGER:
|
2019-10-18 13:27:03 +02:00
|
|
|
*typestring = g_strdup("changer");
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
|
|
|
|
case TYPE_ENCLOSURE:
|
2019-10-18 13:27:03 +02:00
|
|
|
*typestring = g_strdup("enclosure");
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
2011-02-07 17:25:06 +00:00
|
|
|
case TYPE_RAID:
|
2019-10-18 13:27:03 +02:00
|
|
|
*typestring = g_strdup("raid");
|
2011-02-07 17:25:06 +00:00
|
|
|
break;
|
2009-11-12 22:48:24 +01:00
|
|
|
case TYPE_NO_LUN:
|
|
|
|
|
default:
|
|
|
|
|
foundtype = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*typestring == NULL) {
|
|
|
|
|
if (foundtype == 1) {
|
|
|
|
|
ret = -1;
|
|
|
|
|
} else {
|
2011-02-07 17:25:06 +00:00
|
|
|
VIR_DEBUG("Failed to find SCSI device type %d for %s",
|
|
|
|
|
type, def->sysfs_path);
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
2019-10-14 14:45:33 +02:00
|
|
|
udevProcessSCSIDevice(struct udev_device *device G_GNUC_UNUSED,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
|
|
|
|
int ret = -1;
|
|
|
|
|
unsigned int tmp = 0;
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapSCSI *scsi = &def->caps->data.scsi;
|
2019-12-23 15:47:21 +00:00
|
|
|
g_autofree char *filename = NULL;
|
|
|
|
|
char *p = NULL;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2019-12-23 15:47:21 +00:00
|
|
|
filename = g_path_get_basename(def->sysfs_path);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (virStrToLong_ui(filename, &p, 10, &scsi->host) < 0 || p == NULL ||
|
|
|
|
|
virStrToLong_ui(p + 1, &p, 10, &scsi->bus) < 0 || p == NULL ||
|
|
|
|
|
virStrToLong_ui(p + 1, &p, 10, &scsi->target) < 0 || p == NULL ||
|
|
|
|
|
virStrToLong_ui(p + 1, &p, 10, &scsi->lun) < 0) {
|
2016-06-03 14:54:03 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
|
_("failed to parse the SCSI address from filename: '%s'"),
|
|
|
|
|
filename);
|
|
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
2016-06-03 18:49:40 +02:00
|
|
|
if (udev_device_get_sysattr_value(device, "type")) {
|
|
|
|
|
if (udevGetUintSysfsAttr(device, "type", &tmp, 0) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 18:49:40 +02:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetSCSIType(def, tmp, &scsi->type) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-31 16:19:12 +02:00
|
|
|
udevGenerateDeviceName(device, def, NULL);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
2016-06-03 19:40:45 +02:00
|
|
|
cleanup:
|
2009-11-12 22:48:24 +01:00
|
|
|
if (ret != 0) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
|
_("Failed to process SCSI device with sysfs path '%s'"),
|
|
|
|
|
def->sysfs_path);
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevProcessDisk(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapStorage *storage = &def->caps->data.storage;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetUint64SysfsAttr(device, "size", &storage->num_blocks) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 18:54:43 +02:00
|
|
|
if (udevGetUint64SysfsAttr(device, "queue/logical_block_size",
|
2017-03-02 10:59:25 -05:00
|
|
|
&storage->logical_block_size) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
storage->size = storage->num_blocks * storage->logical_block_size;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevProcessRemoveableMedia(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def,
|
2017-05-12 11:49:18 -04:00
|
|
|
int has_media)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapStorage *storage = &def->caps->data.storage;
|
2016-06-03 19:38:59 +02:00
|
|
|
int is_removable = 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:39:39 +02:00
|
|
|
if (udevGetIntSysfsAttr(device, "removable", &is_removable, 0) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
if (is_removable == 1)
|
2009-11-12 22:48:24 +01:00
|
|
|
def->caps->data.storage.flags |= VIR_NODE_DEV_CAP_STORAGE_REMOVABLE;
|
|
|
|
|
|
2016-06-03 19:38:59 +02:00
|
|
|
if (!has_media)
|
|
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:38:59 +02:00
|
|
|
def->caps->data.storage.flags |=
|
|
|
|
|
VIR_NODE_DEV_CAP_STORAGE_REMOVABLE_MEDIA_AVAILABLE;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2021-05-31 16:24:18 +02:00
|
|
|
udevGetStringProperty(device, "ID_FS_LABEL", &storage->media_label);
|
2009-12-14 14:58:23 +01:00
|
|
|
|
2016-06-03 19:38:59 +02:00
|
|
|
if (udevGetUint64SysfsAttr(device, "size",
|
2017-03-02 10:59:25 -05:00
|
|
|
&storage->num_blocks) < 0)
|
2016-06-03 19:38:59 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:38:59 +02:00
|
|
|
if (udevGetUint64SysfsAttr(device, "queue/logical_block_size",
|
2017-03-02 10:59:25 -05:00
|
|
|
&storage->logical_block_size) < 0)
|
2016-06-03 19:38:59 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:38:59 +02:00
|
|
|
/* XXX This calculation is wrong for the qemu virtual cdrom
|
|
|
|
|
* which reports the size in 512 byte blocks, but the logical
|
|
|
|
|
* block size as 2048. I don't have a physical cdrom on a
|
|
|
|
|
* devel system to see how they behave. */
|
|
|
|
|
def->caps->data.storage.removable_media_size =
|
|
|
|
|
def->caps->data.storage.num_blocks *
|
|
|
|
|
def->caps->data.storage.logical_block_size;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:38:59 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
udevProcessCDROM(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2010-01-12 14:56:56 -05:00
|
|
|
{
|
|
|
|
|
int has_media = 0;
|
|
|
|
|
|
|
|
|
|
/* NB: the drive_type string provided by udev is different from
|
|
|
|
|
* that provided by HAL; now it's "cd" instead of "cdrom" We
|
|
|
|
|
* change it to cdrom to preserve compatibility with earlier
|
|
|
|
|
* versions of libvirt. */
|
|
|
|
|
VIR_FREE(def->caps->data.storage.drive_type);
|
2019-10-20 13:49:46 +02:00
|
|
|
def->caps->data.storage.drive_type = g_strdup("cdrom");
|
2010-01-12 14:56:56 -05:00
|
|
|
|
2016-06-03 16:10:21 +02:00
|
|
|
if (udevHasDeviceProperty(device, "ID_CDROM_MEDIA") &&
|
|
|
|
|
udevGetIntProperty(device, "ID_CDROM_MEDIA", &has_media, 0) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2010-01-12 14:56:56 -05:00
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
return udevProcessRemoveableMedia(device, def, has_media);
|
2010-01-12 14:56:56 -05:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
udevProcessFloppy(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2010-01-12 14:56:56 -05:00
|
|
|
{
|
|
|
|
|
int has_media = 0;
|
|
|
|
|
|
2020-11-17 12:22:34 +00:00
|
|
|
if (udevHasDeviceProperty(device, "ID_FS_LABEL")) {
|
2010-01-12 14:56:56 -05:00
|
|
|
/* Legacy floppy */
|
|
|
|
|
has_media = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return udevProcessRemoveableMedia(device, def, has_media);
|
|
|
|
|
}
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2013-04-03 18:20:41 +01:00
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevProcessSD(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2013-04-03 18:20:41 +01:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapStorage *storage = &def->caps->data.storage;
|
2013-04-03 18:20:41 +01:00
|
|
|
|
2016-06-03 18:54:43 +02:00
|
|
|
if (udevGetUint64SysfsAttr(device, "size",
|
2017-03-02 10:59:25 -05:00
|
|
|
&storage->num_blocks) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2013-04-03 18:20:41 +01:00
|
|
|
|
2016-06-03 18:54:43 +02:00
|
|
|
if (udevGetUint64SysfsAttr(device, "queue/logical_block_size",
|
2017-03-02 10:59:25 -05:00
|
|
|
&storage->logical_block_size) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2013-04-03 18:20:41 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
storage->size = storage->num_blocks * storage->logical_block_size;
|
2013-04-03 18:20:41 +01:00
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
return 0;
|
2013-04-03 18:20:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-09-14 21:11:46 +02:00
|
|
|
static int
|
|
|
|
|
udevProcessDASD(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2020-09-14 21:11:46 +02:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapStorage *storage = &def->caps->data.storage;
|
2020-09-14 21:11:46 +02:00
|
|
|
|
|
|
|
|
if (udevGetStringSysfsAttr(device, "device/uid", &storage->serial) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return udevProcessDisk(device, def);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
/* This function exists to deal with the case in which a driver does
|
|
|
|
|
* not provide a device type in the usual place, but udev told us it's
|
|
|
|
|
* a storage device, and we can make a good guess at what kind of
|
|
|
|
|
* storage device it is from other information that is provided. */
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
2021-03-11 08:16:13 +01:00
|
|
|
udevKludgeStorageType(virNodeDeviceDef *def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2011-02-09 16:41:30 +00:00
|
|
|
VIR_DEBUG("Could not find definitive storage type for device "
|
|
|
|
|
"with sysfs path '%s', trying to guess it",
|
|
|
|
|
def->sysfs_path);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 18:04:23 +02:00
|
|
|
/* virtio disk */
|
2019-10-20 13:49:46 +02:00
|
|
|
if (STRPREFIX(def->caps->data.storage.block, "/dev/vd")) {
|
|
|
|
|
def->caps->data.storage.drive_type = g_strdup("disk");
|
2009-11-12 22:48:24 +01:00
|
|
|
VIR_DEBUG("Found storage type '%s' for device "
|
2010-04-02 21:44:04 +02:00
|
|
|
"with sysfs path '%s'",
|
2009-11-12 22:48:24 +01:00
|
|
|
def->caps->data.storage.drive_type,
|
|
|
|
|
def->sysfs_path);
|
2016-06-03 18:04:23 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
2020-09-14 21:11:46 +02:00
|
|
|
|
|
|
|
|
/* For Direct Access Storage Devices (DASDs) there are
|
|
|
|
|
* currently no identifiers in udev besides ID_PATH. Since
|
|
|
|
|
* ID_TYPE=disk does not exist on DASDs they fall through
|
|
|
|
|
* the udevProcessStorage detection logic. */
|
|
|
|
|
if (STRPREFIX(def->caps->data.storage.block, "/dev/dasd")) {
|
|
|
|
|
def->caps->data.storage.drive_type = g_strdup("dasd");
|
|
|
|
|
VIR_DEBUG("Found storage type '%s' for device "
|
|
|
|
|
"with sysfs path '%s'",
|
|
|
|
|
def->caps->data.storage.drive_type,
|
|
|
|
|
def->sysfs_path);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2016-06-03 18:04:23 +02:00
|
|
|
VIR_DEBUG("Could not determine storage type "
|
|
|
|
|
"for device with sysfs path '%s'", def->sysfs_path);
|
|
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevProcessStorage(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapStorage *storage = &def->caps->data.storage;
|
2009-11-12 22:48:24 +01:00
|
|
|
int ret = -1;
|
2021-05-31 16:22:42 +02:00
|
|
|
int rv;
|
2009-12-07 19:00:11 +01:00
|
|
|
const char* devnode;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2009-12-07 19:00:11 +01:00
|
|
|
devnode = udev_device_get_devnode(device);
|
2012-10-17 10:23:12 +01:00
|
|
|
if (!devnode) {
|
2010-01-19 14:17:20 +01:00
|
|
|
VIR_DEBUG("No devnode for '%s'", udev_device_get_devpath(device));
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-12-07 19:00:11 +01:00
|
|
|
}
|
2013-05-03 14:44:20 +02:00
|
|
|
|
2019-10-20 13:49:46 +02:00
|
|
|
storage->block = g_strdup(devnode);
|
2010-01-12 15:01:21 -05:00
|
|
|
|
2021-05-31 16:24:18 +02:00
|
|
|
udevGetStringProperty(device, "ID_BUS", &storage->bus);
|
|
|
|
|
udevGetStringProperty(device, "ID_SERIAL", &storage->serial);
|
2016-06-03 15:54:19 +02:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetStringSysfsAttr(device, "device/vendor", &storage->vendor) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 19:33:05 +02:00
|
|
|
if (def->caps->data.storage.vendor)
|
|
|
|
|
virTrimSpaces(def->caps->data.storage.vendor, NULL);
|
|
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetStringSysfsAttr(device, "device/model", &storage->model) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 19:33:05 +02:00
|
|
|
if (def->caps->data.storage.model)
|
|
|
|
|
virTrimSpaces(def->caps->data.storage.model, NULL);
|
2009-11-12 22:48:24 +01:00
|
|
|
/* There is no equivalent of the hotpluggable property in libudev,
|
|
|
|
|
* but storage is going toward a world in which hotpluggable is
|
|
|
|
|
* expected, so I don't see a problem with not having a property
|
|
|
|
|
* for it. */
|
|
|
|
|
|
2021-05-31 16:24:18 +02:00
|
|
|
udevGetStringProperty(device, "ID_TYPE", &storage->drive_type);
|
2016-06-03 15:54:19 +02:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (!storage->drive_type ||
|
2014-04-23 12:42:01 +02:00
|
|
|
STREQ(def->caps->data.storage.drive_type, "generic")) {
|
2010-01-12 14:56:56 -05:00
|
|
|
/* All floppy drives have the ID_DRIVE_FLOPPY prop. This is
|
|
|
|
|
* needed since legacy floppies don't have a drive_type */
|
2020-11-17 11:56:46 +00:00
|
|
|
if (udevHasDeviceProperty(device, "ID_DRIVE_FLOPPY"))
|
|
|
|
|
storage->drive_type = g_strdup("floppy");
|
|
|
|
|
else if (udevHasDeviceProperty(device, "ID_CDROM"))
|
|
|
|
|
storage->drive_type = g_strdup("cd");
|
|
|
|
|
else if (udevHasDeviceProperty(device, "ID_DRIVE_FLASH_SD"))
|
|
|
|
|
storage->drive_type = g_strdup("sd");
|
|
|
|
|
else if (udevKludgeStorageType(def) != 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
node_device_udev: Also process ID_TYPE=cd/dvd in udevProcessStorage()
When processing node devices, the udevProcessStorage() will be
called if the device is some form of storage. In here, ID_TYPE
attribute is queried and depending on its value one of more
specialized helper functions is called. For instance, for
ID_TYPE=="cd" the udevProcessCDROM() is called, for
ID_TYPE=="disk" the udevProcessDisk() is called, and so on.
But there's a problem with ID_TYPE and its values. Coming from
udev, we are not guaranteed that ID_TYPE will contain "cd" for
CDROM devices. In fact, there's a rule installed by sg3_utils
that will overwrite ID_TYPE to "cd/dvd" leaving us with an
unhandled type. Fortunately, this was fixed in their upstream,
but there are still versions out there, on OS platforms that we
aim to support that contain the problematic rule. Therefore, we
should accept both strings.
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1848875
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
2021-05-31 16:42:13 +02:00
|
|
|
if (STREQ(def->caps->data.storage.drive_type, "cd") ||
|
|
|
|
|
STREQ(def->caps->data.storage.drive_type, "cd/dvd")) {
|
2021-05-31 16:22:42 +02:00
|
|
|
rv = udevProcessCDROM(device, def);
|
2009-11-12 22:48:24 +01:00
|
|
|
} else if (STREQ(def->caps->data.storage.drive_type, "disk")) {
|
2021-05-31 16:22:42 +02:00
|
|
|
rv = udevProcessDisk(device, def);
|
2010-01-12 14:56:56 -05:00
|
|
|
} else if (STREQ(def->caps->data.storage.drive_type, "floppy")) {
|
2021-05-31 16:22:42 +02:00
|
|
|
rv = udevProcessFloppy(device, def);
|
2013-04-03 18:20:41 +01:00
|
|
|
} else if (STREQ(def->caps->data.storage.drive_type, "sd")) {
|
2021-05-31 16:22:42 +02:00
|
|
|
rv = udevProcessSD(device, def);
|
2020-09-14 21:11:46 +02:00
|
|
|
} else if (STREQ(def->caps->data.storage.drive_type, "dasd")) {
|
2021-05-31 16:22:42 +02:00
|
|
|
rv = udevProcessDASD(device, def);
|
2009-11-12 22:48:24 +01:00
|
|
|
} else {
|
2011-02-09 16:41:30 +00:00
|
|
|
VIR_DEBUG("Unsupported storage type '%s'",
|
|
|
|
|
def->caps->data.storage.drive_type);
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-31 16:22:42 +02:00
|
|
|
if (rv < 0)
|
|
|
|
|
goto cleanup;
|
|
|
|
|
|
2021-05-31 16:19:12 +02:00
|
|
|
udevGenerateDeviceName(device, def, storage->serial);
|
2021-05-31 16:22:42 +02:00
|
|
|
ret = 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:40:45 +02:00
|
|
|
cleanup:
|
2013-04-03 18:20:41 +01:00
|
|
|
VIR_DEBUG("Storage ret=%d", ret);
|
2009-11-12 22:48:24 +01:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
2013-06-03 18:05:32 +08:00
|
|
|
static int
|
2014-03-13 11:55:46 +00:00
|
|
|
udevProcessSCSIGeneric(struct udev_device *dev,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2013-06-03 18:05:32 +08:00
|
|
|
{
|
2021-05-31 16:24:18 +02:00
|
|
|
udevGetStringProperty(dev, "DEVNAME", &def->caps->data.sg.path);
|
|
|
|
|
|
|
|
|
|
if (!def->caps->data.sg.path)
|
2013-06-03 18:05:32 +08:00
|
|
|
return -1;
|
|
|
|
|
|
2021-05-31 16:19:12 +02:00
|
|
|
udevGenerateDeviceName(dev, def, NULL);
|
2013-06-03 18:05:32 +08:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
2017-03-06 17:18:48 +01:00
|
|
|
static int
|
|
|
|
|
udevProcessMediatedDevice(struct udev_device *dev,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2017-03-06 17:18:48 +01:00
|
|
|
{
|
|
|
|
|
int ret = -1;
|
|
|
|
|
int iommugrp = -1;
|
|
|
|
|
char *linkpath = NULL;
|
2017-05-18 14:43:05 +02:00
|
|
|
char *canonicalpath = NULL;
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapMdev *data = &def->caps->data.mdev;
|
2017-03-06 17:18:48 +01:00
|
|
|
|
2017-06-20 16:15:22 +02:00
|
|
|
/* Because of a kernel uevent race, we might get the 'add' event prior to
|
|
|
|
|
* the sysfs tree being ready, so any attempt to access any sysfs attribute
|
|
|
|
|
* would result in ENOENT and us dropping the device, so let's work around
|
|
|
|
|
* it by waiting for the attributes to become available.
|
|
|
|
|
*/
|
|
|
|
|
|
2019-10-22 15:26:14 +02:00
|
|
|
linkpath = g_strdup_printf("%s/mdev_type", udev_device_get_syspath(dev));
|
2017-03-06 17:18:48 +01:00
|
|
|
|
2017-06-20 16:15:22 +02:00
|
|
|
if (virFileWaitForExists(linkpath, 1, 100) < 0) {
|
|
|
|
|
virReportSystemError(errno,
|
|
|
|
|
_("failed to wait for file '%s' to appear"),
|
|
|
|
|
linkpath);
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-20 16:50:26 +02:00
|
|
|
if (virFileResolveLink(linkpath, &canonicalpath) < 0) {
|
|
|
|
|
virReportSystemError(errno, _("failed to resolve '%s'"), linkpath);
|
2017-03-06 17:18:48 +01:00
|
|
|
goto cleanup;
|
2017-06-20 16:50:26 +02:00
|
|
|
}
|
2017-03-06 17:18:48 +01:00
|
|
|
|
2019-12-23 15:47:21 +00:00
|
|
|
data->type = g_path_get_basename(canonicalpath);
|
2017-03-06 17:18:48 +01:00
|
|
|
|
2020-06-18 16:05:58 -05:00
|
|
|
data->uuid = g_strdup(udev_device_get_sysname(dev));
|
|
|
|
|
if ((iommugrp = virMediatedDeviceGetIOMMUGroupNum(data->uuid)) < 0)
|
2017-03-06 17:18:48 +01:00
|
|
|
goto cleanup;
|
|
|
|
|
|
2021-05-31 16:19:12 +02:00
|
|
|
udevGenerateDeviceName(dev, def, NULL);
|
2017-03-06 17:18:48 +01:00
|
|
|
|
|
|
|
|
data->iommuGroupNumber = iommugrp;
|
|
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
cleanup:
|
|
|
|
|
VIR_FREE(linkpath);
|
2017-05-18 14:43:05 +02:00
|
|
|
VIR_FREE(canonicalpath);
|
2017-03-06 17:18:48 +01:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-22 08:38:22 +02:00
|
|
|
|
|
|
|
|
static int
|
2020-09-14 21:11:43 +02:00
|
|
|
udevGetCCWAddress(const char *sysfs_path,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapData *data)
|
2017-05-22 08:38:22 +02:00
|
|
|
{
|
|
|
|
|
char *p;
|
|
|
|
|
|
2020-09-14 21:11:43 +02:00
|
|
|
if ((p = strrchr(sysfs_path, '/')) == NULL ||
|
2017-05-22 08:38:22 +02:00
|
|
|
virStrToLong_ui(p + 1, &p, 16, &data->ccw_dev.cssid) < 0 || p == NULL ||
|
|
|
|
|
virStrToLong_ui(p + 1, &p, 16, &data->ccw_dev.ssid) < 0 || p == NULL ||
|
|
|
|
|
virStrToLong_ui(p + 1, &p, 16, &data->ccw_dev.devno) < 0) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
|
_("failed to parse the CCW address from sysfs path: '%s'"),
|
2020-09-14 21:11:43 +02:00
|
|
|
sysfs_path);
|
2017-05-22 08:38:22 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-14 21:11:43 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
udevProcessCCW(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2020-09-14 21:11:43 +02:00
|
|
|
{
|
2021-02-01 13:42:03 +01:00
|
|
|
int online = 0;
|
2020-09-14 21:11:43 +02:00
|
|
|
|
|
|
|
|
/* process only online devices to keep the list sane */
|
|
|
|
|
if (udevGetIntSysfsAttr(device, "online", &online, 0) < 0 || online != 1)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if (udevGetCCWAddress(def->sysfs_path, &def->caps->data) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2021-05-31 16:19:12 +02:00
|
|
|
udevGenerateDeviceName(device, def, NULL);
|
2017-05-22 08:38:22 +02:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-09-14 21:11:44 +02:00
|
|
|
static int
|
|
|
|
|
udevProcessCSS(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2020-09-14 21:11:44 +02:00
|
|
|
{
|
|
|
|
|
/* only process IO subchannel and vfio-ccw devices to keep the list sane */
|
udevProcessCSS: Check if def->driver is non-NULL
Don't process subchannel devices where `def->driver` is not set. This
fixes the following segfault:
Thread 21 "nodedev-init" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x3ffb08fc910 (LWP 64303)]
(gdb) bt
#0 0x000003fffd1272b4 in __strcmp_vx () at /lib64/libc.so.6
#1 0x000003ffc260c3a8 in udevProcessCSS (device=0x3ff9018d130, def=0x3ff90194a90)
#2 0x000003ffc260cb78 in udevGetDeviceDetails (device=0x3ff9018d130, def=0x3ff90194a90)
#3 0x000003ffc260d126 in udevAddOneDevice (device=0x3ff9018d130)
#4 0x000003ffc260d414 in udevProcessDeviceListEntry (udev=0x3ffa810d800, list_entry=0x3ff90001990)
#5 0x000003ffc260d638 in udevEnumerateDevices (udev=0x3ffa810d800)
#6 0x000003ffc260e08e in nodeStateInitializeEnumerate (opaque=0x3ffa810d800)
#7 0x000003fffdaa14b6 in virThreadHelper (data=0x3ffa810df00)
#8 0x000003fffc309ed6 in start_thread ()
#9 0x000003fffd185e66 in thread_start ()
(gdb) p *def
$2 = {
name = 0x0,
sysfs_path = 0x3ff90198e80 "/sys/devices/css0/0.0.ff40",
parent = 0x0,
parent_sysfs_path = 0x0,
parent_wwnn = 0x0,
parent_wwpn = 0x0,
parent_fabric_wwn = 0x0,
driver = 0x0,
devnode = 0x0,
devlinks = 0x3ff90194670,
caps = 0x3ff90194380
}
Fixes: 05e6cdafa6e0 ("node_device: detect CSS devices")
Reviewed-by: Boris Fiuczynski <fiuczy@linux.ibm.com>
Reviewed-by: Erik Skultety <eskultet@redhat.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
2020-09-21 19:06:32 +02:00
|
|
|
if (!def->driver ||
|
|
|
|
|
(STRNEQ(def->driver, "io_subchannel") &&
|
|
|
|
|
STRNEQ(def->driver, "vfio_ccw")))
|
2020-09-14 21:11:44 +02:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if (udevGetCCWAddress(def->sysfs_path, &def->caps->data) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2021-05-31 16:19:12 +02:00
|
|
|
udevGenerateDeviceName(device, def, NULL);
|
2020-09-14 21:11:44 +02:00
|
|
|
|
2020-11-11 13:45:22 +01:00
|
|
|
if (virNodeDeviceGetCSSDynamicCaps(def->sysfs_path, &def->caps->data.ccw_dev) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2020-09-14 21:11:44 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-14 12:08:30 -05:00
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
udevGetVDPACharDev(const char *sysfs_path,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapData *data)
|
2020-10-14 12:08:30 -05:00
|
|
|
{
|
|
|
|
|
struct dirent *entry;
|
2020-10-25 17:50:51 -04:00
|
|
|
g_autoptr(DIR) dir = NULL;
|
2020-10-14 12:08:30 -05:00
|
|
|
int direrr;
|
|
|
|
|
|
|
|
|
|
if (virDirOpenIfExists(&dir, sysfs_path) <= 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
while ((direrr = virDirRead(dir, &entry, NULL)) > 0) {
|
|
|
|
|
if (g_str_has_prefix(entry->d_name, "vhost-vdpa")) {
|
|
|
|
|
g_autofree char *chardev = g_strdup_printf("/dev/%s", entry->d_name);
|
|
|
|
|
|
|
|
|
|
if (!virFileExists(chardev)) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
|
_("vDPA chardev path '%s' does not exist"),
|
|
|
|
|
chardev);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
VIR_DEBUG("vDPA chardev is at '%s'", chardev);
|
|
|
|
|
|
|
|
|
|
data->vdpa.chardev = g_steal_pointer(&chardev);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (direrr < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
udevProcessVDPA(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2020-10-14 12:08:30 -05:00
|
|
|
{
|
2021-05-31 16:19:12 +02:00
|
|
|
udevGenerateDeviceName(device, def, NULL);
|
2020-10-14 12:08:30 -05:00
|
|
|
|
|
|
|
|
if (udevGetVDPACharDev(def->sysfs_path, &def->caps->data) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-12-03 18:59:33 +01:00
|
|
|
static int
|
|
|
|
|
udevProcessAPCard(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2020-12-03 18:59:33 +01:00
|
|
|
{
|
|
|
|
|
char *c;
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapData *data = &def->caps->data;
|
2020-12-03 18:59:33 +01:00
|
|
|
|
|
|
|
|
/* The sysfs path would be in the format /sys/bus/ap/devices/cardXX,
|
|
|
|
|
where XX is the ap adapter id */
|
|
|
|
|
if ((c = strrchr(def->sysfs_path, '/')) == NULL ||
|
|
|
|
|
virStrToLong_ui(c + 1 + strlen("card"), NULL, 16,
|
|
|
|
|
&data->ap_card.ap_adapter) < 0) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
|
_("failed to parse the AP Card from sysfs path: '%s'"),
|
|
|
|
|
def->sysfs_path);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-31 16:19:12 +02:00
|
|
|
udevGenerateDeviceName(device, def, NULL);
|
2020-12-03 18:59:33 +01:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-12-03 18:59:35 +01:00
|
|
|
static int
|
|
|
|
|
udevProcessAPQueue(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2020-12-03 18:59:35 +01:00
|
|
|
{
|
|
|
|
|
char *c;
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapData *data = &def->caps->data;
|
2020-12-03 18:59:35 +01:00
|
|
|
|
|
|
|
|
/* The sysfs path would be in the format /sys/bus/ap/devices
|
|
|
|
|
/XX.YYYY, where XX is the ap adapter id and YYYY is the ap
|
|
|
|
|
domain id */
|
|
|
|
|
if ((c = strrchr(def->sysfs_path, '/')) == NULL ||
|
|
|
|
|
virStrToLong_ui(c + 1, &c, 16, &data->ap_queue.ap_adapter) < 0 ||
|
|
|
|
|
virStrToLong_ui(c + 1, &c, 16, &data->ap_queue.ap_domain) < 0) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
|
_("failed to parse the AP Queue from sysfs path: '%s'"),
|
|
|
|
|
def->sysfs_path);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-31 16:19:12 +02:00
|
|
|
udevGenerateDeviceName(device, def, NULL);
|
2020-12-03 18:59:35 +01:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-12-03 18:59:38 +01:00
|
|
|
static int
|
2020-12-03 18:59:42 +01:00
|
|
|
udevProcessAPMatrix(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2020-12-03 18:59:38 +01:00
|
|
|
{
|
|
|
|
|
/* Both udev_device_get_sysname and udev_device_get_subsystem return
|
|
|
|
|
* "matrix" for an AP matrix device, so in order to prevent confusion in
|
|
|
|
|
* naming, let's fallback to hardcoding the name.
|
|
|
|
|
*/
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapData *data = &def->caps->data;
|
2020-12-03 18:59:42 +01:00
|
|
|
|
|
|
|
|
data->ap_matrix.addr = g_strdup(udev_device_get_sysname(device));
|
2020-12-03 18:59:38 +01:00
|
|
|
def->name = g_strdup("ap_matrix");
|
|
|
|
|
|
2020-12-03 18:59:43 +01:00
|
|
|
if (virNodeDeviceGetAPMatrixDynamicCaps(def->sysfs_path,
|
|
|
|
|
&data->ap_matrix) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2020-12-03 18:59:38 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-02-15 01:04:10 +04:00
|
|
|
static int
|
|
|
|
|
udevGetDeviceNodes(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2017-02-15 01:04:10 +04:00
|
|
|
{
|
|
|
|
|
const char *devnode = NULL;
|
|
|
|
|
struct udev_list_entry *list_entry = NULL;
|
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
|
|
devnode = udev_device_get_devnode(device);
|
|
|
|
|
|
2019-10-20 13:49:46 +02:00
|
|
|
def->devnode = g_strdup(devnode);
|
2017-02-15 01:04:10 +04:00
|
|
|
|
|
|
|
|
udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device))
|
|
|
|
|
n++;
|
|
|
|
|
|
2020-09-23 22:04:41 +02:00
|
|
|
def->devlinks = g_new0(char *, n + 1);
|
2017-02-15 01:04:10 +04:00
|
|
|
|
|
|
|
|
n = 0;
|
|
|
|
|
udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) {
|
2019-10-20 13:49:46 +02:00
|
|
|
def->devlinks[n++] = g_strdup(udev_list_entry_get_name(list_entry));
|
2017-02-15 01:04:10 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
2013-06-03 18:05:31 +08:00
|
|
|
static int
|
|
|
|
|
udevGetDeviceType(struct udev_device *device,
|
2014-05-11 12:08:48 -03:00
|
|
|
virNodeDevCapType *type)
|
2013-06-03 18:05:31 +08:00
|
|
|
{
|
|
|
|
|
const char *devtype = NULL;
|
2013-06-03 18:05:32 +08:00
|
|
|
char *subsystem = NULL;
|
2013-06-03 18:05:31 +08:00
|
|
|
int ret = -1;
|
2010-05-27 10:44:02 -04:00
|
|
|
|
2013-06-03 18:05:31 +08:00
|
|
|
devtype = udev_device_get_devtype(device);
|
|
|
|
|
*type = 0;
|
|
|
|
|
|
|
|
|
|
if (devtype) {
|
|
|
|
|
if (STREQ(devtype, "usb_device"))
|
|
|
|
|
*type = VIR_NODE_DEV_CAP_USB_DEV;
|
|
|
|
|
else if (STREQ(devtype, "usb_interface"))
|
|
|
|
|
*type = VIR_NODE_DEV_CAP_USB_INTERFACE;
|
|
|
|
|
else if (STREQ(devtype, "scsi_host"))
|
|
|
|
|
*type = VIR_NODE_DEV_CAP_SCSI_HOST;
|
|
|
|
|
else if (STREQ(devtype, "scsi_target"))
|
|
|
|
|
*type = VIR_NODE_DEV_CAP_SCSI_TARGET;
|
|
|
|
|
else if (STREQ(devtype, "scsi_device"))
|
|
|
|
|
*type = VIR_NODE_DEV_CAP_SCSI;
|
|
|
|
|
else if (STREQ(devtype, "disk"))
|
|
|
|
|
*type = VIR_NODE_DEV_CAP_STORAGE;
|
|
|
|
|
else if (STREQ(devtype, "wlan"))
|
|
|
|
|
*type = VIR_NODE_DEV_CAP_NET;
|
2017-02-15 01:04:12 +04:00
|
|
|
else if (STREQ(devtype, "drm_minor"))
|
|
|
|
|
*type = VIR_NODE_DEV_CAP_DRM;
|
2020-12-03 18:59:33 +01:00
|
|
|
else if (STREQ(devtype, "ap_card"))
|
|
|
|
|
*type = VIR_NODE_DEV_CAP_AP_CARD;
|
2020-12-03 18:59:35 +01:00
|
|
|
else if (STREQ(devtype, "ap_queue"))
|
|
|
|
|
*type = VIR_NODE_DEV_CAP_AP_QUEUE;
|
2013-06-03 18:05:31 +08:00
|
|
|
} else {
|
|
|
|
|
/* PCI devices don't set the DEVTYPE property. */
|
2013-06-18 16:39:24 +08:00
|
|
|
if (udevHasDeviceProperty(device, "PCI_CLASS"))
|
2013-06-03 18:05:31 +08:00
|
|
|
*type = VIR_NODE_DEV_CAP_PCI_DEV;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2013-06-03 18:05:31 +08:00
|
|
|
/* Wired network interfaces don't set the DEVTYPE property,
|
|
|
|
|
* USB devices also have an INTERFACE property, but they do
|
|
|
|
|
* set DEVTYPE, so if devtype is NULL and the INTERFACE
|
|
|
|
|
* property exists, we have a network device. */
|
2013-06-18 16:39:24 +08:00
|
|
|
if (udevHasDeviceProperty(device, "INTERFACE"))
|
2013-06-03 18:05:31 +08:00
|
|
|
*type = VIR_NODE_DEV_CAP_NET;
|
2013-06-03 18:05:32 +08:00
|
|
|
|
2017-05-22 08:38:22 +02:00
|
|
|
/* The following devices do not set the DEVTYPE property, therefore
|
|
|
|
|
* we need to rely on the SUBSYSTEM property */
|
2021-05-31 16:24:18 +02:00
|
|
|
udevGetStringProperty(device, "SUBSYSTEM", &subsystem);
|
2016-06-03 15:54:19 +02:00
|
|
|
|
|
|
|
|
if (STREQ_NULLABLE(subsystem, "scsi_generic"))
|
2013-06-03 18:05:32 +08:00
|
|
|
*type = VIR_NODE_DEV_CAP_SCSI_GENERIC;
|
2017-03-06 17:18:48 +01:00
|
|
|
else if (STREQ_NULLABLE(subsystem, "mdev"))
|
|
|
|
|
*type = VIR_NODE_DEV_CAP_MDEV;
|
2017-05-22 08:38:22 +02:00
|
|
|
else if (STREQ_NULLABLE(subsystem, "ccw"))
|
|
|
|
|
*type = VIR_NODE_DEV_CAP_CCW_DEV;
|
2020-09-14 21:11:44 +02:00
|
|
|
else if (STREQ_NULLABLE(subsystem, "css"))
|
|
|
|
|
*type = VIR_NODE_DEV_CAP_CSS_DEV;
|
2020-10-14 12:08:30 -05:00
|
|
|
else if (STREQ_NULLABLE(subsystem, "vdpa"))
|
|
|
|
|
*type = VIR_NODE_DEV_CAP_VDPA;
|
2020-12-03 18:59:38 +01:00
|
|
|
else if (STREQ_NULLABLE(subsystem, "matrix"))
|
|
|
|
|
*type = VIR_NODE_DEV_CAP_AP_MATRIX;
|
2017-03-06 17:18:48 +01:00
|
|
|
|
2013-06-03 18:05:32 +08:00
|
|
|
VIR_FREE(subsystem);
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
2013-06-03 18:05:31 +08:00
|
|
|
if (!*type)
|
|
|
|
|
VIR_DEBUG("Could not determine device type for device "
|
|
|
|
|
"with sysfs name '%s'",
|
|
|
|
|
udev_device_get_sysname(device));
|
|
|
|
|
else
|
|
|
|
|
ret = 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevGetDeviceDetails(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2015-05-08 12:55:00 -04:00
|
|
|
switch (def->caps->data.type) {
|
2009-11-12 22:48:24 +01:00
|
|
|
case VIR_NODE_DEV_CAP_PCI_DEV:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessPCI(device, def);
|
2009-11-12 22:48:24 +01:00
|
|
|
case VIR_NODE_DEV_CAP_USB_DEV:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessUSBDevice(device, def);
|
2009-11-12 22:48:24 +01:00
|
|
|
case VIR_NODE_DEV_CAP_USB_INTERFACE:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessUSBInterface(device, def);
|
2009-11-12 22:48:24 +01:00
|
|
|
case VIR_NODE_DEV_CAP_NET:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessNetworkInterface(device, def);
|
2009-11-12 22:48:24 +01:00
|
|
|
case VIR_NODE_DEV_CAP_SCSI_HOST:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessSCSIHost(device, def);
|
2009-11-12 23:03:47 +01:00
|
|
|
case VIR_NODE_DEV_CAP_SCSI_TARGET:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessSCSITarget(device, def);
|
2009-11-12 22:48:24 +01:00
|
|
|
case VIR_NODE_DEV_CAP_SCSI:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessSCSIDevice(device, def);
|
2009-11-12 22:48:24 +01:00
|
|
|
case VIR_NODE_DEV_CAP_STORAGE:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessStorage(device, def);
|
2013-06-03 18:05:32 +08:00
|
|
|
case VIR_NODE_DEV_CAP_SCSI_GENERIC:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessSCSIGeneric(device, def);
|
2017-02-15 01:04:12 +04:00
|
|
|
case VIR_NODE_DEV_CAP_DRM:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessDRMDevice(device, def);
|
2017-03-06 17:20:00 +01:00
|
|
|
case VIR_NODE_DEV_CAP_MDEV:
|
2017-03-06 17:18:48 +01:00
|
|
|
return udevProcessMediatedDevice(device, def);
|
2017-05-22 08:38:22 +02:00
|
|
|
case VIR_NODE_DEV_CAP_CCW_DEV:
|
|
|
|
|
return udevProcessCCW(device, def);
|
2020-09-14 21:11:44 +02:00
|
|
|
case VIR_NODE_DEV_CAP_CSS_DEV:
|
|
|
|
|
return udevProcessCSS(device, def);
|
2020-10-14 12:08:30 -05:00
|
|
|
case VIR_NODE_DEV_CAP_VDPA:
|
|
|
|
|
return udevProcessVDPA(device, def);
|
2020-12-03 18:59:33 +01:00
|
|
|
case VIR_NODE_DEV_CAP_AP_CARD:
|
|
|
|
|
return udevProcessAPCard(device, def);
|
2020-12-03 18:59:35 +01:00
|
|
|
case VIR_NODE_DEV_CAP_AP_QUEUE:
|
|
|
|
|
return udevProcessAPQueue(device, def);
|
2020-12-03 18:59:38 +01:00
|
|
|
case VIR_NODE_DEV_CAP_AP_MATRIX:
|
2020-12-03 18:59:42 +01:00
|
|
|
return udevProcessAPMatrix(device, def);
|
2017-03-06 17:20:00 +01:00
|
|
|
case VIR_NODE_DEV_CAP_MDEV_TYPES:
|
2017-02-24 10:26:42 +01:00
|
|
|
case VIR_NODE_DEV_CAP_SYSTEM:
|
|
|
|
|
case VIR_NODE_DEV_CAP_FC_HOST:
|
|
|
|
|
case VIR_NODE_DEV_CAP_VPORTS:
|
|
|
|
|
case VIR_NODE_DEV_CAP_LAST:
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-24 10:26:42 +01:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
2020-04-20 15:59:19 +02:00
|
|
|
udevRemoveOneDeviceSysPath(const char *path)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceObj *obj = NULL;
|
|
|
|
|
virNodeDeviceDef *def;
|
|
|
|
|
virObjectEvent *event = NULL;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2020-04-20 15:59:19 +02:00
|
|
|
if (!(obj = virNodeDeviceObjListFindBySysfsPath(driver->devs, path))) {
|
|
|
|
|
VIR_DEBUG("Failed to find device to remove that has udev path '%s'",
|
|
|
|
|
path);
|
2017-06-03 07:36:01 -04:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
2017-05-12 12:45:16 -04:00
|
|
|
def = virNodeDeviceObjGetDef(obj);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-06-03 07:36:01 -04:00
|
|
|
event = virNodeDeviceEventLifecycleNew(def->name,
|
2016-07-28 14:02:55 +02:00
|
|
|
VIR_NODE_DEVICE_EVENT_DELETED,
|
|
|
|
|
0);
|
|
|
|
|
|
2020-06-30 17:02:48 -05:00
|
|
|
/* If the device is a mediated device that has been 'stopped', it may still
|
|
|
|
|
* be defined by mdevctl and can therefore be started again. Don't drop it
|
|
|
|
|
* from the list of node devices */
|
|
|
|
|
if (virNodeDeviceObjIsPersistent(obj)) {
|
|
|
|
|
VIR_FREE(def->sysfs_path);
|
|
|
|
|
virNodeDeviceObjSetActive(obj, false);
|
|
|
|
|
} else {
|
|
|
|
|
VIR_DEBUG("Removing device '%s' with sysfs path '%s'",
|
|
|
|
|
def->name, path);
|
|
|
|
|
virNodeDeviceObjListRemove(driver->devs, obj);
|
|
|
|
|
}
|
2020-04-20 15:40:01 +02:00
|
|
|
virNodeDeviceObjEndAPI(&obj);
|
2016-07-28 14:02:55 +02:00
|
|
|
|
2018-06-11 15:38:17 -04:00
|
|
|
virObjectEventStateQueue(driver->nodeDeviceEventState, event);
|
2017-06-03 07:36:01 -04:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
2020-04-20 15:59:19 +02:00
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
udevRemoveOneDevice(struct udev_device *device)
|
|
|
|
|
{
|
|
|
|
|
const char *path = udev_device_get_syspath(device);
|
|
|
|
|
|
|
|
|
|
return udevRemoveOneDeviceSysPath(path);
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevSetParent(struct udev_device *device,
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
|
|
|
|
struct udev_device *parent_device = NULL;
|
|
|
|
|
const char *parent_sysfs_path = NULL;
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceObj *obj = NULL;
|
|
|
|
|
virNodeDeviceDef *objdef;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2010-05-27 17:36:54 -04:00
|
|
|
parent_device = device;
|
|
|
|
|
do {
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2010-05-27 17:36:54 -04:00
|
|
|
parent_device = udev_device_get_parent(parent_device);
|
2014-11-13 15:24:17 +01:00
|
|
|
if (parent_device == NULL)
|
2010-05-27 17:36:54 -04:00
|
|
|
break;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2010-05-27 17:36:54 -04:00
|
|
|
parent_sysfs_path = udev_device_get_syspath(parent_device);
|
|
|
|
|
if (parent_sysfs_path == NULL) {
|
2012-07-18 12:42:38 +01:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
|
_("Could not get syspath for parent of '%s'"),
|
|
|
|
|
udev_device_get_syspath(parent_device));
|
2019-10-21 15:18:56 -03:00
|
|
|
return -1;
|
2010-05-27 17:36:54 -04:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 14:47:17 -04:00
|
|
|
if ((obj = virNodeDeviceObjListFindBySysfsPath(driver->devs,
|
|
|
|
|
parent_sysfs_path))) {
|
2017-05-12 12:45:16 -04:00
|
|
|
objdef = virNodeDeviceObjGetDef(obj);
|
2019-10-20 13:49:46 +02:00
|
|
|
def->parent = g_strdup(objdef->name);
|
2017-05-15 11:00:59 -04:00
|
|
|
virNodeDeviceObjEndAPI(&obj);
|
2010-05-27 17:36:54 -04:00
|
|
|
|
2019-10-20 13:49:46 +02:00
|
|
|
def->parent_sysfs_path = g_strdup(parent_sysfs_path);
|
2010-05-27 17:36:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} while (def->parent == NULL && parent_device != NULL);
|
|
|
|
|
|
2019-10-18 15:08:21 +02:00
|
|
|
if (!def->parent)
|
|
|
|
|
def->parent = g_strdup("computer");
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2019-10-21 15:18:56 -03:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevAddOneDevice(struct udev_device *device)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def = NULL;
|
|
|
|
|
virNodeDeviceObj *obj = NULL;
|
|
|
|
|
virNodeDeviceDef *objdef;
|
|
|
|
|
virObjectEvent *event = NULL;
|
2016-07-28 14:02:55 +02:00
|
|
|
bool new_device = true;
|
2009-11-12 22:48:24 +01:00
|
|
|
int ret = -1;
|
2020-06-30 17:02:48 -05:00
|
|
|
bool was_persistent = false;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2020-09-23 22:04:41 +02:00
|
|
|
def = g_new0(virNodeDeviceDef, 1);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2019-10-20 13:49:46 +02:00
|
|
|
def->sysfs_path = g_strdup(udev_device_get_syspath(device));
|
2013-05-03 14:44:20 +02:00
|
|
|
|
2021-05-31 16:24:18 +02:00
|
|
|
udevGetStringProperty(device, "DRIVER", &def->driver);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2020-09-23 22:04:41 +02:00
|
|
|
def->caps = g_new0(virNodeDevCapsDef, 1);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2015-05-08 12:55:00 -04:00
|
|
|
if (udevGetDeviceType(device, &def->caps->data.type) != 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-02-15 01:04:10 +04:00
|
|
|
if (udevGetDeviceNodes(device, def) != 0)
|
|
|
|
|
goto cleanup;
|
|
|
|
|
|
2014-11-13 15:24:17 +01:00
|
|
|
if (udevGetDeviceDetails(device, def) != 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2014-11-13 15:24:17 +01:00
|
|
|
if (udevSetParent(device, def) != 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-05-12 14:47:17 -04:00
|
|
|
if ((obj = virNodeDeviceObjListFindByName(driver->devs, def->name))) {
|
2020-06-30 17:02:48 -05:00
|
|
|
objdef = virNodeDeviceObjGetDef(obj);
|
|
|
|
|
|
|
|
|
|
if (def->caps->data.type == VIR_NODE_DEV_CAP_MDEV)
|
|
|
|
|
nodeDeviceDefCopyFromMdevctl(def, objdef);
|
|
|
|
|
was_persistent = virNodeDeviceObjIsPersistent(obj);
|
|
|
|
|
/* If the device was defined by mdevctl and was never instantiated, it
|
|
|
|
|
* won't have a sysfs path. We need to emit a CREATED event... */
|
|
|
|
|
new_device = (objdef->sysfs_path == NULL);
|
|
|
|
|
|
2017-05-15 11:00:59 -04:00
|
|
|
virNodeDeviceObjEndAPI(&obj);
|
2016-07-28 14:02:55 +02:00
|
|
|
}
|
|
|
|
|
|
2010-05-28 22:22:05 -04:00
|
|
|
/* If this is a device change, the old definition will be freed
|
|
|
|
|
* and the current definition will take its place. */
|
2017-05-12 14:47:17 -04:00
|
|
|
if (!(obj = virNodeDeviceObjListAssignDef(driver->devs, def)))
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2020-06-30 17:02:48 -05:00
|
|
|
virNodeDeviceObjSetPersistent(obj, was_persistent);
|
2017-05-12 12:45:16 -04:00
|
|
|
objdef = virNodeDeviceObjGetDef(obj);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-07-28 14:02:55 +02:00
|
|
|
if (new_device)
|
2017-06-03 07:36:01 -04:00
|
|
|
event = virNodeDeviceEventLifecycleNew(objdef->name,
|
2016-07-28 14:02:55 +02:00
|
|
|
VIR_NODE_DEVICE_EVENT_CREATED,
|
|
|
|
|
0);
|
2016-08-11 17:15:23 +02:00
|
|
|
else
|
2017-06-03 07:36:01 -04:00
|
|
|
event = virNodeDeviceEventUpdateNew(objdef->name);
|
2016-07-28 14:02:55 +02:00
|
|
|
|
2020-07-09 10:39:34 -05:00
|
|
|
virNodeDeviceObjSetActive(obj, true);
|
2017-05-15 11:00:59 -04:00
|
|
|
virNodeDeviceObjEndAPI(&obj);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
2016-06-03 19:40:45 +02:00
|
|
|
cleanup:
|
2018-06-11 15:38:17 -04:00
|
|
|
virObjectEventStateQueue(driver->nodeDeviceEventState, event);
|
2016-07-28 14:02:55 +02:00
|
|
|
|
2010-05-28 22:22:05 -04:00
|
|
|
if (ret != 0) {
|
2013-04-03 18:20:41 +01:00
|
|
|
VIR_DEBUG("Discarding device %d %p %s", ret, def,
|
2013-05-03 14:44:20 +02:00
|
|
|
def ? NULLSTR(def->sysfs_path) : "");
|
2010-05-28 22:22:05 -04:00
|
|
|
virNodeDeviceDefFree(def);
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevProcessDeviceListEntry(struct udev *udev,
|
|
|
|
|
struct udev_list_entry *list_entry)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
|
|
|
|
struct udev_device *device;
|
|
|
|
|
const char *name = NULL;
|
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
|
|
name = udev_list_entry_get_name(list_entry);
|
|
|
|
|
|
|
|
|
|
device = udev_device_new_from_syspath(udev, name);
|
2010-05-28 22:22:05 -04:00
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
if (device != NULL) {
|
|
|
|
|
if (udevAddOneDevice(device) != 0) {
|
2011-02-09 16:41:30 +00:00
|
|
|
VIR_DEBUG("Failed to create node device for udev device '%s'",
|
|
|
|
|
name);
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
ret = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-28 22:22:05 -04:00
|
|
|
udev_device_unref(device);
|
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-06-02 18:09:56 +02:00
|
|
|
/* We do not care about every device (see udevGetDeviceType).
|
|
|
|
|
* Do not bother enumerating over subsystems that do not
|
|
|
|
|
* contain interesting devices.
|
|
|
|
|
*/
|
2020-06-16 12:16:55 +01:00
|
|
|
const char *subsystem_ignored[] = {
|
2016-06-02 18:09:56 +02:00
|
|
|
"acpi", "tty", "vc", "i2c",
|
|
|
|
|
};
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevEnumerateAddMatches(struct udev_enumerate *udev_enumerate)
|
2016-06-02 18:09:56 +02:00
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
|
2020-06-16 12:16:55 +01:00
|
|
|
for (i = 0; i < G_N_ELEMENTS(subsystem_ignored); i++) {
|
|
|
|
|
const char *s = subsystem_ignored[i];
|
2016-06-02 18:09:56 +02:00
|
|
|
if (udev_enumerate_add_nomatch_subsystem(udev_enumerate, s) < 0) {
|
2020-07-09 12:42:21 +08:00
|
|
|
virReportSystemError(errno, "%s", _("failed to add subsystem filter"));
|
2016-06-02 18:09:56 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevEnumerateDevices(struct udev *udev)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
|
|
|
|
struct udev_enumerate *udev_enumerate = NULL;
|
|
|
|
|
struct udev_list_entry *list_entry = NULL;
|
2016-06-02 18:09:56 +02:00
|
|
|
int ret = -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
|
|
|
|
udev_enumerate = udev_enumerate_new(udev);
|
2016-06-02 18:09:56 +02:00
|
|
|
if (udevEnumerateAddMatches(udev_enumerate) < 0)
|
|
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2019-02-14 10:01:01 +01:00
|
|
|
if (udev_enumerate_scan_devices(udev_enumerate) < 0)
|
|
|
|
|
VIR_WARN("udev scan devices failed");
|
2009-11-12 22:48:24 +01:00
|
|
|
|
|
|
|
|
udev_list_entry_foreach(list_entry,
|
|
|
|
|
udev_enumerate_get_list_entry(udev_enumerate)) {
|
|
|
|
|
|
|
|
|
|
udevProcessDeviceListEntry(udev, list_entry);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-14 10:01:01 +01:00
|
|
|
ret = 0;
|
2016-06-03 19:40:45 +02:00
|
|
|
cleanup:
|
2009-11-12 22:48:24 +01:00
|
|
|
udev_enumerate_unref(udev_enumerate);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static void
|
|
|
|
|
udevPCITranslateDeinit(void)
|
2016-06-03 11:03:41 +02:00
|
|
|
{
|
|
|
|
|
#if defined __s390__ || defined __s390x_
|
|
|
|
|
/* Nothing was initialized, nothing needs to be cleaned up */
|
|
|
|
|
#else
|
|
|
|
|
/* pci_system_cleanup returns void */
|
|
|
|
|
pci_system_cleanup();
|
|
|
|
|
#endif
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
nodeStateCleanup(void)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
udevEventData *priv = NULL;
|
2017-06-28 14:34:41 +02:00
|
|
|
|
2016-06-03 12:38:40 +02:00
|
|
|
if (!driver)
|
|
|
|
|
return -1;
|
2010-01-26 02:58:37 +01:00
|
|
|
|
2017-06-28 14:34:41 +02:00
|
|
|
priv = driver->privateData;
|
|
|
|
|
if (priv) {
|
|
|
|
|
virObjectLock(priv);
|
|
|
|
|
priv->threadQuit = true;
|
|
|
|
|
virCondSignal(&priv->threadCond);
|
|
|
|
|
virObjectUnlock(priv);
|
2021-04-12 16:25:13 +02:00
|
|
|
if (priv->initThread) {
|
|
|
|
|
virThreadJoin(priv->initThread);
|
|
|
|
|
g_clear_pointer(&priv->initThread, g_free);
|
|
|
|
|
}
|
|
|
|
|
if (priv->th) {
|
|
|
|
|
virThreadJoin(priv->th);
|
|
|
|
|
g_clear_pointer(&priv->th, g_free);
|
|
|
|
|
}
|
2017-06-28 14:34:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virObjectUnref(priv);
|
2016-10-11 09:48:36 +02:00
|
|
|
virObjectUnref(driver->nodeDeviceEventState);
|
2016-07-28 14:02:55 +02:00
|
|
|
|
2017-05-12 13:51:25 -04:00
|
|
|
virNodeDeviceObjListFree(driver->devs);
|
2019-05-23 11:34:08 +01:00
|
|
|
|
|
|
|
|
if (driver->lockFD != -1)
|
|
|
|
|
virPidFileRelease(driver->stateDir, "driver", driver->lockFD);
|
|
|
|
|
|
|
|
|
|
VIR_FREE(driver->stateDir);
|
2020-03-13 11:53:25 +00:00
|
|
|
virCondDestroy(&driver->initCond);
|
2016-06-03 12:38:40 +02:00
|
|
|
virMutexDestroy(&driver->lock);
|
|
|
|
|
VIR_FREE(driver);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 12:38:40 +02:00
|
|
|
udevPCITranslateDeinit();
|
|
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-07-25 17:41:31 +02:00
|
|
|
static int
|
|
|
|
|
udevHandleOneDevice(struct udev_device *device)
|
|
|
|
|
{
|
|
|
|
|
const char *action = udev_device_get_action(device);
|
|
|
|
|
|
2020-11-17 11:54:54 +00:00
|
|
|
VIR_DEBUG("udev action: '%s': %s", action, udev_device_get_syspath(device));
|
2017-07-25 17:41:31 +02:00
|
|
|
|
|
|
|
|
if (STREQ(action, "add") || STREQ(action, "change"))
|
|
|
|
|
return udevAddOneDevice(device);
|
|
|
|
|
|
|
|
|
|
if (STREQ(action, "remove"))
|
|
|
|
|
return udevRemoveOneDevice(device);
|
|
|
|
|
|
2020-04-16 11:57:45 -04:00
|
|
|
if (STREQ(action, "move")) {
|
2020-04-20 16:12:03 +02:00
|
|
|
const char *devpath_old = udevGetDeviceProperty(device, "DEVPATH_OLD");
|
|
|
|
|
|
|
|
|
|
if (devpath_old) {
|
|
|
|
|
g_autofree char *devpath_old_fixed = g_strdup_printf("/sys%s", devpath_old);
|
|
|
|
|
|
|
|
|
|
udevRemoveOneDeviceSysPath(devpath_old_fixed);
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-16 11:57:45 -04:00
|
|
|
return udevAddOneDevice(device);
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-25 17:41:31 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-10-06 15:21:55 +02:00
|
|
|
/* the caller must be holding the udevEventData object lock prior to calling
|
|
|
|
|
* this function
|
|
|
|
|
*/
|
2017-07-26 15:16:09 +02:00
|
|
|
static bool
|
2021-03-11 08:16:13 +01:00
|
|
|
udevEventMonitorSanityCheck(udevEventData *priv,
|
2017-07-26 15:16:09 +02:00
|
|
|
int fd)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2017-07-26 15:16:09 +02:00
|
|
|
int rc = -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-10-06 15:21:55 +02:00
|
|
|
rc = udev_monitor_get_fd(priv->udev_monitor);
|
2017-07-26 15:16:09 +02:00
|
|
|
if (fd != rc) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
|
_("File descriptor returned by udev %d does not "
|
|
|
|
|
"match node device file descriptor %d"),
|
2017-07-26 15:16:09 +02:00
|
|
|
fd, rc);
|
2017-06-28 15:39:51 +02:00
|
|
|
|
|
|
|
|
/* this is a non-recoverable error, let's remove the handle, so that we
|
|
|
|
|
* don't get in here again because of some spurious behaviour and report
|
|
|
|
|
* the same error multiple times
|
|
|
|
|
*/
|
|
|
|
|
virEventRemoveHandle(priv->watch);
|
|
|
|
|
priv->watch = -1;
|
|
|
|
|
|
2017-07-26 15:16:09 +02:00
|
|
|
return false;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
2017-07-26 15:16:09 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-10-18 10:22:18 -04:00
|
|
|
/**
|
|
|
|
|
* udevEventHandleThread
|
|
|
|
|
* @opaque: unused
|
|
|
|
|
*
|
|
|
|
|
* Thread to handle the udevEventHandleCallback processing when udev
|
|
|
|
|
* tells us there's a device change for us (add, modify, delete, etc).
|
|
|
|
|
*
|
|
|
|
|
* Once notified there is data to be processed, the actual @device
|
|
|
|
|
* data retrieval by libudev may be delayed due to how threads are
|
|
|
|
|
* scheduled. In fact, the event loop could be scheduled earlier than
|
|
|
|
|
* the handler thread, thus potentially emitting the very same event
|
|
|
|
|
* the handler thread is currently trying to process, simply because
|
|
|
|
|
* the data hadn't been retrieved from the socket.
|
|
|
|
|
*
|
|
|
|
|
* NB: Some older distros, such as CentOS 6, libudev opens sockets
|
|
|
|
|
* without the NONBLOCK flag which might cause issues with event
|
|
|
|
|
* based algorithm. Although the issue can be mitigated by resetting
|
|
|
|
|
* priv->dataReady for each event found; however, the scheduler issues
|
|
|
|
|
* would still come into play.
|
|
|
|
|
*/
|
2017-07-26 15:16:09 +02:00
|
|
|
static void
|
2019-10-14 14:45:33 +02:00
|
|
|
udevEventHandleThread(void *opaque G_GNUC_UNUSED)
|
2017-07-26 15:16:09 +02:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
udevEventData *priv = driver->privateData;
|
2017-07-26 15:16:09 +02:00
|
|
|
struct udev_device *device = NULL;
|
|
|
|
|
|
2017-06-28 14:34:41 +02:00
|
|
|
/* continue rather than break from the loop on non-fatal errors */
|
|
|
|
|
while (1) {
|
|
|
|
|
virObjectLock(priv);
|
|
|
|
|
while (!priv->dataReady && !priv->threadQuit) {
|
|
|
|
|
if (virCondWait(&priv->threadCond, &priv->parent.lock)) {
|
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
|
_("handler failed to wait on condition"));
|
|
|
|
|
virObjectUnlock(priv);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (priv->threadQuit) {
|
|
|
|
|
virObjectUnlock(priv);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-07-26 15:16:09 +02:00
|
|
|
|
2017-06-28 14:34:41 +02:00
|
|
|
errno = 0;
|
|
|
|
|
device = udev_monitor_receive_device(priv->udev_monitor);
|
2017-10-06 15:21:55 +02:00
|
|
|
virObjectUnlock(priv);
|
|
|
|
|
|
2017-06-28 14:34:41 +02:00
|
|
|
if (!device) {
|
|
|
|
|
if (errno == 0) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
_("failed to receive device from udev monitor"));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-07-26 15:16:09 +02:00
|
|
|
|
2017-06-28 14:34:41 +02:00
|
|
|
/* POSIX allows both EAGAIN and EWOULDBLOCK to be used
|
|
|
|
|
* interchangeably when the read would block or timeout was fired
|
|
|
|
|
*/
|
|
|
|
|
VIR_WARNINGS_NO_WLOGICALOP_EQUAL_EXPR
|
|
|
|
|
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
|
|
|
|
VIR_WARNINGS_RESET
|
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
|
_("failed to receive device from udev "
|
|
|
|
|
"monitor"));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2018-10-18 10:22:18 -04:00
|
|
|
/* Trying to move the reset of the @priv->dataReady flag to
|
|
|
|
|
* after the udev_monitor_receive_device wouldn't help much
|
|
|
|
|
* due to event mgmt and scheduler timing. */
|
2017-06-28 14:34:41 +02:00
|
|
|
virObjectLock(priv);
|
|
|
|
|
priv->dataReady = false;
|
|
|
|
|
virObjectUnlock(priv);
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
udevHandleOneDevice(device);
|
|
|
|
|
udev_device_unref(device);
|
2018-10-18 10:22:18 -04:00
|
|
|
|
|
|
|
|
/* Instead of waiting for the next event after processing @device
|
|
|
|
|
* data, let's keep reading from the udev monitor and only wait
|
|
|
|
|
* for the next event once either a EAGAIN or a EWOULDBLOCK error
|
|
|
|
|
* is encountered. */
|
2017-06-28 14:34:41 +02:00
|
|
|
}
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-06-28 14:20:37 +02:00
|
|
|
static void
|
2019-10-14 14:45:33 +02:00
|
|
|
udevEventHandleCallback(int watch G_GNUC_UNUSED,
|
2017-06-28 14:20:37 +02:00
|
|
|
int fd,
|
2019-10-14 14:45:33 +02:00
|
|
|
int events G_GNUC_UNUSED,
|
|
|
|
|
void *data G_GNUC_UNUSED)
|
2017-06-28 14:20:37 +02:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
udevEventData *priv = driver->privateData;
|
2017-06-28 14:20:37 +02:00
|
|
|
|
|
|
|
|
virObjectLock(priv);
|
|
|
|
|
|
2017-06-28 14:34:41 +02:00
|
|
|
if (!udevEventMonitorSanityCheck(priv, fd))
|
|
|
|
|
priv->threadQuit = true;
|
|
|
|
|
else
|
|
|
|
|
priv->dataReady = true;
|
|
|
|
|
|
|
|
|
|
virCondSignal(&priv->threadCond);
|
|
|
|
|
virObjectUnlock(priv);
|
2017-06-28 14:20:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-07-19 16:48:49 +02:00
|
|
|
/* DMI is intel-compatible specific */
|
|
|
|
|
#if defined(__x86_64__) || defined(__i386__) || defined(__amd64__)
|
2010-03-04 13:17:24 -05:00
|
|
|
static void
|
2021-03-11 08:16:13 +01:00
|
|
|
udevGetDMIData(virNodeDevCapSystem *syscap)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
udevEventData *priv = driver->privateData;
|
2009-11-12 22:48:24 +01:00
|
|
|
struct udev *udev = NULL;
|
|
|
|
|
struct udev_device *device = NULL;
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDevCapSystemHardware *hardware = &syscap->hardware;
|
|
|
|
|
virNodeDevCapSystemFirmware *firmware = &syscap->firmware;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-10-18 14:58:51 +02:00
|
|
|
virObjectLock(priv);
|
2017-10-06 15:21:55 +02:00
|
|
|
udev = udev_monitor_get_udev(priv->udev_monitor);
|
2010-03-04 13:17:24 -05:00
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
device = udev_device_new_from_syspath(udev, DMI_DEVPATH);
|
|
|
|
|
if (device == NULL) {
|
2020-09-30 18:44:53 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
|
_("Failed to get udev device for syspath '%s'"),
|
|
|
|
|
DMI_DEVPATH);
|
|
|
|
|
virObjectUnlock(priv);
|
|
|
|
|
return;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
2017-10-18 14:58:51 +02:00
|
|
|
virObjectUnlock(priv);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:27:48 +02:00
|
|
|
if (udevGetStringSysfsAttr(device, "product_name",
|
2017-03-04 10:42:33 -05:00
|
|
|
&syscap->product_name) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 17:27:48 +02:00
|
|
|
if (udevGetStringSysfsAttr(device, "sys_vendor",
|
2017-03-02 10:59:25 -05:00
|
|
|
&hardware->vendor_name) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 17:27:48 +02:00
|
|
|
if (udevGetStringSysfsAttr(device, "product_version",
|
2017-03-02 10:59:25 -05:00
|
|
|
&hardware->version) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 17:27:48 +02:00
|
|
|
if (udevGetStringSysfsAttr(device, "product_serial",
|
2017-03-02 10:59:25 -05:00
|
|
|
&hardware->serial) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (virGetHostUUID(hardware->uuid))
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:27:48 +02:00
|
|
|
if (udevGetStringSysfsAttr(device, "bios_vendor",
|
2017-03-02 10:59:25 -05:00
|
|
|
&firmware->vendor_name) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 17:27:48 +02:00
|
|
|
if (udevGetStringSysfsAttr(device, "bios_version",
|
2017-03-02 10:59:25 -05:00
|
|
|
&firmware->version) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 17:27:48 +02:00
|
|
|
if (udevGetStringSysfsAttr(device, "bios_date",
|
2017-03-02 10:59:25 -05:00
|
|
|
&firmware->release_date) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:40:45 +02:00
|
|
|
cleanup:
|
2014-11-13 15:24:17 +01:00
|
|
|
if (device != NULL)
|
2010-03-04 13:17:24 -05:00
|
|
|
udev_device_unref(device);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-07-19 16:48:49 +02:00
|
|
|
#endif
|
2010-03-04 13:17:24 -05:00
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
udevSetupSystemDev(void)
|
2010-03-04 13:17:24 -05:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
virNodeDeviceDef *def = NULL;
|
|
|
|
|
virNodeDeviceObj *obj = NULL;
|
2010-03-04 13:17:24 -05:00
|
|
|
int ret = -1;
|
|
|
|
|
|
2020-09-23 22:04:41 +02:00
|
|
|
def = g_new0(virNodeDeviceDef, 1);
|
2010-03-04 13:17:24 -05:00
|
|
|
|
2019-10-20 13:49:46 +02:00
|
|
|
def->name = g_strdup("computer");
|
2020-09-23 22:04:41 +02:00
|
|
|
def->caps = g_new0(virNodeDevCapsDef, 1);
|
2010-03-04 13:17:24 -05:00
|
|
|
|
2011-07-19 16:48:49 +02:00
|
|
|
#if defined(__x86_64__) || defined(__i386__) || defined(__amd64__)
|
2017-03-02 10:59:25 -05:00
|
|
|
udevGetDMIData(&def->caps->data.system);
|
2011-07-19 16:48:49 +02:00
|
|
|
#endif
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-05-12 14:47:17 -04:00
|
|
|
if (!(obj = virNodeDeviceObjListAssignDef(driver->devs, def)))
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2020-07-09 10:39:34 -05:00
|
|
|
virNodeDeviceObjSetActive(obj, true);
|
|
|
|
|
|
2017-05-15 11:00:59 -04:00
|
|
|
virNodeDeviceObjEndAPI(&obj);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
2016-06-03 19:40:45 +02:00
|
|
|
cleanup:
|
2014-11-13 15:24:17 +01:00
|
|
|
if (ret == -1)
|
2010-03-04 13:17:24 -05:00
|
|
|
virNodeDeviceDefFree(def);
|
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
2017-11-22 11:55:10 -05:00
|
|
|
static void
|
|
|
|
|
nodeStateInitializeEnumerate(void *opaque)
|
|
|
|
|
{
|
|
|
|
|
struct udev *udev = opaque;
|
2021-03-11 08:16:13 +01:00
|
|
|
udevEventData *priv = driver->privateData;
|
2017-11-22 11:55:10 -05:00
|
|
|
|
|
|
|
|
/* Populate with known devices */
|
|
|
|
|
if (udevEnumerateDevices(udev) != 0)
|
|
|
|
|
goto error;
|
2020-06-30 17:02:48 -05:00
|
|
|
/* Load persistent mdevs (which might not be activated yet) and additional
|
|
|
|
|
* information about active mediated devices from mdevctl */
|
|
|
|
|
if (nodeDeviceUpdateMediatedDevices() != 0)
|
|
|
|
|
goto error;
|
2017-11-22 11:55:10 -05:00
|
|
|
|
2021-04-13 10:45:24 +02:00
|
|
|
cleanup:
|
2020-03-13 11:53:25 +00:00
|
|
|
nodeDeviceLock();
|
|
|
|
|
driver->initialized = true;
|
|
|
|
|
virCondBroadcast(&driver->initCond);
|
2021-04-13 10:52:07 +02:00
|
|
|
nodeDeviceUnlock();
|
2020-03-13 11:53:25 +00:00
|
|
|
|
2017-11-22 11:55:10 -05:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
virObjectLock(priv);
|
2019-02-20 11:05:45 +01:00
|
|
|
ignore_value(virEventRemoveHandle(priv->watch));
|
|
|
|
|
priv->watch = -1;
|
2017-11-22 11:55:10 -05:00
|
|
|
priv->threadQuit = true;
|
2019-02-20 11:05:46 +01:00
|
|
|
virCondSignal(&priv->threadCond);
|
2017-11-22 11:55:10 -05:00
|
|
|
virObjectUnlock(priv);
|
2021-04-13 10:45:24 +02:00
|
|
|
|
|
|
|
|
goto cleanup;
|
2017-11-22 11:55:10 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
2019-10-14 14:45:33 +02:00
|
|
|
udevPCITranslateInit(bool privileged G_GNUC_UNUSED)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2011-04-22 14:24:54 +02:00
|
|
|
#if defined __s390__ || defined __s390x_
|
|
|
|
|
/* On s390(x) system there is no PCI bus.
|
|
|
|
|
* Therefore there is nothing to initialize here. */
|
|
|
|
|
#else
|
2016-06-03 19:03:38 +02:00
|
|
|
int rc;
|
2011-02-07 17:04:35 +00:00
|
|
|
|
2016-06-03 19:03:38 +02:00
|
|
|
if ((rc = pci_system_init()) != 0) {
|
2011-03-16 15:47:32 -06:00
|
|
|
/* Ignore failure as non-root; udev is not as helpful in that
|
|
|
|
|
* situation, but a non-privileged user won't benefit much
|
|
|
|
|
* from udev in the first place. */
|
2011-09-05 21:29:06 +02:00
|
|
|
if (errno != ENOENT && (privileged || errno != EACCES)) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportSystemError(rc, "%s",
|
|
|
|
|
_("Failed to initialize libpciaccess"));
|
2016-06-03 11:03:41 +02:00
|
|
|
return -1;
|
2011-03-16 15:47:32 -06:00
|
|
|
}
|
2011-02-07 17:04:35 +00:00
|
|
|
}
|
2011-04-22 14:24:54 +02:00
|
|
|
#endif
|
2016-06-03 11:03:41 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
2020-07-09 16:16:57 -05:00
|
|
|
static void
|
|
|
|
|
mdevctlHandlerThread(void *opaque G_GNUC_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
udevEventData *priv = driver->privateData;
|
|
|
|
|
|
|
|
|
|
/* ensure only a single thread can query mdevctl at a time */
|
|
|
|
|
virMutexLock(&priv->mdevctlLock);
|
|
|
|
|
if (nodeDeviceUpdateMediatedDevices() < 0)
|
|
|
|
|
VIR_WARN("mdevctl failed to updated mediated devices");
|
|
|
|
|
virMutexUnlock(&priv->mdevctlLock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
scheduleMdevctlHandler(int timer G_GNUC_UNUSED, void *opaque)
|
|
|
|
|
{
|
|
|
|
|
udevEventData *priv = opaque;
|
|
|
|
|
virThread thread;
|
|
|
|
|
|
|
|
|
|
if (priv->mdevctlTimeout > 0) {
|
|
|
|
|
virEventRemoveTimeout(priv->mdevctlTimeout);
|
|
|
|
|
priv->mdevctlTimeout = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (virThreadCreateFull(&thread, false, mdevctlHandlerThread,
|
|
|
|
|
"mdevctl-thread", false, NULL) < 0) {
|
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
|
_("failed to create mdevctl thread"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
mdevctlEventHandleCallback(GFileMonitor *monitor G_GNUC_UNUSED,
|
|
|
|
|
GFile *file,
|
|
|
|
|
GFile *other_file G_GNUC_UNUSED,
|
|
|
|
|
GFileMonitorEvent event_type,
|
|
|
|
|
gpointer user_data);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Recursively monitors a directory and its subdirectories for file changes and
|
|
|
|
|
* returns a GList of GFileMonitor objects */
|
|
|
|
|
static GList*
|
|
|
|
|
monitorFileRecursively(udevEventData *udev,
|
|
|
|
|
GFile *file)
|
|
|
|
|
{
|
|
|
|
|
GList *monitors = NULL;
|
|
|
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
|
g_autoptr(GFileEnumerator) children = NULL;
|
|
|
|
|
GFileMonitor *mon;
|
|
|
|
|
|
|
|
|
|
if (!(children = g_file_enumerate_children(file, "standard::*",
|
|
|
|
|
G_FILE_QUERY_INFO_NONE, NULL, &error)))
|
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
if (!(mon = g_file_monitor(file, G_FILE_MONITOR_NONE, NULL, &error)))
|
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
g_signal_connect(mon, "changed",
|
|
|
|
|
G_CALLBACK(mdevctlEventHandleCallback), udev);
|
|
|
|
|
|
|
|
|
|
monitors = g_list_append(monitors, mon);
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
GFileInfo *info = NULL;
|
|
|
|
|
GFile *child = NULL;
|
|
|
|
|
GList *child_monitors = NULL;
|
|
|
|
|
|
|
|
|
|
if (!g_file_enumerator_iterate(children, &info, &child, NULL, &error))
|
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
if (!info)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (g_file_query_file_type(child, G_FILE_QUERY_INFO_NONE, NULL) ==
|
|
|
|
|
G_FILE_TYPE_DIRECTORY) {
|
|
|
|
|
|
|
|
|
|
child_monitors = monitorFileRecursively(udev, child);
|
|
|
|
|
if (child_monitors)
|
|
|
|
|
monitors = g_list_concat(monitors, child_monitors);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return monitors;
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
g_list_free_full(monitors, g_object_unref);
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
|
_("Unable to monitor directory: %s"), error->message);
|
|
|
|
|
g_clear_error(&error);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-04-12 17:15:25 +02:00
|
|
|
static int
|
|
|
|
|
mdevctlEnableMonitor(udevEventData *priv)
|
|
|
|
|
{
|
2021-04-12 17:26:06 +02:00
|
|
|
g_autoptr(GFile) mdevctlConfigDir = NULL;
|
|
|
|
|
const char *mdevctlDir = "/etc/mdevctl.d";
|
|
|
|
|
|
|
|
|
|
if (!virFileExists(mdevctlDir))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
mdevctlConfigDir = g_file_new_for_path(mdevctlDir);
|
2021-04-12 17:15:25 +02:00
|
|
|
|
|
|
|
|
/* mdevctl may add notification events in the future:
|
|
|
|
|
* https://github.com/mdevctl/mdevctl/issues/27. For now, fall back to
|
|
|
|
|
* monitoring the mdevctl configuration directory for changes.
|
|
|
|
|
* mdevctl configuration is stored in a directory tree within
|
|
|
|
|
* /etc/mdevctl.d/. There is a directory for each parent device, which
|
|
|
|
|
* contains a file defining each mediated device */
|
|
|
|
|
virMutexLock(&priv->mdevctlLock);
|
|
|
|
|
if (!(priv->mdevctlMonitors = monitorFileRecursively(priv,
|
|
|
|
|
mdevctlConfigDir))) {
|
|
|
|
|
virMutexUnlock(&priv->mdevctlLock);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
virMutexUnlock(&priv->mdevctlLock);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-07-09 16:16:57 -05:00
|
|
|
static void
|
|
|
|
|
mdevctlEventHandleCallback(GFileMonitor *monitor G_GNUC_UNUSED,
|
|
|
|
|
GFile *file,
|
|
|
|
|
GFile *other_file G_GNUC_UNUSED,
|
|
|
|
|
GFileMonitorEvent event_type,
|
|
|
|
|
gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
udevEventData *priv = user_data;
|
|
|
|
|
/* if a new directory appears, monitor that directory for changes */
|
|
|
|
|
if (event_type == G_FILE_MONITOR_EVENT_CREATED) {
|
|
|
|
|
GFileType file_type = g_file_query_file_type(file,
|
|
|
|
|
G_FILE_QUERY_INFO_NONE,
|
|
|
|
|
NULL);
|
|
|
|
|
if (file_type == G_FILE_TYPE_DIRECTORY) {
|
|
|
|
|
GList *newmonitors = monitorFileRecursively(priv, file);
|
|
|
|
|
|
|
|
|
|
virMutexLock(&priv->mdevctlLock);
|
|
|
|
|
priv->mdevctlMonitors = g_list_concat(priv->mdevctlMonitors, newmonitors);
|
|
|
|
|
virMutexUnlock(&priv->mdevctlLock);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* When mdevctl creates a device, it can result in multiple notify events
|
|
|
|
|
* emitted for a single logical change (e.g. several CHANGED events, or a
|
|
|
|
|
* CREATED and CHANGED event followed by CHANGES_DONE_HINT). To avoid
|
|
|
|
|
* spawning a mdevctl thread multiple times for a single logical
|
|
|
|
|
* configuration change, try to coalesce these changes by waiting for the
|
|
|
|
|
* CHANGES_DONE_HINT event. As a fallback, add a timeout to trigger the
|
|
|
|
|
* signal if that event never comes */
|
|
|
|
|
if (event_type != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT) {
|
|
|
|
|
if (priv->mdevctlTimeout > 0)
|
|
|
|
|
virEventRemoveTimeout(priv->mdevctlTimeout);
|
|
|
|
|
priv->mdevctlTimeout = virEventAddTimeout(100, scheduleMdevctlHandler,
|
|
|
|
|
priv, NULL);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scheduleMdevctlHandler(-1, priv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
nodeStateInitialize(bool privileged,
|
2019-05-17 12:30:45 +01:00
|
|
|
const char *root,
|
2019-10-14 14:45:33 +02:00
|
|
|
virStateInhibitCallback callback G_GNUC_UNUSED,
|
|
|
|
|
void *opaque G_GNUC_UNUSED)
|
2016-06-03 11:03:41 +02:00
|
|
|
{
|
2021-03-11 08:16:13 +01:00
|
|
|
udevEventData *priv = NULL;
|
2016-06-03 11:03:41 +02:00
|
|
|
struct udev *udev = NULL;
|
|
|
|
|
|
2019-05-17 12:30:45 +01:00
|
|
|
if (root != NULL) {
|
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
|
|
|
_("Driver does not support embedded mode"));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-23 22:04:41 +02:00
|
|
|
driver = g_new0(virNodeDeviceDriverState, 1);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2019-05-23 11:34:08 +01:00
|
|
|
driver->lockFD = -1;
|
2014-11-17 16:30:27 +00:00
|
|
|
if (virMutexInit(&driver->lock) < 0) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
_("Unable to initialize mutex"));
|
2014-11-17 16:30:27 +00:00
|
|
|
VIR_FREE(driver);
|
2019-07-23 13:05:18 +01:00
|
|
|
return VIR_DRV_STATE_INIT_ERROR;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
2020-03-13 11:53:25 +00:00
|
|
|
if (virCondInit(&driver->initCond) < 0) {
|
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
|
_("Unable to initialize condition variable"));
|
|
|
|
|
virMutexDestroy(&driver->lock);
|
|
|
|
|
VIR_FREE(driver);
|
|
|
|
|
return VIR_DRV_STATE_INIT_ERROR;
|
|
|
|
|
}
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-11-22 10:34:11 -05:00
|
|
|
driver->privileged = privileged;
|
|
|
|
|
|
2019-05-23 11:34:08 +01:00
|
|
|
if (privileged) {
|
2019-10-22 15:26:14 +02:00
|
|
|
driver->stateDir = g_strdup_printf("%s/libvirt/nodedev", RUNSTATEDIR);
|
2019-05-23 11:34:08 +01:00
|
|
|
} else {
|
2019-10-15 15:16:31 +02:00
|
|
|
g_autofree char *rundir = NULL;
|
2019-05-23 11:34:08 +01:00
|
|
|
|
2019-12-19 10:23:14 +01:00
|
|
|
rundir = virGetUserRuntimeDirectory();
|
2019-10-22 15:26:14 +02:00
|
|
|
driver->stateDir = g_strdup_printf("%s/nodedev/run", rundir);
|
2019-05-23 11:34:08 +01:00
|
|
|
}
|
|
|
|
|
|
2021-02-26 09:17:30 +01:00
|
|
|
if (g_mkdir_with_parents(driver->stateDir, S_IRWXU) < 0) {
|
2019-05-23 11:34:08 +01:00
|
|
|
virReportSystemError(errno, _("cannot create state directory '%s'"),
|
|
|
|
|
driver->stateDir);
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((driver->lockFD =
|
2019-07-11 15:57:34 +01:00
|
|
|
virPidFileAcquire(driver->stateDir, "driver", false, getpid())) < 0)
|
2019-05-23 11:34:08 +01:00
|
|
|
goto cleanup;
|
|
|
|
|
|
2017-10-06 15:21:55 +02:00
|
|
|
if (!(driver->devs = virNodeDeviceObjListNew()) ||
|
|
|
|
|
!(priv = udevEventDataNew()))
|
2017-10-18 12:51:40 +02:00
|
|
|
goto cleanup;
|
2017-05-12 13:51:25 -04:00
|
|
|
|
2021-04-12 16:20:57 +02:00
|
|
|
virObjectLock(priv);
|
|
|
|
|
|
2017-10-06 15:21:55 +02:00
|
|
|
driver->privateData = priv;
|
2016-07-28 14:02:55 +02:00
|
|
|
driver->nodeDeviceEventState = virObjectEventStateNew();
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 12:57:41 +02:00
|
|
|
if (udevPCITranslateInit(privileged) < 0)
|
2021-04-12 16:20:57 +02:00
|
|
|
goto unlock;
|
2016-06-03 12:20:41 +02:00
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
udev = udev_new();
|
2017-02-10 10:44:44 +01:00
|
|
|
if (!udev) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
_("failed to create udev context"));
|
2021-04-12 16:20:57 +02:00
|
|
|
goto unlock;
|
2017-02-10 10:44:44 +01:00
|
|
|
}
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2020-11-17 12:56:39 +01:00
|
|
|
priv->udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
|
2017-10-06 15:21:55 +02:00
|
|
|
if (!priv->udev_monitor) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
_("udev_monitor_new_from_netlink returned NULL"));
|
2017-07-26 10:45:11 +02:00
|
|
|
goto unlock;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
2010-01-26 02:58:37 +01:00
|
|
|
udev_monitor_enable_receiving(priv->udev_monitor);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-05-09 10:09:07 +08:00
|
|
|
/* mimic udevd's behaviour and override the systems rmem_max limit in case
|
|
|
|
|
* there's a significant number of device 'add' events
|
|
|
|
|
*/
|
|
|
|
|
if (geteuid() == 0)
|
|
|
|
|
udev_monitor_set_receive_buffer_size(priv->udev_monitor,
|
|
|
|
|
128 * 1024 * 1024);
|
|
|
|
|
|
2021-04-12 16:25:13 +02:00
|
|
|
priv->th = g_new0(virThread, 1);
|
|
|
|
|
if (virThreadCreateFull(priv->th, true, udevEventHandleThread,
|
2020-02-14 11:20:10 +00:00
|
|
|
"udev-event", false, NULL) < 0) {
|
2017-06-28 14:34:41 +02:00
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
|
_("failed to create udev handler thread"));
|
2021-04-12 16:25:13 +02:00
|
|
|
g_clear_pointer(&priv->th, g_free);
|
2017-06-28 14:34:41 +02:00
|
|
|
goto unlock;
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
/* We register the monitor with the event callback so we are
|
|
|
|
|
* notified by udev of device changes before we enumerate existing
|
|
|
|
|
* devices because libvirt will simply recreate the device if we
|
|
|
|
|
* try to register it twice, i.e., if the device appears between
|
|
|
|
|
* the time we register the callback and the time we begin
|
|
|
|
|
* enumeration. The alternative is to register the callback after
|
|
|
|
|
* we enumerate, in which case we will fail to create any devices
|
|
|
|
|
* that appear while the enumeration is taking place. */
|
2010-01-26 02:58:37 +01:00
|
|
|
priv->watch = virEventAddHandle(udev_monitor_get_fd(priv->udev_monitor),
|
|
|
|
|
VIR_EVENT_HANDLE_READABLE,
|
|
|
|
|
udevEventHandleCallback, NULL, NULL);
|
2016-06-03 10:21:23 +02:00
|
|
|
if (priv->watch == -1)
|
2017-07-26 10:45:11 +02:00
|
|
|
goto unlock;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2021-04-12 17:15:25 +02:00
|
|
|
if (mdevctlEnableMonitor(priv) < 0)
|
2021-04-12 16:18:24 +02:00
|
|
|
goto unlock;
|
2020-07-09 16:16:57 -05:00
|
|
|
|
2017-10-18 14:58:51 +02:00
|
|
|
virObjectUnlock(priv);
|
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
/* Create a fictional 'computer' device to root the device tree. */
|
2016-06-03 10:21:23 +02:00
|
|
|
if (udevSetupSystemDev() != 0)
|
2017-10-18 14:58:51 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2021-04-12 16:25:13 +02:00
|
|
|
priv->initThread = g_new0(virThread, 1);
|
|
|
|
|
if (virThreadCreateFull(priv->initThread, true, nodeStateInitializeEnumerate,
|
2020-02-14 11:20:10 +00:00
|
|
|
"nodedev-init", false, udev) < 0) {
|
2017-11-22 11:55:10 -05:00
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
|
_("failed to create udev enumerate thread"));
|
2021-04-12 16:25:13 +02:00
|
|
|
g_clear_pointer(&priv->initThread, g_free);
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2017-11-22 11:55:10 -05:00
|
|
|
}
|
2016-06-03 10:21:23 +02:00
|
|
|
|
2019-07-23 13:05:18 +01:00
|
|
|
return VIR_DRV_STATE_INIT_COMPLETE;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:40:45 +02:00
|
|
|
cleanup:
|
2017-06-29 16:51:54 -04:00
|
|
|
nodeStateCleanup();
|
2019-07-23 13:05:18 +01:00
|
|
|
return VIR_DRV_STATE_INIT_ERROR;
|
2017-07-26 10:45:11 +02:00
|
|
|
|
|
|
|
|
unlock:
|
2017-10-18 12:51:40 +02:00
|
|
|
virObjectUnlock(priv);
|
2017-07-26 10:45:11 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
|
nodeStateReload(void)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-04-23 11:15:48 +01:00
|
|
|
static virNodeDeviceDriver udevNodeDeviceDriver = {
|
2015-01-20 16:16:26 +00:00
|
|
|
.name = "udev",
|
2013-04-22 18:26:01 +01:00
|
|
|
.nodeNumOfDevices = nodeNumOfDevices, /* 0.7.3 */
|
|
|
|
|
.nodeListDevices = nodeListDevices, /* 0.7.3 */
|
2013-04-29 10:20:17 +01:00
|
|
|
.connectListAllNodeDevices = nodeConnectListAllNodeDevices, /* 0.10.2 */
|
2016-07-28 14:02:55 +02:00
|
|
|
.connectNodeDeviceEventRegisterAny = nodeConnectNodeDeviceEventRegisterAny, /* 2.2.0 */
|
|
|
|
|
.connectNodeDeviceEventDeregisterAny = nodeConnectNodeDeviceEventDeregisterAny, /* 2.2.0 */
|
2013-04-22 18:26:01 +01:00
|
|
|
.nodeDeviceLookupByName = nodeDeviceLookupByName, /* 0.7.3 */
|
|
|
|
|
.nodeDeviceLookupSCSIHostByWWN = nodeDeviceLookupSCSIHostByWWN, /* 1.0.2 */
|
|
|
|
|
.nodeDeviceGetXMLDesc = nodeDeviceGetXMLDesc, /* 0.7.3 */
|
|
|
|
|
.nodeDeviceGetParent = nodeDeviceGetParent, /* 0.7.3 */
|
|
|
|
|
.nodeDeviceNumOfCaps = nodeDeviceNumOfCaps, /* 0.7.3 */
|
|
|
|
|
.nodeDeviceListCaps = nodeDeviceListCaps, /* 0.7.3 */
|
|
|
|
|
.nodeDeviceCreateXML = nodeDeviceCreateXML, /* 0.7.3 */
|
|
|
|
|
.nodeDeviceDestroy = nodeDeviceDestroy, /* 0.7.3 */
|
2021-04-09 10:38:41 -05:00
|
|
|
.nodeDeviceDefineXML = nodeDeviceDefineXML, /* 7.3.0 */
|
|
|
|
|
.nodeDeviceUndefine = nodeDeviceUndefine, /* 7.3.0 */
|
|
|
|
|
.nodeDeviceCreate = nodeDeviceCreate, /* 7.3.0 */
|
2009-11-12 22:48:24 +01:00
|
|
|
};
|
|
|
|
|
|
2018-01-26 11:16:00 +00:00
|
|
|
|
|
|
|
|
static virHypervisorDriver udevHypervisorDriver = {
|
|
|
|
|
.name = "nodedev",
|
|
|
|
|
.connectOpen = nodeConnectOpen, /* 4.1.0 */
|
|
|
|
|
.connectClose = nodeConnectClose, /* 4.1.0 */
|
|
|
|
|
.connectIsEncrypted = nodeConnectIsEncrypted, /* 4.1.0 */
|
|
|
|
|
.connectIsSecure = nodeConnectIsSecure, /* 4.1.0 */
|
|
|
|
|
.connectIsAlive = nodeConnectIsAlive, /* 4.1.0 */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static virConnectDriver udevConnectDriver = {
|
2018-03-28 10:53:31 +01:00
|
|
|
.localOnly = true,
|
2018-03-27 15:51:45 +01:00
|
|
|
.uriSchemes = (const char *[]){ "nodedev", NULL },
|
2018-01-26 11:16:00 +00:00
|
|
|
.hypervisorDriver = &udevHypervisorDriver,
|
|
|
|
|
.nodeDeviceDriver = &udevNodeDeviceDriver,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
static virStateDriver udevStateDriver = {
|
2010-01-26 03:13:08 +01:00
|
|
|
.name = "udev",
|
2013-04-29 10:20:17 +01:00
|
|
|
.stateInitialize = nodeStateInitialize, /* 0.7.3 */
|
|
|
|
|
.stateCleanup = nodeStateCleanup, /* 0.7.3 */
|
|
|
|
|
.stateReload = nodeStateReload, /* 0.7.3 */
|
2009-11-12 22:48:24 +01:00
|
|
|
};
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
|
|
|
|
int
|
|
|
|
|
udevNodeRegister(void)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2011-05-09 17:24:09 +08:00
|
|
|
VIR_DEBUG("Registering udev node device backend");
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2018-01-26 11:16:00 +00:00
|
|
|
if (virRegisterConnectDriver(&udevConnectDriver, false) < 0)
|
|
|
|
|
return -1;
|
2015-01-20 16:16:26 +00:00
|
|
|
if (virSetSharedNodeDeviceDriver(&udevNodeDeviceDriver) < 0)
|
2009-11-12 22:48:24 +01:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return virRegisterStateDriver(&udevStateDriver);
|
|
|
|
|
}
|