Message ID | 20211222155743.256280-9-miquel.raynal@bootlin.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | IEEE 802.15.4 passive scan support | expand |
On Wed, 22 Dec 2021 16:57:33 +0100 Miquel Raynal wrote: > +/* Maximum number of PAN entries to store */ > +static int max_pan_entries = 100; > +module_param(max_pan_entries, uint, 0644); > +MODULE_PARM_DESC(max_pan_entries, > + "Maximum number of PANs to discover per scan (default is 100)"); > + > +static int pan_expiration = 60; > +module_param(pan_expiration, uint, 0644); > +MODULE_PARM_DESC(pan_expiration, > + "Expiration of the scan validity in seconds (default is 60s)"); Can these be per-device control knobs? Module params are rarely the best answer.
Hi, On Wed, 22 Dec 2021 at 10:57, Miquel Raynal <miquel.raynal@bootlin.com> wrote: > > Let's introduce the basics of PAN management: > - structures defining PANs > - helpers for PANs registration > - helpers discarding old PANs > I think there exists a little misunderstanding about how the architecture is between the structures wpan_phy, wpan_dev and cfg802154. - wpan_phy: represents the PHY layer of IEEE 802154 and is a registered device class. - wpan_dev: represents the MAC layer of IEEE 802154 and is a netdev interface. You can have multiple wpan_dev operate on one wpan_phy. To my best knowledge it's like having multiple access points running on one phy (wireless) or macvlan on ethernet. You can actually do that with the mac802154_hwsim driver. However as there exists currently no (as my knowledge) hardware which supports e.g. multiple address filters we wanted to be prepared for to support such handling. Although, there exists some transceivers which support something like a "pan bridge" which goes into such a direction. What is a cfg802154 registered device? Well, at first it offers an interface between SoftMAC and HardMAC from nl802154, that's the cfg802154_ops structure. In theory a HardMAC transceiver would bypass the SoftMAC stack by implementing "cfg802154_ops" on the driver layer and try to do everything there as much as possible to support it. It is not a registered device class but the instance is tight to a wpan_phy. There can be multiple wpan_dev's (MAC layer instances on a phy/cfg802154 registered device). We currently don't support a HardMAC transceiver and I think because this misunderstanding came up. That means as far I see you should move the most of those attributes to per wpan_dev instead of per cfg802154. - Alex
Hi, On Tue, 28 Dec 2021 at 17:22, Alexander Aring <alex.aring@gmail.com> wrote: ... > That means as far I see you should move the most of those attributes > to per wpan_dev instead of per cfg802154. Sorry that's wrong. I see now, that the result for a scan on every possible wpan_dev for a specific wpan_phy should return the same result, that's why it belongs to cfg802154 and this is correct (as a cfg802154 has a 1:1 mapping to wpan_phy). Same as in wireless... - Alex
Hi Jakub, kuba@kernel.org wrote on Wed, 22 Dec 2021 12:55:55 -0800: > On Wed, 22 Dec 2021 16:57:33 +0100 Miquel Raynal wrote: > > +/* Maximum number of PAN entries to store */ > > +static int max_pan_entries = 100; > > +module_param(max_pan_entries, uint, 0644); > > +MODULE_PARM_DESC(max_pan_entries, > > + "Maximum number of PANs to discover per scan (default is 100)"); > > + > > +static int pan_expiration = 60; > > +module_param(pan_expiration, uint, 0644); > > +MODULE_PARM_DESC(pan_expiration, > > + "Expiration of the scan validity in seconds (default is 60s)"); > > Can these be per-device control knobs? Module params are rarely the > best answer. I believe we can do that on a per FFD device basis (for now it will be on a per-device basis, but later when we will have the necessary information we might do something more fine grained). Would a couple of sysfs entries work? Thanks, Miquèl
On Tue, 4 Jan 2022 15:41:51 +0100 Miquel Raynal wrote: > > On Wed, 22 Dec 2021 16:57:33 +0100 Miquel Raynal wrote: > > > +/* Maximum number of PAN entries to store */ > > > +static int max_pan_entries = 100; > > > +module_param(max_pan_entries, uint, 0644); > > > +MODULE_PARM_DESC(max_pan_entries, > > > + "Maximum number of PANs to discover per scan (default is 100)"); > > > + > > > +static int pan_expiration = 60; > > > +module_param(pan_expiration, uint, 0644); > > > +MODULE_PARM_DESC(pan_expiration, > > > + "Expiration of the scan validity in seconds (default is 60s)"); > > > > Can these be per-device control knobs? Module params are rarely the > > best answer. > > I believe we can do that on a per FFD device basis (for now it will be > on a per-device basis, but later when we will have the necessary > information we might do something more fine grained). Would a couple of > sysfs entries work? Is there no netlink object where this would fit? Sorry, I'm not at all familiar with WPAN. If it's orthogonal to current cfg802154 objects sysfs is fine, I guess.
Hi Alexander, alex.aring@gmail.com wrote on Tue, 28 Dec 2021 17:22:38 -0500: > Hi, > > On Wed, 22 Dec 2021 at 10:57, Miquel Raynal <miquel.raynal@bootlin.com> wrote: > > > > Let's introduce the basics of PAN management: > > - structures defining PANs > > - helpers for PANs registration > > - helpers discarding old PANs > > > > I think there exists a little misunderstanding about how the > architecture is between the structures wpan_phy, wpan_dev and > cfg802154. > > - wpan_phy: represents the PHY layer of IEEE 802154 and is a > registered device class. > - wpan_dev: represents the MAC layer of IEEE 802154 and is a netdev interface. > > You can have multiple wpan_dev operate on one wpan_phy. To my best > knowledge it's like having multiple access points running on one phy > (wireless) or macvlan on ethernet. You can actually do that with the > mac802154_hwsim driver. However as there exists currently no (as my > knowledge) hardware which supports e.g. multiple address filters we > wanted to be prepared for to support such handling. Although, there > exists some transceivers which support something like a "pan bridge" > which goes into such a direction. > > What is a cfg802154 registered device? Well, at first it offers an > interface between SoftMAC and HardMAC from nl802154, that's the > cfg802154_ops structure. In theory a HardMAC transceiver would bypass > the SoftMAC stack by implementing "cfg802154_ops" on the driver layer > and try to do everything there as much as possible to support it. It > is not a registered device class but the instance is tight to a > wpan_phy. There can be multiple wpan_dev's (MAC layer instances on a > phy/cfg802154 registered device). We currently don't support a HardMAC > transceiver and I think because this misunderstanding came up. Thanks for the explanation, I think it helps because the relationship between wpan_dev and wpan_phy was not yet fully clear to me. In order to clarify further your explanation and be sure that I understand it the correct way, I tried to picture the above explanation into a figure. Would you mind looking at it and tell me if something does not fit? https://bootlin.com/~miquel/ieee802154.pdf Thanks, Miquèl
Hi Jakub, kuba@kernel.org wrote on Tue, 4 Jan 2022 07:01:27 -0800: > On Tue, 4 Jan 2022 15:41:51 +0100 Miquel Raynal wrote: > > > On Wed, 22 Dec 2021 16:57:33 +0100 Miquel Raynal wrote: > > > > +/* Maximum number of PAN entries to store */ > > > > +static int max_pan_entries = 100; > > > > +module_param(max_pan_entries, uint, 0644); > > > > +MODULE_PARM_DESC(max_pan_entries, > > > > + "Maximum number of PANs to discover per scan (default is 100)"); > > > > + > > > > +static int pan_expiration = 60; > > > > +module_param(pan_expiration, uint, 0644); > > > > +MODULE_PARM_DESC(pan_expiration, > > > > + "Expiration of the scan validity in seconds (default is 60s)"); > > > > > > Can these be per-device control knobs? Module params are rarely the > > > best answer. > > > > I believe we can do that on a per FFD device basis (for now it will be > > on a per-device basis, but later when we will have the necessary > > information we might do something more fine grained). Would a couple of > > sysfs entries work? > > Is there no netlink object where this would fit? Sorry, I'm not at all > familiar with WPAN. If it's orthogonal to current cfg802154 objects > sysfs is fine, I guess. Yes it's definitely possible to add a netlink arg for these two parameters as well. Thanks, Miquèl
Hi, On Tue, 4 Jan 2022 at 10:05, Miquel Raynal <miquel.raynal@bootlin.com> wrote: > > Hi Alexander, > > alex.aring@gmail.com wrote on Tue, 28 Dec 2021 17:22:38 -0500: > > > Hi, > > > > On Wed, 22 Dec 2021 at 10:57, Miquel Raynal <miquel.raynal@bootlin.com> wrote: > > > > > > Let's introduce the basics of PAN management: > > > - structures defining PANs > > > - helpers for PANs registration > > > - helpers discarding old PANs > > > > > > > I think there exists a little misunderstanding about how the > > architecture is between the structures wpan_phy, wpan_dev and > > cfg802154. > > > > - wpan_phy: represents the PHY layer of IEEE 802154 and is a > > registered device class. > > - wpan_dev: represents the MAC layer of IEEE 802154 and is a netdev interface. > > > > You can have multiple wpan_dev operate on one wpan_phy. To my best > > knowledge it's like having multiple access points running on one phy > > (wireless) or macvlan on ethernet. You can actually do that with the > > mac802154_hwsim driver. However as there exists currently no (as my > > knowledge) hardware which supports e.g. multiple address filters we > > wanted to be prepared for to support such handling. Although, there > > exists some transceivers which support something like a "pan bridge" > > which goes into such a direction. > > > > What is a cfg802154 registered device? Well, at first it offers an > > interface between SoftMAC and HardMAC from nl802154, that's the > > cfg802154_ops structure. In theory a HardMAC transceiver would bypass > > the SoftMAC stack by implementing "cfg802154_ops" on the driver layer > > and try to do everything there as much as possible to support it. It > > is not a registered device class but the instance is tight to a > > wpan_phy. There can be multiple wpan_dev's (MAC layer instances on a > > phy/cfg802154 registered device). We currently don't support a HardMAC > > transceiver and I think because this misunderstanding came up. > > Thanks for the explanation, I think it helps because the relationship > between wpan_dev and wpan_phy was not yet fully clear to me. > > In order to clarify further your explanation and be sure that I > understand it the correct way, I tried to picture the above explanation > into a figure. Would you mind looking at it and tell me if something > does not fit? > > https://bootlin.com/~miquel/ieee802154.pdf I think so, yes... if a transceiver has e.g. two antennas/phy's it can also register two phy's and so on... then phy's can also move into net namespaces (like what we do for hwsim for routing testing [0]). Should keep that in mind. - Alex [0] https://github.com/linux-wpan/rpld/blob/nonstoring_mode/test/ns_setup
diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index 4f36003bca98..4402f93cda32 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -48,6 +48,24 @@ struct ieee802154_addr { }; }; +/** + * struct ieee802154_pan_desc - PAN descriptor information + * @coord: PAN ID and coordinator address + * @page: page this PAN is on + * @channel: channel this PAN is on + * @superframe_spec: SuperFrame specification as received + * @link_quality: link quality indicator at which the beacon was received + * @gts_permit: the PAN coordinator accepts GTS requests + */ +struct ieee802154_pan_desc { + struct ieee802154_addr *coord; + u8 page; + u8 channel; + u16 superframe_spec; + u8 link_quality; + bool gts_permit; +}; + struct cfg802154_ops { struct net_device * (*add_virtual_intf_deprecated)(struct wpan_phy *wpan_phy, const char *name, @@ -415,4 +433,17 @@ static inline const char *wpan_phy_name(struct wpan_phy *phy) return dev_name(&phy->dev); } +/** + * cfg802154_record_pan - Advertize a new PAN following a beacon's reception + * @wpan_phy: PHY receiving the beacon + * @pan: PAN descriptor + * + * Tells the internal pan management layer to either register this PAN if it is + * new or at least update its entry if already discovered. + * + * Returns 0 on success, a negative error code otherwise. + */ +int cfg802154_record_pan(struct wpan_phy *wpan_phy, + struct ieee802154_pan_desc *pan); + #endif /* __NET_CFG802154_H */ diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile index f05b7bdae2aa..6b7c66de730d 100644 --- a/net/ieee802154/Makefile +++ b/net/ieee802154/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_IEEE802154_SOCKET) += ieee802154_socket.o obj-y += 6lowpan/ ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \ - header_ops.o sysfs.o nl802154.o trace.o + header_ops.o sysfs.o nl802154.o pan.o trace.o ieee802154_socket-y := socket.o CFLAGS_trace.o := -I$(src) diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c index de259b5170ab..0f73e0571883 100644 --- a/net/ieee802154/core.c +++ b/net/ieee802154/core.c @@ -115,6 +115,8 @@ wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size) kfree(rdev); return NULL; } + spin_lock_init(&rdev->pan_lock); + INIT_LIST_HEAD(&rdev->pan_list); /* atomic_inc_return makes it start at 1, make it start at 0 */ rdev->wpan_phy_idx--; diff --git a/net/ieee802154/core.h b/net/ieee802154/core.h index 1c19f575d574..dea1d6f70489 100644 --- a/net/ieee802154/core.h +++ b/net/ieee802154/core.h @@ -22,6 +22,12 @@ struct cfg802154_registered_device { struct list_head wpan_dev_list; int devlist_generation, wpan_dev_id; + /* pan management */ + spinlock_t pan_lock; + struct list_head pan_list; + int pan_entries; + int pan_generation; + /* must be last because of the way we do wpan_phy_priv(), * and it should at least be aligned to NETDEV_ALIGN */ @@ -39,6 +45,17 @@ wpan_phy_to_rdev(struct wpan_phy *wpan_phy) extern struct list_head cfg802154_rdev_list; extern int cfg802154_rdev_list_generation; +struct cfg802154_internal_pan { + struct list_head list; + unsigned long discovery_ts; + struct ieee802154_pan_desc desc; +}; + +/* Always update the list by dropping the expired PANs before iterating */ +#define ieee802154_for_each_pan(pan, rdev) \ + cfg802154_expire_pans(rdev); \ + list_for_each_entry((pan), &(rdev)->pan_list, list) + int cfg802154_switch_netns(struct cfg802154_registered_device *rdev, struct net *net); /* free object */ @@ -47,4 +64,7 @@ struct cfg802154_registered_device * cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx); struct wpan_phy *wpan_phy_idx_to_wpan_phy(int wpan_phy_idx); +void cfg802154_expire_pans(struct cfg802154_registered_device *rdev); +void cfg802154_flush_pans(struct cfg802154_registered_device *rdev); + #endif /* __IEEE802154_CORE_H */ diff --git a/net/ieee802154/pan.c b/net/ieee802154/pan.c new file mode 100644 index 000000000000..c71a3664d5c3 --- /dev/null +++ b/net/ieee802154/pan.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IEEE 802.15.4 PAN management + * + * Copyright (C) Qorvo, 2021 + * Authors: + * - David Girault <david.girault@qorvo.com> + * - Miquel Raynal <miquel.raynal@bootlin.com> + */ + +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> + +#include <net/cfg802154.h> +#include <net/af_ieee802154.h> + +#include "ieee802154.h" +#include "core.h" + +/* Maximum number of PAN entries to store */ +static int max_pan_entries = 100; +module_param(max_pan_entries, uint, 0644); +MODULE_PARM_DESC(max_pan_entries, + "Maximum number of PANs to discover per scan (default is 100)"); + +static int pan_expiration = 60; +module_param(pan_expiration, uint, 0644); +MODULE_PARM_DESC(pan_expiration, + "Expiration of the scan validity in seconds (default is 60s)"); + +static struct cfg802154_internal_pan * +cfg802154_alloc_pan(struct ieee802154_pan_desc *desc) +{ + struct cfg802154_internal_pan *new; + struct ieee802154_addr *coord; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) + return ERR_PTR(-ENOMEM); + + coord = kzalloc(sizeof(*coord), GFP_KERNEL); + if (!coord) { + kfree(new); + return ERR_PTR(-ENOMEM); + } + + new->discovery_ts = jiffies; + new->desc = *desc; + + *coord = *desc->coord; + new->desc.coord = coord; + + return new; +} + +static void cfg802154_free_pan(struct cfg802154_internal_pan *pan) +{ + kfree(pan->desc.coord); + kfree(pan); +} + +static void cfg802154_unlink_pan(struct cfg802154_registered_device *rdev, + struct cfg802154_internal_pan *pan) +{ + lockdep_assert_held(&rdev->pan_lock); + + list_del(&pan->list); + cfg802154_free_pan(pan); + rdev->pan_entries--; + rdev->pan_generation++; +} + +static void cfg802154_link_pan(struct cfg802154_registered_device *rdev, + struct cfg802154_internal_pan *pan) +{ + lockdep_assert_held(&rdev->pan_lock); + + list_add_tail(&pan->list, &rdev->pan_list); + rdev->pan_entries++; + rdev->pan_generation++; +} + +void cfg802154_expire_pans(struct cfg802154_registered_device *rdev) +{ + unsigned long expiration_time = jiffies - (pan_expiration * HZ); + struct cfg802154_internal_pan *pan, *tmp; + + lockdep_assert_held(&rdev->pan_lock); + + list_for_each_entry_safe(pan, tmp, &rdev->pan_list, list) { + if (!time_after(expiration_time, pan->discovery_ts)) + continue; + + cfg802154_unlink_pan(rdev, pan); + } +} +EXPORT_SYMBOL(cfg802154_expire_pans); + +static void cfg802154_expire_oldest_pan(struct cfg802154_registered_device *rdev) +{ + struct cfg802154_internal_pan *pan, *oldest; + + lockdep_assert_held(&rdev->pan_lock); + + if (WARN_ON(!max_pan_entries || list_empty(&rdev->pan_list))) + return; + + oldest = list_first_entry(&rdev->pan_list, + struct cfg802154_internal_pan, list); + + list_for_each_entry(pan, &rdev->pan_list, list) { + if (!time_before(oldest->discovery_ts, pan->discovery_ts)) + oldest = pan; + } + + cfg802154_unlink_pan(rdev, oldest); +} + +void cfg802154_flush_pans(struct cfg802154_registered_device *rdev) +{ + struct cfg802154_internal_pan *pan, *tmp; + + lockdep_assert_held(&rdev->pan_lock); + + list_for_each_entry_safe(pan, tmp, &rdev->pan_list, list) + cfg802154_unlink_pan(rdev, pan); +} +EXPORT_SYMBOL(cfg802154_flush_pans); + +static bool cfg802154_same_pan(struct ieee802154_pan_desc *a, + struct ieee802154_pan_desc *b) +{ + int ret; + + if (a->page != b->page) + return false; + + if (a->channel != b->channel) + return false; + + ret = memcmp(&a->coord->pan_id, &b->coord->pan_id, + sizeof(a->coord->pan_id)); + if (ret) + return false; + + if (a->coord->mode != b->coord->mode) + return false; + + if (a->coord->mode == IEEE802154_ADDR_SHORT) + ret = memcmp(&a->coord->short_addr, &b->coord->short_addr, + IEEE802154_SHORT_ADDR_LEN); + else + ret = memcmp(&a->coord->extended_addr, &b->coord->extended_addr, + IEEE802154_EXTENDED_ADDR_LEN); + + return true; +} + +static struct cfg802154_internal_pan * +cfg802154_find_matching_pan(struct cfg802154_registered_device *rdev, + struct cfg802154_internal_pan *tmp) +{ + struct cfg802154_internal_pan *pan; + + list_for_each_entry(pan, &rdev->pan_list, list) { + if (cfg802154_same_pan(&pan->desc, &tmp->desc)) + return pan; + } + + return NULL; +} + +static void cfg802154_pan_update(struct cfg802154_registered_device *rdev, + struct cfg802154_internal_pan *new) +{ + struct cfg802154_internal_pan *found; + + spin_lock_bh(&rdev->pan_lock); + + found = cfg802154_find_matching_pan(rdev, new); + if (found) + cfg802154_unlink_pan(rdev, found); + + if (unlikely(rdev->pan_entries >= max_pan_entries)) + cfg802154_expire_oldest_pan(rdev); + + cfg802154_link_pan(rdev, new); + + spin_unlock_bh(&rdev->pan_lock); +} + +int cfg802154_record_pan(struct wpan_phy *wpan_phy, + struct ieee802154_pan_desc *desc) +{ + struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(wpan_phy); + struct cfg802154_internal_pan *new; + + new = cfg802154_alloc_pan(desc); + if (IS_ERR(new)) + return (PTR_ERR(new)); + + cfg802154_pan_update(rdev, new); + + return 0; +} +EXPORT_SYMBOL(cfg802154_record_pan);