]> Pileus Git - ~andy/linux/blobdiff - net/core/dev.c
net: Switch to using the new packet offload infrustructure
[~andy/linux] / net / core / dev.c
index 09cb3f6dc40c4a573a9597ca851b9a91a09a1c3d..cf843a256cc6feeeabdd20dd0b37332e00dc505c 100644 (file)
 #define PTYPE_HASH_MASK        (PTYPE_HASH_SIZE - 1)
 
 static DEFINE_SPINLOCK(ptype_lock);
+static DEFINE_SPINLOCK(offload_lock);
 static struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
 static struct list_head ptype_all __read_mostly;       /* Taps */
+static struct list_head offload_base __read_mostly;
 
 /*
  * The @dev_base_head list is protected by @dev_base_lock and the rtnl
@@ -470,6 +472,82 @@ void dev_remove_pack(struct packet_type *pt)
 }
 EXPORT_SYMBOL(dev_remove_pack);
 
+
+/**
+ *     dev_add_offload - register offload handlers
+ *     @po: protocol offload declaration
+ *
+ *     Add protocol offload handlers to the networking stack. The passed
+ *     &proto_offload is linked into kernel lists and may not be freed until
+ *     it has been removed from the kernel lists.
+ *
+ *     This call does not sleep therefore it can not
+ *     guarantee all CPU's that are in middle of receiving packets
+ *     will see the new offload handlers (until the next received packet).
+ */
+void dev_add_offload(struct packet_offload *po)
+{
+       struct list_head *head = &offload_base;
+
+       spin_lock(&offload_lock);
+       list_add_rcu(&po->list, head);
+       spin_unlock(&offload_lock);
+}
+EXPORT_SYMBOL(dev_add_offload);
+
+/**
+ *     __dev_remove_offload     - remove offload handler
+ *     @po: packet offload declaration
+ *
+ *     Remove a protocol offload handler that was previously added to the
+ *     kernel offload handlers by dev_add_offload(). The passed &offload_type
+ *     is removed from the kernel lists and can be freed or reused once this
+ *     function returns.
+ *
+ *      The packet type might still be in use by receivers
+ *     and must not be freed until after all the CPU's have gone
+ *     through a quiescent state.
+ */
+void __dev_remove_offload(struct packet_offload *po)
+{
+       struct list_head *head = &offload_base;
+       struct packet_offload *po1;
+
+       spin_lock(&ptype_lock);
+
+       list_for_each_entry(po1, head, list) {
+               if (po == po1) {
+                       list_del_rcu(&po->list);
+                       goto out;
+               }
+       }
+
+       pr_warn("dev_remove_offload: %p not found\n", po);
+out:
+       spin_unlock(&ptype_lock);
+}
+EXPORT_SYMBOL(__dev_remove_offload);
+
+/**
+ *     dev_remove_offload       - remove packet offload handler
+ *     @po: packet offload declaration
+ *
+ *     Remove a packet offload handler that was previously added to the kernel
+ *     offload handlers by dev_add_offload(). The passed &offload_type is
+ *     removed from the kernel lists and can be freed or reused once this
+ *     function returns.
+ *
+ *     This call sleeps to guarantee that no CPU is looking at the packet
+ *     type after return.
+ */
+void dev_remove_offload(struct packet_offload *po)
+{
+       __dev_remove_offload(po);
+
+       synchronize_net();
+}
+EXPORT_SYMBOL(dev_remove_offload);
+
 /******************************************************************************
 
                      Device Boot-time Settings Routines
@@ -1666,7 +1744,7 @@ static inline int deliver_skb(struct sk_buff *skb,
 
 static inline bool skb_loop_sk(struct packet_type *ptype, struct sk_buff *skb)
 {
-       if (ptype->af_packet_priv == NULL)
+       if (!ptype->af_packet_priv || !skb->sk)
                return false;
 
        if (ptype->id_match)
@@ -1994,7 +2072,7 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb,
        netdev_features_t features)
 {
        struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT);
-       struct packet_type *ptype;
+       struct packet_offload *ptype;
        __be16 type = skb->protocol;
        int vlan_depth = ETH_HLEN;
        int err;
@@ -2023,9 +2101,8 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb,
        }
 
        rcu_read_lock();
-       list_for_each_entry_rcu(ptype,
-                       &ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
-               if (ptype->type == type && !ptype->dev && ptype->gso_segment) {
+       list_for_each_entry_rcu(ptype, &offload_base, list) {
+               if (ptype->type == type && ptype->gso_segment) {
                        if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
                                err = ptype->gso_send_check(skb);
                                segs = ERR_PTR(err);
@@ -3444,9 +3521,9 @@ static void flush_backlog(void *arg)
 
 static int napi_gro_complete(struct sk_buff *skb)
 {
-       struct packet_type *ptype;
+       struct packet_offload *ptype;
        __be16 type = skb->protocol;
-       struct list_head *head = &ptype_base[ntohs(type) & PTYPE_HASH_MASK];
+       struct list_head *head = &offload_base;
        int err = -ENOENT;
 
        if (NAPI_GRO_CB(skb)->count == 1) {
@@ -3456,7 +3533,7 @@ static int napi_gro_complete(struct sk_buff *skb)
 
        rcu_read_lock();
        list_for_each_entry_rcu(ptype, head, list) {
-               if (ptype->type != type || ptype->dev || !ptype->gro_complete)
+               if (ptype->type != type || !ptype->gro_complete)
                        continue;
 
                err = ptype->gro_complete(skb);
@@ -3506,9 +3583,9 @@ EXPORT_SYMBOL(napi_gro_flush);
 enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
 {
        struct sk_buff **pp = NULL;
-       struct packet_type *ptype;
+       struct packet_offload *ptype;
        __be16 type = skb->protocol;
-       struct list_head *head = &ptype_base[ntohs(type) & PTYPE_HASH_MASK];
+       struct list_head *head = &offload_base;
        int same_flow;
        int mac_len;
        enum gro_result ret;
@@ -3521,7 +3598,7 @@ enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
 
        rcu_read_lock();
        list_for_each_entry_rcu(ptype, head, list) {
-               if (ptype->type != type || ptype->dev || !ptype->gro_receive)
+               if (ptype->type != type || !ptype->gro_receive)
                        continue;
 
                skb_set_network_header(skb, skb_gro_offset(skb));
@@ -6264,7 +6341,6 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
                goto out;
 
        /* Ensure the device has been registrered */
-       err = -EINVAL;
        if (dev->reg_state != NETREG_REGISTERED)
                goto out;
 
@@ -6662,6 +6738,8 @@ static int __init net_dev_init(void)
        for (i = 0; i < PTYPE_HASH_SIZE; i++)
                INIT_LIST_HEAD(&ptype_base[i]);
 
+       INIT_LIST_HEAD(&offload_base);
+
        if (register_pernet_subsys(&netdev_net_ops))
                goto out;