Merge branch 'MCTP-flow-support'
Jeremy Kerr says: ==================== MCTP flow support For certain MCTP transport bindings, the binding driver will need to be aware of request/response pairing. For example, the i2c binding may need to set multiplexer configuration when expecting a response from a device behind a mux. This series implements a mechanism for informing the driver about these flows, so it can implement transport-specific behaviour when a flow is in progress (ie, a response is expected, and/or we time-out on that expectation). We use a skb extension to notify the driver about the presence of a flow, and a new dev->ops callback to notify about a flow's destruction. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
6689d716fd
@ -4243,6 +4243,9 @@ enum skb_ext_id {
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_MPTCP)
|
||||
SKB_EXT_MPTCP,
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_MCTP_FLOWS)
|
||||
SKB_EXT_MCTP,
|
||||
#endif
|
||||
SKB_EXT_NUM, /* must be last */
|
||||
};
|
||||
|
@ -152,6 +152,12 @@ struct mctp_sk_key {
|
||||
|
||||
/* expiry timeout; valid (above) cleared on expiry */
|
||||
unsigned long expiry;
|
||||
|
||||
/* free to use for device flow state tracking. Initialised to
|
||||
* zero on initial key creation
|
||||
*/
|
||||
unsigned long dev_flow_state;
|
||||
struct mctp_dev *dev;
|
||||
};
|
||||
|
||||
struct mctp_skb_cb {
|
||||
@ -189,6 +195,13 @@ static inline struct mctp_skb_cb *mctp_cb(struct sk_buff *skb)
|
||||
return (void *)(skb->cb);
|
||||
}
|
||||
|
||||
/* If CONFIG_MCTP_FLOWS, we may add one of these as a SKB extension,
|
||||
* indicating the flow to the device driver.
|
||||
*/
|
||||
struct mctp_flow {
|
||||
struct mctp_sk_key *key;
|
||||
};
|
||||
|
||||
/* Route definition.
|
||||
*
|
||||
* These are held in the pernet->mctp.routes list, with RCU protection for
|
||||
|
@ -14,6 +14,8 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/refcount.h>
|
||||
|
||||
struct mctp_sk_key;
|
||||
|
||||
struct mctp_dev {
|
||||
struct net_device *dev;
|
||||
|
||||
@ -21,6 +23,8 @@ struct mctp_dev {
|
||||
|
||||
unsigned int net;
|
||||
|
||||
const struct mctp_netdev_ops *ops;
|
||||
|
||||
/* Only modified under RTNL. Reads have addrs_lock held */
|
||||
u8 *addrs;
|
||||
size_t num_addrs;
|
||||
@ -29,12 +33,24 @@ struct mctp_dev {
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
struct mctp_netdev_ops {
|
||||
void (*release_flow)(struct mctp_dev *dev,
|
||||
struct mctp_sk_key *key);
|
||||
};
|
||||
|
||||
#define MCTP_INITIAL_DEFAULT_NET 1
|
||||
|
||||
struct mctp_dev *mctp_dev_get_rtnl(const struct net_device *dev);
|
||||
struct mctp_dev *__mctp_dev_get(const struct net_device *dev);
|
||||
|
||||
int mctp_register_netdev(struct net_device *dev,
|
||||
const struct mctp_netdev_ops *ops);
|
||||
void mctp_unregister_netdev(struct net_device *dev);
|
||||
|
||||
void mctp_dev_hold(struct mctp_dev *mdev);
|
||||
void mctp_dev_put(struct mctp_dev *mdev);
|
||||
|
||||
void mctp_dev_set_key(struct mctp_dev *dev, struct mctp_sk_key *key);
|
||||
void mctp_dev_release_key(struct mctp_dev *dev, struct mctp_sk_key *key);
|
||||
|
||||
#endif /* __NET_MCTPDEVICE_H */
|
||||
|
@ -70,6 +70,7 @@
|
||||
#include <net/xfrm.h>
|
||||
#include <net/mpls.h>
|
||||
#include <net/mptcp.h>
|
||||
#include <net/mctp.h>
|
||||
#include <net/page_pool.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
@ -4440,6 +4441,9 @@ static const u8 skb_ext_type_len[] = {
|
||||
#if IS_ENABLED(CONFIG_MPTCP)
|
||||
[SKB_EXT_MPTCP] = SKB_EXT_CHUNKSIZEOF(struct mptcp_ext),
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_MCTP_FLOWS)
|
||||
[SKB_EXT_MCTP] = SKB_EXT_CHUNKSIZEOF(struct mctp_flow),
|
||||
#endif
|
||||
};
|
||||
|
||||
static __always_inline unsigned int skb_ext_total_length(void)
|
||||
@ -4456,6 +4460,9 @@ static __always_inline unsigned int skb_ext_total_length(void)
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_MPTCP)
|
||||
skb_ext_type_len[SKB_EXT_MPTCP] +
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_MCTP_FLOWS)
|
||||
skb_ext_type_len[SKB_EXT_MCTP] +
|
||||
#endif
|
||||
0;
|
||||
}
|
||||
@ -6529,6 +6536,14 @@ static void skb_ext_put_sp(struct sec_path *sp)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MCTP_FLOWS
|
||||
static void skb_ext_put_mctp(struct mctp_flow *flow)
|
||||
{
|
||||
if (flow->key)
|
||||
mctp_key_unref(flow->key);
|
||||
}
|
||||
#endif
|
||||
|
||||
void __skb_ext_del(struct sk_buff *skb, enum skb_ext_id id)
|
||||
{
|
||||
struct skb_ext *ext = skb->extensions;
|
||||
@ -6564,6 +6579,10 @@ free_now:
|
||||
if (__skb_ext_exist(ext, SKB_EXT_SEC_PATH))
|
||||
skb_ext_put_sp(skb_ext_get_ptr(ext, SKB_EXT_SEC_PATH));
|
||||
#endif
|
||||
#ifdef CONFIG_MCTP_FLOWS
|
||||
if (__skb_ext_exist(ext, SKB_EXT_MCTP))
|
||||
skb_ext_put_mctp(skb_ext_get_ptr(ext, SKB_EXT_MCTP));
|
||||
#endif
|
||||
|
||||
kmem_cache_free(skbuff_ext_cache, ext);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
menuconfig MCTP
|
||||
depends on NET
|
||||
tristate "MCTP core protocol support"
|
||||
bool "MCTP core protocol support"
|
||||
help
|
||||
Management Component Transport Protocol (MCTP) is an in-system
|
||||
protocol for communicating between management controllers and
|
||||
@ -16,3 +16,8 @@ config MCTP_TEST
|
||||
bool "MCTP core tests" if !KUNIT_ALL_TESTS
|
||||
depends on MCTP=y && KUNIT=y
|
||||
default KUNIT_ALL_TESTS
|
||||
|
||||
config MCTP_FLOWS
|
||||
bool
|
||||
depends on MCTP
|
||||
select SKB_EXTENSIONS
|
||||
|
@ -260,6 +260,24 @@ void mctp_dev_put(struct mctp_dev *mdev)
|
||||
}
|
||||
}
|
||||
|
||||
void mctp_dev_release_key(struct mctp_dev *dev, struct mctp_sk_key *key)
|
||||
__must_hold(&key->lock)
|
||||
{
|
||||
if (!dev)
|
||||
return;
|
||||
if (dev->ops && dev->ops->release_flow)
|
||||
dev->ops->release_flow(dev, key);
|
||||
key->dev = NULL;
|
||||
mctp_dev_put(dev);
|
||||
}
|
||||
|
||||
void mctp_dev_set_key(struct mctp_dev *dev, struct mctp_sk_key *key)
|
||||
__must_hold(&key->lock)
|
||||
{
|
||||
mctp_dev_hold(dev);
|
||||
key->dev = dev;
|
||||
}
|
||||
|
||||
static struct mctp_dev *mctp_add_dev(struct net_device *dev)
|
||||
{
|
||||
struct mctp_dev *mdev;
|
||||
@ -414,6 +432,39 @@ static int mctp_dev_notify(struct notifier_block *this, unsigned long event,
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int mctp_register_netdevice(struct net_device *dev,
|
||||
const struct mctp_netdev_ops *ops)
|
||||
{
|
||||
struct mctp_dev *mdev;
|
||||
|
||||
mdev = mctp_add_dev(dev);
|
||||
if (IS_ERR(mdev))
|
||||
return PTR_ERR(mdev);
|
||||
|
||||
mdev->ops = ops;
|
||||
|
||||
return register_netdevice(dev);
|
||||
}
|
||||
|
||||
int mctp_register_netdev(struct net_device *dev,
|
||||
const struct mctp_netdev_ops *ops)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rtnl_lock();
|
||||
rc = mctp_register_netdevice(dev, ops);
|
||||
rtnl_unlock();
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mctp_register_netdev);
|
||||
|
||||
void mctp_unregister_netdev(struct net_device *dev)
|
||||
{
|
||||
unregister_netdev(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mctp_unregister_netdev);
|
||||
|
||||
static struct rtnl_af_ops mctp_af_ops = {
|
||||
.family = AF_MCTP,
|
||||
.fill_link_af = mctp_fill_link_af,
|
||||
|
@ -29,6 +29,8 @@
|
||||
static const unsigned int mctp_message_maxlen = 64 * 1024;
|
||||
static const unsigned long mctp_key_lifetime = 6 * CONFIG_HZ;
|
||||
|
||||
static void mctp_flow_prepare_output(struct sk_buff *skb, struct mctp_dev *dev);
|
||||
|
||||
/* route output callbacks */
|
||||
static int mctp_route_discard(struct mctp_route *route, struct sk_buff *skb)
|
||||
{
|
||||
@ -152,8 +154,19 @@ static struct mctp_sk_key *mctp_key_alloc(struct mctp_sock *msk,
|
||||
|
||||
void mctp_key_unref(struct mctp_sk_key *key)
|
||||
{
|
||||
if (refcount_dec_and_test(&key->refs))
|
||||
kfree(key);
|
||||
unsigned long flags;
|
||||
|
||||
if (!refcount_dec_and_test(&key->refs))
|
||||
return;
|
||||
|
||||
/* even though no refs exist here, the lock allows us to stay
|
||||
* consistent with the locking requirement of mctp_dev_release_key
|
||||
*/
|
||||
spin_lock_irqsave(&key->lock, flags);
|
||||
mctp_dev_release_key(key->dev, key);
|
||||
spin_unlock_irqrestore(&key->lock, flags);
|
||||
|
||||
kfree(key);
|
||||
}
|
||||
|
||||
static int mctp_key_add(struct mctp_sk_key *key, struct mctp_sock *msk)
|
||||
@ -204,6 +217,7 @@ static void __mctp_key_unlock_drop(struct mctp_sk_key *key, struct net *net,
|
||||
key->reasm_head = NULL;
|
||||
key->reasm_dead = true;
|
||||
key->valid = false;
|
||||
mctp_dev_release_key(key->dev, key);
|
||||
spin_unlock_irqrestore(&key->lock, flags);
|
||||
|
||||
spin_lock_irqsave(&net->mctp.keys_lock, flags);
|
||||
@ -222,6 +236,40 @@ static void __mctp_key_unlock_drop(struct mctp_sk_key *key, struct net *net,
|
||||
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MCTP_FLOWS
|
||||
static void mctp_skb_set_flow(struct sk_buff *skb, struct mctp_sk_key *key)
|
||||
{
|
||||
struct mctp_flow *flow;
|
||||
|
||||
flow = skb_ext_add(skb, SKB_EXT_MCTP);
|
||||
if (!flow)
|
||||
return;
|
||||
|
||||
refcount_inc(&key->refs);
|
||||
flow->key = key;
|
||||
}
|
||||
|
||||
static void mctp_flow_prepare_output(struct sk_buff *skb, struct mctp_dev *dev)
|
||||
{
|
||||
struct mctp_sk_key *key;
|
||||
struct mctp_flow *flow;
|
||||
|
||||
flow = skb_ext_find(skb, SKB_EXT_MCTP);
|
||||
if (!flow)
|
||||
return;
|
||||
|
||||
key = flow->key;
|
||||
|
||||
if (WARN_ON(key->dev && key->dev != dev))
|
||||
return;
|
||||
|
||||
mctp_dev_set_key(dev, key);
|
||||
}
|
||||
#else
|
||||
static void mctp_skb_set_flow(struct sk_buff *skb, struct mctp_sk_key *key) {}
|
||||
static void mctp_flow_prepare_output(struct sk_buff *skb, struct mctp_dev *dev) {}
|
||||
#endif
|
||||
|
||||
static int mctp_frag_queue(struct mctp_sk_key *key, struct sk_buff *skb)
|
||||
{
|
||||
struct mctp_hdr *hdr = mctp_hdr(skb);
|
||||
@ -465,6 +513,8 @@ static int mctp_route_output(struct mctp_route *route, struct sk_buff *skb)
|
||||
return -EHOSTUNREACH;
|
||||
}
|
||||
|
||||
mctp_flow_prepare_output(skb, route->dev);
|
||||
|
||||
rc = dev_queue_xmit(skb);
|
||||
if (rc)
|
||||
rc = net_xmit_errno(rc);
|
||||
@ -532,14 +582,14 @@ static void mctp_reserve_tag(struct net *net, struct mctp_sk_key *key,
|
||||
/* Allocate a locally-owned tag value for (saddr, daddr), and reserve
|
||||
* it for the socket msk
|
||||
*/
|
||||
static int mctp_alloc_local_tag(struct mctp_sock *msk,
|
||||
mctp_eid_t saddr, mctp_eid_t daddr, u8 *tagp)
|
||||
static struct mctp_sk_key *mctp_alloc_local_tag(struct mctp_sock *msk,
|
||||
mctp_eid_t saddr,
|
||||
mctp_eid_t daddr, u8 *tagp)
|
||||
{
|
||||
struct net *net = sock_net(&msk->sk);
|
||||
struct netns_mctp *mns = &net->mctp;
|
||||
struct mctp_sk_key *key, *tmp;
|
||||
unsigned long flags;
|
||||
int rc = -EAGAIN;
|
||||
u8 tagbits;
|
||||
|
||||
/* for NULL destination EIDs, we may get a response from any peer */
|
||||
@ -549,7 +599,7 @@ static int mctp_alloc_local_tag(struct mctp_sock *msk,
|
||||
/* be optimistic, alloc now */
|
||||
key = mctp_key_alloc(msk, saddr, daddr, 0, GFP_KERNEL);
|
||||
if (!key)
|
||||
return -ENOMEM;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* 8 possible tag values */
|
||||
tagbits = 0xff;
|
||||
@ -591,18 +641,16 @@ static int mctp_alloc_local_tag(struct mctp_sock *msk,
|
||||
trace_mctp_key_acquire(key);
|
||||
|
||||
*tagp = key->tag;
|
||||
/* done with the key in this scope */
|
||||
mctp_key_unref(key);
|
||||
key = NULL;
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&mns->keys_lock, flags);
|
||||
|
||||
if (!tagbits)
|
||||
if (!tagbits) {
|
||||
kfree(key);
|
||||
return ERR_PTR(-EBUSY);
|
||||
}
|
||||
|
||||
return rc;
|
||||
return key;
|
||||
}
|
||||
|
||||
/* routing lookups */
|
||||
@ -740,6 +788,7 @@ int mctp_local_output(struct sock *sk, struct mctp_route *rt,
|
||||
struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk);
|
||||
struct mctp_skb_cb *cb = mctp_cb(skb);
|
||||
struct mctp_route tmp_rt;
|
||||
struct mctp_sk_key *key;
|
||||
struct net_device *dev;
|
||||
struct mctp_hdr *hdr;
|
||||
unsigned long flags;
|
||||
@ -799,11 +848,17 @@ int mctp_local_output(struct sock *sk, struct mctp_route *rt,
|
||||
goto out_release;
|
||||
|
||||
if (req_tag & MCTP_HDR_FLAG_TO) {
|
||||
rc = mctp_alloc_local_tag(msk, saddr, daddr, &tag);
|
||||
if (rc)
|
||||
key = mctp_alloc_local_tag(msk, saddr, daddr, &tag);
|
||||
if (IS_ERR(key)) {
|
||||
rc = PTR_ERR(key);
|
||||
goto out_release;
|
||||
}
|
||||
mctp_skb_set_flow(skb, key);
|
||||
/* done with the key in this scope */
|
||||
mctp_key_unref(key);
|
||||
tag |= MCTP_HDR_FLAG_TO;
|
||||
} else {
|
||||
key = NULL;
|
||||
tag = req_tag;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user