Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PCI/IDE: Initialize an ID for all IDE streams

The PCIe spec defines two types of streams - selective and link. Each
stream has an ID from the same bucket so a stream ID does not tell the
type. The spec defines an "enable" bit for every stream and required
stream IDs to be unique among all enabled stream but there is no such
requirement for disabled streams.

However, when IDE_KM is programming keys, an IDE-capable device needs
to know the type of stream being programmed to write it directly to
the hardware as keys are relatively large, possibly many of them and
devices often struggle with keeping around rather big data not being
used.

Walk through all streams on a device and initialise the IDs to some
unique number, both link and selective.

The weakest part of this proposal is the host bridge ide_stream_ids_ida.
Technically, a Stream ID only needs to be unique within a given partner
pair. However, with "anonymous" / unassigned streams there is no convenient
place to track the available ids. Proceed with an ida in the host bridge
for now, but consider moving this tracking to be an ide_stream_ids_ida per
device.

Co-developed-by: Alexey Kardashevskiy <aik@amd.com>
Signed-off-by: Alexey Kardashevskiy <aik@amd.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Link: https://patch.msgid.link/20251113021446.436830-6-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>

+139
+129
drivers/pci/ide.c
··· 35 35 settings->stream_index, pdev->nr_ide_mem); 36 36 } 37 37 38 + static bool reserve_stream_index(struct pci_dev *pdev, u8 idx) 39 + { 40 + int ret; 41 + 42 + ret = ida_alloc_range(&pdev->ide_stream_ida, idx, idx, GFP_KERNEL); 43 + return ret >= 0; 44 + } 45 + 46 + static bool reserve_stream_id(struct pci_host_bridge *hb, u8 id) 47 + { 48 + int ret; 49 + 50 + ret = ida_alloc_range(&hb->ide_stream_ids_ida, id, id, GFP_KERNEL); 51 + return ret >= 0; 52 + } 53 + 54 + static bool claim_stream(struct pci_host_bridge *hb, u8 stream_id, 55 + struct pci_dev *pdev, u8 stream_idx) 56 + { 57 + dev_info(&hb->dev, "Stream ID %d active at init\n", stream_id); 58 + if (!reserve_stream_id(hb, stream_id)) { 59 + dev_info(&hb->dev, "Failed to claim %s Stream ID %d\n", 60 + stream_id == PCI_IDE_RESERVED_STREAM_ID ? "reserved" : 61 + "active", 62 + stream_id); 63 + return false; 64 + } 65 + 66 + /* No stream index to reserve in the Link IDE case */ 67 + if (!pdev) 68 + return true; 69 + 70 + if (!reserve_stream_index(pdev, stream_idx)) { 71 + pci_info(pdev, "Failed to claim active Selective Stream %d\n", 72 + stream_idx); 73 + return false; 74 + } 75 + 76 + return true; 77 + } 78 + 38 79 void pci_ide_init(struct pci_dev *pdev) 39 80 { 81 + struct pci_host_bridge *hb = pci_find_host_bridge(pdev->bus); 40 82 u16 nr_link_ide, nr_ide_mem, nr_streams; 41 83 u16 ide_cap; 42 84 u32 val; ··· 125 83 int pos = __sel_ide_offset(ide_cap, nr_link_ide, i, nr_ide_mem); 126 84 int nr_assoc; 127 85 u32 val; 86 + u8 id; 128 87 129 88 pci_read_config_dword(pdev, pos + PCI_IDE_SEL_CAP, &val); 130 89 ··· 141 98 } 142 99 143 100 nr_ide_mem = nr_assoc; 101 + 102 + /* 103 + * Claim Stream IDs and Selective Stream blocks that are already 104 + * active on the device 105 + */ 106 + pci_read_config_dword(pdev, pos + PCI_IDE_SEL_CTL, &val); 107 + id = FIELD_GET(PCI_IDE_SEL_CTL_ID, val); 108 + if ((val & PCI_IDE_SEL_CTL_EN) && 109 + !claim_stream(hb, id, pdev, i)) 110 + return; 111 + } 112 + 113 + /* Reserve link stream-ids that are already active on the device */ 114 + for (u16 i = 0; i < nr_link_ide; ++i) { 115 + int pos = ide_cap + PCI_IDE_LINK_STREAM_0 + i * PCI_IDE_LINK_BLOCK_SIZE; 116 + u8 id; 117 + 118 + pci_read_config_dword(pdev, pos + PCI_IDE_LINK_CTL_0, &val); 119 + id = FIELD_GET(PCI_IDE_LINK_CTL_ID, val); 120 + if ((val & PCI_IDE_LINK_CTL_EN) && 121 + !claim_stream(hb, id, NULL, -1)) 122 + return; 123 + } 124 + 125 + for (u16 i = 0; i < nr_streams; i++) { 126 + int pos = __sel_ide_offset(ide_cap, nr_link_ide, i, nr_ide_mem); 127 + 128 + pci_read_config_dword(pdev, pos + PCI_IDE_SEL_CAP, &val); 129 + if (val & PCI_IDE_SEL_CTL_EN) 130 + continue; 131 + val &= ~PCI_IDE_SEL_CTL_ID; 132 + val |= FIELD_PREP(PCI_IDE_SEL_CTL_ID, PCI_IDE_RESERVED_STREAM_ID); 133 + pci_write_config_dword(pdev, pos + PCI_IDE_SEL_CTL, val); 134 + } 135 + 136 + for (u16 i = 0; i < nr_link_ide; ++i) { 137 + int pos = ide_cap + PCI_IDE_LINK_STREAM_0 + 138 + i * PCI_IDE_LINK_BLOCK_SIZE; 139 + 140 + pci_read_config_dword(pdev, pos, &val); 141 + if (val & PCI_IDE_LINK_CTL_EN) 142 + continue; 143 + val &= ~PCI_IDE_LINK_CTL_ID; 144 + val |= FIELD_PREP(PCI_IDE_LINK_CTL_ID, PCI_IDE_RESERVED_STREAM_ID); 145 + pci_write_config_dword(pdev, pos, val); 144 146 } 145 147 146 148 pdev->ide_cap = ide_cap; ··· 389 301 } 390 302 EXPORT_SYMBOL_GPL(pci_ide_stream_release); 391 303 304 + struct pci_ide_stream_id { 305 + struct pci_host_bridge *hb; 306 + u8 stream_id; 307 + }; 308 + 309 + static struct pci_ide_stream_id * 310 + request_stream_id(struct pci_host_bridge *hb, u8 stream_id, 311 + struct pci_ide_stream_id *sid) 312 + { 313 + if (!reserve_stream_id(hb, stream_id)) 314 + return NULL; 315 + 316 + *sid = (struct pci_ide_stream_id) { 317 + .hb = hb, 318 + .stream_id = stream_id, 319 + }; 320 + 321 + return sid; 322 + } 323 + DEFINE_FREE(free_stream_id, struct pci_ide_stream_id *, 324 + if (_T) ida_free(&_T->hb->ide_stream_ids_ida, _T->stream_id)) 325 + 392 326 /** 393 327 * pci_ide_stream_register() - Prepare to activate an IDE Stream 394 328 * @ide: IDE settings descriptor ··· 423 313 { 424 314 struct pci_dev *pdev = ide->pdev; 425 315 struct pci_host_bridge *hb = pci_find_host_bridge(pdev->bus); 316 + struct pci_ide_stream_id __sid; 426 317 u8 ep_stream, rp_stream; 427 318 int rc; 428 319 429 320 if (ide->stream_id < 0 || ide->stream_id > U8_MAX) { 430 321 pci_err(pdev, "Setup fail: Invalid Stream ID: %d\n", ide->stream_id); 431 322 return -ENXIO; 323 + } 324 + 325 + struct pci_ide_stream_id *sid __free(free_stream_id) = 326 + request_stream_id(hb, ide->stream_id, &__sid); 327 + if (!sid) { 328 + pci_err(pdev, "Setup fail: Stream ID %d in use\n", ide->stream_id); 329 + return -EBUSY; 432 330 } 433 331 434 332 ep_stream = ide->partner[PCI_IDE_EP].stream_index; ··· 452 334 return rc; 453 335 454 336 ide->name = no_free_ptr(name); 337 + 338 + /* Stream ID reservation recorded in @ide is now successfully registered */ 339 + retain_and_null_ptr(sid); 455 340 456 341 return 0; 457 342 } ··· 474 353 475 354 sysfs_remove_link(&hb->dev.kobj, ide->name); 476 355 kfree(ide->name); 356 + ida_free(&hb->ide_stream_ids_ida, ide->stream_id); 477 357 ide->name = NULL; 478 358 } 479 359 EXPORT_SYMBOL_GPL(pci_ide_stream_unregister); ··· 738 616 { 739 617 hb->nr_ide_streams = 256; 740 618 ida_init(&hb->ide_stream_ida); 619 + ida_init(&hb->ide_stream_ids_ida); 620 + reserve_stream_id(hb, PCI_IDE_RESERVED_STREAM_ID); 741 621 } 742 622 743 623 static ssize_t available_secure_streams_show(struct device *dev, ··· 808 684 sysfs_update_group(&hb->dev.kobj, &pci_ide_attr_group); 809 685 } 810 686 EXPORT_SYMBOL_NS_GPL(pci_ide_set_nr_streams, "PCI_IDE"); 687 + 688 + void pci_ide_destroy(struct pci_dev *pdev) 689 + { 690 + ida_destroy(&pdev->ide_stream_ida); 691 + }
+2
drivers/pci/pci.h
··· 616 616 #ifdef CONFIG_PCI_IDE 617 617 void pci_ide_init(struct pci_dev *dev); 618 618 void pci_ide_init_host_bridge(struct pci_host_bridge *hb); 619 + void pci_ide_destroy(struct pci_dev *dev); 619 620 extern const struct attribute_group pci_ide_attr_group; 620 621 #else 621 622 static inline void pci_ide_init(struct pci_dev *dev) { } 622 623 static inline void pci_ide_init_host_bridge(struct pci_host_bridge *hb) { } 624 + static inline void pci_ide_destroy(struct pci_dev *dev) { } 623 625 #endif 624 626 625 627 #ifdef CONFIG_PCI_TSM
+1
drivers/pci/remove.c
··· 70 70 up_write(&pci_bus_sem); 71 71 72 72 pci_doe_destroy(dev); 73 + pci_ide_destroy(dev); 73 74 pcie_aspm_exit_link_state(dev); 74 75 pci_bridge_d3_update(dev); 75 76 pci_pwrctrl_unregister(&dev->dev);
+6
include/linux/pci-ide.h
··· 97 97 struct tsm_dev *tsm_dev; 98 98 }; 99 99 100 + /* 101 + * Some devices need help with aliased stream-ids even for idle streams. Use 102 + * this id as the "never enabled" place holder. 103 + */ 104 + #define PCI_IDE_RESERVED_STREAM_ID 255 105 + 100 106 void pci_ide_set_nr_streams(struct pci_host_bridge *hb, u16 nr); 101 107 struct pci_ide_partner *pci_ide_to_settings(struct pci_dev *pdev, 102 108 struct pci_ide *ide);
+1
include/linux/pci.h
··· 619 619 #ifdef CONFIG_PCI_IDE 620 620 u16 nr_ide_streams; /* Max streams possibly active in @ide_stream_ida */ 621 621 struct ida ide_stream_ida; 622 + struct ida ide_stream_ids_ida; /* track unique ids per domain */ 622 623 #endif 623 624 u8 (*swizzle_irq)(struct pci_dev *, u8 *); /* Platform IRQ swizzler */ 624 625 int (*map_irq)(const struct pci_dev *, u8, u8);