/* SNET DPU device ID */ #define SNET_DEVICE_ID 0x1000 /* SNET signature */ #define SNET_SIGNATURE 0xD0D06363 /* Max. config version that we can work with */ #define SNET_CFG_VERSION 0x2 /* Queue align */ #define SNET_QUEUE_ALIGNMENT PAGE_SIZE /* Kick value to notify that new data is available */ #define SNET_KICK_VAL 0x1 #define SNET_CONFIG_OFF 0x0 /* How long we are willing to wait for a SNET device */ #define SNET_DETECT_TIMEOUT 5000000 /* How long should we wait for the DPU to read our config */ #define SNET_READ_CFG_TIMEOUT 3000000 /* Size of configs written to the DPU */ #define SNET_GENERAL_CFG_LEN 36 #define SNET_GENERAL_CFG_VQ_LEN 40
/* We can set any state for config version 2+ */ if (SNET_CFG_VER(snet, 2)) {
memcpy(&snet->vqs[idx]->vq_state, state, sizeof(*state)); return 0;
}
/* Older config - we can't set the VQ state. * Return 0 only if this is the initial state we use in the DPU.
*/ if (snet_vq_state_is_initial(snet, state)) return 0;
staticint snet_write_conf(struct snet *snet)
{
u32 off, i, tmp; int ret;
/* No need to write the config twice */ if (snet->dpu_ready) returntrue;
/* Snet data : * * General data: SNET_GENERAL_CFG_LEN bytes long * 0 0x4 0x8 0xC 0x10 0x14 0x1C 0x24 * | MAGIC NUMBER | CFG VER | SNET SID | NUMBER OF QUEUES | IRQ IDX | FEATURES | RSVD | * * For every VQ: SNET_GENERAL_CFG_VQ_LEN bytes long * 0 0x4 0x8 * | VQ SID AND QUEUE SIZE | IRQ Index | * | DESC AREA | * | DEVICE AREA | * | DRIVER AREA | * | VQ STATE (CFG 2+) | RSVD | * * Magic number should be written last, this is the DPU indication that the data is ready
*/
/* Init offset */
off = snet->psnet->cfg.host_cfg_off;
/* Ignore magic number for now */
off += 4;
snet_write32(snet, off, snet->psnet->negotiated_cfg_ver);
off += 4;
snet_write32(snet, off, snet->sid);
off += 4;
snet_write32(snet, off, snet->cfg->vq_num);
off += 4;
snet_write32(snet, off, snet->cfg_irq_idx);
off += 4;
snet_write64(snet, off, snet->negotiated_features);
off += 8; /* Ignore reserved */
off += 8; /* Write VQs */ for (i = 0 ; i < snet->cfg->vq_num ; i++) {
tmp = (i << 16) | (snet->vqs[i]->num & 0xFFFF);
snet_write32(snet, off, tmp);
off += 4;
snet_write32(snet, off, snet->vqs[i]->irq_idx);
off += 4;
snet_write64(snet, off, snet->vqs[i]->desc_area);
off += 8;
snet_write64(snet, off, snet->vqs[i]->device_area);
off += 8;
snet_write64(snet, off, snet->vqs[i]->driver_area);
off += 8; /* Write VQ state if config version is 2+ */ if (SNET_CFG_VER(snet, 2))
snet_write32(snet, off, *(u32 *)&snet->vqs[i]->vq_state);
off += 4;
/* Ignore reserved */
off += 4;
}
/* Write magic number - data is ready */
snet_write32(snet, snet->psnet->cfg.host_cfg_off, SNET_SIGNATURE);
/* The DPU will ACK the config by clearing the signature */
ret = readx_poll_timeout(ioread32, snet->bar + snet->psnet->cfg.host_cfg_off,
tmp, !tmp, 10, SNET_READ_CFG_TIMEOUT); if (ret) {
SNET_ERR(snet->pdev, "Timeout waiting for the DPU to read the config\n"); returnfalse;
}
/* set DPU flag */
snet->dpu_ready = true;
returntrue;
}
staticint snet_request_irqs(struct pci_dev *pdev, struct snet *snet)
{ int ret, i, irq;
name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "psnet[%s]-bars", pci_name(pdev)); if (!name) return -ENOMEM;
/* We don't know which BAR will be used to communicate.. * We will map every bar with len > 0. * * Later, we will discover the BAR and unmap all other BARs.
*/ for (i = 0; i < PCI_STD_NUM_BARS; i++) { void __iomem *io;
if (pci_resource_len(pdev, i) == 0) continue;
io = pcim_iomap_region(pdev, i, name); if (IS_ERR(io)) {
SNET_ERR(pdev, "Failed to request and map PCI BARs\n"); return PTR_ERR(io);
}
psnet->bars[i] = io;
bars_found = true;
}
/* No BAR can be used.. */ if (!bars_found) {
SNET_ERR(pdev, "Failed to find a PCI BAR\n"); return -ENODEV;
}
name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "snet[%s]-bars", pci_name(pdev)); if (!name) return -ENOMEM;
/* Request and map BAR */
io = pcim_iomap_region(pdev, snet->psnet->cfg.vf_bar, name); if (IS_ERR(io)) {
SNET_ERR(pdev, "Failed to request and map PCI BAR for a VF\n"); return PTR_ERR(io);
}
/* Free devices */ for (i = 0; i < cfg->devices_num; i++) { if (!cfg->devs[i]) break;
kfree(cfg->devs[i]);
} /* Free pointers to devices */
kfree(cfg->devs);
}
/* Detect which BAR is used for communication with the device. */ staticint psnet_detect_bar(struct psnet *psnet, u32 off)
{ unsignedlong exit_time; int i;
/* SNET DPU will write SNET's signature when the config is ready. */ while (time_before(jiffies, exit_time)) { for (i = 0; i < PCI_STD_NUM_BARS; i++) { /* Is this BAR mapped? */ if (!psnet->bars[i]) continue;
for (i = 0; i < PCI_STD_NUM_BARS; i++) { if (psnet->bars[i] && i != psnet->barno)
pcim_iounmap_region(pdev, i);
}
}
/* Read SNET config from PCI BAR */ staticint psnet_read_cfg(struct pci_dev *pdev, struct psnet *psnet)
{ struct snet_cfg *cfg = &psnet->cfg;
u32 i, off; int barno;
/* Move to where the config starts */
off = SNET_CONFIG_OFF;
/* Find BAR used for communication */
barno = psnet_detect_bar(psnet, off); if (barno < 0) {
SNET_ERR(pdev, "SNET config is not ready.\n"); return barno;
}
/* Save used BAR number and unmap all other BARs */
psnet->barno = barno;
SNET_DBG(pdev, "Using BAR number %d\n", barno);
psnet_unmap_unused_bars(pdev, psnet);
/* load config from BAR */
cfg->key = psnet_read32(psnet, off);
off += 4;
cfg->cfg_size = psnet_read32(psnet, off);
off += 4;
cfg->cfg_ver = psnet_read32(psnet, off);
off += 4; /* The negotiated config version is the lower one between this driver's config * and the DPU's.
*/
psnet->negotiated_cfg_ver = min_t(u32, cfg->cfg_ver, SNET_CFG_VERSION);
SNET_DBG(pdev, "SNET config version %u\n", psnet->negotiated_cfg_ver);
cfg->vf_num = psnet_read32(psnet, off);
off += 4;
cfg->vf_bar = psnet_read32(psnet, off);
off += 4;
cfg->host_cfg_off = psnet_read32(psnet, off);
off += 4;
cfg->max_size_host_cfg = psnet_read32(psnet, off);
off += 4;
cfg->virtio_cfg_off = psnet_read32(psnet, off);
off += 4;
cfg->kick_off = psnet_read32(psnet, off);
off += 4;
cfg->hwmon_off = psnet_read32(psnet, off);
off += 4;
cfg->ctrl_off = psnet_read32(psnet, off);
off += 4;
cfg->flags = psnet_read32(psnet, off);
off += 4; /* Ignore Reserved */
off += sizeof(cfg->rsvd);
cfg->devices_num = psnet_read32(psnet, off);
off += 4; /* Allocate memory to hold pointer to the devices */
cfg->devs = kcalloc(cfg->devices_num, sizeof(void *), GFP_KERNEL); if (!cfg->devs) return -ENOMEM;
/* Load device configuration from BAR */ for (i = 0; i < cfg->devices_num; i++) {
cfg->devs[i] = kzalloc(sizeof(*cfg->devs[i]), GFP_KERNEL); if (!cfg->devs[i]) {
snet_free_cfg(cfg); return -ENOMEM;
} /* Read device config */
cfg->devs[i]->virtio_id = psnet_read32(psnet, off);
off += 4;
cfg->devs[i]->vq_num = psnet_read32(psnet, off);
off += 4;
cfg->devs[i]->vq_size = psnet_read32(psnet, off);
off += 4;
cfg->devs[i]->vfid = psnet_read32(psnet, off);
off += 4;
cfg->devs[i]->features = psnet_read64(psnet, off);
off += 8; /* Ignore Reserved */
off += sizeof(cfg->devs[i]->rsvd);
cfg->devs[i]->cfg_size = psnet_read32(psnet, off);
off += 4;
/* Is the config witten to the DPU going to be too big? */ if (SNET_GENERAL_CFG_LEN + SNET_GENERAL_CFG_VQ_LEN * cfg->devs[i]->vq_num >
cfg->max_size_host_cfg) {
SNET_ERR(pdev, "Failed to read SNET config, the config is too big..\n");
snet_free_cfg(cfg); return -EINVAL;
}
} return 0;
}
staticint psnet_alloc_irq_vector(struct pci_dev *pdev, struct psnet *psnet)
{ int ret = 0;
u32 i, irq_num = 0;
/* Let's count how many IRQs we need, 1 for every VQ + 1 for config change */ for (i = 0; i < psnet->cfg.devices_num; i++)
irq_num += psnet->cfg.devs[i]->vq_num + 1;
ret = pci_alloc_irq_vectors(pdev, irq_num, irq_num, PCI_IRQ_MSIX); if (ret != irq_num) {
SNET_ERR(pdev, "Failed to allocate IRQ vectors\n"); return ret;
}
SNET_DBG(pdev, "Allocated %u IRQ vectors from physical function\n", irq_num);
return 0;
}
staticint snet_alloc_irq_vector(struct pci_dev *pdev, struct snet_dev_cfg *snet_cfg)
{ int ret = 0;
u32 irq_num;
/* We want 1 IRQ for every VQ + 1 for config change events */
irq_num = snet_cfg->vq_num + 1;
ret = pci_alloc_irq_vectors(pdev, irq_num, irq_num, PCI_IRQ_MSIX); if (ret <= 0) {
SNET_ERR(pdev, "Failed to allocate IRQ vectors\n"); return ret;
}
/* one IRQ for every VQ, and one for config changes */
snet->cfg_irq_idx = psnet_get_next_irq_num(psnet);
snprintf(snet->cfg_irq_name, SNET_NAME_SIZE, "snet[%s]-cfg[%d]",
pci_name(pdev), snet->cfg_irq_idx);
for (i = 0; i < snet->cfg->vq_num; i++) { /* Get next free IRQ ID */
snet->vqs[i]->irq_idx = psnet_get_next_irq_num(psnet); /* Write IRQ name */
snprintf(snet->vqs[i]->irq_name, SNET_NAME_SIZE, "snet[%s]-vq[%d]",
pci_name(pdev), snet->vqs[i]->irq_idx);
}
}
/* Find a device config based on virtual function id */ staticstruct snet_dev_cfg *snet_find_dev_cfg(struct snet_cfg *cfg, u32 vfid)
{
u32 i;
for (i = 0; i < cfg->devices_num; i++) { if (cfg->devs[i]->vfid == vfid) return cfg->devs[i];
} /* Oppss.. no config found.. */ return NULL;
}
/* Probe function for a physical PCI function */ staticint snet_vdpa_probe_pf(struct pci_dev *pdev)
{ struct psnet *psnet; int ret = 0; bool pf_irqs = false;
ret = pcim_enable_device(pdev); if (ret) {
SNET_ERR(pdev, "Failed to enable PCI device\n"); return ret;
}
/* Allocate a PCI physical function device */
psnet = kzalloc(sizeof(*psnet), GFP_KERNEL); if (!psnet) return -ENOMEM;
/* Probe function for a virtual PCI function */ staticint snet_vdpa_probe_vf(struct pci_dev *pdev)
{ struct pci_dev *pdev_pf = pdev->physfn; struct psnet *psnet = pci_get_drvdata(pdev_pf); struct snet_dev_cfg *dev_cfg; struct snet *snet;
u32 vfid; int ret; bool pf_irqs = false;
/* Get virtual function id. * (the DPU counts the VFs from 1)
*/
ret = pci_iov_vf_id(pdev); if (ret < 0) {
SNET_ERR(pdev, "Failed to find a VF id\n"); return ret;
}
vfid = ret + 1;
/* Find the snet_dev_cfg based on vfid */
dev_cfg = snet_find_dev_cfg(&psnet->cfg, vfid); if (!dev_cfg) {
SNET_WARN(pdev, "Failed to find a VF config..\n"); return -ENODEV;
}
/* Which PCI device should allocate the IRQs? * If the SNET_CFG_FLAG_IRQ_PF flag set, the PF device allocates the IRQs
*/
pf_irqs = PSNET_FLAG_ON(psnet, SNET_CFG_FLAG_IRQ_PF);
ret = pcim_enable_device(pdev); if (ret) {
SNET_ERR(pdev, "Failed to enable PCI VF device\n"); return ret;
}
/* Request for MSI-X IRQs */ if (!pf_irqs) {
ret = snet_alloc_irq_vector(pdev, dev_cfg); if (ret) return ret;
}
/* Allocate vdpa device */
snet = vdpa_alloc_device(struct snet, vdpa, &pdev->dev, &snet_config_ops, 1, 1, NULL, false); if (!snet) {
SNET_ERR(pdev, "Failed to allocate a vdpa device\n");
ret = -ENOMEM; goto free_irqs;
}
/* Init control mutex and spinlock */
mutex_init(&snet->ctrl_lock);
spin_lock_init(&snet->ctrl_spinlock);
ret = snet_build_vqs(snet); if (ret) goto put_device;
/* Reserve IRQ indexes, * The IRQs may be requested and freed multiple times, * but the indexes won't change.
*/
snet_reserve_irq_idx(pf_irqs ? pdev_pf : pdev, snet);
pci_disable_sriov(pdev); /* If IRQs are allocated from the PF, we should free the IRQs */ if (PSNET_FLAG_ON(psnet, SNET_CFG_FLAG_IRQ_PF))
pci_free_irq_vectors(pdev);
vdpa_unregister_device(&snet->vdpa);
snet_free_vqs(snet); /* If IRQs are allocated from the VF, we should free the IRQs */ if (!PSNET_FLAG_ON(psnet, SNET_CFG_FLAG_IRQ_PF))
pci_free_irq_vectors(pdev);
}
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.