core: improve handling of NPAR/SR-IOV devices (rh #804527)

Use the new kernel physical_port_id interface property to recognize
when two devices are just virtual devices sharing the same physical
port, and refuse to bond/team multiple slaves on the same port.
This commit is contained in:
Dan Winship 2013-10-11 14:59:26 -04:00
parent a4dcd66698
commit b7300bbe5a
10 changed files with 142 additions and 1 deletions

View file

@ -125,6 +125,14 @@
An array of object paths of every configured connection that is currently 'available' through this device.
</tp:docstring>
</property>
<property name="PhysicalPortId" type="s" access="read">
<tp:docstring>
If non-empty, an (opaque) indicator of the physical network
port associated with the device. This can be used to recognize
when two seemingly-separate hardware devices are actually just
different virtual interfaces to the same physical port.
</tp:docstring>
</property>
<method name="Disconnect">
<annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_device_disconnect"/>

View file

@ -364,6 +364,8 @@ enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection)
const char *iface = nm_device_get_ip_iface (device);
const char *slave_iface = nm_device_get_ip_iface (slave);
nm_device_master_check_slave_physical_port (device, slave, LOGD_BOND);
nm_device_take_down (slave, TRUE);
success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device),

View file

@ -92,6 +92,9 @@ gboolean nm_device_get_enslaved (NMDevice *device);
NMDevice *nm_device_master_get_slave_by_ifindex (NMDevice *dev, int ifindex);
void nm_device_master_check_slave_physical_port (NMDevice *dev, NMDevice *slave,
guint64 log_domain);
void nm_device_set_carrier (NMDevice *device, gboolean carrier);
#endif /* NM_DEVICE_PRIVATE_H */

View file

@ -546,6 +546,8 @@ enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection)
const char *slave_iface = nm_device_get_ip_iface (slave);
NMSettingTeamPort *s_team_port;
nm_device_master_check_slave_physical_port (device, slave, LOGD_TEAM);
nm_device_take_down (slave, TRUE);
s_team_port = nm_connection_get_setting_team_port (connection);

View file

@ -129,6 +129,7 @@ enum {
PROP_RFKILL_TYPE,
PROP_IFINDEX,
PROP_AVAILABLE_CONNECTIONS,
PROP_PHYSICAL_PORT_ID,
PROP_IS_MASTER,
PROP_HW_ADDRESS,
PROP_HAS_PENDING_ACTION,
@ -202,6 +203,7 @@ typedef struct {
GHashTable * available_connections;
guint8 hw_addr[NM_UTILS_HWADDR_LEN_MAX];
guint hw_addr_len;
char * physical_port_id;
gboolean manager_managed; /* whether managed by NMManager or not */
gboolean default_unmanaged; /* whether unmanaged by default */
@ -589,8 +591,10 @@ constructed (GObject *object)
priv->carrier = TRUE;
}
if (priv->ifindex > 0)
if (priv->ifindex > 0) {
priv->is_software = nm_platform_link_is_software (priv->ifindex);
priv->physical_port_id = nm_platform_link_get_physical_port_id (priv->ifindex);
}
if (G_OBJECT_CLASS (nm_device_parent_class)->constructed)
G_OBJECT_CLASS (nm_device_parent_class)->constructed (object);
@ -1337,6 +1341,49 @@ nm_device_master_get_slave_by_ifindex (NMDevice *dev, int ifindex)
return NULL;
}
/**
* nm_device_master_check_slave_physical_port:
* @dev: the master device
* @slave: a slave device
* @log_domain: domain to log a warning in
*
* Checks if @dev already has a slave with the same #NMDevice:physical-port-id
* as @slave, and logs a warning if so.
*/
void
nm_device_master_check_slave_physical_port (NMDevice *dev, NMDevice *slave,
guint64 log_domain)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
const char *slave_physical_port_id, *existing_physical_port_id;
SlaveInfo *info;
GSList *iter;
slave_physical_port_id = nm_device_get_physical_port_id (slave);
if (!slave_physical_port_id)
return;
for (iter = priv->slaves; iter; iter = iter->next) {
info = iter->data;
if (info->slave == slave)
continue;
existing_physical_port_id = nm_device_get_physical_port_id (info->slave);
if (!g_strcmp0 (slave_physical_port_id, existing_physical_port_id)) {
nm_log_warn (log_domain, "(%s): slave %s shares a physical port with existing slave %s",
nm_device_get_ip_iface (dev),
nm_device_get_ip_iface (slave),
nm_device_get_ip_iface (info->slave));
/* Since this function will get called for every slave, we only have
* to warn about the first match we find; if there are other matches
* later in the list, we will have already warned about them matching
* @existing earlier.
*/
return;
}
}
}
/**
* nm_device_is_master:
* @dev: the device
@ -5172,6 +5219,8 @@ dispose (GObject *object)
g_hash_table_unref (priv->available_connections);
priv->available_connections = NULL;
g_clear_pointer (&priv->physical_port_id, g_free);
activation_source_clear (self, TRUE, AF_INET);
activation_source_clear (self, TRUE, AF_INET6);
@ -5432,6 +5481,9 @@ get_property (GObject *object, guint prop_id,
g_ptr_array_add (array, g_strdup (nm_connection_get_path (connection)));
g_value_take_boxed (value, array);
break;
case PROP_PHYSICAL_PORT_ID:
g_value_set_string (value, priv->physical_port_id);
break;
case PROP_IS_MASTER:
g_value_set_boolean (value, priv->is_master);
break;
@ -5688,6 +5740,14 @@ nm_device_class_init (NMDeviceClass *klass)
DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
G_PARAM_READABLE));
g_object_class_install_property
(object_class, PROP_PHYSICAL_PORT_ID,
g_param_spec_string (NM_DEVICE_PHYSICAL_PORT_ID,
"PhysicalPortId",
"PhysicalPortId",
NULL,
G_PARAM_READABLE));
g_object_class_install_property
(object_class, PROP_IS_MASTER,
g_param_spec_boolean (NM_DEVICE_IS_MASTER,
@ -6908,3 +6968,11 @@ nm_device_has_pending_action (NMDevice *device)
return priv->pending_actions > 0;
}
const char *
nm_device_get_physical_port_id (NMDevice *device)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
return priv->physical_port_id;
}

View file

@ -60,6 +60,7 @@
#define NM_DEVICE_AUTOCONNECT "autoconnect"
#define NM_DEVICE_FIRMWARE_MISSING "firmware-missing"
#define NM_DEVICE_AVAILABLE_CONNECTIONS "available-connections"
#define NM_DEVICE_PHYSICAL_PORT_ID "physical-port-id"
#define NM_DEVICE_TYPE_DESC "type-desc" /* Internal only */
#define NM_DEVICE_RFKILL_TYPE "rfkill-type" /* Internal only */
#define NM_DEVICE_IFINDEX "ifindex" /* Internal only */
@ -337,6 +338,8 @@ gboolean nm_device_has_pending_action (NMDevice *device);
GPtrArray *nm_device_get_available_connections (NMDevice *device,
const char *specific_object);
const char *nm_device_get_physical_port_id (NMDevice *device);
G_END_DECLS
/* For testing only */

