* [PATCH 0/3] using guard/__free in networking
@ 2024-03-25 22:31 Johannes Berg
2024-03-25 22:31 ` [PATCH 1/3] rtnetlink: add guard for RTNL Johannes Berg
` (3 more replies)
0 siblings, 4 replies; 19+ messages in thread
From: Johannes Berg @ 2024-03-25 22:31 UTC (permalink / raw)
To: netdev
Hi,
So I started playing with this for wifi, and overall that
does look pretty nice, but it's a bit weird if we can do
guard(wiphy)(&rdev->wiphy);
or so, but still have to manually handle the RTNL in the
same code.
Hence these patches. The third one is a bit more RFC than
the others, I guess. It's also not tested, I'm not sure I
even know how to hit all the BPF parts.
johannes
^ permalink raw reply [flat|nested] 19+ messages in thread* [PATCH 1/3] rtnetlink: add guard for RTNL 2024-03-25 22:31 [PATCH 0/3] using guard/__free in networking Johannes Berg @ 2024-03-25 22:31 ` Johannes Berg 2024-03-25 22:31 ` [PATCH 2/3] netdevice: add DEFINE_FREE() for dev_put Johannes Berg ` (2 subsequent siblings) 3 siblings, 0 replies; 19+ messages in thread From: Johannes Berg @ 2024-03-25 22:31 UTC (permalink / raw) To: netdev; +Cc: Johannes Berg From: Johannes Berg <johannes.berg@intel.com> The new guard/scoped_gard can be useful for the RTNL as well, so add a guard definition for it. It gets used like { guard(rtnl)(); // RTNL held until end of block } or scoped_guard(rtnl) { // RTNL held in this block } as with any other guard/scoped_guard. Signed-off-by: Johannes Berg <johannes.berg@intel.com> --- include/linux/rtnetlink.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index cdfc897f1e3c..a7da7dfc06a2 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -7,6 +7,7 @@ #include <linux/netdevice.h> #include <linux/wait.h> #include <linux/refcount.h> +#include <linux/cleanup.h> #include <uapi/linux/rtnetlink.h> extern int rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, u32 group, int echo); @@ -46,6 +47,8 @@ extern int rtnl_is_locked(void); extern int rtnl_lock_killable(void); extern bool refcount_dec_and_rtnl_lock(refcount_t *r); +DEFINE_LOCK_GUARD_0(rtnl, rtnl_lock(), rtnl_unlock()) + extern wait_queue_head_t netdev_unregistering_wq; extern atomic_t dev_unreg_count; extern struct rw_semaphore pernet_ops_rwsem; -- 2.44.0 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 2/3] netdevice: add DEFINE_FREE() for dev_put 2024-03-25 22:31 [PATCH 0/3] using guard/__free in networking Johannes Berg 2024-03-25 22:31 ` [PATCH 1/3] rtnetlink: add guard for RTNL Johannes Berg @ 2024-03-25 22:31 ` Johannes Berg 2024-03-25 22:31 ` [PATCH 3/3] net: core: use guard/__free in core dev code Johannes Berg 2024-03-26 2:09 ` [PATCH 0/3] using guard/__free in networking Jakub Kicinski 3 siblings, 0 replies; 19+ messages in thread From: Johannes Berg @ 2024-03-25 22:31 UTC (permalink / raw) To: netdev; +Cc: Johannes Berg From: Johannes Berg <johannes.berg@intel.com> For short netdev holds within a function there are still a lot of users of dev_put() rather than netdev_put(). Add DEFINE_FREE() to allow making those safer. Signed-off-by: Johannes Berg <johannes.berg@intel.com> --- include/linux/netdevice.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index cb37817d6382..f6c0d731fa35 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -4127,6 +4127,8 @@ static inline void dev_put(struct net_device *dev) netdev_put(dev, NULL); } +DEFINE_FREE(dev_put, struct net_device *, if (_T) dev_put(_T)) + static inline void netdev_ref_replace(struct net_device *odev, struct net_device *ndev, netdevice_tracker *tracker, -- 2.44.0 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH 3/3] net: core: use guard/__free in core dev code 2024-03-25 22:31 [PATCH 0/3] using guard/__free in networking Johannes Berg 2024-03-25 22:31 ` [PATCH 1/3] rtnetlink: add guard for RTNL Johannes Berg 2024-03-25 22:31 ` [PATCH 2/3] netdevice: add DEFINE_FREE() for dev_put Johannes Berg @ 2024-03-25 22:31 ` Johannes Berg 2024-03-26 2:09 ` [PATCH 0/3] using guard/__free in networking Jakub Kicinski 3 siblings, 0 replies; 19+ messages in thread From: Johannes Berg @ 2024-03-25 22:31 UTC (permalink / raw) To: netdev; +Cc: Johannes Berg From: Johannes Berg <johannes.berg@intel.com> Simplify the code a bit, mostly also to illustrate the new helpers. Signed-off-by: Johannes Berg <johannes.berg@intel.com> --- net/core/dev.c | 182 +++++++++++++++++++------------------------------ 1 file changed, 72 insertions(+), 110 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 9a67003e49db..f4f4627bb1e3 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1367,9 +1367,8 @@ EXPORT_SYMBOL(__netdev_notify_peers); */ void netdev_notify_peers(struct net_device *dev) { - rtnl_lock(); + guard(rtnl)(); __netdev_notify_peers(dev); - rtnl_unlock(); } EXPORT_SYMBOL(netdev_notify_peers); @@ -1722,30 +1721,27 @@ int register_netdevice_notifier(struct notifier_block *nb) int err; /* Close race with setup_net() and cleanup_net() */ - down_write(&pernet_ops_rwsem); - rtnl_lock(); + guard(rwsem_write)(&pernet_ops_rwsem); + guard(rtnl)(); err = raw_notifier_chain_register(&netdev_chain, nb); if (err) - goto unlock; + return err; if (dev_boot_phase) - goto unlock; + return 0; for_each_net(net) { err = call_netdevice_register_net_notifiers(nb, net); if (err) goto rollback; } -unlock: - rtnl_unlock(); - up_write(&pernet_ops_rwsem); - return err; + return 0; rollback: for_each_net_continue_reverse(net) call_netdevice_unregister_net_notifiers(nb, net); raw_notifier_chain_unregister(&netdev_chain, nb); - goto unlock; + return err; } EXPORT_SYMBOL(register_netdevice_notifier); @@ -1769,19 +1765,16 @@ int unregister_netdevice_notifier(struct notifier_block *nb) int err; /* Close race with setup_net() and cleanup_net() */ - down_write(&pernet_ops_rwsem); - rtnl_lock(); + guard(rwsem_write)(&pernet_ops_rwsem); + guard(rtnl)(); err = raw_notifier_chain_unregister(&netdev_chain, nb); if (err) - goto unlock; + return err; for_each_net(net) call_netdevice_unregister_net_notifiers(nb, net); -unlock: - rtnl_unlock(); - up_write(&pernet_ops_rwsem); - return err; + return 0; } EXPORT_SYMBOL(unregister_netdevice_notifier); @@ -1838,12 +1831,9 @@ static int __unregister_netdevice_notifier_net(struct net *net, int register_netdevice_notifier_net(struct net *net, struct notifier_block *nb) { - int err; + guard(rtnl)(); - rtnl_lock(); - err = __register_netdevice_notifier_net(net, nb, false); - rtnl_unlock(); - return err; + return __register_netdevice_notifier_net(net, nb, false); } EXPORT_SYMBOL(register_netdevice_notifier_net); @@ -1866,12 +1856,9 @@ EXPORT_SYMBOL(register_netdevice_notifier_net); int unregister_netdevice_notifier_net(struct net *net, struct notifier_block *nb) { - int err; + guard(rtnl)(); - rtnl_lock(); - err = __unregister_netdevice_notifier_net(net, nb); - rtnl_unlock(); - return err; + return __unregister_netdevice_notifier_net(net, nb); } EXPORT_SYMBOL(unregister_netdevice_notifier_net); @@ -1889,14 +1876,14 @@ int register_netdevice_notifier_dev_net(struct net_device *dev, { int err; - rtnl_lock(); + guard(rtnl)(); err = __register_netdevice_notifier_net(dev_net(dev), nb, false); - if (!err) { - nn->nb = nb; - list_add(&nn->list, &dev->net_notifier_list); - } - rtnl_unlock(); - return err; + if (err) + return err; + + nn->nb = nb; + list_add(&nn->list, &dev->net_notifier_list); + return 0; } EXPORT_SYMBOL(register_netdevice_notifier_dev_net); @@ -1904,13 +1891,10 @@ int unregister_netdevice_notifier_dev_net(struct net_device *dev, struct notifier_block *nb, struct netdev_net_notifier *nn) { - int err; + guard(rtnl)(); - rtnl_lock(); list_del(&nn->list); - err = __unregister_netdevice_notifier_net(dev_net(dev), nb); - rtnl_unlock(); - return err; + return __unregister_netdevice_notifier_net(dev_net(dev), nb); } EXPORT_SYMBOL(unregister_netdevice_notifier_dev_net); @@ -9453,7 +9437,7 @@ static void bpf_xdp_link_release(struct bpf_link *link) { struct bpf_xdp_link *xdp_link = container_of(link, struct bpf_xdp_link, link); - rtnl_lock(); + guard(rtnl)(); /* if racing with net_device's tear down, xdp_link->dev might be * already NULL, in which case link was already auto-detached @@ -9462,8 +9446,6 @@ static void bpf_xdp_link_release(struct bpf_link *link) WARN_ON(dev_xdp_detach_link(xdp_link->dev, NULL, xdp_link)); xdp_link->dev = NULL; } - - rtnl_unlock(); } static int bpf_xdp_link_detach(struct bpf_link *link) @@ -9485,10 +9467,10 @@ static void bpf_xdp_link_show_fdinfo(const struct bpf_link *link, struct bpf_xdp_link *xdp_link = container_of(link, struct bpf_xdp_link, link); u32 ifindex = 0; - rtnl_lock(); - if (xdp_link->dev) - ifindex = xdp_link->dev->ifindex; - rtnl_unlock(); + scoped_guard(rtnl) { + if (xdp_link->dev) + ifindex = xdp_link->dev->ifindex; + } seq_printf(seq, "ifindex:\t%u\n", ifindex); } @@ -9499,10 +9481,10 @@ static int bpf_xdp_link_fill_link_info(const struct bpf_link *link, struct bpf_xdp_link *xdp_link = container_of(link, struct bpf_xdp_link, link); u32 ifindex = 0; - rtnl_lock(); - if (xdp_link->dev) - ifindex = xdp_link->dev->ifindex; - rtnl_unlock(); + scoped_guard(rtnl) { + if (xdp_link->dev) + ifindex = xdp_link->dev->ifindex; + } info->xdp.ifindex = ifindex; return 0; @@ -9514,31 +9496,26 @@ static int bpf_xdp_link_update(struct bpf_link *link, struct bpf_prog *new_prog, struct bpf_xdp_link *xdp_link = container_of(link, struct bpf_xdp_link, link); enum bpf_xdp_mode mode; bpf_op_t bpf_op; - int err = 0; + int err; - rtnl_lock(); + guard(rtnl)(); /* link might have been auto-released already, so fail */ - if (!xdp_link->dev) { - err = -ENOLINK; - goto out_unlock; - } + if (!xdp_link->dev) + return -ENOLINK; + + if (old_prog && link->prog != old_prog) + return -EPERM; - if (old_prog && link->prog != old_prog) { - err = -EPERM; - goto out_unlock; - } old_prog = link->prog; if (old_prog->type != new_prog->type || - old_prog->expected_attach_type != new_prog->expected_attach_type) { - err = -EINVAL; - goto out_unlock; - } + old_prog->expected_attach_type != new_prog->expected_attach_type) + return -EINVAL; if (old_prog == new_prog) { /* no-op, don't disturb drivers */ bpf_prog_put(new_prog); - goto out_unlock; + return 0; } mode = dev_xdp_mode(xdp_link->dev, xdp_link->flags); @@ -9546,14 +9523,11 @@ static int bpf_xdp_link_update(struct bpf_link *link, struct bpf_prog *new_prog, err = dev_xdp_install(xdp_link->dev, mode, bpf_op, NULL, xdp_link->flags, new_prog); if (err) - goto out_unlock; + return err; old_prog = xchg(&link->prog, new_prog); bpf_prog_put(old_prog); - -out_unlock: - rtnl_unlock(); - return err; + return 0; } static const struct bpf_link_ops bpf_xdp_link_lops = { @@ -9567,57 +9541,47 @@ static const struct bpf_link_ops bpf_xdp_link_lops = { int bpf_xdp_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) { + /* link itself doesn't hold dev's refcnt to not complicate shutdown */ + struct net_device *dev __free(dev_put) = NULL; struct net *net = current->nsproxy->net_ns; struct bpf_link_primer link_primer; struct netlink_ext_ack extack = {}; struct bpf_xdp_link *link; - struct net_device *dev; - int err, fd; + int err; - rtnl_lock(); - dev = dev_get_by_index(net, attr->link_create.target_ifindex); - if (!dev) { - rtnl_unlock(); - return -EINVAL; - } + scoped_guard(rtnl) { + dev = dev_get_by_index(net, + attr->link_create.target_ifindex); + if (!dev) + return -EINVAL; - link = kzalloc(sizeof(*link), GFP_USER); - if (!link) { - err = -ENOMEM; - goto unlock; - } + link = kzalloc(sizeof(*link), GFP_USER); + if (!link) + return -ENOMEM; - bpf_link_init(&link->link, BPF_LINK_TYPE_XDP, &bpf_xdp_link_lops, prog); - link->dev = dev; - link->flags = attr->link_create.flags; + bpf_link_init(&link->link, BPF_LINK_TYPE_XDP, + &bpf_xdp_link_lops, prog); - err = bpf_link_prime(&link->link, &link_primer); - if (err) { - kfree(link); - goto unlock; - } + link->dev = dev; + link->flags = attr->link_create.flags; - err = dev_xdp_attach_link(dev, &extack, link); - rtnl_unlock(); + err = bpf_link_prime(&link->link, &link_primer); + if (err) { + kfree(link); + return err; + } + + err = dev_xdp_attach_link(dev, &extack, link); + } if (err) { link->dev = NULL; bpf_link_cleanup(&link_primer); trace_bpf_xdp_link_attach_failed(extack._msg); - goto out_put_dev; + return err; } - fd = bpf_link_settle(&link_primer); - /* link itself doesn't hold dev's refcnt to not complicate shutdown */ - dev_put(dev); - return fd; - -unlock: - rtnl_unlock(); - -out_put_dev: - dev_put(dev); - return err; + return bpf_link_settle(&link_primer); } /** @@ -11171,9 +11135,8 @@ EXPORT_SYMBOL(unregister_netdevice_many); */ void unregister_netdev(struct net_device *dev) { - rtnl_lock(); + guard(rtnl)(); unregister_netdevice(dev); - rtnl_unlock(); } EXPORT_SYMBOL(unregister_netdev); @@ -11617,7 +11580,7 @@ static void __net_exit default_device_exit_batch(struct list_head *net_list) struct net *net; LIST_HEAD(dev_kill_list); - rtnl_lock(); + guard(rtnl)(); list_for_each_entry(net, net_list, exit_list) { default_device_exit_net(net); cond_resched(); @@ -11632,7 +11595,6 @@ static void __net_exit default_device_exit_batch(struct list_head *net_list) } } unregister_netdevice_many(&dev_kill_list); - rtnl_unlock(); } static struct pernet_operations __net_initdata default_device_ops = { -- 2.44.0 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH 0/3] using guard/__free in networking 2024-03-25 22:31 [PATCH 0/3] using guard/__free in networking Johannes Berg ` (2 preceding siblings ...) 2024-03-25 22:31 ` [PATCH 3/3] net: core: use guard/__free in core dev code Johannes Berg @ 2024-03-26 2:09 ` Jakub Kicinski 2024-03-26 8:42 ` Johannes Berg 2024-03-26 15:33 ` Andrew Lunn 3 siblings, 2 replies; 19+ messages in thread From: Jakub Kicinski @ 2024-03-26 2:09 UTC (permalink / raw) To: Johannes Berg; +Cc: netdev On Mon, 25 Mar 2024 23:31:25 +0100 Johannes Berg wrote: > Hi, > > So I started playing with this for wifi, and overall that > does look pretty nice, but it's a bit weird if we can do > > guard(wiphy)(&rdev->wiphy); > > or so, but still have to manually handle the RTNL in the > same code. Dunno, it locks code instead of data accesses. Forgive the comparison but it feels too much like Java to me :) scoped_guard is fine, the guard() not so much. But happy for other netdev maintainers to override me.. Do you have a piece of code in wireless where the conversion made you go "wow, this is so much cleaner"? ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/3] using guard/__free in networking 2024-03-26 2:09 ` [PATCH 0/3] using guard/__free in networking Jakub Kicinski @ 2024-03-26 8:42 ` Johannes Berg 2024-03-26 14:37 ` Jakub Kicinski 2024-03-26 15:33 ` Andrew Lunn 1 sibling, 1 reply; 19+ messages in thread From: Johannes Berg @ 2024-03-26 8:42 UTC (permalink / raw) To: Jakub Kicinski; +Cc: netdev On Mon, 2024-03-25 at 19:09 -0700, Jakub Kicinski wrote: > On Mon, 25 Mar 2024 23:31:25 +0100 Johannes Berg wrote: > > Hi, > > > > So I started playing with this for wifi, and overall that > > does look pretty nice, but it's a bit weird if we can do > > > > guard(wiphy)(&rdev->wiphy); > > > > or so, but still have to manually handle the RTNL in the > > same code. > > Dunno, it locks code instead of data accesses. Well, I'm not sure that's a fair complaint. After all, without any more compiler help, even rtnl_lock()/rtnl_unlock() _necessarily_ locks code. Clearly rtnl_lock(); // something rtnl_unlock(); also locks the "// something" code, after all., and yeah that might be doing data accesses, but it might also be a function call or a whole bunch of other things? Or if you look at something like bpf_xdp_link_attach(), I don't think you can really say that it locks only data. That doesn't even do the allocation outside the lock (though I did convert that one to scoped_guard because of that.) Or even something simple like unregister_netdev(), it just requires the RTNL for some data accesses and consistency deep inside unregister_netdevice(), not for any specific data accessed there. So yeah, this is always going to be a trade-off, but all the locking is. We even make similar trade-offs manually, e.g. look at bpf_xdp_link_update(), it will do the bpf_prog_put() under the RTNL still, for no good reason other than simplifying the cleanup path there. Anyway, I can live with it either way (unless you tell me you won't pull wireless code using guard), just thought doing the wireless locking with guard and the RTNL around it without it (only in a few places do we still use RTNL though) looked odd. > Forgive the comparison but it feels too much like Java to me :) Heh. Haven't used Java in 20 years or so... > scoped_guard is fine, the guard() not so much. I think you can't get scoped_guard() without guard(), so does that mean you'd accept the first patch in the series? > Do you have a piece of code in wireless where the conversion > made you go "wow, this is so much cleaner"? Mostly long and complex error paths. Found a double-unlock bug (in iwlwifi) too, when converting some locking there. Doing a more broader conversion on cfg80211/mac80211 removes around 200 lines of unlocking, mostly error handling, code. Doing __free() too will probably clean up even more. johannes ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/3] using guard/__free in networking 2024-03-26 8:42 ` Johannes Berg @ 2024-03-26 14:37 ` Jakub Kicinski 2024-03-26 15:23 ` Willem de Bruijn ` (2 more replies) 0 siblings, 3 replies; 19+ messages in thread From: Jakub Kicinski @ 2024-03-26 14:37 UTC (permalink / raw) To: Johannes Berg; +Cc: netdev On Tue, 26 Mar 2024 09:42:43 +0100 Johannes Berg wrote: > On Mon, 2024-03-25 at 19:09 -0700, Jakub Kicinski wrote: > > On Mon, 25 Mar 2024 23:31:25 +0100 Johannes Berg wrote: > > > Hi, > > > > > > So I started playing with this for wifi, and overall that > > > does look pretty nice, but it's a bit weird if we can do > > > > > > guard(wiphy)(&rdev->wiphy); > > > > > > or so, but still have to manually handle the RTNL in the > > > same code. > > > > Dunno, it locks code instead of data accesses. > > Well, I'm not sure that's a fair complaint. After all, without any more > compiler help, even rtnl_lock()/rtnl_unlock() _necessarily_ locks code. > Clearly > > rtnl_lock(); > // something > rtnl_unlock(); > > also locks the "// something" code, after all., and yeah that might be > doing data accesses, but it might also be a function call or a whole > bunch of other things? > > Or if you look at something like bpf_xdp_link_attach(), I don't think > you can really say that it locks only data. That doesn't even do the > allocation outside the lock (though I did convert that one to > scoped_guard because of that.) > > Or even something simple like unregister_netdev(), it just requires the > RTNL for some data accesses and consistency deep inside > unregister_netdevice(), not for any specific data accessed there. > > So yeah, this is always going to be a trade-off, but all the locking is. > We even make similar trade-offs manually, e.g. look at > bpf_xdp_link_update(), it will do the bpf_prog_put() under the RTNL > still, for no good reason other than simplifying the cleanup path there. At least to me the mental model is different. 99% of the time the guard is covering the entire body. So now we're moving from "I'm touching X so I need to lock" to "This _function_ is safe to touch X". > Anyway, I can live with it either way (unless you tell me you won't pull > wireless code using guard), just thought doing the wireless locking with > guard and the RTNL around it without it (only in a few places do we > still use RTNL though) looked odd. > > > > Forgive the comparison but it feels too much like Java to me :) > > Heh. Haven't used Java in 20 years or so... I only did at uni, but I think they had a decorator for a method, where you can basically say "this method should be under lock X" and runtime will take that lock before entering and drop it after exit, appropriately. I wonder why the sudden love for this concept :S Is it also present in Rust or some such? > > scoped_guard is fine, the guard() not so much. > > I think you can't get scoped_guard() without guard(), so does that mean > you'd accept the first patch in the series? How can we get one without the other.. do you reckon Joe P would let us add a checkpatch check to warn people against pure guard() under net/ ? > > Do you have a piece of code in wireless where the conversion > > made you go "wow, this is so much cleaner"? > > Mostly long and complex error paths. Found a double-unlock bug (in > iwlwifi) too, when converting some locking there. > > Doing a more broader conversion on cfg80211/mac80211 removes around 200 > lines of unlocking, mostly error handling, code. > > Doing __free() too will probably clean up even more. Not super convinced by that one either: https://lore.kernel.org/all/20240321185640.6f7f4d6b@kernel.org/ maybe I'm too conservative.. ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/3] using guard/__free in networking 2024-03-26 14:37 ` Jakub Kicinski @ 2024-03-26 15:23 ` Willem de Bruijn 2024-03-26 17:33 ` Stanislav Fomichev 2024-03-29 10:23 ` Simon Horman 2024-03-26 15:33 ` Johannes Berg 2024-03-27 11:15 ` Przemek Kitszel 2 siblings, 2 replies; 19+ messages in thread From: Willem de Bruijn @ 2024-03-26 15:23 UTC (permalink / raw) To: Jakub Kicinski, Johannes Berg; +Cc: netdev Jakub Kicinski wrote: > On Tue, 26 Mar 2024 09:42:43 +0100 Johannes Berg wrote: > > On Mon, 2024-03-25 at 19:09 -0700, Jakub Kicinski wrote: > > > On Mon, 25 Mar 2024 23:31:25 +0100 Johannes Berg wrote: > > > > Hi, > > > > > > > > So I started playing with this for wifi, and overall that > > > > does look pretty nice, but it's a bit weird if we can do > > > > > > > > guard(wiphy)(&rdev->wiphy); > > > > > > > > or so, but still have to manually handle the RTNL in the > > > > same code. > > > > > > Dunno, it locks code instead of data accesses. > > > > Well, I'm not sure that's a fair complaint. After all, without any more > > compiler help, even rtnl_lock()/rtnl_unlock() _necessarily_ locks code. > > Clearly > > > > rtnl_lock(); > > // something > > rtnl_unlock(); > > > > also locks the "// something" code, after all., and yeah that might be > > doing data accesses, but it might also be a function call or a whole > > bunch of other things? > > > > Or if you look at something like bpf_xdp_link_attach(), I don't think > > you can really say that it locks only data. That doesn't even do the > > allocation outside the lock (though I did convert that one to > > scoped_guard because of that.) > > > > Or even something simple like unregister_netdev(), it just requires the > > RTNL for some data accesses and consistency deep inside > > unregister_netdevice(), not for any specific data accessed there. > > > > So yeah, this is always going to be a trade-off, but all the locking is. > > We even make similar trade-offs manually, e.g. look at > > bpf_xdp_link_update(), it will do the bpf_prog_put() under the RTNL > > still, for no good reason other than simplifying the cleanup path there. > > At least to me the mental model is different. 99% of the time the guard > is covering the entire body. So now we're moving from "I'm touching X > so I need to lock" to "This _function_ is safe to touch X". > > > Anyway, I can live with it either way (unless you tell me you won't pull > > wireless code using guard), just thought doing the wireless locking with > > guard and the RTNL around it without it (only in a few places do we > > still use RTNL though) looked odd. > > > > > > > Forgive the comparison but it feels too much like Java to me :) > > > > Heh. Haven't used Java in 20 years or so... > > I only did at uni, but I think they had a decorator for a method, where > you can basically say "this method should be under lock X" and runtime > will take that lock before entering and drop it after exit, > appropriately. I wonder why the sudden love for this concept :S > Is it also present in Rust or some such? > > > > scoped_guard is fine, the guard() not so much. > > > > I think you can't get scoped_guard() without guard(), so does that mean > > you'd accept the first patch in the series? > > How can we get one without the other.. do you reckon Joe P would let us > add a checkpatch check to warn people against pure guard() under net/ ? > > > > Do you have a piece of code in wireless where the conversion > > > made you go "wow, this is so much cleaner"? > > > > Mostly long and complex error paths. Found a double-unlock bug (in > > iwlwifi) too, when converting some locking there. > > > > Doing a more broader conversion on cfg80211/mac80211 removes around 200 > > lines of unlocking, mostly error handling, code. > > > > Doing __free() too will probably clean up even more. > > Not super convinced by that one either: > https://lore.kernel.org/all/20240321185640.6f7f4d6b@kernel.org/ > maybe I'm too conservative.. +1 on the concept (fwiw). Even the simple examples, such as unregister_netdevice_notifier_net, show how it avoids boilerplate and so simplifies control flow. That benefit multiplies with the number of resources held and number of exit paths. Or in our case, gotos and (unlock) labels. Error paths are notorious for seeing little test coverage and leaking resources. This is an easy class of bugs that this RAII squashes. Sprinkling guard statements anywhere in the scope itself makes it perhaps hard to follow. Perhaps a heuristic would be to require these statements at the start of scope (after variable declaration)? Function level decorators could further inform static analysis. But that is somewhat tangential. ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/3] using guard/__free in networking 2024-03-26 15:23 ` Willem de Bruijn @ 2024-03-26 17:33 ` Stanislav Fomichev 2024-03-29 10:23 ` Simon Horman 1 sibling, 0 replies; 19+ messages in thread From: Stanislav Fomichev @ 2024-03-26 17:33 UTC (permalink / raw) To: Willem de Bruijn; +Cc: Jakub Kicinski, Johannes Berg, netdev On 03/26, Willem de Bruijn wrote: > Jakub Kicinski wrote: > > On Tue, 26 Mar 2024 09:42:43 +0100 Johannes Berg wrote: > > > On Mon, 2024-03-25 at 19:09 -0700, Jakub Kicinski wrote: > > > > On Mon, 25 Mar 2024 23:31:25 +0100 Johannes Berg wrote: > > > > > Hi, > > > > > > > > > > So I started playing with this for wifi, and overall that > > > > > does look pretty nice, but it's a bit weird if we can do > > > > > > > > > > guard(wiphy)(&rdev->wiphy); > > > > > > > > > > or so, but still have to manually handle the RTNL in the > > > > > same code. > > > > > > > > Dunno, it locks code instead of data accesses. > > > > > > Well, I'm not sure that's a fair complaint. After all, without any more > > > compiler help, even rtnl_lock()/rtnl_unlock() _necessarily_ locks code. > > > Clearly > > > > > > rtnl_lock(); > > > // something > > > rtnl_unlock(); > > > > > > also locks the "// something" code, after all., and yeah that might be > > > doing data accesses, but it might also be a function call or a whole > > > bunch of other things? > > > > > > Or if you look at something like bpf_xdp_link_attach(), I don't think > > > you can really say that it locks only data. That doesn't even do the > > > allocation outside the lock (though I did convert that one to > > > scoped_guard because of that.) > > > > > > Or even something simple like unregister_netdev(), it just requires the > > > RTNL for some data accesses and consistency deep inside > > > unregister_netdevice(), not for any specific data accessed there. > > > > > > So yeah, this is always going to be a trade-off, but all the locking is. > > > We even make similar trade-offs manually, e.g. look at > > > bpf_xdp_link_update(), it will do the bpf_prog_put() under the RTNL > > > still, for no good reason other than simplifying the cleanup path there. > > > > At least to me the mental model is different. 99% of the time the guard > > is covering the entire body. So now we're moving from "I'm touching X > > so I need to lock" to "This _function_ is safe to touch X". > > > > > Anyway, I can live with it either way (unless you tell me you won't pull > > > wireless code using guard), just thought doing the wireless locking with > > > guard and the RTNL around it without it (only in a few places do we > > > still use RTNL though) looked odd. > > > > > > > > > > Forgive the comparison but it feels too much like Java to me :) > > > > > > Heh. Haven't used Java in 20 years or so... > > > > I only did at uni, but I think they had a decorator for a method, where > > you can basically say "this method should be under lock X" and runtime > > will take that lock before entering and drop it after exit, > > appropriately. I wonder why the sudden love for this concept :S > > Is it also present in Rust or some such? It's more of a c++ thing I believe: https://en.cppreference.com/w/cpp/thread/lock_guard Not that anybody is asking my opinion (and my mind has been a bit corrupted by c++), but guard() syntax seems fine :-p Rust's approach is more conventional. There is a mtx.lock() method that returns a scoped guard that can be optionally unlock'ed IIRC. > > > > scoped_guard is fine, the guard() not so much. > > > > > > I think you can't get scoped_guard() without guard(), so does that mean > > > you'd accept the first patch in the series? > > > > How can we get one without the other.. do you reckon Joe P would let us > > add a checkpatch check to warn people against pure guard() under net/ ? > > > > > > Do you have a piece of code in wireless where the conversion > > > > made you go "wow, this is so much cleaner"? > > > > > > Mostly long and complex error paths. Found a double-unlock bug (in > > > iwlwifi) too, when converting some locking there. > > > > > > Doing a more broader conversion on cfg80211/mac80211 removes around 200 > > > lines of unlocking, mostly error handling, code. > > > > > > Doing __free() too will probably clean up even more. > > > > Not super convinced by that one either: > > https://lore.kernel.org/all/20240321185640.6f7f4d6b@kernel.org/ > > maybe I'm too conservative.. > > +1 on the concept (fwiw). > > Even the simple examples, such as unregister_netdevice_notifier_net, > show how it avoids boilerplate and so simplifies control flow. > > That benefit multiplies with the number of resources held and number > of exit paths. Or in our case, gotos and (unlock) labels. > > Error paths are notorious for seeing little test coverage and leaking > resources. This is an easy class of bugs that this RAII squashes. > > Sprinkling guard statements anywhere in the scope itself makes it > perhaps hard to follow. Perhaps a heuristic would be to require these > statements at the start of scope (after variable declaration)? > > Function level decorators could further inform static analysis. > But that is somewhat tangential. ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/3] using guard/__free in networking 2024-03-26 15:23 ` Willem de Bruijn 2024-03-26 17:33 ` Stanislav Fomichev @ 2024-03-29 10:23 ` Simon Horman 1 sibling, 0 replies; 19+ messages in thread From: Simon Horman @ 2024-03-29 10:23 UTC (permalink / raw) To: Willem de Bruijn; +Cc: Jakub Kicinski, Johannes Berg, netdev On Tue, Mar 26, 2024 at 11:23:19AM -0400, Willem de Bruijn wrote: > Jakub Kicinski wrote: > > On Tue, 26 Mar 2024 09:42:43 +0100 Johannes Berg wrote: > > > On Mon, 2024-03-25 at 19:09 -0700, Jakub Kicinski wrote: > > > > On Mon, 25 Mar 2024 23:31:25 +0100 Johannes Berg wrote: > > > > > Hi, > > > > > > > > > > So I started playing with this for wifi, and overall that > > > > > does look pretty nice, but it's a bit weird if we can do > > > > > > > > > > guard(wiphy)(&rdev->wiphy); > > > > > > > > > > or so, but still have to manually handle the RTNL in the > > > > > same code. > > > > > > > > Dunno, it locks code instead of data accesses. > > > > > > Well, I'm not sure that's a fair complaint. After all, without any more > > > compiler help, even rtnl_lock()/rtnl_unlock() _necessarily_ locks code. > > > Clearly > > > > > > rtnl_lock(); > > > // something > > > rtnl_unlock(); > > > > > > also locks the "// something" code, after all., and yeah that might be > > > doing data accesses, but it might also be a function call or a whole > > > bunch of other things? > > > > > > Or if you look at something like bpf_xdp_link_attach(), I don't think > > > you can really say that it locks only data. That doesn't even do the > > > allocation outside the lock (though I did convert that one to > > > scoped_guard because of that.) > > > > > > Or even something simple like unregister_netdev(), it just requires the > > > RTNL for some data accesses and consistency deep inside > > > unregister_netdevice(), not for any specific data accessed there. > > > > > > So yeah, this is always going to be a trade-off, but all the locking is. > > > We even make similar trade-offs manually, e.g. look at > > > bpf_xdp_link_update(), it will do the bpf_prog_put() under the RTNL > > > still, for no good reason other than simplifying the cleanup path there. > > > > At least to me the mental model is different. 99% of the time the guard > > is covering the entire body. So now we're moving from "I'm touching X > > so I need to lock" to "This _function_ is safe to touch X". > > > > > Anyway, I can live with it either way (unless you tell me you won't pull > > > wireless code using guard), just thought doing the wireless locking with > > > guard and the RTNL around it without it (only in a few places do we > > > still use RTNL though) looked odd. > > > > > > > > > > Forgive the comparison but it feels too much like Java to me :) > > > > > > Heh. Haven't used Java in 20 years or so... > > > > I only did at uni, but I think they had a decorator for a method, where > > you can basically say "this method should be under lock X" and runtime > > will take that lock before entering and drop it after exit, > > appropriately. I wonder why the sudden love for this concept :S > > Is it also present in Rust or some such? > > > > > > scoped_guard is fine, the guard() not so much. > > > > > > I think you can't get scoped_guard() without guard(), so does that mean > > > you'd accept the first patch in the series? > > > > How can we get one without the other.. do you reckon Joe P would let us > > add a checkpatch check to warn people against pure guard() under net/ ? > > > > > > Do you have a piece of code in wireless where the conversion > > > > made you go "wow, this is so much cleaner"? > > > > > > Mostly long and complex error paths. Found a double-unlock bug (in > > > iwlwifi) too, when converting some locking there. > > > > > > Doing a more broader conversion on cfg80211/mac80211 removes around 200 > > > lines of unlocking, mostly error handling, code. > > > > > > Doing __free() too will probably clean up even more. > > > > Not super convinced by that one either: > > https://lore.kernel.org/all/20240321185640.6f7f4d6b@kernel.org/ > > maybe I'm too conservative.. > > +1 on the concept (fwiw). > > Even the simple examples, such as unregister_netdevice_notifier_net, > show how it avoids boilerplate and so simplifies control flow. > > That benefit multiplies with the number of resources held and number > of exit paths. Or in our case, gotos and (unlock) labels. > > Error paths are notorious for seeing little test coverage and leaking > resources. This is an easy class of bugs that this RAII squashes. +1 While I'm ambivalent to the constructs that have been proposed, I do see leaking resources in error paths as a very common pattern. Especially in new code. Making that easier to get right seems worthwhile to me. > > Sprinkling guard statements anywhere in the scope itself makes it > perhaps hard to follow. Perhaps a heuristic would be to require these > statements at the start of scope (after variable declaration)? > > Function level decorators could further inform static analysis. > But that is somewhat tangential. > ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/3] using guard/__free in networking 2024-03-26 14:37 ` Jakub Kicinski 2024-03-26 15:23 ` Willem de Bruijn @ 2024-03-26 15:33 ` Johannes Berg 2024-03-27 0:15 ` Jakub Kicinski 2024-03-27 20:25 ` Jakub Sitnicki 2024-03-27 11:15 ` Przemek Kitszel 2 siblings, 2 replies; 19+ messages in thread From: Johannes Berg @ 2024-03-26 15:33 UTC (permalink / raw) To: Jakub Kicinski; +Cc: netdev > > So yeah, this is always going to be a trade-off, but all the locking is. > > We even make similar trade-offs manually, e.g. look at > > bpf_xdp_link_update(), it will do the bpf_prog_put() under the RTNL > > still, for no good reason other than simplifying the cleanup path there. > > At least to me the mental model is different. 99% of the time the guard > is covering the entire body. So now we're moving from "I'm touching X > so I need to lock" to "This _function_ is safe to touch X". Yeah, maybe. But then we also have a lot of trivial wrappers just do to locking, like do_something() { rtnl_lock() ret = __do_something() rtnl_unlock(); return ret; } because __do_something() has a bunch of error paths and it's just messy to maintain the locking directly :) So I guess I don't have that much of a different model, I'd actually say it's an advantage of sorts in many cases, and in some cases you'd still have "rtnl_lock()" only in the middle, or scoped_guard(rtnl) {...} for a small block. But refactoring a function because it's accessing protected data doesn't seem completely out of the question either. > > > Forgive the comparison but it feels too much like Java to me :) > > > > Heh. Haven't used Java in 20 years or so... > > I only did at uni, but I think they had a decorator for a method, where > you can basically say "this method should be under lock X" and runtime > will take that lock before entering and drop it after exit, > appropriately. Yeah, I vaguely remember that. And yes, you can obviously just slap that on everything and call it a day, or you could also there refactor the things that do need the locking into a separate function, and use it there? > I wonder why the sudden love for this concept :S I think it's neither sudden nor love ;-) But all the cleanup paths are _always_ a mess to maintain, and this is the least bad thing folks came up with so far? > Is it also present in Rust or some such? I have no idea. I _think_ Rust actually ties the data and the locks together more? > > > scoped_guard is fine, the guard() not so much. > > > > I think you can't get scoped_guard() without guard(), so does that mean > > you'd accept the first patch in the series? > > How can we get one without the other.. do you reckon Joe P would let us > add a checkpatch check to warn people against pure guard() under net/ ? Maybe? But I think I do want to use guard() ;-) There are a ton of functions like say cfg80211_wext_siwrts() or nl80211_new_interface() that really just want to hold the mutex for the (remainder of) the function. And really _nl80211_new_interface() wouldn't even exist if we had that, because nl80211_new_interface() would just be nl80211_new_interface() { cfg80211_destroy_ifaces(rdev); guard(wiphy)(&rdev->wiphy); // exactly existing content of _nl80211_new_interface() // with all the returns etc. } the only reason for _nl80211_new_interface() is the locking there ... Actually, we might push that further down into the function, to just before rdev_add_virtual_intf(), I guess? But it all just adds to the complexity as long as it's not cleaned up automatically. > > > Do you have a piece of code in wireless where the conversion > > > made you go "wow, this is so much cleaner"? > > > > Mostly long and complex error paths. Found a double-unlock bug (in > > iwlwifi) too, when converting some locking there. > > > > Doing a more broader conversion on cfg80211/mac80211 removes around 200 > > lines of unlocking, mostly error handling, code. > > > > Doing __free() too will probably clean up even more. > > Not super convinced by that one either: > https://lore.kernel.org/all/20240321185640.6f7f4d6b@kernel.org/ > maybe I'm too conservative.. I mean ... it's not great, and it's all new stuff to learn (especially those caveats with the cleanup order), but error paths are the things that are really never tested and tend to be broken, no matter that we have smatch/sparse/coverity/etc. So while I don't think it's perfect, and I tend to agree even that it encourages "over-locking" (though we can refactor and use scope etc. where it matters), I'm pretty firmly on the "we should be using this to not worry about error paths all the time" side of the fence, I guess. johannes ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/3] using guard/__free in networking 2024-03-26 15:33 ` Johannes Berg @ 2024-03-27 0:15 ` Jakub Kicinski 2024-03-27 19:24 ` Johannes Berg 2024-03-27 20:25 ` Jakub Sitnicki 1 sibling, 1 reply; 19+ messages in thread From: Jakub Kicinski @ 2024-03-27 0:15 UTC (permalink / raw) To: Johannes Berg; +Cc: netdev On Tue, 26 Mar 2024 16:33:58 +0100 Johannes Berg wrote: > > > I think you can't get scoped_guard() without guard(), so does that mean > > > you'd accept the first patch in the series? > > > > How can we get one without the other.. do you reckon Joe P would let us > > add a checkpatch check to warn people against pure guard() under net/ ? > > Maybe? But I think I do want to use guard() ;-) IIUC Willem and Stan like the construct too, so I'm fine with patches 1 and 2. But let's not convert the exiting code just yet (leave out 3)? ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/3] using guard/__free in networking 2024-03-27 0:15 ` Jakub Kicinski @ 2024-03-27 19:24 ` Johannes Berg 2024-03-27 20:07 ` Jakub Kicinski 0 siblings, 1 reply; 19+ messages in thread From: Johannes Berg @ 2024-03-27 19:24 UTC (permalink / raw) To: Jakub Kicinski; +Cc: netdev On Tue, 2024-03-26 at 17:15 -0700, Jakub Kicinski wrote: > > IIUC Willem and Stan like the construct too, so I'm fine with patches > 1 and 2. But let's not convert the exiting code just yet (leave out 3)? Sure, fair. It was mostly an illustration. Do you want a resend then? johannes ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/3] using guard/__free in networking 2024-03-27 19:24 ` Johannes Berg @ 2024-03-27 20:07 ` Jakub Kicinski 0 siblings, 0 replies; 19+ messages in thread From: Jakub Kicinski @ 2024-03-27 20:07 UTC (permalink / raw) To: Johannes Berg; +Cc: netdev On Wed, 27 Mar 2024 20:24:17 +0100 Johannes Berg wrote: > > IIUC Willem and Stan like the construct too, so I'm fine with patches > > 1 and 2. But let's not convert the exiting code just yet (leave out 3)? > > Sure, fair. It was mostly an illustration. Do you want a resend then? Yes, please! ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/3] using guard/__free in networking 2024-03-26 15:33 ` Johannes Berg 2024-03-27 0:15 ` Jakub Kicinski @ 2024-03-27 20:25 ` Jakub Sitnicki 2024-03-27 21:28 ` Johannes Berg 1 sibling, 1 reply; 19+ messages in thread From: Jakub Sitnicki @ 2024-03-27 20:25 UTC (permalink / raw) To: Johannes Berg; +Cc: Jakub Kicinski, netdev On Tue, Mar 26, 2024 at 04:33 PM +01, Johannes Berg wrote: >> Is it also present in Rust or some such? > > I have no idea. I _think_ Rust actually ties the data and the locks > together more? That is right. Nicely explained here: https://marabos.nl/atomics/basics.html#rusts-mutex ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/3] using guard/__free in networking 2024-03-27 20:25 ` Jakub Sitnicki @ 2024-03-27 21:28 ` Johannes Berg 2024-03-27 21:43 ` Johannes Berg 0 siblings, 1 reply; 19+ messages in thread From: Johannes Berg @ 2024-03-27 21:28 UTC (permalink / raw) To: Jakub Sitnicki; +Cc: Jakub Kicinski, netdev, Peter Zijlstra, linux-kernel On Wed, 2024-03-27 at 21:25 +0100, Jakub Sitnicki wrote: > On Tue, Mar 26, 2024 at 04:33 PM +01, Johannes Berg wrote: > > > Is it also present in Rust or some such? > > > > I have no idea. I _think_ Rust actually ties the data and the locks > > together more? > > That is right. Nicely explained here: > > https://marabos.nl/atomics/basics.html#rusts-mutex Right. Thinking about that, we _could_ even add support for drop_guard()? With the below patch to cleanup.h, you can write void my_something(my_t *my) { ... named_guard(lock, mutex)(&my->mutex); ... if (foo) return -EINVAL; // automatically unlocks ... // no need for lock any more drop_guard(lock); ... // do other things now unlocked } Is that too ugly? Maybe it's less Java-like and more Rust-like and better for Jakub ;-) In some sense that's nicer than scoped_guard() since it doesn't require a new scope / indentation, but maybe Peter already thought about it and rejected it :-) Patch follows, though maybe that should be rolled into the 'base' CLASS definition instead of defining another "_drop" one for named_guard()? diff --git a/include/linux/cleanup.h b/include/linux/cleanup.h index c2d09bc4f976..31298106c28b 100644 --- a/include/linux/cleanup.h +++ b/include/linux/cleanup.h @@ -106,7 +106,27 @@ typedef _type class_##_name##_t; \ static inline void class_##_name##_destructor(_type *p) \ { _type _T = *p; _exit; } \ static inline _type class_##_name##_constructor(_init_args) \ -{ _type t = _init; return t; } +{ _type t = _init; return t; } \ +typedef struct class_##_name##_drop##_t { \ + class_##_name##_t obj; \ + void (*destructor)(struct class_##_name##_drop##_t *); \ +} class_##_name##_drop##_t; \ +static inline void \ +class_##_name##_drop##_destructor(class_##_name##_drop##_t *p) \ +{ \ + if (p->obj) \ + class_##_name##_destructor(&p->obj); \ + p->obj = NULL; \ +} \ +static inline class_##_name##_drop##_t \ +class_##_name##_drop##_constructor(_init_args) \ +{ \ + class_##_name##_drop##_t t = { \ + .obj = _init, \ + .destructor = class_##_name##_drop##_destructor, \ + }; \ + return t; \ +} #define EXTEND_CLASS(_name, ext, _init, _init_args...) \ typedef class_##_name##_t class_##_name##ext##_t; \ @@ -163,6 +183,11 @@ static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \ #define guard(_name) \ CLASS(_name, __UNIQUE_ID(guard)) +#define named_guard(_name, _class) \ + CLASS(_class##_drop, _name) + +#define drop_guard(_name) do { _name.destructor(&_name); } while (0) + #define __guard_ptr(_name) class_##_name##_lock_ptr #define scoped_guard(_name, args...) \ ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH 0/3] using guard/__free in networking 2024-03-27 21:28 ` Johannes Berg @ 2024-03-27 21:43 ` Johannes Berg 0 siblings, 0 replies; 19+ messages in thread From: Johannes Berg @ 2024-03-27 21:43 UTC (permalink / raw) To: Jakub Sitnicki; +Cc: Jakub Kicinski, netdev, Peter Zijlstra, linux-kernel On Wed, 2024-03-27 at 22:28 +0100, Johannes Berg wrote: > > +typedef struct class_##_name##_drop##_t { \ > + class_##_name##_t obj; \ > + void (*destructor)(struct class_##_name##_drop##_t *); \ > +} class_##_name##_drop##_t; \ No, I misread the compiler output, it does output a real destructor function to push it into a stack variable with this... So I guess it'd have to be void my_something(my_t *my) { ... named_guard(lock, mutex)(&my->mutex); ... if (foo) return -EINVAL; // automatically unlocks ... // no need for lock any more drop_guard(lock, mutex); ... // do other things now unlocked } instead, syntax-wise. Which obviously simplifies the changes: diff --git a/include/linux/cleanup.h b/include/linux/cleanup.h index c2d09bc4f976..cf39a4a3f56f 100644 --- a/include/linux/cleanup.h +++ b/include/linux/cleanup.h @@ -163,6 +163,12 @@ static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \ #define guard(_name) \ CLASS(_name, __UNIQUE_ID(guard)) +#define named_guard(_name, _class) \ + CLASS(_class, _name) + +#define drop_guard(_name, _class) \ + do { class_##_class##_destructor(&_name); _name = NULL; } while (0) + #define __guard_ptr(_name) class_##_name##_lock_ptr #define scoped_guard(_name, args...) \ ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH 0/3] using guard/__free in networking 2024-03-26 14:37 ` Jakub Kicinski 2024-03-26 15:23 ` Willem de Bruijn 2024-03-26 15:33 ` Johannes Berg @ 2024-03-27 11:15 ` Przemek Kitszel 2 siblings, 0 replies; 19+ messages in thread From: Przemek Kitszel @ 2024-03-27 11:15 UTC (permalink / raw) To: Jakub Kicinski, Johannes Berg; +Cc: netdev On 3/26/24 15:37, Jakub Kicinski wrote: > On Tue, 26 Mar 2024 09:42:43 +0100 Johannes Berg wrote: >> On Mon, 2024-03-25 at 19:09 -0700, Jakub Kicinski wrote: >>> On Mon, 25 Mar 2024 23:31:25 +0100 Johannes Berg wrote: >>>> Hi, >>>> >>>> So I started playing with this for wifi, and overall that >>>> does look pretty nice, but it's a bit weird if we can do >>>> >>>> guard(wiphy)(&rdev->wiphy); >>>> >>>> or so, but still have to manually handle the RTNL in the >>>> same code. >>> >>> Dunno, it locks code instead of data accesses. >> >> Well, I'm not sure that's a fair complaint. After all, without any more >> compiler help, even rtnl_lock()/rtnl_unlock() _necessarily_ locks code. >> Clearly >> >> rtnl_lock(); >> // something >> rtnl_unlock(); >> >> also locks the "// something" code, after all., and yeah that might be >> doing data accesses, but it might also be a function call or a whole >> bunch of other things? >> >> Or if you look at something like bpf_xdp_link_attach(), I don't think >> you can really say that it locks only data. That doesn't even do the >> allocation outside the lock (though I did convert that one to >> scoped_guard because of that.) >> >> Or even something simple like unregister_netdev(), it just requires the >> RTNL for some data accesses and consistency deep inside >> unregister_netdevice(), not for any specific data accessed there. >> >> So yeah, this is always going to be a trade-off, but all the locking is. >> We even make similar trade-offs manually, e.g. look at >> bpf_xdp_link_update(), it will do the bpf_prog_put() under the RTNL >> still, for no good reason other than simplifying the cleanup path there. > > At least to me the mental model is different. 99% of the time the guard > is covering the entire body. So now we're moving from "I'm touching X > so I need to lock" to "This _function_ is safe to touch X". > >> Anyway, I can live with it either way (unless you tell me you won't pull >> wireless code using guard), just thought doing the wireless locking with >> guard and the RTNL around it without it (only in a few places do we >> still use RTNL though) looked odd. >> >> >>> Forgive the comparison but it feels too much like Java to me :) >> >> Heh. Haven't used Java in 20 years or so... > > I only did at uni, but I think they had a decorator for a method, where > you can basically say "this method should be under lock X" and runtime > will take that lock before entering and drop it after exit, > appropriately. I wonder why the sudden love for this concept :S > Is it also present in Rust or some such? :D There is indeed a lot of proposals around the topic lately :) I believe that "The first half of the 6.8 merge window" [lwn] article has brought attention to the in-kernel "scope-based resource management" availability. [lwn] https://lwn.net/Articles/957188/ More abstraction/sugar over __free() is cleanly needed to have code both easier to follow and less buggy. You made a good point to encourage scoping the locks to small blocks instead of whole functions. And "less typing" to instantiate the lock guard variable is one way to do that. > >>> scoped_guard is fine, the guard() not so much. >> >> I think you can't get scoped_guard() without guard(), so does that mean >> you'd accept the first patch in the series? > > How can we get one without the other.. do you reckon Joe P would let us > add a checkpatch check to warn people against pure guard() under net/ ? > >>> Do you have a piece of code in wireless where the conversion >>> made you go "wow, this is so much cleaner"? >> >> Mostly long and complex error paths. Found a double-unlock bug (in >> iwlwifi) too, when converting some locking there. >> >> Doing a more broader conversion on cfg80211/mac80211 removes around 200 >> lines of unlocking, mostly error handling, code. >> >> Doing __free() too will probably clean up even more. > > Not super convinced by that one either: > https://lore.kernel.org/all/20240321185640.6f7f4d6b@kernel.org/ > maybe I'm too conservative.. > ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH 0/3] using guard/__free in networking 2024-03-26 2:09 ` [PATCH 0/3] using guard/__free in networking Jakub Kicinski 2024-03-26 8:42 ` Johannes Berg @ 2024-03-26 15:33 ` Andrew Lunn 1 sibling, 0 replies; 19+ messages in thread From: Andrew Lunn @ 2024-03-26 15:33 UTC (permalink / raw) To: Jakub Kicinski; +Cc: Johannes Berg, netdev On Mon, Mar 25, 2024 at 07:09:57PM -0700, Jakub Kicinski wrote: > On Mon, 25 Mar 2024 23:31:25 +0100 Johannes Berg wrote: > > Hi, > > > > So I started playing with this for wifi, and overall that > > does look pretty nice, but it's a bit weird if we can do > > > > guard(wiphy)(&rdev->wiphy); > > > > or so, but still have to manually handle the RTNL in the > > same code. > > Dunno, it locks code instead of data accesses. > Forgive the comparison but it feels too much like Java to me :) > scoped_guard is fine, the guard() not so much. I tend to agree. guard() does not feel like C. scoped_guard does. Andrew ^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2024-03-29 10:23 UTC | newest] Thread overview: 19+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-03-25 22:31 [PATCH 0/3] using guard/__free in networking Johannes Berg 2024-03-25 22:31 ` [PATCH 1/3] rtnetlink: add guard for RTNL Johannes Berg 2024-03-25 22:31 ` [PATCH 2/3] netdevice: add DEFINE_FREE() for dev_put Johannes Berg 2024-03-25 22:31 ` [PATCH 3/3] net: core: use guard/__free in core dev code Johannes Berg 2024-03-26 2:09 ` [PATCH 0/3] using guard/__free in networking Jakub Kicinski 2024-03-26 8:42 ` Johannes Berg 2024-03-26 14:37 ` Jakub Kicinski 2024-03-26 15:23 ` Willem de Bruijn 2024-03-26 17:33 ` Stanislav Fomichev 2024-03-29 10:23 ` Simon Horman 2024-03-26 15:33 ` Johannes Berg 2024-03-27 0:15 ` Jakub Kicinski 2024-03-27 19:24 ` Johannes Berg 2024-03-27 20:07 ` Jakub Kicinski 2024-03-27 20:25 ` Jakub Sitnicki 2024-03-27 21:28 ` Johannes Berg 2024-03-27 21:43 ` Johannes Berg 2024-03-27 11:15 ` Przemek Kitszel 2024-03-26 15:33 ` Andrew Lunn
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).