[B.A.T.M.A.N.] [PATCHv4 09/13] batman-adv: add sysfs framework for VLAN

Antonio Quartulli
2013-06-04T10:11:47Z

Each VLAN can now have its own set of attributes and these
are exported through a new subfolder in the sysfs
environment.

The subfolder is named "vlan%VID", there will be one per
VLAN, and it is created inside the "mesh" sysfs folder
belonging to batman-adv.

Create all the needed macros and data structures to easily
handle new attributes.

Signed-off-by: Antonio Quartulli <antonio@open-mesh.com>
---
soft-interface.c | 7 +++
sysfs.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
sysfs.h | 10 ++++
3 files changed, 155 insertions(+)

diff --git a/soft-interface.c b/soft-interface.c
index 0280dfc..e6679e2 100644
--- a/soft-interface.c
+++ b/soft-interface.c
@@ -383,6 +383,7 @@ static int batadv_softif_create_vlan(struct batadv_priv *bat_priv,
unsigned short vid)
{
struct batadv_softif_vlan *vlan;
+ int err;

vlan = kzalloc(sizeof(*vlan), GFP_ATOMIC);
if (!vlan)
@@ -398,6 +399,10 @@ static int batadv_softif_create_vlan(struct batadv_priv *bat_priv,
bat_priv->soft_iface->dev_addr, vid,
BATADV_NULL_IFINDEX);

+ err = batadv_sysfs_add_vlan(bat_priv->soft_iface, vlan);
+ if (err)
+ return err;
+
spin_lock_bh(&bat_priv->softif_vlan_list_lock);
list_add_rcu(&vlan->list, &bat_priv->softif_vlan_list);
spin_unlock_bh(&bat_priv->softif_vlan_list_lock);
@@ -461,6 +466,8 @@ static int batadv_interface_kill_vid(struct net_device *dev, __be16 proto,
list_del_rcu(&vlan->list);
spin_unlock_bh(&bat_priv->softif_vlan_list_lock);

+ batadv_sysfs_del_vlan(bat_priv, vlan);
+
/* explicitly remove the associated TT local entry because it is marked
* with the NOPURGE flag
*/
diff --git a/sysfs.c b/sysfs.c
index b70ae52..03285dd 100644
--- a/sysfs.c
+++ b/sysfs.c
@@ -39,6 +39,31 @@ static struct batadv_priv *batadv_kobj_to_batpriv(struct kobject *obj)
return netdev_priv(net_dev);
}

+/**
+ * batadv_kobj_to_vlan - convert a kobj in the associated softif_vlan struct
+ * @obj: kobject to covert
+ *
+ * Return the associated softif_vlan struct if cound, NULL otherwise
+ */
+static struct batadv_softif_vlan *batadv_kobj_to_vlan(struct kobject *obj)
+{
+ struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(obj->parent);
+ struct batadv_softif_vlan *vlan, *vlan_tmp = NULL;
+
+ list_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) {
+ if (vlan->kobj != obj)
+ continue;
+
+ if (!atomic_inc_not_zero(&vlan->refcount))
+ continue;
+
+ vlan_tmp = vlan;
+ break;
+ }
+
+ return vlan_tmp;
+}
+
#define BATADV_UEV_TYPE_VAR "BATTYPE="
#define BATADV_UEV_ACTION_VAR "BATACTION="
#define BATADV_UEV_DATA_VAR "BATDATA="
@@ -53,6 +78,15 @@ static char *batadv_uev_type_str[] = {
"gw"
};

