Skip to content

Commit 0791153

Browse files
committed
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>
1 parent f86e513 commit 0791153

5 files changed

Lines changed: 139 additions & 0 deletions

File tree

drivers/pci/ide.c

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,50 @@ static int sel_ide_offset(struct pci_dev *pdev,
3535
settings->stream_index, pdev->nr_ide_mem);
3636
}
3737

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+
3879
void pci_ide_init(struct pci_dev *pdev)
3980
{
81+
struct pci_host_bridge *hb = pci_find_host_bridge(pdev->bus);
4082
u16 nr_link_ide, nr_ide_mem, nr_streams;
4183
u16 ide_cap;
4284
u32 val;
@@ -83,6 +125,7 @@ void pci_ide_init(struct pci_dev *pdev)
83125
int pos = __sel_ide_offset(ide_cap, nr_link_ide, i, nr_ide_mem);
84126
int nr_assoc;
85127
u32 val;
128+
u8 id;
86129

87130
pci_read_config_dword(pdev, pos + PCI_IDE_SEL_CAP, &val);
88131

@@ -98,6 +141,51 @@ void pci_ide_init(struct pci_dev *pdev)
98141
}
99142

100143
nr_ide_mem = nr_assoc;
144+
145+
/*
146+
* Claim Stream IDs and Selective Stream blocks that are already
147+
* active on the device
148+
*/
149+
pci_read_config_dword(pdev, pos + PCI_IDE_SEL_CTL, &val);
150+
id = FIELD_GET(PCI_IDE_SEL_CTL_ID, val);
151+
if ((val & PCI_IDE_SEL_CTL_EN) &&
152+
!claim_stream(hb, id, pdev, i))
153+
return;
154+
}
155+
156+
/* Reserve link stream-ids that are already active on the device */
157+
for (u16 i = 0; i < nr_link_ide; ++i) {
158+
int pos = ide_cap + PCI_IDE_LINK_STREAM_0 + i * PCI_IDE_LINK_BLOCK_SIZE;
159+
u8 id;
160+
161+
pci_read_config_dword(pdev, pos + PCI_IDE_LINK_CTL_0, &val);
162+
id = FIELD_GET(PCI_IDE_LINK_CTL_ID, val);
163+
if ((val & PCI_IDE_LINK_CTL_EN) &&
164+
!claim_stream(hb, id, NULL, -1))
165+
return;
166+
}
167+
168+
for (u16 i = 0; i < nr_streams; i++) {
169+
int pos = __sel_ide_offset(ide_cap, nr_link_ide, i, nr_ide_mem);
170+
171+
pci_read_config_dword(pdev, pos + PCI_IDE_SEL_CAP, &val);
172+
if (val & PCI_IDE_SEL_CTL_EN)
173+
continue;
174+
val &= ~PCI_IDE_SEL_CTL_ID;
175+
val |= FIELD_PREP(PCI_IDE_SEL_CTL_ID, PCI_IDE_RESERVED_STREAM_ID);
176+
pci_write_config_dword(pdev, pos + PCI_IDE_SEL_CTL, val);
177+
}
178+
179+
for (u16 i = 0; i < nr_link_ide; ++i) {
180+
int pos = ide_cap + PCI_IDE_LINK_STREAM_0 +
181+
i * PCI_IDE_LINK_BLOCK_SIZE;
182+
183+
pci_read_config_dword(pdev, pos, &val);
184+
if (val & PCI_IDE_LINK_CTL_EN)
185+
continue;
186+
val &= ~PCI_IDE_LINK_CTL_ID;
187+
val |= FIELD_PREP(PCI_IDE_LINK_CTL_ID, PCI_IDE_RESERVED_STREAM_ID);
188+
pci_write_config_dword(pdev, pos, val);
101189
}
102190

103191
pdev->ide_cap = ide_cap;
@@ -301,6 +389,28 @@ void pci_ide_stream_release(struct pci_ide *ide)
301389
}
302390
EXPORT_SYMBOL_GPL(pci_ide_stream_release);
303391

