Skip to content

Commit

Permalink
amd-xgbe: Add ethtool show/set channels support
Browse files Browse the repository at this point in the history
Add ethtool support to show and set the device channel configuration.
Changing the channel configuration will result in a device restart.

Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Tom Lendacky authored and David S. Miller committed May 23, 2018
1 parent 2244753 commit 01b5277
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 0 deletions.
25 changes: 25 additions & 0 deletions drivers/net/ethernet/amd/xgbe/xgbe-drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -1329,6 +1329,17 @@ static int xgbe_alloc_memory(struct xgbe_prv_data *pdata)
struct net_device *netdev = pdata->netdev;
int ret;

if (pdata->new_tx_ring_count) {
pdata->tx_ring_count = pdata->new_tx_ring_count;
pdata->tx_q_count = pdata->tx_ring_count;
pdata->new_tx_ring_count = 0;
}

if (pdata->new_rx_ring_count) {
pdata->rx_ring_count = pdata->new_rx_ring_count;
pdata->new_rx_ring_count = 0;
}

/* Calculate the Rx buffer size before allocating rings */
pdata->rx_buf_size = xgbe_calc_rx_buf_size(netdev, netdev->mtu);

Expand Down Expand Up @@ -1482,6 +1493,20 @@ static void xgbe_stopdev(struct work_struct *work)
netdev_alert(pdata->netdev, "device stopped\n");
}

void xgbe_full_restart_dev(struct xgbe_prv_data *pdata)
{
/* If not running, "restart" will happen on open */
if (!netif_running(pdata->netdev))
return;

xgbe_stop(pdata);

xgbe_free_memory(pdata);
xgbe_alloc_memory(pdata);

xgbe_start(pdata);
}

