Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b3772891 authored by Mohammed Javid's avatar Mohammed Javid Committed by Gerrit - the friendly Code Review server
Browse files

ks8851: Change to handle crash and permanent stall in driver



While performing bidirectional traffic in a flow-control disabled
environment, the driver crashed due to memory boundations, this
fix handles crashes and partial frames in rx fifo buffer

Change-Id: I81b460967be1888b601ac8537d0b96f0bba2fe6e
Acked-by: default avatarRishav LNU <rna@qti.qualcomm.com>
Signed-off-by: default avatarMohammed Javid <mjavid@codeaurora.org>
parent 0e3466e4
Loading
Loading
Loading
Loading
+57 −15
Original line number Original line Diff line number Diff line
@@ -36,6 +36,7 @@
#define KSZ8851_TX_SPACE (6144 * 3)
#define KSZ8851_TX_SPACE (6144 * 3)
#define TX_DMA_BUFFER_SIZE (8192 * 3)
#define TX_DMA_BUFFER_SIZE (8192 * 3)
#define RX_DMA_BUFFER_SIZE (2048 * 2)
#define RX_DMA_BUFFER_SIZE (2048 * 2)
#define MAX_RXFIFO_SIZE (12 * 1024)
#define CIDER_READ_MAX_ITER 20
#define CIDER_READ_MAX_ITER 20
#define CIDER_READ_MAX_DELAY 20
#define CIDER_READ_MAX_DELAY 20


@@ -159,6 +160,12 @@ static int msg_enable;
/* turn register number and byte-enable mask into data for start of packet */
/* turn register number and byte-enable mask into data for start of packet */
#define MK_OP(_byteen, _reg) (BYTE_EN(_byteen) | (_reg)  << (8+2) | (_reg) >> 6)
#define MK_OP(_byteen, _reg) (BYTE_EN(_byteen) | (_reg)  << (8+2) | (_reg) >> 6)


#define KS8851_RX_WORKQUEUE_NAME "ks8851_rx_wq"
#define KS8851_TX_WORKQUEUE_NAME "ks8851_tx_wq"

static struct workqueue_struct *ks8851_rx_wq;
static struct workqueue_struct *ks8851_tx_wq;

/* SPI register read/write calls.
/* SPI register read/write calls.
 *
 *
 * All these calls issue SPI transactions to access the chip's registers. They
 * All these calls issue SPI transactions to access the chip's registers. They
@@ -500,6 +507,7 @@ static int ks8851_rdfifolen(struct ks8851_net *ks, u16 fc)
	int ret;
	int ret;
	u16 count;
	u16 count;
	u16 rxfifosize = 0;
	u16 rxfifosize = 0;
	u16 tmpfifosize = 0;
	struct spi_transfer *xfer;
	struct spi_transfer *xfer;
	struct spi_message *msg;
	struct spi_message *msg;
	u32 *txd;
	u32 *txd;
@@ -534,9 +542,14 @@ static int ks8851_rdfifolen(struct ks8851_net *ks, u16 fc)


	ret = spi_sync(ks->spidev, msg);
	ret = spi_sync(ks->spidev, msg);


	for (count = 0, rxfifosize = 0; count < fc; count++)
	for (count = 0, tmpfifosize = 0; count < fc; count++) {
		rxfifosize += ALIGN((htons((u16)rxd[count]) & 0xfff), 4)+4;
		if (tmpfifosize >= MAX_RXFIFO_SIZE)
	rxfifosize += 4;
			break;
		tmpfifosize += ALIGN((htons((u16)rxd[count]) & 0xfff), 4)+4;
	}
	tmpfifosize += 4;
	rxfifosize = (tmpfifosize >=
		MAX_RXFIFO_SIZE) ? MAX_RXFIFO_SIZE : tmpfifosize;
	if (ret < 0)
	if (ret < 0)
		netdev_err(ks->netdev, "read: spi_sync() failed\n");
		netdev_err(ks->netdev, "read: spi_sync() failed\n");
	spi_message_free(msg);
	spi_message_free(msg);
@@ -575,10 +588,11 @@ static void ks8851_rx_pkts3(struct ks8851_net *ks)
	unsigned rxfc = 0, rxfct = 0;
	unsigned rxfc = 0, rxfct = 0;
	int rxfifosize = 0;
	int rxfifosize = 0;
	unsigned rxlen = 0;
	unsigned rxlen = 0;
	unsigned totallen = 0;
	unsigned rxalign = 0;
	unsigned rxalign = 0;
	u8 *rxpkt = 0;
	u8 *rxpkt = 0;
	u8 *buf = 0;
	u8 *buf = NULL, *buf1 = NULL;
	u32 *buf32 = 0;
	u32 *buf32 = NULL;
	u16 rxlen32 = 0;
	u16 rxlen32 = 0;
	u16 index32 = 0;
	u16 index32 = 0;


@@ -593,8 +607,7 @@ static void ks8851_rx_pkts3(struct ks8851_net *ks)


	/* tabulate all frame sizes so we can do one read for all frames */
	/* tabulate all frame sizes so we can do one read for all frames */
	rxfifosize = ks8851_rdfifolen(ks, rxfc);
	rxfifosize = ks8851_rdfifolen(ks, rxfc);

	if (rxfifosize <= 0) {
	if (rxfifosize < 0) {
		pr_debug("ks8851:Memory not available");
		pr_debug("ks8851:Memory not available");
		return;
		return;
	}
	}
