Commit b13cead1 authored by David S. Miller's avatar David S. Miller

Merge branch 'ionic-next'

Shannon Nelson says:

====================
ionic: queue and filter mgmt updates

After a pair of simple code cleanups, we change the mac filter
management to split the updates between the driver's filter
list and the device's filter list so that we can keep the calls
to dev_uc_sync() and dev_mc_sync() under the netif_addr_lock
in ndo_set_rx_mode, and then sync the driver's list to the
device later in the rx_mode work task.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 7fe2f1bc a0c007b3
......@@ -15,6 +15,7 @@ static void ionic_watchdog_cb(struct timer_list *t)
{
struct ionic *ionic = from_timer(ionic, t, watchdog_timer);
struct ionic_lif *lif = ionic->lif;
struct ionic_deferred_work *work;
int hb;
mod_timer(&ionic->watchdog_timer,
......@@ -31,6 +32,18 @@ static void ionic_watchdog_cb(struct timer_list *t)
if (hb >= 0 &&
!test_bit(IONIC_LIF_F_FW_RESET, lif->state))
ionic_link_status_check_request(lif, CAN_NOT_SLEEP);
if (test_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state)) {
work = kzalloc(sizeof(*work), GFP_ATOMIC);
if (!work) {
netdev_err(lif->netdev, "rxmode change dropped\n");
return;
}
work->type = IONIC_DW_TYPE_RX_MODE;
netdev_dbg(lif->netdev, "deferred: rx_mode\n");
ionic_lif_deferred_enqueue(&lif->deferred, work);
}
}
void ionic_init_devinfo(struct ionic *ionic)
......
......@@ -98,8 +98,6 @@ struct ionic_qcq {
enum ionic_deferred_work_type {
IONIC_DW_TYPE_RX_MODE,
IONIC_DW_TYPE_RX_ADDR_ADD,
IONIC_DW_TYPE_RX_ADDR_DEL,
IONIC_DW_TYPE_LINK_STATUS,
IONIC_DW_TYPE_LIF_RESET,
};
......@@ -147,6 +145,7 @@ enum ionic_lif_state_flags {
IONIC_LIF_F_SW_DEBUG_STATS,
IONIC_LIF_F_UP,
IONIC_LIF_F_LINK_CHECK_REQUESTED,
IONIC_LIF_F_FILTER_SYNC_NEEDED,
IONIC_LIF_F_FW_RESET,
IONIC_LIF_F_SPLIT_INTR,
IONIC_LIF_F_BROKEN,
......@@ -295,6 +294,10 @@ int ionic_lif_alloc(struct ionic *ionic);
int ionic_lif_init(struct ionic_lif *lif);
void ionic_lif_free(struct ionic_lif *lif);
void ionic_lif_deinit(struct ionic_lif *lif);
int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr);
int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr);
int ionic_lif_register(struct ionic_lif *lif);
void ionic_lif_unregister(struct ionic_lif *lif);
int ionic_lif_identify(struct ionic *ionic, u8 lif_type,
......@@ -342,6 +345,7 @@ int ionic_lif_set_hwstamp_rxfilt(struct ionic_lif *lif, u64 pkt_class);
int ionic_lif_rss_config(struct ionic_lif *lif, u16 types,
const u8 *key, const u32 *indir);
void ionic_lif_rx_mode(struct ionic_lif *lif);
int ionic_reconfigure_queues(struct ionic_lif *lif,
struct ionic_queue_params *qparam);
......
......@@ -4,6 +4,7 @@
#include <linux/netdevice.h>
#include <linux/dynamic_debug.h>
#include <linux/etherdevice.h>
#include <linux/list.h>
#include "ionic.h"
#include "ionic_lif.h"
......@@ -120,11 +121,12 @@ void ionic_rx_filters_deinit(struct ionic_lif *lif)
}
int ionic_rx_filter_save(struct ionic_lif *lif, u32 flow_id, u16 rxq_index,
u32 hash, struct ionic_admin_ctx *ctx)
u32 hash, struct ionic_admin_ctx *ctx,
enum ionic_filter_state state)
{
struct device *dev = lif->ionic->dev;
struct ionic_rx_filter_add_cmd *ac;
struct ionic_rx_filter *f;
struct ionic_rx_filter *f = NULL;
struct hlist_head *head;
unsigned int key;
......@@ -133,9 +135,11 @@ int ionic_rx_filter_save(struct ionic_lif *lif, u32 flow_id, u16 rxq_index,
switch (le16_to_cpu(ac->match)) {
case IONIC_RX_FILTER_MATCH_VLAN:
key = le16_to_cpu(ac->vlan.vlan);
f = ionic_rx_filter_by_vlan(lif, le16_to_cpu(ac->vlan.vlan));
break;
case IONIC_RX_FILTER_MATCH_MAC:
key = *(u32 *)ac->mac.addr;
f = ionic_rx_filter_by_addr(lif, ac->mac.addr);
break;
case IONIC_RX_FILTER_MATCH_MAC_VLAN:
key = le16_to_cpu(ac->mac_vlan.vlan);
......@@ -147,12 +151,19 @@ int ionic_rx_filter_save(struct ionic_lif *lif, u32 flow_id, u16 rxq_index,
return -EINVAL;
}
f = devm_kzalloc(dev, sizeof(*f), GFP_KERNEL);
if (!f)
return -ENOMEM;
if (f) {
/* remove from current linking so we can refresh it */
hlist_del(&f->by_id);
hlist_del(&f->by_hash);
} else {
f = devm_kzalloc(dev, sizeof(*f), GFP_ATOMIC);
if (!f)
return -ENOMEM;
}
f->flow_id = flow_id;
f->filter_id = le32_to_cpu(ctx->comp.rx_filter_add.filter_id);
f->state = state;
f->rxq_index = rxq_index;
memcpy(&f->cmd, ac, sizeof(f->cmd));
netdev_dbg(lif->netdev, "rx_filter add filter_id %d\n", f->filter_id);
......@@ -160,8 +171,6 @@ int ionic_rx_filter_save(struct ionic_lif *lif, u32 flow_id, u16 rxq_index,
INIT_HLIST_NODE(&f->by_hash);
INIT_HLIST_NODE(&f->by_id);
spin_lock_bh(&lif->rx_filters.lock);
key = hash_32(key, IONIC_RX_FILTER_HASH_BITS);
head = &lif->rx_filters.by_hash[key];
hlist_add_head(&f->by_hash, head);
......@@ -170,8 +179,6 @@ int ionic_rx_filter_save(struct ionic_lif *lif, u32 flow_id, u16 rxq_index,
head = &lif->rx_filters.by_id[key];
hlist_add_head(&f->by_id, head);
spin_unlock_bh(&lif->rx_filters.lock);
return 0;
}
......@@ -231,3 +238,121 @@ struct ionic_rx_filter *ionic_rx_filter_rxsteer(struct ionic_lif *lif)
return NULL;
}
int ionic_lif_list_addr(struct ionic_lif *lif, const u8 *addr, bool mode)
{
struct ionic_rx_filter *f;
int err;
spin_lock_bh(&lif->rx_filters.lock);
f = ionic_rx_filter_by_addr(lif, addr);
if (mode == ADD_ADDR && !f) {
struct ionic_admin_ctx ctx = {
.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
.cmd.rx_filter_add = {
.opcode = IONIC_CMD_RX_FILTER_ADD,
.lif_index = cpu_to_le16(lif->index),
.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_MAC),
},
};
memcpy(ctx.cmd.rx_filter_add.mac.addr, addr, ETH_ALEN);
err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
IONIC_FILTER_STATE_NEW);
if (err) {
spin_unlock_bh(&lif->rx_filters.lock);
return err;
}
} else if (mode == ADD_ADDR && f) {
if (f->state == IONIC_FILTER_STATE_OLD)
f->state = IONIC_FILTER_STATE_SYNCED;
} else if (mode == DEL_ADDR && f) {
if (f->state == IONIC_FILTER_STATE_NEW)
ionic_rx_filter_free(lif, f);
else if (f->state == IONIC_FILTER_STATE_SYNCED)
f->state = IONIC_FILTER_STATE_OLD;
} else if (mode == DEL_ADDR && !f) {
spin_unlock_bh(&lif->rx_filters.lock);
return -ENOENT;
}
spin_unlock_bh(&lif->rx_filters.lock);
set_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state);
return 0;
}
struct sync_item {
struct list_head list;
struct ionic_rx_filter f;
};
void ionic_rx_filter_sync(struct ionic_lif *lif)
{
struct device *dev = lif->ionic->dev;
struct list_head sync_add_list;
struct list_head sync_del_list;
struct sync_item *sync_item;
struct ionic_rx_filter *f;
struct hlist_head *head;
struct hlist_node *tmp;
struct sync_item *spos;
unsigned int i;
INIT_LIST_HEAD(&sync_add_list);
INIT_LIST_HEAD(&sync_del_list);
clear_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state);
/* Copy the filters to be added and deleted
* into a separate local list that needs no locking.
*/
spin_lock_bh(&lif->rx_filters.lock);
for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
head = &lif->rx_filters.by_id[i];
hlist_for_each_entry_safe(f, tmp, head, by_id) {
if (f->state == IONIC_FILTER_STATE_NEW ||
f->state == IONIC_FILTER_STATE_OLD) {
sync_item = devm_kzalloc(dev, sizeof(*sync_item),
GFP_KERNEL);
if (!sync_item)
goto loop_out;
sync_item->f = *f;
if (f->state == IONIC_FILTER_STATE_NEW)
list_add(&sync_item->list, &sync_add_list);
else
list_add(&sync_item->list, &sync_del_list);
}
}
}
loop_out:
spin_unlock_bh(&lif->rx_filters.lock);
/* If the add or delete fails, it won't get marked as sync'd
* and will be tried again in the next sync action.
* Do the deletes first in case we're in an overflow state and
* they can clear room for some new filters
*/
list_for_each_entry_safe(sync_item, spos, &sync_del_list, list) {
(void)ionic_lif_addr_del(lif, sync_item->f.cmd.mac.addr);
list_del(&sync_item->list);
devm_kfree(dev, sync_item);
}
list_for_each_entry_safe(sync_item, spos, &sync_add_list, list) {
(void)ionic_lif_addr_add(lif, sync_item->f.cmd.mac.addr);
if (sync_item->f.state != IONIC_FILTER_STATE_SYNCED)
set_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state);
list_del(&sync_item->list);
devm_kfree(dev, sync_item);
}
}
......@@ -5,10 +5,18 @@
#define _IONIC_RX_FILTER_H_
#define IONIC_RXQ_INDEX_ANY (0xFFFF)
enum ionic_filter_state {
IONIC_FILTER_STATE_SYNCED,
IONIC_FILTER_STATE_NEW,
IONIC_FILTER_STATE_OLD,
};
struct ionic_rx_filter {
u32 flow_id;
u32 filter_id;
u16 rxq_index;
enum ionic_filter_state state;
struct ionic_rx_filter_add_cmd cmd;
struct hlist_node by_hash;
struct hlist_node by_id;
......@@ -28,9 +36,13 @@ void ionic_rx_filter_replay(struct ionic_lif *lif);
int ionic_rx_filters_init(struct ionic_lif *lif);
void ionic_rx_filters_deinit(struct ionic_lif *lif);
int ionic_rx_filter_save(struct ionic_lif *lif, u32 flow_id, u16 rxq_index,
u32 hash, struct ionic_admin_ctx *ctx);
u32 hash, struct ionic_admin_ctx *ctx,
enum ionic_filter_state state);
struct ionic_rx_filter *ionic_rx_filter_by_vlan(struct ionic_lif *lif, u16 vid);
struct ionic_rx_filter *ionic_rx_filter_by_addr(struct ionic_lif *lif, const u8 *addr);
struct ionic_rx_filter *ionic_rx_filter_rxsteer(struct ionic_lif *lif);
void ionic_rx_filter_sync(struct ionic_lif *lif);
int ionic_lif_list_addr(struct ionic_lif *lif, const u8 *addr, bool mode);
int ionic_rx_filters_need_sync(struct ionic_lif *lif);
#endif /* _IONIC_RX_FILTER_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment