Merge branch 'NCSI-vlan-filtering'
Samuel Mendoza-Jonas says: ==================== NCSI VLAN Filtering Support This series (mainly patch 2) adds VLAN filtering to the NCSI implementation. A fair amount of code already exists in the NCSI stack for VLAN filtering but none of it is actually hooked up. This goes the final mile and fixes a few bugs in the existing code found along the way (patch 1). Patch 3 adds the appropriate flag and callbacks to the ftgmac100 driver to enable filtering as it's a large consumer of NCSI (and what I've been testing on). v3: - Add comment describing change to ncsi_find_filter() - Catch NULL in clear_one_vid() from ncsi_get_filter() - Simplify state changes when kicking updated channel ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
33b86ba057
|
@ -1623,6 +1623,8 @@ static const struct net_device_ops ftgmac100_netdev_ops = {
|
||||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||||
.ndo_poll_controller = ftgmac100_poll_controller,
|
.ndo_poll_controller = ftgmac100_poll_controller,
|
||||||
#endif
|
#endif
|
||||||
|
.ndo_vlan_rx_add_vid = ncsi_vlan_rx_add_vid,
|
||||||
|
.ndo_vlan_rx_kill_vid = ncsi_vlan_rx_kill_vid,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int ftgmac100_setup_mdio(struct net_device *netdev)
|
static int ftgmac100_setup_mdio(struct net_device *netdev)
|
||||||
|
@ -1837,6 +1839,9 @@ static int ftgmac100_probe(struct platform_device *pdev)
|
||||||
NETIF_F_GRO | NETIF_F_SG | NETIF_F_HW_VLAN_CTAG_RX |
|
NETIF_F_GRO | NETIF_F_SG | NETIF_F_HW_VLAN_CTAG_RX |
|
||||||
NETIF_F_HW_VLAN_CTAG_TX;
|
NETIF_F_HW_VLAN_CTAG_TX;
|
||||||
|
|
||||||
|
if (priv->use_ncsi)
|
||||||
|
netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
|
||||||
|
|
||||||
/* AST2400 doesn't have working HW checksum generation */
|
/* AST2400 doesn't have working HW checksum generation */
|
||||||
if (np && (of_device_is_compatible(np, "aspeed,ast2400-mac")))
|
if (np && (of_device_is_compatible(np, "aspeed,ast2400-mac")))
|
||||||
netdev->hw_features &= ~NETIF_F_HW_CSUM;
|
netdev->hw_features &= ~NETIF_F_HW_CSUM;
|
||||||
|
|
|
@ -28,6 +28,8 @@ struct ncsi_dev {
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_NET_NCSI
|
#ifdef CONFIG_NET_NCSI
|
||||||
|
int ncsi_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid);
|
||||||
|
int ncsi_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid);
|
||||||
struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
|
struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
|
||||||
void (*notifier)(struct ncsi_dev *nd));
|
void (*notifier)(struct ncsi_dev *nd));
|
||||||
int ncsi_start_dev(struct ncsi_dev *nd);
|
int ncsi_start_dev(struct ncsi_dev *nd);
|
||||||
|
|
|
@ -180,6 +180,7 @@ struct ncsi_channel {
|
||||||
#define NCSI_CHANNEL_INACTIVE 1
|
#define NCSI_CHANNEL_INACTIVE 1
|
||||||
#define NCSI_CHANNEL_ACTIVE 2
|
#define NCSI_CHANNEL_ACTIVE 2
|
||||||
#define NCSI_CHANNEL_INVISIBLE 3
|
#define NCSI_CHANNEL_INVISIBLE 3
|
||||||
|
bool reconfigure_needed;
|
||||||
spinlock_t lock; /* Protect filters etc */
|
spinlock_t lock; /* Protect filters etc */
|
||||||
struct ncsi_package *package;
|
struct ncsi_package *package;
|
||||||
struct ncsi_channel_version version;
|
struct ncsi_channel_version version;
|
||||||
|
@ -235,6 +236,9 @@ enum {
|
||||||
ncsi_dev_state_probe_dp,
|
ncsi_dev_state_probe_dp,
|
||||||
ncsi_dev_state_config_sp = 0x0301,
|
ncsi_dev_state_config_sp = 0x0301,
|
||||||
ncsi_dev_state_config_cis,
|
ncsi_dev_state_config_cis,
|
||||||
|
ncsi_dev_state_config_clear_vids,
|
||||||
|
ncsi_dev_state_config_svf,
|
||||||
|
ncsi_dev_state_config_ev,
|
||||||
ncsi_dev_state_config_sma,
|
ncsi_dev_state_config_sma,
|
||||||
ncsi_dev_state_config_ebf,
|
ncsi_dev_state_config_ebf,
|
||||||
#if IS_ENABLED(CONFIG_IPV6)
|
#if IS_ENABLED(CONFIG_IPV6)
|
||||||
|
@ -253,6 +257,12 @@ enum {
|
||||||
ncsi_dev_state_suspend_done
|
ncsi_dev_state_suspend_done
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct vlan_vid {
|
||||||
|
struct list_head list;
|
||||||
|
__be16 proto;
|
||||||
|
u16 vid;
|
||||||
|
};
|
||||||
|
|
||||||
struct ncsi_dev_priv {
|
struct ncsi_dev_priv {
|
||||||
struct ncsi_dev ndev; /* Associated NCSI device */
|
struct ncsi_dev ndev; /* Associated NCSI device */
|
||||||
unsigned int flags; /* NCSI device flags */
|
unsigned int flags; /* NCSI device flags */
|
||||||
|
@ -276,6 +286,7 @@ struct ncsi_dev_priv {
|
||||||
struct work_struct work; /* For channel management */
|
struct work_struct work; /* For channel management */
|
||||||
struct packet_type ptype; /* NCSI packet Rx handler */
|
struct packet_type ptype; /* NCSI packet Rx handler */
|
||||||
struct list_head node; /* Form NCSI device list */
|
struct list_head node; /* Form NCSI device list */
|
||||||
|
struct list_head vlan_vids; /* List of active VLAN IDs */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ncsi_cmd_arg {
|
struct ncsi_cmd_arg {
|
||||||
|
|
|
@ -139,9 +139,9 @@ static int ncsi_cmd_handler_svf(struct sk_buff *skb,
|
||||||
struct ncsi_cmd_svf_pkt *cmd;
|
struct ncsi_cmd_svf_pkt *cmd;
|
||||||
|
|
||||||
cmd = skb_put_zero(skb, sizeof(*cmd));
|
cmd = skb_put_zero(skb, sizeof(*cmd));
|
||||||
cmd->vlan = htons(nca->words[0]);
|
cmd->vlan = htons(nca->words[1]);
|
||||||
cmd->index = nca->bytes[2];
|
cmd->index = nca->bytes[6];
|
||||||
cmd->enable = nca->bytes[3];
|
cmd->enable = nca->bytes[7];
|
||||||
ncsi_cmd_build_header(&cmd->cmd.common, nca);
|
ncsi_cmd_build_header(&cmd->cmd.common, nca);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -153,7 +153,7 @@ static int ncsi_cmd_handler_ev(struct sk_buff *skb,
|
||||||
struct ncsi_cmd_ev_pkt *cmd;
|
struct ncsi_cmd_ev_pkt *cmd;
|
||||||
|
|
||||||
cmd = skb_put_zero(skb, sizeof(*cmd));
|
cmd = skb_put_zero(skb, sizeof(*cmd));
|
||||||
cmd->mode = nca->bytes[0];
|
cmd->mode = nca->bytes[3];
|
||||||
ncsi_cmd_build_header(&cmd->cmd.common, nca);
|
ncsi_cmd_build_header(&cmd->cmd.common, nca);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -228,7 +228,7 @@ static struct ncsi_cmd_handler {
|
||||||
{ NCSI_PKT_CMD_AE, 8, ncsi_cmd_handler_ae },
|
{ NCSI_PKT_CMD_AE, 8, ncsi_cmd_handler_ae },
|
||||||
{ NCSI_PKT_CMD_SL, 8, ncsi_cmd_handler_sl },
|
{ NCSI_PKT_CMD_SL, 8, ncsi_cmd_handler_sl },
|
||||||
{ NCSI_PKT_CMD_GLS, 0, ncsi_cmd_handler_default },
|
{ NCSI_PKT_CMD_GLS, 0, ncsi_cmd_handler_default },
|
||||||
{ NCSI_PKT_CMD_SVF, 4, ncsi_cmd_handler_svf },
|
{ NCSI_PKT_CMD_SVF, 8, ncsi_cmd_handler_svf },
|
||||||
{ NCSI_PKT_CMD_EV, 4, ncsi_cmd_handler_ev },
|
{ NCSI_PKT_CMD_EV, 4, ncsi_cmd_handler_ev },
|
||||||
{ NCSI_PKT_CMD_DV, 0, ncsi_cmd_handler_default },
|
{ NCSI_PKT_CMD_DV, 0, ncsi_cmd_handler_default },
|
||||||
{ NCSI_PKT_CMD_SMA, 8, ncsi_cmd_handler_sma },
|
{ NCSI_PKT_CMD_SMA, 8, ncsi_cmd_handler_sma },
|
||||||
|
|
|
@ -38,6 +38,25 @@ static inline int ncsi_filter_size(int table)
|
||||||
return sizes[table];
|
return sizes[table];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 *ncsi_get_filter(struct ncsi_channel *nc, int table, int index)
|
||||||
|
{
|
||||||
|
struct ncsi_channel_filter *ncf;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
ncf = nc->filters[table];
|
||||||
|
if (!ncf)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
size = ncsi_filter_size(table);
|
||||||
|
if (size < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return ncf->data + size * index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the first active filter in a filter table that matches the given
|
||||||
|
* data parameter. If data is NULL, this returns the first active filter.
|
||||||
|
*/
|
||||||
int ncsi_find_filter(struct ncsi_channel *nc, int table, void *data)
|
int ncsi_find_filter(struct ncsi_channel *nc, int table, void *data)
|
||||||
{
|
{
|
||||||
struct ncsi_channel_filter *ncf;
|
struct ncsi_channel_filter *ncf;
|
||||||
|
@ -58,7 +77,7 @@ int ncsi_find_filter(struct ncsi_channel *nc, int table, void *data)
|
||||||
index = -1;
|
index = -1;
|
||||||
while ((index = find_next_bit(bitmap, ncf->total, index + 1))
|
while ((index = find_next_bit(bitmap, ncf->total, index + 1))
|
||||||
< ncf->total) {
|
< ncf->total) {
|
||||||
if (!memcmp(ncf->data + size * index, data, size)) {
|
if (!data || !memcmp(ncf->data + size * index, data, size)) {
|
||||||
spin_unlock_irqrestore(&nc->lock, flags);
|
spin_unlock_irqrestore(&nc->lock, flags);
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
@ -639,6 +658,95 @@ error:
|
||||||
nd->state = ncsi_dev_state_functional;
|
nd->state = ncsi_dev_state_functional;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check the VLAN filter bitmap for a set filter, and construct a
|
||||||
|
* "Set VLAN Filter - Disable" packet if found.
|
||||||
|
*/
|
||||||
|
static int clear_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
|
||||||
|
struct ncsi_cmd_arg *nca)
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
u32 *data;
|
||||||
|
u16 vid;
|
||||||
|
|
||||||
|
index = ncsi_find_filter(nc, NCSI_FILTER_VLAN, NULL);
|
||||||
|
if (index < 0) {
|
||||||
|
/* Filter table empty */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = ncsi_get_filter(nc, NCSI_FILTER_VLAN, index);
|
||||||
|
if (!data) {
|
||||||
|
netdev_err(ndp->ndev.dev,
|
||||||
|
"ncsi: failed to retrieve filter %d\n", index);
|
||||||
|
/* Set the VLAN id to 0 - this will still disable the entry in
|
||||||
|
* the filter table, but we won't know what it was.
|
||||||
|
*/
|
||||||
|
vid = 0;
|
||||||
|
} else {
|
||||||
|
vid = *(u16 *)data;
|
||||||
|
}
|
||||||
|
|
||||||
|
netdev_printk(KERN_DEBUG, ndp->ndev.dev,
|
||||||
|
"ncsi: removed vlan tag %u at index %d\n",
|
||||||
|
vid, index + 1);
|
||||||
|
ncsi_remove_filter(nc, NCSI_FILTER_VLAN, index);
|
||||||
|
|
||||||
|
nca->type = NCSI_PKT_CMD_SVF;
|
||||||
|
nca->words[1] = vid;
|
||||||
|
/* HW filter index starts at 1 */
|
||||||
|
nca->bytes[6] = index + 1;
|
||||||
|
nca->bytes[7] = 0x00;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find an outstanding VLAN tag and constuct a "Set VLAN Filter - Enable"
|
||||||
|
* packet.
|
||||||
|
*/
|
||||||
|
static int set_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
|
||||||
|
struct ncsi_cmd_arg *nca)
|
||||||
|
{
|
||||||
|
struct vlan_vid *vlan = NULL;
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
|
||||||
|
index = ncsi_find_filter(nc, NCSI_FILTER_VLAN, &vlan->vid);
|
||||||
|
if (index < 0) {
|
||||||
|
/* New tag to add */
|
||||||
|
netdev_printk(KERN_DEBUG, ndp->ndev.dev,
|
||||||
|
"ncsi: new vlan id to set: %u\n",
|
||||||
|
vlan->vid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
netdev_printk(KERN_DEBUG, ndp->ndev.dev,
|
||||||
|
"vid %u already at filter pos %d\n",
|
||||||
|
vlan->vid, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vlan || index >= 0) {
|
||||||
|
netdev_printk(KERN_DEBUG, ndp->ndev.dev,
|
||||||
|
"no vlan ids left to set\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
index = ncsi_add_filter(nc, NCSI_FILTER_VLAN, &vlan->vid);
|
||||||
|
if (index < 0) {
|
||||||
|
netdev_err(ndp->ndev.dev,
|
||||||
|
"Failed to add new VLAN tag, error %d\n", index);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
netdev_printk(KERN_DEBUG, ndp->ndev.dev,
|
||||||
|
"ncsi: set vid %u in packet, index %u\n",
|
||||||
|
vlan->vid, index + 1);
|
||||||
|
nca->type = NCSI_PKT_CMD_SVF;
|
||||||
|
nca->words[1] = vlan->vid;
|
||||||
|
/* HW filter index starts at 1 */
|
||||||
|
nca->bytes[6] = index + 1;
|
||||||
|
nca->bytes[7] = 0x01;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
|
static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
|
||||||
{
|
{
|
||||||
struct ncsi_dev *nd = &ndp->ndev;
|
struct ncsi_dev *nd = &ndp->ndev;
|
||||||
|
@ -683,8 +791,11 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
nd->state = ncsi_dev_state_config_sma;
|
nd->state = ncsi_dev_state_config_clear_vids;
|
||||||
break;
|
break;
|
||||||
|
case ncsi_dev_state_config_clear_vids:
|
||||||
|
case ncsi_dev_state_config_svf:
|
||||||
|
case ncsi_dev_state_config_ev:
|
||||||
case ncsi_dev_state_config_sma:
|
case ncsi_dev_state_config_sma:
|
||||||
case ncsi_dev_state_config_ebf:
|
case ncsi_dev_state_config_ebf:
|
||||||
#if IS_ENABLED(CONFIG_IPV6)
|
#if IS_ENABLED(CONFIG_IPV6)
|
||||||
|
@ -699,11 +810,40 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
|
||||||
nca.package = np->id;
|
nca.package = np->id;
|
||||||
nca.channel = nc->id;
|
nca.channel = nc->id;
|
||||||
|
|
||||||
|
/* Clear any active filters on the channel before setting */
|
||||||
|
if (nd->state == ncsi_dev_state_config_clear_vids) {
|
||||||
|
ret = clear_one_vid(ndp, nc, &nca);
|
||||||
|
if (ret) {
|
||||||
|
nd->state = ncsi_dev_state_config_svf;
|
||||||
|
schedule_work(&ndp->work);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Repeat */
|
||||||
|
nd->state = ncsi_dev_state_config_clear_vids;
|
||||||
|
/* Add known VLAN tags to the filter */
|
||||||
|
} else if (nd->state == ncsi_dev_state_config_svf) {
|
||||||
|
ret = set_one_vid(ndp, nc, &nca);
|
||||||
|
if (ret) {
|
||||||
|
nd->state = ncsi_dev_state_config_ev;
|
||||||
|
schedule_work(&ndp->work);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Repeat */
|
||||||
|
nd->state = ncsi_dev_state_config_svf;
|
||||||
|
/* Enable/Disable the VLAN filter */
|
||||||
|
} else if (nd->state == ncsi_dev_state_config_ev) {
|
||||||
|
if (list_empty(&ndp->vlan_vids)) {
|
||||||
|
nca.type = NCSI_PKT_CMD_DV;
|
||||||
|
} else {
|
||||||
|
nca.type = NCSI_PKT_CMD_EV;
|
||||||
|
nca.bytes[3] = NCSI_CAP_VLAN_NO;
|
||||||
|
}
|
||||||
|
nd->state = ncsi_dev_state_config_sma;
|
||||||
|
} else if (nd->state == ncsi_dev_state_config_sma) {
|
||||||
/* Use first entry in unicast filter table. Note that
|
/* Use first entry in unicast filter table. Note that
|
||||||
* the MAC filter table starts from entry 1 instead of
|
* the MAC filter table starts from entry 1 instead of
|
||||||
* 0.
|
* 0.
|
||||||
*/
|
*/
|
||||||
if (nd->state == ncsi_dev_state_config_sma) {
|
|
||||||
nca.type = NCSI_PKT_CMD_SMA;
|
nca.type = NCSI_PKT_CMD_SMA;
|
||||||
for (index = 0; index < 6; index++)
|
for (index = 0; index < 6; index++)
|
||||||
nca.bytes[index] = dev->dev_addr[index];
|
nca.bytes[index] = dev->dev_addr[index];
|
||||||
|
@ -751,6 +891,25 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
|
||||||
break;
|
break;
|
||||||
case ncsi_dev_state_config_done:
|
case ncsi_dev_state_config_done:
|
||||||
spin_lock_irqsave(&nc->lock, flags);
|
spin_lock_irqsave(&nc->lock, flags);
|
||||||
|
if (nc->reconfigure_needed) {
|
||||||
|
/* This channel's configuration has been updated
|
||||||
|
* part-way during the config state - start the
|
||||||
|
* channel configuration over
|
||||||
|
*/
|
||||||
|
nc->reconfigure_needed = false;
|
||||||
|
nc->state = NCSI_CHANNEL_INACTIVE;
|
||||||
|
spin_unlock_irqrestore(&nc->lock, flags);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&ndp->lock, flags);
|
||||||
|
list_add_tail_rcu(&nc->link, &ndp->channel_queue);
|
||||||
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
||||||
|
|
||||||
|
netdev_printk(KERN_DEBUG, dev,
|
||||||
|
"Dirty NCSI channel state reset\n");
|
||||||
|
ncsi_process_next_channel(ndp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
|
if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
|
||||||
hot_nc = nc;
|
hot_nc = nc;
|
||||||
nc->state = NCSI_CHANNEL_ACTIVE;
|
nc->state = NCSI_CHANNEL_ACTIVE;
|
||||||
|
@ -1191,6 +1350,148 @@ static struct notifier_block ncsi_inet6addr_notifier = {
|
||||||
};
|
};
|
||||||
#endif /* CONFIG_IPV6 */
|
#endif /* CONFIG_IPV6 */
|
||||||
|
|
||||||
|
static int ncsi_kick_channels(struct ncsi_dev_priv *ndp)
|
||||||
|
{
|
||||||
|
struct ncsi_dev *nd = &ndp->ndev;
|
||||||
|
struct ncsi_channel *nc;
|
||||||
|
struct ncsi_package *np;
|
||||||
|
unsigned long flags;
|
||||||
|
unsigned int n = 0;
|
||||||
|
|
||||||
|
NCSI_FOR_EACH_PACKAGE(ndp, np) {
|
||||||
|
NCSI_FOR_EACH_CHANNEL(np, nc) {
|
||||||
|
spin_lock_irqsave(&nc->lock, flags);
|
||||||
|
|
||||||
|
/* Channels may be busy, mark dirty instead of
|
||||||
|
* kicking if;
|
||||||
|
* a) not ACTIVE (configured)
|
||||||
|
* b) in the channel_queue (to be configured)
|
||||||
|
* c) it's ndev is in the config state
|
||||||
|
*/
|
||||||
|
if (nc->state != NCSI_CHANNEL_ACTIVE) {
|
||||||
|
if ((ndp->ndev.state & 0xff00) ==
|
||||||
|
ncsi_dev_state_config ||
|
||||||
|
!list_empty(&nc->link)) {
|
||||||
|
netdev_printk(KERN_DEBUG, nd->dev,
|
||||||
|
"ncsi: channel %p marked dirty\n",
|
||||||
|
nc);
|
||||||
|
nc->reconfigure_needed = true;
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&nc->lock, flags);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&nc->lock, flags);
|
||||||
|
|
||||||
|
ncsi_stop_channel_monitor(nc);
|
||||||
|
spin_lock_irqsave(&nc->lock, flags);
|
||||||
|
nc->state = NCSI_CHANNEL_INACTIVE;
|
||||||
|
spin_unlock_irqrestore(&nc->lock, flags);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&ndp->lock, flags);
|
||||||
|
list_add_tail_rcu(&nc->link, &ndp->channel_queue);
|
||||||
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
||||||
|
|
||||||
|
netdev_printk(KERN_DEBUG, nd->dev,
|
||||||
|
"ncsi: kicked channel %p\n", nc);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ncsi_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
|
||||||
|
{
|
||||||
|
struct ncsi_channel_filter *ncf;
|
||||||
|
struct ncsi_dev_priv *ndp;
|
||||||
|
unsigned int n_vids = 0;
|
||||||
|
struct vlan_vid *vlan;
|
||||||
|
struct ncsi_dev *nd;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
if (vid == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
nd = ncsi_find_dev(dev);
|
||||||
|
if (!nd) {
|
||||||
|
netdev_warn(dev, "ncsi: No net_device?\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ndp = TO_NCSI_DEV_PRIV(nd);
|
||||||
|
ncf = ndp->hot_channel->filters[NCSI_FILTER_VLAN];
|
||||||
|
|
||||||
|
/* Add the VLAN id to our internal list */
|
||||||
|
list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
|
||||||
|
n_vids++;
|
||||||
|
if (vlan->vid == vid) {
|
||||||
|
netdev_printk(KERN_DEBUG, dev,
|
||||||
|
"vid %u already registered\n", vid);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n_vids >= ncf->total) {
|
||||||
|
netdev_info(dev,
|
||||||
|
"NCSI Channel supports up to %u VLAN tags but %u are already set\n",
|
||||||
|
ncf->total, n_vids);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
|
||||||
|
if (!vlan)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
vlan->proto = proto;
|
||||||
|
vlan->vid = vid;
|
||||||
|
list_add_rcu(&vlan->list, &ndp->vlan_vids);
|
||||||
|
|
||||||
|
netdev_printk(KERN_DEBUG, dev, "Added new vid %u\n", vid);
|
||||||
|
|
||||||
|
found = ncsi_kick_channels(ndp) != 0;
|
||||||
|
|
||||||
|
return found ? ncsi_process_next_channel(ndp) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ncsi_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
|
||||||
|
{
|
||||||
|
struct vlan_vid *vlan, *tmp;
|
||||||
|
struct ncsi_dev_priv *ndp;
|
||||||
|
struct ncsi_dev *nd;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
if (vid == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
nd = ncsi_find_dev(dev);
|
||||||
|
if (!nd) {
|
||||||
|
netdev_warn(dev, "ncsi: no net_device?\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ndp = TO_NCSI_DEV_PRIV(nd);
|
||||||
|
|
||||||
|
/* Remove the VLAN id from our internal list */
|
||||||
|
list_for_each_entry_safe(vlan, tmp, &ndp->vlan_vids, list)
|
||||||
|
if (vlan->vid == vid) {
|
||||||
|
netdev_printk(KERN_DEBUG, dev,
|
||||||
|
"vid %u found, removing\n", vid);
|
||||||
|
list_del_rcu(&vlan->list);
|
||||||
|
found = true;
|
||||||
|
kfree(vlan);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
netdev_err(dev, "ncsi: vid %u wasn't registered!\n", vid);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
found = ncsi_kick_channels(ndp) != 0;
|
||||||
|
|
||||||
|
return found ? ncsi_process_next_channel(ndp) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
|
struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
|
||||||
void (*handler)(struct ncsi_dev *ndev))
|
void (*handler)(struct ncsi_dev *ndev))
|
||||||
{
|
{
|
||||||
|
@ -1215,6 +1516,7 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
|
||||||
nd->handler = handler;
|
nd->handler = handler;
|
||||||
ndp->pending_req_num = 0;
|
ndp->pending_req_num = 0;
|
||||||
INIT_LIST_HEAD(&ndp->channel_queue);
|
INIT_LIST_HEAD(&ndp->channel_queue);
|
||||||
|
INIT_LIST_HEAD(&ndp->vlan_vids);
|
||||||
INIT_WORK(&ndp->work, ncsi_dev_work);
|
INIT_WORK(&ndp->work, ncsi_dev_work);
|
||||||
|
|
||||||
/* Initialize private NCSI device */
|
/* Initialize private NCSI device */
|
||||||
|
|
|
@ -104,7 +104,7 @@ struct ncsi_cmd_svf_pkt {
|
||||||
unsigned char index; /* VLAN table index */
|
unsigned char index; /* VLAN table index */
|
||||||
unsigned char enable; /* Enable or disable */
|
unsigned char enable; /* Enable or disable */
|
||||||
__be32 checksum; /* Checksum */
|
__be32 checksum; /* Checksum */
|
||||||
unsigned char pad[14];
|
unsigned char pad[18];
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Enable VLAN */
|
/* Enable VLAN */
|
||||||
|
|
|
@ -354,7 +354,8 @@ static int ncsi_rsp_handler_svf(struct ncsi_request *nr)
|
||||||
|
|
||||||
/* Add or remove the VLAN filter */
|
/* Add or remove the VLAN filter */
|
||||||
if (!(cmd->enable & 0x1)) {
|
if (!(cmd->enable & 0x1)) {
|
||||||
ret = ncsi_remove_filter(nc, NCSI_FILTER_VLAN, cmd->index);
|
/* HW indexes from 1 */
|
||||||
|
ret = ncsi_remove_filter(nc, NCSI_FILTER_VLAN, cmd->index - 1);
|
||||||
} else {
|
} else {
|
||||||
vlan = ntohs(cmd->vlan);
|
vlan = ntohs(cmd->vlan);
|
||||||
ret = ncsi_add_filter(nc, NCSI_FILTER_VLAN, &vlan);
|
ret = ncsi_add_filter(nc, NCSI_FILTER_VLAN, &vlan);
|
||||||
|
@ -693,7 +694,14 @@ static int ncsi_rsp_handler_gc(struct ncsi_request *nr)
|
||||||
|
|
||||||
ncf->index = i;
|
ncf->index = i;
|
||||||
ncf->total = cnt;
|
ncf->total = cnt;
|
||||||
ncf->bitmap = 0x0ul;
|
if (i == NCSI_FILTER_VLAN) {
|
||||||
|
/* Set VLAN filters active so they are cleared in
|
||||||
|
* first configuration state
|
||||||
|
*/
|
||||||
|
ncf->bitmap = U64_MAX;
|
||||||
|
} else {
|
||||||
|
ncf->bitmap = 0x0ul;
|
||||||
|
}
|
||||||
nc->filters[i] = ncf;
|
nc->filters[i] = ncf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue