aboutsummaryrefslogtreecommitdiff
path: root/include/uapi/linux/virtio_bt.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/uapi/linux/virtio_bt.h')
0 files changed, 0 insertions, 0 deletions
/* VFs can only use this many transmit channels */ static unsigned int vf_max_tx_channels = 2; module_param(vf_max_tx_channels, uint, 0444); MODULE_PARM_DESC(vf_max_tx_channels, "Limit the number of TX channels VFs can use"); static int max_vfs = -1; module_param(max_vfs, int, 0444); MODULE_PARM_DESC(max_vfs, "Reduce the number of VFs initialized by the driver"); /* Workqueue used by VFDI communication. We can't use the global * workqueue because it may be running the VF driver's probe() * routine, which will be blocked there waiting for a VFDI response. */ static struct workqueue_struct *vfdi_workqueue; static unsigned abs_index(struct siena_vf *vf, unsigned index) { return EFX_VI_BASE + vf->index * efx_vf_size(vf->efx) + index; } static int efx_siena_sriov_cmd(struct efx_nic *efx, bool enable, unsigned *vi_scale_out, unsigned *vf_total_out) { MCDI_DECLARE_BUF(inbuf, MC_CMD_SRIOV_IN_LEN); MCDI_DECLARE_BUF(outbuf, MC_CMD_SRIOV_OUT_LEN); unsigned vi_scale, vf_total; size_t outlen; int rc; MCDI_SET_DWORD(inbuf, SRIOV_IN_ENABLE, enable ? 1 : 0); MCDI_SET_DWORD(inbuf, SRIOV_IN_VI_BASE, EFX_VI_BASE); MCDI_SET_DWORD(inbuf, SRIOV_IN_VF_COUNT, efx->vf_count); rc = efx_mcdi_rpc_quiet(efx, MC_CMD_SRIOV, inbuf, MC_CMD_SRIOV_IN_LEN, outbuf, MC_CMD_SRIOV_OUT_LEN, &outlen); if (rc) return rc; if (outlen < MC_CMD_SRIOV_OUT_LEN) return -EIO; vf_total = MCDI_DWORD(outbuf, SRIOV_OUT_VF_TOTAL); vi_scale = MCDI_DWORD(outbuf, SRIOV_OUT_VI_SCALE); if (vi_scale > EFX_VI_SCALE_MAX) return -EOPNOTSUPP; if (vi_scale_out) *vi_scale_out = vi_scale; if (vf_total_out) *vf_total_out = vf_total; return 0; } static void efx_siena_sriov_usrev(struct efx_nic *efx, bool enabled) { struct siena_nic_data *nic_data = efx->nic_data; efx_oword_t reg; EFX_POPULATE_OWORD_2(reg, FRF_CZ_USREV_DIS, enabled ? 0 : 1, FRF_CZ_DFLT_EVQ, nic_data->vfdi_channel->channel); efx_writeo(efx, ®, FR_CZ_USR_EV_CFG); } static int efx_siena_sriov_memcpy(struct efx_nic *efx, struct efx_memcpy_req *req, unsigned int count) { MCDI_DECLARE_BUF(inbuf, MCDI_CTL_SDU_LEN_MAX_V1); MCDI_DECLARE_STRUCT_PTR(record); unsigned int index, used; u64 from_addr; u32 from_rid; int rc; mb(); /* Finish writing source/reading dest before DMA starts */ if (WARN_ON(count > MC_CMD_MEMCPY_IN_RECORD_MAXNUM)) return -ENOBUFS; used = MC_CMD_MEMCPY_IN_LEN(count); for (index = 0; index < count; index++) { record = MCDI_ARRAY_STRUCT_PTR(inbuf, MEMCPY_IN_RECORD, index); MCDI_SET_DWORD(record, MEMCPY_RECORD_TYPEDEF_NUM_RECORDS, count); MCDI_SET_DWORD(record, MEMCPY_RECORD_TYPEDEF_TO_RID, req->to_rid); MCDI_SET_QWORD(record, MEMCPY_RECORD_TYPEDEF_TO_ADDR, req->to_addr); if (req->from_buf == NULL) { from_rid = req->from_rid; from_addr = req->from_addr; } else { if (WARN_ON(used + req->length > MCDI_CTL_SDU_LEN_MAX_V1)) { rc = -ENOBUFS; goto out; } from_rid = MC_CMD_MEMCPY_RECORD_TYPEDEF_RID_INLINE; from_addr = used; memcpy(_MCDI_PTR(inbuf, used), req->from_buf, req->length); used += req->length; } MCDI_SET_DWORD(record, MEMCPY_RECORD_TYPEDEF_FROM_RID, from_rid); MCDI_SET_QWORD(record, MEMCPY_RECORD_TYPEDEF_FROM_ADDR, from_addr); MCDI_SET_DWORD(record, MEMCPY_RECORD_TYPEDEF_LENGTH, req->length); ++req; } rc = efx_mcdi_rpc(efx, MC_CMD_MEMCPY, inbuf, used, NULL, 0, NULL); out: mb(); /* Don't write source/read dest before DMA is complete */ return rc; } /* The TX filter is entirely controlled by this driver, and is modified * underneath the feet of the VF */ static void efx_siena_sriov_reset_tx_filter(struct siena_vf *vf) { struct efx_nic *efx = vf->efx; struct efx_filter_spec filter; u16 vlan; int rc; if (vf->tx_filter_id != -1) { efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_REQUIRED, vf->tx_filter_id); netif_dbg(efx, hw, efx->net_dev, "Removed vf %s tx filter %d\n", vf->pci_name, vf->tx_filter_id); vf->tx_filter_id = -1; } if (is_zero_ether_addr(vf->addr.mac_addr)) return; /* Turn on TX filtering automatically if not explicitly * enabled or disabled. */ if (vf->tx_filter_mode == VF_TX_FILTER_AUTO && vf_max_tx_channels <= 2) vf->tx_filter_mode = VF_TX_FILTER_ON; vlan = ntohs(vf->addr.tci) & VLAN_VID_MASK; efx_filter_init_tx(&filter, abs_index(vf, 0)); rc = efx_filter_set_eth_local(&filter, vlan ? vlan : EFX_FILTER_VID_UNSPEC, vf->addr.mac_addr); BUG_ON(rc); rc = efx_filter_insert_filter(efx, &filter, true); if (rc < 0) { netif_warn(efx, hw, efx->net_dev, "Unable to migrate tx filter for vf %s\n", vf->pci_name); } else { netif_dbg(efx, hw, efx->net_dev, "Inserted vf %s tx filter %d\n", vf->pci_name, rc); vf->tx_filter_id = rc; } } /* The RX filter is managed here on behalf of the VF driver */ static void efx_siena_sriov_reset_rx_filter(struct siena_vf *vf) { struct efx_nic *efx = vf->efx; struct efx_filter_spec filter; u16 vlan; int rc; if (vf->rx_filter_id != -1) { efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_REQUIRED, vf->rx_filter_id); netif_dbg(efx, hw, efx->net_dev, "Removed vf %s rx filter %d\n", vf->pci_name, vf->rx_filter_id); vf->rx_filter_id = -1; } if (!vf->rx_filtering || is_zero_ether_addr(vf->addr.mac_addr)) return; vlan = ntohs(vf->addr.tci) & VLAN_VID_MASK; efx_filter_init_rx(&filter, EFX_FILTER_PRI_REQUIRED, vf->rx_filter_flags, abs_index(vf, vf->rx_filter_qid)); rc = efx_filter_set_eth_local(&filter, vlan ? vlan : EFX_FILTER_VID_UNSPEC, vf->addr.mac_addr); BUG_ON(rc); rc = efx_filter_insert_filter(efx, &filter, true); if (rc < 0) { netif_warn(efx, hw, efx->net_dev, "Unable to insert rx filter for vf %s\n", vf->pci_name); } else { netif_dbg(efx, hw, efx->net_dev, "Inserted vf %s rx filter %d\n", vf->pci_name, rc); vf->rx_filter_id = rc; } } static void __efx_siena_sriov_update_vf_addr(struct siena_vf *vf) { struct efx_nic *efx = vf->efx; struct siena_nic_data *nic_data = efx->nic_data; efx_siena_sriov_reset_tx_filter(vf); efx_siena_sriov_reset_rx_filter(vf); queue_work(vfdi_workqueue, &nic_data->peer_work); } /* Push the peer list to this VF. The caller must hold status_lock to interlock * with VFDI requests, and they must be serialised against manipulation of * local_page_list, either by acquiring local_lock or by running from * efx_siena_sriov_peer_work() */ static void __efx_siena_sriov_push_vf_status(struct siena_vf *vf) { struct efx_nic *efx = vf->efx; struct siena_nic_data *nic_data = efx->nic_data; struct vfdi_status *status = nic_data->vfdi_status.addr; struct efx_memcpy_req copy[4]; struct efx_endpoint_page *epp; unsigned int pos, count; unsigned data_offset; efx_qword_t event; WARN_ON(!mutex_is_locked(&vf->status_lock)); WARN_ON(!vf->status_addr); status->local = vf->addr; status->generation_end = ++status->generation_start; memset(copy, '\0', sizeof(copy)); /* Write generation_start */ copy[0].from_buf = &status->generation_start; copy[0].to_rid = vf->pci_rid; copy[0].to_addr = vf->status_addr + offsetof(struct vfdi_status, generation_start); copy[0].length = sizeof(status->generation_start); /* DMA the rest of the structure (excluding the generations). This * assumes that the non-generation portion of vfdi_status is in * one chunk starting at the version member. */ data_offset = offsetof(struct vfdi_status, version); copy[1].from_rid = efx->pci_dev->devfn; copy[1].from_addr = nic_data->vfdi_status.dma_addr + data_offset; copy[1].to_rid = vf->pci_rid; copy[1].to_addr = vf->status_addr + data_offset; copy[1].length = status->length - data_offset; /* Copy the peer pages */ pos = 2; count = 0; list_for_each_entry(epp, &nic_data->local_page_list, link) { if (count == vf->peer_page_count) { /* The VF driver will know they need to provide more * pages because peer_addr_count is too large. */ break; } copy[pos].from_buf = NULL; copy[pos].from_rid = efx->pci_dev->devfn; copy[pos].from_addr = epp->addr; copy[pos].to_rid = vf->pci_rid; copy[pos].to_addr = vf->peer_page_addrs[count]; copy[pos].length = EFX_PAGE_SIZE; if (++pos == ARRAY_SIZE(copy)) { efx_siena_sriov_memcpy(efx, copy, ARRAY_SIZE(copy)); pos = 0; } ++count; } /* Write generation_end */ copy[pos].from_buf = &status->generation_end; copy[pos].to_rid = vf->pci_rid; copy[pos].to_addr = vf->status_addr + offsetof(struct vfdi_status, generation_end); copy[pos].length = sizeof(status->generation_end); efx_siena_sriov_memcpy(efx, copy, pos + 1); /* Notify the guest */ EFX_POPULATE_QWORD_3(event, FSF_AZ_EV_CODE, FSE_CZ_EV_CODE_USER_EV, VFDI_EV_SEQ, (vf->msg_seqno & 0xff), VFDI_EV_TYPE, VFDI_EV_TYPE_STATUS); ++vf->msg_seqno; efx_farch_generate_event(efx, EFX_VI_BASE + vf->index * efx_vf_size(efx), &event); } static void efx_siena_sriov_bufs(struct efx_nic *efx, unsigned offset, u64 *addr, unsigned count) { efx_qword_t buf; unsigned pos; for (pos = 0; pos < count; ++pos) { EFX_POPULATE_QWORD_3(buf, FRF_AZ_BUF_ADR_REGION, 0, FRF_AZ_BUF_ADR_FBUF, addr ? addr[pos] >> 12 : 0, FRF_AZ_BUF_OWNER_ID_FBUF, 0); efx_sram_writeq(efx, efx->membase + FR_BZ_BUF_FULL_TBL, &buf, offset + pos); } } static bool bad_vf_index(struct efx_nic *efx, unsigned index) { return index >= efx_vf_size(efx); } static bool bad_buf_count(unsigned buf_count, unsigned max_entry_count) { unsigned max_buf_count = max_entry_count * sizeof(efx_qword_t) / EFX_BUF_SIZE; return ((buf_count & (buf_count - 1)) || buf_count > max_buf_count); } /* Check that VI specified by per-port index belongs to a VF. * Optionally set VF index and VI index within the VF. */ static bool map_vi_index(struct efx_nic *efx, unsigned abs_index, struct siena_vf **vf_out, unsigned *rel_index_out) { struct siena_nic_data *nic_data = efx->nic_data; unsigned vf_i; if (abs_index < EFX_VI_BASE) return true; vf_i = (abs_index - EFX_VI_BASE) / efx_vf_size(efx); if (vf_i >= efx->vf_init_count) return true; if (vf_out) *vf_out = nic_data->vf + vf_i; if (rel_index_out) *rel_index_out = abs_index % efx_vf_size(efx); return false; } static int efx_vfdi_init_evq(struct siena_vf *vf) { struct efx_nic *efx = vf->efx; struct vfdi_req *req = vf->buf.addr; unsigned vf_evq = req->u.init_evq.index; unsigned buf_count = req->u.init_evq.buf_count; unsigned abs_evq = abs_index(vf, vf_evq); unsigned buftbl = EFX_BUFTBL_EVQ_BASE(vf, vf_evq); efx_oword_t reg; if (bad_vf_index(efx, vf_evq) || bad_buf_count(buf_count, EFX_MAX_VF_EVQ_SIZE)) { if (net_ratelimit()) netif_err(efx, hw, efx->net_dev, "ERROR: Invalid INIT_EVQ from %s: evq %d bufs %d\n", vf->pci_name, vf_evq, buf_count); return VFDI_RC_EINVAL; } efx_siena_sriov_bufs(efx, buftbl, req->u.init_evq.addr, buf_count); EFX_POPULATE_OWORD_3(reg, FRF_CZ_TIMER_Q_EN, 1, FRF_CZ_HOST_NOTIFY_MODE, 0, FRF_CZ_TIMER_MODE, FFE_CZ_TIMER_MODE_DIS); efx_writeo_table(efx, ®, FR_BZ_TIMER_TBL, abs_evq); EFX_POPULATE_OWORD_3(reg, FRF_AZ_EVQ_EN, 1, FRF_AZ_EVQ_SIZE, __ffs(buf_count), FRF_AZ_EVQ_BUF_BASE_ID, buftbl); efx_writeo_table(efx, ®, FR_BZ_EVQ_PTR_TBL, abs_evq); if (vf_evq == 0) { memcpy(vf->evq0_addrs, req->u.init_evq.addr, buf_count * sizeof(u64)); vf->evq0_count = buf_count; } return VFDI_RC_SUCCESS; } static int efx_vfdi_init_rxq(struct siena_vf *vf) { struct efx_nic *efx = vf->efx; struct vfdi_req *req = vf->buf.addr; unsigned vf_rxq = req->u.init_rxq.index; unsigned vf_evq = req->u.init_rxq.evq; unsigned buf_count = req->u.init_rxq.buf_count; unsigned buftbl = EFX_BUFTBL_RXQ_BASE(vf, vf_rxq); unsigned label; efx_oword_t reg; if (bad_vf_index(efx, vf_evq) || bad_vf_index(efx, vf_rxq) || vf_rxq >= VF_MAX_RX_QUEUES || bad_buf_count(buf_count, EFX_MAX_DMAQ_SIZE)) { if (net_ratelimit()) netif_err(efx, hw, efx->net_dev, "ERROR: Invalid INIT_RXQ from %s: rxq %d evq %d " "buf_count %d\n", vf->pci_name, vf_rxq, vf_evq, buf_count); return VFDI_RC_EINVAL; } if (__test_and_set_bit(req->u.init_rxq.index, vf->rxq_mask)) ++vf->rxq_count; efx_siena_sriov_bufs(efx, buftbl, req->u.init_rxq.addr, buf_count); label = req->u.init_rxq.label & EFX_FIELD_MASK(FRF_AZ_RX_DESCQ_LABEL); EFX_POPULATE_OWORD_6(reg, FRF_AZ_RX_DESCQ_BUF_BASE_ID, buftbl, FRF_AZ_RX_DESCQ_EVQ_ID, abs_index(vf, vf_evq), FRF_AZ_RX_DESCQ_LABEL, label, FRF_AZ_RX_DESCQ_SIZE, __ffs(buf_count), FRF_AZ_RX_DESCQ_JUMBO, !!(req->u.init_rxq.flags & VFDI_RXQ_FLAG_SCATTER_EN), FRF_AZ_RX_DESCQ_EN, 1); efx_writeo_table(efx, ®, FR_BZ_RX_DESC_PTR_TBL, abs_index(vf, vf_rxq)); return VFDI_RC_SUCCESS; } static int efx_vfdi_init_txq(struct siena_vf *vf) { struct efx_nic *efx = vf->efx; struct vfdi_req *req = vf->buf.addr; unsigned vf_txq = req->u.init_txq.index; unsigned vf_evq = req->u.init_txq.evq; unsigned buf_count = req->u.init_txq.buf_count; unsigned buftbl = EFX_BUFTBL_TXQ_BASE(vf, vf_txq); unsigned label, eth_filt_en; efx_oword_t reg; if (bad_vf_index(efx, vf_evq) || bad_vf_index(efx, vf_txq) || vf_txq >= vf_max_tx_channels || bad_buf_count(buf_count, EFX_MAX_DMAQ_SIZE)) { if (net_ratelimit()) netif_err(efx, hw, efx->net_dev, "ERROR: Invalid INIT_TXQ from %s: txq %d evq %d " "buf_count %d\n", vf->pci_name, vf_txq, vf_evq, buf_count); return VFDI_RC_EINVAL; } mutex_lock(&vf->txq_lock); if (__test_and_set_bit(req->u.init_txq.index, vf->txq_mask)) ++vf->txq_count; mutex_unlock(&vf->txq_lock); efx_siena_sriov_bufs(efx, buftbl, req->u.init_txq.addr, buf_count); eth_filt_en = vf->tx_filter_mode == VF_TX_FILTER_ON; label = req->u.init_txq.label & EFX_FIELD_MASK(FRF_AZ_TX_DESCQ_LABEL); EFX_POPULATE_OWORD_8(reg, FRF_CZ_TX_DPT_Q_MASK_WIDTH, min(efx->vi_scale, 1U), FRF_CZ_TX_DPT_ETH_FILT_EN, eth_filt_en, FRF_AZ_TX_DESCQ_EN, 1, FRF_AZ_TX_DESCQ_BUF_BASE_ID, buftbl, FRF_AZ_TX_DESCQ_EVQ_ID, abs_index(vf, vf_evq), FRF_AZ_TX_DESCQ_LABEL, label, FRF_AZ_TX_DESCQ_SIZE, __ffs(buf_count), FRF_BZ_TX_NON_IP_DROP_DIS, 1); efx_writeo_table(efx, ®, FR_BZ_TX_DESC_PTR_TBL, abs_index(vf, vf_txq)); return VFDI_RC_SUCCESS; } /* Returns true when efx_vfdi_fini_all_queues should wake */ static bool efx_vfdi_flush_wake(struct siena_vf *vf) { /* Ensure that all updates are visible to efx_vfdi_fini_all_queues() */ smp_mb(); return (!vf->txq_count && !vf->rxq_count) || atomic_read(&vf->rxq_retry_count); } static void efx_vfdi_flush_clear(struct siena_vf *vf) { memset(vf->txq_mask, 0, sizeof(vf->txq_mask)); vf->txq_count = 0; memset(vf->rxq_mask, 0, sizeof(vf->rxq_mask)); vf->rxq_count = 0; memset(vf->rxq_retry_mask, 0, sizeof(vf->rxq_retry_mask)); atomic_set(&vf->rxq_retry_count, 0); } static int efx_vfdi_fini_all_queues(struct siena_vf *vf) { struct efx_nic *efx = vf->efx; efx_oword_t reg; unsigned count = efx_vf_size(efx); unsigned vf_offset = EFX_VI_BASE + vf->index * efx_vf_size(efx); unsigned timeout = HZ; unsigned index, rxqs_count; MCDI_DECLARE_BUF(inbuf, MC_CMD_FLUSH_RX_QUEUES_IN_LENMAX); int rc; BUILD_BUG_ON(VF_MAX_RX_QUEUES > MC_CMD_FLUSH_RX_QUEUES_IN_QID_OFST_MAXNUM); rtnl_lock(); siena_prepare_flush(efx); rtnl_unlock(); /* Flush all the initialized queues */ rxqs_count = 0; for (index = 0; index < count; ++index) { if (test_bit(index, vf->txq_mask)) { EFX_POPULATE_OWORD_2(reg, FRF_AZ_TX_FLUSH_DESCQ_CMD, 1, FRF_AZ_TX_FLUSH_DESCQ, vf_offset + index); efx_writeo(efx, ®, FR_AZ_TX_FLUSH_DESCQ); } if (test_bit(index, vf->rxq_mask)) { MCDI_SET_ARRAY_DWORD( inbuf, FLUSH_RX_QUEUES_IN_QID_OFST, rxqs_count, vf_offset + index); rxqs_count++; } } atomic_set(&vf->rxq_retry_count, 0); while (timeout && (vf->rxq_count || vf->txq_count)) { rc = efx_mcdi_rpc(efx, MC_CMD_FLUSH_RX_QUEUES, inbuf, MC_CMD_FLUSH_RX_QUEUES_IN_LEN(rxqs_count), NULL, 0, NULL); WARN_ON(rc < 0); timeout = wait_event_timeout(vf->flush_waitq, efx_vfdi_flush_wake(vf), timeout); rxqs_count = 0; for (index = 0; index < count; ++index) { if (test_and_clear_bit(index, vf->rxq_retry_mask)) { atomic_dec(&vf->rxq_retry_count); MCDI_SET_ARRAY_DWORD( inbuf, FLUSH_RX_QUEUES_IN_QID_OFST, rxqs_count, vf_offset + index); rxqs_count++; } } } rtnl_lock(); siena_finish_flush(efx); rtnl_unlock(); /* Irrespective of success/failure, fini the queues */ EFX_ZERO_OWORD(reg); for (index = 0; index < count; ++index) { efx_writeo_table(efx, ®, FR_BZ_RX_DESC_PTR_TBL, vf_offset + index); efx_writeo_table(efx, ®, FR_BZ_TX_DESC_PTR_TBL, vf_offset + index); efx_writeo_table(efx, ®, FR_BZ_EVQ_PTR_TBL, vf_offset + index); efx_writeo_table(efx, ®, FR_BZ_TIMER_TBL, vf_offset + index); } efx_siena_sriov_bufs(efx, vf->buftbl_base, NULL, EFX_VF_BUFTBL_PER_VI * efx_vf_size(efx)); efx_vfdi_flush_clear(vf); vf->evq0_count = 0; return timeout ? 0 : VFDI_RC_ETIMEDOUT; } static int efx_vfdi_insert_filter(struct siena_vf *vf) { struct efx_nic *efx = vf->efx; struct siena_nic_data *nic_data = efx->nic_data; struct vfdi_req *req = vf->buf.addr; unsigned vf_rxq = req->u.mac_filter.rxq; unsigned flags; if (bad_vf_index(efx, vf_rxq) || vf->rx_filtering) { if (net_ratelimit()) netif_err(efx, hw, efx->net_dev, "ERROR: Invalid INSERT_FILTER from %s: rxq %d " "flags 0x%x\n", vf->pci_name, vf_rxq, req->u.mac_filter.flags); return VFDI_RC_EINVAL; } flags = 0; if (req->u.mac_filter.flags & VFDI_MAC_FILTER_FLAG_RSS) flags |= EFX_FILTER_FLAG_RX_RSS; if (req->u.mac_filter.flags & VFDI_MAC_FILTER_FLAG_SCATTER) flags |= EFX_FILTER_FLAG_RX_SCATTER; vf->rx_filter_flags = flags; vf->rx_filter_qid = vf_rxq; vf->