Skip to content

Commit c122383

Browse files
niklas88Vasily Gorbik
authored andcommitted
s390/pci: improve zpci_dev reference counting
Currently zpci_dev uses kref based reference counting but only accounts for one original reference plus one reference from an added pci_dev to its underlying zpci_dev. Counting just the original reference worked until the pci_dev reference was added in commit 2a671f7 ("s390/pci: fix use after free of zpci_dev") because once a zpci_dev goes away, i.e. enters the reserved state, it would immediately get released. However with the pci_dev reference this is no longer the case and the zpci_dev may still appear in multiple availability events indicating that it was reserved. This was solved by detecting when the zpci_dev is already on its way out but still hanging around. This has however shown some light on how unusual our zpci_dev reference counting is. Improve upon this by modelling zpci_dev reference counting on pci_dev. Analogous to pci_get_slot() increment the reference count in get_zdev_by_fid(). Thus all users of get_zdev_by_fid() must drop the reference once they are done with the zpci_dev. Similar to pci_scan_single_device(), zpci_create_device() returns the device with an initial count of 1 and the device added to the zpci_list (analogous to the PCI bus' device_list). In turn users of zpci_create_device() must only drop the reference once the device is gone from the point of view of the zPCI subsystem, it might still be referenced by the common PCI subsystem though. Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com> Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com> Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
1 parent 7277b42 commit c122383

4 files changed

Lines changed: 16 additions & 4 deletions

File tree

arch/s390/pci/pci.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ struct zpci_dev *get_zdev_by_fid(u32 fid)
6969
list_for_each_entry(tmp, &zpci_list, entry) {
7070
if (tmp->fid == fid) {
7171
zdev = tmp;
72+
zpci_zdev_get(zdev);
7273
break;
7374
}
7475
}

arch/s390/pci/pci_bus.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ void zpci_bus_remove_device(struct zpci_dev *zdev, bool set_error);
1919
void zpci_release_device(struct kref *kref);
2020
static inline void zpci_zdev_put(struct zpci_dev *zdev)
2121
{
22-
kref_put(&zdev->kref, zpci_release_device);
22+
if (zdev)
23+
kref_put(&zdev->kref, zpci_release_device);
2324
}
2425

2526
static inline void zpci_zdev_get(struct zpci_dev *zdev)

arch/s390/pci/pci_clp.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include <asm/clp.h>
2424
#include <uapi/asm/clp.h>
2525

26+
#include "pci_bus.h"
27+
2628
bool zpci_unique_uid;
2729

2830
void update_uid_checking(bool new)
@@ -404,8 +406,11 @@ static void __clp_add(struct clp_fh_list_entry *entry, void *data)
404406
return;
405407

406408
zdev = get_zdev_by_fid(entry->fid);
407-
if (!zdev)
408-
zpci_create_device(entry->fid, entry->fh, entry->config_state);
409+
if (zdev) {
410+
zpci_zdev_put(zdev);
411+
return;
412+
}
413+
zpci_create_device(entry->fid, entry->fh, entry->config_state);
409414
}
410415

411416
int clp_scan_pci_devices(void)

arch/s390/pci/pci_event.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ static void __zpci_event_error(struct zpci_ccdf_err *ccdf)
269269
pdev ? pci_name(pdev) : "n/a", ccdf->pec, ccdf->fid);
270270

271271
if (!pdev)
272-
return;
272+
goto no_pdev;
273273

274274
switch (ccdf->pec) {
275275
case 0x003a: /* Service Action or Error Recovery Successful */
@@ -286,6 +286,8 @@ static void __zpci_event_error(struct zpci_ccdf_err *ccdf)
286286
break;
287287
}
288288
pci_dev_put(pdev);
289+
no_pdev:
290+
zpci_zdev_put(zdev);
289291
}
290292

291293
void zpci_event_error(void *data)
@@ -314,6 +316,7 @@ static void zpci_event_hard_deconfigured(struct zpci_dev *zdev, u32 fh)
314316
static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
315317
{
316318
struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);
319+
bool existing_zdev = !!zdev;
317320
enum zpci_state state;
318321

319322
zpci_dbg(3, "avl fid:%x, fh:%x, pec:%x\n",
@@ -378,6 +381,8 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
378381
default:
379382
break;
380383
}
384+
if (existing_zdev)
385+
zpci_zdev_put(zdev);
381386
}
382387

383388
void zpci_event_availability(void *data)

0 commit comments

Comments
 (0)