Implement the dstref object, which is a potentially noref pointer to a struct dst_entry. A similar object already exists in skb->_skb_refdst, but is coupled to struct sk_buff, and it can be very useful on its own. For example, it can be used to return a potentially noref dst from a function, which is currently not possible unless we attach it to an skb. Implement dstref as a standalone object, decoupled from sk_buff. Some of the helpers have to be in dst.h to prevent a circular include between skbuff.h and dst.h. Signed-off-by: Marek Mietus --- include/net/dst.h | 28 +++++++++++ include/net/dstref.h | 111 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 include/net/dstref.h diff --git a/include/net/dst.h b/include/net/dst.h index f8aa1239b4db..d7169f067637 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -260,6 +261,33 @@ void dst_release(struct dst_entry *dst); void dst_release_immediate(struct dst_entry *dst); +/** + * dstref_drop - drop the given dstref object. + * @dstref: the dstref object to drop. + * + * This drops the refcount on the dst iff the dstref object holds a reference to it. + */ +static inline void dstref_drop(dstref_t dstref) +{ + if (!dstref_is_noref(dstref)) + dst_release(__dstref_dst(dstref)); +} + +/** + * dstref_clone - clones the given dstref object. + * @dstref: the dstref object to clone. + * + * Clones the dstref while preserving the ownership semantics of the input dstref. + * + * Return: a clone of the provided dstref object. + */ +static inline dstref_t dstref_clone(dstref_t dstref) +{ + if (!dstref_is_noref(dstref)) + dst_clone(__dstref_dst(dstref)); + return dstref; +} + static inline void refdst_drop(unsigned long refdst) { if (!(refdst & SKB_DST_NOREF)) diff --git a/include/net/dstref.h b/include/net/dstref.h new file mode 100644 index 000000000000..637079260c93 --- /dev/null +++ b/include/net/dstref.h @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _NET_DSTREF_H +#define _NET_DSTREF_H + +#include +#include +#include + +/** + * This is required since we can't include dst.h here, in order avoid circular includes between + * skbuff.h and dst.h. + */ +struct dst_entry; + +/** + * typedef dstref_t - a pointer to a dst which may or may not hold a reference to the dst. + */ +typedef unsigned long __bitwise dstref_t; + +/** + * This bit is used to specify whether or not the dstref object holds a reference to its dst_entry. + */ +#define DSTREF_DST_NOREF 1UL +#define DSTREF_DST_PTRMASK ~(DSTREF_DST_NOREF) + +/** + * An empty dstref object which does not point to any dst. + */ +#define DSTREF_EMPTY ((__force dstref_t)0UL) + +/** + * A noref variant of an empty dstref object which does not point to any dst. + */ +#define DSTREF_EMPTY_NOREF ((__force dstref_t)DSTREF_DST_NOREF) + +/** + * dst_to_dstref - create a dstref object which holds a reference to the dst. + * @dst: dst to convert. + * + * The provided dst can be NULL, in which case an empty dstref is returned. + * + * This function steals the reference on the provided dst, and does not take an extra reference on + * it. + * + * Return: dstref object which points to the given dst and holds a reference to it, or an empty + * dstref object if dst is NULL. + */ +static inline dstref_t dst_to_dstref(struct dst_entry *dst) +{ + return (__force dstref_t)dst; +} + +/** + * dst_to_dstref_noref - create a dstref pointer which does not hold a reference to the dst. + * @dst: dst to convert. + * + * The provided dst can be NULL, in which case a noref empty dstref is returned. + * + * This function must be called within an RCU read-side critical section. + * + * Return: dstref object which points to the given dst and does not hold a reference to it, or a + * noref empty dstref object if dst is NULL. + */ +static inline dstref_t dst_to_dstref_noref(struct dst_entry *dst) +{ + WARN_ON(!rcu_read_lock_held() && !rcu_read_lock_bh_held()); + return (__force dstref_t)((unsigned long)dst | DSTREF_DST_NOREF); +} + +/** + * Is the given dstref object a noref dstref, which doesn't hold a reference to the dst that it + * points to? + */ +static inline bool dstref_is_noref(dstref_t dstref) +{ + return (__force unsigned long)dstref & DSTREF_DST_NOREF; +} + +/* + * __dstref_dst - get the dst that is pointed at by the given dstref object, without performing + * safety checks. + * @dstref: the dstref object to get the dst of. + * + * This function returns the dst without performing safety checks. + * Prefer using dstref_dst instead of using this function. + * + * Return: the dst object pointed at by the given dstref object. + */ +static inline struct dst_entry *__dstref_dst(dstref_t dstref) +{ + return (struct dst_entry *)((__force unsigned long)dstref & DSTREF_DST_PTRMASK); +} + +/** + * dstref_dst - get the dst that is pointed at by the given dstref object. + * @dstref: the dstref object to get the dst of. + * + * If the dstref object is noref, this function must be called within an RCU read-side critical + * section. + * + * Return: the dst object pointed at by the given dstref object. + */ +static inline struct dst_entry *dstref_dst(dstref_t dstref) +{ + WARN_ON(dstref_is_noref(dstref) && + !rcu_read_lock_held() && + !rcu_read_lock_bh_held()); + return __dstref_dst(dstref); +} + +#endif /* _NET_DSTREF_H */ -- 2.51.0