Skip to content

Commit

Permalink
s390/pci: improve zpci_dev reference counting
Browse files Browse the repository at this point in the history
[ Upstream commit c122383 ]

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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
niklas88 authored and gregkh committed May 25, 2022
1 parent 7d3f69c commit f0931ee
Show file tree
Hide file tree
Showing 4 changed files with 16 additions and 4 deletions.
1 change: 1 addition & 0 deletions arch/s390/pci/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ struct zpci_dev *get_zdev_by_fid(u32 fid)
list_for_each_entry(tmp, &zpci_list, entry) {
if (tmp->fid == fid) {
zdev = tmp;
zpci_zdev_get(zdev);
break;
}
}
Expand Down
3 changes: 2 additions & 1 deletion arch/s390/pci/pci_bus.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ void zpci_bus_device_unregister(struct zpci_dev *zdev);
void zpci_release_device(struct kref *kref);
static inline void zpci_zdev_put(struct zpci_dev *zdev)
{
kref_put(&zdev->kref, zpci_release_device);
if (zdev)
kref_put(&zdev->kref, zpci_release_device);
}

static inline void zpci_zdev_get(struct zpci_dev *zdev)
Expand Down
9 changes: 7 additions & 2 deletions arch/s390/pci/pci_clp.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
#include <asm/clp.h>
#include <uapi/asm/clp.h>

#include "pci_bus.h"

bool zpci_unique_uid;

void update_uid_checking(bool new)
Expand Down Expand Up @@ -372,8 +374,11 @@ static void __clp_add(struct clp_fh_list_entry *entry, void *data)
return;

zdev = get_zdev_by_fid(entry->fid);
if (!zdev)
zpci_create_device(entry->fid, entry->fh, entry->config_state);
if (zdev) {
zpci_zdev_put(zdev);
return;
}
zpci_create_device(entry->fid, entry->fh, entry->config_state);
}

int clp_scan_pci_devices(void)
Expand Down
7 changes: 6 additions & 1 deletion arch/s390/pci/pci_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,12 @@ static void __zpci_event_error(struct zpci_ccdf_err *ccdf)
pdev ? pci_name(pdev) : "n/a", ccdf->pec, ccdf->fid);

if (!pdev)
return;
goto no_pdev;

pdev->error_state = pci_channel_io_perm_failure;
pci_dev_put(pdev);
no_pdev:
zpci_zdev_put(zdev);
}

void zpci_event_error(void *data)
Expand All @@ -76,6 +78,7 @@ void zpci_event_error(void *data)
static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
{
struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);
bool existing_zdev = !!zdev;
enum zpci_state state;
struct pci_dev *pdev;
int ret;
Expand Down Expand Up @@ -161,6 +164,8 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
default:
break;
}
if (existing_zdev)
zpci_zdev_put(zdev);
}

void zpci_event_availability(void *data)
Expand Down

0 comments on commit f0931ee

Please sign in to comment.