@@ -615,17 +628,29 @@ static void ks8851_rx_pkts3(struct ks8851_net *ks)


	/* read all frames from rx fifo */
	/* read all frames from rx fifo */
	ks8851_rdfifo(ks, buf, rxfifosize);
	ks8851_rdfifo(ks, buf, rxfifosize);

	ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr);

	/* parse frames */
	/* parse frames */
	for (index32 = 1; rxfc > 0; rxfc--) {
	for (index32 = 1; rxfc > 0; rxfc--) {
		/* Get packet data length and pointer to packet data */
		/* Get packet data length and pointer to packet data */
		rxlen = (htonl(buf32[index32]) >> 16) & 0xfff;
		rxlen = (htonl(buf32[index32]) >> 16) & 0xfff;
		totallen += rxlen;
		index32++;
		index32++;
		rxalign = ALIGN(rxlen-4, 4);
		rxalign = ALIGN(rxlen-4, 4);
		rxlen32 = ALIGN(rxlen, 4)/4;
		rxlen32 = ALIGN(rxlen, 4)/4;
		rxpkt = (u8 *)&buf32[index32];
		rxpkt = (u8 *)&buf32[index32];
		if (totallen > rxfifosize) {
			if (rxfifosize >= MAX_RXFIFO_SIZE)
				break;
			buf1 = kzalloc(rxlen, GFP_DMA);
			if (buf1 == NULL)
				return;
			memcpy(buf1, rxpkt, (rxlen - (totallen - rxfifosize)));
			ks8851_rdfifo(ks, buf1 + (rxlen -
				(totallen - rxfifosize)),
						totallen - rxfifosize);
			buf32 = (u32 *)buf1;
			index32 = 0;
			rxpkt = (u8 *)&buf32[index32];
		}
		/* swap bytes to make the correct order */
		/* swap bytes to make the correct order */
		for (; rxlen32 > 0; rxlen32--, index32++)
		for (; rxlen32 > 0; rxlen32--, index32++)
			buf32[index32] = htonl(buf32[index32]);
			buf32[index32] = htonl(buf32[index32]);
@@ -644,8 +669,12 @@ static void ks8851_rx_pkts3(struct ks8851_net *ks)
		/* record packet stats */
		/* record packet stats */
		ks->netdev->stats.rx_packets++;
		ks->netdev->stats.rx_packets++;
		ks->netdev->stats.rx_bytes += rxlen;
		ks->netdev->stats.rx_bytes += rxlen;
			if (totallen > rxfifosize) {
				kfree(buf1);
				break;
			}
			}

	}
	ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr);
	kfree(buf);
	kfree(buf);
}
}


@@ -666,7 +695,7 @@ static irqreturn_t ks8851_irq(int irq, void *_ks)
	struct ks8851_net *ks = _ks;
	struct ks8851_net *ks = _ks;
	unsigned status;
	unsigned status;
	unsigned handled = 0;
	unsigned handled = 0;

	int ret = 0;
	mutex_lock(&ks->lock);
	mutex_lock(&ks->lock);


	status = ks8851_32bitrdreg16(ks, KS_ISR);
	status = ks8851_32bitrdreg16(ks, KS_ISR);
@@ -719,7 +748,9 @@ static irqreturn_t ks8851_irq(int irq, void *_ks)
		 * packet read-out, however we're masking the interrupt
		 * packet read-out, however we're masking the interrupt
		 * from the device so do not bother masking just the RX
		 * from the device so do not bother masking just the RX
		 * from the device. */
		 * from the device. */
		schedule_work(&ks->rx_work);
		ret = queue_work(ks8851_rx_wq, &ks->rx_work);
		if (ret != 0)
			handled |= IRQ_RXI;
	}
	}


	/* if something stopped the rx process, probably due to wanting
	/* if something stopped the rx process, probably due to wanting
@@ -1050,8 +1081,7 @@ static netdev_tx_t ks8851_start_xmit(struct sk_buff *skb,
		skb_queue_tail(&ks->txq, skb);
		skb_queue_tail(&ks->txq, skb);
	}
	}
	spin_unlock(&ks->statelock);
	spin_unlock(&ks->statelock);
	schedule_work(&ks->tx_work);
	queue_work(ks8851_tx_wq, &ks->tx_work);

	return ret;
	return ret;
}
}


@@ -1589,6 +1619,18 @@ static int ks8851_probe(struct spi_device *spi)
	INIT_WORK(&ks->rx_work, ks8851_rx_work);
	INIT_WORK(&ks->rx_work, ks8851_rx_work);




	ks8851_rx_wq = create_singlethread_workqueue(
			KS8851_RX_WORKQUEUE_NAME);
	if (!ks8851_rx_wq) {
		pr_debug("workqueue creation failed\n");
		return -ENOMEM;
	}
	ks8851_tx_wq = create_singlethread_workqueue(
			KS8851_TX_WORKQUEUE_NAME);
	if (!ks8851_tx_wq) {
		pr_debug("tx work queue creation failed");
		return -ENOMEM;
	}
	/* initialise pre-made spi transfer messages */
	/* initialise pre-made spi transfer messages */


	spi_message_init(&ks->spi_msg1);
	spi_message_init(&ks->spi_msg1);