atl1c: add error checking for pci_map_single functions
it is reported that code hit DMA-API errors on 3.8-rc6+, (see https://bugzilla.redhat.com/show_bug.cgi?id=908436, and https://bugzilla.redhat.com/show_bug.cgi?id=908550) this patch just adds error handler for pci_map_single and skb_frag_dma_map. Signed-off-by: xiong <xiong@qca.qualcomm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
9c10f4115c
Коммит
ac574804d4
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
#include "atl1c.h"
|
#include "atl1c.h"
|
||||||
|
|
||||||
#define ATL1C_DRV_VERSION "1.0.1.0-NAPI"
|
#define ATL1C_DRV_VERSION "1.0.1.1-NAPI"
|
||||||
char atl1c_driver_name[] = "atl1c";
|
char atl1c_driver_name[] = "atl1c";
|
||||||
char atl1c_driver_version[] = ATL1C_DRV_VERSION;
|
char atl1c_driver_version[] = ATL1C_DRV_VERSION;
|
||||||
|
|
||||||
|
@ -1652,6 +1652,7 @@ static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter)
|
||||||
u16 num_alloc = 0;
|
u16 num_alloc = 0;
|
||||||
u16 rfd_next_to_use, next_next;
|
u16 rfd_next_to_use, next_next;
|
||||||
struct atl1c_rx_free_desc *rfd_desc;
|
struct atl1c_rx_free_desc *rfd_desc;
|
||||||
|
dma_addr_t mapping;
|
||||||
|
|
||||||
next_next = rfd_next_to_use = rfd_ring->next_to_use;
|
next_next = rfd_next_to_use = rfd_ring->next_to_use;
|
||||||
if (++next_next == rfd_ring->count)
|
if (++next_next == rfd_ring->count)
|
||||||
|
@ -1678,9 +1679,18 @@ static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter)
|
||||||
ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
|
ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
|
||||||
buffer_info->skb = skb;
|
buffer_info->skb = skb;
|
||||||
buffer_info->length = adapter->rx_buffer_len;
|
buffer_info->length = adapter->rx_buffer_len;
|
||||||
buffer_info->dma = pci_map_single(pdev, vir_addr,
|
mapping = pci_map_single(pdev, vir_addr,
|
||||||
buffer_info->length,
|
buffer_info->length,
|
||||||
PCI_DMA_FROMDEVICE);
|
PCI_DMA_FROMDEVICE);
|
||||||
|
if (unlikely(pci_dma_mapping_error(pdev, mapping))) {
|
||||||
|
dev_kfree_skb(skb);
|
||||||
|
buffer_info->skb = NULL;
|
||||||
|
buffer_info->length = 0;
|
||||||
|
ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_FREE);
|
||||||
|
netif_warn(adapter, rx_err, adapter->netdev, "RX pci_map_single failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buffer_info->dma = mapping;
|
||||||
ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE,
|
ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE,
|
||||||
ATL1C_PCIMAP_FROMDEVICE);
|
ATL1C_PCIMAP_FROMDEVICE);
|
||||||
rfd_desc->buffer_addr = cpu_to_le64(buffer_info->dma);
|
rfd_desc->buffer_addr = cpu_to_le64(buffer_info->dma);
|
||||||
|
@ -2015,7 +2025,29 @@ check_sum:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void atl1c_tx_map(struct atl1c_adapter *adapter,
|
static void atl1c_tx_rollback(struct atl1c_adapter *adpt,
|
||||||
|
struct atl1c_tpd_desc *first_tpd,
|
||||||
|
enum atl1c_trans_queue type)
|
||||||
|
{
|
||||||
|
struct atl1c_tpd_ring *tpd_ring = &adpt->tpd_ring[type];
|
||||||
|
struct atl1c_buffer *buffer_info;
|
||||||
|
struct atl1c_tpd_desc *tpd;
|
||||||
|
u16 first_index, index;
|
||||||
|
|
||||||
|
first_index = first_tpd - (struct atl1c_tpd_desc *)tpd_ring->desc;
|
||||||
|
index = first_index;
|
||||||
|
while (index != tpd_ring->next_to_use) {
|
||||||
|
tpd = ATL1C_TPD_DESC(tpd_ring, index);
|
||||||
|
buffer_info = &tpd_ring->buffer_info[index];
|
||||||
|
atl1c_clean_buffer(adpt->pdev, buffer_info, 0);
|
||||||
|
memset(tpd, 0, sizeof(struct atl1c_tpd_desc));
|
||||||
|
if (++index == tpd_ring->count)
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
tpd_ring->next_to_use = first_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int atl1c_tx_map(struct atl1c_adapter *adapter,
|
||||||
struct sk_buff *skb, struct atl1c_tpd_desc *tpd,
|
struct sk_buff *skb, struct atl1c_tpd_desc *tpd,
|
||||||
enum atl1c_trans_queue type)
|
enum atl1c_trans_queue type)
|
||||||
{
|
{
|
||||||
|
@ -2040,7 +2072,10 @@ static void atl1c_tx_map(struct atl1c_adapter *adapter,
|
||||||
buffer_info->length = map_len;
|
buffer_info->length = map_len;
|
||||||
buffer_info->dma = pci_map_single(adapter->pdev,
|
buffer_info->dma = pci_map_single(adapter->pdev,
|
||||||
skb->data, hdr_len, PCI_DMA_TODEVICE);
|
skb->data, hdr_len, PCI_DMA_TODEVICE);
|
||||||
ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
|
if (unlikely(pci_dma_mapping_error(adapter->pdev,
|
||||||
|
buffer_info->dma)))
|
||||||
|
goto err_dma;
|
||||||
|
|
||||||
ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE,
|
ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE,
|
||||||
ATL1C_PCIMAP_TODEVICE);
|
ATL1C_PCIMAP_TODEVICE);
|
||||||
mapped_len += map_len;
|
mapped_len += map_len;
|
||||||
|
@ -2062,6 +2097,10 @@ static void atl1c_tx_map(struct atl1c_adapter *adapter,
|
||||||
buffer_info->dma =
|
buffer_info->dma =
|
||||||
pci_map_single(adapter->pdev, skb->data + mapped_len,
|
pci_map_single(adapter->pdev, skb->data + mapped_len,
|
||||||
buffer_info->length, PCI_DMA_TODEVICE);
|
buffer_info->length, PCI_DMA_TODEVICE);
|
||||||
|
if (unlikely(pci_dma_mapping_error(adapter->pdev,
|
||||||
|
buffer_info->dma)))
|
||||||
|
goto err_dma;
|
||||||
|
|
||||||
ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
|
ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
|
||||||
ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE,
|
ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE,
|
||||||
ATL1C_PCIMAP_TODEVICE);
|
ATL1C_PCIMAP_TODEVICE);
|
||||||
|
@ -2083,6 +2122,9 @@ static void atl1c_tx_map(struct atl1c_adapter *adapter,
|
||||||
frag, 0,
|
frag, 0,
|
||||||
buffer_info->length,
|
buffer_info->length,
|
||||||
DMA_TO_DEVICE);
|
DMA_TO_DEVICE);
|
||||||
|
if (dma_mapping_error(&adapter->pdev->dev, buffer_info->dma))
|
||||||
|
goto err_dma;
|
||||||
|
|
||||||
ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
|
ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
|
||||||
ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_PAGE,
|
ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_PAGE,
|
||||||
ATL1C_PCIMAP_TODEVICE);
|
ATL1C_PCIMAP_TODEVICE);
|
||||||
|
@ -2095,6 +2137,13 @@ static void atl1c_tx_map(struct atl1c_adapter *adapter,
|
||||||
/* The last buffer info contain the skb address,
|
/* The last buffer info contain the skb address,
|
||||||
so it will be free after unmap */
|
so it will be free after unmap */
|
||||||
buffer_info->skb = skb;
|
buffer_info->skb = skb;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_dma:
|
||||||
|
buffer_info->dma = 0;
|
||||||
|
buffer_info->length = 0;
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void atl1c_tx_queue(struct atl1c_adapter *adapter, struct sk_buff *skb,
|
static void atl1c_tx_queue(struct atl1c_adapter *adapter, struct sk_buff *skb,
|
||||||
|
@ -2157,10 +2206,18 @@ static netdev_tx_t atl1c_xmit_frame(struct sk_buff *skb,
|
||||||
if (skb_network_offset(skb) != ETH_HLEN)
|
if (skb_network_offset(skb) != ETH_HLEN)
|
||||||
tpd->word1 |= 1 << TPD_ETH_TYPE_SHIFT; /* Ethernet frame */
|
tpd->word1 |= 1 << TPD_ETH_TYPE_SHIFT; /* Ethernet frame */
|
||||||
|
|
||||||
atl1c_tx_map(adapter, skb, tpd, type);
|
if (atl1c_tx_map(adapter, skb, tpd, type) < 0) {
|
||||||
atl1c_tx_queue(adapter, skb, tpd, type);
|
netif_info(adapter, tx_done, adapter->netdev,
|
||||||
|
"tx-skb droppted due to dma error\n");
|
||||||
|
/* roll back tpd/buffer */
|
||||||
|
atl1c_tx_rollback(adapter, tpd, type);
|
||||||
|
spin_unlock_irqrestore(&adapter->tx_lock, flags);
|
||||||
|
dev_kfree_skb(skb);
|
||||||
|
} else {
|
||||||
|
atl1c_tx_queue(adapter, skb, tpd, type);
|
||||||
|
spin_unlock_irqrestore(&adapter->tx_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&adapter->tx_lock, flags);
|
|
||||||
return NETDEV_TX_OK;
|
return NETDEV_TX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче