diff --git a/Documentation/networking/switchdev.txt b/Documentation/networking/switchdev.txt index 67e43ee7840a..9f9e2587b347 100644 --- a/Documentation/networking/switchdev.txt +++ b/Documentation/networking/switchdev.txt @@ -369,3 +369,22 @@ The driver can monitor for updates to arp_tbl using the netevent notifier NETEVENT_NEIGH_UPDATE. The device can be programmed with resolved nexthops for the routes as arp_tbl updates. The driver implements ndo_neigh_destroy to know when arp_tbl neighbor entries are purged from the port. + +Transaction item queue +^^^^^^^^^^^^^^^^^^^^^^ + +For switchdev ops attr_set and obj_add, there is a 2 phase transaction model +used. First phase is to "prepare" anything needed, including various checks, +memory allocation, etc. The goal is to handle the stuff that is not unlikely +to fail here. The second phase is to "commit" the actual changes. + +Switchdev provides an inftrastructure for sharing items (for example memory +allocations) between the two phases. + +The object created by a driver in "prepare" phase and it is queued up by: +switchdev_trans_item_enqueue() +During the "commit" phase, the driver gets the object by: +switchdev_trans_item_dequeue() + +If a transaction is aborted during "prepare" phase, switchdev code will handle +cleanup of the queued-up objects. diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c index 32c5429ea5fe..d3f663262184 100644 --- a/drivers/net/ethernet/rocker/rocker.c +++ b/drivers/net/ethernet/rocker/rocker.c @@ -228,7 +228,6 @@ struct rocker_port { struct napi_struct napi_rx; struct rocker_dma_ring_info tx_ring; struct rocker_dma_ring_info rx_ring; - struct list_head trans_mem; }; struct rocker { @@ -343,74 +342,63 @@ static bool rocker_port_is_ovsed(const struct rocker_port *rocker_port) #define ROCKER_OP_FLAG_REFRESH BIT(3) static void *__rocker_port_mem_alloc(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, size_t size) { - struct list_head *elem = NULL; + struct switchdev_trans_item *elem = NULL; gfp_t gfp_flags = (flags & ROCKER_OP_FLAG_NOWAIT) ? GFP_ATOMIC : GFP_KERNEL; /* If in transaction prepare phase, allocate the memory - * and enqueue it on a per-port list. If in transaction - * commit phase, dequeue the memory from the per-port list + * and enqueue it on a transaction. If in transaction + * commit phase, dequeue the memory from the transaction * rather than re-allocating the memory. The idea is the * driver code paths for prepare and commit are identical * so the memory allocated in the prepare phase is the * memory used in the commit phase. */ - switch (trans) { - case SWITCHDEV_TRANS_PREPARE: + if (!trans) { + elem = kzalloc(size + sizeof(*elem), gfp_flags); + } else if (switchdev_trans_ph_prepare(trans)) { elem = kzalloc(size + sizeof(*elem), gfp_flags); if (!elem) return NULL; - list_add_tail(elem, &rocker_port->trans_mem); - break; - case SWITCHDEV_TRANS_COMMIT: - BUG_ON(list_empty(&rocker_port->trans_mem)); - elem = rocker_port->trans_mem.next; - list_del_init(elem); - break; - case SWITCHDEV_TRANS_NONE: - elem = kzalloc(size + sizeof(*elem), gfp_flags); - if (elem) - INIT_LIST_HEAD(elem); - break; - default: - break; + switchdev_trans_item_enqueue(trans, elem, kfree, elem); + } else { + elem = switchdev_trans_item_dequeue(trans); } return elem ? elem + 1 : NULL; } static void *rocker_port_kzalloc(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, size_t size) { return __rocker_port_mem_alloc(rocker_port, trans, flags, size); } static void *rocker_port_kcalloc(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, size_t n, size_t size) { return __rocker_port_mem_alloc(rocker_port, trans, flags, n * size); } -static void rocker_port_kfree(enum switchdev_trans trans, const void *mem) +static void rocker_port_kfree(struct switchdev_trans *trans, const void *mem) { - struct list_head *elem; + struct switchdev_trans_item *elem; /* Frees are ignored if in transaction prepare phase. The * memory remains on the per-port list until freed in the * commit phase. */ - if (trans == SWITCHDEV_TRANS_PREPARE) + if (switchdev_trans_ph_prepare(trans)) return; - elem = (struct list_head *)mem - 1; - BUG_ON(!list_empty(elem)); + elem = (struct switchdev_trans_item *) mem - 1; kfree(elem); } @@ -433,7 +421,7 @@ static void rocker_wait_init(struct rocker_wait *wait) } static struct rocker_wait *rocker_wait_create(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, int flags) { struct rocker_wait *wait; @@ -445,7 +433,7 @@ static struct rocker_wait *rocker_wait_create(struct rocker_port *rocker_port, return wait; } -static void rocker_wait_destroy(enum switchdev_trans trans, +static void rocker_wait_destroy(struct switchdev_trans *trans, struct rocker_wait *wait) { rocker_port_kfree(trans, wait); @@ -1411,7 +1399,7 @@ static irqreturn_t rocker_cmd_irq_handler(int irq, void *dev_id) wait = rocker_desc_cookie_ptr_get(desc_info); if (wait->nowait) { rocker_desc_gen_clear(desc_info); - rocker_wait_destroy(SWITCHDEV_TRANS_NONE, wait); + rocker_wait_destroy(NULL, wait); } else { rocker_wait_wake_up(wait); } @@ -1466,7 +1454,7 @@ static int rocker_event_link_change(const struct rocker *rocker, } static int rocker_port_fdb(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, const unsigned char *addr, __be16 vlan_id, int flags); @@ -1499,8 +1487,7 @@ static int rocker_event_mac_vlan_seen(const struct rocker *rocker, rocker_port->stp_state != BR_STATE_FORWARDING) return 0; - return rocker_port_fdb(rocker_port, SWITCHDEV_TRANS_NONE, - addr, vlan_id, flags); + return rocker_port_fdb(rocker_port, NULL, addr, vlan_id, flags); } static int rocker_event_process(const struct rocker *rocker, @@ -1585,7 +1572,7 @@ typedef int (*rocker_cmd_proc_cb_t)(const struct rocker_port *rocker_port, void *priv); static int rocker_cmd_exec(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, rocker_cmd_prep_cb_t prepare, void *prepare_priv, rocker_cmd_proc_cb_t process, void *process_priv) { @@ -1618,7 +1605,7 @@ static int rocker_cmd_exec(struct rocker_port *rocker_port, rocker_desc_cookie_ptr_set(desc_info, wait); - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) rocker_desc_head_set(rocker, &rocker->cmd_ring, desc_info); spin_unlock_irqrestore(&rocker->cmd_ring_lock, lock_flags); @@ -1626,7 +1613,7 @@ static int rocker_cmd_exec(struct rocker_port *rocker_port, if (nowait) return 0; - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) if (!rocker_wait_event_timeout(wait, HZ / 10)) return -EIO; @@ -1878,7 +1865,7 @@ rocker_cmd_set_port_learning_prep(const struct rocker_port *rocker_port, static int rocker_cmd_get_port_settings_ethtool(struct rocker_port *rocker_port, struct ethtool_cmd *ecmd) { - return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + return rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_get_port_settings_prep, NULL, rocker_cmd_get_port_settings_ethtool_proc, ecmd); @@ -1887,7 +1874,7 @@ static int rocker_cmd_get_port_settings_ethtool(struct rocker_port *rocker_port, static int rocker_cmd_get_port_settings_macaddr(struct rocker_port *rocker_port, unsigned char *macaddr) { - return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + return rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_get_port_settings_prep, NULL, rocker_cmd_get_port_settings_macaddr_proc, macaddr); @@ -1896,7 +1883,7 @@ static int rocker_cmd_get_port_settings_macaddr(struct rocker_port *rocker_port, static int rocker_cmd_set_port_settings_ethtool(struct rocker_port *rocker_port, struct ethtool_cmd *ecmd) { - return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + return rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_set_port_settings_ethtool_prep, ecmd, NULL, NULL); } @@ -1904,7 +1891,7 @@ static int rocker_cmd_set_port_settings_ethtool(struct rocker_port *rocker_port, static int rocker_cmd_set_port_settings_macaddr(struct rocker_port *rocker_port, unsigned char *macaddr) { - return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + return rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_set_port_settings_macaddr_prep, macaddr, NULL, NULL); } @@ -1912,13 +1899,13 @@ static int rocker_cmd_set_port_settings_macaddr(struct rocker_port *rocker_port, static int rocker_cmd_set_port_settings_mtu(struct rocker_port *rocker_port, int mtu) { - return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + return rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_set_port_settings_mtu_prep, &mtu, NULL, NULL); } static int rocker_port_set_learning(struct rocker_port *rocker_port, - enum switchdev_trans trans) + struct switchdev_trans *trans) { return rocker_cmd_exec(rocker_port, trans, 0, rocker_cmd_set_port_learning_prep, @@ -2436,7 +2423,7 @@ rocker_flow_tbl_find(const struct rocker *rocker, } static int rocker_flow_tbl_add(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, struct rocker_flow_tbl_entry *match) { struct rocker *rocker = rocker_port->rocker; @@ -2452,7 +2439,7 @@ static int rocker_flow_tbl_add(struct rocker_port *rocker_port, if (found) { match->cookie = found->cookie; - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_del(&found->entry); rocker_port_kfree(trans, found); found = match; @@ -2463,7 +2450,7 @@ static int rocker_flow_tbl_add(struct rocker_port *rocker_port, found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD; } - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_add(rocker->flow_tbl, &found->entry, found->key_crc32); spin_unlock_irqrestore(&rocker->flow_tbl_lock, lock_flags); @@ -2473,7 +2460,7 @@ static int rocker_flow_tbl_add(struct rocker_port *rocker_port, } static int rocker_flow_tbl_del(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, struct rocker_flow_tbl_entry *match) { struct rocker *rocker = rocker_port->rocker; @@ -2489,7 +2476,7 @@ static int rocker_flow_tbl_del(struct rocker_port *rocker_port, found = rocker_flow_tbl_find(rocker, match); if (found) { - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_del(&found->entry); found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL; } @@ -2509,7 +2496,7 @@ static int rocker_flow_tbl_del(struct rocker_port *rocker_port, } static int rocker_flow_tbl_do(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, struct rocker_flow_tbl_entry *entry) { if (flags & ROCKER_OP_FLAG_REMOVE) @@ -2519,7 +2506,7 @@ static int rocker_flow_tbl_do(struct rocker_port *rocker_port, } static int rocker_flow_tbl_ig_port(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, u32 in_pport, u32 in_pport_mask, enum rocker_of_dpa_table_id goto_tbl) { @@ -2539,7 +2526,7 @@ static int rocker_flow_tbl_ig_port(struct rocker_port *rocker_port, } static int rocker_flow_tbl_vlan(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, u32 in_pport, __be16 vlan_id, __be16 vlan_id_mask, enum rocker_of_dpa_table_id goto_tbl, @@ -2565,7 +2552,7 @@ static int rocker_flow_tbl_vlan(struct rocker_port *rocker_port, } static int rocker_flow_tbl_term_mac(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, u32 in_pport, u32 in_pport_mask, __be16 eth_type, const u8 *eth_dst, const u8 *eth_dst_mask, __be16 vlan_id, @@ -2602,7 +2589,7 @@ static int rocker_flow_tbl_term_mac(struct rocker_port *rocker_port, } static int rocker_flow_tbl_bridge(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, const u8 *eth_dst, const u8 *eth_dst_mask, __be16 vlan_id, u32 tunnel_id, enum rocker_of_dpa_table_id goto_tbl, @@ -2656,7 +2643,7 @@ static int rocker_flow_tbl_bridge(struct rocker_port *rocker_port, } static int rocker_flow_tbl_ucast4_routing(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, __be16 eth_type, __be32 dst, __be32 dst_mask, u32 priority, enum rocker_of_dpa_table_id goto_tbl, @@ -2682,7 +2669,7 @@ static int rocker_flow_tbl_ucast4_routing(struct rocker_port *rocker_port, } static int rocker_flow_tbl_acl(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, u32 in_pport, u32 in_pport_mask, const u8 *eth_src, const u8 *eth_src_mask, const u8 *eth_dst, const u8 *eth_dst_mask, @@ -2747,7 +2734,7 @@ rocker_group_tbl_find(const struct rocker *rocker, return NULL; } -static void rocker_group_tbl_entry_free(enum switchdev_trans trans, +static void rocker_group_tbl_entry_free(struct switchdev_trans *trans, struct rocker_group_tbl_entry *entry) { switch (ROCKER_GROUP_TYPE_GET(entry->group_id)) { @@ -2762,7 +2749,7 @@ static void rocker_group_tbl_entry_free(enum switchdev_trans trans, } static int rocker_group_tbl_add(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, struct rocker_group_tbl_entry *match) { struct rocker *rocker = rocker_port->rocker; @@ -2774,7 +2761,7 @@ static int rocker_group_tbl_add(struct rocker_port *rocker_port, found = rocker_group_tbl_find(rocker, match); if (found) { - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_del(&found->entry); rocker_group_tbl_entry_free(trans, found); found = match; @@ -2784,7 +2771,7 @@ static int rocker_group_tbl_add(struct rocker_port *rocker_port, found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD; } - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_add(rocker->group_tbl, &found->entry, found->group_id); spin_unlock_irqrestore(&rocker->group_tbl_lock, lock_flags); @@ -2794,7 +2781,7 @@ static int rocker_group_tbl_add(struct rocker_port *rocker_port, } static int rocker_group_tbl_del(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, struct rocker_group_tbl_entry *match) { struct rocker *rocker = rocker_port->rocker; @@ -2807,7 +2794,7 @@ static int rocker_group_tbl_del(struct rocker_port *rocker_port, found = rocker_group_tbl_find(rocker, match); if (found) { - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_del(&found->entry); found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL; } @@ -2827,7 +2814,7 @@ static int rocker_group_tbl_del(struct rocker_port *rocker_port, } static int rocker_group_tbl_do(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, struct rocker_group_tbl_entry *entry) { if (flags & ROCKER_OP_FLAG_REMOVE) @@ -2837,7 +2824,7 @@ static int rocker_group_tbl_do(struct rocker_port *rocker_port, } static int rocker_group_l2_interface(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, __be16 vlan_id, u32 out_pport, int pop_vlan) { @@ -2854,7 +2841,7 @@ static int rocker_group_l2_interface(struct rocker_port *rocker_port, } static int rocker_group_l2_fan_out(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, int flags, u8 group_count, const u32 *group_ids, u32 group_id) { @@ -2879,7 +2866,7 @@ static int rocker_group_l2_fan_out(struct rocker_port *rocker_port, } static int rocker_group_l2_flood(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, __be16 vlan_id, u8 group_count, const u32 *group_ids, u32 group_id) { @@ -2889,7 +2876,7 @@ static int rocker_group_l2_flood(struct rocker_port *rocker_port, } static int rocker_group_l3_unicast(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, u32 index, const u8 *src_mac, const u8 *dst_mac, __be16 vlan_id, bool ttl_check, u32 pport) { @@ -2925,22 +2912,22 @@ rocker_neigh_tbl_find(const struct rocker *rocker, __be32 ip_addr) } static void _rocker_neigh_add(struct rocker *rocker, - enum switchdev_trans trans, + struct switchdev_trans *trans, struct rocker_neigh_tbl_entry *entry) { - if (trans != SWITCHDEV_TRANS_COMMIT) + if (!switchdev_trans_ph_commit(trans)) entry->index = rocker->neigh_tbl_next_index++; - if (trans == SWITCHDEV_TRANS_PREPARE) + if (switchdev_trans_ph_prepare(trans)) return; entry->ref_count++; hash_add(rocker->neigh_tbl, &entry->entry, be32_to_cpu(entry->ip_addr)); } -static void _rocker_neigh_del(enum switchdev_trans trans, +static void _rocker_neigh_del(struct switchdev_trans *trans, struct rocker_neigh_tbl_entry *entry) { - if (trans == SWITCHDEV_TRANS_PREPARE) + if (switchdev_trans_ph_prepare(trans)) return; if (--entry->ref_count == 0) { hash_del(&entry->entry); @@ -2949,19 +2936,19 @@ static void _rocker_neigh_del(enum switchdev_trans trans, } static void _rocker_neigh_update(struct rocker_neigh_tbl_entry *entry, - enum switchdev_trans trans, + struct switchdev_trans *trans, const u8 *eth_dst, bool ttl_check) { if (eth_dst) { ether_addr_copy(entry->eth_dst, eth_dst); entry->ttl_check = ttl_check; - } else if (trans != SWITCHDEV_TRANS_PREPARE) { + } else if (!switchdev_trans_ph_prepare(trans)) { entry->ref_count++; } } static int rocker_port_ipv4_neigh(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, int flags, __be32 ip_addr, const u8 *eth_dst) { struct rocker *rocker = rocker_port->rocker; @@ -3053,7 +3040,8 @@ static int rocker_port_ipv4_neigh(struct rocker_port *rocker_port, } static int rocker_port_ipv4_resolve(struct rocker_port *rocker_port, - enum switchdev_trans trans, __be32 ip_addr) + struct switchdev_trans *trans, + __be32 ip_addr) { struct net_device *dev = rocker_port->dev; struct neighbour *n = __ipv4_neigh_lookup(dev, (__force u32)ip_addr); @@ -3081,7 +3069,7 @@ static int rocker_port_ipv4_resolve(struct rocker_port *rocker_port, } static int rocker_port_ipv4_nh(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, __be32 ip_addr, u32 *index) { struct rocker *rocker = rocker_port->rocker; @@ -3140,7 +3128,7 @@ static int rocker_port_ipv4_nh(struct rocker_port *rocker_port, } static int rocker_port_vlan_flood_group(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, int flags, __be16 vlan_id) { struct rocker_port *p; @@ -3189,7 +3177,7 @@ static int rocker_port_vlan_flood_group(struct rocker_port *rocker_port, } static int rocker_port_vlan_l2_groups(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, __be16 vlan_id, bool pop_vlan) { const struct rocker *rocker = rocker_port->rocker; @@ -3295,7 +3283,7 @@ static struct rocker_ctrl { }; static int rocker_port_ctrl_vlan_acl(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, const struct rocker_ctrl *ctrl, __be16 vlan_id) { u32 in_pport = rocker_port->pport; @@ -3328,7 +3316,8 @@ static int rocker_port_ctrl_vlan_acl(struct rocker_port *rocker_port, } static int rocker_port_ctrl_vlan_bridge(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, + int flags, const struct rocker_ctrl *ctrl, __be16 vlan_id) { @@ -3353,7 +3342,7 @@ static int rocker_port_ctrl_vlan_bridge(struct rocker_port *rocker_port, } static int rocker_port_ctrl_vlan_term(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, const struct rocker_ctrl *ctrl, __be16 vlan_id) { u32 in_pport_mask = 0xffffffff; @@ -3377,7 +3366,7 @@ static int rocker_port_ctrl_vlan_term(struct rocker_port *rocker_port, } static int rocker_port_ctrl_vlan(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, const struct rocker_ctrl *ctrl, __be16 vlan_id) { if (ctrl->acl) @@ -3395,7 +3384,7 @@ static int rocker_port_ctrl_vlan(struct rocker_port *rocker_port, } static int rocker_port_ctrl_vlan_add(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, __be16 vlan_id) { int err = 0; @@ -3414,7 +3403,7 @@ static int rocker_port_ctrl_vlan_add(struct rocker_port *rocker_port, } static int rocker_port_ctrl(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, const struct rocker_ctrl *ctrl) { u16 vid; @@ -3433,7 +3422,7 @@ static int rocker_port_ctrl(struct rocker_port *rocker_port, } static int rocker_port_vlan(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, u16 vid) + struct switchdev_trans *trans, int flags, u16 vid) { enum rocker_of_dpa_table_id goto_tbl = ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC; @@ -3490,14 +3479,14 @@ static int rocker_port_vlan(struct rocker_port *rocker_port, "Error (%d) port VLAN table\n", err); err_out: - if (trans == SWITCHDEV_TRANS_PREPARE) + if (switchdev_trans_ph_prepare(trans)) change_bit(ntohs(internal_vlan_id), rocker_port->vlan_bitmap); return err; } static int rocker_port_ig_tbl(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags) + struct switchdev_trans *trans, int flags) { enum rocker_of_dpa_table_id goto_tbl; u32 in_pport; @@ -3525,7 +3514,7 @@ static int rocker_port_ig_tbl(struct rocker_port *rocker_port, struct rocker_fdb_learn_work { struct work_struct work; struct rocker_port *rocker_port; - enum switchdev_trans trans; + struct switchdev_trans *trans; int flags; u8 addr[ETH_ALEN]; u16 vid; @@ -3553,7 +3542,7 @@ static void rocker_port_fdb_learn_work(struct work_struct *work) } static int rocker_port_fdb_learn(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, const u8 *addr, __be16 vlan_id) { struct rocker_fdb_learn_work *lw; @@ -3595,7 +3584,7 @@ static int rocker_port_fdb_learn(struct rocker_port *rocker_port, ether_addr_copy(lw->addr, addr); lw->vid = rocker_port_vlan_to_vid(rocker_port, vlan_id); - if (trans == SWITCHDEV_TRANS_PREPARE) + if (switchdev_trans_ph_prepare(trans)) rocker_port_kfree(trans, lw); else schedule_work(&lw->work); @@ -3617,7 +3606,7 @@ rocker_fdb_tbl_find(const struct rocker *rocker, } static int rocker_port_fdb(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, const unsigned char *addr, __be16 vlan_id, int flags) { @@ -3646,11 +3635,11 @@ static int rocker_port_fdb(struct rocker_port *rocker_port, found->touched = jiffies; if (removing) { rocker_port_kfree(trans, fdb); - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_del(&found->entry); } } else if (!removing) { - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_add(rocker->fdb_tbl, &fdb->entry, fdb->key_crc32); } @@ -3670,7 +3659,7 @@ static int rocker_port_fdb(struct rocker_port *rocker_port, } static int rocker_port_fdb_flush(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags) + struct switchdev_trans *trans, int flags) { struct rocker *rocker = rocker_port->rocker; struct rocker_fdb_tbl_entry *found; @@ -3697,7 +3686,7 @@ static int rocker_port_fdb_flush(struct rocker_port *rocker_port, found->key.vlan_id); if (err) goto err_out; - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_del(&found->entry); } @@ -3728,7 +3717,7 @@ static void rocker_fdb_cleanup(unsigned long data) rocker_port = entry->key.rocker_port; expires = entry->touched + rocker_port->ageing_time; if (time_before_eq(expires, jiffies)) { - rocker_port_fdb_learn(rocker_port, SWITCHDEV_TRANS_NONE, + rocker_port_fdb_learn(rocker_port, NULL, flags, entry->key.addr, entry->key.vlan_id); hash_del(&entry->entry); @@ -3743,7 +3732,7 @@ static void rocker_fdb_cleanup(unsigned long data) } static int rocker_port_router_mac(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, __be16 vlan_id) { u32 in_pport_mask = 0xffffffff; @@ -3776,7 +3765,7 @@ static int rocker_port_router_mac(struct rocker_port *rocker_port, } static int rocker_port_fwding(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags) + struct switchdev_trans *trans, int flags) { bool pop_vlan; u32 out_pport; @@ -3815,16 +3804,16 @@ static int rocker_port_fwding(struct rocker_port *rocker_port, } static int rocker_port_stp_update(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, u8 state) { bool want[ROCKER_CTRL_MAX] = { 0, }; bool prev_ctrls[ROCKER_CTRL_MAX]; - u8 prev_state; + u8 uninitialized_var(prev_state); int err; int i; - if (trans == SWITCHDEV_TRANS_PREPARE) { + if (switchdev_trans_ph_prepare(trans)) { memcpy(prev_ctrls, rocker_port->ctrls, sizeof(prev_ctrls)); prev_state = rocker_port->stp_state; } @@ -3876,7 +3865,7 @@ static int rocker_port_stp_update(struct rocker_port *rocker_port, err = rocker_port_fwding(rocker_port, trans, flags); err_out: - if (trans == SWITCHDEV_TRANS_PREPARE) { + if (switchdev_trans_ph_prepare(trans)) { memcpy(rocker_port->ctrls, prev_ctrls, sizeof(prev_ctrls)); rocker_port->stp_state = prev_state; } @@ -3885,7 +3874,7 @@ static int rocker_port_stp_update(struct rocker_port *rocker_port, } static int rocker_port_fwd_enable(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags) + struct switchdev_trans *trans, int flags) { if (rocker_port_is_bridged(rocker_port)) /* bridge STP will enable port */ @@ -3897,7 +3886,7 @@ static int rocker_port_fwd_enable(struct rocker_port *rocker_port, } static int rocker_port_fwd_disable(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags) + struct switchdev_trans *trans, int flags) { if (rocker_port_is_bridged(rocker_port)) /* bridge STP will disable port */ @@ -3995,7 +3984,7 @@ rocker_port_internal_vlan_id_put(const struct rocker_port *rocker_port, } static int rocker_port_fib_ipv4(struct rocker_port *rocker_port, - enum switchdev_trans trans, __be32 dst, + struct switchdev_trans *trans, __be32 dst, int dst_len, const struct fib_info *fi, u32 tb_id, int flags) { @@ -4069,7 +4058,7 @@ static int rocker_port_open(struct net_device *dev) goto err_request_rx_irq; } - err = rocker_port_fwd_enable(rocker_port, SWITCHDEV_TRANS_NONE, 0); + err = rocker_port_fwd_enable(rocker_port, NULL, 0); if (err) goto err_fwd_enable; @@ -4097,7 +4086,7 @@ static int rocker_port_stop(struct net_device *dev) rocker_port_set_enable(rocker_port, false); napi_disable(&rocker_port->napi_rx); napi_disable(&rocker_port->napi_tx); - rocker_port_fwd_disable(rocker_port, SWITCHDEV_TRANS_NONE, + rocker_port_fwd_disable(rocker_port, NULL, ROCKER_OP_FLAG_NOWAIT); free_irq(rocker_msix_rx_vector(rocker_port), rocker_port); free_irq(rocker_msix_tx_vector(rocker_port), rocker_port); @@ -4283,7 +4272,7 @@ static int rocker_port_get_phys_port_name(struct net_device *dev, struct port_name name = { .buf = buf, .len = len }; int err; - err = rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + err = rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_get_port_settings_prep, NULL, rocker_cmd_get_port_settings_phys_name_proc, &name); @@ -4308,7 +4297,7 @@ static void rocker_port_neigh_destroy(struct neighbour *n) int flags = ROCKER_OP_FLAG_REMOVE | ROCKER_OP_FLAG_NOWAIT; __be32 ip_addr = *(__be32 *)n->primary_key; - rocker_port_ipv4_neigh(rocker_port, SWITCHDEV_TRANS_NONE, + rocker_port_ipv4_neigh(rocker_port, NULL, flags, ip_addr, n->ha); } @@ -4354,18 +4343,8 @@ static int rocker_port_attr_get(struct net_device *dev, return 0; } -static void rocker_port_trans_abort(const struct rocker_port *rocker_port) -{ - struct list_head *mem, *tmp; - - list_for_each_safe(mem, tmp, &rocker_port->trans_mem) { - list_del(mem); - kfree(mem); - } -} - static int rocker_port_brport_flags_set(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, unsigned long brport_flags) { unsigned long orig_flags; @@ -4376,37 +4355,27 @@ static int rocker_port_brport_flags_set(struct rocker_port *rocker_port, if ((orig_flags ^ rocker_port->brport_flags) & BR_LEARNING) err = rocker_port_set_learning(rocker_port, trans); - if (trans == SWITCHDEV_TRANS_PREPARE) + if (switchdev_trans_ph_prepare(trans)) rocker_port->brport_flags = orig_flags; return err; } static int rocker_port_attr_set(struct net_device *dev, - struct switchdev_attr *attr) + struct switchdev_attr *attr, + struct switchdev_trans *trans) { struct rocker_port *rocker_port = netdev_priv(dev); int err = 0; - switch (attr->trans) { - case SWITCHDEV_TRANS_PREPARE: - BUG_ON(!list_empty(&rocker_port->trans_mem)); - break; - case SWITCHDEV_TRANS_ABORT: - rocker_port_trans_abort(rocker_port); - return 0; - default: - break; - } - switch (attr->id) { case SWITCHDEV_ATTR_PORT_STP_STATE: - err = rocker_port_stp_update(rocker_port, attr->trans, + err = rocker_port_stp_update(rocker_port, trans, ROCKER_OP_FLAG_NOWAIT, attr->u.stp_state); break; case SWITCHDEV_ATTR_PORT_BRIDGE_FLAGS: - err = rocker_port_brport_flags_set(rocker_port, attr->trans, + err = rocker_port_brport_flags_set(rocker_port, trans, attr->u.brport_flags); break; default: @@ -4418,7 +4387,8 @@ static int rocker_port_attr_set(struct net_device *dev, } static int rocker_port_vlan_add(struct rocker_port *rocker_port, - enum switchdev_trans trans, u16 vid, u16 flags) + struct switchdev_trans *trans, + u16 vid, u16 flags) { int err; @@ -4437,7 +4407,7 @@ static int rocker_port_vlan_add(struct rocker_port *rocker_port, } static int rocker_port_vlans_add(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, const struct switchdev_obj_vlan *vlan) { u16 vid; @@ -4454,7 +4424,7 @@ static int rocker_port_vlans_add(struct rocker_port *rocker_port, } static int rocker_port_fdb_add(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, const struct switchdev_obj_fdb *fdb) { __be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, fdb->vid, NULL); @@ -4467,36 +4437,26 @@ static int rocker_port_fdb_add(struct rocker_port *rocker_port, } static int rocker_port_obj_add(struct net_device *dev, - struct switchdev_obj *obj) + struct switchdev_obj *obj, + struct switchdev_trans *trans) { struct rocker_port *rocker_port = netdev_priv(dev); const struct switchdev_obj_ipv4_fib *fib4; int err = 0; - switch (obj->trans) { - case SWITCHDEV_TRANS_PREPARE: - BUG_ON(!list_empty(&rocker_port->trans_mem)); - break; - case SWITCHDEV_TRANS_ABORT: - rocker_port_trans_abort(rocker_port); - return 0; - default: - break; - } - switch (obj->id) { case SWITCHDEV_OBJ_PORT_VLAN: - err = rocker_port_vlans_add(rocker_port, obj->trans, + err = rocker_port_vlans_add(rocker_port, trans, &obj->u.vlan); break; case SWITCHDEV_OBJ_IPV4_FIB: fib4 = &obj->u.ipv4_fib; - err = rocker_port_fib_ipv4(rocker_port, obj->trans, + err = rocker_port_fib_ipv4(rocker_port, trans, htonl(fib4->dst), fib4->dst_len, fib4->fi, fib4->tb_id, 0); break; case SWITCHDEV_OBJ_PORT_FDB: - err = rocker_port_fdb_add(rocker_port, obj->trans, &obj->u.fdb); + err = rocker_port_fdb_add(rocker_port, trans, &obj->u.fdb); break; default: err = -EOPNOTSUPP; @@ -4511,12 +4471,12 @@ static int rocker_port_vlan_del(struct rocker_port *rocker_port, { int err; - err = rocker_port_router_mac(rocker_port, SWITCHDEV_TRANS_NONE, + err = rocker_port_router_mac(rocker_port, NULL, ROCKER_OP_FLAG_REMOVE, htons(vid)); if (err) return err; - return rocker_port_vlan(rocker_port, SWITCHDEV_TRANS_NONE, + return rocker_port_vlan(rocker_port, NULL, ROCKER_OP_FLAG_REMOVE, vid); } @@ -4536,7 +4496,7 @@ static int rocker_port_vlans_del(struct rocker_port *rocker_port, } static int rocker_port_fdb_del(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, const struct switchdev_obj_fdb *fdb) { __be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, fdb->vid, NULL); @@ -4561,13 +4521,13 @@ static int rocker_port_obj_del(struct net_device *dev, break; case SWITCHDEV_OBJ_IPV4_FIB: fib4 = &obj->u.ipv4_fib; - err = rocker_port_fib_ipv4(rocker_port, SWITCHDEV_TRANS_NONE, + err = rocker_port_fib_ipv4(rocker_port, NULL, htonl(fib4->dst), fib4->dst_len, fib4->fi, fib4->tb_id, ROCKER_OP_FLAG_REMOVE); break; case SWITCHDEV_OBJ_PORT_FDB: - err = rocker_port_fdb_del(rocker_port, obj->trans, &obj->u.fdb); + err = rocker_port_fdb_del(rocker_port, NULL, &obj->u.fdb); break; default: err = -EOPNOTSUPP; @@ -4781,7 +4741,7 @@ rocker_cmd_get_port_stats_ethtool_proc(const struct rocker_port *rocker_port, static int rocker_cmd_get_port_stats_ethtool(struct rocker_port *rocker_port, void *priv) { - return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + return rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_get_port_stats_prep, NULL, rocker_cmd_get_port_stats_ethtool_proc, priv); @@ -4973,8 +4933,7 @@ static void rocker_remove_ports(const struct rocker *rocker) rocker_port = rocker->ports[i]; if (!rocker_port) continue; - rocker_port_ig_tbl(rocker_port, SWITCHDEV_TRANS_NONE, - ROCKER_OP_FLAG_REMOVE); + rocker_port_ig_tbl(rocker_port, NULL, ROCKER_OP_FLAG_REMOVE); unregister_netdev(rocker_port->dev); free_netdev(rocker_port->dev); } @@ -5013,7 +4972,6 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) rocker_port->pport = port_number + 1; rocker_port->brport_flags = BR_LEARNING | BR_LEARNING_SYNC; rocker_port->ageing_time = BR_DEFAULT_AGEING_TIME; - INIT_LIST_HEAD(&rocker_port->trans_mem); rocker_port_dev_addr_init(rocker_port); dev->netdev_ops = &rocker_port_netdev_ops; @@ -5036,9 +4994,9 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) switchdev_port_fwd_mark_set(rocker_port->dev, NULL, false); - rocker_port_set_learning(rocker_port, SWITCHDEV_TRANS_NONE); + rocker_port_set_learning(rocker_port, NULL); - err = rocker_port_ig_tbl(rocker_port, SWITCHDEV_TRANS_NONE, 0); + err = rocker_port_ig_tbl(rocker_port, NULL, 0); if (err) { netdev_err(rocker_port->dev, "install ig port table failed\n"); goto err_port_ig_tbl; @@ -5047,8 +5005,7 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) rocker_port->internal_vlan_id = rocker_port_internal_vlan_id_get(rocker_port, dev->ifindex); - err = rocker_port_vlan_add(rocker_port, SWITCHDEV_TRANS_NONE, - untagged_vid, 0); + err = rocker_port_vlan_add(rocker_port, NULL, untagged_vid, 0); if (err) { netdev_err(rocker_port->dev, "install untagged VLAN failed\n"); goto err_untagged_vlan; @@ -5057,8 +5014,7 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) return 0; err_untagged_vlan: - rocker_port_ig_tbl(rocker_port, SWITCHDEV_TRANS_NONE, - ROCKER_OP_FLAG_REMOVE); + rocker_port_ig_tbl(rocker_port, NULL, ROCKER_OP_FLAG_REMOVE); err_port_ig_tbl: rocker->ports[port_number] = NULL; unregister_netdev(dev); @@ -5325,8 +5281,7 @@ static int rocker_port_bridge_join(struct rocker_port *rocker_port, rocker_port->bridge_dev = bridge; switchdev_port_fwd_mark_set(rocker_port->dev, bridge, true); - return rocker_port_vlan_add(rocker_port, SWITCHDEV_TRANS_NONE, - untagged_vid, 0); + return rocker_port_vlan_add(rocker_port, NULL, untagged_vid, 0); } static int rocker_port_bridge_leave(struct rocker_port *rocker_port) @@ -5348,14 +5303,12 @@ static int rocker_port_bridge_leave(struct rocker_port *rocker_port) false); rocker_port->bridge_dev = NULL; - err = rocker_port_vlan_add(rocker_port, SWITCHDEV_TRANS_NONE, - untagged_vid, 0); + err = rocker_port_vlan_add(rocker_port, NULL, untagged_vid, 0); if (err) return err; if (rocker_port->dev->flags & IFF_UP) - err = rocker_port_fwd_enable(rocker_port, - SWITCHDEV_TRANS_NONE, 0); + err = rocker_port_fwd_enable(rocker_port, NULL, 0); return err; } @@ -5368,10 +5321,10 @@ static int rocker_port_ovs_changed(struct rocker_port *rocker_port, rocker_port->bridge_dev = master; - err = rocker_port_fwd_disable(rocker_port, SWITCHDEV_TRANS_NONE, 0); + err = rocker_port_fwd_disable(rocker_port, NULL, 0); if (err) return err; - err = rocker_port_fwd_enable(rocker_port, SWITCHDEV_TRANS_NONE, 0); + err = rocker_port_fwd_enable(rocker_port, NULL, 0); return err; } @@ -5449,8 +5402,7 @@ static int rocker_neigh_update(struct net_device *dev, struct neighbour *n) ROCKER_OP_FLAG_NOWAIT; __be32 ip_addr = *(__be32 *)n->primary_key; - return rocker_port_ipv4_neigh(rocker_port, SWITCHDEV_TRANS_NONE, - flags, ip_addr, n->ha); + return rocker_port_ipv4_neigh(rocker_port, NULL, flags, ip_addr, n->ha); } static int rocker_netevent_event(struct notifier_block *unused, diff --git a/include/net/switchdev.h b/include/net/switchdev.h index 319baab3b48e..18207878e407 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -1,6 +1,6 @@ /* * include/net/switchdev.h - Switch device API - * Copyright (c) 2014 Jiri Pirko + * Copyright (c) 2014-2015 Jiri Pirko * Copyright (c) 2014-2015 Scott Feldman * * This program is free software; you can redistribute it and/or modify @@ -13,16 +13,31 @@ #include #include +#include #define SWITCHDEV_F_NO_RECURSE BIT(0) -enum switchdev_trans { - SWITCHDEV_TRANS_NONE, - SWITCHDEV_TRANS_PREPARE, - SWITCHDEV_TRANS_ABORT, - SWITCHDEV_TRANS_COMMIT, +struct switchdev_trans_item { + struct list_head list; + void *data; + void (*destructor)(const void *data); }; +struct switchdev_trans { + struct list_head item_list; + bool ph_prepare; +}; + +static inline bool switchdev_trans_ph_prepare(struct switchdev_trans *trans) +{ + return trans && trans->ph_prepare; +} + +static inline bool switchdev_trans_ph_commit(struct switchdev_trans *trans) +{ + return trans && !trans->ph_prepare; +} + enum switchdev_attr_id { SWITCHDEV_ATTR_UNDEFINED, SWITCHDEV_ATTR_PORT_PARENT_ID, @@ -32,7 +47,6 @@ enum switchdev_attr_id { struct switchdev_attr { enum switchdev_attr_id id; - enum switchdev_trans trans; u32 flags; union { struct netdev_phys_item_id ppid; /* PORT_PARENT_ID */ @@ -52,7 +66,6 @@ enum switchdev_obj_id { struct switchdev_obj { enum switchdev_obj_id id; - enum switchdev_trans trans; int (*cb)(struct net_device *dev, struct switchdev_obj *obj); union { struct switchdev_obj_vlan { /* PORT_VLAN */ @@ -77,6 +90,11 @@ struct switchdev_obj { } u; }; +void switchdev_trans_item_enqueue(struct switchdev_trans *trans, + void *data, void (*destructor)(void const *), + struct switchdev_trans_item *tritem); +void *switchdev_trans_item_dequeue(struct switchdev_trans *trans); + /** * struct switchdev_ops - switchdev operations * @@ -94,9 +112,11 @@ struct switchdev_ops { int (*switchdev_port_attr_get)(struct net_device *dev, struct switchdev_attr *attr); int (*switchdev_port_attr_set)(struct net_device *dev, - struct switchdev_attr *attr); + struct switchdev_attr *attr, + struct switchdev_trans *trans); int (*switchdev_port_obj_add)(struct net_device *dev, - struct switchdev_obj *obj); + struct switchdev_obj *obj, + struct switchdev_trans *trans); int (*switchdev_port_obj_del)(struct net_device *dev, struct switchdev_obj *obj); int (*switchdev_port_obj_dump)(struct net_device *dev, diff --git a/net/dsa/slave.c b/net/dsa/slave.c index cce97385f743..71a11559b65f 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -242,7 +242,8 @@ static int dsa_bridge_check_vlan_range(struct dsa_switch *ds, } static int dsa_slave_port_vlan_add(struct net_device *dev, - struct switchdev_obj *obj) + struct switchdev_obj *obj, + struct switchdev_trans *trans) { struct switchdev_obj_vlan *vlan = &obj->u.vlan; struct dsa_slave_priv *p = netdev_priv(dev); @@ -250,8 +251,7 @@ static int dsa_slave_port_vlan_add(struct net_device *dev, u16 vid; int err; - switch (obj->trans) { - case SWITCHDEV_TRANS_PREPARE: + if (switchdev_trans_ph_prepare(trans)) { if (!ds->drv->port_vlan_add || !ds->drv->port_pvid_set) return -EOPNOTSUPP; @@ -263,8 +263,7 @@ static int dsa_slave_port_vlan_add(struct net_device *dev, vlan->vid_end); if (err) return err; - break; - case SWITCHDEV_TRANS_COMMIT: + } else { for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { err = ds->drv->port_vlan_add(ds, p->port, vid, vlan->flags & @@ -274,9 +273,6 @@ static int dsa_slave_port_vlan_add(struct net_device *dev, if (err) return err; } - break; - default: - return -EOPNOTSUPP; } return 0; @@ -347,16 +343,17 @@ static int dsa_slave_port_vlan_dump(struct net_device *dev, } static int dsa_slave_port_fdb_add(struct net_device *dev, - struct switchdev_obj *obj) + struct switchdev_obj *obj, + struct switchdev_trans *trans) { struct switchdev_obj_fdb *fdb = &obj->u.fdb; struct dsa_slave_priv *p = netdev_priv(dev); struct dsa_switch *ds = p->parent; int ret = -EOPNOTSUPP; - if (obj->trans == SWITCHDEV_TRANS_PREPARE) + if (switchdev_trans_ph_prepare(trans)) ret = ds->drv->port_fdb_add ? 0 : -EOPNOTSUPP; - else if (obj->trans == SWITCHDEV_TRANS_COMMIT) + else ret = ds->drv->port_fdb_add(ds, p->port, fdb->addr, fdb->vid); return ret; @@ -456,13 +453,14 @@ static int dsa_slave_stp_update(struct net_device *dev, u8 state) } static int dsa_slave_port_attr_set(struct net_device *dev, - struct switchdev_attr *attr) + struct switchdev_attr *attr, + struct switchdev_trans *trans) { int ret = 0; switch (attr->id) { case SWITCHDEV_ATTR_PORT_STP_STATE: - if (attr->trans == SWITCHDEV_TRANS_COMMIT) + if (switchdev_trans_ph_commit(trans)) ret = dsa_slave_stp_update(dev, attr->u.stp_state); break; default: @@ -474,7 +472,8 @@ static int dsa_slave_port_attr_set(struct net_device *dev, } static int dsa_slave_port_obj_add(struct net_device *dev, - struct switchdev_obj *obj) + struct switchdev_obj *obj, + struct switchdev_trans *trans) { int err; @@ -485,10 +484,10 @@ static int dsa_slave_port_obj_add(struct net_device *dev, switch (obj->id) { case SWITCHDEV_OBJ_PORT_FDB: - err = dsa_slave_port_fdb_add(dev, obj); + err = dsa_slave_port_fdb_add(dev, obj, trans); break; case SWITCHDEV_OBJ_PORT_VLAN: - err = dsa_slave_port_vlan_add(dev, obj); + err = dsa_slave_port_vlan_add(dev, obj, trans); break; default: err = -EOPNOTSUPP; diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index fda38f830a10..00ee547ba45b 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -1,6 +1,6 @@ /* * net/switchdev/switchdev.c - Switch device API - * Copyright (c) 2014 Jiri Pirko + * Copyright (c) 2014-2015 Jiri Pirko * Copyright (c) 2014-2015 Scott Feldman * * This program is free software; you can redistribute it and/or modify @@ -16,9 +16,82 @@ #include #include #include +#include #include #include +/** + * switchdev_trans_item_enqueue - Enqueue data item to transaction queue + * + * @trans: transaction + * @data: pointer to data being queued + * @destructor: data destructor + * @tritem: transaction item being queued + * + * Enqeueue data item to transaction queue. tritem is typically placed in + * cointainter pointed at by data pointer. Destructor is called on + * transaction abort and after successful commit phase in case + * the caller did not dequeue the item before. + */ +void switchdev_trans_item_enqueue(struct switchdev_trans *trans, + void *data, void (*destructor)(void const *), + struct switchdev_trans_item *tritem) +{ + tritem->data = data; + tritem->destructor = destructor; + list_add_tail(&tritem->list, &trans->item_list); +} +EXPORT_SYMBOL_GPL(switchdev_trans_item_enqueue); + +static struct switchdev_trans_item * +__switchdev_trans_item_dequeue(struct switchdev_trans *trans) +{ + struct switchdev_trans_item *tritem; + + if (list_empty(&trans->item_list)) + return NULL; + tritem = list_first_entry(&trans->item_list, + struct switchdev_trans_item, list); + list_del(&tritem->list); + return tritem; +} + +/** + * switchdev_trans_item_dequeue - Dequeue data item from transaction queue + * + * @trans: transaction + */ +void *switchdev_trans_item_dequeue(struct switchdev_trans *trans) +{ + struct switchdev_trans_item *tritem; + + tritem = __switchdev_trans_item_dequeue(trans); + BUG_ON(!tritem); + return tritem->data; +} +EXPORT_SYMBOL_GPL(switchdev_trans_item_dequeue); + +static void switchdev_trans_init(struct switchdev_trans *trans) +{ + INIT_LIST_HEAD(&trans->item_list); +} + +static void switchdev_trans_items_destroy(struct switchdev_trans *trans) +{ + struct switchdev_trans_item *tritem; + + while ((tritem = __switchdev_trans_item_dequeue(trans))) + tritem->destructor(tritem->data); +} + +static void switchdev_trans_items_warn_destroy(struct net_device *dev, + struct switchdev_trans *trans) +{ + WARN(!list_empty(&trans->item_list), "%s: transaction item queue is not empty.\n", + dev->name); + switchdev_trans_items_destroy(trans); +} + /** * switchdev_port_attr_get - Get port attribute * @@ -62,7 +135,8 @@ int switchdev_port_attr_get(struct net_device *dev, struct switchdev_attr *attr) EXPORT_SYMBOL_GPL(switchdev_port_attr_get); static int __switchdev_port_attr_set(struct net_device *dev, - struct switchdev_attr *attr) + struct switchdev_attr *attr, + struct switchdev_trans *trans) { const struct switchdev_ops *ops = dev->switchdev_ops; struct net_device *lower_dev; @@ -70,7 +144,7 @@ static int __switchdev_port_attr_set(struct net_device *dev, int err = -EOPNOTSUPP; if (ops && ops->switchdev_port_attr_set) - return ops->switchdev_port_attr_set(dev, attr); + return ops->switchdev_port_attr_set(dev, attr, trans); if (attr->flags & SWITCHDEV_F_NO_RECURSE) return err; @@ -81,7 +155,7 @@ static int __switchdev_port_attr_set(struct net_device *dev, */ netdev_for_each_lower_dev(dev, lower_dev, iter) { - err = __switchdev_port_attr_set(lower_dev, attr); + err = __switchdev_port_attr_set(lower_dev, attr, trans); if (err) break; } @@ -144,6 +218,7 @@ static int switchdev_port_attr_set_defer(struct net_device *dev, */ int switchdev_port_attr_set(struct net_device *dev, struct switchdev_attr *attr) { + struct switchdev_trans trans; int err; if (!rtnl_is_locked()) { @@ -156,6 +231,8 @@ int switchdev_port_attr_set(struct net_device *dev, struct switchdev_attr *attr) return switchdev_port_attr_set_defer(dev, attr); } + switchdev_trans_init(&trans); + /* Phase I: prepare for attr set. Driver/device should fail * here if there are going to be issues in the commit phase, * such as lack of resources or support. The driver/device @@ -163,18 +240,16 @@ int switchdev_port_attr_set(struct net_device *dev, struct switchdev_attr *attr) * but should not commit the attr. */ - attr->trans = SWITCHDEV_TRANS_PREPARE; - err = __switchdev_port_attr_set(dev, attr); + trans.ph_prepare = true; + err = __switchdev_port_attr_set(dev, attr, &trans); if (err) { /* Prepare phase failed: abort the transaction. Any * resources reserved in the prepare phase are * released. */ - if (err != -EOPNOTSUPP) { - attr->trans = SWITCHDEV_TRANS_ABORT; - __switchdev_port_attr_set(dev, attr); - } + if (err != -EOPNOTSUPP) + switchdev_trans_items_destroy(&trans); return err; } @@ -184,17 +259,19 @@ int switchdev_port_attr_set(struct net_device *dev, struct switchdev_attr *attr) * because the driver said everythings was OK in phase I. */ - attr->trans = SWITCHDEV_TRANS_COMMIT; - err = __switchdev_port_attr_set(dev, attr); + trans.ph_prepare = false; + err = __switchdev_port_attr_set(dev, attr, &trans); WARN(err, "%s: Commit of attribute (id=%d) failed.\n", dev->name, attr->id); + switchdev_trans_items_warn_destroy(dev, &trans); return err; } EXPORT_SYMBOL_GPL(switchdev_port_attr_set); static int __switchdev_port_obj_add(struct net_device *dev, - struct switchdev_obj *obj) + struct switchdev_obj *obj, + struct switchdev_trans *trans) { const struct switchdev_ops *ops = dev->switchdev_ops; struct net_device *lower_dev; @@ -202,7 +279,7 @@ static int __switchdev_port_obj_add(struct net_device *dev, int err = -EOPNOTSUPP; if (ops && ops->switchdev_port_obj_add) - return ops->switchdev_port_obj_add(dev, obj); + return ops->switchdev_port_obj_add(dev, obj, trans); /* Switch device port(s) may be stacked under * bond/team/vlan dev, so recurse down to add object on @@ -210,7 +287,7 @@ static int __switchdev_port_obj_add(struct net_device *dev, */ netdev_for_each_lower_dev(dev, lower_dev, iter) { - err = __switchdev_port_obj_add(lower_dev, obj); + err = __switchdev_port_obj_add(lower_dev, obj, trans); if (err) break; } @@ -232,10 +309,13 @@ static int __switchdev_port_obj_add(struct net_device *dev, */ int switchdev_port_obj_add(struct net_device *dev, struct switchdev_obj *obj) { + struct switchdev_trans trans; int err; ASSERT_RTNL(); + switchdev_trans_init(&trans); + /* Phase I: prepare for obj add. Driver/device should fail * here if there are going to be issues in the commit phase, * such as lack of resources or support. The driver/device @@ -243,18 +323,16 @@ int switchdev_port_obj_add(struct net_device *dev, struct switchdev_obj *obj) * but should not commit the obj. */ - obj->trans = SWITCHDEV_TRANS_PREPARE; - err = __switchdev_port_obj_add(dev, obj); + trans.ph_prepare = true; + err = __switchdev_port_obj_add(dev, obj, &trans); if (err) { /* Prepare phase failed: abort the transaction. Any * resources reserved in the prepare phase are * released. */ - if (err != -EOPNOTSUPP) { - obj->trans = SWITCHDEV_TRANS_ABORT; - __switchdev_port_obj_add(dev, obj); - } + if (err != -EOPNOTSUPP) + switchdev_trans_items_destroy(&trans); return err; } @@ -264,9 +342,10 @@ int switchdev_port_obj_add(struct net_device *dev, struct switchdev_obj *obj) * because the driver said everythings was OK in phase I. */ - obj->trans = SWITCHDEV_TRANS_COMMIT; - err = __switchdev_port_obj_add(dev, obj); + trans.ph_prepare = false; + err = __switchdev_port_obj_add(dev, obj, &trans); WARN(err, "%s: Commit of object (id=%d) failed.\n", dev->name, obj->id); + switchdev_trans_items_warn_destroy(dev, &trans); return err; }