void xgbe_restart_dev(struct xgbe_prv_data *pdata)
{
/* If not running, "restart" will happen on open */
Expand Down
134 changes: 134 additions & 0 deletions drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,138 @@ static int xgbe_set_ringparam(struct net_device *netdev,
return 0;
}

static void xgbe_get_channels(struct net_device *netdev,
struct ethtool_channels *channels)
{
struct xgbe_prv_data *pdata = netdev_priv(netdev);
unsigned int rx, tx, combined;

/* Calculate maximums allowed:
* - Take into account the number of available IRQs
* - Do not take into account the number of online CPUs so that
* the user can over-subscribe if desired
* - Tx is additionally limited by the number of hardware queues
*/
rx = min(pdata->hw_feat.rx_ch_cnt, pdata->rx_max_channel_count);
rx = min(rx, pdata->channel_irq_count);
tx = min(pdata->hw_feat.tx_ch_cnt, pdata->tx_max_channel_count);
tx = min(tx, pdata->channel_irq_count);
tx = min(tx, pdata->tx_max_q_count);

combined = min(rx, tx);

channels->max_combined = combined;
channels->max_rx = rx ? rx - 1 : 0;
channels->max_tx = tx ? tx - 1 : 0;

/* Get current settings based on device state */
rx = pdata->new_rx_ring_count ? : pdata->rx_ring_count;
tx = pdata->new_tx_ring_count ? : pdata->tx_ring_count;

combined = min(rx, tx);
rx -= combined;
tx -= combined;

channels->combined_count = combined;
channels->rx_count = rx;
channels->tx_count = tx;
}

static void xgbe_print_set_channels_input(struct net_device *netdev,
struct ethtool_channels *channels)
{
netdev_err(netdev, "channel inputs: combined=%u, rx-only=%u, tx-only=%u\n",
channels->combined_count, channels->rx_count,
channels->tx_count);
}

static int xgbe_set_channels(struct net_device *netdev,
struct ethtool_channels *channels)
{
struct xgbe_prv_data *pdata = netdev_priv(netdev);
unsigned int rx, rx_curr, tx, tx_curr, combined;

/* Calculate maximums allowed:
* - Take into account the number of available IRQs
* - Do not take into account the number of online CPUs so that
* the user can over-subscribe if desired
* - Tx is additionally limited by the number of hardware queues
*/
rx = min(pdata->hw_feat.rx_ch_cnt, pdata->rx_max_channel_count);
rx = min(rx, pdata->channel_irq_count);
tx = min(pdata->hw_feat.tx_ch_cnt, pdata->tx_max_channel_count);
tx = min(tx, pdata->tx_max_q_count);
tx = min(tx, pdata->channel_irq_count);

combined = min(rx, tx);

/* Should not be setting other count */
if (channels->other_count) {
netdev_err(netdev,
"other channel count must be zero\n");
return -EINVAL;
}

/* Require at least one Combined (Rx and Tx) channel */
if (!channels->combined_count) {
netdev_err(netdev,
"at least one combined Rx/Tx channel is required\n");
xgbe_print_set_channels_input(netdev, channels);
return -EINVAL;
}

/* Check combined channels */
if (channels->combined_count > combined) {
netdev_err(netdev,
"combined channel count cannot exceed %u\n",
combined);
xgbe_print_set_channels_input(netdev, channels);
return -EINVAL;
}

/* Can have some Rx-only or Tx-only channels, but not both */
if (channels->rx_count && channels->tx_count) {
netdev_err(netdev,
"cannot specify both Rx-only and Tx-only channels\n");
xgbe_print_set_channels_input(netdev, channels);
return -EINVAL;
}

/* Check that we don't exceed the maximum number of channels */
if ((channels->combined_count + channels->rx_count) > rx) {
netdev_err(netdev,
"total Rx channels (%u) requested exceeds maximum available (%u)\n",
channels->combined_count + channels->rx_count, rx);
xgbe_print_set_channels_input(netdev, channels);
return -EINVAL;
}

if ((channels->combined_count + channels->tx_count) > tx) {
netdev_err(netdev,
"total Tx channels (%u) requested exceeds maximum available (%u)\n",
channels->combined_count + channels->tx_count, tx);
xgbe_print_set_channels_input(netdev, channels);
return -EINVAL;
}

rx = channels->combined_count + channels->rx_count;
tx = channels->combined_count + channels->tx_count;

rx_curr = pdata->new_rx_ring_count ? : pdata->rx_ring_count;
tx_curr = pdata->new_tx_ring_count ? : pdata->tx_ring_count;

if ((rx == rx_curr) && (tx == tx_curr))
goto out;

pdata->new_rx_ring_count = rx;
pdata->new_tx_ring_count = tx;

xgbe_full_restart_dev(pdata);

out:
return 0;
}

static const struct ethtool_ops xgbe_ethtool_ops = {
.get_drvinfo = xgbe_get_drvinfo,
.get_msglevel = xgbe_get_msglevel,
Expand All @@ -729,6 +861,8 @@ static const struct ethtool_ops xgbe_ethtool_ops = {
.get_module_eeprom = xgbe_get_module_eeprom,
.get_ringparam = xgbe_get_ringparam,
.set_ringparam = xgbe_set_ringparam,
.get_channels = xgbe_get_channels,
.set_channels = xgbe_set_channels,
};

const struct ethtool_ops *xgbe_get_ethtool_ops(void)
Expand Down
4 changes: 4 additions & 0 deletions drivers/net/ethernet/amd/xgbe/xgbe.h
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,9 @@ struct xgbe_prv_data {
unsigned int rx_ring_count;
unsigned int rx_desc_count;

unsigned int new_tx_ring_count;
unsigned int new_rx_ring_count;

unsigned int tx_max_q_count;
unsigned int rx_max_q_count;
unsigned int tx_q_count;
Expand Down Expand Up @@ -1336,6 +1339,7 @@ int xgbe_powerdown(struct net_device *, unsigned int);
void xgbe_init_rx_coalesce(struct xgbe_prv_data *);
void xgbe_init_tx_coalesce(struct xgbe_prv_data *);
void xgbe_restart_dev(struct xgbe_prv_data *pdata);
void xgbe_full_restart_dev(struct xgbe_prv_data *pdata);

#ifdef CONFIG_DEBUG_FS
void xgbe_debugfs_init(struct xgbe_prv_data *);
Expand Down

0 comments on commit 01b5277

Please sign in to comment.