View file

@ -466,6 +466,15 @@ link_get_mtu (NMPlatform *platform, int ifindex)
return device ? device->link.mtu : 0;
}
static char *
link_get_physical_port_id (NMPlatform *platform, int ifindex)
{
/* We call link_get just to cause an error to be set if @ifindex is bad. */
link_get (platform, ifindex);
return NULL;
}
static gboolean
link_supports_carrier_detect (NMPlatform *platform, int ifindex)
{
@ -1189,6 +1198,8 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass)
platform_class->link_get_mtu = link_get_mtu;
platform_class->link_set_mtu = link_set_mtu;
platform_class->link_get_physical_port_id = link_get_physical_port_id;
platform_class->link_supports_carrier_detect = link_supports_carrier_detect;
platform_class->link_supports_vlans = link_supports_vlans;

View file

@ -1725,6 +1725,26 @@ link_get_mtu (NMPlatform *platform, int ifindex)
return rtnllink ? rtnl_link_get_mtu (rtnllink) : 0;
}
static char *
link_get_physical_port_id (NMPlatform *platform, int ifindex)
{
const char *ifname;
char *path, *id;
ifname = nm_platform_link_get_name (ifindex);
if (!ifname)
return NULL;
path = g_strdup_printf ("/sys/class/net/%s/physical_port_id", ifname);
if (g_file_test (path, G_FILE_TEST_EXISTS))
id = sysctl_get (platform, path);
else
id = NULL;
g_free (path);
return id;
}
static int
vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint32 vlan_flags)
{
@ -2719,6 +2739,8 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
platform_class->link_get_mtu = link_get_mtu;
platform_class->link_set_mtu = link_set_mtu;
platform_class->link_get_physical_port_id = link_get_physical_port_id;
platform_class->link_supports_carrier_detect = link_supports_carrier_detect;
platform_class->link_supports_vlans = link_supports_vlans;

View file

@ -733,6 +733,24 @@ nm_platform_link_get_mtu (int ifindex)
return klass->link_get_mtu (platform, ifindex);
}
/**
* nm_platform_link_get_mtu:
* @ifindex: Interface index
*
* Returns: physical port ID for the interface, or %NULL on error
* or if the interface has no physical port ID.
*/
char *
nm_platform_link_get_physical_port_id (int ifindex)
{
reset_error ();
g_return_val_if_fail (ifindex >= 0, NULL);
g_return_val_if_fail (klass->link_get_physical_port_id, NULL);
return klass->link_get_physical_port_id (platform, ifindex);
}
/**
* nm_platform_link_enslave:
* @master: Interface index of the master

View file

@ -251,6 +251,8 @@ typedef struct {
guint32 (*link_get_mtu) (NMPlatform *, int ifindex);
gboolean (*link_set_mtu) (NMPlatform *, int ifindex, guint32 mtu);
char * (*link_get_physical_port_id) (NMPlatform *, int ifindex);
gboolean (*link_supports_carrier_detect) (NMPlatform *, int ifindex);
gboolean (*link_supports_vlans) (NMPlatform *, int ifindex);
@ -370,6 +372,8 @@ gboolean nm_platform_link_set_address (int ifindex, const void *address, size_t
guint32 nm_platform_link_get_mtu (int ifindex);
gboolean nm_platform_link_set_mtu (int ifindex, guint32 mtu);
char *nm_platform_link_get_physical_port_id (int ifindex);
gboolean nm_platform_link_supports_carrier_detect (int ifindex);
gboolean nm_platform_link_supports_vlans (int ifindex);