diff mbox series

[RFC,netifd,3/3] vlandev: add pass-through hotplug ops that pass the VLAN info to the bridge

Message ID 20200807123533.71916-3-nbd@nbd.name
State New
Headers show
Series [RFC,netifd,1/3] bridge: add support for defining port member vlans via hotplug ops | expand

Commit Message

Felix Fietkau Aug. 7, 2020, 12:35 p.m. UTC
Only used for 802.1q devices

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 vlandev.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)
diff mbox series

Patch

diff --git a/vlandev.c b/vlandev.c
index 10b78e241576..7efb5f42b5c4 100644
--- a/vlandev.c
+++ b/vlandev.c
@@ -45,6 +45,7 @@  static const struct uci_blob_param_list vlandev_attr_list = {
 };
 
 static struct device_type vlan8021q_device_type;
+static struct blob_buf b;
 
 struct vlandev_device {
 	struct device dev;
@@ -57,6 +58,67 @@  struct vlandev_device {
 	struct vlandev_config config;
 };
 
+static int
+vlandev_hotplug_add(struct device *dev, struct device *member, struct blob_attr *vlan)
+{
+	struct vlandev_device *mvdev = container_of(dev, struct vlandev_device, dev);
+	void *a;
+
+	dev = mvdev->parent.dev;
+	if (!dev || !dev->hotplug_ops)
+		return UBUS_STATUS_NOT_SUPPORTED;
+
+	blob_buf_init(&b, 0);
+	a = blobmsg_open_array(&b, "vlans");
+	blobmsg_printf(&b, NULL, "%d", mvdev->config.vid);
+	blobmsg_close_array(&b, a);
+
+	return dev->hotplug_ops->add(dev, member, blobmsg_data(b.head));
+}
+
+static int
+vlandev_hotplug_del(struct device *dev, struct device *member)
+{
+	struct vlandev_device *mvdev = container_of(dev, struct vlandev_device, dev);
+
+	dev = mvdev->parent.dev;
+	if (!dev || !dev->hotplug_ops)
+		return UBUS_STATUS_NOT_SUPPORTED;
+
+	return dev->hotplug_ops->del(dev, member);
+}
+
+static int
+vlandev_hotplug_prepare(struct device *dev)
+{
+	struct vlandev_device *mvdev = container_of(dev, struct vlandev_device, dev);
+
+	dev = mvdev->parent.dev;
+	if (!dev || !dev->hotplug_ops)
+		return UBUS_STATUS_NOT_SUPPORTED;
+
+	return dev->hotplug_ops->prepare(dev);
+}
+
+static void vlandev_hotplug_check(struct vlandev_device *mvdev)
+{
+	static const struct device_hotplug_ops hotplug_ops = {
+		.prepare = vlandev_hotplug_prepare,
+		.add = vlandev_hotplug_add,
+		.del = vlandev_hotplug_del
+	};
+	struct device *dev = mvdev->parent.dev;
+
+	if (!dev || !dev->hotplug_ops || avl_is_empty(&dev->vlans.avl) ||
+		dev->type != &vlan8021q_device_type) {
+		mvdev->dev.hotplug_ops = NULL;
+		return;
+	}
+
+	mvdev->dev.hotplug_ops = &hotplug_ops;
+}
+
+
 static void
 vlandev_base_cb(struct device_user *dev, enum device_event ev)
 {
@@ -69,6 +131,9 @@  vlandev_base_cb(struct device_user *dev, enum device_event ev)
 	case DEV_EVENT_REMOVE:
 		device_set_present(&mvdev->dev, false);
 		break;
+	case DEV_EVENT_UPDATE_IFNAME:
+		vlandev_hotplug_check(mvdev);
+		break;
 	default:
 		return;
 	}
@@ -179,6 +244,7 @@  vlandev_config_init(struct device *dev)
 		basedev = device_get(blobmsg_data(mvdev->ifname), true);
 
 	device_add_user(&mvdev->parent, basedev);
+	vlandev_hotplug_check(mvdev);
 }
 
 static void vlandev_qos_mapping_list_apply(struct vlist_simple_tree *qos_mapping_li, struct blob_attr *list)