cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

mcdi_functions.c (12776B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/****************************************************************************
      3 * Driver for Solarflare network controllers and boards
      4 * Copyright 2019 Solarflare Communications Inc.
      5 *
      6 * This program is free software; you can redistribute it and/or modify it
      7 * under the terms of the GNU General Public License version 2 as published
      8 * by the Free Software Foundation, incorporated herein by reference.
      9 */
     10
     11#include "net_driver.h"
     12#include "efx.h"
     13#include "nic.h"
     14#include "mcdi_functions.h"
     15#include "mcdi.h"
     16#include "mcdi_pcol.h"
     17
     18int efx_mcdi_free_vis(struct efx_nic *efx)
     19{
     20	MCDI_DECLARE_BUF_ERR(outbuf);
     21	size_t outlen;
     22	int rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FREE_VIS, NULL, 0,
     23				    outbuf, sizeof(outbuf), &outlen);
     24
     25	/* -EALREADY means nothing to free, so ignore */
     26	if (rc == -EALREADY)
     27		rc = 0;
     28	if (rc)
     29		efx_mcdi_display_error(efx, MC_CMD_FREE_VIS, 0, outbuf, outlen,
     30				       rc);
     31	return rc;
     32}
     33
     34int efx_mcdi_alloc_vis(struct efx_nic *efx, unsigned int min_vis,
     35		       unsigned int max_vis, unsigned int *vi_base,
     36		       unsigned int *allocated_vis)
     37{
     38	MCDI_DECLARE_BUF(outbuf, MC_CMD_ALLOC_VIS_OUT_LEN);
     39	MCDI_DECLARE_BUF(inbuf, MC_CMD_ALLOC_VIS_IN_LEN);
     40	size_t outlen;
     41	int rc;
     42
     43	MCDI_SET_DWORD(inbuf, ALLOC_VIS_IN_MIN_VI_COUNT, min_vis);
     44	MCDI_SET_DWORD(inbuf, ALLOC_VIS_IN_MAX_VI_COUNT, max_vis);
     45	rc = efx_mcdi_rpc(efx, MC_CMD_ALLOC_VIS, inbuf, sizeof(inbuf),
     46			  outbuf, sizeof(outbuf), &outlen);
     47	if (rc != 0)
     48		return rc;
     49
     50	if (outlen < MC_CMD_ALLOC_VIS_OUT_LEN)
     51		return -EIO;
     52
     53	netif_dbg(efx, drv, efx->net_dev, "base VI is A0x%03x\n",
     54		  MCDI_DWORD(outbuf, ALLOC_VIS_OUT_VI_BASE));
     55
     56	if (vi_base)
     57		*vi_base = MCDI_DWORD(outbuf, ALLOC_VIS_OUT_VI_BASE);
     58	if (allocated_vis)
     59		*allocated_vis = MCDI_DWORD(outbuf, ALLOC_VIS_OUT_VI_COUNT);
     60	return 0;
     61}
     62
     63int efx_mcdi_ev_probe(struct efx_channel *channel)
     64{
     65	return efx_nic_alloc_buffer(channel->efx, &channel->eventq.buf,
     66				    (channel->eventq_mask + 1) *
     67				    sizeof(efx_qword_t),
     68				    GFP_KERNEL);
     69}
     70
     71int efx_mcdi_ev_init(struct efx_channel *channel, bool v1_cut_thru, bool v2)
     72{
     73	MCDI_DECLARE_BUF(inbuf,
     74			 MC_CMD_INIT_EVQ_V2_IN_LEN(EFX_MAX_EVQ_SIZE * 8 /
     75						   EFX_BUF_SIZE));
     76	MCDI_DECLARE_BUF(outbuf, MC_CMD_INIT_EVQ_V2_OUT_LEN);
     77	size_t entries = channel->eventq.buf.len / EFX_BUF_SIZE;
     78	struct efx_nic *efx = channel->efx;
     79	size_t inlen, outlen;
     80	dma_addr_t dma_addr;
     81	int rc, i;
     82
     83	/* Fill event queue with all ones (i.e. empty events) */
     84	memset(channel->eventq.buf.addr, 0xff, channel->eventq.buf.len);
     85
     86	MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_SIZE, channel->eventq_mask + 1);
     87	MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_INSTANCE, channel->channel);
     88	/* INIT_EVQ expects index in vector table, not absolute */
     89	MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_IRQ_NUM, channel->channel);
     90	MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_TMR_MODE,
     91		       MC_CMD_INIT_EVQ_IN_TMR_MODE_DIS);
     92	MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_TMR_LOAD, 0);
     93	MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_TMR_RELOAD, 0);
     94	MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_COUNT_MODE,
     95		       MC_CMD_INIT_EVQ_IN_COUNT_MODE_DIS);
     96	MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_COUNT_THRSHLD, 0);
     97
     98	if (v2) {
     99		/* Use the new generic approach to specifying event queue
    100		 * configuration, requesting lower latency or higher throughput.
    101		 * The options that actually get used appear in the output.
    102		 */
    103		MCDI_POPULATE_DWORD_2(inbuf, INIT_EVQ_V2_IN_FLAGS,
    104				      INIT_EVQ_V2_IN_FLAG_INTERRUPTING, 1,
    105				      INIT_EVQ_V2_IN_FLAG_TYPE,
    106				      MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_AUTO);
    107	} else {
    108		MCDI_POPULATE_DWORD_4(inbuf, INIT_EVQ_IN_FLAGS,
    109				      INIT_EVQ_IN_FLAG_INTERRUPTING, 1,
    110				      INIT_EVQ_IN_FLAG_RX_MERGE, 1,
    111				      INIT_EVQ_IN_FLAG_TX_MERGE, 1,
    112				      INIT_EVQ_IN_FLAG_CUT_THRU, v1_cut_thru);
    113	}
    114
    115	dma_addr = channel->eventq.buf.dma_addr;
    116	for (i = 0; i < entries; ++i) {
    117		MCDI_SET_ARRAY_QWORD(inbuf, INIT_EVQ_IN_DMA_ADDR, i, dma_addr);
    118		dma_addr += EFX_BUF_SIZE;
    119	}
    120
    121	inlen = MC_CMD_INIT_EVQ_IN_LEN(entries);
    122
    123	rc = efx_mcdi_rpc(efx, MC_CMD_INIT_EVQ, inbuf, inlen,
    124			  outbuf, sizeof(outbuf), &outlen);
    125
    126	if (outlen >= MC_CMD_INIT_EVQ_V2_OUT_LEN)
    127		netif_dbg(efx, drv, efx->net_dev,
    128			  "Channel %d using event queue flags %08x\n",
    129			  channel->channel,
    130			  MCDI_DWORD(outbuf, INIT_EVQ_V2_OUT_FLAGS));
    131
    132	return rc;
    133}
    134
    135void efx_mcdi_ev_remove(struct efx_channel *channel)
    136{
    137	efx_nic_free_buffer(channel->efx, &channel->eventq.buf);
    138}
    139
    140void efx_mcdi_ev_fini(struct efx_channel *channel)
    141{
    142	MCDI_DECLARE_BUF(inbuf, MC_CMD_FINI_EVQ_IN_LEN);
    143	MCDI_DECLARE_BUF_ERR(outbuf);
    144	struct efx_nic *efx = channel->efx;
    145	size_t outlen;
    146	int rc;
    147
    148	MCDI_SET_DWORD(inbuf, FINI_EVQ_IN_INSTANCE, channel->channel);
    149
    150	rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FINI_EVQ, inbuf, sizeof(inbuf),
    151				outbuf, sizeof(outbuf), &outlen);
    152
    153	if (rc && rc != -EALREADY)
    154		goto fail;
    155
    156	return;
    157
    158fail:
    159	efx_mcdi_display_error(efx, MC_CMD_FINI_EVQ, MC_CMD_FINI_EVQ_IN_LEN,
    160			       outbuf, outlen, rc);
    161}
    162
    163int efx_mcdi_tx_init(struct efx_tx_queue *tx_queue)
    164{
    165	MCDI_DECLARE_BUF(inbuf, MC_CMD_INIT_TXQ_IN_LEN(EFX_MAX_DMAQ_SIZE * 8 /
    166						       EFX_BUF_SIZE));
    167	bool csum_offload = tx_queue->type & EFX_TXQ_TYPE_OUTER_CSUM;
    168	bool inner_csum = tx_queue->type & EFX_TXQ_TYPE_INNER_CSUM;
    169	size_t entries = tx_queue->txd.buf.len / EFX_BUF_SIZE;
    170	struct efx_channel *channel = tx_queue->channel;
    171	struct efx_nic *efx = tx_queue->efx;
    172	dma_addr_t dma_addr;
    173	size_t inlen;
    174	int rc, i;
    175
    176	BUILD_BUG_ON(MC_CMD_INIT_TXQ_OUT_LEN != 0);
    177
    178	MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_SIZE, tx_queue->ptr_mask + 1);
    179	MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_TARGET_EVQ, channel->channel);
    180	MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_LABEL, tx_queue->label);
    181	MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_INSTANCE, tx_queue->queue);
    182	MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_OWNER_ID, 0);
    183	MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_PORT_ID, efx->vport_id);
    184
    185	dma_addr = tx_queue->txd.buf.dma_addr;
    186
    187	netif_dbg(efx, hw, efx->net_dev, "pushing TXQ %d. %zu entries (%llx)\n",
    188		  tx_queue->queue, entries, (u64)dma_addr);
    189
    190	for (i = 0; i < entries; ++i) {
    191		MCDI_SET_ARRAY_QWORD(inbuf, INIT_TXQ_IN_DMA_ADDR, i, dma_addr);
    192		dma_addr += EFX_BUF_SIZE;
    193	}
    194
    195	inlen = MC_CMD_INIT_TXQ_IN_LEN(entries);
    196
    197	do {
    198		bool tso_v2 = tx_queue->tso_version == 2;
    199
    200		/* TSOv2 implies IP header checksum offload for TSO frames,
    201		 * so we can safely disable IP header checksum offload for
    202		 * everything else.  If we don't have TSOv2, then we have to
    203		 * enable IP header checksum offload, which is strictly
    204		 * incorrect but better than breaking TSO.
    205		 */
    206		MCDI_POPULATE_DWORD_6(inbuf, INIT_TXQ_IN_FLAGS,
    207				/* This flag was removed from mcdi_pcol.h for
    208				 * the non-_EXT version of INIT_TXQ.  However,
    209				 * firmware still honours it.
    210				 */
    211				INIT_TXQ_EXT_IN_FLAG_TSOV2_EN, tso_v2,
    212				INIT_TXQ_IN_FLAG_IP_CSUM_DIS, !(csum_offload && tso_v2),
    213				INIT_TXQ_IN_FLAG_TCP_CSUM_DIS, !csum_offload,
    214				INIT_TXQ_EXT_IN_FLAG_TIMESTAMP, tx_queue->timestamping,
    215				INIT_TXQ_IN_FLAG_INNER_IP_CSUM_EN, inner_csum && !tso_v2,
    216				INIT_TXQ_IN_FLAG_INNER_TCP_CSUM_EN, inner_csum);
    217
    218		rc = efx_mcdi_rpc_quiet(efx, MC_CMD_INIT_TXQ, inbuf, inlen,
    219					NULL, 0, NULL);
    220		if (rc == -ENOSPC && tso_v2) {
    221			/* Retry without TSOv2 if we're short on contexts. */
    222			tx_queue->tso_version = 0;
    223			netif_warn(efx, probe, efx->net_dev,
    224				   "TSOv2 context not available to segment in "
    225				   "hardware. TCP performance may be reduced.\n"
    226				   );
    227		} else if (rc) {
    228			efx_mcdi_display_error(efx, MC_CMD_INIT_TXQ,
    229					       MC_CMD_INIT_TXQ_EXT_IN_LEN,
    230					       NULL, 0, rc);
    231			goto fail;
    232		}
    233	} while (rc);
    234
    235	return 0;
    236
    237fail:
    238	return rc;
    239}
    240
    241void efx_mcdi_tx_remove(struct efx_tx_queue *tx_queue)
    242{
    243	efx_nic_free_buffer(tx_queue->efx, &tx_queue->txd.buf);
    244}
    245
    246void efx_mcdi_tx_fini(struct efx_tx_queue *tx_queue)
    247{
    248	MCDI_DECLARE_BUF(inbuf, MC_CMD_FINI_TXQ_IN_LEN);
    249	MCDI_DECLARE_BUF_ERR(outbuf);
    250	struct efx_nic *efx = tx_queue->efx;
    251	size_t outlen;
    252	int rc;
    253
    254	MCDI_SET_DWORD(inbuf, FINI_TXQ_IN_INSTANCE,
    255		       tx_queue->queue);
    256
    257	rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FINI_TXQ, inbuf, sizeof(inbuf),
    258				outbuf, sizeof(outbuf), &outlen);
    259
    260	if (rc && rc != -EALREADY)
    261		goto fail;
    262
    263	return;
    264
    265fail:
    266	efx_mcdi_display_error(efx, MC_CMD_FINI_TXQ, MC_CMD_FINI_TXQ_IN_LEN,
    267			       outbuf, outlen, rc);
    268}
    269
    270int efx_mcdi_rx_probe(struct efx_rx_queue *rx_queue)
    271{
    272	return efx_nic_alloc_buffer(rx_queue->efx, &rx_queue->rxd.buf,
    273				    (rx_queue->ptr_mask + 1) *
    274				    sizeof(efx_qword_t),
    275				    GFP_KERNEL);
    276}
    277
    278void efx_mcdi_rx_init(struct efx_rx_queue *rx_queue)
    279{
    280	struct efx_channel *channel = efx_rx_queue_channel(rx_queue);
    281	size_t entries = rx_queue->rxd.buf.len / EFX_BUF_SIZE;
    282	MCDI_DECLARE_BUF(inbuf, MC_CMD_INIT_RXQ_V4_IN_LEN);
    283	struct efx_nic *efx = rx_queue->efx;
    284	unsigned int buffer_size;
    285	dma_addr_t dma_addr;
    286	int rc;
    287	int i;
    288	BUILD_BUG_ON(MC_CMD_INIT_RXQ_OUT_LEN != 0);
    289
    290	rx_queue->scatter_n = 0;
    291	rx_queue->scatter_len = 0;
    292	if (efx->type->revision == EFX_REV_EF100)
    293		buffer_size = efx->rx_page_buf_step;
    294	else
    295		buffer_size = 0;
    296
    297	MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_SIZE, rx_queue->ptr_mask + 1);
    298	MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_TARGET_EVQ, channel->channel);
    299	MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_LABEL, efx_rx_queue_index(rx_queue));
    300	MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_INSTANCE,
    301		       efx_rx_queue_index(rx_queue));
    302	MCDI_POPULATE_DWORD_2(inbuf, INIT_RXQ_IN_FLAGS,
    303			      INIT_RXQ_IN_FLAG_PREFIX, 1,
    304			      INIT_RXQ_IN_FLAG_TIMESTAMP, 1);
    305	MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_OWNER_ID, 0);
    306	MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_PORT_ID, efx->vport_id);
    307	MCDI_SET_DWORD(inbuf, INIT_RXQ_V4_IN_BUFFER_SIZE_BYTES, buffer_size);
    308
    309	dma_addr = rx_queue->rxd.buf.dma_addr;
    310
    311	netif_dbg(efx, hw, efx->net_dev, "pushing RXQ %d. %zu entries (%llx)\n",
    312		  efx_rx_queue_index(rx_queue), entries, (u64)dma_addr);
    313
    314	for (i = 0; i < entries; ++i) {
    315		MCDI_SET_ARRAY_QWORD(inbuf, INIT_RXQ_IN_DMA_ADDR, i, dma_addr);
    316		dma_addr += EFX_BUF_SIZE;
    317	}
    318
    319	rc = efx_mcdi_rpc(efx, MC_CMD_INIT_RXQ, inbuf, sizeof(inbuf),
    320			  NULL, 0, NULL);
    321	if (rc)
    322		netdev_WARN(efx->net_dev, "failed to initialise RXQ %d\n",
    323			    efx_rx_queue_index(rx_queue));
    324}
    325
    326void efx_mcdi_rx_remove(struct efx_rx_queue *rx_queue)
    327{
    328	efx_nic_free_buffer(rx_queue->efx, &rx_queue->rxd.buf);
    329}
    330
    331void efx_mcdi_rx_fini(struct efx_rx_queue *rx_queue)
    332{
    333	MCDI_DECLARE_BUF(inbuf, MC_CMD_FINI_RXQ_IN_LEN);
    334	MCDI_DECLARE_BUF_ERR(outbuf);
    335	struct efx_nic *efx = rx_queue->efx;
    336	size_t outlen;
    337	int rc;
    338
    339	MCDI_SET_DWORD(inbuf, FINI_RXQ_IN_INSTANCE,
    340		       efx_rx_queue_index(rx_queue));
    341
    342	rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FINI_RXQ, inbuf, sizeof(inbuf),
    343				outbuf, sizeof(outbuf), &outlen);
    344
    345	if (rc && rc != -EALREADY)
    346		goto fail;
    347
    348	return;
    349
    350fail:
    351	efx_mcdi_display_error(efx, MC_CMD_FINI_RXQ, MC_CMD_FINI_RXQ_IN_LEN,
    352			       outbuf, outlen, rc);
    353}
    354
    355int efx_fini_dmaq(struct efx_nic *efx)
    356{
    357	struct efx_tx_queue *tx_queue;
    358	struct efx_rx_queue *rx_queue;
    359	struct efx_channel *channel;
    360	int pending;
    361
    362	/* If the MC has just rebooted, the TX/RX queues will have already been
    363	 * torn down, but efx->active_queues needs to be set to zero.
    364	 */
    365	if (efx->must_realloc_vis) {
    366		atomic_set(&efx->active_queues, 0);
    367		return 0;
    368	}
    369
    370	/* Do not attempt to write to the NIC during EEH recovery */
    371	if (efx->state != STATE_RECOVERY) {
    372		efx_for_each_channel(channel, efx) {
    373			efx_for_each_channel_rx_queue(rx_queue, channel)
    374				efx_mcdi_rx_fini(rx_queue);
    375			efx_for_each_channel_tx_queue(tx_queue, channel)
    376				efx_mcdi_tx_fini(tx_queue);
    377		}
    378
    379		wait_event_timeout(efx->flush_wq,
    380				   atomic_read(&efx->active_queues) == 0,
    381				   msecs_to_jiffies(EFX_MAX_FLUSH_TIME));
    382		pending = atomic_read(&efx->active_queues);
    383		if (pending) {
    384			netif_err(efx, hw, efx->net_dev, "failed to flush %d queues\n",
    385				  pending);
    386			return -ETIMEDOUT;
    387		}
    388	}
    389
    390	return 0;
    391}
    392
    393int efx_mcdi_window_mode_to_stride(struct efx_nic *efx, u8 vi_window_mode)
    394{
    395	switch (vi_window_mode) {
    396	case MC_CMD_GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE_8K:
    397		efx->vi_stride = 8192;
    398		break;
    399	case MC_CMD_GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE_16K:
    400		efx->vi_stride = 16384;
    401		break;
    402	case MC_CMD_GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE_64K:
    403		efx->vi_stride = 65536;
    404		break;
    405	default:
    406		netif_err(efx, probe, efx->net_dev,
    407			  "Unrecognised VI window mode %d\n",
    408			  vi_window_mode);
    409		return -EIO;
    410	}
    411	netif_dbg(efx, probe, efx->net_dev, "vi_stride = %u\n",
    412		  efx->vi_stride);
    413	return 0;
    414}
    415
    416int efx_get_pf_index(struct efx_nic *efx, unsigned int *pf_index)
    417{
    418	MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_FUNCTION_INFO_OUT_LEN);
    419	size_t outlen;
    420	int rc;
    421
    422	rc = efx_mcdi_rpc(efx, MC_CMD_GET_FUNCTION_INFO, NULL, 0, outbuf,
    423			  sizeof(outbuf), &outlen);
    424	if (rc)
    425		return rc;
    426	if (outlen < sizeof(outbuf))
    427		return -EIO;
    428
    429	*pf_index = MCDI_DWORD(outbuf, GET_FUNCTION_INFO_OUT_PF);
    430	return 0;
    431}