diff options
33 files changed, 533 insertions, 232 deletions
diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c index be88728f1106..592f597d8951 100644 --- a/drivers/isdn/mISDN/tei.c +++ b/drivers/isdn/mISDN/tei.c @@ -250,7 +250,7 @@ tei_debug(struct FsmInst *fi, char *fmt, ...) static int get_free_id(struct manager *mgr) { - u64 ids = 0; + DECLARE_BITMAP(ids, 64) = { [0 ... BITS_TO_LONGS(64) - 1] = 0 }; int i; struct layer2 *l2; @@ -261,11 +261,11 @@ get_free_id(struct manager *mgr) __func__); return -EBUSY; } - test_and_set_bit(l2->ch.nr, (u_long *)&ids); + __set_bit(l2->ch.nr, ids); } - for (i = 1; i < 64; i++) - if (!test_bit(i, (u_long *)&ids)) - return i; + i = find_next_zero_bit(ids, 64, 1); + if (i < 64) + return i; printk(KERN_WARNING "%s: more as 63 layer2 for one device\n", __func__); return -EBUSY; @@ -274,7 +274,7 @@ get_free_id(struct manager *mgr) static int get_free_tei(struct manager *mgr) { - u64 ids = 0; + DECLARE_BITMAP(ids, 64) = { [0 ... BITS_TO_LONGS(64) - 1] = 0 }; int i; struct layer2 *l2; @@ -288,11 +288,11 @@ get_free_tei(struct manager *mgr) continue; i -= 64; - test_and_set_bit(i, (u_long *)&ids); + __set_bit(i, ids); } - for (i = 0; i < 64; i++) - if (!test_bit(i, (u_long *)&ids)) - return i + 64; + i = find_first_zero_bit(ids, 64); + if (i < 64) + return i + 64; printk(KERN_WARNING "%s: more as 63 dynamic tei for one device\n", __func__); return -1; diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index e15cc11edbbe..7c9d136e74be 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -84,6 +84,10 @@ static inline struct arp_pkt *arp_pkt(const struct sk_buff *skb) /* Forward declaration */ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[]); +static void rlb_purge_src_ip(struct bonding *bond, struct arp_pkt *arp); +static void rlb_src_unlink(struct bonding *bond, u32 index); +static void rlb_src_link(struct bonding *bond, u32 ip_src_hash, + u32 ip_dst_hash); static inline u8 _simple_hash(const u8 *hash_start, int hash_size) { @@ -354,6 +358,18 @@ static int rlb_arp_recv(const struct sk_buff *skb, struct bonding *bond, if (!arp) goto out; + /* We received an ARP from arp->ip_src. + * We might have used this IP address previously (on the bonding host + * itself or on a system that is bridged together with the bond). + * However, if arp->mac_src is different than what is stored in + * rx_hashtbl, some other host is now using the IP and we must prevent + * sending out client updates with this IP address and the old MAC + * address. + * Clean up all hash table entries that have this address as ip_src but + * have a different mac_src. + */ + rlb_purge_src_ip(bond, arp); + if (arp->op_code == htons(ARPOP_REPLY)) { /* update rx hash table for this ARP */ rlb_update_entry_from_arp(bond, arp); @@ -432,9 +448,9 @@ static void rlb_clear_slave(struct bonding *bond, struct slave *slave) _lock_rx_hashtbl_bh(bond); rx_hash_table = bond_info->rx_hashtbl; - index = bond_info->rx_hashtbl_head; + index = bond_info->rx_hashtbl_used_head; for (; index != RLB_NULL_INDEX; index = next_index) { - next_index = rx_hash_table[index].next; + next_index = rx_hash_table[index].used_next; if (rx_hash_table[index].slave == slave) { struct slave *assigned_slave = rlb_next_rx_slave(bond); @@ -519,8 +535,9 @@ static void rlb_update_rx_clients(struct bonding *bond) _lock_rx_hashtbl_bh(bond); - hash_index = bond_info->rx_hashtbl_head; - for (; hash_index != RLB_NULL_INDEX; hash_index = client_info->next) { + hash_index = bond_info->rx_hashtbl_used_head; + for (; hash_index != RLB_NULL_INDEX; + hash_index = client_info->used_next) { client_info = &(bond_info->rx_hashtbl[hash_index]); if (client_info->ntt) { rlb_update_client(client_info); @@ -548,8 +565,9 @@ static void rlb_req_update_slave_clients(struct bonding *bond, struct slave *sla _lock_rx_hashtbl_bh(bond); - hash_index = bond_info->rx_hashtbl_head; - for (; hash_index != RLB_NULL_INDEX; hash_index = client_info->next) { + hash_index = bond_info->rx_hashtbl_used_head; + for (; hash_index != RLB_NULL_INDEX; + hash_index = client_info->used_next) { client_info = &(bond_info->rx_hashtbl[hash_index]); if ((client_info->slave == slave) && @@ -578,8 +596,9 @@ static void rlb_req_update_subnet_clients(struct bonding *bond, __be32 src_ip) _lock_rx_hashtbl(bond); - hash_index = bond_info->rx_hashtbl_head; - for (; hash_index != RLB_NULL_INDEX; hash_index = client_info->next) { + hash_index = bond_info->rx_hashtbl_used_head; + for (; hash_index != RLB_NULL_INDEX; + hash_index = client_info->used_next) { client_info = &(bond_info->rx_hashtbl[hash_index]); if (!client_info->slave) { @@ -625,6 +644,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon /* update mac address from arp */ memcpy(client_info->mac_dst, arp->mac_dst, ETH_ALEN); } + memcpy(client_info->mac_src, arp->mac_src, ETH_ALEN); assigned_slave = client_info->slave; if (assigned_slave) { @@ -647,6 +667,17 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon assigned_slave = rlb_next_rx_slave(bond); if (assigned_slave) { + if (!(client_info->assigned && + client_info->ip_src == arp->ip_src)) { + /* ip_src is going to be updated, + * fix the src hash list + */ + u32 hash_src = _simple_hash((u8 *)&arp->ip_src, + sizeof(arp->ip_src)); + rlb_src_unlink(bond, hash_index); + rlb_src_link(bond, hash_src, hash_index); + } + client_info->ip_src = arp->ip_src; client_info->ip_dst = arp->ip_dst; /* arp->mac_dst is broadcast for arp reqeusts. @@ -654,6 +685,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon * upon receiving an arp reply. */ memcpy(client_info->mac_dst, arp->mac_dst, ETH_ALEN); + memcpy(client_info->mac_src, arp->mac_src, ETH_ALEN); client_info->slave = assigned_slave; if (!ether_addr_equal_64bits(client_info->mac_dst, mac_bcast)) { @@ -669,11 +701,11 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon } if (!client_info->assigned) { - u32 prev_tbl_head = bond_info->rx_hashtbl_head; - bond_info->rx_hashtbl_head = hash_index; - client_info->next = prev_tbl_head; + u32 prev_tbl_head = bond_info->rx_hashtbl_used_head; + bond_info->rx_hashtbl_used_head = hash_index; + client_info->used_next = prev_tbl_head; if (prev_tbl_head != RLB_NULL_INDEX) { - bond_info->rx_hashtbl[prev_tbl_head].prev = + bond_info->rx_hashtbl[prev_tbl_head].used_prev = hash_index; } client_info->assigned = 1; @@ -694,6 +726,12 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond) struct arp_pkt *arp = arp_pkt(skb); struct slave *tx_slave = NULL; + /* Don't modify or load balance ARPs that do not originate locally + * (e.g.,arrive via a bridge). + */ + if (!bond_slave_has_mac(bond, arp->mac_src)) + return NULL; + if (arp->op_code == htons(ARPOP_REPLY)) { /* the arp must be sent on the selected * rx channel @@ -740,8 +778,9 @@ static void rlb_rebalance(struct bonding *bond) _lock_rx_hashtbl_bh(bond); ntt = 0; - hash_index = bond_info->rx_hashtbl_head; - for (; hash_index != RLB_NULL_INDEX; hash_index = client_info->next) { + hash_index = bond_info->rx_hashtbl_used_head; + for (; hash_index != RLB_NULL_INDEX; + hash_index = client_info->used_next) { client_info = &(bond_info->rx_hashtbl[hash_index]); assigned_slave = rlb_next_rx_slave(bond); if (assigned_slave && (client_info->slave != assigned_slave)) { @@ -759,11 +798,113 @@ static void rlb_rebalance(struct bonding *bond) } /* Caller must hold rx_hashtbl lock */ +static void rlb_init_table_entry_dst(struct rlb_client_info *entry) +{ + entry->used_next = RLB_NULL_INDEX; + entry->used_prev = RLB_NULL_INDEX; + entry->assigned = 0; + entry->slave = NULL; + entry->tag = 0; +} +static void rlb_init_table_entry_src(struct rlb_client_info *entry) +{ + entry->src_first = RLB_NULL_INDEX; + entry->src_prev = RLB_NULL_INDEX; + entry->src_next = RLB_NULL_INDEX; +} + static void rlb_init_table_entry(struct rlb_client_info *entry) { memset(entry, 0, sizeof(struct rlb_client_info)); - entry->next = RLB_NULL_INDEX; - entry->prev = RLB_NULL_INDEX; + rlb_init_table_entry_dst(entry); + rlb_init_table_entry_src(entry); +} + +static void rlb_delete_table_entry_dst(struct bonding *bond, u32 index) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + u32 next_index = bond_info->rx_hashtbl[index].used_next; + u32 prev_index = bond_info->rx_hashtbl[index].used_prev; + + if (index == bond_info->rx_hashtbl_used_head) + bond_info->rx_hashtbl_used_head = next_index; + if (prev_index != RLB_NULL_INDEX) + bond_info->rx_hashtbl[prev_index].used_next = next_index; + if (next_index != RLB_NULL_INDEX) + bond_info->rx_hashtbl[next_index].used_prev = prev_index; +} + +/* unlink a rlb hash table entry from the src list */ +static void rlb_src_unlink(struct bonding *bond, u32 index) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + u32 next_index = bond_info->rx_hashtbl[index].src_next; + u32 prev_index = bond_info->rx_hashtbl[index].src_prev; + + bond_info->rx_hashtbl[index].src_next = RLB_NULL_INDEX; + bond_info->rx_hashtbl[index].src_prev = RLB_NULL_INDEX; + + if (next_index != RLB_NULL_INDEX) + bond_info->rx_hashtbl[next_index].src_prev = prev_index; + + if (prev_index == RLB_NULL_INDEX) + return; + + /* is prev_index pointing to the head of this list? */ + if (bond_info->rx_hashtbl[prev_index].src_first == index) + bond_info->rx_hashtbl[prev_index].src_first = next_index; + else + bond_info->rx_hashtbl[prev_index].src_next = next_index; + +} + +static void rlb_delete_table_entry(struct bonding *bond, u32 index) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + struct rlb_client_info *entry = &(bond_info->rx_hashtbl[index]); + + rlb_delete_table_entry_dst(bond, index); + rlb_init_table_entry_dst(entry); + + rlb_src_unlink(bond, index); +} + +/* add the rx_hashtbl[ip_dst_hash] entry to the list + * of entries with identical ip_src_hash + */ +static void rlb_src_link(struct bonding *bond, u32 ip_src_hash, u32 ip_dst_hash) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + u32 next; + + bond_info->rx_hashtbl[ip_dst_hash].src_prev = ip_src_hash; + next = bond_info->rx_hashtbl[ip_src_hash].src_first; + bond_info->rx_hashtbl[ip_dst_hash].src_next = next; + if (next != RLB_NULL_INDEX) + bond_info->rx_hashtbl[next].src_prev = ip_dst_hash; + bond_info->rx_hashtbl[ip_src_hash].src_first = ip_dst_hash; +} + +/* deletes all rx_hashtbl entries with arp->ip_src if their mac_src does + * not match arp->mac_src */ +static void rlb_purge_src_ip(struct bonding *bond, struct arp_pkt *arp) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + u32 ip_src_hash = _simple_hash((u8*)&(arp->ip_src), sizeof(arp->ip_src)); + u32 index; + + _lock_rx_hashtbl_bh(bond); + + index = bond_info->rx_hashtbl[ip_src_hash].src_first; + while (index != RLB_NULL_INDEX) { + struct rlb_client_info *entry = &(bond_info->rx_hashtbl[index]); + u32 next_index = entry->src_next; + if (entry->ip_src == arp->ip_src && + !ether_addr_equal_64bits(arp->mac_src, entry->mac_src)) + rlb_delete_table_entry(bond, index); + index = next_index; + } + _unlock_rx_hashtbl_bh(bond); } static int rlb_initialize(struct bonding *bond) @@ -781,7 +922,7 @@ static int rlb_initialize(struct bonding *bond) bond_info->rx_hashtbl = new_hashtbl; - bond_info->rx_hashtbl_head = RLB_NULL_INDEX; + bond_info->rx_hashtbl_used_head = RLB_NULL_INDEX; for (i = 0; i < RLB_HASH_TABLE_SIZE; i++) { rlb_init_table_entry(bond_info->rx_hashtbl + i); @@ -803,7 +944,7 @@ static void rlb_deinitialize(struct bonding *bond) kfree(bond_info->rx_hashtbl); bond_info->rx_hashtbl = NULL; - bond_info->rx_hashtbl_head = RLB_NULL_INDEX; + bond_info->rx_hashtbl_used_head = RLB_NULL_INDEX; _unlock_rx_hashtbl_bh(bond); } @@ -815,25 +956,13 @@ static void rlb_clear_vlan(struct bonding *bond, unsigned short vlan_id) _lock_rx_hashtbl_bh(bond); - curr_index = bond_info->rx_hashtbl_head; + curr_index = bond_info->rx_hashtbl_used_head; while (curr_index != RLB_NULL_INDEX) { struct rlb_client_info *curr = &(bond_info->rx_hashtbl[curr_index]); - u32 next_index = bond_info->rx_hashtbl[curr_index].next; - u32 prev_index = bond_info->rx_hashtbl[curr_index].prev; - - if (curr->tag && (curr->vlan_id == vlan_id)) { - if (curr_index == bond_info->rx_hashtbl_head) { - bond_info->rx_hashtbl_head = next_index; - } - if (prev_index != RLB_NULL_INDEX) { - bond_info->rx_hashtbl[prev_index].next = next_index; - } - if (next_index != RLB_NULL_INDEX) { - bond_info->rx_hashtbl[next_index].prev = prev_index; - } + u32 next_index = bond_info->rx_hashtbl[curr_index].used_next; - rlb_init_table_entry(curr); - } + if (curr->tag && (curr->vlan_id == vlan_id)) + rlb_delete_table_entry(bond, curr_index); curr_index = next_index; } diff --git a/drivers/net/bonding/bond_alb.h b/drivers/net/bonding/bond_alb.h index 90f140a2d197..e7a5b8b37ea3 100644 --- a/drivers/net/bonding/bond_alb.h +++ b/drivers/net/bonding/bond_alb.h @@ -94,15 +94,35 @@ struct tlb_client_info { /* ------------------------------------------------------------------------- * struct rlb_client_info contains all info related to a specific rx client - * connection. This is the Clients Hash Table entry struct + * connection. This is the Clients Hash Table entry struct. + * Note that this is not a proper hash table; if a new client's IP address + * hash collides with an existing client entry, the old entry is replaced. + * + * There is a linked list (linked by the used_next and used_prev members) + * linking all the used entries of the hash table. This allows updating + * all the clients without walking over all the unused elements of the table. + * + * There are also linked lists of entries with identical hash(ip_src). These + * allow cleaning up the table from ip_src<->mac_src associations that have + * become outdated and would cause sending out invalid ARP updates to the + * network. These are linked by the (src_next and src_prev members). * ------------------------------------------------------------------------- */ struct rlb_client_info { __be32 ip_src; /* the server IP address */ __be32 ip_dst; /* the client IP address */ + u8 mac_src[ETH_ALEN]; /* the server MAC address */ u8 mac_dst[ETH_ALEN]; /* the client MAC address */ - u32 next; /* The next Hash table entry index */ - u32 prev; /* The previous Hash table entry index */ + + /* list of used hash table entries, starting at rx_hashtbl_used_head */ + u32 used_next; + u32 used_prev; + + /* ip_src based hashing */ + u32 src_next; /* next entry with same hash(ip_src) */ + u32 src_prev; /* prev entry with same hash(ip_src) */ + u32 src_first; /* first entry with hash(ip_src) == this entry's index */ + u8 assigned; /* checking whether this entry is assigned */ u8 ntt; /* flag - need to transmit client info */ struct slave *slave; /* the slave assigned to this client */ @@ -131,7 +151,7 @@ struct alb_bond_info { int rlb_enabled; struct rlb_client_info *rx_hashtbl; /* Receive hash table */ spinlock_t rx_hashtbl_lock; - u32 rx_hashtbl_head; + u32 rx_hashtbl_used_head; u8 rx_ntt; /* flag - need to transmit * to all rx clients */ diff --git a/drivers/net/bonding/bond_debugfs.c b/drivers/net/bonding/bond_debugfs.c index 2cf084eb9d52..5fc4c2351478 100644 --- a/drivers/net/bonding/bond_debugfs.c +++ b/drivers/net/bonding/bond_debugfs.c @@ -31,8 +31,9 @@ static int bond_debug_rlb_hash_show(struct seq_file *m, void *v) spin_lock_bh(&(BOND_ALB_INFO(bond).rx_hashtbl_lock)); - hash_index = bond_info->rx_hashtbl_head; - for (; hash_index != RLB_NULL_INDEX; hash_index = client_info->next) { + hash_index = bond_info->rx_hashtbl_used_head; + for (; hash_index != RLB_NULL_INDEX; + hash_index = client_info->used_next) { client_info = &(bond_info->rx_hashtbl[hash_index]); seq_printf(m, "%-15pI4 %-15pI4 %-17pM %s\n", &client_info->ip_src, diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index f8af2fcd3d16..6dded569b111 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -22,6 +22,7 @@ #include <linux/in6.h> #include <linux/netpoll.h> #include <linux/inetdevice.h> +#include <linux/etherdevice.h> #include "bond_3ad.h" #include "bond_alb.h" @@ -450,6 +451,18 @@ static inline void bond_destroy_proc_dir(struct bond_net *bn) } #endif +static inline struct slave *bond_slave_has_mac(struct bonding *bond, + const u8 *mac) +{ + int i = 0; + struct slave *tmp; + + bond_for_each_slave(bond, tmp, i) + if (ether_addr_equal_64bits(mac, tmp->dev->dev_addr)) + return tmp; + + return NULL; +} /* exported from bond_main.c */ extern int bond_net_id; diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index 963e2ccd10db..8233e5ed2939 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -609,8 +609,7 @@ void close_candev(struct net_device *dev) { struct can_priv *priv = netdev_priv(dev); - if (del_timer_sync(&priv->restart_timer)) - dev_put(dev); + del_timer_sync(&priv->restart_timer); can_flush_echo_skb(dev); } EXPORT_SYMBOL_GPL(close_candev); diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c index 2b104d5f422c..e6b40954e204 100644 --- a/drivers/net/can/mscan/mscan.c +++ b/drivers/net/can/mscan/mscan.c @@ -517,12 +517,8 @@ static irqreturn_t mscan_isr(int irq, void *dev_id) static int mscan_do_set_mode(struct net_device *dev, enum can_mode mode) { - struct mscan_priv *priv = netdev_priv(dev); int ret = 0; - if (!priv->open_time) - return -EINVAL; - switch (mode) { case CAN_MODE_START: ret = mscan_restart(dev); @@ -590,8 +586,6 @@ static int mscan_open(struct net_device *dev) goto exit_napi_disable; } - priv->open_time = jiffies; - if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) setbits8(®s->canctl1, MSCAN_LISTEN); else @@ -606,7 +600,6 @@ static int mscan_open(struct net_device *dev) return 0; exit_free_irq: - priv->open_time = 0; free_irq(dev->irq, dev); exit_napi_disable: napi_disable(&priv->napi); @@ -627,7 +620,6 @@ static int mscan_close(struct net_device *dev) mscan_set_mode(dev, MSCAN_INIT_MODE); close_candev(dev); free_irq(dev->irq, dev); - priv->open_time = 0; return 0; } diff --git a/drivers/net/can/mscan/mscan.h b/drivers/net/can/mscan/mscan.h index b43e9f5d3268..af2ed8baf0a3 100644 --- a/drivers/net/can/mscan/mscan.h +++ b/drivers/net/can/mscan/mscan.h @@ -281,7 +281,6 @@ struct tx_queue_entry { struct mscan_priv { struct can_priv can; /* must be the first member */ unsigned int type; /* MSCAN type variants */ - long open_time; unsigned long flags; void __iomem *reg_base; /* ioremap'ed address to registers */ u8 shadow_statflg; diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c index 25011dbe1b96..83ee11eca0e2 100644 --- a/drivers/net/can/sja1000/sja1000.c +++ b/drivers/net/can/sja1000/sja1000.c @@ -188,11 +188,6 @@ static void sja1000_start(struct net_device *dev) static int sja1000_set_mode(struct net_device *dev, enum can_mode mode) { - struct sja1000_priv *priv = netdev_priv(dev); - - if (!priv->open_time) - return -EINVAL; - switch (mode) { case CAN_MODE_START: sja1000_start(dev); @@ -579,7 +574,6 @@ static int sja1000_open(struct net_device *dev) /* init and start chi */ sja1000_start(dev); - priv->open_time = jiffies; netif_start_queue(dev); @@ -598,8 +592,6 @@ static int sja1000_close(struct net_device *dev) close_candev(dev); - priv->open_time = 0; - return 0; } diff --git a/drivers/net/can/sja1000/sja1000.h b/drivers/net/can/sja1000/sja1000.h index 23fff06875f5..afa99847a510 100644 --- a/drivers/net/can/sja1000/sja1000.h +++ b/drivers/net/can/sja1000/sja1000.h @@ -152,7 +152,6 @@ */ struct sja1000_priv { struct can_priv can; /* must be the first member */ - int open_time; struct sk_buff *echo_skb; /* the lower-layer is responsible for appropriate locking */ diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c index 086fa321677a..c69f0b72b352 100644 --- a/drivers/net/can/usb/ems_usb.c +++ b/drivers/net/can/usb/ems_usb.c @@ -245,7 +245,6 @@ struct ems_tx_urb_context { struct ems_usb { struct can_priv can; /* must be the first member */ - int open_time; struct sk_buff *echo_skb[MAX_TX_URBS]; @@ -728,7 +727,6 @@ static int ems_usb_open(struct net_device *netdev) return err; } - dev->open_time = jiffies; netif_start_queue(netdev); @@ -878,8 +876,6 @@ static int ems_usb_close(struct net_device *netdev) close_candev(netdev); - dev->open_time = 0; - return 0; } @@ -905,9 +901,6 @@ static int ems_usb_set_mode(struct net_device *netdev, enum can_mode mode) { struct ems_usb *dev = netdev_priv(netdev); - if (!dev->open_time) - return -EINVAL; - switch (mode) { case CAN_MODE_START: if (ems_usb_write_mode(dev, SJA1000_MOD_NORMAL)) diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c index 124e0dd3490c..9b74d1e3ad44 100644 --- a/drivers/net/can/usb/esd_usb2.c +++ b/drivers/net/can/usb/esd_usb2.c @@ -217,7 +217,6 @@ struct esd_usb2_net_priv { struct usb_anchor tx_submitted; struct esd_tx_urb_context tx_contexts[MAX_TX_URBS]; - int open_time; struct esd_usb2 *usb2; struct net_device *netdev; int index; @@ -695,8 +694,6 @@ static int esd_usb2_open(struct net_device *netdev) return err; } - priv->open_time = jiffies; - netif_start_queue(netdev); return 0; @@ -864,8 +861,6 @@ static int esd_usb2_close(struct net_device *netdev) close_candev(netdev); - priv->open_time = 0; - return 0; } @@ -941,11 +936,6 @@ static int esd_usb2_get_berr_counter(const struct net_device *netdev, static int esd_usb2_set_mode(struct net_device *netdev, enum can_mode mode) { - struct esd_usb2_net_priv *priv = netdev_priv(netdev); - - if (!priv->open_time) - return -EINVAL; - switch (mode) { case CAN_MODE_START: netif_wake_queue(netdev); diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c index c4643c400d46..d9290ea788e0 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -520,7 +520,6 @@ static int peak_usb_ndo_open(struct net_device *netdev) return err; } - dev->open_time = jiffies; netif_start_queue(netdev); return 0; @@ -576,7 +575,6 @@ static int peak_usb_ndo_stop(struct net_device *netdev) close_candev(netdev); - dev->open_time = 0; dev->can.state = CAN_STATE_STOPPED; /* can set bus off now */ @@ -661,9 +659,6 @@ static int peak_usb_set_mode(struct net_device *netdev, enum can_mode mode) struct peak_usb_device *dev = netdev_priv(netdev); int err = 0; - if (!dev->open_time) - return -EINVAL; - switch (mode) { case CAN_MODE_START: err = peak_usb_restart(dev); diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h index c8e5e91d7cb5..073b47ff8eee 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.h +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h @@ -104,7 +104,6 @@ struct peak_usb_device { struct can_priv can; struct peak_usb_adapter *adapter; unsigned int ctrl_idx; - int open_time; u32 state; struct sk_buff *echo_skb[PCAN_USB_MAX_TX_URBS]; diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h index 5f84c6229dc6..610208b18c05 100644 --- a/include/linux/netfilter_ipv6/ip6_tables.h +++ b/include/linux/netfilter_ipv6/ip6_tables.h @@ -47,15 +47,6 @@ ip6t_ext_hdr(u8 nexthdr) (nexthdr == IPPROTO_DSTOPTS); } -enum { - IP6T_FH_F_FRAG = (1 << 0), - IP6T_FH_F_AUTH = (1 << 1), -}; - -/* find specified header and get offset to it */ -extern int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, - int target, unsigned short *fragoff, int *fragflg); - #ifdef CONFIG_COMPAT #include <net/compat.h> diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h index eb1efa54fe84..d42e174bd0c8 100644 --- a/include/linux/openvswitch.h +++ b/include/linux/openvswitch.h @@ -243,6 +243,7 @@ enum ovs_key_attr { OVS_KEY_ATTR_ICMPV6, /* struct ovs_key_icmpv6 */ OVS_KEY_ATTR_ARP, /* struct ovs_key_arp */ OVS_KEY_ATTR_ND, /* struct ovs_key_nd */ + OVS_KEY_ATTR_SKB_MARK, /* u32 skb mark */ __OVS_KEY_ATTR_MAX }; diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 979bf6c13141..acbd8e034310 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -630,6 +630,16 @@ extern int ipv6_skip_exthdr(const struct sk_buff *, int start, extern bool ipv6_ext_hdr(u8 nexthdr); +enum { + IP6_FH_F_FRAG = (1 << 0), + IP6_FH_F_AUTH = (1 << 1), + IP6_FH_F_SKIP_RH = (1 << 2), +}; + +/* find specified header and get offset to it */ +extern int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, + int target, unsigned short *fragoff, int *fragflg); + extern int ipv6_find_tlv(struct sk_buff *skb, int offset, int type); extern struct in6_addr *fl6_update_dst(struct flowi6 *fl6, diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index afba51e60310..a292e8050ef2 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -242,6 +242,7 @@ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id) * hope the underlying device can handle it. */ new_dev->mtu = real_dev->mtu; + new_dev->priv_flags |= (real_dev->priv_flags & IFF_UNICAST_FLT); vlan_dev_priv(new_dev)->vlan_id = vlan_id; vlan_dev_priv(new_dev)->real_dev = real_dev; diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index 6d42c17af96b..f651da60f161 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -1047,7 +1047,8 @@ static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) goto error; } - if (skb->len <= IEEE802154_MTU) { + /* Send directly if less than the MTU minus the 2 checksum bytes. */ + if (skb->len <= IEEE802154_MTU - IEEE802154_MFR_SIZE) { err = dev_queue_xmit(skb); goto out; } diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c index e7d756e19d1d..c5e83fae4df4 100644 --- a/net/ipv6/exthdrs_core.c +++ b/net/ipv6/exthdrs_core.c @@ -155,3 +155,127 @@ int ipv6_find_tlv(struct sk_buff *skb, int offset, int type) return -1; } EXPORT_SYMBOL_GPL(ipv6_find_tlv); + +/* + * find the offset to specified header or the protocol number of last header + * if target < 0. "last header" is transport protocol header, ESP, or + * "No next header". + * + * Note that *offset is used as input/output parameter. an if it is not zero, + * then it must be a valid offset to an inner IPv6 header. This can be used + * to explore inner IPv6 header, eg. ICMPv6 error messages. + * + * If target header is found, its offset is set in *offset and return protocol + * number. Otherwise, return -1. + * + * If the first fragment doesn't contain the final protocol header or + * NEXTHDR_NONE it is considered invalid. + * + * Note that non-1st fragment is special case that "the protocol number + * of last header" is "next header" field in Fragment header. In this case, + * *offset is meaningless and fragment offset is stored in *fragoff if fragoff + * isn't NULL. + * + * if flags is not NULL and it's a fragment, then the frag flag + * IP6_FH_F_FRAG will be set. If it's an AH header, the + * IP6_FH_F_AUTH flag is set and target < 0, then this function will + * stop at the AH header. If IP6_FH_F_SKIP_RH flag was passed, then this + * function will skip all those routing headers, where segements_left was 0. + */ +int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, + int target, unsigned short *fragoff, int *flags) +{ + unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); + u8 nexthdr = ipv6_hdr(skb)->nexthdr; + unsigned int len; + bool found; + + if (fragoff) + *fragoff = 0; + + if (*offset) { + struct ipv6hdr _ip6, *ip6; + + ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6); + if (!ip6 || (ip6->version != 6)) { + printk(KERN_ERR "IPv6 header not found\n"); + return -EBADMSG; + } + start = *offset + sizeof(struct ipv6hdr); + nexthdr = ip6->nexthdr; + } + len = skb->len - start; + + do { + struct ipv6_opt_hdr _hdr, *hp; + unsigned int hdrlen; + found = (nexthdr == target); + + if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) { + if (target < 0) + break; + return -ENOENT; + } + + hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); + if (hp == NULL) + return -EBADMSG; + + if (nexthdr == NEXTHDR_ROUTING) { + struct ipv6_rt_hdr _rh, *rh; + + rh = skb_header_pointer(skb, start, sizeof(_rh), + &_rh); + if (rh == NULL) + return -EBADMSG; + + if (flags && (*flags & IP6_FH_F_SKIP_RH) && + rh->segments_left == 0) + found = false; + } + + if (nexthdr == NEXTHDR_FRAGMENT) { + unsigned short _frag_off; + __be16 *fp; + + if (flags) /* Indicate that this is a fragment */ + *flags |= IP6_FH_F_FRAG; + fp = skb_header_pointer(skb, + start+offsetof(struct frag_hdr, + frag_off), + sizeof(_frag_off), + &_frag_off); + if (fp == NULL) + return -EBADMSG; + + _frag_off = ntohs(*fp) & ~0x7; + if (_frag_off) { + if (target < 0 && + ((!ipv6_ext_hdr(hp->nexthdr)) || + hp->nexthdr == NEXTHDR_NONE)) { + if (fragoff) + *fragoff = _frag_off; + return hp->nexthdr; + } + return -ENOENT; + } + hdrlen = 8; + } else if (nexthdr == NEXTHDR_AUTH) { + if (flags && (*flags & IP6_FH_F_AUTH) && (target < 0)) + break; + hdrlen = (hp->hdrlen + 2) << 2; + } else + hdrlen = ipv6_optlen(hp); + + if (!found) { + nexthdr = hp->nexthdr; + len -= hdrlen; + start += hdrlen; + } + } while (!found); + + *offset = start; + return nexthdr; +} +EXPORT_SYMBOL(ipv6_find_hdr); + diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 74cadd0719a5..125a90d6a795 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -2271,112 +2271,9 @@ static void __exit ip6_tables_fini(void) unregister_pernet_subsys(&ip6_tables_net_ops); } -/* - * find the offset to specified header or the protocol number of last header - * if target < 0. "last header" is transport protocol header, ESP, or - * "No next header". - * - * Note that *offset is used as input/output parameter. an if it is not zero, - * then it must be a valid offset to an inner IPv6 header. This can be used - * to explore inner IPv6 header, eg. ICMPv6 error messages. - * - * If target header is found, its offset is set in *offset and return protocol - * number. Otherwise, return -1. - * - * If the first fragment doesn't contain the final protocol header or - * NEXTHDR_NONE it is considered invalid. - * - * Note that non-1st fragment is special case that "the protocol number - * of last header" is "next header" field in Fragment header. In this case, - * *offset is meaningless and fragment offset is stored in *fragoff if fragoff - * isn't NULL. - * - * if flags is not NULL and it's a fragment, then the frag flag IP6T_FH_F_FRAG - * will be set. If it's an AH header, the IP6T_FH_F_AUTH flag is set and - * target < 0, then this function will stop at the AH header. - */ -int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, - int target, unsigned short *fragoff, int *flags) -{ - unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); - u8 nexthdr = ipv6_hdr(skb)->nexthdr; - unsigned int len; - - if (fragoff) - *fragoff = 0; - - if (*offset) { - struct ipv6hdr _ip6, *ip6; - - ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6); - if (!ip6 || (ip6->version != 6)) { - printk(KERN_ERR "IPv6 header not found\n"); - return -EBADMSG; - } - start = *offset + sizeof(struct ipv6hdr); - nexthdr = ip6->nexthdr; - } - len = skb->len - start; - - while (nexthdr != target) { - struct ipv6_opt_hdr _hdr, *hp; - unsigned int hdrlen; - - if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) { - if (target < 0) - break; - return -ENOENT; - } - - hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); - if (hp == NULL) - return -EBADMSG; - if (nexthdr == NEXTHDR_FRAGMENT) { - unsigned short _frag_off; - __be16 *fp; - - if (flags) /* Indicate that this is a fragment */ - *flags |= IP6T_FH_F_FRAG; - fp = skb_header_pointer(skb, - start+offsetof(struct frag_hdr, - frag_off), - sizeof(_frag_off), - &_frag_off); - if (fp == NULL) - return -EBADMSG; - - _frag_off = ntohs(*fp) & ~0x7; - if (_frag_off) { - if (target < 0 && - ((!ipv6_ext_hdr(hp->nexthdr)) || - hp->nexthdr == NEXTHDR_NONE)) { - if (fragoff) - *fragoff = _frag_off; - return hp->nexthdr; - } - return -ENOENT; - } - hdrlen = 8; - } else if (nexthdr == NEXTHDR_AUTH) { - if (flags && (*flags & IP6T_FH_F_AUTH) && (target < 0)) - break; - hdrlen = (hp->hdrlen + 2) << 2; - } else - hdrlen = ipv6_optlen(hp); - - nexthdr = hp->nexthdr; - len -= hdrlen; - start += hdrlen; - } - - *offset = start; - return nexthdr; -} - EXPORT_SYMBOL(ip6t_register_table); EXPORT_SYMBOL(ip6t_unregister_table); EXPORT_SYMBOL(ip6t_do_table); -EXPORT_SYMBOL(ipv6_find_hdr); module_init(ip6_tables_init); module_exit(ip6_tables_fini); diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index 1a4df39c722e..4e09d070995a 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -85,6 +85,7 @@ netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb, if (!(priv->phy->channels_supported[page] & (1 << chan))) { WARN_ON(1); + kfree_skb(skb); return NETDEV_TX_OK; } @@ -98,13 +99,15 @@ netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb, } if (skb_cow_head(skb, priv->hw.extra_tx_headroom)) { - dev_kfree_skb(skb); + kfree_skb(skb); return NETDEV_TX_OK; } work = kzalloc(sizeof(struct xmit_work), GFP_ATOMIC); - if (!work) + if (!work) { + kfree_skb(skb); return NETDEV_TX_BUSY; + } INIT_WORK(&work->work, mac802154_xmit_worker); work->skb = skb; diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c index f30f6d4beea1..1191039c2b1b 100644 --- a/net/mac802154/wpan.c +++ b/net/mac802154/wpan.c @@ -327,8 +327,10 @@ mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev) if (chan == MAC802154_CHAN_NONE || page >= WPAN_NUM_PAGES || - chan >= WPAN_NUM_CHANNELS) + chan >= WPAN_NUM_CHANNELS) { + kfree_skb(skb); return NETDEV_TX_OK; + } skb->skb_iif = dev->ifindex; dev->stats.tx_packets++; diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index fb45640dc1fb..47edf5a40a59 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -942,7 +942,7 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related, /* Fragment header that is before ICMP header tells us that: * it's not an error message since they can't be fragmented. */ - if (ipvsh->flags & IP6T_FH_F_FRAG) + if (ipvsh->flags & IP6_FH_F_FRAG) return NF_DROP; IP_VS_DBG(8, "Outgoing ICMPv6 (%d,%d) %pI6c->%pI6c\n", @@ -1475,7 +1475,7 @@ static int ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, /* Fragment header that is before ICMP header tells us that: * it's not an error message since they can't be fragmented. */ - if (iph->flags & IP6T_FH_F_FRAG) + if (iph->flags & IP6_FH_F_FRAG) return NF_DROP; IP_VS_DBG(8, "Incoming ICMPv6 (%d,%d) %pI6c->%pI6c\n", diff --git a/net/netfilter/xt_HMARK.c b/net/netfilter/xt_HMARK.c index 1686ca1b53a1..73b73f687c58 100644 --- a/net/netfilter/xt_HMARK.c +++ b/net/netfilter/xt_HMARK.c @@ -167,7 +167,7 @@ hmark_pkt_set_htuple_ipv6(const struct sk_buff *skb, struct hmark_tuple *t, const struct xt_hmark_info *info) { struct ipv6hdr *ip6, _ip6; - int flag = IP6T_FH_F_AUTH; + int flag = IP6_FH_F_AUTH; unsigned int nhoff = 0; u16 fragoff = 0; int nexthdr; @@ -177,7 +177,7 @@ hmark_pkt_set_htuple_ipv6(const struct sk_buff *skb, struct hmark_tuple *t, if (nexthdr < 0) return 0; /* No need to check for icmp errors on fragments */ - if ((flag & IP6T_FH_F_FRAG) || (nexthdr != IPPROTO_ICMPV6)) + if ((flag & IP6_FH_F_FRAG) || (nexthdr != IPPROTO_ICMPV6)) goto noicmp; /* Use inner header in case of ICMP errors */ if (get_inner6_hdr(skb, &nhoff)) { @@ -185,7 +185,7 @@ hmark_pkt_set_htuple_ipv6(const struct sk_buff *skb, struct hmark_tuple *t, if (ip6 == NULL) return -1; /* If AH present, use SPI like in ESP. */ - flag = IP6T_FH_F_AUTH; + flag = IP6_FH_F_AUTH; nexthdr = ipv6_find_hdr(skb, &nhoff, -1, &fragoff, &flag); if (nexthdr < 0) return -1; @@ -201,7 +201,7 @@ noicmp: if (t->proto == IPPROTO_ICMPV6) return 0; - if (flag & IP6T_FH_F_FRAG) + if (flag & IP6_FH_F_FRAG) return 0; hmark_set_tuple_ports(skb, nhoff, t, info); diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 08114478cb85..ac2defeeba83 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -28,6 +28,7 @@ #include <linux/if_arp.h> #include <linux/if_vlan.h> #include <net/ip.h> +#include <net/ipv6.h> #include <net/checksum.h> #include <net/dsfield.h> @@ -162,6 +163,53 @@ static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh, *addr = new_addr; } +static void update_ipv6_checksum(struct sk_buff *skb, u8 l4_proto, + __be32 addr[4], const __be32 new_addr[4]) +{ + int transport_len = skb->len - skb_transport_offset(skb); + + if (l4_proto == IPPROTO_TCP) { + if (likely(transport_len >= sizeof(struct tcphdr))) + inet_proto_csum_replace16(&tcp_hdr(skb)->check, skb, + addr, new_addr, 1); + } else if (l4_proto == IPPROTO_UDP) { + if (likely(transport_len >= sizeof(struct udphdr))) { + struct udphdr *uh = udp_hdr(skb); + + if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) { + inet_proto_csum_replace16(&uh->check, skb, + addr, new_addr, 1); + if (!uh->check) + uh->check = CSUM_MANGLED_0; + } + } + } +} + +static void set_ipv6_addr(struct sk_buff *skb, u8 l4_proto, + __be32 addr[4], const __be32 new_addr[4], + bool recalculate_csum) +{ + if (recalculate_csum) + update_ipv6_checksum(skb, l4_proto, addr, new_addr); + + skb->rxhash = 0; + memcpy(addr, new_addr, sizeof(__be32[4])); +} + +static void set_ipv6_tc(struct ipv6hdr *nh, u8 tc) +{ + nh->priority = tc >> 4; + nh->flow_lbl[0] = (nh->flow_lbl[0] & 0x0F) | ((tc & 0x0F) << 4); +} + +static void set_ipv6_fl(struct ipv6hdr *nh, u32 fl) +{ + nh->flow_lbl[0] = (nh->flow_lbl[0] & 0xF0) | (fl & 0x000F0000) >> 16; + nh->flow_lbl[1] = (fl & 0x0000FF00) >> 8; + nh->flow_lbl[2] = fl & 0x000000FF; +} + static void set_ip_ttl(struct sk_buff *skb, struct iphdr *nh, u8 new_ttl) { csum_replace2(&nh->check, htons(nh->ttl << 8), htons(new_ttl << 8)); @@ -195,6 +243,47 @@ static int set_ipv4(struct sk_buff *skb, const struct ovs_key_ipv4 *ipv4_key) return 0; } +static int set_ipv6(struct sk_buff *skb, const struct ovs_key_ipv6 *ipv6_key) +{ + struct ipv6hdr *nh; + int err; + __be32 *saddr; + __be32 *daddr; + + err = make_writable(skb, skb_network_offset(skb) + + sizeof(struct ipv6hdr)); + if (unlikely(err)) + return err; + + nh = ipv6_hdr(skb); + saddr = (__be32 *)&nh->saddr; + daddr = (__be32 *)&nh->daddr; + + if (memcmp(ipv6_key->ipv6_src, saddr, sizeof(ipv6_key->ipv6_src))) + set_ipv6_addr(skb, ipv6_key->ipv6_proto, saddr, + ipv6_key->ipv6_src, true); + + if (memcmp(ipv6_key->ipv6_dst, daddr, sizeof(ipv6_key->ipv6_dst))) { + unsigned int offset = 0; + int flags = IP6_FH_F_SKIP_RH; + bool recalc_csum = true; + + if (ipv6_ext_hdr(nh->nexthdr)) + recalc_csum = ipv6_find_hdr(skb, &offset, + NEXTHDR_ROUTING, NULL, + &flags) != NEXTHDR_ROUTING; + + set_ipv6_addr(skb, ipv6_key->ipv6_proto, daddr, + ipv6_key->ipv6_dst, recalc_csum); + } + + set_ipv6_tc(nh, ipv6_key->ipv6_tclass); + set_ipv6_fl(nh, ntohl(ipv6_key->ipv6_label)); + nh->hop_limit = ipv6_key->ipv6_hlimit; + + return 0; +} + /* Must follow make_writable() since that can move the skb data. */ static void set_tp_port(struct sk_buff *skb, __be16 *port, __be16 new_port, __sum16 *check) @@ -339,6 +428,10 @@ static int execute_set_action(struct sk_buff *skb, skb->priority = nla_get_u32(nested_attr); break; + case OVS_KEY_ATTR_SKB_MARK: + skb->mark = nla_get_u32(nested_attr); + break; + case OVS_KEY_ATTR_ETHERNET: err = set_eth_addr(skb, nla_data(nested_attr)); break; @@ -347,6 +440,10 @@ static int execute_set_action(struct sk_buff *skb, err = set_ipv4(skb, nla_data(nested_attr)); break; + case OVS_KEY_ATTR_IPV6: + err = set_ipv6(skb, nla_data(nested_attr)); + break; + case OVS_KEY_ATTR_TCP: err = set_tcp(skb, nla_data(nested_attr)); break; diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 4c4b62ccc7d7..f996db343247 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -208,7 +208,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb) int error; int key_len; - stats = per_cpu_ptr(dp->stats_percpu, smp_processor_id()); + stats = this_cpu_ptr(dp->stats_percpu); /* Extract flow from 'skb' into 'key'. */ error = ovs_flow_extract(skb, p->port_no, &key, &key_len); @@ -282,7 +282,7 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb, return 0; err: - stats = per_cpu_ptr(dp->stats_percpu, smp_processor_id()); + stats = this_cpu_ptr(dp->stats_percpu); u64_stats_update_begin(&stats->sync); stats->n_lost++; @@ -479,8 +479,10 @@ static int validate_set(const struct nlattr *a, switch (key_type) { const struct ovs_key_ipv4 *ipv4_key; + const struct ovs_key_ipv6 *ipv6_key; case OVS_KEY_ATTR_PRIORITY: + case OVS_KEY_ATTR_SKB_MARK: case OVS_KEY_ATTR_ETHERNET: break; @@ -500,6 +502,25 @@ static int validate_set(const struct nlattr *a, break; + case OVS_KEY_ATTR_IPV6: + if (flow_key->eth.type != htons(ETH_P_IPV6)) + return -EINVAL; + + if (!flow_key->ip.proto) + return -EINVAL; + + ipv6_key = nla_data(ovs_key); + if (ipv6_key->ipv6_proto != flow_key->ip.proto) + return -EINVAL; + + if (ipv6_key->ipv6_frag != flow_key->ip.frag) + return -EINVAL; + + if (ntohl(ipv6_key->ipv6_label) & 0xFFF00000) + return -EINVAL; + + break; + case OVS_KEY_ATTR_TCP: if (flow_key->ip.proto != IPPROTO_TCP) return -EINVAL; @@ -675,6 +696,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) goto err_flow_free; err = ovs_flow_metadata_from_nlattrs(&flow->key.phy.priority, + &flow->key.phy.skb_mark, &flow->key.phy.in_port, a[OVS_PACKET_ATTR_KEY]); if (err) @@ -694,6 +716,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) OVS_CB(packet)->flow = flow; packet->priority = flow->key.phy.priority; + packet->mark = flow->key.phy.skb_mark; rcu_read_lock(); dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 733cbf49ed1f..c3294cebc4f2 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -604,6 +604,7 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key, key->phy.priority = skb->priority; key->phy.in_port = in_port; + key->phy.skb_mark = skb->mark; skb_reset_mac_header(skb); @@ -689,7 +690,8 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key, } } - } else if (key->eth.type == htons(ETH_P_ARP) && arphdr_ok(skb)) { + } else if ((key->eth.type == htons(ETH_P_ARP) || + key->eth.type == htons(ETH_P_RARP)) && arphdr_ok(skb)) { struct arp_eth_header *arp; arp = (struct arp_eth_header *)skb_network_header(skb); @@ -802,6 +804,7 @@ const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { [OVS_KEY_ATTR_ENCAP] = -1, [OVS_KEY_ATTR_PRIORITY] = sizeof(u32), [OVS_KEY_ATTR_IN_PORT] = sizeof(u32), + [OVS_KEY_ATTR_SKB_MARK] = sizeof(u32), [OVS_KEY_ATTR_ETHERNET] = sizeof(struct ovs_key_ethernet), [OVS_KEY_ATTR_VLAN] = sizeof(__be16), [OVS_KEY_ATTR_ETHERTYPE] = sizeof(__be16), @@ -987,6 +990,10 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, } else { swkey->phy.in_port = DP_MAX_PORTS; } + if (attrs & (1 << OVS_KEY_ATTR_SKB_MARK)) { + swkey->phy.skb_mark = nla_get_u32(a[OVS_KEY_ATTR_SKB_MARK]); + attrs &= ~(1 << OVS_KEY_ATTR_SKB_MARK); + } /* Data attributes. */ if (!(attrs & (1 << OVS_KEY_ATTR_ETHERNET))) @@ -1086,7 +1093,8 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, if (err) return err; } - } else if (swkey->eth.type == htons(ETH_P_ARP)) { + } else if (swkey->eth.type == htons(ETH_P_ARP) || + swkey->eth.type == htons(ETH_P_RARP)) { const struct ovs_key_arp *arp_key; if (!(attrs & (1 << OVS_KEY_ATTR_ARP))) @@ -1113,6 +1121,8 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, /** * ovs_flow_metadata_from_nlattrs - parses Netlink attributes into a flow key. + * @priority: receives the skb priority + * @mark: receives the skb mark * @in_port: receives the extracted input port. * @key: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute * sequence. @@ -1122,7 +1132,7 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, * get the metadata, that is, the parts of the flow key that cannot be * extracted from the packet itself. */ -int ovs_flow_metadata_from_nlattrs(u32 *priority, u16 *in_port, +int ovs_flow_metadata_from_nlattrs(u32 *priority, u32 *mark, u16 *in_port, const struct nlattr *attr) { const struct nlattr *nla; @@ -1130,6 +1140,7 @@ int ovs_flow_metadata_from_nlattrs(u32 *priority, u16 *in_port, *in_port = DP_MAX_PORTS; *priority = 0; + *mark = 0; nla_for_each_nested(nla, attr, rem) { int type = nla_type(nla); @@ -1148,6 +1159,10 @@ int ovs_flow_metadata_from_nlattrs(u32 *priority, u16 *in_port, return -EINVAL; *in_port = nla_get_u32(nla); break; + + case OVS_KEY_ATTR_SKB_MARK: + *mark = nla_get_u32(nla); + break; } } } @@ -1169,6 +1184,10 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, swkey->phy.in_port)) goto nla_put_failure; + if (swkey->phy.skb_mark && + nla_put_u32(skb, OVS_KEY_ATTR_SKB_MARK, swkey->phy.skb_mark)) + goto nla_put_failure; + nla = nla_reserve(skb, OVS_KEY_ATTR_ETHERNET, sizeof(*eth_key)); if (!nla) goto nla_put_failure; @@ -1222,7 +1241,8 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) ipv6_key->ipv6_tclass = swkey->ip.tos; ipv6_key->ipv6_hlimit = swkey->ip.ttl; ipv6_key->ipv6_frag = swkey->ip.frag; - } else if (swkey->eth.type == htons(ETH_P_ARP)) { + } else if (swkey->eth.type == htons(ETH_P_ARP) || + swkey->eth.type == htons(ETH_P_RARP)) { struct ovs_key_arp *arp_key; nla = nla_reserve(skb, OVS_KEY_ATTR_ARP, sizeof(*arp_key)); diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h index 14a324eb017b..a7bb60ff3b5b 100644 --- a/net/openvswitch/flow.h +++ b/net/openvswitch/flow.h @@ -43,6 +43,7 @@ struct sw_flow_actions { struct sw_flow_key { struct { u32 priority; /* Packet QoS priority. */ + u32 skb_mark; /* SKB mark. */ u16 in_port; /* Input switch port (or DP_MAX_PORTS). */ } phy; struct { @@ -144,6 +145,7 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies); * ------ --- ------ ----- * OVS_KEY_ATTR_PRIORITY 4 -- 4 8 * OVS_KEY_ATTR_IN_PORT 4 -- 4 8 + * OVS_KEY_ATTR_SKB_MARK 4 -- 4 8 * OVS_KEY_ATTR_ETHERNET 12 -- 4 16 * OVS_KEY_ATTR_ETHERTYPE 2 2 4 8 (outer VLAN ethertype) * OVS_KEY_ATTR_8021Q 4 -- 4 8 @@ -153,14 +155,14 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies); * OVS_KEY_ATTR_ICMPV6 2 2 4 8 * OVS_KEY_ATTR_ND 28 -- 4 32 * ------------------------------------------------- - * total 144 + * total 152 */ -#define FLOW_BUFSIZE 144 +#define FLOW_BUFSIZE 152 int ovs_flow_to_nlattrs(const struct sw_flow_key *, struct sk_buff *); int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, const struct nlattr *); -int ovs_flow_metadata_from_nlattrs(u32 *priority, u16 *in_port, +int ovs_flow_metadata_from_nlattrs(u32 *priority, u32 *mark, u16 *in_port, const struct nlattr *); #define MAX_ACTIONS_BUFSIZE (16 * 1024) diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index a9033481fa5e..a9327e2e48ce 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -114,6 +114,15 @@ error: return ERR_PTR(err); } +static void free_port_rcu(struct rcu_head *rcu) +{ + struct netdev_vport *netdev_vport = container_of(rcu, + struct netdev_vport, rcu); + + dev_put(netdev_vport->dev); + ovs_vport_free(vport_from_priv(netdev_vport)); +} + static void netdev_destroy(struct vport *vport) { struct netdev_vport *netdev_vport = netdev_vport_priv(vport); @@ -122,10 +131,7 @@ static void netdev_destroy(struct vport *vport) netdev_rx_handler_unregister(netdev_vport->dev); dev_set_promiscuity(netdev_vport->dev, -1); - synchronize_rcu(); - - dev_put(netdev_vport->dev); - ovs_vport_free(vport); + call_rcu(&netdev_vport->rcu, free_port_rcu); } const char *ovs_netdev_get_name(const struct vport *vport) diff --git a/net/openvswitch/vport-netdev.h b/net/openvswitch/vport-netdev.h index f7072a25c604..6478079b3417 100644 --- a/net/openvswitch/vport-netdev.h +++ b/net/openvswitch/vport-netdev.h @@ -20,12 +20,15 @@ #define VPORT_NETDEV_H 1 #include <linux/netdevice.h> +#include <linux/rcupdate.h> #include "vport.h" struct vport *ovs_netdev_get_vport(struct net_device *dev); struct netdev_vport { + struct rcu_head rcu; + struct net_device *dev; }; diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 03779e8a2622..70af0bedbac4 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -333,8 +333,7 @@ void ovs_vport_receive(struct vport *vport, struct sk_buff *skb) { struct vport_percpu_stats *stats; - stats = per_cpu_ptr(vport->percpu_stats, smp_processor_id()); - + stats = this_cpu_ptr(vport->percpu_stats); u64_stats_update_begin(&stats->sync); stats->rx_packets++; stats->rx_bytes += skb->len; @@ -359,7 +358,7 @@ int ovs_vport_send(struct vport *vport, struct sk_buff *skb) if (likely(sent)) { struct vport_percpu_stats *stats; - stats = per_cpu_ptr(vport->percpu_stats, smp_processor_id()); + stats = this_cpu_ptr(vport->percpu_stats); u64_stats_update_begin(&stats->sync); stats->tx_packets++; diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index ea14cb445295..f3f0f4dc31dd 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -345,7 +345,7 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, } out: - if (!IS_ERR(dst)) { + if (!IS_ERR_OR_NULL(dst)) { struct rt6_info *rt; rt = (struct rt6_info *)dst; t->dst = dst; |