return flits + nsgl;
}
-static inline void *write_eo_wr(struct adapter *adap,
- struct sge_eosw_txq *eosw_txq,
- struct sk_buff *skb, struct fw_eth_tx_eo_wr *wr,
- u32 hdr_len, u32 wrlen)
+static void *write_eo_wr(struct adapter *adap, struct sge_eosw_txq *eosw_txq,
+ struct sk_buff *skb, struct fw_eth_tx_eo_wr *wr,
+ u32 hdr_len, u32 wrlen)
{
const struct skb_shared_info *ssi = skb_shinfo(skb);
struct cpl_tx_pkt_core *cpl;
immd_len += hdr_len;
if (!eosw_txq->ncompl ||
- eosw_txq->last_compl >= adap->params.ofldq_wr_cred / 2) {
+ (eosw_txq->last_compl + wrlen16) >=
+ (adap->params.ofldq_wr_cred / 2)) {
compl = true;
eosw_txq->ncompl++;
eosw_txq->last_compl = 0;
return cpl;
}
-static void ethofld_hard_xmit(struct net_device *dev,
- struct sge_eosw_txq *eosw_txq)
+static int ethofld_hard_xmit(struct net_device *dev,
+ struct sge_eosw_txq *eosw_txq)
{
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
bool skip_eotx_wr = false;
struct tx_sw_desc *d;
struct sk_buff *skb;
+ int left, ret = 0;
u8 flits, ndesc;
- int left;
eohw_txq = &adap->sge.eohw_txq[eosw_txq->hwqid];
spin_lock(&eohw_txq->lock);
wrlen = flits * 8;
wrlen16 = DIV_ROUND_UP(wrlen, 16);
- /* If there are no CPL credits, then wait for credits
- * to come back and retry again
+ left = txq_avail(&eohw_txq->q) - ndesc;
+
+ /* If there are no descriptors left in hardware queues or no
+ * CPL credits left in software queues, then wait for them
+ * to come back and retry again. Note that we always request
+ * for credits update via interrupt for every half credits
+ * consumed. So, the interrupt will eventually restore the
+ * credits and invoke the Tx path again.
*/
- if (unlikely(wrlen16 > eosw_txq->cred))
+ if (unlikely(left < 0 || wrlen16 > eosw_txq->cred)) {
+ ret = -ENOMEM;
goto out_unlock;
+ }
if (unlikely(skip_eotx_wr)) {
start = (u64 *)wr;
sgl = (u64 *)inline_tx_skb_header(skb, &eohw_txq->q, (void *)start,
hdr_len);
if (data_len) {
- if (unlikely(cxgb4_map_skb(adap->pdev_dev, skb, d->addr))) {
+ ret = cxgb4_map_skb(adap->pdev_dev, skb, d->addr);
+ if (unlikely(ret)) {
memset(d->addr, 0, sizeof(d->addr));
eohw_txq->mapping_err++;
goto out_unlock;
out_unlock:
spin_unlock(&eohw_txq->lock);
+ return ret;
}
static void ethofld_xmit(struct net_device *dev, struct sge_eosw_txq *eosw_txq)
{
struct sk_buff *skb;
- int pktcount;
+ int pktcount, ret;
switch (eosw_txq->state) {
case CXGB4_EO_STATE_ACTIVE:
continue;
}
- ethofld_hard_xmit(dev, eosw_txq);
+ ret = ethofld_hard_xmit(dev, eosw_txq);
+ if (ret)
+ break;
}
}