datapath: Free skb(s) on recirculation error
authorSimon Horman <horms@verge.net.au>
Tue, 13 May 2014 05:46:18 +0000 (14:46 +0900)
committerJesse Gross <jesse@nicira.com>
Tue, 13 May 2014 20:39:53 +0000 (13:39 -0700)
This patch attempts to ensure that skb(s) are always freed (once)
if if an error occurs in execute_recirc(). It does so in two ways:

1. Ensure that execute_recirc() always consimes skb passed to it.
   Specifically, free the skb if the call to ovs_flow_extract() fails.

2. Return from the recirc case in execute_recirc() whenever
   the skb has not been cloned immediately above.

   This is only the case if the action is both the last action and the
   keep_skb parameter of execute_recirc is not true.  As it is the last
   action and the skb is consumed one way or another by execute_recirc() it
   is correct to return here.  In particular this avoids the possibility of
   the skb, which has been consumed by execute_recirc() from being freed.

   Conversely if this is not the case then the skb has been cloned
   and the clone has been consumed by execute_recirc().
   This leads to three sub-cases:
   * If execute_recirc() returned an error code then the original skb
     will be freed by the error handling code below the case statement in
     do_execute_actions().
   * If this is not the last action then action processing will continue,
     using the original skb.
   * If this is the last action then it must also be the case that keep_skb
     is true (otherwise the skb would not have been cloned). Thus
     do_execute_actions() will return without freeing the original skb.

Signed-off-by: Simon Horman <horms@verge.net.au>
[jesse: use kfree_skb instead of consume_skb on error path]
Signed-off-by: Jesse Gross <jesse@nicira.com>
datapath/actions.c

index 7fe2f54..3c1615b 100644 (file)
@@ -530,8 +530,10 @@ static int execute_recirc(struct datapath *dp, struct sk_buff *skb,
        int err;
 
        err = ovs_flow_extract(skb, p->port_no, &recirc_key);
-       if (err)
+       if (err) {
+               kfree_skb(skb);
                return err;
+       }
 
        recirc_key.ovs_flow_hash = hash;
        recirc_key.recirc_id = nla_get_u32(a);
@@ -596,7 +598,7 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
 
                        err = execute_recirc(dp, recirc_skb, a);
 
-                       if (last_action || err)
+                       if (recirc_skb == skb)
                                return err;
 
                        break;