diff options
Diffstat (limited to 'drivers/net/ethernet/pensando/ionic/ionic_lif.c')
| -rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_lif.c | 536 | 
1 files changed, 472 insertions, 64 deletions
| diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c index 11140915c2da..af3a5368529c 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c @@ -120,23 +120,34 @@ static void ionic_link_status_check(struct ionic_lif *lif)  	if (!test_bit(IONIC_LIF_F_LINK_CHECK_REQUESTED, lif->state))  		return; +	/* Don't put carrier back up if we're in a broken state */ +	if (test_bit(IONIC_LIF_F_BROKEN, lif->state)) { +		clear_bit(IONIC_LIF_F_LINK_CHECK_REQUESTED, lif->state); +		return; +	} +  	link_status = le16_to_cpu(lif->info->status.link_status);  	link_up = link_status == IONIC_PORT_OPER_STATUS_UP;  	if (link_up) { -		if (lif->netdev->flags & IFF_UP && netif_running(lif->netdev)) { +		int err = 0; + +		if (netdev->flags & IFF_UP && netif_running(netdev)) {  			mutex_lock(&lif->queue_lock); -			ionic_start_queues(lif); +			err = ionic_start_queues(lif); +			if (err && err != -EBUSY) { +				netdev_err(lif->netdev, +					   "Failed to start queues: %d\n", err); +				set_bit(IONIC_LIF_F_BROKEN, lif->state); +				netif_carrier_off(lif->netdev); +			}  			mutex_unlock(&lif->queue_lock);  		} -		if (!netif_carrier_ok(netdev)) { -			u32 link_speed; - +		if (!err && !netif_carrier_ok(netdev)) {  			ionic_port_identify(lif->ionic); -			link_speed = le32_to_cpu(lif->info->status.link_speed);  			netdev_info(netdev, "Link up - %d Gbps\n", -				    link_speed / 1000); +				    le32_to_cpu(lif->info->status.link_speed) / 1000);  			netif_carrier_on(netdev);  		}  	} else { @@ -145,7 +156,7 @@ static void ionic_link_status_check(struct ionic_lif *lif)  			netif_carrier_off(netdev);  		} -		if (lif->netdev->flags & IFF_UP && netif_running(lif->netdev)) { +		if (netdev->flags & IFF_UP && netif_running(netdev)) {  			mutex_lock(&lif->queue_lock);  			ionic_stop_queues(lif);  			mutex_unlock(&lif->queue_lock); @@ -382,6 +393,8 @@ static void ionic_qcq_free(struct ionic_lif *lif, struct ionic_qcq *qcq)  static void ionic_qcqs_free(struct ionic_lif *lif)  {  	struct device *dev = lif->ionic->dev; +	struct ionic_qcq *adminqcq; +	unsigned long irqflags;  	if (lif->notifyqcq) {  		ionic_qcq_free(lif, lif->notifyqcq); @@ -390,9 +403,14 @@ static void ionic_qcqs_free(struct ionic_lif *lif)  	}  	if (lif->adminqcq) { -		ionic_qcq_free(lif, lif->adminqcq); -		devm_kfree(dev, lif->adminqcq); +		spin_lock_irqsave(&lif->adminq_lock, irqflags); +		adminqcq = READ_ONCE(lif->adminqcq);  		lif->adminqcq = NULL; +		spin_unlock_irqrestore(&lif->adminq_lock, irqflags); +		if (adminqcq) { +			ionic_qcq_free(lif, adminqcq); +			devm_kfree(dev, adminqcq); +		}  	}  	if (lif->rxqcqs) { @@ -495,6 +513,7 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,  		goto err_out;  	} +	new->q.dev = dev;  	new->flags = flags;  	new->q.info = devm_kcalloc(dev, num_descs, sizeof(*new->q.info), @@ -506,6 +525,7 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,  	}  	new->q.type = type; +	new->q.max_sg_elems = lif->qtype_info[type].max_sg_elems;  	err = ionic_q_init(lif, idev, &new->q, index, name, num_descs,  			   desc_size, sg_desc_size, pid); @@ -656,20 +676,20 @@ static int ionic_qcqs_alloc(struct ionic_lif *lif)  	err = -ENOMEM;  	lif->txqcqs = devm_kcalloc(dev, lif->ionic->ntxqs_per_lif, -				   sizeof(struct ionic_qcq *), GFP_KERNEL); +				   sizeof(*lif->txqcqs), GFP_KERNEL);  	if (!lif->txqcqs)  		goto err_out;  	lif->rxqcqs = devm_kcalloc(dev, lif->ionic->nrxqs_per_lif, -				   sizeof(struct ionic_qcq *), GFP_KERNEL); +				   sizeof(*lif->rxqcqs), GFP_KERNEL);  	if (!lif->rxqcqs)  		goto err_out; -	lif->txqstats = devm_kcalloc(dev, lif->ionic->ntxqs_per_lif, -				     sizeof(struct ionic_tx_stats), GFP_KERNEL); +	lif->txqstats = devm_kcalloc(dev, lif->ionic->ntxqs_per_lif + 1, +				     sizeof(*lif->txqstats), GFP_KERNEL);  	if (!lif->txqstats)  		goto err_out; -	lif->rxqstats = devm_kcalloc(dev, lif->ionic->nrxqs_per_lif, -				     sizeof(struct ionic_rx_stats), GFP_KERNEL); +	lif->rxqstats = devm_kcalloc(dev, lif->ionic->nrxqs_per_lif + 1, +				     sizeof(*lif->rxqstats), GFP_KERNEL);  	if (!lif->rxqstats)  		goto err_out; @@ -711,15 +731,14 @@ static int ionic_lif_txq_init(struct ionic_lif *lif, struct ionic_qcq *qcq)  			.ring_base = cpu_to_le64(q->base_pa),  			.cq_ring_base = cpu_to_le64(cq->base_pa),  			.sg_ring_base = cpu_to_le64(q->sg_base_pa), +			.features = cpu_to_le64(q->features),  		},  	};  	unsigned int intr_index;  	int err; -	if (qcq->flags & IONIC_QCQ_F_INTR) -		intr_index = qcq->intr.index; -	else -		intr_index = lif->rxqcqs[q->index]->intr.index; +	intr_index = qcq->intr.index; +  	ctx.cmd.q_init.intr_index = cpu_to_le16(intr_index);  	dev_dbg(dev, "txq_init.pid %d\n", ctx.cmd.q_init.pid); @@ -773,6 +792,7 @@ static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq)  			.ring_base = cpu_to_le64(q->base_pa),  			.cq_ring_base = cpu_to_le64(cq->base_pa),  			.sg_ring_base = cpu_to_le64(q->sg_base_pa), +			.features = cpu_to_le64(q->features),  		},  	};  	int err; @@ -810,6 +830,254 @@ static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq)  	return 0;  } +int ionic_lif_create_hwstamp_txq(struct ionic_lif *lif) +{ +	unsigned int num_desc, desc_sz, comp_sz, sg_desc_sz; +	unsigned int txq_i, flags; +	struct ionic_qcq *txq; +	u64 features; +	int err; + +	mutex_lock(&lif->queue_lock); + +	if (lif->hwstamp_txq) +		goto out; + +	features = IONIC_Q_F_2X_CQ_DESC | IONIC_TXQ_F_HWSTAMP; + +	num_desc = IONIC_MIN_TXRX_DESC; +	desc_sz = sizeof(struct ionic_txq_desc); +	comp_sz = 2 * sizeof(struct ionic_txq_comp); + +	if (lif->qtype_info[IONIC_QTYPE_TXQ].version >= 1 && +	    lif->qtype_info[IONIC_QTYPE_TXQ].sg_desc_sz == sizeof(struct ionic_txq_sg_desc_v1)) +		sg_desc_sz = sizeof(struct ionic_txq_sg_desc_v1); +	else +		sg_desc_sz = sizeof(struct ionic_txq_sg_desc); + +	txq_i = lif->ionic->ntxqs_per_lif; +	flags = IONIC_QCQ_F_TX_STATS | IONIC_QCQ_F_SG; + +	err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, txq_i, "hwstamp_tx", flags, +			      num_desc, desc_sz, comp_sz, sg_desc_sz, +			      lif->kern_pid, &txq); +	if (err) +		goto err_qcq_alloc; + +	txq->q.features = features; + +	ionic_link_qcq_interrupts(lif->adminqcq, txq); +	ionic_debugfs_add_qcq(lif, txq); + +	lif->hwstamp_txq = txq; + +	if (netif_running(lif->netdev)) { +		err = ionic_lif_txq_init(lif, txq); +		if (err) +			goto err_qcq_init; + +		if (test_bit(IONIC_LIF_F_UP, lif->state)) { +			err = ionic_qcq_enable(txq); +			if (err) +				goto err_qcq_enable; +		} +	} + +out: +	mutex_unlock(&lif->queue_lock); + +	return 0; + +err_qcq_enable: +	ionic_lif_qcq_deinit(lif, txq); +err_qcq_init: +	lif->hwstamp_txq = NULL; +	ionic_debugfs_del_qcq(txq); +	ionic_qcq_free(lif, txq); +	devm_kfree(lif->ionic->dev, txq); +err_qcq_alloc: +	mutex_unlock(&lif->queue_lock); +	return err; +} + +int ionic_lif_create_hwstamp_rxq(struct ionic_lif *lif) +{ +	unsigned int num_desc, desc_sz, comp_sz, sg_desc_sz; +	unsigned int rxq_i, flags; +	struct ionic_qcq *rxq; +	u64 features; +	int err; + +	mutex_lock(&lif->queue_lock); + +	if (lif->hwstamp_rxq) +		goto out; + +	features = IONIC_Q_F_2X_CQ_DESC | IONIC_RXQ_F_HWSTAMP; + +	num_desc = IONIC_MIN_TXRX_DESC; +	desc_sz = sizeof(struct ionic_rxq_desc); +	comp_sz = 2 * sizeof(struct ionic_rxq_comp); +	sg_desc_sz = sizeof(struct ionic_rxq_sg_desc); + +	rxq_i = lif->ionic->nrxqs_per_lif; +	flags = IONIC_QCQ_F_RX_STATS | IONIC_QCQ_F_SG; + +	err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, rxq_i, "hwstamp_rx", flags, +			      num_desc, desc_sz, comp_sz, sg_desc_sz, +			      lif->kern_pid, &rxq); +	if (err) +		goto err_qcq_alloc; + +	rxq->q.features = features; + +	ionic_link_qcq_interrupts(lif->adminqcq, rxq); +	ionic_debugfs_add_qcq(lif, rxq); + +	lif->hwstamp_rxq = rxq; + +	if (netif_running(lif->netdev)) { +		err = ionic_lif_rxq_init(lif, rxq); +		if (err) +			goto err_qcq_init; + +		if (test_bit(IONIC_LIF_F_UP, lif->state)) { +			ionic_rx_fill(&rxq->q); +			err = ionic_qcq_enable(rxq); +			if (err) +				goto err_qcq_enable; +		} +	} + +out: +	mutex_unlock(&lif->queue_lock); + +	return 0; + +err_qcq_enable: +	ionic_lif_qcq_deinit(lif, rxq); +err_qcq_init: +	lif->hwstamp_rxq = NULL; +	ionic_debugfs_del_qcq(rxq); +	ionic_qcq_free(lif, rxq); +	devm_kfree(lif->ionic->dev, rxq); +err_qcq_alloc: +	mutex_unlock(&lif->queue_lock); +	return err; +} + +int ionic_lif_config_hwstamp_rxq_all(struct ionic_lif *lif, bool rx_all) +{ +	struct ionic_queue_params qparam; + +	ionic_init_queue_params(lif, &qparam); + +	if (rx_all) +		qparam.rxq_features = IONIC_Q_F_2X_CQ_DESC | IONIC_RXQ_F_HWSTAMP; +	else +		qparam.rxq_features = 0; + +	/* if we're not running, just set the values and return */ +	if (!netif_running(lif->netdev)) { +		lif->rxq_features = qparam.rxq_features; +		return 0; +	} + +	return ionic_reconfigure_queues(lif, &qparam); +} + +int ionic_lif_set_hwstamp_txmode(struct ionic_lif *lif, u16 txstamp_mode) +{ +	struct ionic_admin_ctx ctx = { +		.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), +		.cmd.lif_setattr = { +			.opcode = IONIC_CMD_LIF_SETATTR, +			.index = cpu_to_le16(lif->index), +			.attr = IONIC_LIF_ATTR_TXSTAMP, +			.txstamp_mode = cpu_to_le16(txstamp_mode), +		}, +	}; + +	return ionic_adminq_post_wait(lif, &ctx); +} + +static void ionic_lif_del_hwstamp_rxfilt(struct ionic_lif *lif) +{ +	struct ionic_admin_ctx ctx = { +		.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), +		.cmd.rx_filter_del = { +			.opcode = IONIC_CMD_RX_FILTER_DEL, +			.lif_index = cpu_to_le16(lif->index), +		}, +	}; +	struct ionic_rx_filter *f; +	u32 filter_id; +	int err; + +	spin_lock_bh(&lif->rx_filters.lock); + +	f = ionic_rx_filter_rxsteer(lif); +	if (!f) { +		spin_unlock_bh(&lif->rx_filters.lock); +		return; +	} + +	filter_id = f->filter_id; +	ionic_rx_filter_free(lif, f); + +	spin_unlock_bh(&lif->rx_filters.lock); + +	netdev_dbg(lif->netdev, "rx_filter del RXSTEER (id %d)\n", filter_id); + +	ctx.cmd.rx_filter_del.filter_id = cpu_to_le32(filter_id); + +	err = ionic_adminq_post_wait(lif, &ctx); +	if (err && err != -EEXIST) +		netdev_dbg(lif->netdev, "failed to delete rx_filter RXSTEER (id %d)\n", filter_id); +} + +static int ionic_lif_add_hwstamp_rxfilt(struct ionic_lif *lif, u64 pkt_class) +{ +	struct ionic_admin_ctx ctx = { +		.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), +		.cmd.rx_filter_add = { +			.opcode = IONIC_CMD_RX_FILTER_ADD, +			.lif_index = cpu_to_le16(lif->index), +			.match = cpu_to_le16(IONIC_RX_FILTER_STEER_PKTCLASS), +			.pkt_class = cpu_to_le64(pkt_class), +		}, +	}; +	u8 qtype; +	u32 qid; +	int err; + +	if (!lif->hwstamp_rxq) +		return -EINVAL; + +	qtype = lif->hwstamp_rxq->q.type; +	ctx.cmd.rx_filter_add.qtype = qtype; + +	qid = lif->hwstamp_rxq->q.index; +	ctx.cmd.rx_filter_add.qid = cpu_to_le32(qid); + +	netdev_dbg(lif->netdev, "rx_filter add RXSTEER\n"); +	err = ionic_adminq_post_wait(lif, &ctx); +	if (err && err != -EEXIST) +		return err; + +	return ionic_rx_filter_save(lif, 0, qid, 0, &ctx); +} + +int ionic_lif_set_hwstamp_rxfilt(struct ionic_lif *lif, u64 pkt_class) +{ +	ionic_lif_del_hwstamp_rxfilt(lif); + +	if (!pkt_class) +		return 0; + +	return ionic_lif_add_hwstamp_rxfilt(lif, pkt_class); +} +  static bool ionic_notifyq_service(struct ionic_cq *cq,  				  struct ionic_cq_info *cq_info)  { @@ -837,7 +1105,7 @@ static bool ionic_notifyq_service(struct ionic_cq *cq,  	switch (le16_to_cpu(comp->event.ecode)) {  	case IONIC_EVENT_LINK_CHANGE: -		ionic_link_status_check_request(lif, false); +		ionic_link_status_check_request(lif, CAN_NOT_SLEEP);  		break;  	case IONIC_EVENT_RESET:  		work = kzalloc(sizeof(*work), GFP_ATOMIC); @@ -875,30 +1143,43 @@ static int ionic_adminq_napi(struct napi_struct *napi, int budget)  	struct ionic_intr_info *intr = napi_to_cq(napi)->bound_intr;  	struct ionic_lif *lif = napi_to_cq(napi)->lif;  	struct ionic_dev *idev = &lif->ionic->idev; +	unsigned long irqflags;  	unsigned int flags = 0; +	int rx_work = 0; +	int tx_work = 0;  	int n_work = 0;  	int a_work = 0;  	int work_done; +	int credits;  	if (lif->notifyqcq && lif->notifyqcq->flags & IONIC_QCQ_F_INITED)  		n_work = ionic_cq_service(&lif->notifyqcq->cq, budget,  					  ionic_notifyq_service, NULL, NULL); +	spin_lock_irqsave(&lif->adminq_lock, irqflags);  	if (lif->adminqcq && lif->adminqcq->flags & IONIC_QCQ_F_INITED)  		a_work = ionic_cq_service(&lif->adminqcq->cq, budget,  					  ionic_adminq_service, NULL, NULL); +	spin_unlock_irqrestore(&lif->adminq_lock, irqflags); + +	if (lif->hwstamp_rxq) +		rx_work = ionic_cq_service(&lif->hwstamp_rxq->cq, budget, +					   ionic_rx_service, NULL, NULL); + +	if (lif->hwstamp_txq) +		tx_work = ionic_cq_service(&lif->hwstamp_txq->cq, budget, +					   ionic_tx_service, NULL, NULL); -	work_done = max(n_work, a_work); +	work_done = max(max(n_work, a_work), max(rx_work, tx_work));  	if (work_done < budget && napi_complete_done(napi, work_done)) {  		flags |= IONIC_INTR_CRED_UNMASK; -		lif->adminqcq->cq.bound_intr->rearm_count++; +		intr->rearm_count++;  	}  	if (work_done || flags) {  		flags |= IONIC_INTR_CRED_RESET_COALESCE; -		ionic_intr_credits(idev->intr_ctrl, -				   intr->index, -				   n_work + a_work, flags); +		credits = n_work + a_work + rx_work + tx_work; +		ionic_intr_credits(idev->intr_ctrl, intr->index, credits, flags);  	}  	return work_done; @@ -1258,6 +1539,10 @@ static int ionic_set_nic_features(struct ionic_lif *lif,  	int err;  	ctx.cmd.lif_setattr.features = ionic_netdev_features_to_nic(features); + +	if (lif->phc) +		ctx.cmd.lif_setattr.features |= cpu_to_le64(IONIC_ETH_HW_TIMESTAMP); +  	err = ionic_adminq_post_wait(lif, &ctx);  	if (err)  		return err; @@ -1305,6 +1590,8 @@ static int ionic_set_nic_features(struct ionic_lif *lif,  		dev_dbg(dev, "feature ETH_HW_TSO_UDP\n");  	if (lif->hw_features & IONIC_ETH_HW_TSO_UDP_CSUM)  		dev_dbg(dev, "feature ETH_HW_TSO_UDP_CSUM\n"); +	if (lif->hw_features & IONIC_ETH_HW_TIMESTAMP) +		dev_dbg(dev, "feature ETH_HW_TIMESTAMP\n");  	return 0;  } @@ -1441,7 +1728,7 @@ static int ionic_start_queues_reconfig(struct ionic_lif *lif)  	 */  	err = ionic_txrx_init(lif);  	mutex_unlock(&lif->queue_lock); -	ionic_link_status_check_request(lif, true); +	ionic_link_status_check_request(lif, CAN_SLEEP);  	netif_device_attach(lif->netdev);  	return err; @@ -1480,7 +1767,8 @@ static void ionic_tx_timeout_work(struct work_struct *ws)  {  	struct ionic_lif *lif = container_of(ws, struct ionic_lif, tx_timeout_work); -	netdev_info(lif->netdev, "Tx Timeout recovery\n"); +	if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) +		return;  	/* if we were stopped before this scheduled job was launched,  	 * don't bother the queues as they are already stopped. @@ -1496,6 +1784,7 @@ static void ionic_tx_timeout(struct net_device *netdev, unsigned int txqueue)  {  	struct ionic_lif *lif = netdev_priv(netdev); +	netdev_info(lif->netdev, "Tx Timeout triggered - txq %d\n", txqueue);  	schedule_work(&lif->tx_timeout_work);  } @@ -1645,11 +1934,17 @@ static void ionic_txrx_disable(struct ionic_lif *lif)  			err = ionic_qcq_disable(lif->txqcqs[i], (err != -ETIMEDOUT));  	} +	if (lif->hwstamp_txq) +		err = ionic_qcq_disable(lif->hwstamp_txq, (err != -ETIMEDOUT)); +  	if (lif->rxqcqs) {  		for (i = 0; i < lif->nxqs; i++)  			err = ionic_qcq_disable(lif->rxqcqs[i], (err != -ETIMEDOUT));  	} +	if (lif->hwstamp_rxq) +		err = ionic_qcq_disable(lif->hwstamp_rxq, (err != -ETIMEDOUT)); +  	ionic_lif_quiesce(lif);  } @@ -1672,6 +1967,17 @@ static void ionic_txrx_deinit(struct ionic_lif *lif)  		}  	}  	lif->rx_mode = 0; + +	if (lif->hwstamp_txq) { +		ionic_lif_qcq_deinit(lif, lif->hwstamp_txq); +		ionic_tx_flush(&lif->hwstamp_txq->cq); +		ionic_tx_empty(&lif->hwstamp_txq->q); +	} + +	if (lif->hwstamp_rxq) { +		ionic_lif_qcq_deinit(lif, lif->hwstamp_rxq); +		ionic_rx_empty(&lif->hwstamp_rxq->q); +	}  }  static void ionic_txrx_free(struct ionic_lif *lif) @@ -1693,15 +1999,30 @@ static void ionic_txrx_free(struct ionic_lif *lif)  			lif->rxqcqs[i] = NULL;  		}  	} + +	if (lif->hwstamp_txq) { +		ionic_qcq_free(lif, lif->hwstamp_txq); +		devm_kfree(lif->ionic->dev, lif->hwstamp_txq); +		lif->hwstamp_txq = NULL; +	} + +	if (lif->hwstamp_rxq) { +		ionic_qcq_free(lif, lif->hwstamp_rxq); +		devm_kfree(lif->ionic->dev, lif->hwstamp_rxq); +		lif->hwstamp_rxq = NULL; +	}  }  static int ionic_txrx_alloc(struct ionic_lif *lif)  { -	unsigned int sg_desc_sz; -	unsigned int flags; -	unsigned int i; +	unsigned int comp_sz, desc_sz, num_desc, sg_desc_sz; +	unsigned int flags, i;  	int err = 0; +	num_desc = lif->ntxq_descs; +	desc_sz = sizeof(struct ionic_txq_desc); +	comp_sz = sizeof(struct ionic_txq_comp); +  	if (lif->qtype_info[IONIC_QTYPE_TXQ].version >= 1 &&  	    lif->qtype_info[IONIC_QTYPE_TXQ].sg_desc_sz ==  					  sizeof(struct ionic_txq_sg_desc_v1)) @@ -1714,10 +2035,7 @@ static int ionic_txrx_alloc(struct ionic_lif *lif)  		flags |= IONIC_QCQ_F_INTR;  	for (i = 0; i < lif->nxqs; i++) {  		err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, i, "tx", flags, -				      lif->ntxq_descs, -				      sizeof(struct ionic_txq_desc), -				      sizeof(struct ionic_txq_comp), -				      sg_desc_sz, +				      num_desc, desc_sz, comp_sz, sg_desc_sz,  				      lif->kern_pid, &lif->txqcqs[i]);  		if (err)  			goto err_out; @@ -1734,16 +2052,24 @@ static int ionic_txrx_alloc(struct ionic_lif *lif)  	}  	flags = IONIC_QCQ_F_RX_STATS | IONIC_QCQ_F_SG | IONIC_QCQ_F_INTR; + +	num_desc = lif->nrxq_descs; +	desc_sz = sizeof(struct ionic_rxq_desc); +	comp_sz = sizeof(struct ionic_rxq_comp); +	sg_desc_sz = sizeof(struct ionic_rxq_sg_desc); + +	if (lif->rxq_features & IONIC_Q_F_2X_CQ_DESC) +		comp_sz *= 2; +  	for (i = 0; i < lif->nxqs; i++) {  		err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, i, "rx", flags, -				      lif->nrxq_descs, -				      sizeof(struct ionic_rxq_desc), -				      sizeof(struct ionic_rxq_comp), -				      sizeof(struct ionic_rxq_sg_desc), +				      num_desc, desc_sz, comp_sz, sg_desc_sz,  				      lif->kern_pid, &lif->rxqcqs[i]);  		if (err)  			goto err_out; +		lif->rxqcqs[i]->q.features = lif->rxq_features; +  		ionic_intr_coal_init(lif->ionic->idev.intr_ctrl,  				     lif->rxqcqs[i]->intr.index,  				     lif->rx_coalesce_hw); @@ -1822,8 +2148,26 @@ static int ionic_txrx_enable(struct ionic_lif *lif)  		}  	} +	if (lif->hwstamp_rxq) { +		ionic_rx_fill(&lif->hwstamp_rxq->q); +		err = ionic_qcq_enable(lif->hwstamp_rxq); +		if (err) +			goto err_out_hwstamp_rx; +	} + +	if (lif->hwstamp_txq) { +		err = ionic_qcq_enable(lif->hwstamp_txq); +		if (err) +			goto err_out_hwstamp_tx; +	} +  	return 0; +err_out_hwstamp_tx: +	if (lif->hwstamp_rxq) +		derr = ionic_qcq_disable(lif->hwstamp_rxq, (derr != -ETIMEDOUT)); +err_out_hwstamp_rx: +	i = lif->nxqs;  err_out:  	while (i--) {  		derr = ionic_qcq_disable(lif->txqcqs[i], (derr != -ETIMEDOUT)); @@ -1837,6 +2181,12 @@ static int ionic_start_queues(struct ionic_lif *lif)  {  	int err; +	if (test_bit(IONIC_LIF_F_BROKEN, lif->state)) +		return -EIO; + +	if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) +		return -EBUSY; +  	if (test_and_set_bit(IONIC_LIF_F_UP, lif->state))  		return 0; @@ -1855,13 +2205,17 @@ static int ionic_open(struct net_device *netdev)  	struct ionic_lif *lif = netdev_priv(netdev);  	int err; +	/* If recovering from a broken state, clear the bit and we'll try again */ +	if (test_and_clear_bit(IONIC_LIF_F_BROKEN, lif->state)) +		netdev_info(netdev, "clearing broken state\n"); +  	err = ionic_txrx_alloc(lif);  	if (err)  		return err;  	err = ionic_txrx_init(lif);  	if (err) -		goto err_out; +		goto err_txrx_free;  	err = netif_set_real_num_tx_queues(netdev, lif->nxqs);  	if (err) @@ -1882,7 +2236,7 @@ static int ionic_open(struct net_device *netdev)  err_txrx_deinit:  	ionic_txrx_deinit(lif); -err_out: +err_txrx_free:  	ionic_txrx_free(lif);  	return err;  } @@ -1910,6 +2264,20 @@ static int ionic_stop(struct net_device *netdev)  	return 0;  } +static int ionic_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ +	struct ionic_lif *lif = netdev_priv(netdev); + +	switch (cmd) { +	case SIOCSHWTSTAMP: +		return ionic_lif_hwstamp_set(lif, ifr); +	case SIOCGHWTSTAMP: +		return ionic_lif_hwstamp_get(lif, ifr); +	default: +		return -EOPNOTSUPP; +	} +} +  static int ionic_get_vf_config(struct net_device *netdev,  			       int vf, struct ifla_vf_info *ivf)  { @@ -2158,6 +2526,7 @@ static int ionic_set_vf_link_state(struct net_device *netdev, int vf, int set)  static const struct net_device_ops ionic_netdev_ops = {  	.ndo_open               = ionic_open,  	.ndo_stop               = ionic_stop, +	.ndo_do_ioctl		= ionic_do_ioctl,  	.ndo_start_xmit		= ionic_start_xmit,  	.ndo_get_stats64	= ionic_get_stats64,  	.ndo_set_rx_mode	= ionic_ndo_set_rx_mode, @@ -2181,7 +2550,9 @@ static const struct net_device_ops ionic_netdev_ops = {  static void ionic_swap_queues(struct ionic_qcq *a, struct ionic_qcq *b)  {  	/* only swapping the queues, not the napi, flags, or other stuff */ +	swap(a->q.features,   b->q.features);  	swap(a->q.num_descs,  b->q.num_descs); +	swap(a->q.desc_size,  b->q.desc_size);  	swap(a->q.base,       b->q.base);  	swap(a->q.base_pa,    b->q.base_pa);  	swap(a->q.info,       b->q.info); @@ -2189,6 +2560,7 @@ static void ionic_swap_queues(struct ionic_qcq *a, struct ionic_qcq *b)  	swap(a->q_base_pa,    b->q_base_pa);  	swap(a->q_size,       b->q_size); +	swap(a->q.sg_desc_size, b->q.sg_desc_size);  	swap(a->q.sg_base,    b->q.sg_base);  	swap(a->q.sg_base_pa, b->q.sg_base_pa);  	swap(a->sg_base,      b->sg_base); @@ -2196,23 +2568,26 @@ static void ionic_swap_queues(struct ionic_qcq *a, struct ionic_qcq *b)  	swap(a->sg_size,      b->sg_size);  	swap(a->cq.num_descs, b->cq.num_descs); +	swap(a->cq.desc_size, b->cq.desc_size);  	swap(a->cq.base,      b->cq.base);  	swap(a->cq.base_pa,   b->cq.base_pa);  	swap(a->cq.info,      b->cq.info);  	swap(a->cq_base,      b->cq_base);  	swap(a->cq_base_pa,   b->cq_base_pa);  	swap(a->cq_size,      b->cq_size); + +	ionic_debugfs_del_qcq(a); +	ionic_debugfs_add_qcq(a->q.lif, a);  }  int ionic_reconfigure_queues(struct ionic_lif *lif,  			     struct ionic_queue_params *qparam)  { +	unsigned int comp_sz, desc_sz, num_desc, sg_desc_sz;  	struct ionic_qcq **tx_qcqs = NULL;  	struct ionic_qcq **rx_qcqs = NULL; -	unsigned int sg_desc_sz; -	unsigned int flags; +	unsigned int flags, i;  	int err = -ENOMEM; -	unsigned int i;  	/* allocate temporary qcq arrays to hold new queue structs */  	if (qparam->nxqs != lif->nxqs || qparam->ntxq_descs != lif->ntxq_descs) { @@ -2221,7 +2596,9 @@ int ionic_reconfigure_queues(struct ionic_lif *lif,  		if (!tx_qcqs)  			goto err_out;  	} -	if (qparam->nxqs != lif->nxqs || qparam->nrxq_descs != lif->nrxq_descs) { +	if (qparam->nxqs != lif->nxqs || +	    qparam->nrxq_descs != lif->nrxq_descs || +	    qparam->rxq_features != lif->rxq_features) {  		rx_qcqs = devm_kcalloc(lif->ionic->dev, lif->ionic->nrxqs_per_lif,  				       sizeof(struct ionic_qcq *), GFP_KERNEL);  		if (!rx_qcqs) @@ -2231,21 +2608,22 @@ int ionic_reconfigure_queues(struct ionic_lif *lif,  	/* allocate new desc_info and rings, but leave the interrupt setup  	 * until later so as to not mess with the still-running queues  	 */ -	if (lif->qtype_info[IONIC_QTYPE_TXQ].version >= 1 && -	    lif->qtype_info[IONIC_QTYPE_TXQ].sg_desc_sz == -					  sizeof(struct ionic_txq_sg_desc_v1)) -		sg_desc_sz = sizeof(struct ionic_txq_sg_desc_v1); -	else -		sg_desc_sz = sizeof(struct ionic_txq_sg_desc); -  	if (tx_qcqs) { +		num_desc = qparam->ntxq_descs; +		desc_sz = sizeof(struct ionic_txq_desc); +		comp_sz = sizeof(struct ionic_txq_comp); + +		if (lif->qtype_info[IONIC_QTYPE_TXQ].version >= 1 && +		    lif->qtype_info[IONIC_QTYPE_TXQ].sg_desc_sz == +		    sizeof(struct ionic_txq_sg_desc_v1)) +			sg_desc_sz = sizeof(struct ionic_txq_sg_desc_v1); +		else +			sg_desc_sz = sizeof(struct ionic_txq_sg_desc); +  		for (i = 0; i < qparam->nxqs; i++) {  			flags = lif->txqcqs[i]->flags & ~IONIC_QCQ_F_INTR;  			err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, i, "tx", flags, -					      qparam->ntxq_descs, -					      sizeof(struct ionic_txq_desc), -					      sizeof(struct ionic_txq_comp), -					      sg_desc_sz, +					      num_desc, desc_sz, comp_sz, sg_desc_sz,  					      lif->kern_pid, &tx_qcqs[i]);  			if (err)  				goto err_out; @@ -2253,16 +2631,23 @@ int ionic_reconfigure_queues(struct ionic_lif *lif,  	}  	if (rx_qcqs) { +		num_desc = qparam->nrxq_descs; +		desc_sz = sizeof(struct ionic_rxq_desc); +		comp_sz = sizeof(struct ionic_rxq_comp); +		sg_desc_sz = sizeof(struct ionic_rxq_sg_desc); + +		if (qparam->rxq_features & IONIC_Q_F_2X_CQ_DESC) +			comp_sz *= 2; +  		for (i = 0; i < qparam->nxqs; i++) {  			flags = lif->rxqcqs[i]->flags & ~IONIC_QCQ_F_INTR;  			err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, i, "rx", flags, -					      qparam->nrxq_descs, -					      sizeof(struct ionic_rxq_desc), -					      sizeof(struct ionic_rxq_comp), -					      sizeof(struct ionic_rxq_sg_desc), +					      num_desc, desc_sz, comp_sz, sg_desc_sz,  					      lif->kern_pid, &rx_qcqs[i]);  			if (err)  				goto err_out; + +			rx_qcqs[i]->q.features = qparam->rxq_features;  		}  	} @@ -2349,9 +2734,10 @@ int ionic_reconfigure_queues(struct ionic_lif *lif,  	}  	swap(lif->nxqs, qparam->nxqs); +	swap(lif->rxq_features, qparam->rxq_features);  err_out_reinit_unlock: -	/* re-init the queues, but don't loose an error code */ +	/* re-init the queues, but don't lose an error code */  	if (err)  		ionic_start_queues_reconfig(lif);  	else @@ -2450,7 +2836,6 @@ int ionic_lif_alloc(struct ionic *ionic)  	lif->index = 0;  	lif->ntxq_descs = IONIC_DEF_TXRX_DESC;  	lif->nrxq_descs = IONIC_DEF_TXRX_DESC; -	lif->tx_budget = IONIC_TX_BUDGET_DEFAULT;  	/* Convert the default coalesce value to actual hw resolution */  	lif->rx_coalesce_usecs = IONIC_ITR_COAL_USEC_DEFAULT; @@ -2501,6 +2886,8 @@ int ionic_lif_alloc(struct ionic *ionic)  	}  	netdev_rss_key_fill(lif->rss_hash_key, IONIC_RSS_HASH_KEY_SIZE); +	ionic_lif_alloc_phc(lif); +  	return 0;  err_out_free_qcqs: @@ -2601,10 +2988,13 @@ static void ionic_lif_handle_fw_up(struct ionic_lif *lif)  	}  	clear_bit(IONIC_LIF_F_FW_RESET, lif->state); -	ionic_link_status_check_request(lif, true); +	ionic_link_status_check_request(lif, CAN_SLEEP);  	netif_device_attach(lif->netdev);  	dev_info(ionic->dev, "FW Up: LIFs restarted\n"); +	/* restore the hardware timestamping queues */ +	ionic_lif_hwstamp_replay(lif); +  	return;  err_txrx_free: @@ -2621,6 +3011,8 @@ void ionic_lif_free(struct ionic_lif *lif)  {  	struct device *dev = lif->ionic->dev; +	ionic_lif_free_phc(lif); +  	/* free rss indirection table */  	dma_free_coherent(dev, lif->rss_ind_tbl_sz, lif->rss_ind_tbl,  			  lif->rss_ind_tbl_pa); @@ -2957,6 +3349,8 @@ int ionic_lif_register(struct ionic_lif *lif)  {  	int err; +	ionic_lif_register_phc(lif); +  	INIT_WORK(&lif->ionic->nb_work, ionic_lif_notify_work);  	lif->ionic->nb.notifier_call = ionic_lif_notify; @@ -2969,10 +3363,11 @@ int ionic_lif_register(struct ionic_lif *lif)  	err = register_netdev(lif->netdev);  	if (err) {  		dev_err(lif->ionic->dev, "Cannot register net device, aborting\n"); +		ionic_lif_unregister_phc(lif);  		return err;  	} -	ionic_link_status_check_request(lif, true); +	ionic_link_status_check_request(lif, CAN_SLEEP);  	lif->registered = true;  	ionic_lif_set_netdev_info(lif); @@ -2989,6 +3384,9 @@ void ionic_lif_unregister(struct ionic_lif *lif)  	if (lif->netdev->reg_state == NETREG_REGISTERED)  		unregister_netdev(lif->netdev); + +	ionic_lif_unregister_phc(lif); +  	lif->registered = false;  } @@ -3128,6 +3526,16 @@ int ionic_lif_size(struct ionic *ionic)  	ntxqs_per_lif = le32_to_cpu(lc->queue_count[IONIC_QTYPE_TXQ]);  	nrxqs_per_lif = le32_to_cpu(lc->queue_count[IONIC_QTYPE_RXQ]); +	/* reserve last queue id for hardware timestamping */ +	if (lc->features & cpu_to_le64(IONIC_ETH_HW_TIMESTAMP)) { +		if (ntxqs_per_lif <= 1 || nrxqs_per_lif <= 1) { +			lc->features &= cpu_to_le64(~IONIC_ETH_HW_TIMESTAMP); +		} else { +			ntxqs_per_lif -= 1; +			nrxqs_per_lif -= 1; +		} +	} +  	nxqs = min(ntxqs_per_lif, nrxqs_per_lif);  	nxqs = min(nxqs, num_online_cpus());  	neqs = min(neqs_per_lif, num_online_cpus()); | 
