Commit ff5756d0 authored by David S. Miller's avatar David S. Miller

Merge branch 'sfc-vf'

Bert Kenward says:

====================
sfc: additional virtual function support​

This introduces the client side of a mechanism to defer authorisation of
operations, for example multicast subscription. Although primarily aimed at
SRIOV VFs this can also apply to unprivileged PFs.

Also handle reboot ordering corner cases better and reduce the level of some
logging.

v2: remove #ifdef DEBUG around new WARN_ON in mcdi.c.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 2e4c8baa 09a04204
......@@ -486,10 +486,17 @@ static int efx_ef10_alloc_piobufs(struct efx_nic *efx, unsigned int n)
BUILD_BUG_ON(MC_CMD_ALLOC_PIOBUF_IN_LEN != 0);
for (i = 0; i < n; i++) {
rc = efx_mcdi_rpc(efx, MC_CMD_ALLOC_PIOBUF, NULL, 0,
rc = efx_mcdi_rpc_quiet(efx, MC_CMD_ALLOC_PIOBUF, NULL, 0,
outbuf, sizeof(outbuf), &outlen);
if (rc)
if (rc) {
/* Don't display the MC error if we didn't have space
* for a VF.
*/
if (!(efx_ef10_is_vf(efx) && rc == -ENOSPC))
efx_mcdi_display_error(efx, MC_CMD_ALLOC_PIOBUF,
0, outbuf, outlen, rc);
break;
}
if (outlen < MC_CMD_ALLOC_PIOBUF_OUT_LEN) {
rc = -EIO;
break;
......@@ -3833,13 +3840,12 @@ static void efx_ef10_filter_table_remove(struct efx_nic *efx)
MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE);
MCDI_SET_QWORD(inbuf, FILTER_OP_IN_HANDLE,
table->entry[filter_idx].handle);
rc = efx_mcdi_rpc(efx, MC_CMD_FILTER_OP, inbuf, sizeof(inbuf),
NULL, 0, NULL);
rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FILTER_OP, inbuf,
sizeof(inbuf), NULL, 0, NULL);
if (rc)
netdev_WARN(efx->net_dev,
"filter_idx=%#x handle=%#llx\n",
filter_idx,
table->entry[filter_idx].handle);
netif_info(efx, drv, efx->net_dev,
"%s: filter %04x remove failed\n",
__func__, filter_idx);
kfree(spec);
}
......@@ -3850,8 +3856,11 @@ static void efx_ef10_filter_table_remove(struct efx_nic *efx)
#define EFX_EF10_FILTER_DO_MARK_OLD(id) \
if (id != EFX_EF10_FILTER_ID_INVALID) { \
filter_idx = efx_ef10_filter_get_unsafe_id(efx, id); \
WARN_ON(!table->entry[filter_idx].spec); \
table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD; \
if (!table->entry[filter_idx].spec) \
netif_dbg(efx, drv, efx->net_dev, \
"%s: marked null spec old %04x:%04x\n", \
__func__, id, filter_idx); \
table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD;\
}
static void efx_ef10_filter_mark_old(struct efx_nic *efx)
{
......@@ -4026,7 +4035,8 @@ static int efx_ef10_filter_insert_def(struct efx_nic *efx, bool multicast,
rc = efx_ef10_filter_insert(efx, &spec, true);
if (rc < 0) {
netif_warn(efx, drv, efx->net_dev,
netif_printk(efx, drv, rc == -EPERM ? KERN_DEBUG : KERN_WARNING,
efx->net_dev,
"%scast mismatch filter insert failed rc=%d\n",
multicast ? "Multi" : "Uni", rc);
} else if (multicast) {
......@@ -4070,19 +4080,31 @@ static int efx_ef10_filter_insert_def(struct efx_nic *efx, bool multicast,
static void efx_ef10_filter_remove_old(struct efx_nic *efx)
{
struct efx_ef10_filter_table *table = efx->filter_state;
bool remove_failed = false;
int remove_failed = 0;
int remove_noent = 0;
int rc;
int i;
for (i = 0; i < HUNT_FILTER_TBL_ROWS; i++) {
if (ACCESS_ONCE(table->entry[i].spec) &
EFX_EF10_FILTER_FLAG_AUTO_OLD) {
if (efx_ef10_filter_remove_internal(
efx, 1U << EFX_FILTER_PRI_AUTO,
i, true) < 0)
remove_failed = true;
rc = efx_ef10_filter_remove_internal(efx,
1U << EFX_FILTER_PRI_AUTO, i, true);
if (rc == -ENOENT)
remove_noent++;
else if (rc)
remove_failed++;
}
}
WARN_ON(remove_failed);
if (remove_failed)
netif_info(efx, drv, efx->net_dev,
"%s: failed to remove %d filters\n",
__func__, remove_failed);
if (remove_noent)
netif_info(efx, drv, efx->net_dev,
"%s: failed to remove %d non-existent filters\n",
__func__, remove_noent);
}
static int efx_ef10_vport_set_mac_address(struct efx_nic *efx)
......
......@@ -3174,14 +3174,15 @@ static int efx_pci_probe(struct pci_dev *pci_dev,
rtnl_lock();
rc = efx_mtd_probe(efx);
rtnl_unlock();
if (rc)
if (rc && rc != -EPERM)
netif_warn(efx, probe, efx->net_dev,
"failed to create MTDs (%d)\n", rc);
rc = pci_enable_pcie_error_reporting(pci_dev);
if (rc && rc != -EINVAL)
netif_warn(efx, probe, efx->net_dev,
"pci_enable_pcie_error_reporting failed (%d)\n", rc);
netif_notice(efx, probe, efx->net_dev,
"PCIE error reporting unavailable (%d).\n",
rc);
return 0;
......
......@@ -82,6 +82,7 @@ int efx_mcdi_init(struct efx_nic *efx)
mcdi->logging_enabled = mcdi_logging_default;
#endif
init_waitqueue_head(&mcdi->wq);
init_waitqueue_head(&mcdi->proxy_rx_wq);
spin_lock_init(&mcdi->iface_lock);
mcdi->state = MCDI_STATE_QUIESCENT;
mcdi->mode = MCDI_MODE_POLL;
......@@ -315,6 +316,7 @@ static void efx_mcdi_read_response_header(struct efx_nic *efx)
}
#endif
mcdi->resprc_raw = 0;
if (error && mcdi->resp_data_len == 0) {
netif_err(efx, hw, efx->net_dev, "MC rebooted\n");
mcdi->resprc = -EIO;
......@@ -325,8 +327,8 @@ static void efx_mcdi_read_response_header(struct efx_nic *efx)
mcdi->resprc = -EIO;
} else if (error) {
efx->type->mcdi_read_response(efx, &hdr, mcdi->resp_hdr_len, 4);
mcdi->resprc =
efx_mcdi_errno(EFX_DWORD_FIELD(hdr, EFX_DWORD_0));
mcdi->resprc_raw = EFX_DWORD_FIELD(hdr, EFX_DWORD_0);
mcdi->resprc = efx_mcdi_errno(mcdi->resprc_raw);
} else {
mcdi->resprc = 0;
}
......@@ -621,9 +623,30 @@ efx_mcdi_check_supported(struct efx_nic *efx, unsigned int cmd, size_t inlen)
return 0;
}
static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
static bool efx_mcdi_get_proxy_handle(struct efx_nic *efx,
size_t hdr_len, size_t data_len,
u32 *proxy_handle)
{
MCDI_DECLARE_BUF_ERR(testbuf);
const size_t buflen = sizeof(testbuf);
if (!proxy_handle || data_len < buflen)
return false;
efx->type->mcdi_read_response(efx, testbuf, hdr_len, buflen);
if (MCDI_DWORD(testbuf, ERR_CODE) == MC_CMD_ERR_PROXY_PENDING) {
*proxy_handle = MCDI_DWORD(testbuf, ERR_PROXY_PENDING_HANDLE);
return true;
}
return false;
}
static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned int cmd,
size_t inlen,
efx_dword_t *outbuf, size_t outlen,
size_t *outlen_actual, bool quiet)
size_t *outlen_actual, bool quiet,
u32 *proxy_handle, int *raw_rc)
{
struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
MCDI_DECLARE_BUF_ERR(errbuf);
......@@ -657,6 +680,9 @@ static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
spin_unlock_bh(&mcdi->iface_lock);
}
if (proxy_handle)
*proxy_handle = 0;
if (rc != 0) {
if (outlen_actual)
*outlen_actual = 0;
......@@ -669,6 +695,8 @@ static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
* acquiring the iface_lock. */
spin_lock_bh(&mcdi->iface_lock);
rc = mcdi->resprc;
if (raw_rc)
*raw_rc = mcdi->resprc_raw;
hdr_len = mcdi->resp_hdr_len;
data_len = mcdi->resp_data_len;
err_len = min(sizeof(errbuf), data_len);
......@@ -689,6 +717,12 @@ static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
netif_err(efx, hw, efx->net_dev, "MC fatal error %d\n",
-rc);
efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE);
} else if (proxy_handle && (rc == -EPROTO) &&
efx_mcdi_get_proxy_handle(efx, hdr_len, data_len,
proxy_handle)) {
mcdi->proxy_rx_status = 0;
mcdi->proxy_rx_handle = 0;
mcdi->state = MCDI_STATE_PROXY_WAIT;
} else if (rc && !quiet) {
efx_mcdi_display_error(efx, cmd, inlen, errbuf, err_len,
rc);
......@@ -701,33 +735,194 @@ static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
}
}
if (!proxy_handle || !*proxy_handle)
efx_mcdi_release(mcdi);
return rc;
}
static int _efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd,
static void efx_mcdi_proxy_abort(struct efx_mcdi_iface *mcdi)
{
if (mcdi->state == MCDI_STATE_PROXY_WAIT) {
/* Interrupt the proxy wait. */
mcdi->proxy_rx_status = -EINTR;
wake_up(&mcdi->proxy_rx_wq);
}
}
static void efx_mcdi_ev_proxy_response(struct efx_nic *efx,
u32 handle, int status)
{
struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
WARN_ON(mcdi->state != MCDI_STATE_PROXY_WAIT);
mcdi->proxy_rx_status = efx_mcdi_errno(status);
/* Ensure the status is written before we update the handle, since the
* latter is used to check if we've finished.
*/
wmb();
mcdi->proxy_rx_handle = handle;
wake_up(&mcdi->proxy_rx_wq);
}
static int efx_mcdi_proxy_wait(struct efx_nic *efx, u32 handle, bool quiet)
{
struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
int rc;
/* Wait for a proxy event, or timeout. */
rc = wait_event_timeout(mcdi->proxy_rx_wq,
mcdi->proxy_rx_handle != 0 ||
mcdi->proxy_rx_status == -EINTR,
MCDI_RPC_TIMEOUT);
if (rc <= 0) {
netif_dbg(efx, hw, efx->net_dev,
"MCDI proxy timeout %d\n", handle);
return -ETIMEDOUT;
} else if (mcdi->proxy_rx_handle != handle) {
netif_warn(efx, hw, efx->net_dev,
"MCDI proxy unexpected handle %d (expected %d)\n",
mcdi->proxy_rx_handle, handle);
return -EINVAL;
}
return mcdi->proxy_rx_status;
}
static int _efx_mcdi_rpc(struct efx_nic *efx, unsigned int cmd,
const efx_dword_t *inbuf, size_t inlen,
efx_dword_t *outbuf, size_t outlen,
size_t *outlen_actual, bool quiet)
size_t *outlen_actual, bool quiet, int *raw_rc)
{
u32 proxy_handle = 0; /* Zero is an invalid proxy handle. */
int rc;
if (inbuf && inlen && (inbuf == outbuf)) {
/* The input buffer can't be aliased with the output. */
WARN_ON(1);
return -EINVAL;
}
rc = efx_mcdi_rpc_start(efx, cmd, inbuf, inlen);
if (rc) {
if (outlen_actual)
*outlen_actual = 0;
if (rc)
return rc;
rc = _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen,
outlen_actual, quiet, &proxy_handle, raw_rc);
if (proxy_handle) {
/* Handle proxy authorisation. This allows approval of MCDI
* operations to be delegated to the admin function, allowing
* fine control over (eg) multicast subscriptions.
*/
struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
netif_dbg(efx, hw, efx->net_dev,
"MCDI waiting for proxy auth %d\n",
proxy_handle);
rc = efx_mcdi_proxy_wait(efx, proxy_handle, quiet);
if (rc == 0) {
netif_dbg(efx, hw, efx->net_dev,
"MCDI proxy retry %d\n", proxy_handle);
/* We now retry the original request. */
mcdi->state = MCDI_STATE_RUNNING_SYNC;
efx_mcdi_send_request(efx, cmd, inbuf, inlen);
rc = _efx_mcdi_rpc_finish(efx, cmd, inlen,
outbuf, outlen, outlen_actual,
quiet, NULL, raw_rc);
} else {
netif_printk(efx, hw,
rc == -EPERM ? KERN_DEBUG : KERN_ERR,
efx->net_dev,
"MC command 0x%x failed after proxy auth rc=%d\n",
cmd, rc);
if (rc == -EINTR || rc == -EIO)
efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE);
efx_mcdi_release(mcdi);
}
return _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen,
outlen_actual, quiet);
}
return rc;
}
static int _efx_mcdi_rpc_evb_retry(struct efx_nic *efx, unsigned cmd,
const efx_dword_t *inbuf, size_t inlen,
efx_dword_t *outbuf, size_t outlen,
size_t *outlen_actual, bool quiet)
{
int raw_rc = 0;
int rc;
rc = _efx_mcdi_rpc(efx, cmd, inbuf, inlen,
outbuf, outlen, outlen_actual, true, &raw_rc);
if ((rc == -EPROTO) && (raw_rc == MC_CMD_ERR_NO_EVB_PORT) &&
efx->type->is_vf) {
/* If the EVB port isn't available within a VF this may
* mean the PF is still bringing the switch up. We should
* retry our request shortly.
*/
unsigned long abort_time = jiffies + MCDI_RPC_TIMEOUT;
unsigned int delay_us = 10000;
netif_dbg(efx, hw, efx->net_dev,
"%s: NO_EVB_PORT; will retry request\n",
__func__);
do {
usleep_range(delay_us, delay_us + 10000);
rc = _efx_mcdi_rpc(efx, cmd, inbuf, inlen,
outbuf, outlen, outlen_actual,
true, &raw_rc);
if (delay_us < 100000)
delay_us <<= 1;
} while ((rc == -EPROTO) &&
(raw_rc == MC_CMD_ERR_NO_EVB_PORT) &&
time_before(jiffies, abort_time));
}
if (rc && !quiet && !(cmd == MC_CMD_REBOOT && rc == -EIO))
efx_mcdi_display_error(efx, cmd, inlen,
outbuf, outlen, rc);
return rc;
}
/**
* efx_mcdi_rpc - Issue an MCDI command and wait for completion
* @efx: NIC through which to issue the command
* @cmd: Command type number
* @inbuf: Command parameters
* @inlen: Length of command parameters, in bytes. Must be a multiple
* of 4 and no greater than %MCDI_CTL_SDU_LEN_MAX_V1.
* @outbuf: Response buffer. May be %NULL if @outlen is 0.
* @outlen: Length of response buffer, in bytes. If the actual
* response is longer than @outlen & ~3, it will be truncated
* to that length.
* @outlen_actual: Pointer through which to return the actual response
* length. May be %NULL if this is not needed.
*
* This function may sleep and therefore must be called in an appropriate
* context.
*
* Return: A negative error code, or zero if successful. The error
* code may come from the MCDI response or may indicate a failure
* to communicate with the MC. In the former case, the response
* will still be copied to @outbuf and *@outlen_actual will be
* set accordingly. In the latter case, *@outlen_actual will be
* set to zero.
*/
int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd,
const efx_dword_t *inbuf, size_t inlen,
efx_dword_t *outbuf, size_t outlen,
size_t *outlen_actual)
{
return _efx_mcdi_rpc(efx, cmd, inbuf, inlen, outbuf, outlen,
return _efx_mcdi_rpc_evb_retry(efx, cmd, inbuf, inlen, outbuf, outlen,
outlen_actual, false);
}
......@@ -744,7 +939,7 @@ int efx_mcdi_rpc_quiet(struct efx_nic *efx, unsigned cmd,
efx_dword_t *outbuf, size_t outlen,
size_t *outlen_actual)
{
return _efx_mcdi_rpc(efx, cmd, inbuf, inlen, outbuf, outlen,
return _efx_mcdi_rpc_evb_retry(efx, cmd, inbuf, inlen, outbuf, outlen,
outlen_actual, true);
}
......@@ -866,7 +1061,7 @@ int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
size_t *outlen_actual)
{
return _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen,
outlen_actual, false);
outlen_actual, false, NULL, NULL);
}
int efx_mcdi_rpc_finish_quiet(struct efx_nic *efx, unsigned cmd, size_t inlen,
......@@ -874,7 +1069,7 @@ int efx_mcdi_rpc_finish_quiet(struct efx_nic *efx, unsigned cmd, size_t inlen,
size_t *outlen_actual)
{
return _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen,
outlen_actual, true);
outlen_actual, true, NULL, NULL);
}
void efx_mcdi_display_error(struct efx_nic *efx, unsigned cmd,
......@@ -887,9 +1082,10 @@ void efx_mcdi_display_error(struct efx_nic *efx, unsigned cmd,
code = MCDI_DWORD(outbuf, ERR_CODE);
if (outlen >= MC_CMD_ERR_ARG_OFST + 4)
err_arg = MCDI_DWORD(outbuf, ERR_ARG);
netif_err(efx, hw, efx->net_dev,
"MC command 0x%x inlen %d failed rc=%d (raw=%d) arg=%d\n",
cmd, (int)inlen, rc, code, err_arg);
netif_printk(efx, hw, rc == -EPERM ? KERN_DEBUG : KERN_ERR,
efx->net_dev,
"MC command 0x%x inlen %zu failed rc=%d (raw=%d) arg=%d\n",
cmd, inlen, rc, code, err_arg);
}
/* Switch to polled MCDI completions. This can be called in various
......@@ -1014,8 +1210,13 @@ static void efx_mcdi_ev_death(struct efx_nic *efx, int rc)
* receiving a REBOOT event after posting the MCDI
* request. Did the mc reboot before or after the copyout? The
* best we can do always is just return failure.
*
* If there is an outstanding proxy response expected it is not going
* to arrive. We should thus abort it.
*/
spin_lock(&mcdi->iface_lock);
efx_mcdi_proxy_abort(mcdi);
if (efx_mcdi_complete_sync(mcdi)) {
if (mcdi->mode == MCDI_MODE_EVENTS) {
mcdi->resprc = rc;
......@@ -1063,6 +1264,8 @@ static void efx_mcdi_ev_bist(struct efx_nic *efx)
spin_lock(&mcdi->iface_lock);
efx->mc_bist_for_other_fn = true;
efx_mcdi_proxy_abort(mcdi);
if (efx_mcdi_complete_sync(mcdi)) {
if (mcdi->mode == MCDI_MODE_EVENTS) {
mcdi->resprc = -EIO;
......@@ -1171,6 +1374,11 @@ void efx_mcdi_process_event(struct efx_channel *channel,
EFX_QWORD_VAL(*event));
efx_schedule_reset(efx, RESET_TYPE_DMA_ERROR);
break;
case MCDI_EVENT_CODE_PROXY_RESPONSE:
efx_mcdi_ev_proxy_response(efx,
MCDI_EVENT_FIELD(*event, PROXY_RESPONSE_HANDLE),
MCDI_EVENT_FIELD(*event, PROXY_RESPONSE_RC));
break;
default:
netif_err(efx, hw, efx->net_dev, "Unknown MCDI event 0x%x\n",
code);
......
......@@ -17,6 +17,8 @@
* @MCDI_STATE_RUNNING_SYNC: There is a synchronous MCDI request pending.
* Only the thread that moved into this state is allowed to move out of it.
* @MCDI_STATE_RUNNING_ASYNC: There is an asynchronous MCDI request pending.
* @MCDI_STATE_PROXY_WAIT: An MCDI request has completed with a response that
* indicates we must wait for a proxy try again message.
* @MCDI_STATE_COMPLETED: An MCDI request has completed, but the owning thread
* has not yet consumed the result. For all other threads, equivalent to
* %MCDI_STATE_RUNNING.
......@@ -25,6 +27,7 @@ enum efx_mcdi_state {
MCDI_STATE_QUIESCENT,
MCDI_STATE_RUNNING_SYNC,
MCDI_STATE_RUNNING_ASYNC,
MCDI_STATE_PROXY_WAIT,
MCDI_STATE_COMPLETED,
};
......@@ -60,6 +63,9 @@ enum efx_mcdi_mode {
* @async_timer: Timer for asynchronous request timeout
* @logging_buffer: buffer that may be used to build MCDI tracing messages
* @logging_enabled: whether to trace MCDI
* @proxy_rx_handle: Most recently received proxy authorisation handle
* @proxy_rx_status: Status of most recent proxy authorisation
* @proxy_rx_wq: Wait queue for updates to proxy_rx_handle
*/
struct efx_mcdi_iface {
struct efx_nic *efx;
......@@ -71,6 +77,7 @@ struct efx_mcdi_iface {
unsigned int credits;
unsigned int seqno;
int resprc;
int resprc_raw;
size_t resp_hdr_len;
size_t resp_data_len;
spinlock_t async_lock;
......@@ -80,6 +87,9 @@ struct efx_mcdi_iface {
char *logging_buffer;
bool logging_enabled;
#endif
unsigned int proxy_rx_handle;
int proxy_rx_status;
wait_queue_head_t proxy_rx_wq;
};
struct efx_mcdi_mon {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment