| From da78e46fc50630bac57dacac3d2bc32e06343c85 Mon Sep 17 00:00:00 2001 |
| From: Niklas Schnelle <schnelle@linux.ibm.com> |
| Date: Tue, 17 Dec 2019 09:34:46 +0100 |
| Subject: [PATCH] s390/pci: Recover handle in clp_set_pci_fn() |
| |
| commit 17cdec960cf776b20b1fb08c622221babe591d51 upstream. |
| |
| When we try to recover a PCI function using |
| |
| echo 1 > /sys/bus/pci/devices/<id>/recover |
| |
| or manually with |
| |
| echo 1 > /sys/bus/pci/devices/<id>/remove |
| echo 0 > /sys/bus/pci/slots/<slot>/power |
| echo 1 > /sys/bus/pci/slots/<slot>/power |
| |
| clp_disable_fn() / clp_enable_fn() call clp_set_pci_fn() to first |
| disable and then reenable the function. |
| |
| When the function is already in the requested state we may be left with |
| an invalid function handle. |
| |
| To get a new valid handle we do a clp_list_pci() call. For this we need |
| both the function ID and function handle in clp_set_pci_fn() so pass the |
| zdev and get both. |
| |
| To simplify things also pull setting the refreshed function handle into |
| clp_set_pci_fn() |
| |
| Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com> |
| Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com> |
| Signed-off-by: Vasily Gorbik <gor@linux.ibm.com> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h |
| index 305befd55326..0cca0e2d2a35 100644 |
| --- a/arch/s390/include/asm/pci.h |
| +++ b/arch/s390/include/asm/pci.h |
| @@ -183,7 +183,7 @@ void zpci_remove_reserved_devices(void); |
| /* CLP */ |
| int clp_scan_pci_devices(void); |
| int clp_rescan_pci_devices(void); |
| -int clp_rescan_pci_devices_simple(void); |
| +int clp_rescan_pci_devices_simple(u32 *fid); |
| int clp_add_pci_device(u32, u32, int); |
| int clp_enable_fh(struct zpci_dev *, u8); |
| int clp_disable_fh(struct zpci_dev *); |
| diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c |
| index 86ca7f88fb22..a61348b96ad2 100644 |
| --- a/arch/s390/pci/pci.c |
| +++ b/arch/s390/pci/pci.c |
| @@ -931,5 +931,5 @@ subsys_initcall_sync(pci_base_init); |
| void zpci_rescan(void) |
| { |
| if (zpci_is_enabled()) |
| - clp_rescan_pci_devices_simple(); |
| + clp_rescan_pci_devices_simple(NULL); |
| } |
| diff --git a/arch/s390/pci/pci_clp.c b/arch/s390/pci/pci_clp.c |
| index d03631dba7c2..ff5a46e22839 100644 |
| --- a/arch/s390/pci/pci_clp.c |
| +++ b/arch/s390/pci/pci_clp.c |
| @@ -240,12 +240,14 @@ int clp_add_pci_device(u32 fid, u32 fh, int configured) |
| } |
| |
| /* |
| - * Enable/Disable a given PCI function defined by its function handle. |
| + * Enable/Disable a given PCI function and update its function handle if |
| + * necessary |
| */ |
| -static int clp_set_pci_fn(u32 *fh, u8 nr_dma_as, u8 command) |
| +static int clp_set_pci_fn(struct zpci_dev *zdev, u8 nr_dma_as, u8 command) |
| { |
| struct clp_req_rsp_set_pci *rrb; |
| int rc, retries = 100; |
| + u32 fid = zdev->fid; |
| |
| rrb = clp_alloc_block(GFP_KERNEL); |
| if (!rrb) |
| @@ -256,7 +258,7 @@ static int clp_set_pci_fn(u32 *fh, u8 nr_dma_as, u8 command) |
| rrb->request.hdr.len = sizeof(rrb->request); |
| rrb->request.hdr.cmd = CLP_SET_PCI_FN; |
| rrb->response.hdr.len = sizeof(rrb->response); |
| - rrb->request.fh = *fh; |
| + rrb->request.fh = zdev->fh; |
| rrb->request.oc = command; |
| rrb->request.ndas = nr_dma_as; |
| |
| @@ -269,12 +271,17 @@ static int clp_set_pci_fn(u32 *fh, u8 nr_dma_as, u8 command) |
| } |
| } while (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY); |
| |
| - if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) |
| - *fh = rrb->response.fh; |
| - else { |
| + if (rc || rrb->response.hdr.rsp != CLP_RC_OK) { |
| zpci_err("Set PCI FN:\n"); |
| zpci_err_clp(rrb->response.hdr.rsp, rc); |
| - rc = -EIO; |
| + } |
| + |
| + if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) { |
| + zdev->fh = rrb->response.fh; |
| + } else if (!rc && rrb->response.hdr.rsp == CLP_RC_SETPCIFN_ALRDY && |
| + rrb->response.fh == 0) { |
| + /* Function is already in desired state - update handle */ |
| + rc = clp_rescan_pci_devices_simple(&fid); |
| } |
| clp_free_block(rrb); |
| return rc; |
| @@ -282,18 +289,17 @@ static int clp_set_pci_fn(u32 *fh, u8 nr_dma_as, u8 command) |
| |
| int clp_enable_fh(struct zpci_dev *zdev, u8 nr_dma_as) |
| { |
| - u32 fh = zdev->fh; |
| int rc; |
| |
| - rc = clp_set_pci_fn(&fh, nr_dma_as, CLP_SET_ENABLE_PCI_FN); |
| - zpci_dbg(3, "ena fid:%x, fh:%x, rc:%d\n", zdev->fid, fh, rc); |
| + rc = clp_set_pci_fn(zdev, nr_dma_as, CLP_SET_ENABLE_PCI_FN); |
| + zpci_dbg(3, "ena fid:%x, fh:%x, rc:%d\n", zdev->fid, zdev->fh, rc); |
| if (rc) |
| goto out; |
| |
| - zdev->fh = fh; |
| if (zdev->mio_capable) { |
| - rc = clp_set_pci_fn(&fh, nr_dma_as, CLP_SET_ENABLE_MIO); |
| - zpci_dbg(3, "ena mio fid:%x, fh:%x, rc:%d\n", zdev->fid, fh, rc); |
| + rc = clp_set_pci_fn(zdev, nr_dma_as, CLP_SET_ENABLE_MIO); |
| + zpci_dbg(3, "ena mio fid:%x, fh:%x, rc:%d\n", |
| + zdev->fid, zdev->fh, rc); |
| if (rc) |
| clp_disable_fh(zdev); |
| } |
| @@ -309,11 +315,8 @@ int clp_disable_fh(struct zpci_dev *zdev) |
| if (!zdev_enabled(zdev)) |
| return 0; |
| |
| - rc = clp_set_pci_fn(&fh, 0, CLP_SET_DISABLE_PCI_FN); |
| + rc = clp_set_pci_fn(zdev, 0, CLP_SET_DISABLE_PCI_FN); |
| zpci_dbg(3, "dis fid:%x, fh:%x, rc:%d\n", zdev->fid, fh, rc); |
| - if (!rc) |
| - zdev->fh = fh; |
| - |
| return rc; |
| } |
| |
| @@ -370,10 +373,14 @@ static void __clp_add(struct clp_fh_list_entry *entry, void *data) |
| static void __clp_update(struct clp_fh_list_entry *entry, void *data) |
| { |
| struct zpci_dev *zdev; |
| + u32 *fid = data; |
| |
| if (!entry->vendor_id) |
| return; |
| |
| + if (fid && *fid != entry->fid) |
| + return; |
| + |
| zdev = get_zdev_by_fid(entry->fid); |
| if (!zdev) |
| return; |
| @@ -413,7 +420,10 @@ int clp_rescan_pci_devices(void) |
| return rc; |
| } |
| |
| -int clp_rescan_pci_devices_simple(void) |
| +/* Rescan PCI functions and refresh function handles. If fid is non-NULL only |
| + * refresh the handle of the function matching @fid |
| + */ |
| +int clp_rescan_pci_devices_simple(u32 *fid) |
| { |
| struct clp_req_rsp_list_pci *rrb; |
| int rc; |
| @@ -422,7 +432,7 @@ int clp_rescan_pci_devices_simple(void) |
| if (!rrb) |
| return -ENOMEM; |
| |
| - rc = clp_list_pci(rrb, NULL, __clp_update); |
| + rc = clp_list_pci(rrb, fid, __clp_update); |
| |
| clp_free_block(rrb); |
| return rc; |
| -- |
| 2.7.4 |
| |