device: make attach_port() asynchronous

For some device types the attach-port operation doesn't complete
immediately. NMDevice needs to wait that the operation completes
before proceeding (for example, before starting stage3 for the port).

Change attach_port() so that it can return TERNARY_DEFAULT to indicate
that the operation will complete asynchronously. Most of devices are
not affected by this and can continue returning TRUE/FALSE as before
without callback.
This commit is contained in:
Beniamino Galvani
2022-04-27 16:27:24 +02:00
parent bcc958c411
commit 9fcbc6b37d
8 changed files with 136 additions and 55 deletions

View File

@@ -424,8 +424,14 @@ commit_port_options(NMDevice *bond_device, NMDevice *port, NMSettingBondPort *s_
queue_id_str); queue_id_str);
} }
static gboolean static NMTernary
attach_port(NMDevice *device, NMDevice *port, NMConnection *connection, gboolean configure) attach_port(NMDevice *device,
NMDevice *port,
NMConnection *connection,
gboolean configure,
GCancellable *cancellable,
NMDeviceAttachPortCallback callback,
gpointer user_data)
{ {
NMDeviceBond *self = NM_DEVICE_BOND(device); NMDeviceBond *self = NM_DEVICE_BOND(device);
NMSettingBondPort *s_port; NMSettingBondPort *s_port;

View File

@@ -974,8 +974,14 @@ deactivate(NMDevice *device)
} }
} }
static gboolean static NMTernary
attach_port(NMDevice *device, NMDevice *port, NMConnection *connection, gboolean configure) attach_port(NMDevice *device,
NMDevice *port,
NMConnection *connection,
gboolean configure,
GCancellable *cancellable,
NMDeviceAttachPortCallback callback,
gpointer user_data)
{ {
NMDeviceBridge *self = NM_DEVICE_BRIDGE(device); NMDeviceBridge *self = NM_DEVICE_BRIDGE(device);
NMConnection *master_connection; NMConnection *master_connection;

View File

@@ -206,8 +206,14 @@ update_connection(NMDevice *device, NMConnection *connection)
g_object_set(G_OBJECT(s_vrf), NM_SETTING_VRF_TABLE, priv->props.table, NULL); g_object_set(G_OBJECT(s_vrf), NM_SETTING_VRF_TABLE, priv->props.table, NULL);
} }
static gboolean static NMTernary
attach_port(NMDevice *device, NMDevice *port, NMConnection *connection, gboolean configure) attach_port(NMDevice *device,
NMDevice *port,
NMConnection *connection,
gboolean configure,
GCancellable *cancellable,
NMDeviceAttachPortCallback callback,
gpointer user_data)
{ {
NMDeviceVrf *self = NM_DEVICE_VRF(device); NMDeviceVrf *self = NM_DEVICE_VRF(device);
gboolean success = TRUE; gboolean success = TRUE;

View File

@@ -122,6 +122,7 @@ typedef enum _nm_packed {
typedef struct { typedef struct {
CList lst_slave; CList lst_slave;
NMDevice *slave; NMDevice *slave;
GCancellable *cancellable;
gulong watch_id; gulong watch_id;
bool slave_is_enslaved; bool slave_is_enslaved;
bool configure; bool configure;
@@ -5927,43 +5928,16 @@ find_slave_info(NMDevice *self, NMDevice *slave)
return NULL; return NULL;
} }
/** static void
* nm_device_master_enslave_slave: attach_port_done(NMDevice *self, NMDevice *slave, gboolean success)
* @self: the master device
* @slave: the slave device to enslave
* @connection: (allow-none): the slave device's connection
*
* If @self is capable of enslaving other devices (ie it's a bridge, bond, team,
* etc) then this function enslaves @slave.
*
* Returns: %TRUE on success, %FALSE on failure or if this device cannot enslave
* other devices.
*/
static gboolean
nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *connection)
{ {
SlaveInfo *info; SlaveInfo *info;
gboolean success = FALSE;
gboolean configure;
g_return_val_if_fail(self != NULL, FALSE);
g_return_val_if_fail(slave != NULL, FALSE);
g_return_val_if_fail(NM_DEVICE_GET_CLASS(self)->attach_port != NULL, FALSE);
info = find_slave_info(self, slave); info = find_slave_info(self, slave);
if (!info) if (!info)
return FALSE; return;
if (info->slave_is_enslaved)
success = TRUE;
else {
configure = (info->configure && connection != NULL);
if (configure)
g_return_val_if_fail(nm_device_get_state(slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE);
success = NM_DEVICE_GET_CLASS(self)->attach_port(self, slave, connection, configure);
info->slave_is_enslaved = success; info->slave_is_enslaved = success;
}
nm_device_slave_notify_enslave(info->slave, success); nm_device_slave_notify_enslave(info->slave, success);
@@ -5983,8 +5957,71 @@ nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *co
*/ */
if (success) if (success)
nm_device_activate_schedule_stage3_ip_config(self, FALSE); nm_device_activate_schedule_stage3_ip_config(self, FALSE);
}
return success; static void
attach_port_cb(NMDevice *self, GError *error, gpointer user_data)
{
NMDevice *slave = user_data;
SlaveInfo *info;
if (nm_utils_error_is_cancelled(error))
return;
info = find_slave_info(self, slave);
if (!info)
return;
nm_clear_g_cancellable(&info->cancellable);
attach_port_done(self, slave, !error);
}
/**
* nm_device_master_enslave_slave:
* @self: the master device
* @slave: the slave device to enslave
* @connection: (allow-none): the slave device's connection
*
* If @self is capable of enslaving other devices (ie it's a bridge, bond, team,
* etc) then this function enslaves @slave.
*/
static void
nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *connection)
{
SlaveInfo *info;
NMTernary success;
gboolean configure;
g_return_if_fail(self);
g_return_if_fail(slave);
g_return_if_fail(NM_DEVICE_GET_CLASS(self)->attach_port);
info = find_slave_info(self, slave);
if (!info)
return;
if (info->slave_is_enslaved)
success = TRUE;
else {
configure = (info->configure && connection != NULL);
if (configure)
g_return_if_fail(nm_device_get_state(slave) >= NM_DEVICE_STATE_DISCONNECTED);
nm_clear_g_cancellable(&info->cancellable);
info->cancellable = g_cancellable_new();
success = NM_DEVICE_GET_CLASS(self)->attach_port(self,
slave,
connection,
configure,
info->cancellable,
attach_port_cb,
slave);
if (success == NM_TERNARY_DEFAULT)
return;
}
attach_port_done(self, slave, success);
} }
/** /**
@@ -6038,6 +6075,7 @@ nm_device_master_release_slave(NMDevice *self,
g_return_if_fail(self == slave_priv->master); g_return_if_fail(self == slave_priv->master);
nm_assert(slave == info->slave); nm_assert(slave == info->slave);
nm_clear_g_cancellable(&info->cancellable);
/* first, let subclasses handle the release ... */ /* first, let subclasses handle the release ... */
if (info->slave_is_enslaved || nm_device_sys_iface_state_is_external(slave) if (info->slave_is_enslaved || nm_device_sys_iface_state_is_external(slave)
@@ -7609,7 +7647,7 @@ nm_device_master_add_slave(NMDevice *self, NMDevice *slave, gboolean configure)
g_return_val_if_fail(NM_IS_DEVICE(self), FALSE); g_return_val_if_fail(NM_IS_DEVICE(self), FALSE);
g_return_val_if_fail(NM_IS_DEVICE(slave), FALSE); g_return_val_if_fail(NM_IS_DEVICE(slave), FALSE);
g_return_val_if_fail(NM_DEVICE_GET_CLASS(self)->attach_port != NULL, FALSE); g_return_val_if_fail(NM_DEVICE_GET_CLASS(self)->attach_port, FALSE);
priv = NM_DEVICE_GET_PRIVATE(self); priv = NM_DEVICE_GET_PRIVATE(self);
slave_priv = NM_DEVICE_GET_PRIVATE(slave); slave_priv = NM_DEVICE_GET_PRIVATE(slave);

View File

@@ -163,6 +163,7 @@ typedef enum {
} NMDeviceCheckDevAvailableFlags; } NMDeviceCheckDevAvailableFlags;
typedef void (*NMDeviceDeactivateCallback)(NMDevice *self, GError *error, gpointer user_data); typedef void (*NMDeviceDeactivateCallback)(NMDevice *self, GError *error, gpointer user_data);
typedef void (*NMDeviceAttachPortCallback)(NMDevice *self, GError *error, gpointer user_data);
typedef struct _NMDeviceClass { typedef struct _NMDeviceClass {
NMDBusObjectClass parent; NMDBusObjectClass parent;
@@ -373,11 +374,17 @@ typedef struct _NMDeviceClass {
NMConnection *connection, NMConnection *connection,
GError **error); GError **error);
gboolean (*attach_port)(NMDevice *self, /* Attachs a port asynchronously. Returns TRUE/FALSE on immediate
* success/error; in such cases, the callback is not invoked. If the
* action couldn't be completed immediately, DEFAULT is returned and
* the callback will always be invoked asynchronously. */
NMTernary (*attach_port)(NMDevice *self,
NMDevice *port, NMDevice *port,
NMConnection *connection, NMConnection *connection,
gboolean configure); gboolean configure,
GCancellable *cancellable,
NMDeviceAttachPortCallback callback,
gpointer user_data);
void (*detach_port)(NMDevice *self, NMDevice *port, gboolean configure); void (*detach_port)(NMDevice *self, NMDevice *port, gboolean configure);
void (*parent_changed_notify)(NMDevice *self, void (*parent_changed_notify)(NMDevice *self,

View File

@@ -78,8 +78,14 @@ act_stage3_ip_config(NMDevice *device, int addr_family)
nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, NULL); nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, NULL);
} }
static gboolean static NMTernary
attach_port(NMDevice *device, NMDevice *port, NMConnection *connection, gboolean configure) attach_port(NMDevice *device,
NMDevice *port,
NMConnection *connection,
gboolean configure,
GCancellable *cancellable,
NMDeviceAttachPortCallback callback,
gpointer user_data)
{ {
if (!configure) if (!configure)
return TRUE; return TRUE;

View File

@@ -115,8 +115,14 @@ set_mtu_cb(GError *error, gpointer user_data)
g_object_unref(self); g_object_unref(self);
} }
static gboolean static NMTernary
attach_port(NMDevice *device, NMDevice *port, NMConnection *connection, gboolean configure) attach_port(NMDevice *device,
NMDevice *port,
NMConnection *connection,
gboolean configure,
GCancellable *cancellable,
NMDeviceEnslaveSlaveCallback callback,
gpointer user_data)
{ {
NMDeviceOvsPort *self = NM_DEVICE_OVS_PORT(device); NMDeviceOvsPort *self = NM_DEVICE_OVS_PORT(device);
NMActiveConnection *ac_port = NULL; NMActiveConnection *ac_port = NULL;

View File

@@ -790,8 +790,14 @@ deactivate(NMDevice *device)
teamd_cleanup(self, TRUE); teamd_cleanup(self, TRUE);
} }
static gboolean static NMTernary
attach_port(NMDevice *device, NMDevice *port, NMConnection *connection, gboolean configure) attach_port(NMDevice *device,
NMDevice *port,
NMConnection *connection,
gboolean configure,
GCancellable *cancellable,
NMDeviceAttachPortCallback callback,
gpointer user_data)
{ {
NMDeviceTeam *self = NM_DEVICE_TEAM(device); NMDeviceTeam *self = NM_DEVICE_TEAM(device);
NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE(self); NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE(self);