Skip to content

Commit

Permalink
alx: add msi-x support
Browse files Browse the repository at this point in the history
Add msi-x support to the alx driver. This is in preparation for multi queue
support.

msi-x interrupts are disabled by default because without multi queue support
there is no advantage over msi interrupts. The performance numbers observed
with iperf stay the same.

Based on information of the downstream driver at github.com/qca/alx

Signed-off-by: Tobias Regnery <tobias.regnery@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Tobias Regnery authored and David S. Miller committed Sep 10, 2016
1 parent a0373ae commit dc39a78
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 8 deletions.
5 changes: 5 additions & 0 deletions drivers/net/ethernet/atheros/alx/alx.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ struct alx_priv {

struct alx_hw hw;

/* msi-x vectors */
int num_vec;
struct msix_entry *msix_entries;
char irq_lbl[IFNAMSIZ + 8];

/* all descriptor memory */
struct {
dma_addr_t dma;
Expand Down
14 changes: 14 additions & 0 deletions drivers/net/ethernet/atheros/alx/hw.c
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,20 @@ void alx_configure_basic(struct alx_hw *hw)
alx_write_mem32(hw, ALX_WRR, val);
}

void alx_mask_msix(struct alx_hw *hw, int index, bool mask)
{
u32 reg, val;

reg = ALX_MSIX_ENTRY_BASE + index * PCI_MSIX_ENTRY_SIZE +
PCI_MSIX_ENTRY_VECTOR_CTRL;

val = mask ? PCI_MSIX_ENTRY_CTRL_MASKBIT : 0;

alx_write_mem32(hw, reg, val);
alx_post_write(hw);
}


bool alx_get_phy_info(struct alx_hw *hw)
{
u16 devs1, devs2;
Expand Down
1 change: 1 addition & 0 deletions drivers/net/ethernet/atheros/alx/hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,7 @@ int alx_reset_mac(struct alx_hw *hw);
void alx_set_macaddr(struct alx_hw *hw, const u8 *addr);
bool alx_phy_configured(struct alx_hw *hw);
void alx_configure_basic(struct alx_hw *hw);
void alx_mask_msix(struct alx_hw *hw, int index, bool mask);
void alx_disable_rss(struct alx_hw *hw);
bool alx_get_phy_info(struct alx_hw *hw);
void alx_update_hw_stats(struct alx_hw *hw);
Expand Down
172 changes: 164 additions & 8 deletions drivers/net/ethernet/atheros/alx/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -292,10 +292,14 @@ static int alx_poll(struct napi_struct *napi, int budget)
napi_complete(&alx->napi);

/* enable interrupt */
spin_lock_irqsave(&alx->irq_lock, flags);
alx->int_mask |= ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0;
alx_write_mem32(hw, ALX_IMR, alx->int_mask);
spin_unlock_irqrestore(&alx->irq_lock, flags);
if (alx->flags & ALX_FLAG_USING_MSIX) {
alx_mask_msix(hw, 1, false);
} else {
spin_lock_irqsave(&alx->irq_lock, flags);
alx->int_mask |= ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0;
alx_write_mem32(hw, ALX_IMR, alx->int_mask);
spin_unlock_irqrestore(&alx->irq_lock, flags);
}

alx_post_write(hw);

Expand Down Expand Up @@ -356,6 +360,46 @@ static irqreturn_t alx_intr_handle(struct alx_priv *alx, u32 intr)
return IRQ_HANDLED;
}

static irqreturn_t alx_intr_msix_ring(int irq, void *data)
{
struct alx_priv *alx = data;
struct alx_hw *hw = &alx->hw;

/* mask interrupt to ACK chip */
alx_mask_msix(hw, 1, true);
/* clear interrupt status */
alx_write_mem32(hw, ALX_ISR, (ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0));

napi_schedule(&alx->napi);

return IRQ_HANDLED;
}

static irqreturn_t alx_intr_msix_misc(int irq, void *data)
{
struct alx_priv *alx = data;
struct alx_hw *hw = &alx->hw;
u32 intr;

/* mask interrupt to ACK chip */
alx_mask_msix(hw, 0, true);

/* read interrupt status */
intr = alx_read_mem32(hw, ALX_ISR);
intr &= (alx->int_mask & ~ALX_ISR_ALL_QUEUES);

if (alx_intr_handle_misc(alx, intr))
return IRQ_HANDLED;

/* clear interrupt status */
alx_write_mem32(hw, ALX_ISR, intr);

/* enable interrupt again */
alx_mask_msix(hw, 0, false);

return IRQ_HANDLED;
}

static irqreturn_t alx_intr_msi(int irq, void *data)
{
struct alx_priv *alx = data;
Expand Down Expand Up @@ -620,22 +664,97 @@ static void alx_free_rings(struct alx_priv *alx)
static void alx_config_vector_mapping(struct alx_priv *alx)
{
struct alx_hw *hw = &alx->hw;
u32 tbl = 0;

if (alx->flags & ALX_FLAG_USING_MSIX) {
tbl |= 1 << ALX_MSI_MAP_TBL1_TXQ0_SHIFT;
tbl |= 1 << ALX_MSI_MAP_TBL1_RXQ0_SHIFT;
}

alx_write_mem32(hw, ALX_MSI_MAP_TBL1, 0);
alx_write_mem32(hw, ALX_MSI_MAP_TBL1, tbl);
alx_write_mem32(hw, ALX_MSI_MAP_TBL2, 0);
alx_write_mem32(hw, ALX_MSI_ID_MAP, 0);
}

static bool alx_enable_msix(struct alx_priv *alx)
{
int i, err, num_vec = 2;

alx->msix_entries = kcalloc(num_vec, sizeof(struct msix_entry),
GFP_KERNEL);
if (!alx->msix_entries) {
netdev_warn(alx->dev, "Allocation of msix entries failed!\n");
return false;
}

for (i = 0; i < num_vec; i++)
alx->msix_entries[i].entry = i;

err = pci_enable_msix(alx->hw.pdev, alx->msix_entries, num_vec);
if (err) {
kfree(alx->msix_entries);
netdev_warn(alx->dev, "Enabling MSI-X interrupts failed!\n");
return false;
}

alx->num_vec = num_vec;
return true;
}

static int alx_request_msix(struct alx_priv *alx)
{
struct net_device *netdev = alx->dev;
int i, err, vector = 0, free_vector = 0;

err = request_irq(alx->msix_entries[0].vector, alx_intr_msix_misc,
0, netdev->name, alx);
if (err)
goto out_err;

vector++;
sprintf(alx->irq_lbl, "%s-TxRx-0", netdev->name);

err = request_irq(alx->msix_entries[vector].vector,
alx_intr_msix_ring, 0, alx->irq_lbl, alx);
if (err)
goto out_free;

return 0;

out_free:
free_irq(alx->msix_entries[free_vector++].vector, alx);

vector--;
for (i = 0; i < vector; i++)
free_irq(alx->msix_entries[free_vector++].vector, alx);

out_err:
return err;
}

static void alx_init_intr(struct alx_priv *alx, bool msix)
{
if (msix) {
if (alx_enable_msix(alx))
alx->flags |= ALX_FLAG_USING_MSIX;
}

if (!(alx->flags & ALX_FLAG_USING_MSIX)) {
alx->num_vec = 1;

if (!pci_enable_msi(alx->hw.pdev))
alx->flags |= ALX_FLAG_USING_MSI;
}
}

static void alx_disable_advanced_intr(struct alx_priv *alx)
{
if (alx->flags & ALX_FLAG_USING_MSIX) {
kfree(alx->msix_entries);
pci_disable_msix(alx->hw.pdev);
alx->flags &= ~ALX_FLAG_USING_MSIX;
}

if (alx->flags & ALX_FLAG_USING_MSI) {
pci_disable_msi(alx->hw.pdev);
alx->flags &= ~ALX_FLAG_USING_MSI;
Expand All @@ -645,22 +764,36 @@ static void alx_disable_advanced_intr(struct alx_priv *alx)
static void alx_irq_enable(struct alx_priv *alx)
{
struct alx_hw *hw = &alx->hw;
int i;

/* level-1 interrupt switch */
alx_write_mem32(hw, ALX_ISR, 0);
alx_write_mem32(hw, ALX_IMR, alx->int_mask);
alx_post_write(hw);

if (alx->flags & ALX_FLAG_USING_MSIX)
/* enable all msix irqs */
for (i = 0; i < alx->num_vec; i++)
alx_mask_msix(hw, i, false);
}

static void alx_irq_disable(struct alx_priv *alx)
{
struct alx_hw *hw = &alx->hw;
int i;

alx_write_mem32(hw, ALX_ISR, ALX_ISR_DIS);
alx_write_mem32(hw, ALX_IMR, 0);
alx_post_write(hw);

synchronize_irq(alx->hw.pdev->irq);
if (alx->flags & ALX_FLAG_USING_MSIX) {
for (i = 0; i < alx->num_vec; i++) {
alx_mask_msix(hw, i, true);
synchronize_irq(alx->msix_entries[i].vector);
}
} else {
synchronize_irq(alx->hw.pdev->irq);
}
}

static int alx_request_irq(struct alx_priv *alx)
Expand All @@ -672,6 +805,17 @@ static int alx_request_irq(struct alx_priv *alx)

msi_ctrl = (hw->imt >> 1) << ALX_MSI_RETRANS_TM_SHIFT;

if (alx->flags & ALX_FLAG_USING_MSIX) {
alx_write_mem32(hw, ALX_MSI_RETRANS_TIMER, msi_ctrl);
err = alx_request_msix(alx);
if (!err)
goto out;

/* msix request failed, realloc resources */
alx_disable_advanced_intr(alx);
alx_init_intr(alx, false);
}

if (alx->flags & ALX_FLAG_USING_MSI) {
alx_write_mem32(hw, ALX_MSI_RETRANS_TIMER,
msi_ctrl | ALX_MSI_MASK_SEL_LINE);
Expand All @@ -690,14 +834,23 @@ static int alx_request_irq(struct alx_priv *alx)
out:
if (!err)
alx_config_vector_mapping(alx);
else
netdev_err(alx->dev, "IRQ registration failed!\n");
return err;
}

static void alx_free_irq(struct alx_priv *alx)
{
struct pci_dev *pdev = alx->hw.pdev;
int i;

free_irq(pdev->irq, alx);
if (alx->flags & ALX_FLAG_USING_MSIX) {
/* we have only 2 vectors without multi queue support */
for (i = 0; i < 2; i++)
free_irq(alx->msix_entries[i].vector, alx);
} else {
free_irq(pdev->irq, alx);
}

alx_disable_advanced_intr(alx);
}
Expand Down Expand Up @@ -1256,7 +1409,10 @@ static void alx_poll_controller(struct net_device *netdev)
{
struct alx_priv *alx = netdev_priv(netdev);

if (alx->flags & ALX_FLAG_USING_MSI)
if (alx->flags & ALX_FLAG_USING_MSIX) {
alx_intr_msix_misc(0, alx);
alx_intr_msix_ring(0, alx);
} else if (alx->flags & ALX_FLAG_USING_MSI)
alx_intr_msi(0, alx);
else
alx_intr_legacy(0, alx);
Expand Down

0 comments on commit dc39a78

Please sign in to comment.