diff options
Diffstat (limited to 'net/mctp/route.c')
-rw-r--r-- | net/mctp/route.c | 55 |
1 files changed, 53 insertions, 2 deletions
diff --git a/net/mctp/route.c b/net/mctp/route.c index 82cc10a2fb0c..46c44823edb7 100644 --- a/net/mctp/route.c +++ b/net/mctp/route.c @@ -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); @@ -803,6 +853,7 @@ int mctp_local_output(struct sock *sk, struct mctp_route *rt, 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; |