switch (event) { case VIRTCHNL2_EVENT_LINK_CHANGE:
idpf_handle_event_link(adapter, v2e); return; default:
dev_err(&adapter->pdev->dev, "Unknown event %d from PF\n", event); break;
}
}
/** * idpf_mb_clean - Reclaim the send mailbox queue entries * @adapter: Driver specific private structure * * Reclaim the send mailbox queue entries to be used to send further messages * * Returns 0 on success, negative on failure
*/ staticint idpf_mb_clean(struct idpf_adapter *adapter)
{
u16 i, num_q_msg = IDPF_DFLT_MBX_Q_LEN; struct idpf_ctlq_msg **q_msg; struct idpf_dma_mem *dma_mem; int err;
err = idpf_ctlq_clean_sq(adapter->hw.asq, &num_q_msg, q_msg); if (err) goto err_kfree;
for (i = 0; i < num_q_msg; i++) { if (!q_msg[i]) continue;
dma_mem = q_msg[i]->ctx.indirect.payload; if (dma_mem)
dma_free_coherent(&adapter->pdev->dev, dma_mem->size,
dma_mem->va, dma_mem->pa);
kfree(q_msg[i]);
kfree(dma_mem);
}
err_kfree:
kfree(q_msg);
return err;
}
#if IS_ENABLED(CONFIG_PTP_1588_CLOCK) /** * idpf_ptp_is_mb_msg - Check if the message is PTP-related * @op: virtchnl opcode * * Return: true if msg is PTP-related, false otherwise.
*/ staticbool idpf_ptp_is_mb_msg(u32 op)
{ switch (op) { case VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME: case VIRTCHNL2_OP_PTP_GET_CROSS_TIME: case VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME: case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE: case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME: case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS: case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP: returntrue; default: returnfalse;
}
}
/** * idpf_prepare_ptp_mb_msg - Prepare PTP related message * * @adapter: Driver specific private structure * @op: virtchnl opcode * @ctlq_msg: Corresponding control queue message
*/ staticvoid idpf_prepare_ptp_mb_msg(struct idpf_adapter *adapter, u32 op, struct idpf_ctlq_msg *ctlq_msg)
{ /* If the message is PTP-related and the secondary mailbox is available, * send the message through the secondary mailbox.
*/ if (!idpf_ptp_is_mb_msg(op) || !adapter->ptp->secondary_mbx.valid) return;
/** * idpf_send_mb_msg - Send message over mailbox * @adapter: Driver specific private structure * @op: virtchnl opcode * @msg_size: size of the payload * @msg: pointer to buffer holding the payload * @cookie: unique SW generated cookie per message * * Will prepare the control queue message and initiates the send api * * Returns 0 on success, negative on failure
*/ int idpf_send_mb_msg(struct idpf_adapter *adapter, u32 op,
u16 msg_size, u8 *msg, u16 cookie)
{ struct idpf_ctlq_msg *ctlq_msg; struct idpf_dma_mem *dma_mem; int err;
/* If we are here and a reset is detected nothing much can be * done. This thread should silently abort and expected to * be corrected with a new run either by user or driver * flows after reset
*/ if (idpf_is_reset_detected(adapter)) return 0;
err = idpf_mb_clean(adapter); if (err) return err;
ctlq_msg = kzalloc(sizeof(*ctlq_msg), GFP_ATOMIC); if (!ctlq_msg) return -ENOMEM;
/* It's possible we're just sending an opcode but no buffer */ if (msg && msg_size)
memcpy(dma_mem->va, msg, msg_size);
ctlq_msg->ctx.indirect.payload = dma_mem;
ctlq_msg->ctx.sw_cookie.data = cookie;
err = idpf_ctlq_send(&adapter->hw, adapter->hw.asq, 1, ctlq_msg); if (err) goto send_error;
/* API for virtchnl "transaction" support ("xn" for short). * * We are reusing the completion lock to serialize the accesses to the * transaction state for simplicity, but it could be its own separate synchro * as well. For now, this API is only used from within a workqueue context; * raw_spin_lock() is enough.
*/ /** * idpf_vc_xn_lock - Request exclusive access to vc transaction * @xn: struct idpf_vc_xn* to access
*/ #define idpf_vc_xn_lock(xn) \
raw_spin_lock(&(xn)->completed.wait.lock)
/** * idpf_vc_xn_shutdown - Uninitialize virtchnl transaction object * @vcxn_mngr: pointer to vc transaction manager struct * * All waiting threads will be woken-up and their transaction aborted. Further * operations on that object will fail.
*/ void idpf_vc_xn_shutdown(struct idpf_vc_xn_manager *vcxn_mngr)
{ int i;
/** * idpf_vc_xn_pop_free - Pop a free transaction from free list * @vcxn_mngr: transaction manager to pop from * * Returns NULL if no free transactions
*/ static struct idpf_vc_xn *idpf_vc_xn_pop_free(struct idpf_vc_xn_manager *vcxn_mngr)
{ struct idpf_vc_xn *xn = NULL; unsignedlong free_idx;
/** * idpf_vc_xn_push_free - Push a free transaction to free list * @vcxn_mngr: transaction manager to push to * @xn: transaction to push
*/ staticvoid idpf_vc_xn_push_free(struct idpf_vc_xn_manager *vcxn_mngr, struct idpf_vc_xn *xn)
{
idpf_vc_xn_release_bufs(xn);
set_bit(xn->idx, vcxn_mngr->free_xn_bm);
}
/** * idpf_vc_xn_exec - Perform a send/recv virtchnl transaction * @adapter: driver specific private structure with vcxn_mngr * @params: parameters for this particular transaction including * -vc_op: virtchannel operation to send * -send_buf: kvec iov for send buf and len * -recv_buf: kvec iov for recv buf and len (ignored if NULL) * -timeout_ms: timeout waiting for a reply (milliseconds) * -async: don't wait for message reply, will lose caller context * -async_handler: callback to handle async replies * * @returns >= 0 for success, the size of the initial reply (may or may not be * >= @recv_buf.iov_len, but we never overflow @@recv_buf_iov_base). < 0 for * error.
*/
ssize_t idpf_vc_xn_exec(struct idpf_adapter *adapter, conststruct idpf_vc_xn_params *params)
{ conststruct kvec *send_buf = ¶ms->send_buf; struct idpf_vc_xn *xn;
ssize_t retval;
u16 cookie;
xn = idpf_vc_xn_pop_free(adapter->vcxn_mngr); /* no free transactions available */ if (!xn) return -ENOSPC;
idpf_vc_xn_lock(xn); if (xn->state == IDPF_VC_XN_SHUTDOWN) {
retval = -ENXIO; goto only_unlock;
} elseif (xn->state != IDPF_VC_XN_IDLE) { /* We're just going to clobber this transaction even though * it's not IDLE. If we don't reuse it we could theoretically * eventually leak all the free transactions and not be able to * send any messages. At least this way we make an attempt to * remain functional even though something really bad is * happening that's corrupting what was supposed to be free * transactions.
*/
WARN_ONCE(1, "There should only be idle transactions in free list (idx %d op %d)\n",
xn->idx, xn->vc_op);
}
/* No need to check the return value; we check the final state of the * transaction below. It's possible the transaction actually gets more * timeout than specified if we get preempted here but after * wait_for_completion_timeout returns. This should be non-issue * however.
*/
idpf_vc_xn_lock(xn); switch (xn->state) { case IDPF_VC_XN_SHUTDOWN:
retval = -ENXIO; goto only_unlock; case IDPF_VC_XN_WAITING:
dev_notice_ratelimited(&adapter->pdev->dev, "Transaction timed-out (op:%d cookie:%04x vc_op:%d salt:%02x timeout:%dms)\n",
params->vc_op, cookie, xn->vc_op,
xn->salt, params->timeout_ms);
retval = -ETIME; break; case IDPF_VC_XN_COMPLETED_SUCCESS:
retval = xn->reply_sz; break; case IDPF_VC_XN_COMPLETED_FAILED:
dev_notice_ratelimited(&adapter->pdev->dev, "Transaction failed (op %d)\n",
params->vc_op);
retval = -EIO; break; default: /* Invalid state. */
WARN_ON_ONCE(1);
retval = -EIO; break;
}
release_and_unlock:
idpf_vc_xn_push_free(adapter->vcxn_mngr, xn); /* If we receive a VC reply after here, it will be dropped. */
only_unlock:
idpf_vc_xn_unlock(xn);
return retval;
}
/** * idpf_vc_xn_forward_async - Handle async reply receives * @adapter: private data struct * @xn: transaction to handle * @ctlq_msg: corresponding ctlq_msg * * For async sends we're going to lose the caller's context so, if an * async_handler was provided, it can deal with the reply, otherwise we'll just * check and report if there is an error.
*/ staticint
idpf_vc_xn_forward_async(struct idpf_adapter *adapter, struct idpf_vc_xn *xn, conststruct idpf_ctlq_msg *ctlq_msg)
{ int err = 0;
if (ctlq_msg->cookie.mbx.chnl_opcode != xn->vc_op) {
dev_err_ratelimited(&adapter->pdev->dev, "Async message opcode does not match transaction opcode (msg: %d) (xn: %d)\n",
ctlq_msg->cookie.mbx.chnl_opcode, xn->vc_op);
xn->reply_sz = 0;
err = -EINVAL; goto release_bufs;
}
/** * idpf_vc_xn_forward_reply - copy a reply back to receiving thread * @adapter: driver specific private structure with vcxn_mngr * @ctlq_msg: controlq message to send back to receiving thread
*/ staticint
idpf_vc_xn_forward_reply(struct idpf_adapter *adapter, conststruct idpf_ctlq_msg *ctlq_msg)
{ constvoid *payload = NULL;
size_t payload_size = 0; struct idpf_vc_xn *xn;
u16 msg_info; int err = 0;
u16 xn_idx;
u16 salt;
msg_info = ctlq_msg->ctx.sw_cookie.data;
xn_idx = FIELD_GET(IDPF_VC_XN_IDX_M, msg_info); if (xn_idx >= ARRAY_SIZE(adapter->vcxn_mngr->ring)) {
dev_err_ratelimited(&adapter->pdev->dev, "Out of bounds cookie received: %02x\n",
xn_idx); return -EINVAL;
}
xn = &adapter->vcxn_mngr->ring[xn_idx];
idpf_vc_xn_lock(xn);
salt = FIELD_GET(IDPF_VC_XN_SALT_M, msg_info); if (xn->salt != salt) {
dev_err_ratelimited(&adapter->pdev->dev, "Transaction salt does not match (exp:%d@%02x(%d) != got:%d@%02x)\n",
xn->vc_op, xn->salt, xn->state,
ctlq_msg->cookie.mbx.chnl_opcode, salt);
idpf_vc_xn_unlock(xn); return -EINVAL;
}
switch (xn->state) { case IDPF_VC_XN_WAITING: /* success */ break; case IDPF_VC_XN_IDLE:
dev_err_ratelimited(&adapter->pdev->dev, "Unexpected or belated VC reply (op %d)\n",
ctlq_msg->cookie.mbx.chnl_opcode);
err = -EINVAL; goto out_unlock; case IDPF_VC_XN_SHUTDOWN: /* ENXIO is a bit special here as the recv msg loop uses that * know if it should stop trying to clean the ring if we lost * the virtchnl. We need to stop playing with registers and * yield.
*/
err = -ENXIO; goto out_unlock; case IDPF_VC_XN_ASYNC:
err = idpf_vc_xn_forward_async(adapter, xn, ctlq_msg);
idpf_vc_xn_unlock(xn); return err; default:
dev_err_ratelimited(&adapter->pdev->dev, "Overwriting VC reply (op %d)\n",
ctlq_msg->cookie.mbx.chnl_opcode);
err = -EBUSY; goto out_unlock;
}
if (ctlq_msg->cookie.mbx.chnl_opcode != xn->vc_op) {
dev_err_ratelimited(&adapter->pdev->dev, "Message opcode does not match transaction opcode (msg: %d) (xn: %d)\n",
ctlq_msg->cookie.mbx.chnl_opcode, xn->vc_op);
xn->reply_sz = 0;
xn->state = IDPF_VC_XN_COMPLETED_FAILED;
err = -EINVAL; goto out_unlock;
}
out_unlock:
idpf_vc_xn_unlock(xn); /* we _cannot_ hold lock while calling complete */
complete(&xn->completed);
return err;
}
/** * idpf_recv_mb_msg - Receive message over mailbox * @adapter: Driver specific private structure * * Will receive control queue message and posts the receive buffer. Returns 0 * on success and negative on failure.
*/ int idpf_recv_mb_msg(struct idpf_adapter *adapter)
{ struct idpf_ctlq_msg ctlq_msg; struct idpf_dma_mem *dma_mem; int post_err, err;
u16 num_recv;
while (1) { /* This will get <= num_recv messages and output how many * actually received on num_recv.
*/
num_recv = 1;
err = idpf_ctlq_recv(adapter->hw.arq, &num_recv, &ctlq_msg); if (err || !num_recv) break;
/* If post failed clear the only buffer we supplied */ if (post_err) { if (dma_mem)
dma_free_coherent(&adapter->pdev->dev,
dma_mem->size, dma_mem->va,
dma_mem->pa); break;
}
/* virtchnl trying to shutdown, stop cleaning */ if (err == -ENXIO) break;
}
return err;
}
/** * idpf_wait_for_marker_event - wait for software marker response * @vport: virtual port data structure * * Returns 0 success, negative on failure.
**/ staticint idpf_wait_for_marker_event(struct idpf_vport *vport)
{ int event; int i;
for (i = 0; i < vport->num_txq; i++)
idpf_queue_set(SW_MARKER, vport->txqs[i]);
reply_sz = idpf_vc_xn_exec(adapter, &xn_params); if (reply_sz < 0) return reply_sz; if (reply_sz < sizeof(vvi)) return -EIO;
major = le32_to_cpu(vvi.major);
minor = le32_to_cpu(vvi.minor);
if (major > IDPF_VIRTCHNL_VERSION_MAJOR) {
dev_warn(&adapter->pdev->dev, "Virtchnl major version greater than supported\n"); return -EINVAL;
}
if (major == IDPF_VIRTCHNL_VERSION_MAJOR &&
minor > IDPF_VIRTCHNL_VERSION_MINOR)
dev_warn(&adapter->pdev->dev, "Virtchnl minor version didn't match\n");
/* If we have a mismatch, resend version to update receiver on what * version we will use.
*/ if (!adapter->virt_ver_maj &&
major != IDPF_VIRTCHNL_VERSION_MAJOR &&
minor != IDPF_VIRTCHNL_VERSION_MINOR)
err = -EAGAIN;
for (int i = 0; i < num_regions; i++) {
hw->lan_regs[i].addr_len =
le64_to_cpu(rcvd_regions->mem_reg[i].size);
hw->lan_regs[i].addr_start =
le64_to_cpu(rcvd_regions->mem_reg[i].start_offset);
}
hw->num_lan_regs = num_regions;
return err;
}
/** * idpf_calc_remaining_mmio_regs - calculate MMIO regions outside mbx and rstat * @adapter: Driver specific private structure * * Called when idpf_send_get_lan_memory_regions is not supported. This will * calculate the offsets and sizes for the regions before, in between, and * after the mailbox and rstat MMIO mappings. * * Return: 0 on success or error code on failure.
*/ staticint idpf_calc_remaining_mmio_regs(struct idpf_adapter *adapter)
{ struct resource *rstat_reg = &adapter->dev_ops.static_reg_info[1]; struct resource *mbx_reg = &adapter->dev_ops.static_reg_info[0]; struct idpf_hw *hw = &adapter->hw;
/** * idpf_get_reg_intr_vecs - Get vector queue register offset * @vport: virtual port structure * @reg_vals: Register offsets to store in * * Returns number of registers that got populated
*/ int idpf_get_reg_intr_vecs(struct idpf_vport *vport, struct idpf_vec_regs *reg_vals)
{ struct virtchnl2_vector_chunks *chunks; struct idpf_vec_regs reg_val;
u16 num_vchunks, num_vec; int num_regs = 0, i, j;
/** * idpf_vport_get_q_reg - Get the queue registers for the vport * @reg_vals: register values needing to be set * @num_regs: amount we expect to fill * @q_type: queue model * @chunks: queue regs received over mailbox * * This function parses the queue register offsets from the queue register * chunk information, with a specific queue type and stores it into the array * passed as an argument. It returns the actual number of queue registers that * are filled.
*/ staticint idpf_vport_get_q_reg(u32 *reg_vals, int num_regs, u32 q_type, struct virtchnl2_queue_reg_chunks *chunks)
{
u16 num_chunks = le16_to_cpu(chunks->num_chunks); int reg_filled = 0, i;
u32 reg_val;
while (num_chunks--) { struct virtchnl2_queue_reg_chunk *chunk;
u16 num_q;
chunk = &chunks->chunks[num_chunks]; if (le32_to_cpu(chunk->type) != q_type) continue;
num_q = le32_to_cpu(chunk->num_queues);
reg_val = le64_to_cpu(chunk->qtail_reg_start); for (i = 0; i < num_q && reg_filled < num_regs ; i++) {
reg_vals[reg_filled++] = reg_val;
reg_val += le32_to_cpu(chunk->qtail_reg_spacing);
}
}
return reg_filled;
}
/** * __idpf_queue_reg_init - initialize queue registers * @vport: virtual port structure * @reg_vals: registers we are initializing * @num_regs: how many registers there are in total * @q_type: queue model * * Return number of queues that are initialized
*/ staticint __idpf_queue_reg_init(struct idpf_vport *vport, u32 *reg_vals, int num_regs, u32 q_type)
{ struct idpf_adapter *adapter = vport->adapter; int i, j, k = 0;
switch (q_type) { case VIRTCHNL2_QUEUE_TYPE_TX: for (i = 0; i < vport->num_txq_grp; i++) { struct idpf_txq_group *tx_qgrp = &vport->txq_grps[i];
for (j = 0; j < tx_qgrp->num_txq && k < num_regs; j++, k++)
tx_qgrp->txqs[j]->tail =
idpf_get_reg_addr(adapter, reg_vals[k]);
} break; case VIRTCHNL2_QUEUE_TYPE_RX: for (i = 0; i < vport->num_rxq_grp; i++) { struct idpf_rxq_group *rx_qgrp = &vport->rxq_grps[i];
u16 num_rxq = rx_qgrp->singleq.num_rxq;
for (j = 0; j < num_rxq && k < num_regs; j++, k++) { struct idpf_rx_queue *q;
q = rx_qgrp->singleq.rxqs[j];
q->tail = idpf_get_reg_addr(adapter,
reg_vals[k]);
}
} break; case VIRTCHNL2_QUEUE_TYPE_RX_BUFFER: for (i = 0; i < vport->num_rxq_grp; i++) { struct idpf_rxq_group *rx_qgrp = &vport->rxq_grps[i];
u8 num_bufqs = vport->num_bufqs_per_qgrp;
for (j = 0; j < num_bufqs && k < num_regs; j++, k++) { struct idpf_buf_queue *q;
/** * idpf_queue_reg_init - initialize queue registers * @vport: virtual port structure * * Return 0 on success, negative on failure
*/ int idpf_queue_reg_init(struct idpf_vport *vport)
{ struct virtchnl2_create_vport *vport_params; struct virtchnl2_queue_reg_chunks *chunks; struct idpf_vport_config *vport_config;
u16 vport_idx = vport->idx; int num_regs, ret = 0;
u32 *reg_vals;
/* We may never deal with more than 256 same type of queues */
reg_vals = kzalloc(sizeof(void *) * IDPF_LARGE_MAX_Q, GFP_KERNEL); if (!reg_vals) return -ENOMEM;
if (idpf_is_queue_model_split(vport->rxq_model)) { if (!(rx_desc_ids & VIRTCHNL2_RXDID_2_FLEX_SPLITQ_M)) {
dev_info(&adapter->pdev->dev, "Minimum RX descriptor support not provided, using the default\n");
vport_msg->rx_desc_ids = cpu_to_le64(VIRTCHNL2_RXDID_2_FLEX_SPLITQ_M);
}
} else { if (!(rx_desc_ids & VIRTCHNL2_RXDID_2_FLEX_SQ_NIC_M))
vport->base_rxd = true;
}
if (!idpf_is_queue_model_split(vport->txq_model)) return 0;
if ((tx_desc_ids & MIN_SUPPORT_TXDID) != MIN_SUPPORT_TXDID) {
dev_info(&adapter->pdev->dev, "Minimum TX descriptor support not provided, using the default\n");
vport_msg->tx_desc_ids = cpu_to_le64(MIN_SUPPORT_TXDID);
}
return 0;
}
/** * idpf_send_destroy_vport_msg - Send virtchnl destroy vport message * @vport: virtual port data structure * * Send virtchnl destroy vport message. Returns 0 on success, negative on * failure.
*/ int idpf_send_destroy_vport_msg(struct idpf_vport *vport)
{ struct idpf_vc_xn_params xn_params = {}; struct virtchnl2_vport v_id;
ssize_t reply_sz;
/* Populate the queue info buffer with all queue context info */ for (i = 0; i < vport->num_txq_grp; i++) { struct idpf_txq_group *tx_qgrp = &vport->txq_grps[i]; int j, sched_mode;
/* Make sure accounting agrees */ if (k != totqs) return -EINVAL;
/* Chunk up the queue contexts into multiple messages to avoid * sending a control queue message buffer that is too large
*/
config_sz = sizeof(struct virtchnl2_config_tx_queues);
chunk_sz = sizeof(struct virtchnl2_txq_info);
/* Populate the queue info buffer with all queue context info */ for (i = 0; i < vport->num_rxq_grp; i++) { struct idpf_rxq_group *rx_qgrp = &vport->rxq_grps[i];
u16 num_rxq; int j;
if (!idpf_is_queue_model_split(vport->rxq_model)) goto setup_rxqs;
/* In splitq mode, RXQ buffer size should be * set to that of the first buffer queue * associated with this RXQ.
*/
rxq->rx_buf_size = sets[0].bufq.rx_buf_size;
/* Make sure accounting agrees */ if (k != totqs) return -EINVAL;
/* Chunk up the queue contexts into multiple messages to avoid * sending a control queue message buffer that is too large
*/
config_sz = sizeof(struct virtchnl2_config_rx_queues);
chunk_sz = sizeof(struct virtchnl2_rxq_info);
send_msg: /* Chunk up the queue info into multiple messages */
config_sz = sizeof(struct virtchnl2_del_ena_dis_queues);
chunk_sz = sizeof(struct virtchnl2_queue_chunk);
if (idpf_is_queue_model_split(vport->txq_model)) { if (vport->num_rxq != k - vport->num_complq) return -EINVAL;
} else { if (vport->num_rxq != k - vport->num_txq) return -EINVAL;
}
/* Chunk up the vector info into multiple messages */
config_sz = sizeof(struct virtchnl2_queue_vector_maps);
chunk_sz = sizeof(struct virtchnl2_queue_vector);
/** * idpf_send_enable_queues_msg - send enable queues virtchnl message * @vport: Virtual port private data structure * * Will send enable queues virtchnl message. Returns 0 on success, negative on * failure.
*/ int idpf_send_enable_queues_msg(struct idpf_vport *vport)
{ return idpf_send_ena_dis_queues_msg(vport, true);
}
/** * idpf_send_disable_queues_msg - send disable queues virtchnl message * @vport: Virtual port private data structure * * Will send disable queues virtchnl message. Returns 0 on success, negative * on failure.
*/ int idpf_send_disable_queues_msg(struct idpf_vport *vport)
{ int err, i;
err = idpf_send_ena_dis_queues_msg(vport, false); if (err) return err;
/* switch to poll mode as interrupts will be disabled after disable * queues virtchnl message is sent
*/ for (i = 0; i < vport->num_txq; i++)
idpf_queue_set(POLL_MODE, vport->txqs[i]);
/* schedule the napi to receive all the marker packets */
local_bh_disable(); for (i = 0; i < vport->num_q_vectors; i++)
napi_schedule(&vport->q_vectors[i].napi);
local_bh_enable();
return idpf_wait_for_marker_event(vport);
}
/** * idpf_convert_reg_to_queue_chunks - Copy queue chunk information to the right * structure * @dchunks: Destination chunks to store data to * @schunks: Source chunks to copy data from * @num_chunks: number of chunks to copy
*/ staticvoid idpf_convert_reg_to_queue_chunks(struct virtchnl2_queue_chunk *dchunks, struct virtchnl2_queue_reg_chunk *schunks,
u16 num_chunks)
{
u16 i;
for (i = 0; i < num_chunks; i++) {
dchunks[i].type = schunks[i].type;
dchunks[i].start_queue_id = schunks[i].start_queue_id;
dchunks[i].num_queues = schunks[i].num_queues;
}
}
/** * idpf_send_delete_queues_msg - send delete queues virtchnl message * @vport: Virtual port private data structure * * Will send delete queues virtchnl message. Return 0 on success, negative on * failure.
*/ int idpf_send_delete_queues_msg(struct idpf_vport *vport)
{ struct virtchnl2_del_ena_dis_queues *eq __free(kfree) = NULL; struct virtchnl2_create_vport *vport_params; struct virtchnl2_queue_reg_chunks *chunks; struct idpf_vc_xn_params xn_params = {}; struct idpf_vport_config *vport_config;
u16 vport_idx = vport->idx;
ssize_t reply_sz;
u16 num_chunks; int buf_size;
/** * idpf_send_config_queues_msg - Send config queues virtchnl message * @vport: Virtual port private data structure * * Will send config queues virtchnl message. Returns 0 on success, negative on * failure.
*/ int idpf_send_config_queues_msg(struct idpf_vport *vport)
{ int err;
err = idpf_send_config_tx_queues_msg(vport); if (err) return err;
return idpf_send_config_rx_queues_msg(vport);
}
/** * idpf_send_add_queues_msg - Send virtchnl add queues message * @vport: Virtual port private data structure * @num_tx_q: number of transmit queues * @num_complq: number of transmit completion queues * @num_rx_q: number of receive queues * @num_rx_bufq: number of receive buffer queues * * Returns 0 on success, negative on failure. vport _MUST_ be const here as * we should not change any fields within vport itself in this function.
*/ int idpf_send_add_queues_msg(conststruct idpf_vport *vport, u16 num_tx_q,
u16 num_complq, u16 num_rx_q, u16 num_rx_bufq)
{ struct virtchnl2_add_queues *vc_msg __free(kfree) = NULL; struct idpf_vc_xn_params xn_params = {}; struct idpf_vport_config *vport_config; struct virtchnl2_add_queues aq = {};
u16 vport_idx = vport->idx;
ssize_t reply_sz; int size;
vc_msg = kzalloc(IDPF_CTLQ_MAX_BUF_LEN, GFP_KERNEL); if (!vc_msg) return -ENOMEM;
/** * idpf_get_max_vfs - Get max number of vfs supported * @adapter: Driver specific private structure * * Returns max number of VFs
*/ staticint idpf_get_max_vfs(struct idpf_adapter *adapter)
{ return le16_to_cpu(adapter->caps.max_sriov_vfs);
}
/** * idpf_send_set_sriov_vfs_msg - Send virtchnl set sriov vfs message * @adapter: Driver specific private structure * @num_vfs: number of virtual functions to be created * * Returns 0 on success, negative on failure.
*/ int idpf_send_set_sriov_vfs_msg(struct idpf_adapter *adapter, u16 num_vfs)
{ struct virtchnl2_sriov_vfs_info svi = {}; struct idpf_vc_xn_params xn_params = {};
ssize_t reply_sz;
/** * idpf_send_get_set_rss_lut_msg - Send virtchnl get or set rss lut message * @vport: virtual port data structure * @get: flag to set or get rss look up table * * Returns 0 on success, negative on failure.
*/ int idpf_send_get_set_rss_lut_msg(struct idpf_vport *vport, bool get)
{ struct virtchnl2_rss_lut *recv_rl __free(kfree) = NULL; struct virtchnl2_rss_lut *rl __free(kfree) = NULL; struct idpf_vc_xn_params xn_params = {}; struct idpf_rss_data *rss_data; int buf_size, lut_buf_size;
ssize_t reply_sz; int i;
/** * idpf_send_get_set_rss_key_msg - Send virtchnl get or set rss key message * @vport: virtual port data structure * @get: flag to set or get rss look up table * * Returns 0 on success, negative on failure
*/ int idpf_send_get_set_rss_key_msg(struct idpf_vport *vport, bool get)
{ struct virtchnl2_rss_key *recv_rk __free(kfree) = NULL; struct virtchnl2_rss_key *rk __free(kfree) = NULL; struct idpf_vc_xn_params xn_params = {}; struct idpf_rss_data *rss_data;
ssize_t reply_sz; int i, buf_size;
u16 key_size;
ptype_offset += IDPF_GET_PTYPE_SIZE(ptype); if (ptype_offset > IDPF_CTLQ_MAX_BUF_LEN) return -EINVAL;
/* 0xFFFF indicates end of ptypes */ if (le16_to_cpu(ptype->ptype_id_10) ==
IDPF_INVALID_PTYPE_ID) goto out;
if (idpf_is_queue_model_split(vport->rxq_model))
k = le16_to_cpu(ptype->ptype_id_10); else
k = ptype->ptype_id_8;
for (j = 0; j < ptype->proto_id_count; j++) {
id = le16_to_cpu(ptype->proto_id[j]); switch (id) { case VIRTCHNL2_PROTO_HDR_GRE: if (pstate.tunnel_state ==
IDPF_PTYPE_TUNNEL_IP) {
ptype_lkup[k].tunnel_type =
LIBETH_RX_PT_TUNNEL_IP_GRENAT;
pstate.tunnel_state |=
IDPF_PTYPE_TUNNEL_IP_GRENAT;
} break; case VIRTCHNL2_PROTO_HDR_MAC:
ptype_lkup[k].outer_ip =
LIBETH_RX_PT_OUTER_L2; if (pstate.tunnel_state ==
IDPF_TUN_IP_GRE) {
ptype_lkup[k].tunnel_type =
LIBETH_RX_PT_TUNNEL_IP_GRENAT_MAC;
pstate.tunnel_state |=
IDPF_PTYPE_TUNNEL_IP_GRENAT_MAC;
} break; case VIRTCHNL2_PROTO_HDR_IPV4:
idpf_fill_ptype_lookup(&ptype_lkup[k],
&pstate, true, false); break; case VIRTCHNL2_PROTO_HDR_IPV6:
idpf_fill_ptype_lookup(&ptype_lkup[k],
&pstate, false, false); break; case VIRTCHNL2_PROTO_HDR_IPV4_FRAG:
idpf_fill_ptype_lookup(&ptype_lkup[k],
&pstate, true, true); break; case VIRTCHNL2_PROTO_HDR_IPV6_FRAG:
idpf_fill_ptype_lookup(&ptype_lkup[k],
&pstate, false, true); break; case VIRTCHNL2_PROTO_HDR_UDP:
ptype_lkup[k].inner_prot =
LIBETH_RX_PT_INNER_UDP; break; case VIRTCHNL2_PROTO_HDR_TCP:
ptype_lkup[k].inner_prot =
LIBETH_RX_PT_INNER_TCP; break; case VIRTCHNL2_PROTO_HDR_SCTP:
ptype_lkup[k].inner_prot =
LIBETH_RX_PT_INNER_SCTP; break; case VIRTCHNL2_PROTO_HDR_ICMP:
ptype_lkup[k].inner_prot =
LIBETH_RX_PT_INNER_ICMP; break; case VIRTCHNL2_PROTO_HDR_PAY:
ptype_lkup[k].payload_layer =
LIBETH_RX_PT_PAYLOAD_L2; break; case VIRTCHNL2_PROTO_HDR_ICMPV6: case VIRTCHNL2_PROTO_HDR_IPV6_EH: case VIRTCHNL2_PROTO_HDR_PRE_MAC: case VIRTCHNL2_PROTO_HDR_POST_MAC: case VIRTCHNL2_PROTO_HDR_ETHERTYPE: case VIRTCHNL2_PROTO_HDR_SVLAN: case VIRTCHNL2_PROTO_HDR_CVLAN: case VIRTCHNL2_PROTO_HDR_MPLS: case VIRTCHNL2_PROTO_HDR_MMPLS: case VIRTCHNL2_PROTO_HDR_PTP: case VIRTCHNL2_PROTO_HDR_CTRL: case VIRTCHNL2_PROTO_HDR_LLDP: case VIRTCHNL2_PROTO_HDR_ARP: case VIRTCHNL2_PROTO_HDR_ECP: case VIRTCHNL2_PROTO_HDR_EAPOL: case VIRTCHNL2_PROTO_HDR_PPPOD: case VIRTCHNL2_PROTO_HDR_PPPOE: case VIRTCHNL2_PROTO_HDR_IGMP: case VIRTCHNL2_PROTO_HDR_AH: case VIRTCHNL2_PROTO_HDR_ESP: case VIRTCHNL2_PROTO_HDR_IKE: case VIRTCHNL2_PROTO_HDR_NATT_KEEP: case VIRTCHNL2_PROTO_HDR_L2TPV2: case VIRTCHNL2_PROTO_HDR_L2TPV2_CONTROL: case VIRTCHNL2_PROTO_HDR_L2TPV3: case VIRTCHNL2_PROTO_HDR_GTP: case VIRTCHNL2_PROTO_HDR_GTP_EH: case VIRTCHNL2_PROTO_HDR_GTPCV2: case VIRTCHNL2_PROTO_HDR_GTPC_TEID: case VIRTCHNL2_PROTO_HDR_GTPU: case VIRTCHNL2_PROTO_HDR_GTPU_UL: case VIRTCHNL2_PROTO_HDR_GTPU_DL: case VIRTCHNL2_PROTO_HDR_ECPRI: case VIRTCHNL2_PROTO_HDR_VRRP: case VIRTCHNL2_PROTO_HDR_OSPF: case VIRTCHNL2_PROTO_HDR_TUN: case VIRTCHNL2_PROTO_HDR_NVGRE: case VIRTCHNL2_PROTO_HDR_VXLAN: case VIRTCHNL2_PROTO_HDR_VXLAN_GPE: case VIRTCHNL2_PROTO_HDR_GENEVE: case VIRTCHNL2_PROTO_HDR_NSH: case VIRTCHNL2_PROTO_HDR_QUIC: case VIRTCHNL2_PROTO_HDR_PFCP: case VIRTCHNL2_PROTO_HDR_PFCP_NODE: case VIRTCHNL2_PROTO_HDR_PFCP_SESSION: case VIRTCHNL2_PROTO_HDR_RTP: case VIRTCHNL2_PROTO_HDR_NO_PROTO: break; default: break;
}
}
/** * idpf_find_ctlq - Given a type and id, find ctlq info * @hw: hardware struct * @type: type of ctrlq to find * @id: ctlq id to find * * Returns pointer to found ctlq info struct, NULL otherwise.
*/ staticstruct idpf_ctlq_info *idpf_find_ctlq(struct idpf_hw *hw, enum idpf_ctlq_type type, int id)
{ struct idpf_ctlq_info *cq, *tmp;
list_for_each_entry_safe(cq, tmp, &hw->cq_list_head, cq_list) if (cq->q_id == id && cq->cq_type == type) return cq;
/** * idpf_deinit_dflt_mbx - Free up ctlqs setup * @adapter: Driver specific private data structure
*/ void idpf_deinit_dflt_mbx(struct idpf_adapter *adapter)
{ if (adapter->hw.arq && adapter->hw.asq) {
idpf_mb_clean(adapter);
idpf_ctlq_deinit(&adapter->hw);
}
adapter->hw.arq = NULL;
adapter->hw.asq = NULL;
}
/** * idpf_vport_params_buf_rel - Release memory for MailBox resources * @adapter: Driver specific private data structure * * Will release memory to hold the vport parameters received on MailBox
*/ staticvoid idpf_vport_params_buf_rel(struct idpf_adapter *adapter)
{
kfree(adapter->vport_params_recvd);
adapter->vport_params_recvd = NULL;
kfree(adapter->vport_params_reqd);
adapter->vport_params_reqd = NULL;
kfree(adapter->vport_ids);
adapter->vport_ids = NULL;
}
/** * idpf_vport_params_buf_alloc - Allocate memory for MailBox resources * @adapter: Driver specific private data structure * * Will alloc memory to hold the vport parameters received on MailBox
*/ staticint idpf_vport_params_buf_alloc(struct idpf_adapter *adapter)
{
u16 num_max_vports = idpf_get_max_vports(adapter);
adapter->vport_params_reqd = kcalloc(num_max_vports, sizeof(*adapter->vport_params_reqd),
GFP_KERNEL); if (!adapter->vport_params_reqd) return -ENOMEM;
adapter->vport_params_recvd = kcalloc(num_max_vports, sizeof(*adapter->vport_params_recvd),
GFP_KERNEL); if (!adapter->vport_params_recvd) goto err_mem;
adapter->vport_ids = kcalloc(num_max_vports, sizeof(u32), GFP_KERNEL); if (!adapter->vport_ids) goto err_mem;
if (adapter->vport_config) return 0;
adapter->vport_config = kcalloc(num_max_vports, sizeof(*adapter->vport_config),
GFP_KERNEL); if (!adapter->vport_config) goto err_mem;
return 0;
err_mem:
idpf_vport_params_buf_rel(adapter);
return -ENOMEM;
}
/** * idpf_vc_core_init - Initialize state machine and get driver specific * resources * @adapter: Driver specific private structure * * This function will initialize the state machine and request all necessary * resources required by the device driver. Once the state machine is * initialized, allocate memory to store vport specific information and also * requests required interrupts. * * Returns 0 on success, -EAGAIN function will get called again, * otherwise negative on failure.
*/ int idpf_vc_core_init(struct idpf_adapter *adapter)
{ int task_delay = 30;
u16 num_max_vports; int err = 0;
if (!adapter->vcxn_mngr) {
adapter->vcxn_mngr = kzalloc(sizeof(*adapter->vcxn_mngr), GFP_KERNEL); if (!adapter->vcxn_mngr) {
err = -ENOMEM; goto init_failed;
}
}
idpf_vc_xn_init(adapter->vcxn_mngr);
while (adapter->state != __IDPF_INIT_SW) { switch (adapter->state) { case __IDPF_VER_CHECK:
err = idpf_send_ver_msg(adapter); switch (err) { case 0: /* success, move state machine forward */
adapter->state = __IDPF_GET_CAPS;
fallthrough; case -EAGAIN: goto restart; default: /* Something bad happened, try again but only a * few times.
*/ goto init_failed;
} case __IDPF_GET_CAPS:
err = idpf_send_get_caps_msg(adapter); if (err) goto init_failed;
adapter->state = __IDPF_INIT_SW; break; default:
dev_err(&adapter->pdev->dev, "Device is in bad state: %d\n",
adapter->state);
err = -EINVAL; goto init_failed;
} break;
restart: /* Give enough time before proceeding further with * state machine
*/
msleep(task_delay);
}
if (idpf_is_cap_ena(adapter, IDPF_OTHER_CAPS, VIRTCHNL2_CAP_LAN_MEMORY_REGIONS)) {
err = idpf_send_get_lan_memory_regions(adapter); if (err) {
dev_err(&adapter->pdev->dev, "Failed to get LAN memory regions: %d\n",
err); return -EINVAL;
}
} else { /* Fallback to mapping the remaining regions of the entire BAR */
err = idpf_calc_remaining_mmio_regs(adapter); if (err) {
dev_err(&adapter->pdev->dev, "Failed to allocate BAR0 region(s): %d\n",
err); return -ENOMEM;
}
}
err = idpf_map_lan_mmio_regs(adapter); if (err) {
dev_err(&adapter->pdev->dev, "Failed to map BAR0 region(s): %d\n",
err); return -ENOMEM;
}
if (!adapter->netdevs) {
adapter->netdevs = kcalloc(num_max_vports, sizeof(struct net_device *),
GFP_KERNEL); if (!adapter->netdevs) {
err = -ENOMEM; goto err_netdev_alloc;
}
}
err = idpf_vport_params_buf_alloc(adapter); if (err) {
dev_err(&adapter->pdev->dev, "Failed to alloc vport params buffer: %d\n",
err); goto err_netdev_alloc;
}
/* Start the mailbox task before requesting vectors. This will ensure * vector information response from mailbox is handled
*/
queue_delayed_work(adapter->mbx_wq, &adapter->mbx_task, 0);
/* Skew the delay for init tasks for each function based on fn number * to prevent every function from making the same call simultaneously.
*/
queue_delayed_work(adapter->init_wq, &adapter->init_task,
msecs_to_jiffies(5 * (adapter->pdev->devfn & 0x07)));
init_failed: /* Don't retry if we're trying to go down, just bail. */ if (test_bit(IDPF_REMOVE_IN_PROG, adapter->flags)) return err;
if (++adapter->mb_wait_count > IDPF_MB_MAX_ERR) {
dev_err(&adapter->pdev->dev, "Failed to establish mailbox communications with hardware\n");
return -EFAULT;
} /* If it reached here, it is possible that mailbox queue initialization * register writes might not have taken effect. Retry to initialize * the mailbox again
*/
adapter->state = __IDPF_VER_CHECK; if (adapter->vcxn_mngr)
idpf_vc_xn_shutdown(adapter->vcxn_mngr);
set_bit(IDPF_HR_DRV_LOAD, adapter->flags);
queue_delayed_work(adapter->vc_event_wq, &adapter->vc_event_task,
msecs_to_jiffies(task_delay));
if (!test_bit(IDPF_VC_CORE_INIT, adapter->flags)) return;
/* Avoid transaction timeouts when called during reset */
remove_in_prog = test_bit(IDPF_REMOVE_IN_PROG, adapter->flags); if (!remove_in_prog)
idpf_vc_xn_shutdown(adapter->vcxn_mngr);
/** * idpf_vport_alloc_vec_indexes - Get relative vector indexes * @vport: virtual port data struct * * This function requests the vector information required for the vport and * stores the vector indexes received from the 'global vector distribution' * in the vport's queue vectors array. * * Return 0 on success, error on failure
*/ int idpf_vport_alloc_vec_indexes(struct idpf_vport *vport)
{ struct idpf_vector_info vec_info; int num_alloc_vecs;
/** * idpf_get_vec_ids - Initialize vector id from Mailbox parameters * @adapter: adapter structure to get the mailbox vector id * @vecids: Array of vector ids * @num_vecids: number of vector ids * @chunks: vector ids received over mailbox * * Will initialize the mailbox vector id which is received from the * get capabilities and data queue vector ids with ids received as * mailbox parameters. * Returns number of ids filled
*/ int idpf_get_vec_ids(struct idpf_adapter *adapter,
u16 *vecids, int num_vecids, struct virtchnl2_vector_chunks *chunks)
{
u16 num_chunks = le16_to_cpu(chunks->num_vchunks); int num_vecid_filled = 0; int i, j;
for (i = 0; i < num_vec; i++) { if ((num_vecid_filled + i) < num_vecids) {
vecids[num_vecid_filled + i] = start_vecid;
start_vecid++;
} else { break;
}
}
num_vecid_filled = num_vecid_filled + i;
}
return num_vecid_filled;
}
/** * idpf_vport_get_queue_ids - Initialize queue id from Mailbox parameters * @qids: Array of queue ids * @num_qids: number of queue ids * @q_type: queue model * @chunks: queue ids received over mailbox * * Will initialize all queue ids with ids received as mailbox parameters * Returns number of ids filled
*/ staticint idpf_vport_get_queue_ids(u32 *qids, int num_qids, u16 q_type, struct virtchnl2_queue_reg_chunks *chunks)
{
u16 num_chunks = le16_to_cpu(chunks->num_chunks);
u32 num_q_id_filled = 0, i;
u32 start_q_id, num_q;
while (num_chunks--) { struct virtchnl2_queue_reg_chunk *chunk;
chunk = &chunks->chunks[num_chunks]; if (le32_to_cpu(chunk->type) != q_type) continue;
for (i = 0; i < num_q; i++) { if ((num_q_id_filled + i) < num_qids) {
qids[num_q_id_filled + i] = start_q_id;
start_q_id++;
} else { break;
}
}
num_q_id_filled = num_q_id_filled + i;
}
return num_q_id_filled;
}
/** * __idpf_vport_queue_ids_init - Initialize queue ids from Mailbox parameters * @vport: virtual port for which the queues ids are initialized * @qids: queue ids * @num_qids: number of queue ids * @q_type: type of queue * * Will initialize all queue ids with ids received as mailbox * parameters. Returns number of queue ids initialized.
*/ staticint __idpf_vport_queue_ids_init(struct idpf_vport *vport, const u32 *qids, int num_qids,
u32 q_type)
{ int i, j, k = 0;
switch (q_type) { case VIRTCHNL2_QUEUE_TYPE_TX: for (i = 0; i < vport->num_txq_grp; i++) { struct idpf_txq_group *tx_qgrp = &vport->txq_grps[i];
for (j = 0; j < tx_qgrp->num_txq && k < num_qids; j++, k++)
tx_qgrp->txqs[j]->q_id = qids[k];
} break; case VIRTCHNL2_QUEUE_TYPE_RX: for (i = 0; i < vport->num_rxq_grp; i++) { struct idpf_rxq_group *rx_qgrp = &vport->rxq_grps[i];
u16 num_rxq;
if (idpf_is_queue_model_split(vport->rxq_model))
num_rxq = rx_qgrp->splitq.num_rxq_sets; else
num_rxq = rx_qgrp->singleq.num_rxq;
for (j = 0; j < num_rxq && k < num_qids; j++, k++) { struct idpf_rx_queue *q;
if (idpf_is_queue_model_split(vport->rxq_model))
q = &rx_qgrp->splitq.rxq_sets[j]->rxq; else
q = rx_qgrp->singleq.rxqs[j];
q->q_id = qids[k];
}
} break; case VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION: for (i = 0; i < vport->num_txq_grp && k < num_qids; i++, k++) { struct idpf_txq_group *tx_qgrp = &vport->txq_grps[i];
tx_qgrp->complq->q_id = qids[k];
} break; case VIRTCHNL2_QUEUE_TYPE_RX_BUFFER: for (i = 0; i < vport->num_rxq_grp; i++) { struct idpf_rxq_group *rx_qgrp = &vport->rxq_grps[i];
u8 num_bufqs = vport->num_bufqs_per_qgrp;
for (j = 0; j < num_bufqs && k < num_qids; j++, k++) { struct idpf_buf_queue *q;
/** * idpf_vport_queue_ids_init - Initialize queue ids from Mailbox parameters * @vport: virtual port for which the queues ids are initialized * * Will initialize all queue ids with ids received as mailbox parameters. * Returns 0 on success, negative if all the queues are not initialized.
*/ int idpf_vport_queue_ids_init(struct idpf_vport *vport)
{ struct virtchnl2_create_vport *vport_params; struct virtchnl2_queue_reg_chunks *chunks; struct idpf_vport_config *vport_config;
u16 vport_idx = vport->idx; int num_ids, err = 0;
u16 q_type;
u32 *qids;
/** * idpf_vport_adjust_qs - Adjust to new requested queues * @vport: virtual port data struct * * Renegotiate queues. Returns 0 on success, negative on failure.
*/ int idpf_vport_adjust_qs(struct idpf_vport *vport)
{ struct virtchnl2_create_vport vport_msg; int err;
/** * idpf_is_capability_ena - Default implementation of capability checking * @adapter: Private data struct * @all: all or one flag * @field: caps field to check for flags * @flag: flag to check * * Return true if all capabilities are supported, false otherwise
*/ bool idpf_is_capability_ena(struct idpf_adapter *adapter, bool all, enum idpf_cap_field field, u64 flag)
{
u8 *caps = (u8 *)&adapter->caps;
u32 *cap_field;
/** * idpf_get_vport_id: Get vport id * @vport: virtual port structure * * Return vport id from the adapter persistent data
*/
u32 idpf_get_vport_id(struct idpf_vport *vport)
{ struct virtchnl2_create_vport *vport_msg;
/** * idpf_mac_filter_async_handler - Async callback for mac filters * @adapter: private data struct * @xn: transaction for message * @ctlq_msg: received message * * In some scenarios driver can't sleep and wait for a reply (e.g.: stack is * holding rtnl_lock) when adding a new mac filter. It puts us in a difficult * situation to deal with errors returned on the reply. The best we can * ultimately do is remove it from our list of mac filters and report the * error.
*/ staticint idpf_mac_filter_async_handler(struct idpf_adapter *adapter, struct idpf_vc_xn *xn, conststruct idpf_ctlq_msg *ctlq_msg)
{ struct virtchnl2_mac_addr_list *ma_list; struct idpf_vport_config *vport_config; struct virtchnl2_mac_addr *mac_addr; struct idpf_mac_filter *f, *tmp; struct list_head *ma_list_head; struct idpf_vport *vport;
u16 num_entries; int i;
/* if success we're done, we're only here if something bad happened */ if (!ctlq_msg->cookie.mbx.chnl_retval) return 0;
/* make sure at least struct is there */ if (xn->reply_sz < sizeof(*ma_list)) goto invalid_payload;
ma_list = ctlq_msg->ctx.indirect.payload->va;
mac_addr = ma_list->mac_addr_list;
num_entries = le16_to_cpu(ma_list->num_mac_addr); /* we should have received a buffer at least this big */ if (xn->reply_sz < struct_size(ma_list, mac_addr_list, num_entries)) goto invalid_payload;
vport = idpf_vid_to_vport(adapter, le32_to_cpu(ma_list->vport_id)); if (!vport) goto invalid_payload;
/* We can't do much to reconcile bad filters at this point, however we * should at least remove them from our list one way or the other so we * have some idea what good filters we have.
*/
spin_lock_bh(&vport_config->mac_filter_list_lock);
list_for_each_entry_safe(f, tmp, ma_list_head, list) for (i = 0; i < num_entries; i++) if (ether_addr_equal(mac_addr[i].addr, f->macaddr))
list_del(&f->list);
spin_unlock_bh(&vport_config->mac_filter_list_lock);
dev_err_ratelimited(&adapter->pdev->dev, "Received error sending MAC filter request (op %d)\n",
xn->vc_op);
/* Find the number of newly added filters */
list_for_each_entry(f, &vport_config->user_config.mac_filter_list,
list) { if (add && f->add)
total_filters++; elseif (!add && f->remove)
total_filters++;
}
if (!total_filters) {
spin_unlock_bh(&vport_config->mac_filter_list_lock);
return 0;
}
/* Fill all the new filters into virtchannel message */
mac_addr = kcalloc(total_filters, sizeof(struct virtchnl2_mac_addr),
GFP_ATOMIC); if (!mac_addr) {
spin_unlock_bh(&vport_config->mac_filter_list_lock);
return -ENOMEM;
}
list_for_each_entry(f, &vport_config->user_config.mac_filter_list,
list) { if (add && f->add) {
ether_addr_copy(mac_addr[i].addr, f->macaddr);
idpf_set_mac_type(vport, &mac_addr[i]);
i++;
f->add = false; if (i == total_filters) break;
} if (!add && f->remove) {
ether_addr_copy(mac_addr[i].addr, f->macaddr);
idpf_set_mac_type(vport, &mac_addr[i]);
i++;
f->remove = false; if (i == total_filters) break;
}
}
/* Chunk up the filters into multiple messages to avoid * sending a control queue message buffer that is too large
*/
num_msgs = DIV_ROUND_UP(total_filters, IDPF_NUM_FILTERS_PER_MSG);
for (i = 0, k = 0; i < num_msgs; i++) {
u32 entries_size, buf_size, num_entries;
/** * idpf_set_promiscuous - set promiscuous and send message to mailbox * @adapter: Driver specific private structure * @config_data: Vport specific config data * @vport_id: Vport identifier * * Request to enable promiscuous mode for the vport. Message is sent * asynchronously and won't wait for response. Returns 0 on success, negative * on failure;
*/ int idpf_set_promiscuous(struct idpf_adapter *adapter, struct idpf_vport_user_config_data *config_data,
u32 vport_id)
{ struct idpf_vc_xn_params xn_params = {}; struct virtchnl2_promisc_info vpi;
ssize_t reply_sz;
u16 flags = 0;
if (test_bit(__IDPF_PROMISC_UC, config_data->user_flags))
flags |= VIRTCHNL2_UNICAST_PROMISC; if (test_bit(__IDPF_PROMISC_MC, config_data->user_flags))
flags |= VIRTCHNL2_MULTICAST_PROMISC;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.