Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Alvaro Karsz | 5400 | 100.00% | 6 | 100.00% |
Total | 5400 | 6 |
// SPDX-License-Identifier: GPL-2.0-only /* * SolidRun DPU driver for control plane * * Copyright (C) 2022-2023 SolidRun * * Author: Alvaro Karsz <alvaro.karsz@solid-run.com> * */ #include <linux/iopoll.h> #include "snet_vdpa.h" /* 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 static struct snet *vdpa_to_snet(struct vdpa_device *vdpa) { return container_of(vdpa, struct snet, vdpa); } static irqreturn_t snet_cfg_irq_hndlr(int irq, void *data) { struct snet *snet = data; /* Call callback if any */ if (likely(snet->cb.callback)) return snet->cb.callback(snet->cb.private); return IRQ_HANDLED; } static irqreturn_t snet_vq_irq_hndlr(int irq, void *data) { struct snet_vq *vq = data; /* Call callback if any */ if (likely(vq->cb.callback)) return vq->cb.callback(vq->cb.private); return IRQ_HANDLED; } static void snet_free_irqs(struct snet *snet) { struct psnet *psnet = snet->psnet; struct pci_dev *pdev; u32 i; /* Which Device allcoated the IRQs? */ if (PSNET_FLAG_ON(psnet, SNET_CFG_FLAG_IRQ_PF)) pdev = snet->pdev->physfn; else pdev = snet->pdev; /* Free config's IRQ */ if (snet->cfg_irq != -1) { devm_free_irq(&pdev->dev, snet->cfg_irq, snet); snet->cfg_irq = -1; } /* Free VQ IRQs */ for (i = 0; i < snet->cfg->vq_num; i++) { if (snet->vqs[i] && snet->vqs[i]->irq != -1) { devm_free_irq(&pdev->dev, snet->vqs[i]->irq, snet->vqs[i]); snet->vqs[i]->irq = -1; } } /* IRQ vectors are freed when the pci remove callback is called */ } static int snet_set_vq_address(struct vdpa_device *vdev, u16 idx, u64 desc_area, u64 driver_area, u64 device_area) { struct snet *snet = vdpa_to_snet(vdev); /* save received parameters in vqueue sturct */ snet->vqs[idx]->desc_area = desc_area; snet->vqs[idx]->driver_area = driver_area; snet->vqs[idx]->device_area = device_area; return 0; } static void snet_set_vq_num(struct vdpa_device *vdev, u16 idx, u32 num) { struct snet *snet = vdpa_to_snet(vdev); /* save num in vqueue */ snet->vqs[idx]->num = num; } static void snet_kick_vq(struct vdpa_device *vdev, u16 idx) { struct snet *snet = vdpa_to_snet(vdev); /* not ready - ignore */ if (unlikely(!snet->vqs[idx]->ready)) return; iowrite32(SNET_KICK_VAL, snet->vqs[idx]->kick_ptr); } static void snet_kick_vq_with_data(struct vdpa_device *vdev, u32 data) { struct snet *snet = vdpa_to_snet(vdev); u16 idx = data & 0xFFFF; /* not ready - ignore */ if (unlikely(!snet->vqs[idx]->ready)) return; iowrite32((data & 0xFFFF0000) | SNET_KICK_VAL, snet->vqs[idx]->kick_ptr); } static void snet_set_vq_cb(struct vdpa_device *vdev, u16 idx, struct vdpa_callback *cb) { struct snet *snet = vdpa_to_snet(vdev); snet->vqs[idx]->cb.callback = cb->callback; snet->vqs[idx]->cb.private = cb->private; } static void snet_set_vq_ready(struct vdpa_device *vdev, u16 idx, bool ready) { struct snet *snet = vdpa_to_snet(vdev); snet->vqs[idx]->ready = ready; } static bool snet_get_vq_ready(struct vdpa_device *vdev, u16 idx) { struct snet *snet = vdpa_to_snet(vdev); return snet->vqs[idx]->ready; } static bool snet_vq_state_is_initial(struct snet *snet, const struct vdpa_vq_state *state) { if (SNET_HAS_FEATURE(snet, VIRTIO_F_RING_PACKED)) { const struct vdpa_vq_state_packed *p = &state->packed; if (p->last_avail_counter == 1 && p->last_used_counter == 1 && p->last_avail_idx == 0 && p->last_used_idx == 0) return true; } else { const struct vdpa_vq_state_split *s = &state->split; if (s->avail_index == 0) return true; } return false; } static int snet_set_vq_state(struct vdpa_device *vdev, u16 idx, const struct vdpa_vq_state *state) { struct snet *snet = vdpa_to_snet(vdev); /* 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; return -EOPNOTSUPP; } static int snet_get_vq_state(struct vdpa_device *vdev, u16 idx, struct vdpa_vq_state *state) { struct snet *snet = vdpa_to_snet(vdev); return snet_read_vq_state(snet, idx, state); } static int snet_get_vq_irq(struct vdpa_device *vdev, u16 idx) { struct snet *snet = vdpa_to_snet(vdev); return snet->vqs[idx]->irq; } static u32 snet_get_vq_align(struct vdpa_device *vdev) { return (u32)SNET_QUEUE_ALIGNMENT; } static int snet_reset_dev(struct snet *snet) { struct pci_dev *pdev = snet->pdev; int ret = 0; u32 i; /* If status is 0, nothing to do */ if (!snet->status) return 0; /* If DPU started, destroy it */ if (snet->status & VIRTIO_CONFIG_S_DRIVER_OK) ret = snet_destroy_dev(snet); /* Clear VQs */ for (i = 0; i < snet->cfg->vq_num; i++) { if (!snet->vqs[i]) continue; snet->vqs[i]->cb.callback = NULL; snet->vqs[i]->cb.private = NULL; snet->vqs[i]->desc_area = 0; snet->vqs[i]->device_area = 0; snet->vqs[i]->driver_area = 0; snet->vqs[i]->ready = false; } /* Clear config callback */ snet->cb.callback = NULL; snet->cb.private = NULL; /* Free IRQs */ snet_free_irqs(snet); /* Reset status */ snet->status = 0; snet->dpu_ready = false; if (ret) SNET_WARN(pdev, "Incomplete reset to SNET[%u] device, err: %d\n", snet->sid, ret); else SNET_DBG(pdev, "Reset SNET[%u] device\n", snet->sid); return 0; } static int snet_reset(struct vdpa_device *vdev) { struct snet *snet = vdpa_to_snet(vdev); return snet_reset_dev(snet); } static size_t snet_get_config_size(struct vdpa_device *vdev) { struct snet *snet = vdpa_to_snet(vdev); return (size_t)snet->cfg->cfg_size; } static u64 snet_get_features(struct vdpa_device *vdev) { struct snet *snet = vdpa_to_snet(vdev); return snet->cfg->features; } static int snet_set_drv_features(struct vdpa_device *vdev, u64 features) { struct snet *snet = vdpa_to_snet(vdev); snet->negotiated_features = snet->cfg->features & features; return 0; } static u64 snet_get_drv_features(struct vdpa_device *vdev) { struct snet *snet = vdpa_to_snet(vdev); return snet->negotiated_features; } static u16 snet_get_vq_num_max(struct vdpa_device *vdev) { struct snet *snet = vdpa_to_snet(vdev); return (u16)snet->cfg->vq_size; } static void snet_set_config_cb(struct vdpa_device *vdev, struct vdpa_callback *cb) { struct snet *snet = vdpa_to_snet(vdev); snet->cb.callback = cb->callback; snet->cb.private = cb->private; } static u32 snet_get_device_id(struct vdpa_device *vdev) { struct snet *snet = vdpa_to_snet(vdev); return snet->cfg->virtio_id; } static u32 snet_get_vendor_id(struct vdpa_device *vdev) { return (u32)PCI_VENDOR_ID_SOLIDRUN; } static u8 snet_get_status(struct vdpa_device *vdev) { struct snet *snet = vdpa_to_snet(vdev); return snet->status; } static int snet_write_conf(struct snet *snet) { u32 off, i, tmp; int ret; /* No need to write the config twice */ if (snet->dpu_ready) return true; /* 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"); return false; } /* set DPU flag */ snet->dpu_ready = true; return true; } static int snet_request_irqs(struct pci_dev *pdev, struct snet *snet) { int ret, i, irq; /* Request config IRQ */ irq = pci_irq_vector(pdev, snet->cfg_irq_idx); ret = devm_request_irq(&pdev->dev, irq, snet_cfg_irq_hndlr, 0, snet->cfg_irq_name, snet); if (ret) { SNET_ERR(pdev, "Failed to request IRQ\n"); return ret; } snet->cfg_irq = irq; /* Request IRQ for every VQ */ for (i = 0; i < snet->cfg->vq_num; i++) { irq = pci_irq_vector(pdev, snet->vqs[i]->irq_idx); ret = devm_request_irq(&pdev->dev, irq, snet_vq_irq_hndlr, 0, snet->vqs[i]->irq_name, snet->vqs[i]); if (ret) { SNET_ERR(pdev, "Failed to request IRQ\n"); return ret; } snet->vqs[i]->irq = irq; } return 0; } static void snet_set_status(struct vdpa_device *vdev, u8 status) { struct snet *snet = vdpa_to_snet(vdev); struct psnet *psnet = snet->psnet; struct pci_dev *pdev = snet->pdev; int ret; bool pf_irqs; if (status == snet->status) return; if ((status & VIRTIO_CONFIG_S_DRIVER_OK) && !(snet->status & VIRTIO_CONFIG_S_DRIVER_OK)) { /* Request IRQs */ pf_irqs = PSNET_FLAG_ON(psnet, SNET_CFG_FLAG_IRQ_PF); ret = snet_request_irqs(pf_irqs ? pdev->physfn : pdev, snet); if (ret) goto set_err; /* Write config to the DPU */ if (snet_write_conf(snet)) { SNET_INFO(pdev, "Create SNET[%u] device\n", snet->sid); } else { snet_free_irqs(snet); goto set_err; } } /* Save the new status */ snet->status = status; return; set_err: snet->status |= VIRTIO_CONFIG_S_FAILED; } static void snet_get_config(struct vdpa_device *vdev, unsigned int offset, void *buf, unsigned int len) { struct snet *snet = vdpa_to_snet(vdev); void __iomem *cfg_ptr = snet->cfg->virtio_cfg + offset; u8 *buf_ptr = buf; u32 i; /* check for offset error */ if (offset + len > snet->cfg->cfg_size) return; /* Write into buffer */ for (i = 0; i < len; i++) *buf_ptr++ = ioread8(cfg_ptr + i); } static void snet_set_config(struct vdpa_device *vdev, unsigned int offset, const void *buf, unsigned int len) { struct snet *snet = vdpa_to_snet(vdev); void __iomem *cfg_ptr = snet->cfg->virtio_cfg + offset; const u8 *buf_ptr = buf; u32 i; /* check for offset error */ if (offset + len > snet->cfg->cfg_size) return; /* Write into PCI BAR */ for (i = 0; i < len; i++) iowrite8(*buf_ptr++, cfg_ptr + i); } static int snet_suspend(struct vdpa_device *vdev) { struct snet *snet = vdpa_to_snet(vdev); int ret; ret = snet_suspend_dev(snet); if (ret) SNET_ERR(snet->pdev, "SNET[%u] suspend failed, err: %d\n", snet->sid, ret); else SNET_DBG(snet->pdev, "Suspend SNET[%u] device\n", snet->sid); return ret; } static int snet_resume(struct vdpa_device *vdev) { struct snet *snet = vdpa_to_snet(vdev); int ret; ret = snet_resume_dev(snet); if (ret) SNET_ERR(snet->pdev, "SNET[%u] resume failed, err: %d\n", snet->sid, ret); else SNET_DBG(snet->pdev, "Resume SNET[%u] device\n", snet->sid); return ret; } static const struct vdpa_config_ops snet_config_ops = { .set_vq_address = snet_set_vq_address, .set_vq_num = snet_set_vq_num, .kick_vq = snet_kick_vq, .kick_vq_with_data = snet_kick_vq_with_data, .set_vq_cb = snet_set_vq_cb, .set_vq_ready = snet_set_vq_ready, .get_vq_ready = snet_get_vq_ready, .set_vq_state = snet_set_vq_state, .get_vq_state = snet_get_vq_state, .get_vq_irq = snet_get_vq_irq, .get_vq_align = snet_get_vq_align, .reset = snet_reset, .get_config_size = snet_get_config_size, .get_device_features = snet_get_features, .set_driver_features = snet_set_drv_features, .get_driver_features = snet_get_drv_features, .get_vq_num_min = snet_get_vq_num_max, .get_vq_num_max = snet_get_vq_num_max, .set_config_cb = snet_set_config_cb, .get_device_id = snet_get_device_id, .get_vendor_id = snet_get_vendor_id, .get_status = snet_get_status, .set_status = snet_set_status, .get_config = snet_get_config, .set_config = snet_set_config, .suspend = snet_suspend, .resume = snet_resume, }; static int psnet_open_pf_bar(struct pci_dev *pdev, struct psnet *psnet) { char name[50]; int ret, i, mask = 0; /* 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++) { if (pci_resource_len(pdev, i)) mask |= (1 << i); } /* No BAR can be used.. */ if (!mask) { SNET_ERR(pdev, "Failed to find a PCI BAR\n"); return -ENODEV; } snprintf(name, sizeof(name), "psnet[%s]-bars", pci_name(pdev)); ret = pcim_iomap_regions(pdev, mask, name); if (ret) { SNET_ERR(pdev, "Failed to request and map PCI BARs\n"); return ret; } for (i = 0; i < PCI_STD_NUM_BARS; i++) { if (mask & (1 << i)) psnet->bars[i] = pcim_iomap_table(pdev)[i]; } return 0; } static int snet_open_vf_bar(struct pci_dev *pdev, struct snet *snet) { char name[50]; int ret; snprintf(name, sizeof(name), "snet[%s]-bar", pci_name(pdev)); /* Request and map BAR */ ret = pcim_iomap_regions(pdev, BIT(snet->psnet->cfg.vf_bar), name); if (ret) { SNET_ERR(pdev, "Failed to request and map PCI BAR for a VF\n"); return ret; } snet->bar = pcim_iomap_table(pdev)[snet->psnet->cfg.vf_bar]; return 0; } static void snet_free_cfg(struct snet_cfg *cfg) { u32 i; if (!cfg->devs) return; /* 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. */ static int psnet_detect_bar(struct psnet *psnet, u32 off) { unsigned long exit_time; int i; exit_time = jiffies + usecs_to_jiffies(SNET_DETECT_TIMEOUT); /* 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; if (ioread32(psnet->bars[i] + off) == SNET_SIGNATURE) return i; } usleep_range(1000, 10000); } return -ENODEV; } static void psnet_unmap_unused_bars(struct pci_dev *pdev, struct psnet *psnet) { int i, mask = 0; for (i = 0; i < PCI_STD_NUM_BARS; i++) { if (psnet->bars[i] && i != psnet->barno) mask |= (1 << i); } if (mask) pcim_iounmap_regions(pdev, mask); } /* Read SNET config from PCI BAR */ static int 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; } static int 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; } static int 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; } return 0; } static void snet_free_vqs(struct snet *snet) { u32 i; if (!snet->vqs) return; for (i = 0 ; i < snet->cfg->vq_num ; i++) { if (!snet->vqs[i]) break; kfree(snet->vqs[i]); } kfree(snet->vqs); } static int snet_build_vqs(struct snet *snet) { u32 i; /* Allocate the VQ pointers array */ snet->vqs = kcalloc(snet->cfg->vq_num, sizeof(void *), GFP_KERNEL); if (!snet->vqs) return -ENOMEM; /* Allocate the VQs */ for (i = 0; i < snet->cfg->vq_num; i++) { snet->vqs[i] = kzalloc(sizeof(*snet->vqs[i]), GFP_KERNEL); if (!snet->vqs[i]) { snet_free_vqs(snet); return -ENOMEM; } /* Reset IRQ num */ snet->vqs[i]->irq = -1; /* VQ serial ID */ snet->vqs[i]->sid = i; /* Kick address - every VQ gets 4B */ snet->vqs[i]->kick_ptr = snet->bar + snet->psnet->cfg.kick_off + snet->vqs[i]->sid * 4; /* Clear kick address for this VQ */ iowrite32(0, snet->vqs[i]->kick_ptr); } return 0; } static int psnet_get_next_irq_num(struct psnet *psnet) { int irq; spin_lock(&psnet->lock); irq = psnet->next_irq++; spin_unlock(&psnet->lock); return irq; } static void snet_reserve_irq_idx(struct pci_dev *pdev, struct snet *snet) { struct psnet *psnet = snet->psnet; int i; /* 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 */ static struct 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 */ static int 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; /* Init PSNET spinlock */ spin_lock_init(&psnet->lock); pci_set_master(pdev); pci_set_drvdata(pdev, psnet); /* Open SNET MAIN BAR */ ret = psnet_open_pf_bar(pdev, psnet); if (ret) goto free_psnet; /* Try to read SNET's config from PCI BAR */ ret = psnet_read_cfg(pdev, psnet); if (ret) goto free_psnet; /* If SNET_CFG_FLAG_IRQ_PF flag is set, we should use * PF MSI-X vectors */ pf_irqs = PSNET_FLAG_ON(psnet, SNET_CFG_FLAG_IRQ_PF); if (pf_irqs) { ret = psnet_alloc_irq_vector(pdev, psnet); if (ret) goto free_cfg; } SNET_DBG(pdev, "Enable %u virtual functions\n", psnet->cfg.vf_num); ret = pci_enable_sriov(pdev, psnet->cfg.vf_num); if (ret) { SNET_ERR(pdev, "Failed to enable SR-IOV\n"); goto free_irq; } /* Create HW monitor device */ if (PSNET_FLAG_ON(psnet, SNET_CFG_FLAG_HWMON)) { #if IS_ENABLED(CONFIG_HWMON) psnet_create_hwmon(pdev); #else SNET_WARN(pdev, "Can't start HWMON, CONFIG_HWMON is not enabled\n"); #endif } return 0; free_irq: if (pf_irqs) pci_free_irq_vectors(pdev); free_cfg: snet_free_cfg(&psnet->cfg); free_psnet: kfree(psnet); return ret; } /* Probe function for a virtual PCI function */ static int 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); /* Save pci device pointer */ snet->pdev = pdev; snet->psnet = psnet; snet->cfg = dev_cfg; snet->dpu_ready = false; snet->sid = vfid; /* Reset IRQ value */ snet->cfg_irq = -1; ret = snet_open_vf_bar(pdev, snet); if (ret) goto put_device; /* Create a VirtIO config pointer */ snet->cfg->virtio_cfg = snet->bar + snet->psnet->cfg.virtio_cfg_off; /* Clear control registers */ snet_ctrl_clear(snet); pci_set_master(pdev); pci_set_drvdata(pdev, snet); 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); /*set DMA device*/ snet->vdpa.dma_dev = &pdev->dev; /* Register VDPA device */ ret = vdpa_register_device(&snet->vdpa, snet->cfg->vq_num); if (ret) { SNET_ERR(pdev, "Failed to register vdpa device\n"); goto free_vqs; } return 0; free_vqs: snet_free_vqs(snet); put_device: put_device(&snet->vdpa.dev); free_irqs: if (!pf_irqs) pci_free_irq_vectors(pdev); return ret; } static int snet_vdpa_probe(struct pci_dev *pdev, const struct pci_device_id *id) { if (pdev->is_virtfn) return snet_vdpa_probe_vf(pdev); else return snet_vdpa_probe_pf(pdev); } static void snet_vdpa_remove_pf(struct pci_dev *pdev) { struct psnet *psnet = pci_get_drvdata(pdev); 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); snet_free_cfg(&psnet->cfg); kfree(psnet); } static void snet_vdpa_remove_vf(struct pci_dev *pdev) { struct snet *snet = pci_get_drvdata(pdev); struct psnet *psnet = snet->psnet; 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); } static void snet_vdpa_remove(struct pci_dev *pdev) { if (pdev->is_virtfn) snet_vdpa_remove_vf(pdev); else snet_vdpa_remove_pf(pdev); } static struct pci_device_id snet_driver_pci_ids[] = { { PCI_DEVICE_SUB(PCI_VENDOR_ID_SOLIDRUN, SNET_DEVICE_ID, PCI_VENDOR_ID_SOLIDRUN, SNET_DEVICE_ID) }, { 0 }, }; MODULE_DEVICE_TABLE(pci, snet_driver_pci_ids); static struct pci_driver snet_vdpa_driver = { .name = "snet-vdpa-driver", .id_table = snet_driver_pci_ids, .probe = snet_vdpa_probe, .remove = snet_vdpa_remove, }; module_pci_driver(snet_vdpa_driver); MODULE_AUTHOR("Alvaro Karsz <alvaro.karsz@solid-run.com>"); MODULE_DESCRIPTION("SolidRun vDPA driver"); MODULE_LICENSE("GPL v2");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1