Skip to content

Commit

Permalink
net: qca_spi: Fix race condition in spi transfers
Browse files Browse the repository at this point in the history
With performance optimization the spi transfer and messages of basic
register operations like qcaspi_read_register moved into the private
driver structure. But they weren't protected against mutual access
(e.g. between driver kthread and ethtool). So dumping the QCA7000
registers via ethtool during network traffic could make spi_sync
hang forever, because the completion in spi_message is overwritten.

So revert the optimization completely.

Fixes: 291ab06 ("net: qualcomm: new Ethernet over SPI driver for QCA700")
Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Stefan Wahren authored and David S. Miller committed Sep 5, 2018
1 parent 9d7f19d commit e65a9e4
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 98 deletions.
76 changes: 36 additions & 40 deletions drivers/net/ethernet/qualcomm/qca_7k.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,34 +45,33 @@ qcaspi_read_register(struct qcaspi *qca, u16 reg, u16 *result)
{
__be16 rx_data;
__be16 tx_data;
struct spi_transfer *transfer;
struct spi_message *msg;
struct spi_transfer transfer[2];
struct spi_message msg;
int ret;

memset(transfer, 0, sizeof(transfer));

spi_message_init(&msg);

tx_data = cpu_to_be16(QCA7K_SPI_READ | QCA7K_SPI_INTERNAL | reg);
*result = 0;

transfer[0].tx_buf = &tx_data;
transfer[0].len = QCASPI_CMD_LEN;
transfer[1].rx_buf = &rx_data;
transfer[1].len = QCASPI_CMD_LEN;

spi_message_add_tail(&transfer[0], &msg);

if (qca->legacy_mode) {
msg = &qca->spi_msg1;
transfer = &qca->spi_xfer1;
transfer->tx_buf = &tx_data;
transfer->rx_buf = NULL;
transfer->len = QCASPI_CMD_LEN;
spi_sync(qca->spi_dev, msg);
} else {
msg = &qca->spi_msg2;
transfer = &qca->spi_xfer2[0];
transfer->tx_buf = &tx_data;
transfer->rx_buf = NULL;
transfer->len = QCASPI_CMD_LEN;
transfer = &qca->spi_xfer2[1];
spi_sync(qca->spi_dev, &msg);
spi_message_init(&msg);
}
transfer->tx_buf = NULL;
transfer->rx_buf = &rx_data;
transfer->len = QCASPI_CMD_LEN;
ret = spi_sync(qca->spi_dev, msg);
spi_message_add_tail(&transfer[1], &msg);
ret = spi_sync(qca->spi_dev, &msg);

if (!ret)
ret = msg->status;
ret = msg.status;

if (ret)
qcaspi_spi_error(qca);
Expand All @@ -86,35 +85,32 @@ int
qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value)
{
__be16 tx_data[2];
struct spi_transfer *transfer;
struct spi_message *msg;
struct spi_transfer transfer[2];
struct spi_message msg;
int ret;

memset(&transfer, 0, sizeof(transfer));

spi_message_init(&msg);

tx_data[0] = cpu_to_be16(QCA7K_SPI_WRITE | QCA7K_SPI_INTERNAL | reg);
tx_data[1] = cpu_to_be16(value);

transfer[0].tx_buf = &tx_data[0];
transfer[0].len = QCASPI_CMD_LEN;
transfer[1].tx_buf = &tx_data[1];
transfer[1].len = QCASPI_CMD_LEN;

spi_message_add_tail(&transfer[0], &msg);
if (qca->legacy_mode) {
msg = &qca->spi_msg1;
transfer = &qca->spi_xfer1;
transfer->tx_buf = &tx_data[0];
transfer->rx_buf = NULL;
transfer->len = QCASPI_CMD_LEN;
spi_sync(qca->spi_dev, msg);
} else {
msg = &qca->spi_msg2;
transfer = &qca->spi_xfer2[0];
transfer->tx_buf = &tx_data[0];
transfer->rx_buf = NULL;
transfer->len = QCASPI_CMD_LEN;
transfer = &qca->spi_xfer2[1];
spi_sync(qca->spi_dev, &msg);
spi_message_init(&msg);
}
transfer->tx_buf = &tx_data[1];
transfer->rx_buf = NULL;
transfer->len = QCASPI_CMD_LEN;
ret = spi_sync(qca->spi_dev, msg);
spi_message_add_tail(&transfer[1], &msg);
ret = spi_sync(qca->spi_dev, &msg);

if (!ret)
ret = msg->status;
ret = msg.status;

if (ret)
qcaspi_spi_error(qca);
Expand Down
110 changes: 57 additions & 53 deletions drivers/net/ethernet/qualcomm/qca_spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,22 +99,24 @@ static u32
qcaspi_write_burst(struct qcaspi *qca, u8 *src, u32 len)
{
__be16 cmd;
struct spi_message *msg = &qca->spi_msg2;
struct spi_transfer *transfer = &qca->spi_xfer2[0];
struct spi_message msg;
struct spi_transfer transfer[2];
int ret;

memset(&transfer, 0, sizeof(transfer));
spi_message_init(&msg);

cmd = cpu_to_be16(QCA7K_SPI_WRITE | QCA7K_SPI_EXTERNAL);
transfer->tx_buf = &cmd;
transfer->rx_buf = NULL;
transfer->len = QCASPI_CMD_LEN;
transfer = &qca->spi_xfer2[1];
transfer->tx_buf = src;
transfer->rx_buf = NULL;
transfer->len = len;
transfer[0].tx_buf = &cmd;
transfer[0].len = QCASPI_CMD_LEN;
transfer[1].tx_buf = src;
transfer[1].len = len;

ret = spi_sync(qca->spi_dev, msg);
spi_message_add_tail(&transfer[0], &msg);
spi_message_add_tail(&transfer[1], &msg);
ret = spi_sync(qca->spi_dev, &msg);

if (ret || (msg->actual_length != QCASPI_CMD_LEN + len)) {
if (ret || (msg.actual_length != QCASPI_CMD_LEN + len)) {
qcaspi_spi_error(qca);
return 0;
}
Expand All @@ -125,17 +127,20 @@ qcaspi_write_burst(struct qcaspi *qca, u8 *src, u32 len)
static u32
qcaspi_write_legacy(struct qcaspi *qca, u8 *src, u32 len)
{
struct spi_message *msg = &qca->spi_msg1;
struct spi_transfer *transfer = &qca->spi_xfer1;
struct spi_message msg;
struct spi_transfer transfer;
int ret;

transfer->tx_buf = src;
transfer->rx_buf = NULL;
transfer->len = len;
memset(&transfer, 0, sizeof(transfer));
spi_message_init(&msg);

transfer.tx_buf = src;
transfer.len = len;

ret = spi_sync(qca->spi_dev, msg);
spi_message_add_tail(&transfer, &msg);
ret = spi_sync(qca->spi_dev, &msg);

if (ret || (msg->actual_length != len)) {
if (ret || (msg.actual_length != len)) {
qcaspi_spi_error(qca);
return 0;
}
Expand All @@ -146,23 +151,25 @@ qcaspi_write_legacy(struct qcaspi *qca, u8 *src, u32 len)
static u32
qcaspi_read_burst(struct qcaspi *qca, u8 *dst, u32 len)
{
struct spi_message *msg = &qca->spi_msg2;
struct spi_message msg;
__be16 cmd;
struct spi_transfer *transfer = &qca->spi_xfer2[0];
struct spi_transfer transfer[2];
int ret;

memset(&transfer, 0, sizeof(transfer));
spi_message_init(&msg);

cmd = cpu_to_be16(QCA7K_SPI_READ | QCA7K_SPI_EXTERNAL);
transfer->tx_buf = &cmd;
transfer->rx_buf = NULL;
transfer->len = QCASPI_CMD_LEN;
transfer = &qca->spi_xfer2[1];
transfer->tx_buf = NULL;
transfer->rx_buf = dst;
transfer->len = len;
transfer[0].tx_buf = &cmd;
transfer[0].len = QCASPI_CMD_LEN;
transfer[1].rx_buf = dst;
transfer[1].len = len;

ret = spi_sync(qca->spi_dev, msg);
spi_message_add_tail(&transfer[0], &msg);
spi_message_add_tail(&transfer[1], &msg);
ret = spi_sync(qca->spi_dev, &msg);

if (ret || (msg->actual_length != QCASPI_CMD_LEN + len)) {
if (ret || (msg.actual_length != QCASPI_CMD_LEN + len)) {
qcaspi_spi_error(qca);
return 0;
}
Expand All @@ -173,17 +180,20 @@ qcaspi_read_burst(struct qcaspi *qca, u8 *dst, u32 len)
static u32
qcaspi_read_legacy(struct qcaspi *qca, u8 *dst, u32 len)
{
struct spi_message *msg = &qca->spi_msg1;
struct spi_transfer *transfer = &qca->spi_xfer1;
struct spi_message msg;
struct spi_transfer transfer;
int ret;

transfer->tx_buf = NULL;
transfer->rx_buf = dst;
transfer->len = len;
memset(&transfer, 0, sizeof(transfer));
spi_message_init(&msg);

ret = spi_sync(qca->spi_dev, msg);
transfer.rx_buf = dst;
transfer.len = len;

if (ret || (msg->actual_length != len)) {
spi_message_add_tail(&transfer, &msg);
ret = spi_sync(qca->spi_dev, &msg);

if (ret || (msg.actual_length != len)) {
qcaspi_spi_error(qca);
return 0;
}
Expand All @@ -195,19 +205,23 @@ static int
qcaspi_tx_cmd(struct qcaspi *qca, u16 cmd)
{
__be16 tx_data;
struct spi_message *msg = &qca->spi_msg1;
struct spi_transfer *transfer = &qca->spi_xfer1;
struct spi_message msg;
struct spi_transfer transfer;
int ret;

memset(&transfer, 0, sizeof(transfer));

spi_message_init(&msg);

tx_data = cpu_to_be16(cmd);
transfer->len = sizeof(tx_data);
transfer->tx_buf = &tx_data;
transfer->rx_buf = NULL;
transfer.len = sizeof(cmd);
transfer.tx_buf = &tx_data;
spi_message_add_tail(&transfer, &msg);

ret = spi_sync(qca->spi_dev, msg);
ret = spi_sync(qca->spi_dev, &msg);

if (!ret)
ret = msg->status;
ret = msg.status;

if (ret)
qcaspi_spi_error(qca);
Expand Down Expand Up @@ -835,16 +849,6 @@ qcaspi_netdev_setup(struct net_device *dev)
qca = netdev_priv(dev);
memset(qca, 0, sizeof(struct qcaspi));

memset(&qca->spi_xfer1, 0, sizeof(struct spi_transfer));
memset(&qca->spi_xfer2, 0, sizeof(struct spi_transfer) * 2);

spi_message_init(&qca->spi_msg1);
spi_message_add_tail(&qca->spi_xfer1, &qca->spi_msg1);

spi_message_init(&qca->spi_msg2);
spi_message_add_tail(&qca->spi_xfer2[0], &qca->spi_msg2);
spi_message_add_tail(&qca->spi_xfer2[1], &qca->spi_msg2);

memset(&qca->txr, 0, sizeof(qca->txr));
qca->txr.count = TX_RING_MAX_LEN;
}
Expand Down
5 changes: 0 additions & 5 deletions drivers/net/ethernet/qualcomm/qca_spi.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,6 @@ struct qcaspi {
struct tx_ring txr;
struct qcaspi_stats stats;

struct spi_message spi_msg1;
struct spi_message spi_msg2;
struct spi_transfer spi_xfer1;
struct spi_transfer spi_xfer2[2];

u8 *rx_buffer;
u32 buffer_size;
u8 sync;
Expand Down

0 comments on commit e65a9e4

Please sign in to comment.