Commit 6689d716 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents e311eb91 67737c45
......@@ -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 @@ void __skb_ext_put(struct skb_ext *ext)
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);
}
......
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,7 +154,18 @@ 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))
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);
}
......@@ -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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment