Commit 8b705f52 authored by David S. Miller's avatar David S. Miller

Merge branch 'sunvnet-better-connection-management'

Shannon Nelson says:

====================
sunvnet: better connection management

These patches remove some problems in handling of carrier state
with the ldmvsw vswitch, remove  an xoff misuse in sunvnet, and
add stats for debug and tracking of point-to-point connections
between the ldom VMs.

v2:
 - added ldmvsw ndo_open to reset the LDC channel
 - updated copyrights
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 19ddde1e 9c5a3a1f
/* ldmvsw.c: Sun4v LDOM Virtual Switch Driver. /* ldmvsw.c: Sun4v LDOM Virtual Switch Driver.
* *
* Copyright (C) 2016 Oracle. All rights reserved. * Copyright (C) 2016-2017 Oracle. All rights reserved.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
...@@ -41,8 +41,8 @@ ...@@ -41,8 +41,8 @@
static u8 vsw_port_hwaddr[ETH_ALEN] = {0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; static u8 vsw_port_hwaddr[ETH_ALEN] = {0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
#define DRV_MODULE_NAME "ldmvsw" #define DRV_MODULE_NAME "ldmvsw"
#define DRV_MODULE_VERSION "1.1" #define DRV_MODULE_VERSION "1.2"
#define DRV_MODULE_RELDATE "February 3, 2017" #define DRV_MODULE_RELDATE "March 4, 2017"
static char version[] = static char version[] =
DRV_MODULE_NAME " " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")"; DRV_MODULE_NAME " " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")";
...@@ -123,6 +123,20 @@ static void vsw_set_rx_mode(struct net_device *dev) ...@@ -123,6 +123,20 @@ static void vsw_set_rx_mode(struct net_device *dev)
return sunvnet_set_rx_mode_common(dev, port->vp); return sunvnet_set_rx_mode_common(dev, port->vp);
} }
int ldmvsw_open(struct net_device *dev)
{
struct vnet_port *port = netdev_priv(dev);
struct vio_driver_state *vio = &port->vio;
/* reset the channel */
vio_link_state_change(vio, LDC_EVENT_RESET);
vnet_port_reset(port);
vio_port_up(vio);
return 0;
}
EXPORT_SYMBOL_GPL(ldmvsw_open);
#ifdef CONFIG_NET_POLL_CONTROLLER #ifdef CONFIG_NET_POLL_CONTROLLER
static void vsw_poll_controller(struct net_device *dev) static void vsw_poll_controller(struct net_device *dev)
{ {
...@@ -133,7 +147,7 @@ static void vsw_poll_controller(struct net_device *dev) ...@@ -133,7 +147,7 @@ static void vsw_poll_controller(struct net_device *dev)
#endif #endif
static const struct net_device_ops vsw_ops = { static const struct net_device_ops vsw_ops = {
.ndo_open = sunvnet_open_common, .ndo_open = ldmvsw_open,
.ndo_stop = sunvnet_close_common, .ndo_stop = sunvnet_close_common,
.ndo_set_rx_mode = vsw_set_rx_mode, .ndo_set_rx_mode = vsw_set_rx_mode,
.ndo_set_mac_address = sunvnet_set_mac_addr_common, .ndo_set_mac_address = sunvnet_set_mac_addr_common,
...@@ -365,6 +379,11 @@ static int vsw_port_probe(struct vio_dev *vdev, const struct vio_device_id *id) ...@@ -365,6 +379,11 @@ static int vsw_port_probe(struct vio_dev *vdev, const struct vio_device_id *id)
napi_enable(&port->napi); napi_enable(&port->napi);
vio_port_up(&port->vio); vio_port_up(&port->vio);
/* assure no carrier until we receive an LDC_EVENT_UP,
* even if the vsw config script tries to force us up
*/
netif_carrier_off(dev);
netdev_info(dev, "LDOM vsw-port %pM\n", dev->dev_addr); netdev_info(dev, "LDOM vsw-port %pM\n", dev->dev_addr);
pr_info("%s: PORT ( remote-mac %pM%s )\n", dev->name, pr_info("%s: PORT ( remote-mac %pM%s )\n", dev->name,
......
/* sunvnet.c: Sun LDOM Virtual Network Driver. /* sunvnet.c: Sun LDOM Virtual Network Driver.
* *
* Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net> * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net>
* Copyright (C) 2016 Oracle. All rights reserved. * Copyright (C) 2016-2017 Oracle. All rights reserved.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
...@@ -77,11 +77,125 @@ static void vnet_set_msglevel(struct net_device *dev, u32 value) ...@@ -77,11 +77,125 @@ static void vnet_set_msglevel(struct net_device *dev, u32 value)
vp->msg_enable = value; vp->msg_enable = value;
} }
static const struct {
const char string[ETH_GSTRING_LEN];
} ethtool_stats_keys[] = {
{ "rx_packets" },
{ "tx_packets" },
{ "rx_bytes" },
{ "tx_bytes" },
{ "rx_errors" },
{ "tx_errors" },
{ "rx_dropped" },
{ "tx_dropped" },
{ "multicast" },
{ "rx_length_errors" },
{ "rx_frame_errors" },
{ "rx_missed_errors" },
{ "tx_carrier_errors" },
{ "nports" },
};
static int vnet_get_sset_count(struct net_device *dev, int sset)
{
struct vnet *vp = (struct vnet *)netdev_priv(dev);
switch (sset) {
case ETH_SS_STATS:
return ARRAY_SIZE(ethtool_stats_keys)
+ (NUM_VNET_PORT_STATS * vp->nports);
default:
return -EOPNOTSUPP;
}
}
static void vnet_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
{
struct vnet *vp = (struct vnet *)netdev_priv(dev);
struct vnet_port *port;
char *p = (char *)buf;
switch (stringset) {
case ETH_SS_STATS:
memcpy(buf, &ethtool_stats_keys, sizeof(ethtool_stats_keys));
p += sizeof(ethtool_stats_keys);
rcu_read_lock();
list_for_each_entry_rcu(port, &vp->port_list, list) {
snprintf(p, ETH_GSTRING_LEN, "p%u.%s-%pM",
port->q_index, port->switch_port ? "s" : "q",
port->raddr);
p += ETH_GSTRING_LEN;
snprintf(p, ETH_GSTRING_LEN, "p%u.rx_packets",
port->q_index);
p += ETH_GSTRING_LEN;
snprintf(p, ETH_GSTRING_LEN, "p%u.tx_packets",
port->q_index);
p += ETH_GSTRING_LEN;
snprintf(p, ETH_GSTRING_LEN, "p%u.rx_bytes",
port->q_index);
p += ETH_GSTRING_LEN;
snprintf(p, ETH_GSTRING_LEN, "p%u.tx_bytes",
port->q_index);
p += ETH_GSTRING_LEN;
snprintf(p, ETH_GSTRING_LEN, "p%u.event_up",
port->q_index);
p += ETH_GSTRING_LEN;
snprintf(p, ETH_GSTRING_LEN, "p%u.event_reset",
port->q_index);
p += ETH_GSTRING_LEN;
}
rcu_read_unlock();
break;
default:
WARN_ON(1);
break;
}
}
static void vnet_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *estats, u64 *data)
{
struct vnet *vp = (struct vnet *)netdev_priv(dev);
struct vnet_port *port;
int i = 0;
data[i++] = dev->stats.rx_packets;
data[i++] = dev->stats.tx_packets;
data[i++] = dev->stats.rx_bytes;
data[i++] = dev->stats.tx_bytes;
data[i++] = dev->stats.rx_errors;
data[i++] = dev->stats.tx_errors;
data[i++] = dev->stats.rx_dropped;
data[i++] = dev->stats.tx_dropped;
data[i++] = dev->stats.multicast;
data[i++] = dev->stats.rx_length_errors;
data[i++] = dev->stats.rx_frame_errors;
data[i++] = dev->stats.rx_missed_errors;
data[i++] = dev->stats.tx_carrier_errors;
data[i++] = vp->nports;
rcu_read_lock();
list_for_each_entry_rcu(port, &vp->port_list, list) {
data[i++] = port->q_index;
data[i++] = port->stats.rx_packets;
data[i++] = port->stats.tx_packets;
data[i++] = port->stats.rx_bytes;
data[i++] = port->stats.tx_bytes;
data[i++] = port->stats.event_up;
data[i++] = port->stats.event_reset;
}
rcu_read_unlock();
}
static const struct ethtool_ops vnet_ethtool_ops = { static const struct ethtool_ops vnet_ethtool_ops = {
.get_drvinfo = vnet_get_drvinfo, .get_drvinfo = vnet_get_drvinfo,
.get_msglevel = vnet_get_msglevel, .get_msglevel = vnet_get_msglevel,
.set_msglevel = vnet_set_msglevel, .set_msglevel = vnet_set_msglevel,
.get_link = ethtool_op_get_link, .get_link = ethtool_op_get_link,
.get_sset_count = vnet_get_sset_count,
.get_strings = vnet_get_strings,
.get_ethtool_stats = vnet_get_ethtool_stats,
}; };
static LIST_HEAD(vnet_list); static LIST_HEAD(vnet_list);
......
/* sunvnet.c: Sun LDOM Virtual Network Driver. /* sunvnet.c: Sun LDOM Virtual Network Driver.
* *
* Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net> * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net>
* Copyright (C) 2016 Oracle. All rights reserved. * Copyright (C) 2016-2017 Oracle. All rights reserved.
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -43,7 +43,6 @@ MODULE_LICENSE("GPL"); ...@@ -43,7 +43,6 @@ MODULE_LICENSE("GPL");
MODULE_VERSION("1.1"); MODULE_VERSION("1.1");
static int __vnet_tx_trigger(struct vnet_port *port, u32 start); static int __vnet_tx_trigger(struct vnet_port *port, u32 start);
static void vnet_port_reset(struct vnet_port *port);
static inline u32 vnet_tx_dring_avail(struct vio_dring_state *dr) static inline u32 vnet_tx_dring_avail(struct vio_dring_state *dr)
{ {
...@@ -410,8 +409,12 @@ static int vnet_rx_one(struct vnet_port *port, struct vio_net_desc *desc) ...@@ -410,8 +409,12 @@ static int vnet_rx_one(struct vnet_port *port, struct vio_net_desc *desc)
skb->ip_summed = port->switch_port ? CHECKSUM_NONE : CHECKSUM_PARTIAL; skb->ip_summed = port->switch_port ? CHECKSUM_NONE : CHECKSUM_PARTIAL;
if (unlikely(is_multicast_ether_addr(eth_hdr(skb)->h_dest)))
dev->stats.multicast++;
dev->stats.rx_packets++; dev->stats.rx_packets++;
dev->stats.rx_bytes += len; dev->stats.rx_bytes += len;
port->stats.rx_packets++;
port->stats.rx_bytes += len;
napi_gro_receive(&port->napi, skb); napi_gro_receive(&port->napi, skb);
return 0; return 0;
...@@ -747,6 +750,13 @@ static int vnet_event_napi(struct vnet_port *port, int budget) ...@@ -747,6 +750,13 @@ static int vnet_event_napi(struct vnet_port *port, int budget)
/* RESET takes precedent over any other event */ /* RESET takes precedent over any other event */
if (port->rx_event & LDC_EVENT_RESET) { if (port->rx_event & LDC_EVENT_RESET) {
/* a link went down */
if (port->vsw == 1) {
netif_tx_stop_all_queues(dev);
netif_carrier_off(dev);
}
vio_link_state_change(vio, LDC_EVENT_RESET); vio_link_state_change(vio, LDC_EVENT_RESET);
vnet_port_reset(port); vnet_port_reset(port);
vio_port_up(vio); vio_port_up(vio);
...@@ -762,12 +772,21 @@ static int vnet_event_napi(struct vnet_port *port, int budget) ...@@ -762,12 +772,21 @@ static int vnet_event_napi(struct vnet_port *port, int budget)
maybe_tx_wakeup(port); maybe_tx_wakeup(port);
port->rx_event = 0; port->rx_event = 0;
port->stats.event_reset++;
return 0; return 0;
} }
if (port->rx_event & LDC_EVENT_UP) { if (port->rx_event & LDC_EVENT_UP) {
/* a link came up */
if (port->vsw == 1) {
netif_carrier_on(port->dev);
netif_tx_start_all_queues(port->dev);
}
vio_link_state_change(vio, LDC_EVENT_UP); vio_link_state_change(vio, LDC_EVENT_UP);
port->rx_event = 0; port->rx_event = 0;
port->stats.event_up++;
return 0; return 0;
} }
...@@ -1417,6 +1436,8 @@ int sunvnet_start_xmit_common(struct sk_buff *skb, struct net_device *dev, ...@@ -1417,6 +1436,8 @@ int sunvnet_start_xmit_common(struct sk_buff *skb, struct net_device *dev,
dev->stats.tx_packets++; dev->stats.tx_packets++;
dev->stats.tx_bytes += port->tx_bufs[txi].skb->len; dev->stats.tx_bytes += port->tx_bufs[txi].skb->len;
port->stats.tx_packets++;
port->stats.tx_bytes += port->tx_bufs[txi].skb->len;
dr->prod = (dr->prod + 1) & (VNET_TX_RING_SIZE - 1); dr->prod = (dr->prod + 1) & (VNET_TX_RING_SIZE - 1);
if (unlikely(vnet_tx_dring_avail(dr) < 1)) { if (unlikely(vnet_tx_dring_avail(dr) < 1)) {
...@@ -1631,7 +1652,7 @@ void sunvnet_port_free_tx_bufs_common(struct vnet_port *port) ...@@ -1631,7 +1652,7 @@ void sunvnet_port_free_tx_bufs_common(struct vnet_port *port)
} }
EXPORT_SYMBOL_GPL(sunvnet_port_free_tx_bufs_common); EXPORT_SYMBOL_GPL(sunvnet_port_free_tx_bufs_common);
static void vnet_port_reset(struct vnet_port *port) void vnet_port_reset(struct vnet_port *port)
{ {
del_timer(&port->clean_timer); del_timer(&port->clean_timer);
sunvnet_port_free_tx_bufs_common(port); sunvnet_port_free_tx_bufs_common(port);
...@@ -1639,6 +1660,7 @@ static void vnet_port_reset(struct vnet_port *port) ...@@ -1639,6 +1660,7 @@ static void vnet_port_reset(struct vnet_port *port)
port->tso = (port->vsw == 0); /* no tso in vsw, misbehaves in bridge */ port->tso = (port->vsw == 0); /* no tso in vsw, misbehaves in bridge */
port->tsolen = 0; port->tsolen = 0;
} }
EXPORT_SYMBOL_GPL(vnet_port_reset);
static int vnet_port_alloc_tx_ring(struct vnet_port *port) static int vnet_port_alloc_tx_ring(struct vnet_port *port)
{ {
...@@ -1708,20 +1730,32 @@ EXPORT_SYMBOL_GPL(sunvnet_poll_controller_common); ...@@ -1708,20 +1730,32 @@ EXPORT_SYMBOL_GPL(sunvnet_poll_controller_common);
void sunvnet_port_add_txq_common(struct vnet_port *port) void sunvnet_port_add_txq_common(struct vnet_port *port)
{ {
struct vnet *vp = port->vp; struct vnet *vp = port->vp;
int n; int smallest = 0;
int i;
/* find the first least-used q
* When there are more ldoms than q's, we start to
* double up on ports per queue.
*/
for (i = 0; i < VNET_MAX_TXQS; i++) {
if (vp->q_used[i] == 0) {
smallest = i;
break;
}
if (vp->q_used[i] < vp->q_used[smallest])
smallest = i;
}
n = vp->nports++; vp->nports++;
n = n & (VNET_MAX_TXQS - 1); vp->q_used[smallest]++;
port->q_index = n; port->q_index = smallest;
netif_tx_wake_queue(netdev_get_tx_queue(VNET_PORT_TO_NET_DEVICE(port),
port->q_index));
} }
EXPORT_SYMBOL_GPL(sunvnet_port_add_txq_common); EXPORT_SYMBOL_GPL(sunvnet_port_add_txq_common);
void sunvnet_port_rm_txq_common(struct vnet_port *port) void sunvnet_port_rm_txq_common(struct vnet_port *port)
{ {
port->vp->nports--; port->vp->nports--;
netif_tx_stop_queue(netdev_get_tx_queue(VNET_PORT_TO_NET_DEVICE(port), port->vp->q_used[port->q_index]--;
port->q_index)); port->q_index = 0;
} }
EXPORT_SYMBOL_GPL(sunvnet_port_rm_txq_common); EXPORT_SYMBOL_GPL(sunvnet_port_rm_txq_common);
...@@ -35,6 +35,19 @@ struct vnet_tx_entry { ...@@ -35,6 +35,19 @@ struct vnet_tx_entry {
struct vnet; struct vnet;
struct vnet_port_stats {
/* keep them all the same size */
u32 rx_bytes;
u32 tx_bytes;
u32 rx_packets;
u32 tx_packets;
u32 event_up;
u32 event_reset;
u32 q_placeholder;
};
#define NUM_VNET_PORT_STATS (sizeof(struct vnet_port_stats) / sizeof(u32))
/* Structure to describe a vnet-port or vsw-port in the MD. /* Structure to describe a vnet-port or vsw-port in the MD.
* If the vsw bit is set, this structure represents a vswitch * If the vsw bit is set, this structure represents a vswitch
* port, and the net_device can be found from ->dev. If the * port, and the net_device can be found from ->dev. If the
...@@ -44,6 +57,8 @@ struct vnet; ...@@ -44,6 +57,8 @@ struct vnet;
struct vnet_port { struct vnet_port {
struct vio_driver_state vio; struct vio_driver_state vio;
struct vnet_port_stats stats;
struct hlist_node hash; struct hlist_node hash;
u8 raddr[ETH_ALEN]; u8 raddr[ETH_ALEN];
unsigned switch_port:1; unsigned switch_port:1;
...@@ -97,22 +112,15 @@ struct vnet_mcast_entry { ...@@ -97,22 +112,15 @@ struct vnet_mcast_entry {
}; };
struct vnet { struct vnet {
/* Protects port_list and port_hash. */ spinlock_t lock; /* Protects port_list and port_hash. */
spinlock_t lock;
struct net_device *dev; struct net_device *dev;
u32 msg_enable; u32 msg_enable;
u8 q_used[VNET_MAX_TXQS];
struct list_head port_list; struct list_head port_list;
struct hlist_head port_hash[VNET_PORT_HASH_SIZE]; struct hlist_head port_hash[VNET_PORT_HASH_SIZE];
struct vnet_mcast_entry *mcast_list; struct vnet_mcast_entry *mcast_list;
struct list_head list; struct list_head list;
u64 local_mac; u64 local_mac;
int nports; int nports;
}; };
...@@ -139,6 +147,7 @@ int sunvnet_handle_attr_common(struct vio_driver_state *vio, void *arg); ...@@ -139,6 +147,7 @@ int sunvnet_handle_attr_common(struct vio_driver_state *vio, void *arg);
void sunvnet_handshake_complete_common(struct vio_driver_state *vio); void sunvnet_handshake_complete_common(struct vio_driver_state *vio);
int sunvnet_poll_common(struct napi_struct *napi, int budget); int sunvnet_poll_common(struct napi_struct *napi, int budget);
void sunvnet_port_free_tx_bufs_common(struct vnet_port *port); void sunvnet_port_free_tx_bufs_common(struct vnet_port *port);
void vnet_port_reset(struct vnet_port *port);
bool sunvnet_port_is_up_common(struct vnet_port *vnet); bool sunvnet_port_is_up_common(struct vnet_port *vnet);
void sunvnet_port_add_txq_common(struct vnet_port *port); void sunvnet_port_add_txq_common(struct vnet_port *port);
void sunvnet_port_rm_txq_common(struct vnet_port *port); void sunvnet_port_rm_txq_common(struct vnet_port *port);
......
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