+/* Use this, if you have customized show and store functions for vlan attrs */
+#define BATADV_ATTR_VLAN(_name, _mode, _show, _store) \
+struct batadv_attribute batadv_attr_vlan_##_name = { \
+ .attr = {.name = __stringify(_name), \
+ .mode = _mode }, \
+ .show = _show, \
+ .store = _store, \
+};
+
/* Use this, if you have customized show and store functions */
#define BATADV_ATTR(_name, _mode, _show, _store) \
struct batadv_attribute batadv_attr_##_name = { \
@@ -122,6 +156,38 @@ ssize_t batadv_show_##_name(struct kobject *kobj, \
static BATADV_ATTR(_name, _mode, batadv_show_##_name, \
batadv_store_##_name)

+#define BATADV_ATTR_VLAN_STORE_BOOL(_name, _post_func) \
+ssize_t batadv_store_vlan_##_name(struct kobject *kobj, \
+ struct attribute *attr, char *buff, \
+ size_t count) \
+{ \
+ struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj->parent);\
+ struct batadv_softif_vlan *vlan = batadv_kobj_to_vlan(kobj); \
+ size_t res = __batadv_store_bool_attr(buff, count, _post_func, attr,\
+ &vlan->_name, \
+ bat_priv->soft_iface); \
+ batadv_softif_vlan_free_ref(vlan); \
+ return res; \
+}
+
+#define BATADV_ATTR_VLAN_SHOW_BOOL(_name) \
+ssize_t batadv_show_vlan_##_name(struct kobject *kobj, \
+ struct attribute *attr, char *buff) \
+{ \
+ struct batadv_softif_vlan *vlan = batadv_kobj_to_vlan(kobj); \
+ size_t res = sprintf(buff, "%s\n", \
+ atomic_read(&vlan->_name) == 0 ? \
+ "disabled" : "enabled"); \
+ batadv_softif_vlan_free_ref(vlan); \
+ return res; \
+}
+
+/* Use this, if you are going to turn a [name] in the vlan struct on or off */
+#define BATADV_ATTR_VLAN_BOOL(_name, _mode, _post_func) \
+ static BATADV_ATTR_VLAN_STORE_BOOL(_name, _post_func) \
+ static BATADV_ATTR_VLAN_SHOW_BOOL(_name) \
+ static BATADV_ATTR_VLAN(_name, _mode, batadv_show_vlan_##_name, \
+ batadv_store_vlan_##_name)

static int batadv_store_bool_attr(char *buff, size_t count,
struct net_device *net_dev,
@@ -403,6 +469,13 @@ static struct batadv_attribute *batadv_mesh_attrs[] = {
NULL,
};

+/**
+ * batadv_vlan_attrs - array of vlan specific sysfs attributes
+ */
+static struct batadv_attribute *batadv_vlan_attrs[] = {
+ NULL,
+};
+
int batadv_sysfs_add_meshif(struct net_device *dev)
{
struct kobject *batif_kobject = &dev->dev.kobj;
@@ -453,6 +526,71 @@ void batadv_sysfs_del_meshif(struct net_device *dev)
bat_priv->mesh_obj = NULL;
}

+/**
+ * batadv_sysfs_add_vlan - add all the needed sysfs objects for the new vlan
+ * @dev: netdev of the mesh interface
+ * @vlan: private data of the newly added VLAN interface
+ *
+ * Return 0 on success and -ENOMEM if any of the structure allocations fails
+ */
+int batadv_sysfs_add_vlan(struct net_device *dev,
+ struct batadv_softif_vlan *vlan)
+{
+ struct batadv_priv *bat_priv = netdev_priv(dev);
+ struct batadv_attribute **bat_attr;
+ char vlan_subdir[256];
+ int err;
+
+ sprintf(vlan_subdir, BATADV_SYSFS_VLAN_SUBDIR_PREFIX "%u",
+ vlan->vid & VLAN_VID_MASK);
+
+ vlan->kobj = kobject_create_and_add(vlan_subdir, bat_priv->mesh_obj);
+ if (!vlan->kobj) {
+ batadv_err(dev, "Can't add sysfs directory: %s/%s\n", dev->name,
+ vlan_subdir);
+ goto out;
+ }
+
+ for (bat_attr = batadv_vlan_attrs; *bat_attr; ++bat_attr) {
+ err = sysfs_create_file(vlan->kobj,
+ &((*bat_attr)->attr));
+ if (err) {
+ batadv_err(dev, "Can't add sysfs file: %s/%s/%s\n",
+ dev->name, vlan_subdir,
+ ((*bat_attr)->attr).name);
+ goto rem_attr;
+ }
+ }
+
+ return 0;
+
+rem_attr:
+ for (bat_attr = batadv_vlan_attrs; *bat_attr; ++bat_attr)
+ sysfs_remove_file(vlan->kobj, &((*bat_attr)->attr));
+
+ kobject_put(vlan->kobj);
+ vlan->kobj = NULL;
+out:
+ return -ENOMEM;
+}
+
+/**
+ * batadv_sysfs_del_vlan - remove all the sysfs objects for a given VLAN
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vlan: the private data of the VLAN to destroy
+ */
+void batadv_sysfs_del_vlan(struct batadv_priv *bat_priv,
+ struct batadv_softif_vlan *vlan)
+{
+ struct batadv_attribute **bat_attr;
+
+ for (bat_attr = batadv_vlan_attrs; *bat_attr; ++bat_attr)
+ sysfs_remove_file(vlan->kobj, &((*bat_attr)->attr));
+
+ kobject_put(vlan->kobj);
+ vlan->kobj = NULL;
+}
+
static ssize_t batadv_show_mesh_iface(struct kobject *kobj,
struct attribute *attr, char *buff)
{
diff --git a/sysfs.h b/sysfs.h
index 479acf4..c4f96c1 100644
--- a/sysfs.h
+++ b/sysfs.h
@@ -22,6 +22,12 @@

#define BATADV_SYSFS_IF_MESH_SUBDIR "mesh"
#define BATADV_SYSFS_IF_BAT_SUBDIR "batman_adv"
+/**
+ * BATADV_SYSFS_VLAN_SUBDIR_PREFIX - prefix of the subfolder that will be
+ * created in the sysfs hierarchy for each VLAN interface. The subfolder will
+ * be named "BATADV_SYSFS_VLAN_SUBDIR_PREFIX%vid"
+ */
+#define BATADV_SYSFS_VLAN_SUBDIR_PREFIX "vlan"

struct batadv_attribute {
struct attribute attr;
@@ -36,6 +42,10 @@ void batadv_sysfs_del_meshif(struct net_device *dev);
int batadv_sysfs_add_hardif(struct kobject **hardif_obj,
struct net_device *dev);
void batadv_sysfs_del_hardif(struct kobject **hardif_obj);
+int batadv_sysfs_add_vlan(struct net_device *dev,
+ struct batadv_softif_vlan *vlan);
+void batadv_sysfs_del_vlan(struct batadv_priv *bat_priv,
+ struct batadv_softif_vlan *vlan);
int batadv_throw_uevent(struct batadv_priv *bat_priv, enum batadv_uev_type type,
enum batadv_uev_action action, const char *data);

--
1.8.1.5