392+
struct pci_ide_stream_id {
393+
struct pci_host_bridge *hb;
394+
u8 stream_id;
395+
};
396+
397+
static struct pci_ide_stream_id *
398+
request_stream_id(struct pci_host_bridge *hb, u8 stream_id,
399+
struct pci_ide_stream_id *sid)
400+
{
401+
if (!reserve_stream_id(hb, stream_id))
402+
return NULL;
403+
404+
*sid = (struct pci_ide_stream_id) {
405+
.hb = hb,
406+
.stream_id = stream_id,
407+
};
408+
409+
return sid;
410+
}
411+
DEFINE_FREE(free_stream_id, struct pci_ide_stream_id *,
412+
if (_T) ida_free(&_T->hb->ide_stream_ids_ida, _T->stream_id))
413+
304414
/**
305415
* pci_ide_stream_register() - Prepare to activate an IDE Stream
306416
* @ide: IDE settings descriptor
@@ -313,6 +423,7 @@ int pci_ide_stream_register(struct pci_ide *ide)
313423
{
314424
struct pci_dev *pdev = ide->pdev;
315425
struct pci_host_bridge *hb = pci_find_host_bridge(pdev->bus);
426+
struct pci_ide_stream_id __sid;
316427
u8 ep_stream, rp_stream;
317428
int rc;
318429

@@ -321,6 +432,13 @@ int pci_ide_stream_register(struct pci_ide *ide)
321432
return -ENXIO;
322433
}
323434

435+
struct pci_ide_stream_id *sid __free(free_stream_id) =
436+
request_stream_id(hb, ide->stream_id, &__sid);
437+
if (!sid) {
438+
pci_err(pdev, "Setup fail: Stream ID %d in use\n", ide->stream_id);
439+
return -EBUSY;
440+
}
441+
324442
ep_stream = ide->partner[PCI_IDE_EP].stream_index;
325443
rp_stream = ide->partner[PCI_IDE_RP].stream_index;
326444
const char *name __free(kfree) = kasprintf(GFP_KERNEL, "stream%d.%d.%d",
@@ -335,6 +453,9 @@ int pci_ide_stream_register(struct pci_ide *ide)
335453

336454
ide->name = no_free_ptr(name);
337455

456+
/* Stream ID reservation recorded in @ide is now successfully registered */
457+
retain_and_null_ptr(sid);
458+
338459
return 0;
339460
}
340461
EXPORT_SYMBOL_GPL(pci_ide_stream_register);
@@ -353,6 +474,7 @@ void pci_ide_stream_unregister(struct pci_ide *ide)
353474

354475
sysfs_remove_link(&hb->dev.kobj, ide->name);
355476
kfree(ide->name);
477+
ida_free(&hb->ide_stream_ids_ida, ide->stream_id);
356478
ide->name = NULL;
357479
}
358480
EXPORT_SYMBOL_GPL(pci_ide_stream_unregister);
@@ -616,6 +738,8 @@ void pci_ide_init_host_bridge(struct pci_host_bridge *hb)
616738
{
617739
hb->nr_ide_streams = 256;
618740
ida_init(&hb->ide_stream_ida);
741+
ida_init(&hb->ide_stream_ids_ida);
742+
reserve_stream_id(hb, PCI_IDE_RESERVED_STREAM_ID);
619743
}
620744

621745
static ssize_t available_secure_streams_show(struct device *dev,
@@ -684,3 +808,8 @@ void pci_ide_set_nr_streams(struct pci_host_bridge *hb, u16 nr)
684808
sysfs_update_group(&hb->dev.kobj, &pci_ide_attr_group);
685809
}
686810
EXPORT_SYMBOL_NS_GPL(pci_ide_set_nr_streams, "PCI_IDE");
811+
812+
void pci_ide_destroy(struct pci_dev *pdev)
813+
{
814+
ida_destroy(&pdev->ide_stream_ida);
815+
}

drivers/pci/pci.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,10 +616,12 @@ static inline void pci_doe_sysfs_teardown(struct pci_dev *pdev) { }
616616
#ifdef CONFIG_PCI_IDE
617617
void pci_ide_init(struct pci_dev *dev);
618618
void pci_ide_init_host_bridge(struct pci_host_bridge *hb);
619+
void pci_ide_destroy(struct pci_dev *dev);
619620
extern const struct attribute_group pci_ide_attr_group;
620621
#else
621622
static inline void pci_ide_init(struct pci_dev *dev) { }
622623
static inline void pci_ide_init_host_bridge(struct pci_host_bridge *hb) { }
624+
static inline void pci_ide_destroy(struct pci_dev *dev) { }
623625
#endif
624626

625627
#ifdef CONFIG_PCI_TSM

drivers/pci/remove.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ static void pci_destroy_dev(struct pci_dev *dev)
7070
up_write(&pci_bus_sem);
7171

7272
pci_doe_destroy(dev);
73+
pci_ide_destroy(dev);
7374
pcie_aspm_exit_link_state(dev);
7475
pci_bridge_d3_update(dev);
7576
pci_pwrctrl_unregister(&dev->dev);

include/linux/pci-ide.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ struct pci_ide {
9797
struct tsm_dev *tsm_dev;
9898
};
9999

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+
100106
void pci_ide_set_nr_streams(struct pci_host_bridge *hb, u16 nr);
101107
struct pci_ide_partner *pci_ide_to_settings(struct pci_dev *pdev,
102108
struct pci_ide *ide);

include/linux/pci.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,7 @@ struct pci_host_bridge {
619619
#ifdef CONFIG_PCI_IDE
620620
u16 nr_ide_streams; /* Max streams possibly active in @ide_stream_ida */
621621
struct ida ide_stream_ida;
622+
struct ida ide_stream_ids_ida; /* track unique ids per domain */
622623
#endif
623624
u8 (*swizzle_irq)(struct pci_dev *, u8 *); /* Platform IRQ swizzler */
624625
int (*map_irq)(const struct pci_dev *, u8, u8);

0 commit comments

Comments
 (0)