]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge branch 'pci/virtualization'
authorBjorn Helgaas <bhelgaas@google.com>
Sat, 20 Oct 2018 16:45:35 +0000 (11:45 -0500)
committerBjorn Helgaas <bhelgaas@google.com>
Sat, 20 Oct 2018 16:45:35 +0000 (11:45 -0500)
  - Cache VF config space size to optimize enumeration of many VFs
    (KarimAllah Ahmed)

  - Remove unnecessary <linux/pci-ats.h> include (Bjorn Helgaas)

* pci/virtualization:
  PCI/IOV: Remove unnecessary include of <linux/pci-ats.h>
  PCI/IOV: Use VF0 cached config space size for other VFs

122 files changed:
Documentation/ABI/testing/sysfs-bus-pci
Documentation/PCI/pci-error-recovery.txt
Documentation/driver-api/index.rst
Documentation/driver-api/pci/index.rst [new file with mode: 0644]
Documentation/driver-api/pci/p2pdma.rst [new file with mode: 0644]
Documentation/driver-api/pci/pci.rst [moved from Documentation/driver-api/pci.rst with 100% similarity]
Documentation/switchtec.txt
arch/arm64/kernel/pci.c
arch/powerpc/include/asm/pnv-pci.h
arch/x86/pci/acpi.c
drivers/acpi/pci_root.c
drivers/acpi/property.c
drivers/acpi/x86/apple.c
drivers/ata/sata_inic162x.c
drivers/block/rsxx/core.c
drivers/crypto/qat/qat_common/adf_aer.c
drivers/dma/ioat/init.c
drivers/gpio/gpiolib-acpi.c
drivers/infiniband/core/rw.c
drivers/infiniband/hw/cxgb4/qp.c
drivers/infiniband/hw/cxgb4/t4.h
drivers/infiniband/hw/hfi1/pcie.c
drivers/infiniband/hw/qib/qib_pcie.c
drivers/net/ethernet/atheros/alx/main.c
drivers/net/ethernet/broadcom/bnx2.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
drivers/net/ethernet/broadcom/bnxt/bnxt.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/ethernet/emulex/benet/be_main.c
drivers/net/ethernet/intel/e1000e/netdev.c
drivers/net/ethernet/intel/fm10k/fm10k_pci.c
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
drivers/net/ethernet/sfc/efx.c
drivers/net/ethernet/sfc/falcon/efx.c
drivers/nvme/host/core.c
drivers/nvme/host/nvme.h
drivers/nvme/host/pci.c
drivers/nvme/target/configfs.c
drivers/nvme/target/core.c
drivers/nvme/target/io-cmd-bdev.c
drivers/nvme/target/nvmet.h
drivers/nvme/target/rdma.c
drivers/pci/Kconfig
drivers/pci/Makefile
drivers/pci/access.c
drivers/pci/hotplug/TODO [new file with mode: 0644]
drivers/pci/hotplug/acpiphp.h
drivers/pci/hotplug/acpiphp_core.c
drivers/pci/hotplug/acpiphp_ibm.c
drivers/pci/hotplug/cpci_hotplug.h
drivers/pci/hotplug/cpci_hotplug_core.c
drivers/pci/hotplug/cpci_hotplug_pci.c
drivers/pci/hotplug/cpqphp.h
drivers/pci/hotplug/cpqphp_core.c
drivers/pci/hotplug/cpqphp_ctrl.c
drivers/pci/hotplug/ibmphp.h
drivers/pci/hotplug/ibmphp_core.c
drivers/pci/hotplug/ibmphp_ebda.c
drivers/pci/hotplug/pci_hotplug_core.c
drivers/pci/hotplug/pciehp.h
drivers/pci/hotplug/pciehp_core.c
drivers/pci/hotplug/pciehp_ctrl.c
drivers/pci/hotplug/pciehp_hpc.c
drivers/pci/hotplug/pciehp_pci.c
drivers/pci/hotplug/pnv_php.c
drivers/pci/hotplug/rpaphp.h
drivers/pci/hotplug/rpaphp_core.c
drivers/pci/hotplug/rpaphp_pci.c
drivers/pci/hotplug/rpaphp_slot.c
drivers/pci/hotplug/s390_pci_hpc.c
drivers/pci/hotplug/sgi_hotplug.c
drivers/pci/hotplug/shpchp.h
drivers/pci/hotplug/shpchp_core.c
drivers/pci/hotplug/shpchp_ctrl.c
drivers/pci/msi.c
drivers/pci/p2pdma.c [new file with mode: 0644]
drivers/pci/pci-acpi.c
drivers/pci/pci.c
drivers/pci/pci.h
drivers/pci/pcie/Kconfig
drivers/pci/pcie/aer.c
drivers/pci/pcie/aer_inject.c
drivers/pci/pcie/aspm.c
drivers/pci/pcie/dpc.c
drivers/pci/pcie/err.c
drivers/pci/pcie/pme.c
drivers/pci/pcie/portdrv.h
drivers/pci/pcie/portdrv_core.c
drivers/pci/pcie/portdrv_pci.c
drivers/pci/probe.c
drivers/pci/quirks.c
drivers/pci/remove.c
drivers/pci/setup-bus.c
drivers/pci/slot.c
drivers/platform/x86/asus-wmi.c
drivers/platform/x86/eeepc-laptop.c
drivers/s390/net/ism_drv.c
drivers/scsi/aacraid/linit.c
drivers/scsi/be2iscsi/be_main.c
drivers/scsi/bfa/bfad.c
drivers/scsi/csiostor/csio_init.c
drivers/scsi/lpfc/lpfc_init.c
drivers/scsi/mpt3sas/mpt3sas_scsih.c
drivers/scsi/qla2xxx/qla_os.c
drivers/scsi/qla4xxx/ql4_os.c
include/acpi/acpi_bus.h
include/linux/acpi.h
include/linux/blkdev.h
include/linux/memremap.h
include/linux/mm.h
include/linux/pci-dma-compat.h
include/linux/pci-dma.h [deleted file]
include/linux/pci-p2pdma.h [new file with mode: 0644]
include/linux/pci.h
include/linux/pci_hotplug.h
include/linux/pci_ids.h
include/uapi/linux/pci_regs.h

index 44d4b2be92fd4a56ab2420944f76669a2466a304..8bfee557e50eab1fc66cbd3bc790731f1719bd04 100644 (file)
@@ -323,3 +323,27 @@ Description:
 
                This is similar to /sys/bus/pci/drivers_autoprobe, but
                affects only the VFs associated with a specific PF.
+
+What:          /sys/bus/pci/devices/.../p2pmem/size
+Date:          November 2017
+Contact:       Logan Gunthorpe <logang@deltatee.com>
+Description:
+               If the device has any Peer-to-Peer memory registered, this
+               file contains the total amount of memory that the device
+               provides (in decimal).
+
+What:          /sys/bus/pci/devices/.../p2pmem/available
+Date:          November 2017
+Contact:       Logan Gunthorpe <logang@deltatee.com>
+Description:
+               If the device has any Peer-to-Peer memory registered, this
+               file contains the amount of memory that has not been
+               allocated (in decimal).
+
+What:          /sys/bus/pci/devices/.../p2pmem/published
+Date:          November 2017
+Contact:       Logan Gunthorpe <logang@deltatee.com>
+Description:
+               If the device has any Peer-to-Peer memory registered, this
+               file contains a '1' if the memory has been published for
+               use outside the driver that owns the device.
index 688b69121e8294ca81d47689c25345e97d5de720..0b6bb3ef449ee7a8ee45afe9b4be61a2859ac8e3 100644 (file)
@@ -110,7 +110,7 @@ The actual steps taken by a platform to recover from a PCI error
 event will be platform-dependent, but will follow the general
 sequence described below.
 
-STEP 0: Error Event: ERR_NONFATAL
+STEP 0: Error Event
 -------------------
 A PCI bus error is detected by the PCI hardware.  On powerpc, the slot
 is isolated, in that all I/O is blocked: all reads return 0xffffffff,
@@ -228,7 +228,13 @@ proceeds to either STEP3 (Link Reset) or to STEP 5 (Resume Operations).
 If any driver returned PCI_ERS_RESULT_NEED_RESET, then the platform
 proceeds to STEP 4 (Slot Reset)
 
-STEP 3: Slot Reset
+STEP 3: Link Reset
+------------------
+The platform resets the link.  This is a PCI-Express specific step
+and is done whenever a fatal error has been detected that can be
+"solved" by resetting the link.
+
+STEP 4: Slot Reset
 ------------------
 
 In response to a return value of PCI_ERS_RESULT_NEED_RESET, the
@@ -314,7 +320,7 @@ Failure).
 >>> However, it probably should.
 
 
-STEP 4: Resume Operations
+STEP 5: Resume Operations
 -------------------------
 The platform will call the resume() callback on all affected device
 drivers if all drivers on the segment have returned
@@ -326,7 +332,7 @@ a result code.
 At this point, if a new error happens, the platform will restart
 a new error recovery sequence.
 
-STEP 5: Permanent Failure
+STEP 6: Permanent Failure
 -------------------------
 A "permanent failure" has occurred, and the platform cannot recover
 the device.  The platform will call error_detected() with a
@@ -349,27 +355,6 @@ errors. See the discussion in powerpc/eeh-pci-error-recovery.txt
 for additional detail on real-life experience of the causes of
 software errors.
 
-STEP 0: Error Event: ERR_FATAL
--------------------
-PCI bus error is detected by the PCI hardware. On powerpc, the slot is
-isolated, in that all I/O is blocked: all reads return 0xffffffff, all
-writes are ignored.
-
-STEP 1: Remove devices
---------------------
-Platform removes the devices depending on the error agent, it could be
-this port for all subordinates or upstream component (likely downstream
-port)
-
-STEP 2: Reset link
---------------------
-The platform resets the link.  This is a PCI-Express specific step and is
-done whenever a fatal error has been detected that can be "solved" by
-resetting the link.
-
-STEP 3: Re-enumerate the devices
---------------------
-Initiates the re-enumeration.
 
 Conclusion; General Remarks
 ---------------------------
index 6d9f2f9fe20ee65623833326c98b79d5ded44fbd..e9e7d24169cf135b0b98ecb23de8495c205dc066 100644 (file)
@@ -29,7 +29,7 @@ available subsections can be seen below.
    iio/index
    input
    usb/index
-   pci
+   pci/index
    spi
    i2c
    hsi
diff --git a/Documentation/driver-api/pci/index.rst b/Documentation/driver-api/pci/index.rst
new file mode 100644 (file)
index 0000000..c6cf1fe
--- /dev/null
@@ -0,0 +1,22 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============================================
+The Linux PCI driver implementer's API guide
+============================================
+
+.. class:: toc-title
+
+          Table of contents
+
+.. toctree::
+   :maxdepth: 2
+
+   pci
+   p2pdma
+
+.. only::  subproject and html
+
+   Indices
+   =======
+
+   * :ref:`genindex`
diff --git a/Documentation/driver-api/pci/p2pdma.rst b/Documentation/driver-api/pci/p2pdma.rst
new file mode 100644 (file)
index 0000000..4c577fa
--- /dev/null
@@ -0,0 +1,145 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============================
+PCI Peer-to-Peer DMA Support
+============================
+
+The PCI bus has pretty decent support for performing DMA transfers
+between two devices on the bus. This type of transaction is henceforth
+called Peer-to-Peer (or P2P). However, there are a number of issues that
+make P2P transactions tricky to do in a perfectly safe way.
+
+One of the biggest issues is that PCI doesn't require forwarding
+transactions between hierarchy domains, and in PCIe, each Root Port
+defines a separate hierarchy domain. To make things worse, there is no
+simple way to determine if a given Root Complex supports this or not.
+(See PCIe r4.0, sec 1.3.1). Therefore, as of this writing, the kernel
+only supports doing P2P when the endpoints involved are all behind the
+same PCI bridge, as such devices are all in the same PCI hierarchy
+domain, and the spec guarantees that all transactions within the
+hierarchy will be routable, but it does not require routing
+between hierarchies.
+
+The second issue is that to make use of existing interfaces in Linux,
+memory that is used for P2P transactions needs to be backed by struct
+pages. However, PCI BARs are not typically cache coherent so there are
+a few corner case gotchas with these pages so developers need to
+be careful about what they do with them.
+
+
+Driver Writer's Guide
+=====================
+
+In a given P2P implementation there may be three or more different
+types of kernel drivers in play:
+
+* Provider - A driver which provides or publishes P2P resources like
+  memory or doorbell registers to other drivers.
+* Client - A driver which makes use of a resource by setting up a
+  DMA transaction to or from it.
+* Orchestrator - A driver which orchestrates the flow of data between
+  clients and providers.
+
+In many cases there could be overlap between these three types (i.e.,
+it may be typical for a driver to be both a provider and a client).
+
+For example, in the NVMe Target Copy Offload implementation:
+
+* The NVMe PCI driver is both a client, provider and orchestrator
+  in that it exposes any CMB (Controller Memory Buffer) as a P2P memory
+  resource (provider), it accepts P2P memory pages as buffers in requests
+  to be used directly (client) and it can also make use of the CMB as
+  submission queue entries (orchastrator).
+* The RDMA driver is a client in this arrangement so that an RNIC
+  can DMA directly to the memory exposed by the NVMe device.
+* The NVMe Target driver (nvmet) can orchestrate the data from the RNIC
+  to the P2P memory (CMB) and then to the NVMe device (and vice versa).
+
+This is currently the only arrangement supported by the kernel but
+one could imagine slight tweaks to this that would allow for the same
+functionality. For example, if a specific RNIC added a BAR with some
+memory behind it, its driver could add support as a P2P provider and
+then the NVMe Target could use the RNIC's memory instead of the CMB
+in cases where the NVMe cards in use do not have CMB support.
+
+
+Provider Drivers
+----------------
+
+A provider simply needs to register a BAR (or a portion of a BAR)
+as a P2P DMA resource using :c:func:`pci_p2pdma_add_resource()`.
+This will register struct pages for all the specified memory.
+
+After that it may optionally publish all of its resources as
+P2P memory using :c:func:`pci_p2pmem_publish()`. This will allow
+any orchestrator drivers to find and use the memory. When marked in
+this way, the resource must be regular memory with no side effects.
+
+For the time being this is fairly rudimentary in that all resources
+are typically going to be P2P memory. Future work will likely expand
+this to include other types of resources like doorbells.
+
+
+Client Drivers
+--------------
+
+A client driver typically only has to conditionally change its DMA map
+routine to use the mapping function :c:func:`pci_p2pdma_map_sg()` instead
+of the usual :c:func:`dma_map_sg()` function. Memory mapped in this
+way does not need to be unmapped.
+
+The client may also, optionally, make use of
+:c:func:`is_pci_p2pdma_page()` to determine when to use the P2P mapping
+functions and when to use the regular mapping functions. In some
+situations, it may be more appropriate to use a flag to indicate a
+given request is P2P memory and map appropriately. It is important to
+ensure that struct pages that back P2P memory stay out of code that
+does not have support for them as other code may treat the pages as
+regular memory which may not be appropriate.
+
+
+Orchestrator Drivers
+--------------------
+
+The first task an orchestrator driver must do is compile a list of
+all client devices that will be involved in a given transaction. For
+example, the NVMe Target driver creates a list including the namespace
+block device and the RNIC in use. If the orchestrator has access to
+a specific P2P provider to use it may check compatibility using
+:c:func:`pci_p2pdma_distance()` otherwise it may find a memory provider
+that's compatible with all clients using  :c:func:`pci_p2pmem_find()`.
+If more than one provider is supported, the one nearest to all the clients will
+be chosen first. If more than one provider is an equal distance away, the
+one returned will be chosen at random (it is not an arbitrary but
+truely random). This function returns the PCI device to use for the provider
+with a reference taken and therefore when it's no longer needed it should be
+returned with pci_dev_put().
+
+Once a provider is selected, the orchestrator can then use
+:c:func:`pci_alloc_p2pmem()` and :c:func:`pci_free_p2pmem()` to
+allocate P2P memory from the provider. :c:func:`pci_p2pmem_alloc_sgl()`
+and :c:func:`pci_p2pmem_free_sgl()` are convenience functions for
+allocating scatter-gather lists with P2P memory.
+
+Struct Page Caveats
+-------------------
+
+Driver writers should be very careful about not passing these special
+struct pages to code that isn't prepared for it. At this time, the kernel
+interfaces do not have any checks for ensuring this. This obviously
+precludes passing these pages to userspace.
+
+P2P memory is also technically IO memory but should never have any side
+effects behind it. Thus, the order of loads and stores should not be important
+and ioreadX(), iowriteX() and friends should not be necessary.
+However, as the memory is not cache coherent, if access ever needs to
+be protected by a spinlock then :c:func:`mmiowb()` must be used before
+unlocking the lock. (See ACQUIRES VS I/O ACCESSES in
+Documentation/memory-barriers.txt)
+
+
+P2P DMA Support Library
+=======================
+
+.. kernel-doc:: drivers/pci/p2pdma.c
+   :export:
index f788264921ffa21415169997225ff40a70469bd7..30d6a64e53f74e6f877a050e59ac9874f11227d2 100644 (file)
@@ -23,7 +23,7 @@ The primary means of communicating with the Switchtec management firmware is
 through the Memory-mapped Remote Procedure Call (MRPC) interface.
 Commands are submitted to the interface with a 4-byte command
 identifier and up to 1KB of command specific data. The firmware will
-respond with a 4 bytes return code and up to 1KB of command specific
+respond with a 4-byte return code and up to 1KB of command-specific
 data. The interface only processes a single command at a time.
 
 
@@ -36,8 +36,8 @@ device: /dev/switchtec#, one for each management endpoint in the system.
 The char device has the following semantics:
 
 * A write must consist of at least 4 bytes and no more than 1028 bytes.
-  The first four bytes will be interpreted as the command to run and
-  the remainder will be used as the input data. A write will send the
+  The first 4 bytes will be interpreted as the Command ID and the
+  remainder will be used as the input data. A write will send the
   command to the firmware to begin processing.
 
 * Each write must be followed by exactly one read. Any double write will
@@ -45,9 +45,9 @@ The char device has the following semantics:
   produce an error.
 
 * A read will block until the firmware completes the command and return
-  the four bytes of status plus up to 1024 bytes of output data. (The
-  length will be specified by the size parameter of the read call --
-  reading less than 4 bytes will produce an error.
+  the 4-byte Command Return Value plus up to 1024 bytes of output
+  data. (The length will be specified by the size parameter of the read
+  call -- reading less than 4 bytes will produce an error.)
 
 * The poll call will also be supported for userspace applications that
   need to do other things while waiting for the command to complete.
@@ -83,10 +83,20 @@ The following IOCTLs are also supported by the device:
 Non-Transparent Bridge (NTB) Driver
 ===================================
 
-An NTB driver is provided for the switchtec hardware in switchtec_ntb.
-Currently, it only supports switches configured with exactly 2
-partitions. It also requires the following configuration settings:
+An NTB hardware driver is provided for the Switchtec hardware in
+ntb_hw_switchtec. Currently, it only supports switches configured with
+exactly 2 NT partitions and zero or more non-NT partitions. It also requires
+the following configuration settings:
 
-* Both partitions must be able to access each other's GAS spaces.
+* Both NT partitions must be able to access each other's GAS spaces.
   Thus, the bits in the GAS Access Vector under Management Settings
   must be set to support this.
+* Kernel configuration MUST include support for NTB (CONFIG_NTB needs
+  to be set)
+
+NT EP BAR 2 will be dynamically configured as a Direct Window, and
+the configuration file does not need to configure it explicitly.
+
+Please refer to Documentation/ntb.txt in Linux source tree for an overall
+understanding of the Linux NTB stack. ntb_hw_switchtec works as an NTB
+Hardware Driver in this stack.
index 0e2ea1c785427849b68435eb1c188e8b7d65eb64..bb85e2f4603f88d527a97da32c6e29c662053a89 100644 (file)
@@ -165,16 +165,15 @@ static void pci_acpi_generic_release_info(struct acpi_pci_root_info *ci)
 /* Interface called from ACPI code to setup PCI host controller */
 struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
 {
-       int node = acpi_get_node(root->device->handle);
        struct acpi_pci_generic_root_info *ri;
        struct pci_bus *bus, *child;
        struct acpi_pci_root_ops *root_ops;
 
-       ri = kzalloc_node(sizeof(*ri), GFP_KERNEL, node);
+       ri = kzalloc(sizeof(*ri), GFP_KERNEL);
        if (!ri)
                return NULL;
 
-       root_ops = kzalloc_node(sizeof(*root_ops), GFP_KERNEL, node);
+       root_ops = kzalloc(sizeof(*root_ops), GFP_KERNEL);
        if (!root_ops) {
                kfree(ri);
                return NULL;
index 7f627e3f4da452a523a6b65a779118c34c3a8677..630eb8b1b7ed3a2c1e2097c9df81acd88633e42c 100644 (file)
@@ -54,7 +54,6 @@ void pnv_cxl_release_hwirq_ranges(struct cxl_irq_ranges *irqs,
 
 struct pnv_php_slot {
        struct hotplug_slot             slot;
-       struct hotplug_slot_info        slot_info;
        uint64_t                        id;
        char                            *name;
        int                             slot_no;
@@ -72,6 +71,7 @@ struct pnv_php_slot {
        struct pci_dev                  *pdev;
        struct pci_bus                  *bus;
        bool                            power_state_check;
+       u8                              attention_state;
        void                            *fdt;
        void                            *dt;
        struct of_changeset             ocs;
index 5559dcaddd5e0345f50fdc81299a7d9452a79254..948656069cddd40ebad391c9833911d7b283a85c 100644 (file)
@@ -356,7 +356,7 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
        } else {
                struct pci_root_info *info;
 
-               info = kzalloc_node(sizeof(*info), GFP_KERNEL, node);
+               info = kzalloc(sizeof(*info), GFP_KERNEL);
                if (!info)
                        dev_err(&root->device->dev,
                                "pci_bus %04x:%02x: ignored (out of memory)\n",
index 7433035ded95543e13f67fdc23a42667dcdd6be3..707aafc7c2aa9238576525c52f3469938da55b33 100644 (file)
@@ -421,7 +421,8 @@ acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req)
 }
 EXPORT_SYMBOL(acpi_pci_osc_control_set);
 
-static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm)
+static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm,
+                                bool is_pcie)
 {
        u32 support, control, requested;
        acpi_status status;
@@ -455,9 +456,15 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm)
        decode_osc_support(root, "OS supports", support);
        status = acpi_pci_osc_support(root, support);
        if (ACPI_FAILURE(status)) {
-               dev_info(&device->dev, "_OSC failed (%s); disabling ASPM\n",
-                        acpi_format_exception(status));
                *no_aspm = 1;
+
+               /* _OSC is optional for PCI host bridges */
+               if ((status == AE_NOT_FOUND) && !is_pcie)
+                       return;
+
+               dev_info(&device->dev, "_OSC failed (%s)%s\n",
+                        acpi_format_exception(status),
+                        pcie_aspm_support_enabled() ? "; disabling ASPM" : "");
                return;
        }
 
@@ -533,6 +540,7 @@ static int acpi_pci_root_add(struct acpi_device *device,
        acpi_handle handle = device->handle;
        int no_aspm = 0;
        bool hotadd = system_state == SYSTEM_RUNNING;
+       bool is_pcie;
 
        root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL);
        if (!root)
@@ -590,7 +598,8 @@ static int acpi_pci_root_add(struct acpi_device *device,
 
        root->mcfg_addr = acpi_pci_root_get_mcfg_addr(handle);
 
-       negotiate_os_control(root, &no_aspm);
+       is_pcie = strcmp(acpi_device_hid(device), "PNP0A08") == 0;
+       negotiate_os_control(root, &no_aspm, is_pcie);
 
        /*
         * TBD: Need PCI interface for enumeration/configuration of roots.
index 693cf05b0cc44ffba54427fa12abf3017830e0e1..8c7c4583b52d92c5b323523af8388ce7b14814f4 100644 (file)
@@ -24,11 +24,15 @@ static int acpi_data_get_property_array(const struct acpi_device_data *data,
                                        acpi_object_type type,
                                        const union acpi_object **obj);
 
-/* ACPI _DSD device properties GUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */
-static const guid_t prp_guid =
+static const guid_t prp_guids[] = {
+       /* ACPI _DSD device properties GUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */
        GUID_INIT(0xdaffd814, 0x6eba, 0x4d8c,
-                 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01);
-/* ACPI _DSD data subnodes GUID: dbb8e3e6-5886-4ba6-8795-1319f52a966b */
+                 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01),
+       /* Hotplug in D3 GUID: 6211e2c0-58a3-4af3-90e1-927a4e0c55a4 */
+       GUID_INIT(0x6211e2c0, 0x58a3, 0x4af3,
+                 0x90, 0xe1, 0x92, 0x7a, 0x4e, 0x0c, 0x55, 0xa4),
+};
+
 static const guid_t ads_guid =
        GUID_INIT(0xdbb8e3e6, 0x5886, 0x4ba6,
                  0x87, 0x95, 0x13, 0x19, 0xf5, 0x2a, 0x96, 0x6b);
@@ -56,6 +60,7 @@ static bool acpi_nondev_subnode_extract(const union acpi_object *desc,
        dn->name = link->package.elements[0].string.pointer;
        dn->fwnode.ops = &acpi_data_fwnode_ops;
        dn->parent = parent;
+       INIT_LIST_HEAD(&dn->data.properties);
        INIT_LIST_HEAD(&dn->data.subnodes);
 
        result = acpi_extract_properties(desc, &dn->data);
@@ -288,6 +293,35 @@ static void acpi_init_of_compatible(struct acpi_device *adev)
        adev->flags.of_compatible_ok = 1;
 }
 
+static bool acpi_is_property_guid(const guid_t *guid)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(prp_guids); i++) {
+               if (guid_equal(guid, &prp_guids[i]))
+                       return true;
+       }
+
+       return false;
+}
+
+struct acpi_device_properties *
+acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid,
+                   const union acpi_object *properties)
+{
+       struct acpi_device_properties *props;
+
+       props = kzalloc(sizeof(*props), GFP_KERNEL);
+       if (props) {
+               INIT_LIST_HEAD(&props->list);
+               props->guid = guid;
+               props->properties = properties;
+               list_add_tail(&props->list, &data->properties);
+       }
+
+       return props;
+}
+
 static bool acpi_extract_properties(const union acpi_object *desc,
                                    struct acpi_device_data *data)
 {
@@ -312,7 +346,7 @@ static bool acpi_extract_properties(const union acpi_object *desc,
                    properties->type != ACPI_TYPE_PACKAGE)
                        break;
 
-               if (!guid_equal((guid_t *)guid->buffer.pointer, &prp_guid))
+               if (!acpi_is_property_guid((guid_t *)guid->buffer.pointer))
                        continue;
 
                /*
@@ -320,13 +354,13 @@ static bool acpi_extract_properties(const union acpi_object *desc,
                 * package immediately following it.
                 */
                if (!acpi_properties_format_valid(properties))
-                       break;
+                       continue;
 
-               data->properties = properties;
-               return true;
+               acpi_data_add_props(data, (const guid_t *)guid->buffer.pointer,
+                                   properties);
        }
 
-       return false;
+       return !list_empty(&data->properties);
 }
 
 void acpi_init_properties(struct acpi_device *adev)
@@ -336,6 +370,7 @@ void acpi_init_properties(struct acpi_device *adev)
        acpi_status status;
        bool acpi_of = false;
 
+       INIT_LIST_HEAD(&adev->data.properties);
        INIT_LIST_HEAD(&adev->data.subnodes);
 
        if (!adev->handle)
@@ -398,11 +433,16 @@ static void acpi_destroy_nondev_subnodes(struct list_head *list)
 
 void acpi_free_properties(struct acpi_device *adev)
 {
+       struct acpi_device_properties *props, *tmp;
+
        acpi_destroy_nondev_subnodes(&adev->data.subnodes);
        ACPI_FREE((void *)adev->data.pointer);
        adev->data.of_compatible = NULL;
        adev->data.pointer = NULL;
-       adev->data.properties = NULL;
+       list_for_each_entry_safe(props, tmp, &adev->data.properties, list) {
+               list_del(&props->list);
+               kfree(props);
+       }
 }
 
 /**
@@ -427,32 +467,37 @@ static int acpi_data_get_property(const struct acpi_device_data *data,
                                  const char *name, acpi_object_type type,
                                  const union acpi_object **obj)
 {
-       const union acpi_object *properties;
-       int i;
+       const struct acpi_device_properties *props;
 
        if (!data || !name)
                return -EINVAL;
 
-       if (!data->pointer || !data->properties)
+       if (!data->pointer || list_empty(&data->properties))
                return -EINVAL;
 
-       properties = data->properties;
-       for (i = 0; i < properties->package.count; i++) {
-               const union acpi_object *propname, *propvalue;
-               const union acpi_object *property;
+       list_for_each_entry(props, &data->properties, list) {
+               const union acpi_object *properties;
+               unsigned int i;
 
-               property = &properties->package.elements[i];
+               properties = props->properties;
+               for (i = 0; i < properties->package.count; i++) {
+                       const union acpi_object *propname, *propvalue;
+                       const union acpi_object *property;
 
-               propname = &property->package.elements[0];
-               propvalue = &property->package.elements[1];
+                       property = &properties->package.elements[i];
 
-               if (!strcmp(name, propname->string.pointer)) {
-                       if (type != ACPI_TYPE_ANY && propvalue->type != type)
-                               return -EPROTO;
-                       if (obj)
-                               *obj = propvalue;
+                       propname = &property->package.elements[0];
+                       propvalue = &property->package.elements[1];
 
-                       return 0;
+                       if (!strcmp(name, propname->string.pointer)) {
+                               if (type != ACPI_TYPE_ANY &&
+                                   propvalue->type != type)
+                                       return -EPROTO;
+                               if (obj)
+                                       *obj = propvalue;
+
+                               return 0;
+                       }
                }
        }
        return -EINVAL;
index 51b4cf9f25da9686d4014e743ef387df0829f93c..130df1c8ed7d80a809e56759c78f056367a4cf15 100644 (file)
@@ -132,8 +132,8 @@ void acpi_extract_apple_properties(struct acpi_device *adev)
        }
        WARN_ON(free_space != (void *)newprops + newsize);
 
-       adev->data.properties = newprops;
        adev->data.pointer = newprops;
+       acpi_data_add_props(&adev->data, &apple_prp_guid, newprops);
 
 out_free:
        ACPI_FREE(props);
index 9b6d7930d1c7940025877657e9062723b40921d4..e0bcf9b2dab040d7342c1eb4f4ead9abef550ba9 100644 (file)
@@ -873,7 +873,7 @@ static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
         * like others but it will lock up the whole machine HARD if
         * 65536 byte PRD entry is fed. Reduce maximum segment size.
         */
-       rc = pci_set_dma_max_seg_size(pdev, 65536 - 512);
+       rc = dma_set_max_seg_size(&pdev->dev, 65536 - 512);
        if (rc) {
                dev_err(&pdev->dev, "failed to set the maximum segment size\n");
                return rc;
index f2c631ce793cc8a342b44381592824bf902282f7..37df486c7c3c342af440171d0a0a054976004329 100644 (file)
@@ -780,7 +780,7 @@ static int rsxx_pci_probe(struct pci_dev *dev,
                goto failed_enable;
 
        pci_set_master(dev);
-       pci_set_dma_max_seg_size(dev, RSXX_HW_BLK_SIZE);
+       dma_set_max_seg_size(&dev->dev, RSXX_HW_BLK_SIZE);
 
        st = pci_set_dma_mask(dev, DMA_BIT_MASK(64));
        if (st) {
index 9225d060e18f45cae459be381b0d2a0d79e25793..f5e960d23a7a7990fe3634bb1033203a3846b577 100644 (file)
@@ -198,7 +198,6 @@ static pci_ers_result_t adf_slot_reset(struct pci_dev *pdev)
                pr_err("QAT: Can't find acceleration device\n");
                return PCI_ERS_RESULT_DISCONNECT;
        }
-       pci_cleanup_aer_uncorrect_error_status(pdev);
        if (adf_dev_aer_schedule_reset(accel_dev, ADF_DEV_RESET_SYNC))
                return PCI_ERS_RESULT_DISCONNECT;
 
index 4fa4c06c9edb9809675ea98a2d058e3c8b5dfa35..bd8db5c995970397abf010621727a423be2b775e 100644 (file)
@@ -1252,7 +1252,6 @@ static pci_ers_result_t ioat_pcie_error_detected(struct pci_dev *pdev,
 static pci_ers_result_t ioat_pcie_error_slot_reset(struct pci_dev *pdev)
 {
        pci_ers_result_t result = PCI_ERS_RESULT_RECOVERED;
-       int err;
 
        dev_dbg(&pdev->dev, "%s post reset handling\n", DRV_NAME);
 
@@ -1267,12 +1266,6 @@ static pci_ers_result_t ioat_pcie_error_slot_reset(struct pci_dev *pdev)
                pci_wake_from_d3(pdev, false);
        }
 
-       err = pci_cleanup_aer_uncorrect_error_status(pdev);
-       if (err) {
-               dev_err(&pdev->dev,
-                       "AER uncorrect error status clear failed: %#x\n", err);
-       }
-
        return result;
 }
 
index 8b9d7e42c600b60d26bad7f26c32ed938be6e7a2..f74aa0e60300b01115db8f79967d757b53cb0d2c 100644 (file)
@@ -1198,7 +1198,7 @@ int acpi_gpio_count(struct device *dev, const char *con_id)
 bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id)
 {
        /* Never allow fallback if the device has properties */
-       if (adev->data.properties || adev->driver_gpios)
+       if (acpi_dev_has_props(adev) || adev->driver_gpios)
                return false;
 
        return con_id == NULL;
index 683e6d11a564417df6d104b7726ad3c78fb6d7bf..d22c4a2ebac6b8c4b683471d094cd7078c632611 100644 (file)
@@ -12,6 +12,7 @@
  */
 #include <linux/moduleparam.h>
 #include <linux/slab.h>
+#include <linux/pci-p2pdma.h>
 #include <rdma/mr_pool.h>
 #include <rdma/rw.h>
 
@@ -280,7 +281,11 @@ int rdma_rw_ctx_init(struct rdma_rw_ctx *ctx, struct ib_qp *qp, u8 port_num,
        struct ib_device *dev = qp->pd->device;
        int ret;
 
-       ret = ib_dma_map_sg(dev, sg, sg_cnt, dir);
+       if (is_pci_p2pdma_page(sg_page(sg)))
+               ret = pci_p2pdma_map_sg(dev->dma_device, sg, sg_cnt, dir);
+       else
+               ret = ib_dma_map_sg(dev, sg, sg_cnt, dir);
+
        if (!ret)
                return -ENOMEM;
        sg_cnt = ret;
@@ -602,7 +607,9 @@ void rdma_rw_ctx_destroy(struct rdma_rw_ctx *ctx, struct ib_qp *qp, u8 port_num,
                break;
        }
 
-       ib_dma_unmap_sg(qp->pd->device, sg, sg_cnt, dir);
+       /* P2PDMA contexts do not need to be unmapped */
+       if (!is_pci_p2pdma_page(sg_page(sg)))
+               ib_dma_unmap_sg(qp->pd->device, sg, sg_cnt, dir);
 }
 EXPORT_SYMBOL(rdma_rw_ctx_destroy);
 
index 347fe18b1a41c0990c753aa29f2efaaea0cd119b..62d6f197ec0b9ded3e7f1a037fbc189a99c7fb32 100644 (file)
@@ -99,7 +99,7 @@ static void dealloc_oc_sq(struct c4iw_rdev *rdev, struct t4_sq *sq)
 static void dealloc_host_sq(struct c4iw_rdev *rdev, struct t4_sq *sq)
 {
        dma_free_coherent(&(rdev->lldi.pdev->dev), sq->memsize, sq->queue,
-                         pci_unmap_addr(sq, mapping));
+                         dma_unmap_addr(sq, mapping));
 }
 
 static void dealloc_sq(struct c4iw_rdev *rdev, struct t4_sq *sq)
@@ -132,7 +132,7 @@ static int alloc_host_sq(struct c4iw_rdev *rdev, struct t4_sq *sq)
        if (!sq->queue)
                return -ENOMEM;
        sq->phys_addr = virt_to_phys(sq->queue);
-       pci_unmap_addr_set(sq, mapping, sq->dma_addr);
+       dma_unmap_addr_set(sq, mapping, sq->dma_addr);
        return 0;
 }
 
@@ -2521,7 +2521,7 @@ static void free_srq_queue(struct c4iw_srq *srq, struct c4iw_dev_ucontext *uctx,
 
        dma_free_coherent(&rdev->lldi.pdev->dev,
                          wq->memsize, wq->queue,
-                       pci_unmap_addr(wq, mapping));
+                       dma_unmap_addr(wq, mapping));
        c4iw_rqtpool_free(rdev, wq->rqt_hwaddr, wq->rqt_size);
        kfree(wq->sw_rq);
        c4iw_put_qpid(rdev, wq->qid, uctx);
@@ -2570,7 +2570,7 @@ static int alloc_srq_queue(struct c4iw_srq *srq, struct c4iw_dev_ucontext *uctx,
                goto err_free_rqtpool;
 
        memset(wq->queue, 0, wq->memsize);
-       pci_unmap_addr_set(wq, mapping, wq->dma_addr);
+       dma_unmap_addr_set(wq, mapping, wq->dma_addr);
 
        wq->bar2_va = c4iw_bar2_addrs(rdev, wq->qid, T4_BAR2_QTYPE_EGRESS,
                                      &wq->bar2_qid,
@@ -2649,7 +2649,7 @@ static int alloc_srq_queue(struct c4iw_srq *srq, struct c4iw_dev_ucontext *uctx,
 err_free_queue:
        dma_free_coherent(&rdev->lldi.pdev->dev,
                          wq->memsize, wq->queue,
-                       pci_unmap_addr(wq, mapping));
+                       dma_unmap_addr(wq, mapping));
 err_free_rqtpool:
        c4iw_rqtpool_free(rdev, wq->rqt_hwaddr, wq->rqt_size);
 err_free_pending_wrs:
index e42021fd6fd60b0cd2488182998723b6146a25d0..fff6d48d262f060c1f438c47ab2bb2e7fab9e9da 100644 (file)
@@ -397,7 +397,7 @@ struct t4_srq_pending_wr {
 struct t4_srq {
        union t4_recv_wr *queue;
        dma_addr_t dma_addr;
-       DECLARE_PCI_UNMAP_ADDR(mapping);
+       DEFINE_DMA_UNMAP_ADDR(mapping);
        struct t4_swrqe *sw_rq;
        void __iomem *bar2_va;
        u64 bar2_pa;
index 6c967dde58e702c228835a84c3b1ea19abc1ea64..cca413eaa74ecf482eda1422d3db9ede5cdd24a1 100644 (file)
@@ -650,7 +650,6 @@ pci_resume(struct pci_dev *pdev)
        struct hfi1_devdata *dd = pci_get_drvdata(pdev);
 
        dd_dev_info(dd, "HFI1 resume function called\n");
-       pci_cleanup_aer_uncorrect_error_status(pdev);
        /*
         * Running jobs will fail, since it's asynchronous
         * unlike sysfs-requested reset.   Better than
index 5ac7b31c346bdd171cedfd760c12db53146aca16..30595b358d8f8a6eddbfaab799067e29e8fc259b 100644 (file)
@@ -597,7 +597,6 @@ qib_pci_resume(struct pci_dev *pdev)
        struct qib_devdata *dd = pci_get_drvdata(pdev);
 
        qib_devinfo(pdev, "QIB resume function called\n");
-       pci_cleanup_aer_uncorrect_error_status(pdev);
        /*
         * Running jobs will fail, since it's asynchronous
         * unlike sysfs-requested reset.   Better than
index 6d32211349275d260dbc7817c639df71c91b7084..7968c644ad8617fef2fec1360e869a622c525a02 100644 (file)
@@ -1964,8 +1964,6 @@ static pci_ers_result_t alx_pci_error_slot_reset(struct pci_dev *pdev)
        if (!alx_reset_mac(hw))
                rc = PCI_ERS_RESULT_RECOVERED;
 out:
-       pci_cleanup_aer_uncorrect_error_status(pdev);
-
        rtnl_unlock();
 
        return rc;
index 122fdb80a789982b00137053fb879179c424e5a9..bbb2471160455b0ee3f1ec56d26baa281759ae3c 100644 (file)
@@ -8793,13 +8793,6 @@ static pci_ers_result_t bnx2_io_slot_reset(struct pci_dev *pdev)
        if (!(bp->flags & BNX2_FLAG_AER_ENABLED))
                return result;
 
-       err = pci_cleanup_aer_uncorrect_error_status(pdev);
-       if (err) {
-               dev_err(&pdev->dev,
-                       "pci_cleanup_aer_uncorrect_error_status failed 0x%0x\n",
-                        err); /* non-fatal, continue */
-       }
-
        return result;
 }
 
index 71362b7f60402545c3b4aa2bc391a1e2d3cd7f7f..1b1f0c1b82b700f9c57b2d65f5ec5cf56c44da0e 100644 (file)
@@ -14385,14 +14385,6 @@ static pci_ers_result_t bnx2x_io_slot_reset(struct pci_dev *pdev)
 
        rtnl_unlock();
 
-       /* If AER, perform cleanup of the PCIe registers */
-       if (bp->flags & AER_ENABLED) {
-               if (pci_cleanup_aer_uncorrect_error_status(pdev))
-                       BNX2X_ERR("pci_cleanup_aer_uncorrect_error_status failed\n");
-               else
-                       DP(NETIF_MSG_HW, "pci_cleanup_aer_uncorrect_error_status succeeded\n");
-       }
-
        return PCI_ERS_RESULT_RECOVERED;
 }
 
index cecbb1d1f587f936b0d2960739c911fc28f05d51..1d90f0469093255f69c06b199a5c7d5f96bb9a97 100644 (file)
@@ -9231,13 +9231,6 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev)
 
        rtnl_unlock();
 
-       err = pci_cleanup_aer_uncorrect_error_status(pdev);
-       if (err) {
-               dev_err(&pdev->dev,
-                       "pci_cleanup_aer_uncorrect_error_status failed 0x%0x\n",
-                        err); /* non-fatal, continue */
-       }
-
        return PCI_ERS_RESULT_RECOVERED;
 }
 
index 961e3087d1d38c3294c5e49c0fab9f1922bf15b9..2e06434122c01f2be742d26783dc28d7032ca7b8 100644 (file)
@@ -4747,7 +4747,6 @@ static pci_ers_result_t eeh_slot_reset(struct pci_dev *pdev)
        pci_set_master(pdev);
        pci_restore_state(pdev);
        pci_save_state(pdev);
-       pci_cleanup_aer_uncorrect_error_status(pdev);
 
        if (t4_wait_dev_ready(adap->regs) < 0)
                return PCI_ERS_RESULT_DISCONNECT;
index 74d122616e76a2d876793a610dd6672e3e8b9910..544e2e3655d1f1d003e056874351487d2f2572fb 100644 (file)
@@ -6151,7 +6151,6 @@ static pci_ers_result_t be_eeh_reset(struct pci_dev *pdev)
        if (status)
                return PCI_ERS_RESULT_DISCONNECT;
 
-       pci_cleanup_aer_uncorrect_error_status(pdev);
        be_clear_error(adapter, BE_CLEAR_ALL);
        return PCI_ERS_RESULT_RECOVERED;
 }
index 3ba0c90e7055b14a666566279fb7a58728da7427..7cd23324f698e0e85827294cef08db7d29153b1c 100644 (file)
@@ -6854,8 +6854,6 @@ static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev)
                result = PCI_ERS_RESULT_RECOVERED;
        }
 
-       pci_cleanup_aer_uncorrect_error_status(pdev);
-
        return result;
 }
 
index 15071e4adb98c9924ab2e9a17c7f616f23104bd0..55138d6a3182e1f16cd52a401b8079edbf4af3c9 100644 (file)
@@ -2462,8 +2462,6 @@ static pci_ers_result_t fm10k_io_slot_reset(struct pci_dev *pdev)
                result = PCI_ERS_RESULT_RECOVERED;
        }
 
-       pci_cleanup_aer_uncorrect_error_status(pdev);
-
        return result;
 }
 
index ac685ad4d8773125b059f1209d2b60747996b39b..784caf3e67009749a10cc002fcf2d4fcfc3d0b62 100644 (file)
@@ -14227,7 +14227,6 @@ static pci_ers_result_t i40e_pci_error_slot_reset(struct pci_dev *pdev)
 {
        struct i40e_pf *pf = pci_get_drvdata(pdev);
        pci_ers_result_t result;
-       int err;
        u32 reg;
 
        dev_dbg(&pdev->dev, "%s\n", __func__);
@@ -14248,14 +14247,6 @@ static pci_ers_result_t i40e_pci_error_slot_reset(struct pci_dev *pdev)
                        result = PCI_ERS_RESULT_DISCONNECT;
        }
 
-       err = pci_cleanup_aer_uncorrect_error_status(pdev);
-       if (err) {
-               dev_info(&pdev->dev,
-                        "pci_cleanup_aer_uncorrect_error_status failed 0x%0x\n",
-                        err);
-               /* non-fatal, continue */
-       }
-
        return result;
 }
 
index a32c576c1e656c0102989413cad114d1d8f03771..c7ff2f861247b443b3775fd476fa07cf3018176b 100644 (file)
@@ -9116,7 +9116,6 @@ static pci_ers_result_t igb_io_slot_reset(struct pci_dev *pdev)
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
        pci_ers_result_t result;
-       int err;
 
        if (pci_enable_device_mem(pdev)) {
                dev_err(&pdev->dev,
@@ -9140,14 +9139,6 @@ static pci_ers_result_t igb_io_slot_reset(struct pci_dev *pdev)
                result = PCI_ERS_RESULT_RECOVERED;
        }
 
-       err = pci_cleanup_aer_uncorrect_error_status(pdev);
-       if (err) {
-               dev_err(&pdev->dev,
-                       "pci_cleanup_aer_uncorrect_error_status failed 0x%0x\n",
-                       err);
-               /* non-fatal, continue */
-       }
-
        return result;
 }
 
index 9a23d33a47ed52bfeb10d79d970e114ee4702d6e..a0f716713e878e454c607b17a0ecc6bdc93f7b27 100644 (file)
@@ -11075,8 +11075,6 @@ static pci_ers_result_t ixgbe_io_error_detected(struct pci_dev *pdev,
                        /* Free device reference count */
                        pci_dev_put(vfdev);
                }
-
-               pci_cleanup_aer_uncorrect_error_status(pdev);
        }
 
        /*
@@ -11126,7 +11124,6 @@ static pci_ers_result_t ixgbe_io_slot_reset(struct pci_dev *pdev)
 {
        struct ixgbe_adapter *adapter = pci_get_drvdata(pdev);
        pci_ers_result_t result;
-       int err;
 
        if (pci_enable_device_mem(pdev)) {
                e_err(probe, "Cannot re-enable PCI device after reset.\n");
@@ -11146,13 +11143,6 @@ static pci_ers_result_t ixgbe_io_slot_reset(struct pci_dev *pdev)
                result = PCI_ERS_RESULT_RECOVERED;
        }
 
-       err = pci_cleanup_aer_uncorrect_error_status(pdev);
-       if (err) {
-               e_dev_err("pci_cleanup_aer_uncorrect_error_status "
-                         "failed 0x%0x\n", err);
-               /* non-fatal, continue */
-       }
-
        return result;
 }
 
index 69aa7fc392c5e4ad1cbcd9025f56bffdf3aa92c7..200edc61aa0741c43b34d9772bfcc9111c86dbc0 100644 (file)
@@ -1790,11 +1790,6 @@ static pci_ers_result_t netxen_io_slot_reset(struct pci_dev *pdev)
        return err ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
 }
 
-static void netxen_io_resume(struct pci_dev *pdev)
-{
-       pci_cleanup_aer_uncorrect_error_status(pdev);
-}
-
 static void netxen_nic_shutdown(struct pci_dev *pdev)
 {
        struct netxen_adapter *adapter = pci_get_drvdata(pdev);
@@ -3488,7 +3483,6 @@ netxen_free_ip_list(struct netxen_adapter *adapter, bool master)
 static const struct pci_error_handlers netxen_err_handler = {
        .error_detected = netxen_io_error_detected,
        .slot_reset = netxen_io_slot_reset,
-       .resume = netxen_io_resume,
 };
 
 static struct pci_driver netxen_driver = {
index 569d54ededeca2e6472a3f8502e91c45be8e5232..635ac7339b3e4b55d0ce47be18935b9550344298 100644 (file)
@@ -4232,7 +4232,6 @@ static void qlcnic_83xx_io_resume(struct pci_dev *pdev)
 {
        struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
 
-       pci_cleanup_aer_uncorrect_error_status(pdev);
        if (test_and_clear_bit(__QLCNIC_AER, &adapter->state))
                qlcnic_83xx_aer_start_poll_work(adapter);
 }
index 2d38d1ac2aae58fd210030c7b143011f76b921cc..6b3ea531324d80b1d4b65d6a410621bad4e14b19 100644 (file)
@@ -3975,7 +3975,6 @@ static void qlcnic_82xx_io_resume(struct pci_dev *pdev)
        u32 state;
        struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
 
-       pci_cleanup_aer_uncorrect_error_status(pdev);
        state = QLC_SHARED_REG_RD32(adapter, QLCNIC_CRB_DEV_STATE);
        if (state == QLCNIC_DEV_READY && test_and_clear_bit(__QLCNIC_AER,
                                                            &adapter->state))
index 330233286e785254f5f29c87f9557a305974f606..c8e8294ddac50f483654094f886dbee82ad03a06 100644 (file)
@@ -3847,7 +3847,6 @@ static pci_ers_result_t efx_io_slot_reset(struct pci_dev *pdev)
 {
        struct efx_nic *efx = pci_get_drvdata(pdev);
        pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
-       int rc;
 
        if (pci_enable_device(pdev)) {
                netif_err(efx, hw, efx->net_dev,
@@ -3855,13 +3854,6 @@ static pci_ers_result_t efx_io_slot_reset(struct pci_dev *pdev)
                status =  PCI_ERS_RESULT_DISCONNECT;
        }
 
-       rc = pci_cleanup_aer_uncorrect_error_status(pdev);
-       if (rc) {
-               netif_err(efx, hw, efx->net_dev,
-               "pci_cleanup_aer_uncorrect_error_status failed (%d)\n", rc);
-               /* Non-fatal error. Continue. */
-       }
-
        return status;
 }
 
index dd5530a4f8c8936868aed7171bd9481f93730d76..ccdba0bb00a3b16b17b33e5b3386bcf70591ab86 100644 (file)
@@ -3186,7 +3186,6 @@ static pci_ers_result_t ef4_io_slot_reset(struct pci_dev *pdev)
 {
        struct ef4_nic *efx = pci_get_drvdata(pdev);
        pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
-       int rc;
 
        if (pci_enable_device(pdev)) {
                netif_err(efx, hw, efx->net_dev,
@@ -3194,13 +3193,6 @@ static pci_ers_result_t ef4_io_slot_reset(struct pci_dev *pdev)
                status =  PCI_ERS_RESULT_DISCONNECT;
        }
 
-       rc = pci_cleanup_aer_uncorrect_error_status(pdev);
-       if (rc) {
-               netif_err(efx, hw, efx->net_dev,
-               "pci_cleanup_aer_uncorrect_error_status failed (%d)\n", rc);
-               /* Non-fatal error. Continue. */
-       }
-
        return status;
 }
 
index dd8ec1dd92190997f823e0bfb72389baea73b268..6033ce2fd3e9bd3721292c09584ce714aacfcb1c 100644 (file)
@@ -3051,7 +3051,11 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
        ns->queue = blk_mq_init_queue(ctrl->tagset);
        if (IS_ERR(ns->queue))
                goto out_free_ns;
+
        blk_queue_flag_set(QUEUE_FLAG_NONROT, ns->queue);
+       if (ctrl->ops->flags & NVME_F_PCI_P2PDMA)
+               blk_queue_flag_set(QUEUE_FLAG_PCI_P2PDMA, ns->queue);
+
        ns->queue->queuedata = ns;
        ns->ctrl = ctrl;
 
index bb4a2003c0978722230a1e875ade13dc8c28311f..4030743c90aa8759d7615ec1e32ba626e68217d3 100644 (file)
@@ -343,6 +343,7 @@ struct nvme_ctrl_ops {
        unsigned int flags;
 #define NVME_F_FABRICS                 (1 << 0)
 #define NVME_F_METADATA_SUPPORTED      (1 << 1)
+#define NVME_F_PCI_P2PDMA              (1 << 2)
        int (*reg_read32)(struct nvme_ctrl *ctrl, u32 off, u32 *val);
        int (*reg_write32)(struct nvme_ctrl *ctrl, u32 off, u32 val);
        int (*reg_read64)(struct nvme_ctrl *ctrl, u32 off, u64 *val);
index d668682f91dfdb3428e02a44df2c8ade9ccf0042..7e09e45b0b28db2a1f556dbea9414d32c6206465 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/types.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
 #include <linux/sed-opal.h>
+#include <linux/pci-p2pdma.h>
 
 #include "nvme.h"
 
@@ -99,9 +100,8 @@ struct nvme_dev {
        struct work_struct remove_work;
        struct mutex shutdown_lock;
        bool subsystem;
-       void __iomem *cmb;
-       pci_bus_addr_t cmb_bus_addr;
        u64 cmb_size;
+       bool cmb_use_sqes;
        u32 cmbsz;
        u32 cmbloc;
        struct nvme_ctrl ctrl;
@@ -158,7 +158,7 @@ struct nvme_queue {
        struct nvme_dev *dev;
        spinlock_t sq_lock;
        struct nvme_command *sq_cmds;
-       struct nvme_command __iomem *sq_cmds_io;
+       bool sq_cmds_is_io;
        spinlock_t cq_lock ____cacheline_aligned_in_smp;
        volatile struct nvme_completion *cqes;
        struct blk_mq_tags **tags;
@@ -447,11 +447,8 @@ static int nvme_pci_map_queues(struct blk_mq_tag_set *set)
 static void nvme_submit_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd)
 {
        spin_lock(&nvmeq->sq_lock);
-       if (nvmeq->sq_cmds_io)
-               memcpy_toio(&nvmeq->sq_cmds_io[nvmeq->sq_tail], cmd,
-                               sizeof(*cmd));
-       else
-               memcpy(&nvmeq->sq_cmds[nvmeq->sq_tail], cmd, sizeof(*cmd));
+
+       memcpy(&nvmeq->sq_cmds[nvmeq->sq_tail], cmd, sizeof(*cmd));
 
        if (++nvmeq->sq_tail == nvmeq->q_depth)
                nvmeq->sq_tail = 0;
@@ -748,8 +745,13 @@ static blk_status_t nvme_map_data(struct nvme_dev *dev, struct request *req,
                goto out;
 
        ret = BLK_STS_RESOURCE;
-       nr_mapped = dma_map_sg_attrs(dev->dev, iod->sg, iod->nents, dma_dir,
-                       DMA_ATTR_NO_WARN);
+
+       if (is_pci_p2pdma_page(sg_page(iod->sg)))
+               nr_mapped = pci_p2pdma_map_sg(dev->dev, iod->sg, iod->nents,
+                                         dma_dir);
+       else
+               nr_mapped = dma_map_sg_attrs(dev->dev, iod->sg, iod->nents,
+                                            dma_dir,  DMA_ATTR_NO_WARN);
        if (!nr_mapped)
                goto out;
 
@@ -791,7 +793,10 @@ static void nvme_unmap_data(struct nvme_dev *dev, struct request *req)
                        DMA_TO_DEVICE : DMA_FROM_DEVICE;
 
        if (iod->nents) {
-               dma_unmap_sg(dev->dev, iod->sg, iod->nents, dma_dir);
+               /* P2PDMA requests do not need to be unmapped */
+               if (!is_pci_p2pdma_page(sg_page(iod->sg)))
+                       dma_unmap_sg(dev->dev, iod->sg, iod->nents, dma_dir);
+
                if (blk_integrity_rq(req))
                        dma_unmap_sg(dev->dev, &iod->meta_sg, 1, dma_dir);
        }
@@ -1232,9 +1237,18 @@ static void nvme_free_queue(struct nvme_queue *nvmeq)
 {
        dma_free_coherent(nvmeq->q_dmadev, CQ_SIZE(nvmeq->q_depth),
                                (void *)nvmeq->cqes, nvmeq->cq_dma_addr);
-       if (nvmeq->sq_cmds)
-               dma_free_coherent(nvmeq->q_dmadev, SQ_SIZE(nvmeq->q_depth),
-                                       nvmeq->sq_cmds, nvmeq->sq_dma_addr);
+
+       if (nvmeq->sq_cmds) {
+               if (nvmeq->sq_cmds_is_io)
+                       pci_free_p2pmem(to_pci_dev(nvmeq->q_dmadev),
+                                       nvmeq->sq_cmds,
+                                       SQ_SIZE(nvmeq->q_depth));
+               else
+                       dma_free_coherent(nvmeq->q_dmadev,
+                                         SQ_SIZE(nvmeq->q_depth),
+                                         nvmeq->sq_cmds,
+                                         nvmeq->sq_dma_addr);
+       }
 }
 
 static void nvme_free_queues(struct nvme_dev *dev, int lowest)
@@ -1323,12 +1337,21 @@ static int nvme_cmb_qdepth(struct nvme_dev *dev, int nr_io_queues,
 static int nvme_alloc_sq_cmds(struct nvme_dev *dev, struct nvme_queue *nvmeq,
                                int qid, int depth)
 {
-       /* CMB SQEs will be mapped before creation */
-       if (qid && dev->cmb && use_cmb_sqes && (dev->cmbsz & NVME_CMBSZ_SQS))
-               return 0;
+       struct pci_dev *pdev = to_pci_dev(dev->dev);
+
+       if (qid && dev->cmb_use_sqes && (dev->cmbsz & NVME_CMBSZ_SQS)) {
+               nvmeq->sq_cmds = pci_alloc_p2pmem(pdev, SQ_SIZE(depth));
+               nvmeq->sq_dma_addr = pci_p2pmem_virt_to_bus(pdev,
+                                               nvmeq->sq_cmds);
+               nvmeq->sq_cmds_is_io = true;
+       }
+
+       if (!nvmeq->sq_cmds) {
+               nvmeq->sq_cmds = dma_alloc_coherent(dev->dev, SQ_SIZE(depth),
+                                       &nvmeq->sq_dma_addr, GFP_KERNEL);
+               nvmeq->sq_cmds_is_io = false;
+       }
 
-       nvmeq->sq_cmds = dma_alloc_coherent(dev->dev, SQ_SIZE(depth),
-                                           &nvmeq->sq_dma_addr, GFP_KERNEL);
        if (!nvmeq->sq_cmds)
                return -ENOMEM;
        return 0;
@@ -1405,13 +1428,6 @@ static int nvme_create_queue(struct nvme_queue *nvmeq, int qid)
        int result;
        s16 vector;
 
-       if (dev->cmb && use_cmb_sqes && (dev->cmbsz & NVME_CMBSZ_SQS)) {
-               unsigned offset = (qid - 1) * roundup(SQ_SIZE(nvmeq->q_depth),
-                                                     dev->ctrl.page_size);
-               nvmeq->sq_dma_addr = dev->cmb_bus_addr + offset;
-               nvmeq->sq_cmds_io = dev->cmb + offset;
-       }
-
        /*
         * A queue's vector matches the queue identifier unless the controller
         * has only one vector available.
@@ -1652,9 +1668,6 @@ static void nvme_map_cmb(struct nvme_dev *dev)
                return;
        dev->cmbloc = readl(dev->bar + NVME_REG_CMBLOC);
 
-       if (!use_cmb_sqes)
-               return;
-
        size = nvme_cmb_size_unit(dev) * nvme_cmb_size(dev);
        offset = nvme_cmb_size_unit(dev) * NVME_CMB_OFST(dev->cmbloc);
        bar = NVME_CMB_BIR(dev->cmbloc);
@@ -1671,11 +1684,18 @@ static void nvme_map_cmb(struct nvme_dev *dev)
        if (size > bar_size - offset)
                size = bar_size - offset;
 
-       dev->cmb = ioremap_wc(pci_resource_start(pdev, bar) + offset, size);
-       if (!dev->cmb)
+       if (pci_p2pdma_add_resource(pdev, bar, size, offset)) {
+               dev_warn(dev->ctrl.device,
+                        "failed to register the CMB\n");
                return;
-       dev->cmb_bus_addr = pci_bus_address(pdev, bar) + offset;
+       }
+
        dev->cmb_size = size;
+       dev->cmb_use_sqes = use_cmb_sqes && (dev->cmbsz & NVME_CMBSZ_SQS);
+
+       if ((dev->cmbsz & (NVME_CMBSZ_WDS | NVME_CMBSZ_RDS)) ==
+                       (NVME_CMBSZ_WDS | NVME_CMBSZ_RDS))
+               pci_p2pmem_publish(pdev, true);
 
        if (sysfs_add_file_to_group(&dev->ctrl.device->kobj,
                                    &dev_attr_cmb.attr, NULL))
@@ -1685,12 +1705,10 @@ static void nvme_map_cmb(struct nvme_dev *dev)
 
 static inline void nvme_release_cmb(struct nvme_dev *dev)
 {
-       if (dev->cmb) {
-               iounmap(dev->cmb);
-               dev->cmb = NULL;
+       if (dev->cmb_size) {
                sysfs_remove_file_from_group(&dev->ctrl.device->kobj,
                                             &dev_attr_cmb.attr, NULL);
-               dev->cmbsz = 0;
+               dev->cmb_size = 0;
        }
 }
 
@@ -1889,13 +1907,13 @@ static int nvme_setup_io_queues(struct nvme_dev *dev)
        if (nr_io_queues == 0)
                return 0;
 
-       if (dev->cmb && (dev->cmbsz & NVME_CMBSZ_SQS)) {
+       if (dev->cmb_use_sqes) {
                result = nvme_cmb_qdepth(dev, nr_io_queues,
                                sizeof(struct nvme_command));
                if (result > 0)
                        dev->q_depth = result;
                else
-                       nvme_release_cmb(dev);
+                       dev->cmb_use_sqes = false;
        }
 
        do {
@@ -2390,7 +2408,8 @@ static int nvme_pci_get_address(struct nvme_ctrl *ctrl, char *buf, int size)
 static const struct nvme_ctrl_ops nvme_pci_ctrl_ops = {
        .name                   = "pcie",
        .module                 = THIS_MODULE,
-       .flags                  = NVME_F_METADATA_SUPPORTED,
+       .flags                  = NVME_F_METADATA_SUPPORTED |
+                                 NVME_F_PCI_P2PDMA,
        .reg_read32             = nvme_pci_reg_read32,
        .reg_write32            = nvme_pci_reg_write32,
        .reg_read64             = nvme_pci_reg_read64,
@@ -2649,7 +2668,6 @@ static void nvme_error_resume(struct pci_dev *pdev)
        struct nvme_dev *dev = pci_get_drvdata(pdev);
 
        flush_work(&dev->ctrl.reset_work);
-       pci_cleanup_aer_uncorrect_error_status(pdev);
 }
 
 static const struct pci_error_handlers nvme_err_handler = {
index b37a8e3e3f80012ff28a490e28d3583fc6664700..d895579b6c5dc19477a4ccea2597c3092d15ab1e 100644 (file)
@@ -17,6 +17,8 @@
 #include <linux/slab.h>
 #include <linux/stat.h>
 #include <linux/ctype.h>
+#include <linux/pci.h>
+#include <linux/pci-p2pdma.h>
 
 #include "nvmet.h"
 
@@ -340,6 +342,48 @@ static ssize_t nvmet_ns_device_path_store(struct config_item *item,
 
 CONFIGFS_ATTR(nvmet_ns_, device_path);
 
+#ifdef CONFIG_PCI_P2PDMA
+static ssize_t nvmet_ns_p2pmem_show(struct config_item *item, char *page)
+{
+       struct nvmet_ns *ns = to_nvmet_ns(item);
+
+       return pci_p2pdma_enable_show(page, ns->p2p_dev, ns->use_p2pmem);
+}
+
+static ssize_t nvmet_ns_p2pmem_store(struct config_item *item,
+               const char *page, size_t count)
+{
+       struct nvmet_ns *ns = to_nvmet_ns(item);
+       struct pci_dev *p2p_dev = NULL;
+       bool use_p2pmem;
+       int ret = count;
+       int error;
+
+       mutex_lock(&ns->subsys->lock);
+       if (ns->enabled) {
+               ret = -EBUSY;
+               goto out_unlock;
+       }
+
+       error = pci_p2pdma_enable_store(page, &p2p_dev, &use_p2pmem);
+       if (error) {
+               ret = error;
+               goto out_unlock;
+       }
+
+       ns->use_p2pmem = use_p2pmem;
+       pci_dev_put(ns->p2p_dev);
+       ns->p2p_dev = p2p_dev;
+
+out_unlock:
+       mutex_unlock(&ns->subsys->lock);
+
+       return ret;
+}
+
+CONFIGFS_ATTR(nvmet_ns_, p2pmem);
+#endif /* CONFIG_PCI_P2PDMA */
+
 static ssize_t nvmet_ns_device_uuid_show(struct config_item *item, char *page)
 {
        return sprintf(page, "%pUb\n", &to_nvmet_ns(item)->uuid);
@@ -509,6 +553,9 @@ static struct configfs_attribute *nvmet_ns_attrs[] = {
        &nvmet_ns_attr_ana_grpid,
        &nvmet_ns_attr_enable,
        &nvmet_ns_attr_buffered_io,
+#ifdef CONFIG_PCI_P2PDMA
+       &nvmet_ns_attr_p2pmem,
+#endif
        NULL,
 };
 
index b5ec96abd04870209ed7ea97452180cf6cb63038..9b4d84cfc2245b60d8f9703070eec703aead7da7 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/module.h>
 #include <linux/random.h>
 #include <linux/rculist.h>
+#include <linux/pci-p2pdma.h>
 
 #include "nvmet.h"
 
@@ -365,9 +366,93 @@ static void nvmet_ns_dev_disable(struct nvmet_ns *ns)
        nvmet_file_ns_disable(ns);
 }
 
+static int nvmet_p2pmem_ns_enable(struct nvmet_ns *ns)
+{
+       int ret;
+       struct pci_dev *p2p_dev;
+
+       if (!ns->use_p2pmem)
+               return 0;
+
+       if (!ns->bdev) {
+               pr_err("peer-to-peer DMA is not supported by non-block device namespaces\n");
+               return -EINVAL;
+       }
+
+       if (!blk_queue_pci_p2pdma(ns->bdev->bd_queue)) {
+               pr_err("peer-to-peer DMA is not supported by the driver of %s\n",
+                      ns->device_path);
+               return -EINVAL;
+       }
+
+       if (ns->p2p_dev) {
+               ret = pci_p2pdma_distance(ns->p2p_dev, nvmet_ns_dev(ns), true);
+               if (ret < 0)
+                       return -EINVAL;
+       } else {
+               /*
+                * Right now we just check that there is p2pmem available so
+                * we can report an error to the user right away if there
+                * is not. We'll find the actual device to use once we
+                * setup the controller when the port's device is available.
+                */
+
+               p2p_dev = pci_p2pmem_find(nvmet_ns_dev(ns));
+               if (!p2p_dev) {
+                       pr_err("no peer-to-peer memory is available for %s\n",
+                              ns->device_path);
+                       return -EINVAL;
+               }
+
+               pci_dev_put(p2p_dev);
+       }
+
+       return 0;
+}
+
+/*
+ * Note: ctrl->subsys->lock should be held when calling this function
+ */
+static void nvmet_p2pmem_ns_add_p2p(struct nvmet_ctrl *ctrl,
+                                   struct nvmet_ns *ns)
+{
+       struct device *clients[2];
+       struct pci_dev *p2p_dev;
+       int ret;
+
+       if (!ctrl->p2p_client)
+               return;
+
+       if (ns->p2p_dev) {
+               ret = pci_p2pdma_distance(ns->p2p_dev, ctrl->p2p_client, true);
+               if (ret < 0)
+                       return;
+
+               p2p_dev = pci_dev_get(ns->p2p_dev);
+       } else {
+               clients[0] = ctrl->p2p_client;
+               clients[1] = nvmet_ns_dev(ns);
+
+               p2p_dev = pci_p2pmem_find_many(clients, ARRAY_SIZE(clients));
+               if (!p2p_dev) {
+                       pr_err("no peer-to-peer memory is available that's supported by %s and %s\n",
+                              dev_name(ctrl->p2p_client), ns->device_path);
+                       return;
+               }
+       }
+
+       ret = radix_tree_insert(&ctrl->p2p_ns_map, ns->nsid, p2p_dev);
+       if (ret < 0)
+               pci_dev_put(p2p_dev);
+
+       pr_info("using p2pmem on %s for nsid %d\n", pci_name(p2p_dev),
+               ns->nsid);
+}
+
 int nvmet_ns_enable(struct nvmet_ns *ns)
 {
        struct nvmet_subsys *subsys = ns->subsys;
+       struct nvmet_ctrl *ctrl;
        int ret;
 
        mutex_lock(&subsys->lock);
@@ -384,6 +469,13 @@ int nvmet_ns_enable(struct nvmet_ns *ns)
        if (ret)
                goto out_unlock;
 
+       ret = nvmet_p2pmem_ns_enable(ns);
+       if (ret)
+               goto out_unlock;
+
+       list_for_each_entry(ctrl, &subsys->ctrls, subsys_entry)
+               nvmet_p2pmem_ns_add_p2p(ctrl, ns);
+
        ret = percpu_ref_init(&ns->ref, nvmet_destroy_namespace,
                                0, GFP_KERNEL);
        if (ret)
@@ -418,6 +510,9 @@ int nvmet_ns_enable(struct nvmet_ns *ns)
        mutex_unlock(&subsys->lock);
        return ret;
 out_dev_put:
+       list_for_each_entry(ctrl, &subsys->ctrls, subsys_entry)
+               pci_dev_put(radix_tree_delete(&ctrl->p2p_ns_map, ns->nsid));
+
        nvmet_ns_dev_disable(ns);
        goto out_unlock;
 }
@@ -425,6 +520,7 @@ int nvmet_ns_enable(struct nvmet_ns *ns)
 void nvmet_ns_disable(struct nvmet_ns *ns)
 {
        struct nvmet_subsys *subsys = ns->subsys;
+       struct nvmet_ctrl *ctrl;
 
        mutex_lock(&subsys->lock);
        if (!ns->enabled)
@@ -434,6 +530,10 @@ void nvmet_ns_disable(struct nvmet_ns *ns)
        list_del_rcu(&ns->dev_link);
        if (ns->nsid == subsys->max_nsid)
                subsys->max_nsid = nvmet_max_nsid(subsys);
+
+       list_for_each_entry(ctrl, &subsys->ctrls, subsys_entry)
+               pci_dev_put(radix_tree_delete(&ctrl->p2p_ns_map, ns->nsid));
+
        mutex_unlock(&subsys->lock);
 
        /*
@@ -450,6 +550,7 @@ void nvmet_ns_disable(struct nvmet_ns *ns)
        percpu_ref_exit(&ns->ref);
 
        mutex_lock(&subsys->lock);
+
        subsys->nr_namespaces--;
        nvmet_ns_changed(subsys, ns->nsid);
        nvmet_ns_dev_disable(ns);
@@ -725,6 +826,51 @@ void nvmet_req_execute(struct nvmet_req *req)
 }
 EXPORT_SYMBOL_GPL(nvmet_req_execute);
 
+int nvmet_req_alloc_sgl(struct nvmet_req *req)
+{
+       struct pci_dev *p2p_dev = NULL;
+
+       if (IS_ENABLED(CONFIG_PCI_P2PDMA)) {
+               if (req->sq->ctrl && req->ns)
+                       p2p_dev = radix_tree_lookup(&req->sq->ctrl->p2p_ns_map,
+                                                   req->ns->nsid);
+
+               req->p2p_dev = NULL;
+               if (req->sq->qid && p2p_dev) {
+                       req->sg = pci_p2pmem_alloc_sgl(p2p_dev, &req->sg_cnt,
+                                                      req->transfer_len);
+                       if (req->sg) {
+                               req->p2p_dev = p2p_dev;
+                               return 0;
+                       }
+               }
+
+               /*
+                * If no P2P memory was available we fallback to using
+                * regular memory
+                */
+       }
+
+       req->sg = sgl_alloc(req->transfer_len, GFP_KERNEL, &req->sg_cnt);
+       if (!req->sg)
+               return -ENOMEM;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(nvmet_req_alloc_sgl);
+
+void nvmet_req_free_sgl(struct nvmet_req *req)
+{
+       if (req->p2p_dev)
+               pci_p2pmem_free_sgl(req->p2p_dev, req->sg);
+       else
+               sgl_free(req->sg);
+
+       req->sg = NULL;
+       req->sg_cnt = 0;
+}
+EXPORT_SYMBOL_GPL(nvmet_req_free_sgl);
+
 static inline bool nvmet_cc_en(u32 cc)
 {
        return (cc >> NVME_CC_EN_SHIFT) & 0x1;
@@ -921,6 +1067,37 @@ bool nvmet_host_allowed(struct nvmet_req *req, struct nvmet_subsys *subsys,
                return __nvmet_host_allowed(subsys, hostnqn);
 }
 
+/*
+ * Note: ctrl->subsys->lock should be held when calling this function
+ */
+static void nvmet_setup_p2p_ns_map(struct nvmet_ctrl *ctrl,
+               struct nvmet_req *req)
+{
+       struct nvmet_ns *ns;
+
+       if (!req->p2p_client)
+               return;
+
+       ctrl->p2p_client = get_device(req->p2p_client);
+
+       list_for_each_entry_rcu(ns, &ctrl->subsys->namespaces, dev_link)
+               nvmet_p2pmem_ns_add_p2p(ctrl, ns);
+}
+
+/*
+ * Note: ctrl->subsys->lock should be held when calling this function
+ */
+static void nvmet_release_p2p_ns_map(struct nvmet_ctrl *ctrl)
+{
+       struct radix_tree_iter iter;
+       void __rcu **slot;
+
+       radix_tree_for_each_slot(slot, &ctrl->p2p_ns_map, &iter, 0)
+               pci_dev_put(radix_tree_deref_slot(slot));
+
+       put_device(ctrl->p2p_client);
+}
+
 u16 nvmet_alloc_ctrl(const char *subsysnqn, const char *hostnqn,
                struct nvmet_req *req, u32 kato, struct nvmet_ctrl **ctrlp)
 {
@@ -962,6 +1139,7 @@ u16 nvmet_alloc_ctrl(const char *subsysnqn, const char *hostnqn,
 
        INIT_WORK(&ctrl->async_event_work, nvmet_async_event_work);
        INIT_LIST_HEAD(&ctrl->async_events);
+       INIT_RADIX_TREE(&ctrl->p2p_ns_map, GFP_KERNEL);
 
        memcpy(ctrl->subsysnqn, subsysnqn, NVMF_NQN_SIZE);
        memcpy(ctrl->hostnqn, hostnqn, NVMF_NQN_SIZE);
@@ -1026,6 +1204,7 @@ u16 nvmet_alloc_ctrl(const char *subsysnqn, const char *hostnqn,
 
        mutex_lock(&subsys->lock);
        list_add_tail(&ctrl->subsys_entry, &subsys->ctrls);
+       nvmet_setup_p2p_ns_map(ctrl, req);
        mutex_unlock(&subsys->lock);
 
        *ctrlp = ctrl;
@@ -1053,6 +1232,7 @@ static void nvmet_ctrl_free(struct kref *ref)
        struct nvmet_subsys *subsys = ctrl->subsys;
 
        mutex_lock(&subsys->lock);
+       nvmet_release_p2p_ns_map(ctrl);
        list_del(&ctrl->subsys_entry);
        mutex_unlock(&subsys->lock);
 
index 7bc9f624043296c2bd71d625b6a7ec36d9319015..5660dd7ca75515f0ea257a9ae5d14d964e24576d 100644 (file)
@@ -78,6 +78,9 @@ static void nvmet_bdev_execute_rw(struct nvmet_req *req)
                op = REQ_OP_READ;
        }
 
+       if (is_pci_p2pdma_page(sg_page(req->sg)))
+               op_flags |= REQ_NOMERGE;
+
        sector = le64_to_cpu(req->cmd->rw.slba);
        sector <<= (req->ns->blksize_shift - 9);
 
index ec9af4ee03b603cb2e4e68c23e78d9b59a1a331a..d6be098f342b349bca0952cb5aa376b6db24d1a3 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/configfs.h>
 #include <linux/rcupdate.h>
 #include <linux/blkdev.h>
+#include <linux/radix-tree.h>
 
 #define NVMET_ASYNC_EVENTS             4
 #define NVMET_ERROR_LOG_SLOTS          128
@@ -77,6 +78,9 @@ struct nvmet_ns {
        struct completion       disable_done;
        mempool_t               *bvec_pool;
        struct kmem_cache       *bvec_cache;
+
+       int                     use_p2pmem;
+       struct pci_dev          *p2p_dev;
 };
 
 static inline struct nvmet_ns *to_nvmet_ns(struct config_item *item)
@@ -84,6 +88,11 @@ static inline struct nvmet_ns *to_nvmet_ns(struct config_item *item)
        return container_of(to_config_group(item), struct nvmet_ns, group);
 }
 
+static inline struct device *nvmet_ns_dev(struct nvmet_ns *ns)
+{
+       return ns->bdev ? disk_to_dev(ns->bdev->bd_disk) : NULL;
+}
+
 struct nvmet_cq {
        u16                     qid;
        u16                     size;
@@ -184,6 +193,9 @@ struct nvmet_ctrl {
 
        char                    subsysnqn[NVMF_NQN_FIELD_LEN];
        char                    hostnqn[NVMF_NQN_FIELD_LEN];
+
+       struct device *p2p_client;
+       struct radix_tree_root p2p_ns_map;
 };
 
 struct nvmet_subsys {
@@ -294,6 +306,9 @@ struct nvmet_req {
 
        void (*execute)(struct nvmet_req *req);
        const struct nvmet_fabrics_ops *ops;
+
+       struct pci_dev *p2p_dev;
+       struct device *p2p_client;
 };
 
 extern struct workqueue_struct *buffered_io_wq;
@@ -336,6 +351,8 @@ bool nvmet_req_init(struct nvmet_req *req, struct nvmet_cq *cq,
 void nvmet_req_uninit(struct nvmet_req *req);
 void nvmet_req_execute(struct nvmet_req *req);
 void nvmet_req_complete(struct nvmet_req *req, u16 status);
+int nvmet_req_alloc_sgl(struct nvmet_req *req);
+void nvmet_req_free_sgl(struct nvmet_req *req);
 
 void nvmet_cq_setup(struct nvmet_ctrl *ctrl, struct nvmet_cq *cq, u16 qid,
                u16 size);
index bfc4da660bb4036c9d53764793824f5befb3edb2..3f7971d3706d90d5fbf382072ed8d2da2ac6e8b6 100644 (file)
@@ -503,7 +503,7 @@ static void nvmet_rdma_release_rsp(struct nvmet_rdma_rsp *rsp)
        }
 
        if (rsp->req.sg != rsp->cmd->inline_sg)
-               sgl_free(rsp->req.sg);
+               nvmet_req_free_sgl(&rsp->req);
 
        if (unlikely(!list_empty_careful(&queue->rsp_wr_wait_list)))
                nvmet_rdma_process_wr_wait_list(queue);
@@ -652,24 +652,24 @@ static u16 nvmet_rdma_map_sgl_keyed(struct nvmet_rdma_rsp *rsp,
 {
        struct rdma_cm_id *cm_id = rsp->queue->cm_id;
        u64 addr = le64_to_cpu(sgl->addr);
-       u32 len = get_unaligned_le24(sgl->length);
        u32 key = get_unaligned_le32(sgl->key);
        int ret;
 
+       rsp->req.transfer_len = get_unaligned_le24(sgl->length);
+
        /* no data command? */
-       if (!len)
+       if (!rsp->req.transfer_len)
                return 0;
 
-       rsp->req.sg = sgl_alloc(len, GFP_KERNEL, &rsp->req.sg_cnt);
-       if (!rsp->req.sg)
-               return NVME_SC_INTERNAL;
+       ret = nvmet_req_alloc_sgl(&rsp->req);
+       if (ret < 0)
+               goto error_out;
 
        ret = rdma_rw_ctx_init(&rsp->rw, cm_id->qp, cm_id->port_num,
                        rsp->req.sg, rsp->req.sg_cnt, 0, addr, key,
                        nvmet_data_dir(&rsp->req));
        if (ret < 0)
-               return NVME_SC_INTERNAL;
-       rsp->req.transfer_len += len;
+               goto error_out;
        rsp->n_rdma += ret;
 
        if (invalidate) {
@@ -678,6 +678,10 @@ static u16 nvmet_rdma_map_sgl_keyed(struct nvmet_rdma_rsp *rsp,
        }
 
        return 0;
+
+error_out:
+       rsp->req.transfer_len = 0;
+       return NVME_SC_INTERNAL;
 }
 
 static u16 nvmet_rdma_map_sgl(struct nvmet_rdma_rsp *rsp)
@@ -745,6 +749,8 @@ static void nvmet_rdma_handle_command(struct nvmet_rdma_queue *queue,
                cmd->send_sge.addr, cmd->send_sge.length,
                DMA_TO_DEVICE);
 
+       cmd->req.p2p_client = &queue->dev->device->dev;
+
        if (!nvmet_req_init(&cmd->req, &queue->nvme_cq,
                        &queue->nvme_sq, &nvmet_rdma_ops))
                return;
index 56ff8f6d31fc503598954811e495ac44dcefe597..deb68be4fdac686a73de1061aaada885187b4f1b 100644 (file)
@@ -132,6 +132,23 @@ config PCI_PASID
 
          If unsure, say N.
 
+config PCI_P2PDMA
+       bool "PCI peer-to-peer transfer support"
+       depends on PCI && ZONE_DEVICE
+       select GENERIC_ALLOCATOR
+       help
+         EnableÑ• drivers to do PCI peer-to-peer transactions to and from
+         BARs that are exposed in other devices that are the part of
+         the hierarchy where peer-to-peer DMA is guaranteed by the PCI
+         specification to work (ie. anything below a single PCI bridge).
+
+         Many PCIe root complexes do not support P2P transactions and
+         it's hard to tell which support it at all, so at this time,
+         P2P DMA transations must be between devices behind the same root
+         port.
+
+         If unsure, say N.
+
 config PCI_LABEL
        def_bool y if (DMI || ACPI)
        depends on PCI
index 1b2cfe51e8d719ce6cd6976fbaad485cc8143fd8..85f4a703b2be260bced8c465f4e989ff25fd62c3 100644 (file)
@@ -26,6 +26,7 @@ obj-$(CONFIG_PCI_SYSCALL)     += syscall.o
 obj-$(CONFIG_PCI_STUB)         += pci-stub.o
 obj-$(CONFIG_PCI_PF_STUB)      += pci-pf-stub.o
 obj-$(CONFIG_PCI_ECAM)         += ecam.o
+obj-$(CONFIG_PCI_P2PDMA)       += p2pdma.o
 obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
 
 # Endpoint library must be initialized before its users
index a3ad2fe185b9c517923fc0a26897af0e289dcdd6..544922f097c044a7e434d679ddddf588f72e8467 100644 (file)
@@ -33,7 +33,7 @@ DEFINE_RAW_SPINLOCK(pci_lock);
 #endif
 
 #define PCI_OP_READ(size, type, len) \
-int pci_bus_read_config_##size \
+int noinline pci_bus_read_config_##size \
        (struct pci_bus *bus, unsigned int devfn, int pos, type *value) \
 {                                                                      \
        int res;                                                        \
@@ -48,7 +48,7 @@ int pci_bus_read_config_##size \
 }
 
 #define PCI_OP_WRITE(size, type, len) \
-int pci_bus_write_config_##size \
+int noinline pci_bus_write_config_##size \
        (struct pci_bus *bus, unsigned int devfn, int pos, type value)  \
 {                                                                      \
        int res;                                                        \
diff --git a/drivers/pci/hotplug/TODO b/drivers/pci/hotplug/TODO
new file mode 100644 (file)
index 0000000..a32070b
--- /dev/null
@@ -0,0 +1,74 @@
+Contributions are solicited in particular to remedy the following issues:
+
+cpcihp:
+
+* There are no implementations of the ->hardware_test, ->get_power and
+  ->set_power callbacks in struct cpci_hp_controller_ops.  Why were they
+  introduced?  Can they be removed from the struct?
+
+cpqphp:
+
+* The driver spawns a kthread cpqhp_event_thread() which is woken by the
+  hardirq handler cpqhp_ctrl_intr().  Convert this to threaded IRQ handling.
+  The kthread is also woken from the timer pushbutton_helper_thread(),
+  convert it to call irq_wake_thread().  Use pciehp as a template.
+
+* A large portion of cpqphp_ctrl.c and cpqphp_pci.c concerns resource
+  management.  Doesn't this duplicate functionality in the core?
+
+ibmphp:
+
+* Implementations of hotplug_slot_ops callbacks such as get_adapter_present()
+  in ibmphp_core.c create a copy of the struct slot on the stack, then perform
+  the actual operation on that copy.  Determine if this overhead is necessary,
+  delete it if not.  The functions also perform a NULL pointer check on the
+  struct hotplug_slot, this seems superfluous.
+
+* Several functions access the pci_slot member in struct hotplug_slot even
+  though pci_hotplug.h declares it private.  See get_max_bus_speed() for an
+  example.  Either the pci_slot member should no longer be declared private
+  or ibmphp should store a pointer to its bus in struct slot.  Probably the
+  former.
+
+* The functions get_max_adapter_speed() and get_bus_name() are commented out.
+  Can they be deleted?  There are also forward declarations at the top of
+  ibmphp_core.c as well as pointers in ibmphp_hotplug_slot_ops, likewise
+  commented out.
+
+* ibmphp_init_devno() takes a struct slot **, it could instead take a
+  struct slot *.
+
+* The return value of pci_hp_register() is not checked.
+
+* iounmap(io_mem) is called in the error path of ebda_rsrc_controller()
+  and once more in the error path of its caller ibmphp_access_ebda().
+
+* The various slot data structures are difficult to follow and need to be
+  simplified.  A lot of functions are too large and too complex, they need
+  to be broken up into smaller, manageable pieces.  Negative examples are
+  ebda_rsrc_controller() and configure_bridge().
+
+* A large portion of ibmphp_res.c and ibmphp_pci.c concerns resource
+  management.  Doesn't this duplicate functionality in the core?
+
+sgi_hotplug:
+
+* Several functions access the pci_slot member in struct hotplug_slot even
+  though pci_hotplug.h declares it private.  See sn_hp_destroy() for an
+  example.  Either the pci_slot member should no longer be declared private
+  or sgi_hotplug should store a pointer to it in struct slot.  Probably the
+  former.
+
+shpchp:
+
+* There is only a single implementation of struct hpc_ops.  Can the struct be
+  removed and its functions invoked directly?  This has already been done in
+  pciehp with commit 82a9e79ef132 ("PCI: pciehp: remove hpc_ops").  Clarify
+  if there was a specific reason not to apply the same change to shpchp.
+
+* The ->get_mode1_ECC_cap callback in shpchp_hpc_ops is never invoked.
+  Why was it introduced?  Can it be removed?
+
+* The hardirq handler shpc_isr() queues events on a workqueue.  It can be
+  simplified by converting it to threaded IRQ handling.  Use pciehp as a
+  template.
index e438a2d734f2ae96822f9018a1ae44eb9f4d6ba5..cf3058404f4120f3d8c78d656dabd5587957d85b 100644 (file)
@@ -33,15 +33,19 @@ struct acpiphp_slot;
  * struct slot - slot information for each *physical* slot
  */
 struct slot {
-       struct hotplug_slot     *hotplug_slot;
+       struct hotplug_slot     hotplug_slot;
        struct acpiphp_slot     *acpi_slot;
-       struct hotplug_slot_info info;
        unsigned int sun;       /* ACPI _SUN (Slot User Number) value */
 };
 
 static inline const char *slot_name(struct slot *slot)
 {
-       return hotplug_slot_name(slot->hotplug_slot);
+       return hotplug_slot_name(&slot->hotplug_slot);
+}
+
+static inline struct slot *to_slot(struct hotplug_slot *hotplug_slot)
+{
+       return container_of(hotplug_slot, struct slot, hotplug_slot);
 }
 
 /*
index ad32ffbc4b91900cf15c7533dfcea81694bf6ddf..c9e2bd40c0385e72b3c18643d8642b3d1a41f818 100644 (file)
@@ -57,7 +57,7 @@ static int get_attention_status(struct hotplug_slot *slot, u8 *value);
 static int get_latch_status(struct hotplug_slot *slot, u8 *value);
 static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
 
-static struct hotplug_slot_ops acpi_hotplug_slot_ops = {
+static const struct hotplug_slot_ops acpi_hotplug_slot_ops = {
        .enable_slot            = enable_slot,
        .disable_slot           = disable_slot,
        .set_attention_status   = set_attention_status,
@@ -118,7 +118,7 @@ EXPORT_SYMBOL_GPL(acpiphp_unregister_attention);
  */
 static int enable_slot(struct hotplug_slot *hotplug_slot)
 {
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
 
        pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
 
@@ -135,7 +135,7 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
  */
 static int disable_slot(struct hotplug_slot *hotplug_slot)
 {
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
 
        pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
 
@@ -179,7 +179,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
  */
 static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
 
        pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
 
@@ -225,7 +225,7 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
  */
 static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
 
        pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
 
@@ -245,7 +245,7 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
  */
 static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
 
        pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
 
@@ -266,39 +266,26 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot,
        if (!slot)
                goto error;
 
-       slot->hotplug_slot = kzalloc(sizeof(*slot->hotplug_slot), GFP_KERNEL);
-       if (!slot->hotplug_slot)
-               goto error_slot;
-
-       slot->hotplug_slot->info = &slot->info;
-
-       slot->hotplug_slot->private = slot;
-       slot->hotplug_slot->ops = &acpi_hotplug_slot_ops;
+       slot->hotplug_slot.ops = &acpi_hotplug_slot_ops;
 
        slot->acpi_slot = acpiphp_slot;
-       slot->hotplug_slot->info->power_status = acpiphp_get_power_status(slot->acpi_slot);
-       slot->hotplug_slot->info->attention_status = 0;
-       slot->hotplug_slot->info->latch_status = acpiphp_get_latch_status(slot->acpi_slot);
-       slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot);
 
        acpiphp_slot->slot = slot;
        slot->sun = sun;
        snprintf(name, SLOT_NAME_SIZE, "%u", sun);
 
-       retval = pci_hp_register(slot->hotplug_slot, acpiphp_slot->bus,
+       retval = pci_hp_register(&slot->hotplug_slot, acpiphp_slot->bus,
                                 acpiphp_slot->device, name);
        if (retval == -EBUSY)
-               goto error_hpslot;
+               goto error_slot;
        if (retval) {
                pr_err("pci_hp_register failed with error %d\n", retval);
-               goto error_hpslot;
+               goto error_slot;
        }
 
        pr_info("Slot [%s] registered\n", slot_name(slot));
 
        return 0;
-error_hpslot:
-       kfree(slot->hotplug_slot);
 error_slot:
        kfree(slot);
 error:
@@ -312,8 +299,7 @@ void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
 
        pr_info("Slot [%s] unregistered\n", slot_name(slot));
 
-       pci_hp_deregister(slot->hotplug_slot);
-       kfree(slot->hotplug_slot);
+       pci_hp_deregister(&slot->hotplug_slot);
        kfree(slot);
 }
 
index 41713f16ff97250aa9c314de3b3a83263c61c77d..df48b3b03ab48d0ecb0b69092e908f8be4c1bc62 100644 (file)
@@ -41,7 +41,7 @@ MODULE_VERSION(DRIVER_VERSION);
 #define IBM_HARDWARE_ID1 "IBM37D0"
 #define IBM_HARDWARE_ID2 "IBM37D4"
 
-#define hpslot_to_sun(A) (((struct slot *)((A)->private))->sun)
+#define hpslot_to_sun(A) (to_slot(A)->sun)
 
 /* union apci_descriptor - allows access to the
  * various device descriptors that are embedded in the
index 4658557be01a30ccae3897fd69805dad389c63fc..f33ff2bca414f00e5e1479973abeb5c7e58b1d6c 100644 (file)
@@ -32,8 +32,10 @@ struct slot {
        unsigned int devfn;
        struct pci_bus *bus;
        struct pci_dev *dev;
+       unsigned int latch_status:1;
+       unsigned int adapter_status:1;
        unsigned int extracting;
-       struct hotplug_slot *hotplug_slot;
+       struct hotplug_slot hotplug_slot;
        struct list_head slot_list;
 };
 
@@ -58,7 +60,12 @@ struct cpci_hp_controller {
 
 static inline const char *slot_name(struct slot *slot)
 {
-       return hotplug_slot_name(slot->hotplug_slot);
+       return hotplug_slot_name(&slot->hotplug_slot);
+}
+
+static inline struct slot *to_slot(struct hotplug_slot *hotplug_slot)
+{
+       return container_of(hotplug_slot, struct slot, hotplug_slot);
 }
 
 int cpci_hp_register_controller(struct cpci_hp_controller *controller);
index 52a339baf06c03913ceb7993b4cf3a7eb008539e..603eadf3d96595f942c60d03903772da5f21b61f 100644 (file)
@@ -57,7 +57,7 @@ static int get_attention_status(struct hotplug_slot *slot, u8 *value);
 static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
 static int get_latch_status(struct hotplug_slot *slot, u8 *value);
 
-static struct hotplug_slot_ops cpci_hotplug_slot_ops = {
+static const struct hotplug_slot_ops cpci_hotplug_slot_ops = {
        .enable_slot = enable_slot,
        .disable_slot = disable_slot,
        .set_attention_status = set_attention_status,
@@ -67,30 +67,10 @@ static struct hotplug_slot_ops cpci_hotplug_slot_ops = {
        .get_latch_status = get_latch_status,
 };
 
-static int
-update_latch_status(struct hotplug_slot *hotplug_slot, u8 value)
-{
-       struct hotplug_slot_info info;
-
-       memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info));
-       info.latch_status = value;
-       return pci_hp_change_slot_info(hotplug_slot, &info);
-}
-
-static int
-update_adapter_status(struct hotplug_slot *hotplug_slot, u8 value)
-{
-       struct hotplug_slot_info info;
-
-       memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info));
-       info.adapter_status = value;
-       return pci_hp_change_slot_info(hotplug_slot, &info);
-}
-
 static int
 enable_slot(struct hotplug_slot *hotplug_slot)
 {
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
        int retval = 0;
 
        dbg("%s - physical_slot = %s", __func__, slot_name(slot));
@@ -103,7 +83,7 @@ enable_slot(struct hotplug_slot *hotplug_slot)
 static int
 disable_slot(struct hotplug_slot *hotplug_slot)
 {
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
        int retval = 0;
 
        dbg("%s - physical_slot = %s", __func__, slot_name(slot));
@@ -135,8 +115,7 @@ disable_slot(struct hotplug_slot *hotplug_slot)
                        goto disable_error;
        }
 
-       if (update_adapter_status(slot->hotplug_slot, 0))
-               warn("failure to update adapter file");
+       slot->adapter_status = 0;
 
        if (slot->extracting) {
                slot->extracting = 0;
@@ -160,7 +139,7 @@ cpci_get_power_status(struct slot *slot)
 static int
 get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
 
        *value = cpci_get_power_status(slot);
        return 0;
@@ -169,7 +148,7 @@ get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 static int
 get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
 
        *value = cpci_get_attention_status(slot);
        return 0;
@@ -178,27 +157,29 @@ get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
 static int
 set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
 {
-       return cpci_set_attention_status(hotplug_slot->private, status);
+       return cpci_set_attention_status(to_slot(hotplug_slot), status);
 }
 
 static int
 get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
-       *value = hotplug_slot->info->adapter_status;
+       struct slot *slot = to_slot(hotplug_slot);
+
+       *value = slot->adapter_status;
        return 0;
 }
 
 static int
 get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
-       *value = hotplug_slot->info->latch_status;
+       struct slot *slot = to_slot(hotplug_slot);
+
+       *value = slot->latch_status;
        return 0;
 }
 
 static void release_slot(struct slot *slot)
 {
-       kfree(slot->hotplug_slot->info);
-       kfree(slot->hotplug_slot);
        pci_dev_put(slot->dev);
        kfree(slot);
 }
@@ -209,8 +190,6 @@ int
 cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
 {
        struct slot *slot;
-       struct hotplug_slot *hotplug_slot;
-       struct hotplug_slot_info *info;
        char name[SLOT_NAME_SIZE];
        int status;
        int i;
@@ -229,43 +208,19 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
                        goto error;
                }
 
-               hotplug_slot =
-                       kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
-               if (!hotplug_slot) {
-                       status = -ENOMEM;
-                       goto error_slot;
-               }
-               slot->hotplug_slot = hotplug_slot;
-
-               info = kzalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
-               if (!info) {
-                       status = -ENOMEM;
-                       goto error_hpslot;
-               }
-               hotplug_slot->info = info;
-
                slot->bus = bus;
                slot->number = i;
                slot->devfn = PCI_DEVFN(i, 0);
 
                snprintf(name, SLOT_NAME_SIZE, "%02x:%02x", bus->number, i);
 
-               hotplug_slot->private = slot;
-               hotplug_slot->ops = &cpci_hotplug_slot_ops;
-
-               /*
-                * Initialize the slot info structure with some known
-                * good values.
-                */
-               dbg("initializing slot %s", name);
-               info->power_status = cpci_get_power_status(slot);
-               info->attention_status = cpci_get_attention_status(slot);
+               slot->hotplug_slot.ops = &cpci_hotplug_slot_ops;
 
                dbg("registering slot %s", name);
-               status = pci_hp_register(slot->hotplug_slot, bus, i, name);
+               status = pci_hp_register(&slot->hotplug_slot, bus, i, name);
                if (status) {
                        err("pci_hp_register failed with error %d", status);
-                       goto error_info;
+                       goto error_slot;
                }
                dbg("slot registered with name: %s", slot_name(slot));
 
@@ -276,10 +231,6 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
                up_write(&list_rwsem);
        }
        return 0;
-error_info:
-       kfree(info);
-error_hpslot:
-       kfree(hotplug_slot);
 error_slot:
        kfree(slot);
 error:
@@ -305,7 +256,7 @@ cpci_hp_unregister_bus(struct pci_bus *bus)
                        slots--;
 
                        dbg("deregistering slot %s", slot_name(slot));
-                       pci_hp_deregister(slot->hotplug_slot);
+                       pci_hp_deregister(&slot->hotplug_slot);
                        release_slot(slot);
                }
        }
@@ -359,10 +310,8 @@ init_slots(int clear_ins)
                            __func__, slot_name(slot));
                dev = pci_get_slot(slot->bus, PCI_DEVFN(slot->number, 0));
                if (dev) {
-                       if (update_adapter_status(slot->hotplug_slot, 1))
-                               warn("failure to update adapter file");
-                       if (update_latch_status(slot->hotplug_slot, 1))
-                               warn("failure to update latch file");
+                       slot->adapter_status = 1;
+                       slot->latch_status = 1;
                        slot->dev = dev;
                }
        }
@@ -424,11 +373,8 @@ check_slots(void)
                        dbg("%s - slot %s HS_CSR (2) = %04x",
                            __func__, slot_name(slot), hs_csr);
 
-                       if (update_latch_status(slot->hotplug_slot, 1))
-                               warn("failure to update latch file");
-
-                       if (update_adapter_status(slot->hotplug_slot, 1))
-                               warn("failure to update adapter file");
+                       slot->latch_status = 1;
+                       slot->adapter_status = 1;
 
                        cpci_led_off(slot);
 
@@ -449,9 +395,7 @@ check_slots(void)
                            __func__, slot_name(slot), hs_csr);
 
                        if (!slot->extracting) {
-                               if (update_latch_status(slot->hotplug_slot, 0))
-                                       warn("failure to update latch file");
-
+                               slot->latch_status = 0;
                                slot->extracting = 1;
                                atomic_inc(&extracting);
                        }
@@ -465,8 +409,7 @@ check_slots(void)
                                 */
                                err("card in slot %s was improperly removed",
                                    slot_name(slot));
-                               if (update_adapter_status(slot->hotplug_slot, 0))
-                                       warn("failure to update adapter file");
+                               slot->adapter_status = 0;
                                slot->extracting = 0;
                                atomic_dec(&extracting);
                        }
@@ -615,7 +558,7 @@ cleanup_slots(void)
                goto cleanup_null;
        list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
                list_del(&slot->slot_list);
-               pci_hp_deregister(slot->hotplug_slot);
+               pci_hp_deregister(&slot->hotplug_slot);
                release_slot(slot);
        }
 cleanup_null:
index 389b8fb50cd9f2fafed41837b1f369a68f71772f..2c16adb7f4ec1b92ca931eddda34f5ee5903335c 100644 (file)
@@ -194,8 +194,7 @@ int cpci_led_on(struct slot *slot)
                                              slot->devfn,
                                              hs_cap + 2,
                                              hs_csr)) {
-                       err("Could not set LOO for slot %s",
-                           hotplug_slot_name(slot->hotplug_slot));
+                       err("Could not set LOO for slot %s", slot_name(slot));
                        return -ENODEV;
                }
        }
@@ -223,8 +222,7 @@ int cpci_led_off(struct slot *slot)
                                              slot->devfn,
                                              hs_cap + 2,
                                              hs_csr)) {
-                       err("Could not clear LOO for slot %s",
-                           hotplug_slot_name(slot->hotplug_slot));
+                       err("Could not clear LOO for slot %s", slot_name(slot));
                        return -ENODEV;
                }
        }
index db78b394a0756ca880f91f31114f685b63c549cb..77e4e0142fbc54d2994311ebe372af480b4c0779 100644 (file)
@@ -260,7 +260,7 @@ struct slot {
        u8 hp_slot;
        struct controller *ctrl;
        void __iomem *p_sm_slot;
-       struct hotplug_slot *hotplug_slot;
+       struct hotplug_slot hotplug_slot;
 };
 
 struct pci_resource {
@@ -445,7 +445,12 @@ extern u8 cpqhp_disk_irq;
 
 static inline const char *slot_name(struct slot *slot)
 {
-       return hotplug_slot_name(slot->hotplug_slot);
+       return hotplug_slot_name(&slot->hotplug_slot);
+}
+
+static inline struct slot *to_slot(struct hotplug_slot *hotplug_slot)
+{
+       return container_of(hotplug_slot, struct slot, hotplug_slot);
 }
 
 /*
index 5a06636e910ad4e359cbd79d37e5f6b5bb268709..16bbb183695acc020f2478d6499ae3736b419c03 100644 (file)
@@ -121,7 +121,6 @@ static int init_SERR(struct controller *ctrl)
 {
        u32 tempdword;
        u32 number_of_slots;
-       u8 physical_slot;
 
        if (!ctrl)
                return 1;
@@ -131,7 +130,6 @@ static int init_SERR(struct controller *ctrl)
        number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F;
        /* Loop through slots */
        while (number_of_slots) {
-               physical_slot = tempdword;
                writeb(0, ctrl->hpc_reg + SLOT_SERR);
                tempdword++;
                number_of_slots--;
@@ -275,9 +273,7 @@ static int ctrl_slot_cleanup(struct controller *ctrl)
 
        while (old_slot) {
                next_slot = old_slot->next;
-               pci_hp_deregister(old_slot->hotplug_slot);
-               kfree(old_slot->hotplug_slot->info);
-               kfree(old_slot->hotplug_slot);
+               pci_hp_deregister(&old_slot->hotplug_slot);
                kfree(old_slot);
                old_slot = next_slot;
        }
@@ -419,7 +415,7 @@ cpqhp_set_attention_status(struct controller *ctrl, struct pci_func *func,
 static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
 {
        struct pci_func *slot_func;
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
        struct controller *ctrl = slot->ctrl;
        u8 bus;
        u8 devfn;
@@ -446,7 +442,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
 static int process_SI(struct hotplug_slot *hotplug_slot)
 {
        struct pci_func *slot_func;
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
        struct controller *ctrl = slot->ctrl;
        u8 bus;
        u8 devfn;
@@ -478,7 +474,7 @@ static int process_SI(struct hotplug_slot *hotplug_slot)
 static int process_SS(struct hotplug_slot *hotplug_slot)
 {
        struct pci_func *slot_func;
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
        struct controller *ctrl = slot->ctrl;
        u8 bus;
        u8 devfn;
@@ -505,7 +501,7 @@ static int process_SS(struct hotplug_slot *hotplug_slot)
 
 static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value)
 {
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
        struct controller *ctrl = slot->ctrl;
 
        dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
@@ -516,7 +512,7 @@ static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value)
 
 static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
        struct controller *ctrl = slot->ctrl;
 
        dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
@@ -527,7 +523,7 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 
 static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
        struct controller *ctrl = slot->ctrl;
 
        dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
@@ -538,7 +534,7 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
 
 static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
        struct controller *ctrl = slot->ctrl;
 
        dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
@@ -550,7 +546,7 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
 
 static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
        struct controller *ctrl = slot->ctrl;
 
        dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
@@ -560,7 +556,7 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
        return 0;
 }
 
-static struct hotplug_slot_ops cpqphp_hotplug_slot_ops = {
+static const struct hotplug_slot_ops cpqphp_hotplug_slot_ops = {
        .set_attention_status = set_attention_status,
        .enable_slot =          process_SI,
        .disable_slot =         process_SS,
@@ -578,8 +574,6 @@ static int ctrl_slot_setup(struct controller *ctrl,
                        void __iomem *smbios_table)
 {
        struct slot *slot;
-       struct hotplug_slot *hotplug_slot;
-       struct hotplug_slot_info *hotplug_slot_info;
        struct pci_bus *bus = ctrl->pci_bus;
        u8 number_of_slots;
        u8 slot_device;
@@ -605,22 +599,6 @@ static int ctrl_slot_setup(struct controller *ctrl,
                        goto error;
                }
 
-               slot->hotplug_slot = kzalloc(sizeof(*(slot->hotplug_slot)),
-                                               GFP_KERNEL);
-               if (!slot->hotplug_slot) {
-                       result = -ENOMEM;
-                       goto error_slot;
-               }
-               hotplug_slot = slot->hotplug_slot;
-
-               hotplug_slot->info = kzalloc(sizeof(*(hotplug_slot->info)),
-                                                       GFP_KERNEL);
-               if (!hotplug_slot->info) {
-                       result = -ENOMEM;
-                       goto error_hpslot;
-               }
-               hotplug_slot_info = hotplug_slot->info;
-
                slot->ctrl = ctrl;
                slot->bus = ctrl->bus;
                slot->device = slot_device;
@@ -669,29 +647,20 @@ static int ctrl_slot_setup(struct controller *ctrl,
                        ((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04;
 
                /* register this slot with the hotplug pci core */
-               hotplug_slot->private = slot;
                snprintf(name, SLOT_NAME_SIZE, "%u", slot->number);
-               hotplug_slot->ops = &cpqphp_hotplug_slot_ops;
-
-               hotplug_slot_info->power_status = get_slot_enabled(ctrl, slot);
-               hotplug_slot_info->attention_status =
-                       cpq_get_attention_status(ctrl, slot);
-               hotplug_slot_info->latch_status =
-                       cpq_get_latch_status(ctrl, slot);
-               hotplug_slot_info->adapter_status =
-                       get_presence_status(ctrl, slot);
+               slot->hotplug_slot.ops = &cpqphp_hotplug_slot_ops;
 
                dbg("registering bus %d, dev %d, number %d, ctrl->slot_device_offset %d, slot %d\n",
                                slot->bus, slot->device,
                                slot->number, ctrl->slot_device_offset,
                                slot_number);
-               result = pci_hp_register(hotplug_slot,
+               result = pci_hp_register(&slot->hotplug_slot,
                                         ctrl->pci_dev->bus,
                                         slot->device,
                                         name);
                if (result) {
                        err("pci_hp_register failed with error %d\n", result);
-                       goto error_info;
+                       goto error_slot;
                }
 
                slot->next = ctrl->slot;
@@ -703,10 +672,6 @@ static int ctrl_slot_setup(struct controller *ctrl,
        }
 
        return 0;
-error_info:
-       kfree(hotplug_slot_info);
-error_hpslot:
-       kfree(hotplug_slot);
 error_slot:
        kfree(slot);
 error:
index 616df442520b4b69cb929ece971e54da0b8de806..b7f4e1f099d91fa8bf12d52330104b622d548a3f 100644 (file)
@@ -1130,9 +1130,7 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_
        for (slot = ctrl->slot; slot; slot = slot->next) {
                if (slot->device == (hp_slot + ctrl->slot_device_offset))
                        continue;
-               if (!slot->hotplug_slot || !slot->hotplug_slot->info)
-                       continue;
-               if (slot->hotplug_slot->info->adapter_status == 0)
+               if (get_presence_status(ctrl, slot) == 0)
                        continue;
                /* If another adapter is running on the same segment but at a
                 * lower speed/mode, we allow the new adapter to function at
@@ -1767,24 +1765,6 @@ void cpqhp_event_stop_thread(void)
 }
 
 
-static int update_slot_info(struct controller *ctrl, struct slot *slot)
-{
-       struct hotplug_slot_info *info;
-       int result;
-
-       info = kmalloc(sizeof(*info), GFP_KERNEL);
-       if (!info)
-               return -ENOMEM;
-
-       info->power_status = get_slot_enabled(ctrl, slot);
-       info->attention_status = cpq_get_attention_status(ctrl, slot);
-       info->latch_status = cpq_get_latch_status(ctrl, slot);
-       info->adapter_status = get_presence_status(ctrl, slot);
-       result = pci_hp_change_slot_info(slot->hotplug_slot, info);
-       kfree(info);
-       return result;
-}
-
 static void interrupt_event_handler(struct controller *ctrl)
 {
        int loop = 0;
@@ -1884,9 +1864,6 @@ static void interrupt_event_handler(struct controller *ctrl)
                                /***********POWER FAULT */
                                else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) {
                                        dbg("power fault\n");
-                               } else {
-                                       /* refresh notification */
-                                       update_slot_info(ctrl, p_slot);
                                }
 
                                ctrl->event_queue[loop].event_type = 0;
@@ -2057,9 +2034,6 @@ int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func)
        if (rc)
                dbg("%s: rc = %d\n", __func__, rc);
 
-       if (p_slot)
-               update_slot_info(ctrl, p_slot);
-
        return rc;
 }
 
@@ -2125,9 +2099,6 @@ int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func)
                rc = 1;
        }
 
-       if (p_slot)
-               update_slot_info(ctrl, p_slot);
-
        return rc;
 }
 
index fddb78606c7446c7eaaf6fb065b4b1ab84653601..b89f850c3a4e91a5af3e7455b114cc6443e6e121 100644 (file)
@@ -698,7 +698,7 @@ struct slot {
        u8 supported_bus_mode;
        u8 flag;                /* this is for disable slot and polling */
        u8 ctlr_index;
-       struct hotplug_slot *hotplug_slot;
+       struct hotplug_slot hotplug_slot;
        struct controller *ctrl;
        struct pci_func *func;
        u8 irq[4];
@@ -740,7 +740,12 @@ int ibmphp_do_disable_slot(struct slot *slot_cur);
 int ibmphp_update_slot_info(struct slot *);    /* This function is called from HPC, so we need it to not be be static */
 int ibmphp_configure_card(struct pci_func *, u8);
 int ibmphp_unconfigure_card(struct slot **, int);
-extern struct hotplug_slot_ops ibmphp_hotplug_slot_ops;
+extern const struct hotplug_slot_ops ibmphp_hotplug_slot_ops;
+
+static inline struct slot *to_slot(struct hotplug_slot *hotplug_slot)
+{
+       return container_of(hotplug_slot, struct slot, hotplug_slot);
+}
 
 #endif                         //__IBMPHP_H
 
index 4ea57e9019f16b5876b0b51d89a50f734e3a7e1b..08a58e911fc25c87b5d55116f99238209478380b 100644 (file)
@@ -247,11 +247,8 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
                        break;
                }
                if (rc == 0) {
-                       pslot = hotplug_slot->private;
-                       if (pslot)
-                               rc = ibmphp_hpc_writeslot(pslot, cmd);
-                       else
-                               rc = -ENODEV;
+                       pslot = to_slot(hotplug_slot);
+                       rc = ibmphp_hpc_writeslot(pslot, cmd);
                }
        } else
                rc = -ENODEV;
@@ -273,19 +270,15 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
 
        ibmphp_lock_operations();
        if (hotplug_slot) {
-               pslot = hotplug_slot->private;
-               if (pslot) {
-                       memcpy(&myslot, pslot, sizeof(struct slot));
-                       rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
-                                               &(myslot.status));
-                       if (!rc)
-                               rc = ibmphp_hpc_readslot(pslot,
-                                               READ_EXTSLOTSTATUS,
-                                               &(myslot.ext_status));
-                       if (!rc)
-                               *value = SLOT_ATTN(myslot.status,
-                                               myslot.ext_status);
-               }
+               pslot = to_slot(hotplug_slot);
+               memcpy(&myslot, pslot, sizeof(struct slot));
+               rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
+                                        &myslot.status);
+               if (!rc)
+                       rc = ibmphp_hpc_readslot(pslot, READ_EXTSLOTSTATUS,
+                                                &myslot.ext_status);
+               if (!rc)
+                       *value = SLOT_ATTN(myslot.status, myslot.ext_status);
        }
 
        ibmphp_unlock_operations();
@@ -303,14 +296,12 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
                                        (ulong) hotplug_slot, (ulong) value);
        ibmphp_lock_operations();
        if (hotplug_slot) {
-               pslot = hotplug_slot->private;
-               if (pslot) {
-                       memcpy(&myslot, pslot, sizeof(struct slot));
-                       rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
-                                               &(myslot.status));
-                       if (!rc)
-                               *value = SLOT_LATCH(myslot.status);
-               }
+               pslot = to_slot(hotplug_slot);
+               memcpy(&myslot, pslot, sizeof(struct slot));
+               rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
+                                        &myslot.status);
+               if (!rc)
+                       *value = SLOT_LATCH(myslot.status);
        }
 
        ibmphp_unlock_operations();
@@ -330,14 +321,12 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
                                        (ulong) hotplug_slot, (ulong) value);
        ibmphp_lock_operations();
        if (hotplug_slot) {
-               pslot = hotplug_slot->private;
-               if (pslot) {
-                       memcpy(&myslot, pslot, sizeof(struct slot));
-                       rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
-                                               &(myslot.status));
-                       if (!rc)
-                               *value = SLOT_PWRGD(myslot.status);
-               }
+               pslot = to_slot(hotplug_slot);
+               memcpy(&myslot, pslot, sizeof(struct slot));
+               rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
+                                        &myslot.status);
+               if (!rc)
+                       *value = SLOT_PWRGD(myslot.status);
        }
 
        ibmphp_unlock_operations();
@@ -357,18 +346,16 @@ static int get_adapter_present(struct hotplug_slot *hotplug_slot, u8 *value)
                                        (ulong) hotplug_slot, (ulong) value);
        ibmphp_lock_operations();
        if (hotplug_slot) {
-               pslot = hotplug_slot->private;
-               if (pslot) {
-                       memcpy(&myslot, pslot, sizeof(struct slot));
-                       rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
-                                               &(myslot.status));
-                       if (!rc) {
-                               present = SLOT_PRESENT(myslot.status);
-                               if (present == HPC_SLOT_EMPTY)
-                                       *value = 0;
-                               else
-                                       *value = 1;
-                       }
+               pslot = to_slot(hotplug_slot);
+               memcpy(&myslot, pslot, sizeof(struct slot));
+               rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
+                                        &myslot.status);
+               if (!rc) {
+                       present = SLOT_PRESENT(myslot.status);
+                       if (present == HPC_SLOT_EMPTY)
+                               *value = 0;
+                       else
+                               *value = 1;
                }
        }
 
@@ -382,7 +369,7 @@ static int get_max_bus_speed(struct slot *slot)
        int rc = 0;
        u8 mode = 0;
        enum pci_bus_speed speed;
-       struct pci_bus *bus = slot->hotplug_slot->pci_slot->bus;
+       struct pci_bus *bus = slot->hotplug_slot.pci_slot->bus;
 
        debug("%s - Entry slot[%p]\n", __func__, slot);
 
@@ -582,29 +569,10 @@ static int validate(struct slot *slot_cur, int opn)
  ****************************************************************************/
 int ibmphp_update_slot_info(struct slot *slot_cur)
 {
-       struct hotplug_slot_info *info;
-       struct pci_bus *bus = slot_cur->hotplug_slot->pci_slot->bus;
-       int rc;
+       struct pci_bus *bus = slot_cur->hotplug_slot.pci_slot->bus;
        u8 bus_speed;
        u8 mode;
 
-       info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
-       if (!info)
-               return -ENOMEM;
-
-       info->power_status = SLOT_PWRGD(slot_cur->status);
-       info->attention_status = SLOT_ATTN(slot_cur->status,
-                                               slot_cur->ext_status);
-       info->latch_status = SLOT_LATCH(slot_cur->status);
-       if (!SLOT_PRESENT(slot_cur->status)) {
-               info->adapter_status = 0;
-/*             info->max_adapter_speed_status = MAX_ADAPTER_NONE; */
-       } else {
-               info->adapter_status = 1;
-/*             get_max_adapter_speed_1(slot_cur->hotplug_slot,
-                                       &info->max_adapter_speed_status, 0); */
-       }
-
        bus_speed = slot_cur->bus_on->current_speed;
        mode = slot_cur->bus_on->current_bus_mode;
 
@@ -630,9 +598,7 @@ int ibmphp_update_slot_info(struct slot *slot_cur)
        bus->cur_bus_speed = bus_speed;
        // To do: bus_names
 
-       rc = pci_hp_change_slot_info(slot_cur->hotplug_slot, info);
-       kfree(info);
-       return rc;
+       return 0;
 }
 
 
@@ -673,7 +639,7 @@ static void free_slots(void)
 
        list_for_each_entry_safe(slot_cur, next, &ibmphp_slot_head,
                                 ibm_slot_list) {
-               pci_hp_del(slot_cur->hotplug_slot);
+               pci_hp_del(&slot_cur->hotplug_slot);
                slot_cur->ctrl = NULL;
                slot_cur->bus_on = NULL;
 
@@ -683,9 +649,7 @@ static void free_slots(void)
                 */
                ibmphp_unconfigure_card(&slot_cur, -1);
 
-               pci_hp_destroy(slot_cur->hotplug_slot);
-               kfree(slot_cur->hotplug_slot->info);
-               kfree(slot_cur->hotplug_slot);
+               pci_hp_destroy(&slot_cur->hotplug_slot);
                kfree(slot_cur);
        }
        debug("%s -- exit\n", __func__);
@@ -1007,7 +971,7 @@ static int enable_slot(struct hotplug_slot *hs)
        ibmphp_lock_operations();
 
        debug("ENABLING SLOT........\n");
-       slot_cur = hs->private;
+       slot_cur = to_slot(hs);
 
        rc = validate(slot_cur, ENABLE);
        if (rc) {
@@ -1095,8 +1059,7 @@ static int enable_slot(struct hotplug_slot *hs)
 
        slot_cur->func = kzalloc(sizeof(struct pci_func), GFP_KERNEL);
        if (!slot_cur->func) {
-               /* We cannot do update_slot_info here, since no memory for
-                * kmalloc n.e.ways, and update_slot_info allocates some */
+               /* do update_slot_info here? */
                rc = -ENOMEM;
                goto error_power;
        }
@@ -1169,7 +1132,7 @@ static int enable_slot(struct hotplug_slot *hs)
 **************************************************************/
 static int ibmphp_disable_slot(struct hotplug_slot *hotplug_slot)
 {
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
        int rc;
 
        ibmphp_lock_operations();
@@ -1259,7 +1222,7 @@ int ibmphp_do_disable_slot(struct slot *slot_cur)
        goto exit;
 }
 
-struct hotplug_slot_ops ibmphp_hotplug_slot_ops = {
+const struct hotplug_slot_ops ibmphp_hotplug_slot_ops = {
        .set_attention_status =         set_attention_status,
        .enable_slot =                  enable_slot,
        .disable_slot =                 ibmphp_disable_slot,
index 6f8e90e3ec08ce4d41c1477dd92b7583ea35ecf6..11a2661dc06277319e0cd2ed873cd4b6408bdebc 100644 (file)
@@ -666,36 +666,8 @@ static int fillslotinfo(struct hotplug_slot *hotplug_slot)
        struct slot *slot;
        int rc = 0;
 
-       if (!hotplug_slot || !hotplug_slot->private)
-               return -EINVAL;
-
-       slot = hotplug_slot->private;
+       slot = to_slot(hotplug_slot);
        rc = ibmphp_hpc_readslot(slot, READ_ALLSTAT, NULL);
-       if (rc)
-               return rc;
-
-       // power - enabled:1  not:0
-       hotplug_slot->info->power_status = SLOT_POWER(slot->status);
-
-       // attention - off:0, on:1, blinking:2
-       hotplug_slot->info->attention_status = SLOT_ATTN(slot->status, slot->ext_status);
-
-       // latch - open:1 closed:0
-       hotplug_slot->info->latch_status = SLOT_LATCH(slot->status);
-
-       // pci board - present:1 not:0
-       if (SLOT_PRESENT(slot->status))
-               hotplug_slot->info->adapter_status = 1;
-       else
-               hotplug_slot->info->adapter_status = 0;
-/*
-       if (slot->bus_on->supported_bus_mode
-               && (slot->bus_on->supported_speed == BUS_SPEED_66))
-               hotplug_slot->info->max_bus_speed_status = BUS_SPEED_66PCIX;
-       else
-               hotplug_slot->info->max_bus_speed_status = slot->bus_on->supported_speed;
-*/
-
        return rc;
 }
 
@@ -712,7 +684,6 @@ static int __init ebda_rsrc_controller(void)
        u8 ctlr_id, temp, bus_index;
        u16 ctlr, slot, bus;
        u16 slot_num, bus_num, index;
-       struct hotplug_slot *hp_slot_ptr;
        struct controller *hpc_ptr;
        struct ebda_hpc_bus *bus_ptr;
        struct ebda_hpc_slot *slot_ptr;
@@ -771,7 +742,7 @@ static int __init ebda_rsrc_controller(void)
                                bus_info_ptr1 = kzalloc(sizeof(struct bus_info), GFP_KERNEL);
                                if (!bus_info_ptr1) {
                                        rc = -ENOMEM;
-                                       goto error_no_hp_slot;
+                                       goto error_no_slot;
                                }
                                bus_info_ptr1->slot_min = slot_ptr->slot_num;
                                bus_info_ptr1->slot_max = slot_ptr->slot_num;
@@ -842,7 +813,7 @@ static int __init ebda_rsrc_controller(void)
                                                     (hpc_ptr->u.isa_ctlr.io_end - hpc_ptr->u.isa_ctlr.io_start + 1),
                                                     "ibmphp")) {
                                        rc = -ENODEV;
-                                       goto error_no_hp_slot;
+                                       goto error_no_slot;
                                }
                                hpc_ptr->irq = readb(io_mem + addr + 4);
                                addr += 5;
@@ -857,7 +828,7 @@ static int __init ebda_rsrc_controller(void)
                                break;
                        default:
                                rc = -ENODEV;
-                               goto error_no_hp_slot;
+                               goto error_no_slot;
                }
 
                //reorganize chassis' linked list
@@ -870,19 +841,6 @@ static int __init ebda_rsrc_controller(void)
 
                // register slots with hpc core as well as create linked list of ibm slot
                for (index = 0; index < hpc_ptr->slot_count; index++) {
-
-                       hp_slot_ptr = kzalloc(sizeof(*hp_slot_ptr), GFP_KERNEL);
-                       if (!hp_slot_ptr) {
-                               rc = -ENOMEM;
-                               goto error_no_hp_slot;
-                       }
-
-                       hp_slot_ptr->info = kzalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
-                       if (!hp_slot_ptr->info) {
-                               rc = -ENOMEM;
-                               goto error_no_hp_info;
-                       }
-
                        tmp_slot = kzalloc(sizeof(*tmp_slot), GFP_KERNEL);
                        if (!tmp_slot) {
                                rc = -ENOMEM;
@@ -909,7 +867,6 @@ static int __init ebda_rsrc_controller(void)
 
                        bus_info_ptr1 = ibmphp_find_same_bus_num(hpc_ptr->slots[index].slot_bus_num);
                        if (!bus_info_ptr1) {
-                               kfree(tmp_slot);
                                rc = -ENODEV;
                                goto error;
                        }
@@ -919,22 +876,19 @@ static int __init ebda_rsrc_controller(void)
 
                        tmp_slot->ctlr_index = hpc_ptr->slots[index].ctl_index;
                        tmp_slot->number = hpc_ptr->slots[index].slot_num;
-                       tmp_slot->hotplug_slot = hp_slot_ptr;
-
-                       hp_slot_ptr->private = tmp_slot;
 
-                       rc = fillslotinfo(hp_slot_ptr);
+                       rc = fillslotinfo(&tmp_slot->hotplug_slot);
                        if (rc)
                                goto error;
 
-                       rc = ibmphp_init_devno((struct slot **) &hp_slot_ptr->private);
+                       rc = ibmphp_init_devno(&tmp_slot);
                        if (rc)
                                goto error;
-                       hp_slot_ptr->ops = &ibmphp_hotplug_slot_ops;
+                       tmp_slot->hotplug_slot.ops = &ibmphp_hotplug_slot_ops;
 
                        // end of registering ibm slot with hotplug core
 
-                       list_add(&((struct slot *)(hp_slot_ptr->private))->ibm_slot_list, &ibmphp_slot_head);
+                       list_add(&tmp_slot->ibm_slot_list, &ibmphp_slot_head);
                }
 
                print_bus_info();
@@ -944,7 +898,7 @@ static int __init ebda_rsrc_controller(void)
 
        list_for_each_entry(tmp_slot, &ibmphp_slot_head, ibm_slot_list) {
                snprintf(name, SLOT_NAME_SIZE, "%s", create_file_name(tmp_slot));
-               pci_hp_register(tmp_slot->hotplug_slot,
+               pci_hp_register(&tmp_slot->hotplug_slot,
                        pci_find_bus(0, tmp_slot->bus), tmp_slot->device, name);
        }
 
@@ -953,12 +907,8 @@ static int __init ebda_rsrc_controller(void)
        return 0;
 
 error:
-       kfree(hp_slot_ptr->private);
+       kfree(tmp_slot);
 error_no_slot:
-       kfree(hp_slot_ptr->info);
-error_no_hp_info:
-       kfree(hp_slot_ptr);
-error_no_hp_slot:
        free_ebda_hpc(hpc_ptr);
 error_no_hpc:
        iounmap(io_mem);
index 90fde5f106d833fb3ff063cb20bf48a29e0d903e..5ac31f683b85362c1bca32cbc01a9c77a51a4202 100644 (file)
@@ -49,15 +49,13 @@ static DEFINE_MUTEX(pci_hp_mutex);
 #define GET_STATUS(name, type) \
 static int get_##name(struct hotplug_slot *slot, type *value)          \
 {                                                                      \
-       struct hotplug_slot_ops *ops = slot->ops;                       \
+       const struct hotplug_slot_ops *ops = slot->ops;                 \
        int retval = 0;                                                 \
-       if (!try_module_get(ops->owner))                                \
+       if (!try_module_get(slot->owner))                               \
                return -ENODEV;                                         \
        if (ops->get_##name)                                            \
                retval = ops->get_##name(slot, value);                  \
-       else                                                            \
-               *value = slot->info->name;                              \
-       module_put(ops->owner);                                         \
+       module_put(slot->owner);                                        \
        return retval;                                                  \
 }
 
@@ -90,7 +88,7 @@ static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
        power = (u8)(lpower & 0xff);
        dbg("power = %d\n", power);
 
-       if (!try_module_get(slot->ops->owner)) {
+       if (!try_module_get(slot->owner)) {
                retval = -ENODEV;
                goto exit;
        }
@@ -109,7 +107,7 @@ static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
                err("Illegal value specified for power\n");
                retval = -EINVAL;
        }
-       module_put(slot->ops->owner);
+       module_put(slot->owner);
 
 exit:
        if (retval)
@@ -138,7 +136,8 @@ static ssize_t attention_read_file(struct pci_slot *pci_slot, char *buf)
 static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf,
                                    size_t count)
 {
-       struct hotplug_slot_ops *ops = pci_slot->hotplug->ops;
+       struct hotplug_slot *slot = pci_slot->hotplug;
+       const struct hotplug_slot_ops *ops = slot->ops;
        unsigned long lattention;
        u8 attention;
        int retval = 0;
@@ -147,13 +146,13 @@ static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf,
        attention = (u8)(lattention & 0xff);
        dbg(" - attention = %d\n", attention);
 
-       if (!try_module_get(ops->owner)) {
+       if (!try_module_get(slot->owner)) {
                retval = -ENODEV;
                goto exit;
        }
        if (ops->set_attention_status)
-               retval = ops->set_attention_status(pci_slot->hotplug, attention);
-       module_put(ops->owner);
+               retval = ops->set_attention_status(slot, attention);
+       module_put(slot->owner);
 
 exit:
        if (retval)
@@ -213,13 +212,13 @@ static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
        test = (u32)(ltest & 0xffffffff);
        dbg("test = %d\n", test);
 
-       if (!try_module_get(slot->ops->owner)) {
+       if (!try_module_get(slot->owner)) {
                retval = -ENODEV;
                goto exit;
        }
        if (slot->ops->hardware_test)
                retval = slot->ops->hardware_test(slot, test);
-       module_put(slot->ops->owner);
+       module_put(slot->owner);
 
 exit:
        if (retval)
@@ -444,11 +443,11 @@ int __pci_hp_initialize(struct hotplug_slot *slot, struct pci_bus *bus,
 
        if (slot == NULL)
                return -ENODEV;
-       if ((slot->info == NULL) || (slot->ops == NULL))
+       if (slot->ops == NULL)
                return -EINVAL;
 
-       slot->ops->owner = owner;
-       slot->ops->mod_name = mod_name;
+       slot->owner = owner;
+       slot->mod_name = mod_name;
 
        /*
         * No problems if we call this interface from both ACPI_PCI_SLOT
@@ -559,28 +558,6 @@ void pci_hp_destroy(struct hotplug_slot *slot)
 }
 EXPORT_SYMBOL_GPL(pci_hp_destroy);
 
-/**
- * pci_hp_change_slot_info - changes the slot's information structure in the core
- * @slot: pointer to the slot whose info has changed
- * @info: pointer to the info copy into the slot's info structure
- *
- * @slot must have been registered with the pci
- * hotplug subsystem previously with a call to pci_hp_register().
- *
- * Returns 0 if successful, anything else for an error.
- */
-int pci_hp_change_slot_info(struct hotplug_slot *slot,
-                           struct hotplug_slot_info *info)
-{
-       if (!slot || !info)
-               return -ENODEV;
-
-       memcpy(slot->info, info, sizeof(struct hotplug_slot_info));
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
-
 static int __init pci_hotplug_init(void)
 {
        int result;
index 811cf83f956de4db38856abdc79712a19494c1ef..506e1d923a1ffc74d27ce4e608bf6cc5744e89bc 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/pci.h>
 #include <linux/pci_hotplug.h>
 #include <linux/delay.h>
-#include <linux/sched/signal.h>                /* signal_pending() */
 #include <linux/mutex.h>
 #include <linux/rwsem.h>
 #include <linux/workqueue.h>
@@ -59,72 +58,64 @@ do {                                                                        \
 
 #define SLOT_NAME_SIZE 10
 
-/**
- * struct slot - PCIe hotplug slot
- * @state: current state machine position
- * @ctrl: pointer to the slot's controller structure
- * @hotplug_slot: pointer to the structure registered with the PCI hotplug core
- * @work: work item to turn the slot on or off after 5 seconds in response to
- *     an Attention Button press
- * @lock: protects reads and writes of @state;
- *     protects scheduling, execution and cancellation of @work
- */
-struct slot {
-       u8 state;
-       struct controller *ctrl;
-       struct hotplug_slot *hotplug_slot;
-       struct delayed_work work;
-       struct mutex lock;
-};
-
 /**
  * struct controller - PCIe hotplug controller
- * @ctrl_lock: serializes writes to the Slot Control register
  * @pcie: pointer to the controller's PCIe port service device
- * @reset_lock: prevents access to the Data Link Layer Link Active bit in the
- *     Link Status register and to the Presence Detect State bit in the Slot
- *     Status register during a slot reset which may cause them to flap
- * @slot: pointer to the controller's slot structure
- * @queue: wait queue to wake up on reception of a Command Completed event,
- *     used for synchronous writes to the Slot Control register
  * @slot_cap: cached copy of the Slot Capabilities register
  * @slot_ctrl: cached copy of the Slot Control register
- * @poll_thread: thread to poll for slot events if no IRQ is available,
- *     enabled with pciehp_poll_mode module parameter
+ * @ctrl_lock: serializes writes to the Slot Control register
  * @cmd_started: jiffies when the Slot Control register was last written;
  *     the next write is allowed 1 second later, absent a Command Completed
  *     interrupt (PCIe r4.0, sec 6.7.3.2)
  * @cmd_busy: flag set on Slot Control register write, cleared by IRQ handler
  *     on reception of a Command Completed event
- * @link_active_reporting: cached copy of Data Link Layer Link Active Reporting
- *     Capable bit in Link Capabilities register; if this bit is zero, the
- *     Data Link Layer Link Active bit in the Link Status register will never
- *     be set and the driver is thus confined to wait 1 second before assuming
- *     the link to a hotplugged device is up and accessing it
+ * @queue: wait queue to wake up on reception of a Command Completed event,
+ *     used for synchronous writes to the Slot Control register
+ * @pending_events: used by the IRQ handler to save events retrieved from the
+ *     Slot Status register for later consumption by the IRQ thread
  * @notification_enabled: whether the IRQ was requested successfully
  * @power_fault_detected: whether a power fault was detected by the hardware
  *     that has not yet been cleared by the user
- * @pending_events: used by the IRQ handler to save events retrieved from the
- *     Slot Status register for later consumption by the IRQ thread
+ * @poll_thread: thread to poll for slot events if no IRQ is available,
+ *     enabled with pciehp_poll_mode module parameter
+ * @state: current state machine position
+ * @state_lock: protects reads and writes of @state;
+ *     protects scheduling, execution and cancellation of @button_work
+ * @button_work: work item to turn the slot on or off after 5 seconds
+ *     in response to an Attention Button press
+ * @hotplug_slot: structure registered with the PCI hotplug core
+ * @reset_lock: prevents access to the Data Link Layer Link Active bit in the
+ *     Link Status register and to the Presence Detect State bit in the Slot
+ *     Status register during a slot reset which may cause them to flap
  * @request_result: result of last user request submitted to the IRQ thread
  * @requester: wait queue to wake up on completion of user request,
  *     used for synchronous slot enable/disable request via sysfs
+ *
+ * PCIe hotplug has a 1:1 relationship between controller and slot, hence
+ * unlike other drivers, the two aren't represented by separate structures.
  */
 struct controller {
-       struct mutex ctrl_lock;
        struct pcie_device *pcie;
-       struct rw_semaphore reset_lock;
-       struct slot *slot;
-       wait_queue_head_t queue;
-       u32 slot_cap;
-       u16 slot_ctrl;
-       struct task_struct *poll_thread;
-       unsigned long cmd_started;      /* jiffies */
+
+       u32 slot_cap;                           /* capabilities and quirks */
+
+       u16 slot_ctrl;                          /* control register access */
+       struct mutex ctrl_lock;
+       unsigned long cmd_started;
        unsigned int cmd_busy:1;
-       unsigned int link_active_reporting:1;
+       wait_queue_head_t queue;
+
+       atomic_t pending_events;                /* event handling */
        unsigned int notification_enabled:1;
        unsigned int power_fault_detected;
-       atomic_t pending_events;
+       struct task_struct *poll_thread;
+
+       u8 state;                               /* state machine */
+       struct mutex state_lock;
+       struct delayed_work button_work;
+
+       struct hotplug_slot hotplug_slot;       /* hotplug core interface */
+       struct rw_semaphore reset_lock;
        int request_result;
        wait_queue_head_t requester;
 };
@@ -174,42 +165,50 @@ struct controller {
 #define NO_CMD_CMPL(ctrl)      ((ctrl)->slot_cap & PCI_EXP_SLTCAP_NCCS)
 #define PSN(ctrl)              (((ctrl)->slot_cap & PCI_EXP_SLTCAP_PSN) >> 19)
 
-int pciehp_sysfs_enable_slot(struct slot *slot);
-int pciehp_sysfs_disable_slot(struct slot *slot);
 void pciehp_request(struct controller *ctrl, int action);
-void pciehp_handle_button_press(struct slot *slot);
-void pciehp_handle_disable_request(struct slot *slot);
-void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events);
-int pciehp_configure_device(struct slot *p_slot);
-void pciehp_unconfigure_device(struct slot *p_slot);
+void pciehp_handle_button_press(struct controller *ctrl);
+void pciehp_handle_disable_request(struct controller *ctrl);
+void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events);
+int pciehp_configure_device(struct controller *ctrl);
+void pciehp_unconfigure_device(struct controller *ctrl, bool presence);
 void pciehp_queue_pushbutton_work(struct work_struct *work);
 struct controller *pcie_init(struct pcie_device *dev);
 int pcie_init_notification(struct controller *ctrl);
 void pcie_shutdown_notification(struct controller *ctrl);
 void pcie_clear_hotplug_events(struct controller *ctrl);
-int pciehp_power_on_slot(struct slot *slot);
-void pciehp_power_off_slot(struct slot *slot);
-void pciehp_get_power_status(struct slot *slot, u8 *status);
-void pciehp_get_attention_status(struct slot *slot, u8 *status);
-
-void pciehp_set_attention_status(struct slot *slot, u8 status);
-void pciehp_get_latch_status(struct slot *slot, u8 *status);
-void pciehp_get_adapter_status(struct slot *slot, u8 *status);
-int pciehp_query_power_fault(struct slot *slot);
-void pciehp_green_led_on(struct slot *slot);
-void pciehp_green_led_off(struct slot *slot);
-void pciehp_green_led_blink(struct slot *slot);
+void pcie_enable_interrupt(struct controller *ctrl);
+void pcie_disable_interrupt(struct controller *ctrl);
+int pciehp_power_on_slot(struct controller *ctrl);
+void pciehp_power_off_slot(struct controller *ctrl);
+void pciehp_get_power_status(struct controller *ctrl, u8 *status);
+
+void pciehp_set_attention_status(struct controller *ctrl, u8 status);
+void pciehp_get_latch_status(struct controller *ctrl, u8 *status);
+int pciehp_query_power_fault(struct controller *ctrl);
+void pciehp_green_led_on(struct controller *ctrl);
+void pciehp_green_led_off(struct controller *ctrl);
+void pciehp_green_led_blink(struct controller *ctrl);
+bool pciehp_card_present(struct controller *ctrl);
+bool pciehp_card_present_or_link_active(struct controller *ctrl);
 int pciehp_check_link_status(struct controller *ctrl);
 bool pciehp_check_link_active(struct controller *ctrl);
 void pciehp_release_ctrl(struct controller *ctrl);
-int pciehp_reset_slot(struct slot *slot, int probe);
 
+int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot);
+int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot);
+int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, int probe);
+int pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status);
 int pciehp_set_raw_indicator_status(struct hotplug_slot *h_slot, u8 status);
 int pciehp_get_raw_indicator_status(struct hotplug_slot *h_slot, u8 *status);
 
-static inline const char *slot_name(struct slot *slot)
+static inline const char *slot_name(struct controller *ctrl)
+{
+       return hotplug_slot_name(&ctrl->hotplug_slot);
+}
+
+static inline struct controller *to_ctrl(struct hotplug_slot *hotplug_slot)
 {
-       return hotplug_slot_name(slot->hotplug_slot);
+       return container_of(hotplug_slot, struct controller, hotplug_slot);
 }
 
 #endif                         /* _PCIEHP_H */
index ec48c9433ae507cd5e61157adbc0c50551a9f1ee..fc5366b50e9565cc4ad0d9d193ab42d926d1dfef 100644 (file)
@@ -23,8 +23,6 @@
 #include <linux/types.h>
 #include <linux/pci.h>
 #include "pciehp.h"
-#include <linux/interrupt.h>
-#include <linux/time.h>
 
 #include "../pci.h"
 
@@ -47,45 +45,30 @@ MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
 #define PCIE_MODULE_NAME "pciehp"
 
 static int set_attention_status(struct hotplug_slot *slot, u8 value);
-static int enable_slot(struct hotplug_slot *slot);
-static int disable_slot(struct hotplug_slot *slot);
 static int get_power_status(struct hotplug_slot *slot, u8 *value);
-static int get_attention_status(struct hotplug_slot *slot, u8 *value);
 static int get_latch_status(struct hotplug_slot *slot, u8 *value);
 static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
-static int reset_slot(struct hotplug_slot *slot, int probe);
 
 static int init_slot(struct controller *ctrl)
 {
-       struct slot *slot = ctrl->slot;
-       struct hotplug_slot *hotplug = NULL;
-       struct hotplug_slot_info *info = NULL;
-       struct hotplug_slot_ops *ops = NULL;
+       struct hotplug_slot_ops *ops;
        char name[SLOT_NAME_SIZE];
-       int retval = -ENOMEM;
-
-       hotplug = kzalloc(sizeof(*hotplug), GFP_KERNEL);
-       if (!hotplug)
-               goto out;
-
-       info = kzalloc(sizeof(*info), GFP_KERNEL);
-       if (!info)
-               goto out;
+       int retval;
 
        /* Setup hotplug slot ops */
        ops = kzalloc(sizeof(*ops), GFP_KERNEL);
        if (!ops)
-               goto out;
+               return -ENOMEM;
 
-       ops->enable_slot = enable_slot;
-       ops->disable_slot = disable_slot;
+       ops->enable_slot = pciehp_sysfs_enable_slot;
+       ops->disable_slot = pciehp_sysfs_disable_slot;
        ops->get_power_status = get_power_status;
        ops->get_adapter_status = get_adapter_status;
-       ops->reset_slot = reset_slot;
+       ops->reset_slot = pciehp_reset_slot;
        if (MRL_SENS(ctrl))
                ops->get_latch_status = get_latch_status;
        if (ATTN_LED(ctrl)) {
-               ops->get_attention_status = get_attention_status;
+               ops->get_attention_status = pciehp_get_attention_status;
                ops->set_attention_status = set_attention_status;
        } else if (ctrl->pcie->port->hotplug_user_indicators) {
                ops->get_attention_status = pciehp_get_raw_indicator_status;
@@ -93,33 +76,24 @@ static int init_slot(struct controller *ctrl)
        }
 
        /* register this slot with the hotplug pci core */
-       hotplug->info = info;
-       hotplug->private = slot;
-       hotplug->ops = ops;
-       slot->hotplug_slot = hotplug;
+       ctrl->hotplug_slot.ops = ops;
        snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl));
 
-       retval = pci_hp_initialize(hotplug,
+       retval = pci_hp_initialize(&ctrl->hotplug_slot,
                                   ctrl->pcie->port->subordinate, 0, name);
-       if (retval)
-               ctrl_err(ctrl, "pci_hp_initialize failed: error %d\n", retval);
-out:
        if (retval) {
+               ctrl_err(ctrl, "pci_hp_initialize failed: error %d\n", retval);
                kfree(ops);
-               kfree(info);
-               kfree(hotplug);
        }
        return retval;
 }
 
 static void cleanup_slot(struct controller *ctrl)
 {
-       struct hotplug_slot *hotplug_slot = ctrl->slot->hotplug_slot;
+       struct hotplug_slot *hotplug_slot = &ctrl->hotplug_slot;
 
        pci_hp_destroy(hotplug_slot);
        kfree(hotplug_slot->ops);
-       kfree(hotplug_slot->info);
-       kfree(hotplug_slot);
 }
 
 /*
@@ -127,79 +101,48 @@ static void cleanup_slot(struct controller *ctrl)
  */
 static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
 {
-       struct slot *slot = hotplug_slot->private;
-       struct pci_dev *pdev = slot->ctrl->pcie->port;
+       struct controller *ctrl = to_ctrl(hotplug_slot);
+       struct pci_dev *pdev = ctrl->pcie->port;
 
        pci_config_pm_runtime_get(pdev);
-       pciehp_set_attention_status(slot, status);
+       pciehp_set_attention_status(ctrl, status);
        pci_config_pm_runtime_put(pdev);
        return 0;
 }
 
-
-static int enable_slot(struct hotplug_slot *hotplug_slot)
-{
-       struct slot *slot = hotplug_slot->private;
-
-       return pciehp_sysfs_enable_slot(slot);
-}
-
-
-static int disable_slot(struct hotplug_slot *hotplug_slot)
-{
-       struct slot *slot = hotplug_slot->private;
-
-       return pciehp_sysfs_disable_slot(slot);
-}
-
 static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
-       struct slot *slot = hotplug_slot->private;
-       struct pci_dev *pdev = slot->ctrl->pcie->port;
+       struct controller *ctrl = to_ctrl(hotplug_slot);
+       struct pci_dev *pdev = ctrl->pcie->port;
 
        pci_config_pm_runtime_get(pdev);
-       pciehp_get_power_status(slot, value);
+       pciehp_get_power_status(ctrl, value);
        pci_config_pm_runtime_put(pdev);
        return 0;
 }
 
-static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
-{
-       struct slot *slot = hotplug_slot->private;
-
-       pciehp_get_attention_status(slot, value);
-       return 0;
-}
-
 static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
-       struct slot *slot = hotplug_slot->private;
-       struct pci_dev *pdev = slot->ctrl->pcie->port;
+       struct controller *ctrl = to_ctrl(hotplug_slot);
+       struct pci_dev *pdev = ctrl->pcie->port;
 
        pci_config_pm_runtime_get(pdev);
-       pciehp_get_latch_status(slot, value);
+       pciehp_get_latch_status(ctrl, value);
        pci_config_pm_runtime_put(pdev);
        return 0;
 }
 
 static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
-       struct slot *slot = hotplug_slot->private;
-       struct pci_dev *pdev = slot->ctrl->pcie->port;
+       struct controller *ctrl = to_ctrl(hotplug_slot);
+       struct pci_dev *pdev = ctrl->pcie->port;
 
        pci_config_pm_runtime_get(pdev);
-       pciehp_get_adapter_status(slot, value);
+       *value = pciehp_card_present_or_link_active(ctrl);
        pci_config_pm_runtime_put(pdev);
        return 0;
 }
 
-static int reset_slot(struct hotplug_slot *hotplug_slot, int probe)
-{
-       struct slot *slot = hotplug_slot->private;
-
-       return pciehp_reset_slot(slot, probe);
-}
-
 /**
  * pciehp_check_presence() - synthesize event if presence has changed
  *
@@ -212,20 +155,19 @@ static int reset_slot(struct hotplug_slot *hotplug_slot, int probe)
  */
 static void pciehp_check_presence(struct controller *ctrl)
 {
-       struct slot *slot = ctrl->slot;
-       u8 occupied;
+       bool occupied;
 
        down_read(&ctrl->reset_lock);
-       mutex_lock(&slot->lock);
+       mutex_lock(&ctrl->state_lock);
 
-       pciehp_get_adapter_status(slot, &occupied);
-       if ((occupied && (slot->state == OFF_STATE ||
-                         slot->state == BLINKINGON_STATE)) ||
-           (!occupied && (slot->state == ON_STATE ||
-                          slot->state == BLINKINGOFF_STATE)))
+       occupied = pciehp_card_present_or_link_active(ctrl);
+       if ((occupied && (ctrl->state == OFF_STATE ||
+                         ctrl->state == BLINKINGON_STATE)) ||
+           (!occupied && (ctrl->state == ON_STATE ||
+                          ctrl->state == BLINKINGOFF_STATE)))
                pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
 
-       mutex_unlock(&slot->lock);
+       mutex_unlock(&ctrl->state_lock);
        up_read(&ctrl->reset_lock);
 }
 
@@ -233,7 +175,6 @@ static int pciehp_probe(struct pcie_device *dev)
 {
        int rc;
        struct controller *ctrl;
-       struct slot *slot;
 
        /* If this is not a "hotplug" service, we have no business here. */
        if (dev->service != PCIE_PORT_SERVICE_HP)
@@ -271,8 +212,7 @@ static int pciehp_probe(struct pcie_device *dev)
        }
 
        /* Publish to user space */
-       slot = ctrl->slot;
-       rc = pci_hp_add(slot->hotplug_slot);
+       rc = pci_hp_add(&ctrl->hotplug_slot);
        if (rc) {
                ctrl_err(ctrl, "Publication to user space failed (%d)\n", rc);
                goto err_out_shutdown_notification;
@@ -295,29 +235,43 @@ static void pciehp_remove(struct pcie_device *dev)
 {
        struct controller *ctrl = get_service_data(dev);
 
-       pci_hp_del(ctrl->slot->hotplug_slot);
+       pci_hp_del(&ctrl->hotplug_slot);
        pcie_shutdown_notification(ctrl);
        cleanup_slot(ctrl);
        pciehp_release_ctrl(ctrl);
 }
 
 #ifdef CONFIG_PM
+static bool pme_is_native(struct pcie_device *dev)
+{
+       const struct pci_host_bridge *host;
+
+       host = pci_find_host_bridge(dev->port->bus);
+       return pcie_ports_native || host->native_pme;
+}
+
 static int pciehp_suspend(struct pcie_device *dev)
 {
+       /*
+        * Disable hotplug interrupt so that it does not trigger
+        * immediately when the downstream link goes down.
+        */
+       if (pme_is_native(dev))
+               pcie_disable_interrupt(get_service_data(dev));
+
        return 0;
 }
 
 static int pciehp_resume_noirq(struct pcie_device *dev)
 {
        struct controller *ctrl = get_service_data(dev);
-       struct slot *slot = ctrl->slot;
 
        /* pci_restore_state() just wrote to the Slot Control register */
        ctrl->cmd_started = jiffies;
        ctrl->cmd_busy = true;
 
        /* clear spurious events from rediscovery of inserted card */
-       if (slot->state == ON_STATE || slot->state == BLINKINGOFF_STATE)
+       if (ctrl->state == ON_STATE || ctrl->state == BLINKINGOFF_STATE)
                pcie_clear_hotplug_events(ctrl);
 
        return 0;
@@ -327,10 +281,29 @@ static int pciehp_resume(struct pcie_device *dev)
 {
        struct controller *ctrl = get_service_data(dev);
 
+       if (pme_is_native(dev))
+               pcie_enable_interrupt(ctrl);
+
        pciehp_check_presence(ctrl);
 
        return 0;
 }
+
+static int pciehp_runtime_resume(struct pcie_device *dev)
+{
+       struct controller *ctrl = get_service_data(dev);
+
+       /* pci_restore_state() just wrote to the Slot Control register */
+       ctrl->cmd_started = jiffies;
+       ctrl->cmd_busy = true;
+
+       /* clear spurious events from rediscovery of inserted card */
+       if ((ctrl->state == ON_STATE || ctrl->state == BLINKINGOFF_STATE) &&
+            pme_is_native(dev))
+               pcie_clear_hotplug_events(ctrl);
+
+       return pciehp_resume(dev);
+}
 #endif /* PM */
 
 static struct pcie_port_service_driver hpdriver_portdrv = {
@@ -345,10 +318,12 @@ static struct pcie_port_service_driver hpdriver_portdrv = {
        .suspend        = pciehp_suspend,
        .resume_noirq   = pciehp_resume_noirq,
        .resume         = pciehp_resume,
+       .runtime_suspend = pciehp_suspend,
+       .runtime_resume = pciehp_runtime_resume,
 #endif /* PM */
 };
 
-static int __init pcied_init(void)
+int __init pcie_hp_init(void)
 {
        int retval = 0;
 
@@ -359,4 +334,3 @@ static int __init pcied_init(void)
 
        return retval;
 }
-device_initcall(pcied_init);
index da7c72372ffcfc15bc706062baa1038c8681a318..3f3df4c29f6e1d40112343e91f902735a6b4d535 100644 (file)
  *
  */
 
-#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
-#include <linux/slab.h>
 #include <linux/pm_runtime.h>
 #include <linux/pci.h>
-#include "../pci.h"
 #include "pciehp.h"
 
 /* The following routines constitute the bulk of the
    hotplug controller logic
  */
 
-static void set_slot_off(struct controller *ctrl, struct slot *pslot)
+#define SAFE_REMOVAL    true
+#define SURPRISE_REMOVAL false
+
+static void set_slot_off(struct controller *ctrl)
 {
        /* turn off slot, turn on Amber LED, turn off Green LED if supported*/
        if (POWER_CTRL(ctrl)) {
-               pciehp_power_off_slot(pslot);
+               pciehp_power_off_slot(ctrl);
 
                /*
                 * After turning power off, we must wait for at least 1 second
@@ -40,31 +40,30 @@ static void set_slot_off(struct controller *ctrl, struct slot *pslot)
                msleep(1000);
        }
 
-       pciehp_green_led_off(pslot);
-       pciehp_set_attention_status(pslot, 1);
+       pciehp_green_led_off(ctrl);
+       pciehp_set_attention_status(ctrl, 1);
 }
 
 /**
  * board_added - Called after a board has been added to the system.
- * @p_slot: &slot where board is added
+ * @ctrl: PCIe hotplug controller where board is added
  *
  * Turns power on for the board.
  * Configures board.
  */
-static int board_added(struct slot *p_slot)
+static int board_added(struct controller *ctrl)
 {
        int retval = 0;
-       struct controller *ctrl = p_slot->ctrl;
        struct pci_bus *parent = ctrl->pcie->port->subordinate;
 
        if (POWER_CTRL(ctrl)) {
                /* Power on slot */
-               retval = pciehp_power_on_slot(p_slot);
+               retval = pciehp_power_on_slot(ctrl);
                if (retval)
                        return retval;
        }
 
-       pciehp_green_led_blink(p_slot);
+       pciehp_green_led_blink(ctrl);
 
        /* Check link training status */
        retval = pciehp_check_link_status(ctrl);
@@ -74,13 +73,13 @@ static int board_added(struct slot *p_slot)
        }
 
        /* Check for a power fault */
-       if (ctrl->power_fault_detected || pciehp_query_power_fault(p_slot)) {
-               ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(p_slot));
+       if (ctrl->power_fault_detected || pciehp_query_power_fault(ctrl)) {
+               ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl));
                retval = -EIO;
                goto err_exit;
        }
 
-       retval = pciehp_configure_device(p_slot);
+       retval = pciehp_configure_device(ctrl);
        if (retval) {
                if (retval != -EEXIST) {
                        ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n",
@@ -89,27 +88,26 @@ static int board_added(struct slot *p_slot)
                }
        }
 
-       pciehp_green_led_on(p_slot);
-       pciehp_set_attention_status(p_slot, 0);
+       pciehp_green_led_on(ctrl);
+       pciehp_set_attention_status(ctrl, 0);
        return 0;
 
 err_exit:
-       set_slot_off(ctrl, p_slot);
+       set_slot_off(ctrl);
        return retval;
 }
 
 /**
  * remove_board - Turns off slot and LEDs
- * @p_slot: slot where board is being removed
+ * @ctrl: PCIe hotplug controller where board is being removed
+ * @safe_removal: whether the board is safely removed (versus surprise removed)
  */
-static void remove_board(struct slot *p_slot)
+static void remove_board(struct controller *ctrl, bool safe_removal)
 {
-       struct controller *ctrl = p_slot->ctrl;
-
-       pciehp_unconfigure_device(p_slot);
+       pciehp_unconfigure_device(ctrl, safe_removal);
 
        if (POWER_CTRL(ctrl)) {
-               pciehp_power_off_slot(p_slot);
+               pciehp_power_off_slot(ctrl);
 
                /*
                 * After turning power off, we must wait for at least 1 second
@@ -120,11 +118,11 @@ static void remove_board(struct slot *p_slot)
        }
 
        /* turn off Green LED */
-       pciehp_green_led_off(p_slot);
+       pciehp_green_led_off(ctrl);
 }
 
-static int pciehp_enable_slot(struct slot *slot);
-static int pciehp_disable_slot(struct slot *slot);
+static int pciehp_enable_slot(struct controller *ctrl);
+static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal);
 
 void pciehp_request(struct controller *ctrl, int action)
 {
@@ -135,11 +133,11 @@ void pciehp_request(struct controller *ctrl, int action)
 
 void pciehp_queue_pushbutton_work(struct work_struct *work)
 {
-       struct slot *p_slot = container_of(work, struct slot, work.work);
-       struct controller *ctrl = p_slot->ctrl;
+       struct controller *ctrl = container_of(work, struct controller,
+                                              button_work.work);
 
-       mutex_lock(&p_slot->lock);
-       switch (p_slot->state) {
+       mutex_lock(&ctrl->state_lock);
+       switch (ctrl->state) {
        case BLINKINGOFF_STATE:
                pciehp_request(ctrl, DISABLE_SLOT);
                break;
@@ -149,30 +147,28 @@ void pciehp_queue_pushbutton_work(struct work_struct *work)
        default:
                break;
        }
-       mutex_unlock(&p_slot->lock);
+       mutex_unlock(&ctrl->state_lock);
 }
 
-void pciehp_handle_button_press(struct slot *p_slot)
+void pciehp_handle_button_press(struct controller *ctrl)
 {
-       struct controller *ctrl = p_slot->ctrl;
-
-       mutex_lock(&p_slot->lock);
-       switch (p_slot->state) {
+       mutex_lock(&ctrl->state_lock);
+       switch (ctrl->state) {
        case OFF_STATE:
        case ON_STATE:
-               if (p_slot->state == ON_STATE) {
-                       p_slot->state = BLINKINGOFF_STATE;
+               if (ctrl->state == ON_STATE) {
+                       ctrl->state = BLINKINGOFF_STATE;
                        ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n",
-                                 slot_name(p_slot));
+                                 slot_name(ctrl));
                } else {
-                       p_slot->state = BLINKINGON_STATE;
+                       ctrl->state = BLINKINGON_STATE;
                        ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n",
-                                 slot_name(p_slot));
+                                 slot_name(ctrl));
                }
                /* blink green LED and turn off amber */
-               pciehp_green_led_blink(p_slot);
-               pciehp_set_attention_status(p_slot, 0);
-               schedule_delayed_work(&p_slot->work, 5 * HZ);
+               pciehp_green_led_blink(ctrl);
+               pciehp_set_attention_status(ctrl, 0);
+               schedule_delayed_work(&ctrl->button_work, 5 * HZ);
                break;
        case BLINKINGOFF_STATE:
        case BLINKINGON_STATE:
@@ -181,197 +177,184 @@ void pciehp_handle_button_press(struct slot *p_slot)
                 * press the attention again before the 5 sec. limit
                 * expires to cancel hot-add or hot-remove
                 */
-               ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(p_slot));
-               cancel_delayed_work(&p_slot->work);
-               if (p_slot->state == BLINKINGOFF_STATE) {
-                       p_slot->state = ON_STATE;
-                       pciehp_green_led_on(p_slot);
+               ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(ctrl));
+               cancel_delayed_work(&ctrl->button_work);
+               if (ctrl->state == BLINKINGOFF_STATE) {
+                       ctrl->state = ON_STATE;
+                       pciehp_green_led_on(ctrl);
                } else {
-                       p_slot->state = OFF_STATE;
-                       pciehp_green_led_off(p_slot);
+                       ctrl->state = OFF_STATE;
+                       pciehp_green_led_off(ctrl);
                }
-               pciehp_set_attention_status(p_slot, 0);
+               pciehp_set_attention_status(ctrl, 0);
                ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n",
-                         slot_name(p_slot));
+                         slot_name(ctrl));
                break;
        default:
                ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n",
-                        slot_name(p_slot), p_slot->state);
+                        slot_name(ctrl), ctrl->state);
                break;
        }
-       mutex_unlock(&p_slot->lock);
+       mutex_unlock(&ctrl->state_lock);
 }
 
-void pciehp_handle_disable_request(struct slot *slot)
+void pciehp_handle_disable_request(struct controller *ctrl)
 {
-       struct controller *ctrl = slot->ctrl;
-
-       mutex_lock(&slot->lock);
-       switch (slot->state) {
+       mutex_lock(&ctrl->state_lock);
+       switch (ctrl->state) {
        case BLINKINGON_STATE:
        case BLINKINGOFF_STATE:
-               cancel_delayed_work(&slot->work);
+               cancel_delayed_work(&ctrl->button_work);
                break;
        }
-       slot->state = POWEROFF_STATE;
-       mutex_unlock(&slot->lock);
+       ctrl->state = POWEROFF_STATE;
+       mutex_unlock(&ctrl->state_lock);
 
-       ctrl->request_result = pciehp_disable_slot(slot);
+       ctrl->request_result = pciehp_disable_slot(ctrl, SAFE_REMOVAL);
 }
 
-void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events)
+void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events)
 {
-       struct controller *ctrl = slot->ctrl;
-       bool link_active;
-       u8 present;
+       bool present, link_active;
 
        /*
         * If the slot is on and presence or link has changed, turn it off.
         * Even if it's occupied again, we cannot assume the card is the same.
         */
-       mutex_lock(&slot->lock);
-       switch (slot->state) {
+       mutex_lock(&ctrl->state_lock);
+       switch (ctrl->state) {
        case BLINKINGOFF_STATE:
-               cancel_delayed_work(&slot->work);
+               cancel_delayed_work(&ctrl->button_work);
                /* fall through */
        case ON_STATE:
-               slot->state = POWEROFF_STATE;
-               mutex_unlock(&slot->lock);
+               ctrl->state = POWEROFF_STATE;
+               mutex_unlock(&ctrl->state_lock);
                if (events & PCI_EXP_SLTSTA_DLLSC)
                        ctrl_info(ctrl, "Slot(%s): Link Down\n",
-                                 slot_name(slot));
+                                 slot_name(ctrl));
                if (events & PCI_EXP_SLTSTA_PDC)
                        ctrl_info(ctrl, "Slot(%s): Card not present\n",
-                                 slot_name(slot));
-               pciehp_disable_slot(slot);
+                                 slot_name(ctrl));
+               pciehp_disable_slot(ctrl, SURPRISE_REMOVAL);
                break;
        default:
-               mutex_unlock(&slot->lock);
+               mutex_unlock(&ctrl->state_lock);
                break;
        }
 
        /* Turn the slot on if it's occupied or link is up */
-       mutex_lock(&slot->lock);
-       pciehp_get_adapter_status(slot, &present);
+       mutex_lock(&ctrl->state_lock);
+       present = pciehp_card_present(ctrl);
        link_active = pciehp_check_link_active(ctrl);
        if (!present && !link_active) {
-               mutex_unlock(&slot->lock);
+               mutex_unlock(&ctrl->state_lock);
                return;
        }
 
-       switch (slot->state) {
+       switch (ctrl->state) {
        case BLINKINGON_STATE:
-               cancel_delayed_work(&slot->work);
+               cancel_delayed_work(&ctrl->button_work);
                /* fall through */
        case OFF_STATE:
-               slot->state = POWERON_STATE;
-               mutex_unlock(&slot->lock);
+               ctrl->state = POWERON_STATE;
+               mutex_unlock(&ctrl->state_lock);
                if (present)
                        ctrl_info(ctrl, "Slot(%s): Card present\n",
-                                 slot_name(slot));
+                                 slot_name(ctrl));
                if (link_active)
                        ctrl_info(ctrl, "Slot(%s): Link Up\n",
-                                 slot_name(slot));
-               ctrl->request_result = pciehp_enable_slot(slot);
+                                 slot_name(ctrl));
+               ctrl->request_result = pciehp_enable_slot(ctrl);
                break;
        default:
-               mutex_unlock(&slot->lock);
+               mutex_unlock(&ctrl->state_lock);
                break;
        }
 }
 
-static int __pciehp_enable_slot(struct slot *p_slot)
+static int __pciehp_enable_slot(struct controller *ctrl)
 {
        u8 getstatus = 0;
-       struct controller *ctrl = p_slot->ctrl;
 
-       pciehp_get_adapter_status(p_slot, &getstatus);
-       if (!getstatus) {
-               ctrl_info(ctrl, "Slot(%s): No adapter\n", slot_name(p_slot));
-               return -ENODEV;
-       }
-       if (MRL_SENS(p_slot->ctrl)) {
-               pciehp_get_latch_status(p_slot, &getstatus);
+       if (MRL_SENS(ctrl)) {
+               pciehp_get_latch_status(ctrl, &getstatus);
                if (getstatus) {
                        ctrl_info(ctrl, "Slot(%s): Latch open\n",
-                                 slot_name(p_slot));
+                                 slot_name(ctrl));
                        return -ENODEV;
                }
        }
 
-       if (POWER_CTRL(p_slot->ctrl)) {
-               pciehp_get_power_status(p_slot, &getstatus);
+       if (POWER_CTRL(ctrl)) {
+               pciehp_get_power_status(ctrl, &getstatus);
                if (getstatus) {
                        ctrl_info(ctrl, "Slot(%s): Already enabled\n",
-                                 slot_name(p_slot));
+                                 slot_name(ctrl));
                        return 0;
                }
        }
 
-       return board_added(p_slot);
+       return board_added(ctrl);
 }
 
-static int pciehp_enable_slot(struct slot *slot)
+static int pciehp_enable_slot(struct controller *ctrl)
 {
-       struct controller *ctrl = slot->ctrl;
        int ret;
 
        pm_runtime_get_sync(&ctrl->pcie->port->dev);
-       ret = __pciehp_enable_slot(slot);
+       ret = __pciehp_enable_slot(ctrl);
        if (ret && ATTN_BUTTN(ctrl))
-               pciehp_green_led_off(slot); /* may be blinking */
+               pciehp_green_led_off(ctrl); /* may be blinking */
        pm_runtime_put(&ctrl->pcie->port->dev);
 
-       mutex_lock(&slot->lock);
-       slot->state = ret ? OFF_STATE : ON_STATE;
-       mutex_unlock(&slot->lock);
+       mutex_lock(&ctrl->state_lock);
+       ctrl->state = ret ? OFF_STATE : ON_STATE;
+       mutex_unlock(&ctrl->state_lock);
 
        return ret;
 }
 
-static int __pciehp_disable_slot(struct slot *p_slot)
+static int __pciehp_disable_slot(struct controller *ctrl, bool safe_removal)
 {
        u8 getstatus = 0;
-       struct controller *ctrl = p_slot->ctrl;
 
-       if (POWER_CTRL(p_slot->ctrl)) {
-               pciehp_get_power_status(p_slot, &getstatus);
+       if (POWER_CTRL(ctrl)) {
+               pciehp_get_power_status(ctrl, &getstatus);
                if (!getstatus) {
                        ctrl_info(ctrl, "Slot(%s): Already disabled\n",
-                                 slot_name(p_slot));
+                                 slot_name(ctrl));
                        return -EINVAL;
                }
        }
 
-       remove_board(p_slot);
+       remove_board(ctrl, safe_removal);
        return 0;
 }
 
-static int pciehp_disable_slot(struct slot *slot)
+static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal)
 {
-       struct controller *ctrl = slot->ctrl;
        int ret;
 
        pm_runtime_get_sync(&ctrl->pcie->port->dev);
-       ret = __pciehp_disable_slot(slot);
+       ret = __pciehp_disable_slot(ctrl, safe_removal);
        pm_runtime_put(&ctrl->pcie->port->dev);
 
-       mutex_lock(&slot->lock);
-       slot->state = OFF_STATE;
-       mutex_unlock(&slot->lock);
+       mutex_lock(&ctrl->state_lock);
+       ctrl->state = OFF_STATE;
+       mutex_unlock(&ctrl->state_lock);
 
        return ret;
 }
 
-int pciehp_sysfs_enable_slot(struct slot *p_slot)
+int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot)
 {
-       struct controller *ctrl = p_slot->ctrl;
+       struct controller *ctrl = to_ctrl(hotplug_slot);
 
-       mutex_lock(&p_slot->lock);
-       switch (p_slot->state) {
+       mutex_lock(&ctrl->state_lock);
+       switch (ctrl->state) {
        case BLINKINGON_STATE:
        case OFF_STATE:
-               mutex_unlock(&p_slot->lock);
+               mutex_unlock(&ctrl->state_lock);
                /*
                 * The IRQ thread becomes a no-op if the user pulls out the
                 * card before the thread wakes up, so initialize to -ENODEV.
@@ -383,53 +366,53 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot)
                return ctrl->request_result;
        case POWERON_STATE:
                ctrl_info(ctrl, "Slot(%s): Already in powering on state\n",
-                         slot_name(p_slot));
+                         slot_name(ctrl));
                break;
        case BLINKINGOFF_STATE:
        case ON_STATE:
        case POWEROFF_STATE:
                ctrl_info(ctrl, "Slot(%s): Already enabled\n",
-                         slot_name(p_slot));
+                         slot_name(ctrl));
                break;
        default:
                ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
-                        slot_name(p_slot), p_slot->state);
+                        slot_name(ctrl), ctrl->state);
                break;
        }
-       mutex_unlock(&p_slot->lock);
+       mutex_unlock(&ctrl->state_lock);
 
        return -ENODEV;
 }
 
-int pciehp_sysfs_disable_slot(struct slot *p_slot)
+int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot)
 {
-       struct controller *ctrl = p_slot->ctrl;
+       struct controller *ctrl = to_ctrl(hotplug_slot);
 
-       mutex_lock(&p_slot->lock);
-       switch (p_slot->state) {
+       mutex_lock(&ctrl->state_lock);
+       switch (ctrl->state) {
        case BLINKINGOFF_STATE:
        case ON_STATE:
-               mutex_unlock(&p_slot->lock);
+               mutex_unlock(&ctrl->state_lock);
                pciehp_request(ctrl, DISABLE_SLOT);
                wait_event(ctrl->requester,
                           !atomic_read(&ctrl->pending_events));
                return ctrl->request_result;
        case POWEROFF_STATE:
                ctrl_info(ctrl, "Slot(%s): Already in powering off state\n",
-                         slot_name(p_slot));
+                         slot_name(ctrl));
                break;
        case BLINKINGON_STATE:
        case OFF_STATE:
        case POWERON_STATE:
                ctrl_info(ctrl, "Slot(%s): Already disabled\n",
-                         slot_name(p_slot));
+                         slot_name(ctrl));
                break;
        default:
                ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
-                        slot_name(p_slot), p_slot->state);
+                        slot_name(ctrl), ctrl->state);
                break;
        }
-       mutex_unlock(&p_slot->lock);
+       mutex_unlock(&ctrl->state_lock);
 
        return -ENODEV;
 }
index a938abdb41ceeb575dc62af740d78d278104280c..7dd443aea5a541f737d02af41a9fc39b2be450a9 100644 (file)
  */
 
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/types.h>
-#include <linux/signal.h>
 #include <linux/jiffies.h>
 #include <linux/kthread.h>
 #include <linux/pci.h>
 #include <linux/pm_runtime.h>
 #include <linux/interrupt.h>
-#include <linux/time.h>
 #include <linux/slab.h>
 
 #include "../pci.h"
@@ -43,7 +40,7 @@ static inline int pciehp_request_irq(struct controller *ctrl)
        if (pciehp_poll_mode) {
                ctrl->poll_thread = kthread_run(&pciehp_poll, ctrl,
                                                "pciehp_poll-%s",
-                                               slot_name(ctrl->slot));
+                                               slot_name(ctrl));
                return PTR_ERR_OR_ZERO(ctrl->poll_thread);
        }
 
@@ -217,13 +214,6 @@ bool pciehp_check_link_active(struct controller *ctrl)
        return ret;
 }
 
-static void pcie_wait_link_active(struct controller *ctrl)
-{
-       struct pci_dev *pdev = ctrl_dev(ctrl);
-
-       pcie_wait_for_link(pdev, true);
-}
-
 static bool pci_bus_check_dev(struct pci_bus *bus, int devfn)
 {
        u32 l;
@@ -256,18 +246,9 @@ int pciehp_check_link_status(struct controller *ctrl)
        bool found;
        u16 lnk_status;
 
-       /*
-        * Data Link Layer Link Active Reporting must be capable for
-        * hot-plug capable downstream port. But old controller might
-        * not implement it. In this case, we wait for 1000 ms.
-       */
-       if (ctrl->link_active_reporting)
-               pcie_wait_link_active(ctrl);
-       else
-               msleep(1000);
+       if (!pcie_wait_for_link(pdev, true))
+               return -1;
 
-       /* wait 100ms before read pci conf, and try in 1s */
-       msleep(100);
        found = pci_bus_check_dev(ctrl->pcie->port->subordinate,
                                        PCI_DEVFN(0, 0));
 
@@ -318,8 +299,8 @@ static int pciehp_link_enable(struct controller *ctrl)
 int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot,
                                    u8 *status)
 {
-       struct slot *slot = hotplug_slot->private;
-       struct pci_dev *pdev = ctrl_dev(slot->ctrl);
+       struct controller *ctrl = to_ctrl(hotplug_slot);
+       struct pci_dev *pdev = ctrl_dev(ctrl);
        u16 slot_ctrl;
 
        pci_config_pm_runtime_get(pdev);
@@ -329,9 +310,9 @@ int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot,
        return 0;
 }
 
-void pciehp_get_attention_status(struct slot *slot, u8 *status)
+int pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status)
 {
-       struct controller *ctrl = slot->ctrl;
+       struct controller *ctrl = to_ctrl(hotplug_slot);
        struct pci_dev *pdev = ctrl_dev(ctrl);
        u16 slot_ctrl;
 
@@ -355,11 +336,12 @@ void pciehp_get_attention_status(struct slot *slot, u8 *status)
                *status = 0xFF;
                break;
        }
+
+       return 0;
 }
 
-void pciehp_get_power_status(struct slot *slot, u8 *status)
+void pciehp_get_power_status(struct controller *ctrl, u8 *status)
 {
-       struct controller *ctrl = slot->ctrl;
        struct pci_dev *pdev = ctrl_dev(ctrl);
        u16 slot_ctrl;
 
@@ -380,27 +362,41 @@ void pciehp_get_power_status(struct slot *slot, u8 *status)
        }
 }
 
-void pciehp_get_latch_status(struct slot *slot, u8 *status)
+void pciehp_get_latch_status(struct controller *ctrl, u8 *status)
 {
-       struct pci_dev *pdev = ctrl_dev(slot->ctrl);
+       struct pci_dev *pdev = ctrl_dev(ctrl);
        u16 slot_status;
 
        pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
        *status = !!(slot_status & PCI_EXP_SLTSTA_MRLSS);
 }
 
-void pciehp_get_adapter_status(struct slot *slot, u8 *status)
+bool pciehp_card_present(struct controller *ctrl)
 {
-       struct pci_dev *pdev = ctrl_dev(slot->ctrl);
+       struct pci_dev *pdev = ctrl_dev(ctrl);
        u16 slot_status;
 
        pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
-       *status = !!(slot_status & PCI_EXP_SLTSTA_PDS);
+       return slot_status & PCI_EXP_SLTSTA_PDS;
 }
 
-int pciehp_query_power_fault(struct slot *slot)
+/**
+ * pciehp_card_present_or_link_active() - whether given slot is occupied
+ * @ctrl: PCIe hotplug controller
+ *
+ * Unlike pciehp_card_present(), which determines presence solely from the
+ * Presence Detect State bit, this helper also returns true if the Link Active
+ * bit is set.  This is a concession to broken hotplug ports which hardwire
+ * Presence Detect State to zero, such as Wilocity's [1ae9:0200].
+ */
+bool pciehp_card_present_or_link_active(struct controller *ctrl)
+{
+       return pciehp_card_present(ctrl) || pciehp_check_link_active(ctrl);
+}
+
+int pciehp_query_power_fault(struct controller *ctrl)
 {
-       struct pci_dev *pdev = ctrl_dev(slot->ctrl);
+       struct pci_dev *pdev = ctrl_dev(ctrl);
        u16 slot_status;
 
        pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
@@ -410,8 +406,7 @@ int pciehp_query_power_fault(struct slot *slot)
 int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot,
                                    u8 status)
 {
-       struct slot *slot = hotplug_slot->private;
-       struct controller *ctrl = slot->ctrl;
+       struct controller *ctrl = to_ctrl(hotplug_slot);
        struct pci_dev *pdev = ctrl_dev(ctrl);
 
        pci_config_pm_runtime_get(pdev);
@@ -421,9 +416,8 @@ int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot,
        return 0;
 }
 
-void pciehp_set_attention_status(struct slot *slot, u8 value)
+void pciehp_set_attention_status(struct controller *ctrl, u8 value)
 {
-       struct controller *ctrl = slot->ctrl;
        u16 slot_cmd;
 
        if (!ATTN_LED(ctrl))
@@ -447,10 +441,8 @@ void pciehp_set_attention_status(struct slot *slot, u8 value)
                 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd);
 }
 
-void pciehp_green_led_on(struct slot *slot)
+void pciehp_green_led_on(struct controller *ctrl)
 {
-       struct controller *ctrl = slot->ctrl;
-
        if (!PWR_LED(ctrl))
                return;
 
@@ -461,10 +453,8 @@ void pciehp_green_led_on(struct slot *slot)
                 PCI_EXP_SLTCTL_PWR_IND_ON);
 }
 
-void pciehp_green_led_off(struct slot *slot)
+void pciehp_green_led_off(struct controller *ctrl)
 {
-       struct controller *ctrl = slot->ctrl;
-
        if (!PWR_LED(ctrl))
                return;
 
@@ -475,10 +465,8 @@ void pciehp_green_led_off(struct slot *slot)
                 PCI_EXP_SLTCTL_PWR_IND_OFF);
 }
 
-void pciehp_green_led_blink(struct slot *slot)
+void pciehp_green_led_blink(struct controller *ctrl)
 {
-       struct controller *ctrl = slot->ctrl;
-
        if (!PWR_LED(ctrl))
                return;
 
@@ -489,9 +477,8 @@ void pciehp_green_led_blink(struct slot *slot)
                 PCI_EXP_SLTCTL_PWR_IND_BLINK);
 }
 
-int pciehp_power_on_slot(struct slot *slot)
+int pciehp_power_on_slot(struct controller *ctrl)
 {
-       struct controller *ctrl = slot->ctrl;
        struct pci_dev *pdev = ctrl_dev(ctrl);
        u16 slot_status;
        int retval;
@@ -515,10 +502,8 @@ int pciehp_power_on_slot(struct slot *slot)
        return retval;
 }
 
-void pciehp_power_off_slot(struct slot *slot)
+void pciehp_power_off_slot(struct controller *ctrl)
 {
-       struct controller *ctrl = slot->ctrl;
-
        pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTCTL_PCC);
        ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
                 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
@@ -533,9 +518,11 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id)
        u16 status, events;
 
        /*
-        * Interrupts only occur in D3hot or shallower (PCIe r4.0, sec 6.7.3.4).
+        * Interrupts only occur in D3hot or shallower and only if enabled
+        * in the Slot Control register (PCIe r4.0, sec 6.7.3.4).
         */
-       if (pdev->current_state == PCI_D3cold)
+       if (pdev->current_state == PCI_D3cold ||
+           (!(ctrl->slot_ctrl & PCI_EXP_SLTCTL_HPIE) && !pciehp_poll_mode))
                return IRQ_NONE;
 
        /*
@@ -616,7 +603,6 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id)
 {
        struct controller *ctrl = (struct controller *)dev_id;
        struct pci_dev *pdev = ctrl_dev(ctrl);
-       struct slot *slot = ctrl->slot;
        irqreturn_t ret;
        u32 events;
 
@@ -642,16 +628,16 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id)
        /* Check Attention Button Pressed */
        if (events & PCI_EXP_SLTSTA_ABP) {
                ctrl_info(ctrl, "Slot(%s): Attention button pressed\n",
-                         slot_name(slot));
-               pciehp_handle_button_press(slot);
+                         slot_name(ctrl));
+               pciehp_handle_button_press(ctrl);
        }
 
        /* Check Power Fault Detected */
        if ((events & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) {
                ctrl->power_fault_detected = 1;
-               ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(slot));
-               pciehp_set_attention_status(slot, 1);
-               pciehp_green_led_off(slot);
+               ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl));
+               pciehp_set_attention_status(ctrl, 1);
+               pciehp_green_led_off(ctrl);
        }
 
        /*
@@ -660,9 +646,9 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id)
         */
        down_read(&ctrl->reset_lock);
        if (events & DISABLE_SLOT)
-               pciehp_handle_disable_request(slot);
+               pciehp_handle_disable_request(ctrl);
        else if (events & (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC))
-               pciehp_handle_presence_or_link_change(slot, events);
+               pciehp_handle_presence_or_link_change(ctrl, events);
        up_read(&ctrl->reset_lock);
 
        pci_config_pm_runtime_put(pdev);
@@ -748,6 +734,16 @@ void pcie_clear_hotplug_events(struct controller *ctrl)
                                   PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
 }
 
+void pcie_enable_interrupt(struct controller *ctrl)
+{
+       pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_HPIE, PCI_EXP_SLTCTL_HPIE);
+}
+
+void pcie_disable_interrupt(struct controller *ctrl)
+{
+       pcie_write_cmd(ctrl, 0, PCI_EXP_SLTCTL_HPIE);
+}
+
 /*
  * pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary
  * bus reset of the bridge, but at the same time we want to ensure that it is
@@ -756,9 +752,9 @@ void pcie_clear_hotplug_events(struct controller *ctrl)
  * momentarily, if we see that they could interfere. Also, clear any spurious
  * events after.
  */
-int pciehp_reset_slot(struct slot *slot, int probe)
+int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, int probe)
 {
-       struct controller *ctrl = slot->ctrl;
+       struct controller *ctrl = to_ctrl(hotplug_slot);
        struct pci_dev *pdev = ctrl_dev(ctrl);
        u16 stat_mask = 0, ctrl_mask = 0;
        int rc;
@@ -808,34 +804,6 @@ void pcie_shutdown_notification(struct controller *ctrl)
        }
 }
 
-static int pcie_init_slot(struct controller *ctrl)
-{
-       struct pci_bus *subordinate = ctrl_dev(ctrl)->subordinate;
-       struct slot *slot;
-
-       slot = kzalloc(sizeof(*slot), GFP_KERNEL);
-       if (!slot)
-               return -ENOMEM;
-
-       down_read(&pci_bus_sem);
-       slot->state = list_empty(&subordinate->devices) ? OFF_STATE : ON_STATE;
-       up_read(&pci_bus_sem);
-
-       slot->ctrl = ctrl;
-       mutex_init(&slot->lock);
-       INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work);
-       ctrl->slot = slot;
-       return 0;
-}
-
-static void pcie_cleanup_slot(struct controller *ctrl)
-{
-       struct slot *slot = ctrl->slot;
-
-       cancel_delayed_work_sync(&slot->work);
-       kfree(slot);
-}
-
 static inline void dbg_ctrl(struct controller *ctrl)
 {
        struct pci_dev *pdev = ctrl->pcie->port;
@@ -857,12 +825,13 @@ struct controller *pcie_init(struct pcie_device *dev)
 {
        struct controller *ctrl;
        u32 slot_cap, link_cap;
-       u8 occupied, poweron;
+       u8 poweron;
        struct pci_dev *pdev = dev->port;
+       struct pci_bus *subordinate = pdev->subordinate;
 
        ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
        if (!ctrl)
-               goto abort;
+               return NULL;
 
        ctrl->pcie = dev;
        pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap);
@@ -879,15 +848,19 @@ struct controller *pcie_init(struct pcie_device *dev)
 
        ctrl->slot_cap = slot_cap;
        mutex_init(&ctrl->ctrl_lock);
+       mutex_init(&ctrl->state_lock);
        init_rwsem(&ctrl->reset_lock);
        init_waitqueue_head(&ctrl->requester);
        init_waitqueue_head(&ctrl->queue);
+       INIT_DELAYED_WORK(&ctrl->button_work, pciehp_queue_pushbutton_work);
        dbg_ctrl(ctrl);
 
+       down_read(&pci_bus_sem);
+       ctrl->state = list_empty(&subordinate->devices) ? OFF_STATE : ON_STATE;
+       up_read(&pci_bus_sem);
+
        /* Check if Data Link Layer Link Active Reporting is implemented */
        pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap);
-       if (link_cap & PCI_EXP_LNKCAP_DLLLARC)
-               ctrl->link_active_reporting = 1;
 
        /* Clear all remaining event bits in Slot Status register. */
        pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
@@ -909,33 +882,24 @@ struct controller *pcie_init(struct pcie_device *dev)
                FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC),
                pdev->broken_cmd_compl ? " (with Cmd Compl erratum)" : "");
 
-       if (pcie_init_slot(ctrl))
-               goto abort_ctrl;
-
        /*
         * If empty slot's power status is on, turn power off.  The IRQ isn't
         * requested yet, so avoid triggering a notification with this command.
         */
        if (POWER_CTRL(ctrl)) {
-               pciehp_get_adapter_status(ctrl->slot, &occupied);
-               pciehp_get_power_status(ctrl->slot, &poweron);
-               if (!occupied && poweron) {
+               pciehp_get_power_status(ctrl, &poweron);
+               if (!pciehp_card_present_or_link_active(ctrl) && poweron) {
                        pcie_disable_notification(ctrl);
-                       pciehp_power_off_slot(ctrl->slot);
+                       pciehp_power_off_slot(ctrl);
                }
        }
 
        return ctrl;
-
-abort_ctrl:
-       kfree(ctrl);
-abort:
-       return NULL;
 }
 
 void pciehp_release_ctrl(struct controller *ctrl)
 {
-       pcie_cleanup_slot(ctrl);
+       cancel_delayed_work_sync(&ctrl->button_work);
        kfree(ctrl);
 }
 
index 5c58c22e0c084145115662855c6efadc0dea6268..b9c1396db6fe9575065ba28188f4eef59bd1a3b5 100644 (file)
  *
  */
 
-#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/pci.h>
 #include "../pci.h"
 #include "pciehp.h"
 
-int pciehp_configure_device(struct slot *p_slot)
+/**
+ * pciehp_configure_device() - enumerate PCI devices below a hotplug bridge
+ * @ctrl: PCIe hotplug controller
+ *
+ * Enumerate PCI devices below a hotplug bridge and add them to the system.
+ * Return 0 on success, %-EEXIST if the devices are already enumerated or
+ * %-ENODEV if enumeration failed.
+ */
+int pciehp_configure_device(struct controller *ctrl)
 {
        struct pci_dev *dev;
-       struct pci_dev *bridge = p_slot->ctrl->pcie->port;
+       struct pci_dev *bridge = ctrl->pcie->port;
        struct pci_bus *parent = bridge->subordinate;
        int num, ret = 0;
-       struct controller *ctrl = p_slot->ctrl;
 
        pci_lock_rescan_remove();
 
@@ -62,17 +68,28 @@ int pciehp_configure_device(struct slot *p_slot)
        return ret;
 }
 
-void pciehp_unconfigure_device(struct slot *p_slot)
+/**
+ * pciehp_unconfigure_device() - remove PCI devices below a hotplug bridge
+ * @ctrl: PCIe hotplug controller
+ * @presence: whether the card is still present in the slot;
+ *     true for safe removal via sysfs or an Attention Button press,
+ *     false for surprise removal
+ *
+ * Unbind PCI devices below a hotplug bridge from their drivers and remove
+ * them from the system.  Safely removed devices are quiesced.  Surprise
+ * removed devices are marked as such to prevent further accesses.
+ */
+void pciehp_unconfigure_device(struct controller *ctrl, bool presence)
 {
-       u8 presence = 0;
        struct pci_dev *dev, *temp;
-       struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate;
+       struct pci_bus *parent = ctrl->pcie->port->subordinate;
        u16 command;
-       struct controller *ctrl = p_slot->ctrl;
 
        ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n",
                 __func__, pci_domain_nr(parent), parent->number);
-       pciehp_get_adapter_status(p_slot, &presence);
+
+       if (!presence)
+               pci_walk_bus(parent, pci_dev_set_disconnected, NULL);
 
        pci_lock_rescan_remove();
 
@@ -85,12 +102,6 @@ void pciehp_unconfigure_device(struct slot *p_slot)
        list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
                                         bus_list) {
                pci_dev_get(dev);
-               if (!presence) {
-                       pci_dev_set_disconnected(dev, NULL);
-                       if (pci_has_subordinate(dev))
-                               pci_walk_bus(dev->subordinate,
-                                            pci_dev_set_disconnected, NULL);
-               }
                pci_stop_and_remove_bus_device(dev);
                /*
                 * Ensure that no new Requests will be generated from
index 3276a5e4c430b6b9d4fbc96d1f1522959ffcb7a1..ee54f5bacad15975297dbeaef69fb5fd316d8925 100644 (file)
@@ -275,14 +275,13 @@ static int pnv_php_add_devtree(struct pnv_php_slot *php_slot)
                goto free_fdt1;
        }
 
-       fdt = kzalloc(fdt_totalsize(fdt1), GFP_KERNEL);
+       fdt = kmemdup(fdt1, fdt_totalsize(fdt1), GFP_KERNEL);
        if (!fdt) {
                ret = -ENOMEM;
                goto free_fdt1;
        }
 
        /* Unflatten device tree blob */
-       memcpy(fdt, fdt1, fdt_totalsize(fdt1));
        dt = of_fdt_unflatten_tree(fdt, php_slot->dn, NULL);
        if (!dt) {
                ret = -EINVAL;
@@ -328,10 +327,15 @@ static int pnv_php_add_devtree(struct pnv_php_slot *php_slot)
        return ret;
 }
 
+static inline struct pnv_php_slot *to_pnv_php_slot(struct hotplug_slot *slot)
+{
+       return container_of(slot, struct pnv_php_slot, slot);
+}
+
 int pnv_php_set_slot_power_state(struct hotplug_slot *slot,
                                 uint8_t state)
 {
-       struct pnv_php_slot *php_slot = slot->private;
+       struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
        struct opal_msg msg;
        int ret;
 
@@ -363,7 +367,7 @@ EXPORT_SYMBOL_GPL(pnv_php_set_slot_power_state);
 
 static int pnv_php_get_power_state(struct hotplug_slot *slot, u8 *state)
 {
-       struct pnv_php_slot *php_slot = slot->private;
+       struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
        uint8_t power_state = OPAL_PCI_SLOT_POWER_ON;
        int ret;
 
@@ -378,7 +382,6 @@ static int pnv_php_get_power_state(struct hotplug_slot *slot, u8 *state)
                         ret);
        } else {
                *state = power_state;
-               slot->info->power_status = power_state;
        }
 
        return 0;
@@ -386,7 +389,7 @@ static int pnv_php_get_power_state(struct hotplug_slot *slot, u8 *state)
 
 static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state)
 {
-       struct pnv_php_slot *php_slot = slot->private;
+       struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
        uint8_t presence = OPAL_PCI_SLOT_EMPTY;
        int ret;
 
@@ -397,7 +400,6 @@ static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state)
        ret = pnv_pci_get_presence_state(php_slot->id, &presence);
        if (ret >= 0) {
                *state = presence;
-               slot->info->adapter_status = presence;
                ret = 0;
        } else {
                pci_warn(php_slot->pdev, "Error %d getting presence\n", ret);
@@ -406,10 +408,20 @@ static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state)
        return ret;
 }
 
+static int pnv_php_get_attention_state(struct hotplug_slot *slot, u8 *state)
+{
+       struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
+
+       *state = php_slot->attention_state;
+       return 0;
+}
+
 static int pnv_php_set_attention_state(struct hotplug_slot *slot, u8 state)
 {
+       struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
+
        /* FIXME: Make it real once firmware supports it */
-       slot->info->attention_status = state;
+       php_slot->attention_state = state;
 
        return 0;
 }
@@ -501,15 +513,14 @@ static int pnv_php_enable(struct pnv_php_slot *php_slot, bool rescan)
 
 static int pnv_php_enable_slot(struct hotplug_slot *slot)
 {
-       struct pnv_php_slot *php_slot = container_of(slot,
-                                                    struct pnv_php_slot, slot);
+       struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
 
        return pnv_php_enable(php_slot, true);
 }
 
 static int pnv_php_disable_slot(struct hotplug_slot *slot)
 {
-       struct pnv_php_slot *php_slot = slot->private;
+       struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
        int ret;
 
        if (php_slot->state != PNV_PHP_STATE_POPULATED)
@@ -530,9 +541,10 @@ static int pnv_php_disable_slot(struct hotplug_slot *slot)
        return ret;
 }
 
-static struct hotplug_slot_ops php_slot_ops = {
+static const struct hotplug_slot_ops php_slot_ops = {
        .get_power_status       = pnv_php_get_power_state,
        .get_adapter_status     = pnv_php_get_adapter_state,
+       .get_attention_status   = pnv_php_get_attention_state,
        .set_attention_status   = pnv_php_set_attention_state,
        .enable_slot            = pnv_php_enable_slot,
        .disable_slot           = pnv_php_disable_slot,
@@ -594,8 +606,6 @@ static struct pnv_php_slot *pnv_php_alloc_slot(struct device_node *dn)
        php_slot->id                    = id;
        php_slot->power_state_check     = false;
        php_slot->slot.ops              = &php_slot_ops;
-       php_slot->slot.info             = &php_slot->slot_info;
-       php_slot->slot.private          = php_slot;
 
        INIT_LIST_HEAD(&php_slot->children);
        INIT_LIST_HEAD(&php_slot->link);
index c8311724bd76af468d2bf6afce8c1e1f5028d07e..bdc954d70869839a436e8f1f7fa51e6dac169f76 100644 (file)
@@ -63,16 +63,22 @@ struct slot {
        u32 index;
        u32 type;
        u32 power_domain;
+       u8 attention_status;
        char *name;
        struct device_node *dn;
        struct pci_bus *bus;
        struct list_head *pci_devs;
-       struct hotplug_slot *hotplug_slot;
+       struct hotplug_slot hotplug_slot;
 };
 
-extern struct hotplug_slot_ops rpaphp_hotplug_slot_ops;
+extern const struct hotplug_slot_ops rpaphp_hotplug_slot_ops;
 extern struct list_head rpaphp_slot_head;
 
+static inline struct slot *to_slot(struct hotplug_slot *hotplug_slot)
+{
+       return container_of(hotplug_slot, struct slot, hotplug_slot);
+}
+
 /* function prototypes */
 
 /* rpaphp_pci.c */
index 857c358b727b839aa1bca6f67b11c16bee282cb1..bcd5d357ca238f9bfcd149ff7605805b9e049cd9 100644 (file)
@@ -52,7 +52,7 @@ module_param_named(debug, rpaphp_debug, bool, 0644);
 static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
 {
        int rc;
-       struct slot *slot = (struct slot *)hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
 
        switch (value) {
        case 0:
@@ -66,7 +66,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
 
        rc = rtas_set_indicator(DR_INDICATOR, slot->index, value);
        if (!rc)
-               hotplug_slot->info->attention_status = value;
+               slot->attention_status = value;
 
        return rc;
 }
@@ -79,7 +79,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
 static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
        int retval, level;
-       struct slot *slot = (struct slot *)hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
 
        retval = rtas_get_power_level(slot->power_domain, &level);
        if (!retval)
@@ -94,14 +94,14 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
  */
 static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
-       struct slot *slot = (struct slot *)hotplug_slot->private;
-       *value = slot->hotplug_slot->info->attention_status;
+       struct slot *slot = to_slot(hotplug_slot);
+       *value = slot->attention_status;
        return 0;
 }
 
 static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
-       struct slot *slot = (struct slot *)hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
        int rc, state;
 
        rc = rpaphp_get_sensor_state(slot, &state);
@@ -409,7 +409,7 @@ static void __exit cleanup_slots(void)
        list_for_each_entry_safe(slot, next, &rpaphp_slot_head,
                                 rpaphp_slot_list) {
                list_del(&slot->rpaphp_slot_list);
-               pci_hp_deregister(slot->hotplug_slot);
+               pci_hp_deregister(&slot->hotplug_slot);
                dealloc_slot_struct(slot);
        }
        return;
@@ -434,7 +434,7 @@ static void __exit rpaphp_exit(void)
 
 static int enable_slot(struct hotplug_slot *hotplug_slot)
 {
-       struct slot *slot = (struct slot *)hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
        int state;
        int retval;
 
@@ -464,7 +464,7 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
 
 static int disable_slot(struct hotplug_slot *hotplug_slot)
 {
-       struct slot *slot = (struct slot *)hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
        if (slot->state == NOT_CONFIGURED)
                return -EINVAL;
 
@@ -477,7 +477,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)
        return 0;
 }
 
-struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
+const struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
        .enable_slot = enable_slot,
        .disable_slot = disable_slot,
        .set_attention_status = set_attention_status,
index 0aac33e15dab2de21e68b487bc907a3d0251d241..beca61badeea4cc9015c10961915c5aec9176539 100644 (file)
@@ -54,25 +54,21 @@ int rpaphp_get_sensor_state(struct slot *slot, int *state)
  * rpaphp_enable_slot - record slot state, config pci device
  * @slot: target &slot
  *
- * Initialize values in the slot, and the hotplug_slot info
- * structures to indicate if there is a pci card plugged into
- * the slot. If the slot is not empty, run the pcibios routine
+ * Initialize values in the slot structure to indicate if there is a pci card
+ * plugged into the slot. If the slot is not empty, run the pcibios routine
  * to get pcibios stuff correctly set up.
  */
 int rpaphp_enable_slot(struct slot *slot)
 {
        int rc, level, state;
        struct pci_bus *bus;
-       struct hotplug_slot_info *info = slot->hotplug_slot->info;
 
-       info->adapter_status = NOT_VALID;
        slot->state = EMPTY;
 
        /* Find out if the power is turned on for the slot */
        rc = rtas_get_power_level(slot->power_domain, &level);
        if (rc)
                return rc;
-       info->power_status = level;
 
        /* Figure out if there is an adapter in the slot */
        rc = rpaphp_get_sensor_state(slot, &state);
@@ -85,13 +81,11 @@ int rpaphp_enable_slot(struct slot *slot)
                return -EINVAL;
        }
 
-       info->adapter_status = EMPTY;
        slot->bus = bus;
        slot->pci_devs = &bus->devices;
 
        /* if there's an adapter in the slot, go add the pci devices */
        if (state == PRESENT) {
-               info->adapter_status = NOT_CONFIGURED;
                slot->state = NOT_CONFIGURED;
 
                /* non-empty slot has to have child */
@@ -105,7 +99,6 @@ int rpaphp_enable_slot(struct slot *slot)
                        pci_hp_add_devices(bus);
 
                if (!list_empty(&bus->devices)) {
-                       info->adapter_status = CONFIGURED;
                        slot->state = CONFIGURED;
                }
 
index b916c8e4372d8a225e214f8049bf50e4b481d0a0..5282aa3e33c506aa7995cd36132545ea354a01d7 100644 (file)
@@ -21,9 +21,7 @@
 /* free up the memory used by a slot */
 void dealloc_slot_struct(struct slot *slot)
 {
-       kfree(slot->hotplug_slot->info);
        kfree(slot->name);
-       kfree(slot->hotplug_slot);
        kfree(slot);
 }
 
@@ -35,28 +33,16 @@ struct slot *alloc_slot_struct(struct device_node *dn,
        slot = kzalloc(sizeof(struct slot), GFP_KERNEL);
        if (!slot)
                goto error_nomem;
-       slot->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
-       if (!slot->hotplug_slot)
-               goto error_slot;
-       slot->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
-                                          GFP_KERNEL);
-       if (!slot->hotplug_slot->info)
-               goto error_hpslot;
        slot->name = kstrdup(drc_name, GFP_KERNEL);
        if (!slot->name)
-               goto error_info;
+               goto error_slot;
        slot->dn = dn;
        slot->index = drc_index;
        slot->power_domain = power_domain;
-       slot->hotplug_slot->private = slot;
-       slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops;
+       slot->hotplug_slot.ops = &rpaphp_hotplug_slot_ops;
 
        return (slot);
 
-error_info:
-       kfree(slot->hotplug_slot->info);
-error_hpslot:
-       kfree(slot->hotplug_slot);
 error_slot:
        kfree(slot);
 error_nomem:
@@ -77,7 +63,7 @@ static int is_registered(struct slot *slot)
 int rpaphp_deregister_slot(struct slot *slot)
 {
        int retval = 0;
-       struct hotplug_slot *php_slot = slot->hotplug_slot;
+       struct hotplug_slot *php_slot = &slot->hotplug_slot;
 
         dbg("%s - Entry: deregistering slot=%s\n",
                __func__, slot->name);
@@ -93,7 +79,7 @@ EXPORT_SYMBOL_GPL(rpaphp_deregister_slot);
 
 int rpaphp_register_slot(struct slot *slot)
 {
-       struct hotplug_slot *php_slot = slot->hotplug_slot;
+       struct hotplug_slot *php_slot = &slot->hotplug_slot;
        struct device_node *child;
        u32 my_index;
        int retval;
index 93b5341d282c3b12e2eeb5923387d456165ad6b8..30ee72268790ce2fd09461204f623d334fe073cf 100644 (file)
@@ -32,10 +32,15 @@ static int zpci_fn_configured(enum zpci_state state)
  */
 struct slot {
        struct list_head slot_list;
-       struct hotplug_slot *hotplug_slot;
+       struct hotplug_slot hotplug_slot;
        struct zpci_dev *zdev;
 };
 
+static inline struct slot *to_slot(struct hotplug_slot *hotplug_slot)
+{
+       return container_of(hotplug_slot, struct slot, hotplug_slot);
+}
+
 static inline int slot_configure(struct slot *slot)
 {
        int ret = sclp_pci_configure(slot->zdev->fid);
@@ -60,7 +65,7 @@ static inline int slot_deconfigure(struct slot *slot)
 
 static int enable_slot(struct hotplug_slot *hotplug_slot)
 {
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
        int rc;
 
        if (slot->zdev->state != ZPCI_FN_STATE_STANDBY)
@@ -88,7 +93,7 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
 
 static int disable_slot(struct hotplug_slot *hotplug_slot)
 {
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
        struct pci_dev *pdev;
        int rc;
 
@@ -110,7 +115,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)
 
 static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
-       struct slot *slot = hotplug_slot->private;
+       struct slot *slot = to_slot(hotplug_slot);
 
        switch (slot->zdev->state) {
        case ZPCI_FN_STATE_STANDBY:
@@ -130,7 +135,7 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
        return 0;
 }
 
-static struct hotplug_slot_ops s390_hotplug_slot_ops = {
+static const struct hotplug_slot_ops s390_hotplug_slot_ops = {
        .enable_slot =          enable_slot,
        .disable_slot =         disable_slot,
        .get_power_status =     get_power_status,
@@ -139,8 +144,6 @@ static struct hotplug_slot_ops s390_hotplug_slot_ops = {
 
 int zpci_init_slot(struct zpci_dev *zdev)
 {
-       struct hotplug_slot *hotplug_slot;
-       struct hotplug_slot_info *info;
        char name[SLOT_NAME_SIZE];
        struct slot *slot;
        int rc;
@@ -152,26 +155,11 @@ int zpci_init_slot(struct zpci_dev *zdev)
        if (!slot)
                goto error;
 
-       hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
-       if (!hotplug_slot)
-               goto error_hp;
-       hotplug_slot->private = slot;
-
-       slot->hotplug_slot = hotplug_slot;
        slot->zdev = zdev;
-
-       info = kzalloc(sizeof(*info), GFP_KERNEL);
-       if (!info)
-               goto error_info;
-       hotplug_slot->info = info;
-
-       hotplug_slot->ops = &s390_hotplug_slot_ops;
-
-       get_power_status(hotplug_slot, &info->power_status);
-       get_adapter_status(hotplug_slot, &info->adapter_status);
+       slot->hotplug_slot.ops = &s390_hotplug_slot_ops;
 
        snprintf(name, SLOT_NAME_SIZE, "%08x", zdev->fid);
-       rc = pci_hp_register(slot->hotplug_slot, zdev->bus,
+       rc = pci_hp_register(&slot->hotplug_slot, zdev->bus,
                             ZPCI_DEVFN, name);
        if (rc)
                goto error_reg;
@@ -180,10 +168,6 @@ int zpci_init_slot(struct zpci_dev *zdev)
        return 0;
 
 error_reg:
-       kfree(info);
-error_info:
-       kfree(hotplug_slot);
-error_hp:
        kfree(slot);
 error:
        return -ENOMEM;
@@ -198,9 +182,7 @@ void zpci_exit_slot(struct zpci_dev *zdev)
                if (slot->zdev != zdev)
                        continue;
                list_del(&slot->slot_list);
-               pci_hp_deregister(slot->hotplug_slot);
-               kfree(slot->hotplug_slot->info);
-               kfree(slot->hotplug_slot);
+               pci_hp_deregister(&slot->hotplug_slot);
                kfree(slot);
        }
 }
index babd23409f61c2bfb844df703fa7409a0adb509c..231f5bdd3d2d185a64ea031de2342b2d6c72fd19 100644 (file)
@@ -56,7 +56,7 @@ struct slot {
        int device_num;
        struct pci_bus *pci_bus;
        /* this struct for glue internal only */
-       struct hotplug_slot *hotplug_slot;
+       struct hotplug_slot hotplug_slot;
        struct list_head hp_list;
        char physical_path[SN_SLOT_NAME_SIZE];
 };
@@ -80,7 +80,7 @@ static int enable_slot(struct hotplug_slot *slot);
 static int disable_slot(struct hotplug_slot *slot);
 static inline int get_power_status(struct hotplug_slot *slot, u8 *value);
 
-static struct hotplug_slot_ops sn_hotplug_slot_ops = {
+static const struct hotplug_slot_ops sn_hotplug_slot_ops = {
        .enable_slot            = enable_slot,
        .disable_slot           = disable_slot,
        .get_power_status       = get_power_status,
@@ -88,10 +88,15 @@ static struct hotplug_slot_ops sn_hotplug_slot_ops = {
 
 static DEFINE_MUTEX(sn_hotplug_mutex);
 
+static struct slot *to_slot(struct hotplug_slot *bss_hotplug_slot)
+{
+       return container_of(bss_hotplug_slot, struct slot, hotplug_slot);
+}
+
 static ssize_t path_show(struct pci_slot *pci_slot, char *buf)
 {
        int retval = -ENOENT;
-       struct slot *slot = pci_slot->hotplug->private;
+       struct slot *slot = to_slot(pci_slot->hotplug);
 
        if (!slot)
                return retval;
@@ -156,7 +161,7 @@ static int sn_pci_bus_valid(struct pci_bus *pci_bus)
        return -EIO;
 }
 
-static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot,
+static int sn_hp_slot_private_alloc(struct hotplug_slot **bss_hotplug_slot,
                                    struct pci_bus *pci_bus, int device,
                                    char *name)
 {
@@ -168,7 +173,6 @@ static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot,
        slot = kzalloc(sizeof(*slot), GFP_KERNEL);
        if (!slot)
                return -ENOMEM;
-       bss_hotplug_slot->private = slot;
 
        slot->device_num = device;
        slot->pci_bus = pci_bus;
@@ -179,8 +183,8 @@ static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot,
 
        sn_generate_path(pci_bus, slot->physical_path);
 
-       slot->hotplug_slot = bss_hotplug_slot;
        list_add(&slot->hp_list, &sn_hp_list);
+       *bss_hotplug_slot = &slot->hotplug_slot;
 
        return 0;
 }
@@ -192,10 +196,9 @@ static struct hotplug_slot *sn_hp_destroy(void)
        struct hotplug_slot *bss_hotplug_slot = NULL;
 
        list_for_each_entry(slot, &sn_hp_list, hp_list) {
-               bss_hotplug_slot = slot->hotplug_slot;
+               bss_hotplug_slot = &slot->hotplug_slot;
                pci_slot = bss_hotplug_slot->pci_slot;
-               list_del(&((struct slot *)bss_hotplug_slot->private)->
-                        hp_list);
+               list_del(&slot->hp_list);
                sysfs_remove_file(&pci_slot->kobj,
                                  &sn_slot_path_attr.attr);
                break;
@@ -227,7 +230,7 @@ static void sn_bus_free_data(struct pci_dev *dev)
 static int sn_slot_enable(struct hotplug_slot *bss_hotplug_slot,
                          int device_num, char **ssdt)
 {
-       struct slot *slot = bss_hotplug_slot->private;
+       struct slot *slot = to_slot(bss_hotplug_slot);
        struct pcibus_info *pcibus_info;
        struct pcibr_slot_enable_resp resp;
        int rc;
@@ -267,7 +270,7 @@ static int sn_slot_enable(struct hotplug_slot *bss_hotplug_slot,
 static int sn_slot_disable(struct hotplug_slot *bss_hotplug_slot,
                           int device_num, int action)
 {
-       struct slot *slot = bss_hotplug_slot->private;
+       struct slot *slot = to_slot(bss_hotplug_slot);
        struct pcibus_info *pcibus_info;
        struct pcibr_slot_disable_resp resp;
        int rc;
@@ -323,7 +326,7 @@ static int sn_slot_disable(struct hotplug_slot *bss_hotplug_slot,
  */
 static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
 {
-       struct slot *slot = bss_hotplug_slot->private;
+       struct slot *slot = to_slot(bss_hotplug_slot);
        struct pci_bus *new_bus = NULL;
        struct pci_dev *dev;
        int num_funcs;
@@ -469,7 +472,7 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
 
 static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
 {
-       struct slot *slot = bss_hotplug_slot->private;
+       struct slot *slot = to_slot(bss_hotplug_slot);
        struct pci_dev *dev, *temp;
        int rc;
        acpi_handle ssdt_hdl = NULL;
@@ -571,7 +574,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
 static inline int get_power_status(struct hotplug_slot *bss_hotplug_slot,
                                   u8 *value)
 {
-       struct slot *slot = bss_hotplug_slot->private;
+       struct slot *slot = to_slot(bss_hotplug_slot);
        struct pcibus_info *pcibus_info;
        u32 power;
 
@@ -585,9 +588,7 @@ static inline int get_power_status(struct hotplug_slot *bss_hotplug_slot,
 
 static void sn_release_slot(struct hotplug_slot *bss_hotplug_slot)
 {
-       kfree(bss_hotplug_slot->info);
-       kfree(bss_hotplug_slot->private);
-       kfree(bss_hotplug_slot);
+       kfree(to_slot(bss_hotplug_slot));
 }
 
 static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
@@ -607,22 +608,7 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
                if (sn_pci_slot_valid(pci_bus, device) != 1)
                        continue;
 
-               bss_hotplug_slot = kzalloc(sizeof(*bss_hotplug_slot),
-                                          GFP_KERNEL);
-               if (!bss_hotplug_slot) {
-                       rc = -ENOMEM;
-                       goto alloc_err;
-               }
-
-               bss_hotplug_slot->info =
-                       kzalloc(sizeof(struct hotplug_slot_info),
-                               GFP_KERNEL);
-               if (!bss_hotplug_slot->info) {
-                       rc = -ENOMEM;
-                       goto alloc_err;
-               }
-
-               if (sn_hp_slot_private_alloc(bss_hotplug_slot,
+               if (sn_hp_slot_private_alloc(&bss_hotplug_slot,
                                             pci_bus, device, name)) {
                        rc = -ENOMEM;
                        goto alloc_err;
@@ -637,7 +623,7 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
                rc = sysfs_create_file(&pci_slot->kobj,
                                       &sn_slot_path_attr.attr);
                if (rc)
-                       goto register_err;
+                       goto alloc_err;
        }
        pci_dbg(pci_bus->self, "Registered bus with hotplug\n");
        return rc;
@@ -646,14 +632,11 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
        pci_dbg(pci_bus->self, "bus failed to register with err = %d\n",
                rc);
 
-alloc_err:
-       if (rc == -ENOMEM)
-               pci_dbg(pci_bus->self, "Memory allocation error\n");
-
        /* destroy THIS element */
-       if (bss_hotplug_slot)
-               sn_release_slot(bss_hotplug_slot);
+       sn_hp_destroy();
+       sn_release_slot(bss_hotplug_slot);
 
+alloc_err:
        /* destroy anything else on the list */
        while ((bss_hotplug_slot = sn_hp_destroy())) {
                pci_hp_deregister(bss_hotplug_slot);
index 516e4835019ccf5cbc673c73599a40db858b75eb..f7f13ee5d06e096f3805044978df330b0ed77495 100644 (file)
@@ -67,11 +67,13 @@ struct slot {
        u32 number;
        u8 is_a_board;
        u8 state;
+       u8 attention_save;
        u8 presence_save;
+       u8 latch_save;
        u8 pwr_save;
        struct controller *ctrl;
        const struct hpc_ops *hpc_ops;
-       struct hotplug_slot *hotplug_slot;
+       struct hotplug_slot hotplug_slot;
        struct list_head        slot_list;
        struct delayed_work work;       /* work for button event */
        struct mutex lock;
@@ -169,7 +171,7 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev);
 
 static inline const char *slot_name(struct slot *slot)
 {
-       return hotplug_slot_name(slot->hotplug_slot);
+       return hotplug_slot_name(&slot->hotplug_slot);
 }
 
 struct ctrl_reg {
@@ -207,7 +209,7 @@ enum ctrl_offsets {
 
 static inline struct slot *get_slot(struct hotplug_slot *hotplug_slot)
 {
-       return hotplug_slot->private;
+       return container_of(hotplug_slot, struct slot, hotplug_slot);
 }
 
 static inline struct slot *shpchp_find_slot(struct controller *ctrl, u8 device)
index 97cee23f3d516e0d37c93f5f2f4f3af78410a529..81a918d47895df954d05724262965751ef9baaa7 100644 (file)
@@ -51,7 +51,7 @@ static int get_attention_status(struct hotplug_slot *slot, u8 *value);
 static int get_latch_status(struct hotplug_slot *slot, u8 *value);
 static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
 
-static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
+static const struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
        .set_attention_status = set_attention_status,
        .enable_slot =          enable_slot,
        .disable_slot =         disable_slot,
@@ -65,7 +65,6 @@ static int init_slots(struct controller *ctrl)
 {
        struct slot *slot;
        struct hotplug_slot *hotplug_slot;
-       struct hotplug_slot_info *info;
        char name[SLOT_NAME_SIZE];
        int retval;
        int i;
@@ -77,19 +76,7 @@ static int init_slots(struct controller *ctrl)
                        goto error;
                }
 
-               hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
-               if (!hotplug_slot) {
-                       retval = -ENOMEM;
-                       goto error_slot;
-               }
-               slot->hotplug_slot = hotplug_slot;
-
-               info = kzalloc(sizeof(*info), GFP_KERNEL);
-               if (!info) {
-                       retval = -ENOMEM;
-                       goto error_hpslot;
-               }
-               hotplug_slot->info = info;
+               hotplug_slot = &slot->hotplug_slot;
 
                slot->hp_slot = i;
                slot->ctrl = ctrl;
@@ -101,14 +88,13 @@ static int init_slots(struct controller *ctrl)
                slot->wq = alloc_workqueue("shpchp-%d", 0, 0, slot->number);
                if (!slot->wq) {
                        retval = -ENOMEM;
-                       goto error_info;
+                       goto error_slot;
                }
 
                mutex_init(&slot->lock);
                INIT_DELAYED_WORK(&slot->work, shpchp_queue_pushbutton_work);
 
                /* register this slot with the hotplug pci core */
-               hotplug_slot->private = slot;
                snprintf(name, SLOT_NAME_SIZE, "%d", slot->number);
                hotplug_slot->ops = &shpchp_hotplug_slot_ops;
 
@@ -116,7 +102,7 @@ static int init_slots(struct controller *ctrl)
                         pci_domain_nr(ctrl->pci_dev->subordinate),
                         slot->bus, slot->device, slot->hp_slot, slot->number,
                         ctrl->slot_device_offset);
-               retval = pci_hp_register(slot->hotplug_slot,
+               retval = pci_hp_register(hotplug_slot,
                                ctrl->pci_dev->subordinate, slot->device, name);
                if (retval) {
                        ctrl_err(ctrl, "pci_hp_register failed with error %d\n",
@@ -124,10 +110,10 @@ static int init_slots(struct controller *ctrl)
                        goto error_slotwq;
                }
 
-               get_power_status(hotplug_slot, &info->power_status);
-               get_attention_status(hotplug_slot, &info->attention_status);
-               get_latch_status(hotplug_slot, &info->latch_status);
-               get_adapter_status(hotplug_slot, &info->adapter_status);
+               get_power_status(hotplug_slot, &slot->pwr_save);
+               get_attention_status(hotplug_slot, &slot->attention_save);
+               get_latch_status(hotplug_slot, &slot->latch_save);
+               get_adapter_status(hotplug_slot, &slot->presence_save);
 
                list_add(&slot->slot_list, &ctrl->slot_list);
        }
@@ -135,10 +121,6 @@ static int init_slots(struct controller *ctrl)
        return 0;
 error_slotwq:
        destroy_workqueue(slot->wq);
-error_info:
-       kfree(info);
-error_hpslot:
-       kfree(hotplug_slot);
 error_slot:
        kfree(slot);
 error:
@@ -153,9 +135,7 @@ void cleanup_slots(struct controller *ctrl)
                list_del(&slot->slot_list);
                cancel_delayed_work(&slot->work);
                destroy_workqueue(slot->wq);
-               pci_hp_deregister(slot->hotplug_slot);
-               kfree(slot->hotplug_slot->info);
-               kfree(slot->hotplug_slot);
+               pci_hp_deregister(&slot->hotplug_slot);
                kfree(slot);
        }
 }
@@ -170,7 +150,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
        ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
                 __func__, slot_name(slot));
 
-       hotplug_slot->info->attention_status = status;
+       slot->attention_save = status;
        slot->hpc_ops->set_attention_status(slot, status);
 
        return 0;
@@ -206,7 +186,7 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 
        retval = slot->hpc_ops->get_power_status(slot, value);
        if (retval < 0)
-               *value = hotplug_slot->info->power_status;
+               *value = slot->pwr_save;
 
        return 0;
 }
@@ -221,7 +201,7 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
 
        retval = slot->hpc_ops->get_attention_status(slot, value);
        if (retval < 0)
-               *value = hotplug_slot->info->attention_status;
+               *value = slot->attention_save;
 
        return 0;
 }
@@ -236,7 +216,7 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
 
        retval = slot->hpc_ops->get_latch_status(slot, value);
        if (retval < 0)
-               *value = hotplug_slot->info->latch_status;
+               *value = slot->latch_save;
 
        return 0;
 }
@@ -251,7 +231,7 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
 
        retval = slot->hpc_ops->get_adapter_status(slot, value);
        if (retval < 0)
-               *value = hotplug_slot->info->adapter_status;
+               *value = slot->presence_save;
 
        return 0;
 }
index 1267dcc5a5313c0e7010c0251154d5df1ac533ae..078003dcde5bbd4b20e7e09f0d0d2f9afecf0aac 100644 (file)
@@ -446,23 +446,12 @@ void shpchp_queue_pushbutton_work(struct work_struct *work)
        mutex_unlock(&p_slot->lock);
 }
 
-static int update_slot_info (struct slot *slot)
+static void update_slot_info(struct slot *slot)
 {
-       struct hotplug_slot_info *info;
-       int result;
-
-       info = kmalloc(sizeof(*info), GFP_KERNEL);
-       if (!info)
-               return -ENOMEM;
-
-       slot->hpc_ops->get_power_status(slot, &(info->power_status));
-       slot->hpc_ops->get_attention_status(slot, &(info->attention_status));
-       slot->hpc_ops->get_latch_status(slot, &(info->latch_status));
-       slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status));
-
-       result = pci_hp_change_slot_info(slot->hotplug_slot, info);
-       kfree (info);
-       return result;
+       slot->hpc_ops->get_power_status(slot, &slot->pwr_save);
+       slot->hpc_ops->get_attention_status(slot, &slot->attention_save);
+       slot->hpc_ops->get_latch_status(slot, &slot->latch_save);
+       slot->hpc_ops->get_adapter_status(slot, &slot->presence_save);
 }
 
 /*
index f2ef896464b376077462c53315533a555cf0198d..af24ed50a2452008e32dc837834682a506739fd9 100644 (file)
@@ -958,7 +958,6 @@ static int __pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries,
                        }
                }
        }
-       WARN_ON(!!dev->msix_enabled);
 
        /* Check whether driver already requested for MSI irq */
        if (dev->msi_enabled) {
@@ -1028,8 +1027,6 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec,
        if (!pci_msi_supported(dev, minvec))
                return -EINVAL;
 
-       WARN_ON(!!dev->msi_enabled);
-
        /* Check whether driver already requested MSI-X irqs */
        if (dev->msix_enabled) {
                pci_info(dev, "can't enable MSI (MSI-X already enabled)\n");
@@ -1039,6 +1036,9 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec,
        if (maxvec < minvec)
                return -ERANGE;
 
+       if (WARN_ON_ONCE(dev->msi_enabled))
+               return -EINVAL;
+
        nvec = pci_msi_vec_count(dev);
        if (nvec < 0)
                return nvec;
@@ -1087,6 +1087,9 @@ static int __pci_enable_msix_range(struct pci_dev *dev,
        if (maxvec < minvec)
                return -ERANGE;
 
+       if (WARN_ON_ONCE(dev->msix_enabled))
+               return -EINVAL;
+
        for (;;) {
                if (affd) {
                        nvec = irq_calc_affinity_vectors(minvec, nvec, affd);
diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c
new file mode 100644 (file)
index 0000000..ae3c5b2
--- /dev/null
@@ -0,0 +1,805 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCI Peer 2 Peer DMA support.
+ *
+ * Copyright (c) 2016-2018, Logan Gunthorpe
+ * Copyright (c) 2016-2017, Microsemi Corporation
+ * Copyright (c) 2017, Christoph Hellwig
+ * Copyright (c) 2018, Eideticom Inc.
+ */
+
+#define pr_fmt(fmt) "pci-p2pdma: " fmt
+#include <linux/ctype.h>
+#include <linux/pci-p2pdma.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/genalloc.h>
+#include <linux/memremap.h>
+#include <linux/percpu-refcount.h>
+#include <linux/random.h>
+#include <linux/seq_buf.h>
+
+struct pci_p2pdma {
+       struct percpu_ref devmap_ref;
+       struct completion devmap_ref_done;
+       struct gen_pool *pool;
+       bool p2pmem_published;
+};
+
+static ssize_t size_show(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       size_t size = 0;
+
+       if (pdev->p2pdma->pool)
+               size = gen_pool_size(pdev->p2pdma->pool);
+
+       return snprintf(buf, PAGE_SIZE, "%zd\n", size);
+}
+static DEVICE_ATTR_RO(size);
+
+static ssize_t available_show(struct device *dev, struct device_attribute *attr,
+                             char *buf)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       size_t avail = 0;
+
+       if (pdev->p2pdma->pool)
+               avail = gen_pool_avail(pdev->p2pdma->pool);
+
+       return snprintf(buf, PAGE_SIZE, "%zd\n", avail);
+}
+static DEVICE_ATTR_RO(available);
+
+static ssize_t published_show(struct device *dev, struct device_attribute *attr,
+                             char *buf)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+                       pdev->p2pdma->p2pmem_published);
+}
+static DEVICE_ATTR_RO(published);
+
+static struct attribute *p2pmem_attrs[] = {
+       &dev_attr_size.attr,
+       &dev_attr_available.attr,
+       &dev_attr_published.attr,
+       NULL,
+};
+
+static const struct attribute_group p2pmem_group = {
+       .attrs = p2pmem_attrs,
+       .name = "p2pmem",
+};
+
+static void pci_p2pdma_percpu_release(struct percpu_ref *ref)
+{
+       struct pci_p2pdma *p2p =
+               container_of(ref, struct pci_p2pdma, devmap_ref);
+
+       complete_all(&p2p->devmap_ref_done);
+}
+
+static void pci_p2pdma_percpu_kill(void *data)
+{
+       struct percpu_ref *ref = data;
+
+       /*
+        * pci_p2pdma_add_resource() may be called multiple times
+        * by a driver and may register the percpu_kill devm action multiple
+        * times. We only want the first action to actually kill the
+        * percpu_ref.
+        */
+       if (percpu_ref_is_dying(ref))
+               return;
+
+       percpu_ref_kill(ref);
+}
+
+static void pci_p2pdma_release(void *data)
+{
+       struct pci_dev *pdev = data;
+
+       if (!pdev->p2pdma)
+               return;
+
+       wait_for_completion(&pdev->p2pdma->devmap_ref_done);
+       percpu_ref_exit(&pdev->p2pdma->devmap_ref);
+
+       gen_pool_destroy(pdev->p2pdma->pool);
+       sysfs_remove_group(&pdev->dev.kobj, &p2pmem_group);
+       pdev->p2pdma = NULL;
+}
+
+static int pci_p2pdma_setup(struct pci_dev *pdev)
+{
+       int error = -ENOMEM;
+       struct pci_p2pdma *p2p;
+
+       p2p = devm_kzalloc(&pdev->dev, sizeof(*p2p), GFP_KERNEL);
+       if (!p2p)
+               return -ENOMEM;
+
+       p2p->pool = gen_pool_create(PAGE_SHIFT, dev_to_node(&pdev->dev));
+       if (!p2p->pool)
+               goto out;
+
+       init_completion(&p2p->devmap_ref_done);
+       error = percpu_ref_init(&p2p->devmap_ref,
+                       pci_p2pdma_percpu_release, 0, GFP_KERNEL);
+       if (error)
+               goto out_pool_destroy;
+
+       error = devm_add_action_or_reset(&pdev->dev, pci_p2pdma_release, pdev);
+       if (error)
+               goto out_pool_destroy;
+
+       pdev->p2pdma = p2p;
+
+       error = sysfs_create_group(&pdev->dev.kobj, &p2pmem_group);
+       if (error)
+               goto out_pool_destroy;
+
+       return 0;
+
+out_pool_destroy:
+       pdev->p2pdma = NULL;
+       gen_pool_destroy(p2p->pool);
+out:
+       devm_kfree(&pdev->dev, p2p);
+       return error;
+}
+
+/**
+ * pci_p2pdma_add_resource - add memory for use as p2p memory
+ * @pdev: the device to add the memory to
+ * @bar: PCI BAR to add
+ * @size: size of the memory to add, may be zero to use the whole BAR
+ * @offset: offset into the PCI BAR
+ *
+ * The memory will be given ZONE_DEVICE struct pages so that it may
+ * be used with any DMA request.
+ */
+int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
+                           u64 offset)
+{
+       struct dev_pagemap *pgmap;
+       void *addr;
+       int error;
+
+       if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM))
+               return -EINVAL;
+
+       if (offset >= pci_resource_len(pdev, bar))
+               return -EINVAL;
+
+       if (!size)
+               size = pci_resource_len(pdev, bar) - offset;
+
+       if (size + offset > pci_resource_len(pdev, bar))
+               return -EINVAL;
+
+       if (!pdev->p2pdma) {
+               error = pci_p2pdma_setup(pdev);
+               if (error)
+                       return error;
+       }
+
+       pgmap = devm_kzalloc(&pdev->dev, sizeof(*pgmap), GFP_KERNEL);
+       if (!pgmap)
+               return -ENOMEM;
+
+       pgmap->res.start = pci_resource_start(pdev, bar) + offset;
+       pgmap->res.end = pgmap->res.start + size - 1;
+       pgmap->res.flags = pci_resource_flags(pdev, bar);
+       pgmap->ref = &pdev->p2pdma->devmap_ref;
+       pgmap->type = MEMORY_DEVICE_PCI_P2PDMA;
+       pgmap->pci_p2pdma_bus_offset = pci_bus_address(pdev, bar) -
+               pci_resource_start(pdev, bar);
+
+       addr = devm_memremap_pages(&pdev->dev, pgmap);
+       if (IS_ERR(addr)) {
+               error = PTR_ERR(addr);
+               goto pgmap_free;
+       }
+
+       error = gen_pool_add_virt(pdev->p2pdma->pool, (unsigned long)addr,
+                       pci_bus_address(pdev, bar) + offset,
+                       resource_size(&pgmap->res), dev_to_node(&pdev->dev));
+       if (error)
+               goto pgmap_free;
+
+       error = devm_add_action_or_reset(&pdev->dev, pci_p2pdma_percpu_kill,
+                                         &pdev->p2pdma->devmap_ref);
+       if (error)
+               goto pgmap_free;
+
+       pci_info(pdev, "added peer-to-peer DMA memory %pR\n",
+                &pgmap->res);
+
+       return 0;
+
+pgmap_free:
+       devm_kfree(&pdev->dev, pgmap);
+       return error;
+}
+EXPORT_SYMBOL_GPL(pci_p2pdma_add_resource);
+
+/*
+ * Note this function returns the parent PCI device with a
+ * reference taken. It is the caller's responsibily to drop
+ * the reference.
+ */
+static struct pci_dev *find_parent_pci_dev(struct device *dev)
+{
+       struct device *parent;
+
+       dev = get_device(dev);
+
+       while (dev) {
+               if (dev_is_pci(dev))
+                       return to_pci_dev(dev);
+
+               parent = get_device(dev->parent);
+               put_device(dev);
+               dev = parent;
+       }
+
+       return NULL;
+}
+
+/*
+ * Check if a PCI bridge has its ACS redirection bits set to redirect P2P
+ * TLPs upstream via ACS. Returns 1 if the packets will be redirected
+ * upstream, 0 otherwise.
+ */
+static int pci_bridge_has_acs_redir(struct pci_dev *pdev)
+{
+       int pos;
+       u16 ctrl;
+
+       pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS);
+       if (!pos)
+               return 0;
+
+       pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl);
+
+       if (ctrl & (PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC))
+               return 1;
+
+       return 0;
+}
+
+static void seq_buf_print_bus_devfn(struct seq_buf *buf, struct pci_dev *pdev)
+{
+       if (!buf)
+               return;
+
+       seq_buf_printf(buf, "%s;", pci_name(pdev));
+}
+
+/*
+ * Find the distance through the nearest common upstream bridge between
+ * two PCI devices.
+ *
+ * If the two devices are the same device then 0 will be returned.
+ *
+ * If there are two virtual functions of the same device behind the same
+ * bridge port then 2 will be returned (one step down to the PCIe switch,
+ * then one step back to the same device).
+ *
+ * In the case where two devices are connected to the same PCIe switch, the
+ * value 4 will be returned. This corresponds to the following PCI tree:
+ *
+ *     -+  Root Port
+ *      \+ Switch Upstream Port
+ *       +-+ Switch Downstream Port
+ *       + \- Device A
+ *       \-+ Switch Downstream Port
+ *         \- Device B
+ *
+ * The distance is 4 because we traverse from Device A through the downstream
+ * port of the switch, to the common upstream port, back up to the second
+ * downstream port and then to Device B.
+ *
+ * Any two devices that don't have a common upstream bridge will return -1.
+ * In this way devices on separate PCIe root ports will be rejected, which
+ * is what we want for peer-to-peer seeing each PCIe root port defines a
+ * separate hierarchy domain and there's no way to determine whether the root
+ * complex supports forwarding between them.
+ *
+ * In the case where two devices are connected to different PCIe switches,
+ * this function will still return a positive distance as long as both
+ * switches eventually have a common upstream bridge. Note this covers
+ * the case of using multiple PCIe switches to achieve a desired level of
+ * fan-out from a root port. The exact distance will be a function of the
+ * number of switches between Device A and Device B.
+ *
+ * If a bridge which has any ACS redirection bits set is in the path
+ * then this functions will return -2. This is so we reject any
+ * cases where the TLPs are forwarded up into the root complex.
+ * In this case, a list of all infringing bridge addresses will be
+ * populated in acs_list (assuming it's non-null) for printk purposes.
+ */
+static int upstream_bridge_distance(struct pci_dev *a,
+                                   struct pci_dev *b,
+                                   struct seq_buf *acs_list)
+{
+       int dist_a = 0;
+       int dist_b = 0;
+       struct pci_dev *bb = NULL;
+       int acs_cnt = 0;
+
+       /*
+        * Note, we don't need to take references to devices returned by
+        * pci_upstream_bridge() seeing we hold a reference to a child
+        * device which will already hold a reference to the upstream bridge.
+        */
+
+       while (a) {
+               dist_b = 0;
+
+               if (pci_bridge_has_acs_redir(a)) {
+                       seq_buf_print_bus_devfn(acs_list, a);
+                       acs_cnt++;
+               }
+
+               bb = b;
+
+               while (bb) {
+                       if (a == bb)
+                               goto check_b_path_acs;
+
+                       bb = pci_upstream_bridge(bb);
+                       dist_b++;
+               }
+
+               a = pci_upstream_bridge(a);
+               dist_a++;
+       }
+
+       return -1;
+
+check_b_path_acs:
+       bb = b;
+
+       while (bb) {
+               if (a == bb)
+                       break;
+
+               if (pci_bridge_has_acs_redir(bb)) {
+                       seq_buf_print_bus_devfn(acs_list, bb);
+                       acs_cnt++;
+               }
+
+               bb = pci_upstream_bridge(bb);
+       }
+
+       if (acs_cnt)
+               return -2;
+
+       return dist_a + dist_b;
+}
+
+static int upstream_bridge_distance_warn(struct pci_dev *provider,
+                                        struct pci_dev *client)
+{
+       struct seq_buf acs_list;
+       int ret;
+
+       seq_buf_init(&acs_list, kmalloc(PAGE_SIZE, GFP_KERNEL), PAGE_SIZE);
+       if (!acs_list.buffer)
+               return -ENOMEM;
+
+       ret = upstream_bridge_distance(provider, client, &acs_list);
+       if (ret == -2) {
+               pci_warn(client, "cannot be used for peer-to-peer DMA as ACS redirect is set between the client and provider (%s)\n",
+                        pci_name(provider));
+               /* Drop final semicolon */
+               acs_list.buffer[acs_list.len-1] = 0;
+               pci_warn(client, "to disable ACS redirect for this path, add the kernel parameter: pci=disable_acs_redir=%s\n",
+                        acs_list.buffer);
+
+       } else if (ret < 0) {
+               pci_warn(client, "cannot be used for peer-to-peer DMA as the client and provider (%s) do not share an upstream bridge\n",
+                        pci_name(provider));
+       }
+
+       kfree(acs_list.buffer);
+
+       return ret;
+}
+
+/**
+ * pci_p2pdma_distance_many - Determive the cumulative distance between
+ *     a p2pdma provider and the clients in use.
+ * @provider: p2pdma provider to check against the client list
+ * @clients: array of devices to check (NULL-terminated)
+ * @num_clients: number of clients in the array
+ * @verbose: if true, print warnings for devices when we return -1
+ *
+ * Returns -1 if any of the clients are not compatible (behind the same
+ * root port as the provider), otherwise returns a positive number where
+ * a lower number is the preferrable choice. (If there's one client
+ * that's the same as the provider it will return 0, which is best choice).
+ *
+ * For now, "compatible" means the provider and the clients are all behind
+ * the same PCI root port. This cuts out cases that may work but is safest
+ * for the user. Future work can expand this to white-list root complexes that
+ * can safely forward between each ports.
+ */
+int pci_p2pdma_distance_many(struct pci_dev *provider, struct device **clients,
+                            int num_clients, bool verbose)
+{
+       bool not_supported = false;
+       struct pci_dev *pci_client;
+       int distance = 0;
+       int i, ret;
+
+       if (num_clients == 0)
+               return -1;
+
+       for (i = 0; i < num_clients; i++) {
+               pci_client = find_parent_pci_dev(clients[i]);
+               if (!pci_client) {
+                       if (verbose)
+                               dev_warn(clients[i],
+                                        "cannot be used for peer-to-peer DMA as it is not a PCI device\n");
+                       return -1;
+               }
+
+               if (verbose)
+                       ret = upstream_bridge_distance_warn(provider,
+                                                           pci_client);
+               else
+                       ret = upstream_bridge_distance(provider, pci_client,
+                                                      NULL);
+
+               pci_dev_put(pci_client);
+
+               if (ret < 0)
+                       not_supported = true;
+
+               if (not_supported && !verbose)
+                       break;
+
+               distance += ret;
+       }
+
+       if (not_supported)
+               return -1;
+
+       return distance;
+}
+EXPORT_SYMBOL_GPL(pci_p2pdma_distance_many);
+
+/**
+ * pci_has_p2pmem - check if a given PCI device has published any p2pmem
+ * @pdev: PCI device to check
+ */
+bool pci_has_p2pmem(struct pci_dev *pdev)
+{
+       return pdev->p2pdma && pdev->p2pdma->p2pmem_published;
+}
+EXPORT_SYMBOL_GPL(pci_has_p2pmem);
+
+/**
+ * pci_p2pmem_find - find a peer-to-peer DMA memory device compatible with
+ *     the specified list of clients and shortest distance (as determined
+ *     by pci_p2pmem_dma())
+ * @clients: array of devices to check (NULL-terminated)
+ * @num_clients: number of client devices in the list
+ *
+ * If multiple devices are behind the same switch, the one "closest" to the
+ * client devices in use will be chosen first. (So if one of the providers are
+ * the same as one of the clients, that provider will be used ahead of any
+ * other providers that are unrelated). If multiple providers are an equal
+ * distance away, one will be chosen at random.
+ *
+ * Returns a pointer to the PCI device with a reference taken (use pci_dev_put
+ * to return the reference) or NULL if no compatible device is found. The
+ * found provider will also be assigned to the client list.
+ */
+struct pci_dev *pci_p2pmem_find_many(struct device **clients, int num_clients)
+{
+       struct pci_dev *pdev = NULL;
+       int distance;
+       int closest_distance = INT_MAX;
+       struct pci_dev **closest_pdevs;
+       int dev_cnt = 0;
+       const int max_devs = PAGE_SIZE / sizeof(*closest_pdevs);
+       int i;
+
+       closest_pdevs = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!closest_pdevs)
+               return NULL;
+
+       while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev))) {
+               if (!pci_has_p2pmem(pdev))
+                       continue;
+
+               distance = pci_p2pdma_distance_many(pdev, clients,
+                                                   num_clients, false);
+               if (distance < 0 || distance > closest_distance)
+                       continue;
+
+               if (distance == closest_distance && dev_cnt >= max_devs)
+                       continue;
+
+               if (distance < closest_distance) {
+                       for (i = 0; i < dev_cnt; i++)
+                               pci_dev_put(closest_pdevs[i]);
+
+                       dev_cnt = 0;
+                       closest_distance = distance;
+               }
+
+               closest_pdevs[dev_cnt++] = pci_dev_get(pdev);
+       }
+
+       if (dev_cnt)
+               pdev = pci_dev_get(closest_pdevs[prandom_u32_max(dev_cnt)]);
+
+       for (i = 0; i < dev_cnt; i++)
+               pci_dev_put(closest_pdevs[i]);
+
+       kfree(closest_pdevs);
+       return pdev;
+}
+EXPORT_SYMBOL_GPL(pci_p2pmem_find_many);
+
+/**
+ * pci_alloc_p2p_mem - allocate peer-to-peer DMA memory
+ * @pdev: the device to allocate memory from
+ * @size: number of bytes to allocate
+ *
+ * Returns the allocated memory or NULL on error.
+ */
+void *pci_alloc_p2pmem(struct pci_dev *pdev, size_t size)
+{
+       void *ret;
+
+       if (unlikely(!pdev->p2pdma))
+               return NULL;
+
+       if (unlikely(!percpu_ref_tryget_live(&pdev->p2pdma->devmap_ref)))
+               return NULL;
+
+       ret = (void *)gen_pool_alloc(pdev->p2pdma->pool, size);
+
+       if (unlikely(!ret))
+               percpu_ref_put(&pdev->p2pdma->devmap_ref);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(pci_alloc_p2pmem);
+
+/**
+ * pci_free_p2pmem - free peer-to-peer DMA memory
+ * @pdev: the device the memory was allocated from
+ * @addr: address of the memory that was allocated
+ * @size: number of bytes that was allocated
+ */
+void pci_free_p2pmem(struct pci_dev *pdev, void *addr, size_t size)
+{
+       gen_pool_free(pdev->p2pdma->pool, (uintptr_t)addr, size);
+       percpu_ref_put(&pdev->p2pdma->devmap_ref);
+}
+EXPORT_SYMBOL_GPL(pci_free_p2pmem);
+
+/**
+ * pci_virt_to_bus - return the PCI bus address for a given virtual
+ *     address obtained with pci_alloc_p2pmem()
+ * @pdev: the device the memory was allocated from
+ * @addr: address of the memory that was allocated
+ */
+pci_bus_addr_t pci_p2pmem_virt_to_bus(struct pci_dev *pdev, void *addr)
+{
+       if (!addr)
+               return 0;
+       if (!pdev->p2pdma)
+               return 0;
+
+       /*
+        * Note: when we added the memory to the pool we used the PCI
+        * bus address as the physical address. So gen_pool_virt_to_phys()
+        * actually returns the bus address despite the misleading name.
+        */
+       return gen_pool_virt_to_phys(pdev->p2pdma->pool, (unsigned long)addr);
+}
+EXPORT_SYMBOL_GPL(pci_p2pmem_virt_to_bus);
+
+/**
+ * pci_p2pmem_alloc_sgl - allocate peer-to-peer DMA memory in a scatterlist
+ * @pdev: the device to allocate memory from
+ * @nents: the number of SG entries in the list
+ * @length: number of bytes to allocate
+ *
+ * Returns 0 on success
+ */
+struct scatterlist *pci_p2pmem_alloc_sgl(struct pci_dev *pdev,
+                                        unsigned int *nents, u32 length)
+{
+       struct scatterlist *sg;
+       void *addr;
+
+       sg = kzalloc(sizeof(*sg), GFP_KERNEL);
+       if (!sg)
+               return NULL;
+
+       sg_init_table(sg, 1);
+
+       addr = pci_alloc_p2pmem(pdev, length);
+       if (!addr)
+               goto out_free_sg;
+
+       sg_set_buf(sg, addr, length);
+       *nents = 1;
+       return sg;
+
+out_free_sg:
+       kfree(sg);
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(pci_p2pmem_alloc_sgl);
+
+/**
+ * pci_p2pmem_free_sgl - free a scatterlist allocated by pci_p2pmem_alloc_sgl()
+ * @pdev: the device to allocate memory from
+ * @sgl: the allocated scatterlist
+ */
+void pci_p2pmem_free_sgl(struct pci_dev *pdev, struct scatterlist *sgl)
+{
+       struct scatterlist *sg;
+       int count;
+
+       for_each_sg(sgl, sg, INT_MAX, count) {
+               if (!sg)
+                       break;
+
+               pci_free_p2pmem(pdev, sg_virt(sg), sg->length);
+       }
+       kfree(sgl);
+}
+EXPORT_SYMBOL_GPL(pci_p2pmem_free_sgl);
+
+/**
+ * pci_p2pmem_publish - publish the peer-to-peer DMA memory for use by
+ *     other devices with pci_p2pmem_find()
+ * @pdev: the device with peer-to-peer DMA memory to publish
+ * @publish: set to true to publish the memory, false to unpublish it
+ *
+ * Published memory can be used by other PCI device drivers for
+ * peer-2-peer DMA operations. Non-published memory is reserved for
+ * exlusive use of the device driver that registers the peer-to-peer
+ * memory.
+ */
+void pci_p2pmem_publish(struct pci_dev *pdev, bool publish)
+{
+       if (pdev->p2pdma)
+               pdev->p2pdma->p2pmem_published = publish;
+}
+EXPORT_SYMBOL_GPL(pci_p2pmem_publish);
+
+/**
+ * pci_p2pdma_map_sg - map a PCI peer-to-peer scatterlist for DMA
+ * @dev: device doing the DMA request
+ * @sg: scatter list to map
+ * @nents: elements in the scatterlist
+ * @dir: DMA direction
+ *
+ * Scatterlists mapped with this function should not be unmapped in any way.
+ *
+ * Returns the number of SG entries mapped or 0 on error.
+ */
+int pci_p2pdma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+                     enum dma_data_direction dir)
+{
+       struct dev_pagemap *pgmap;
+       struct scatterlist *s;
+       phys_addr_t paddr;
+       int i;
+
+       /*
+        * p2pdma mappings are not compatible with devices that use
+        * dma_virt_ops. If the upper layers do the right thing
+        * this should never happen because it will be prevented
+        * by the check in pci_p2pdma_add_client()
+        */
+       if (WARN_ON_ONCE(IS_ENABLED(CONFIG_DMA_VIRT_OPS) &&
+                        dev->dma_ops == &dma_virt_ops))
+               return 0;
+
+       for_each_sg(sg, s, nents, i) {
+               pgmap = sg_page(s)->pgmap;
+               paddr = sg_phys(s);
+
+               s->dma_address = paddr - pgmap->pci_p2pdma_bus_offset;
+               sg_dma_len(s) = s->length;
+       }
+
+       return nents;
+}
+EXPORT_SYMBOL_GPL(pci_p2pdma_map_sg);
+
+/**
+ * pci_p2pdma_enable_store - parse a configfs/sysfs attribute store
+ *             to enable p2pdma
+ * @page: contents of the value to be stored
+ * @p2p_dev: returns the PCI device that was selected to be used
+ *             (if one was specified in the stored value)
+ * @use_p2pdma: returns whether to enable p2pdma or not
+ *
+ * Parses an attribute value to decide whether to enable p2pdma.
+ * The value can select a PCI device (using it's full BDF device
+ * name) or a boolean (in any format strtobool() accepts). A false
+ * value disables p2pdma, a true value expects the caller
+ * to automatically find a compatible device and specifying a PCI device
+ * expects the caller to use the specific provider.
+ *
+ * pci_p2pdma_enable_show() should be used as the show operation for
+ * the attribute.
+ *
+ * Returns 0 on success
+ */
+int pci_p2pdma_enable_store(const char *page, struct pci_dev **p2p_dev,
+                           bool *use_p2pdma)
+{
+       struct device *dev;
+
+       dev = bus_find_device_by_name(&pci_bus_type, NULL, page);
+       if (dev) {
+               *use_p2pdma = true;
+               *p2p_dev = to_pci_dev(dev);
+
+               if (!pci_has_p2pmem(*p2p_dev)) {
+                       pci_err(*p2p_dev,
+                               "PCI device has no peer-to-peer memory: %s\n",
+                               page);
+                       pci_dev_put(*p2p_dev);
+                       return -ENODEV;
+               }
+
+               return 0;
+       } else if ((page[0] == '0' || page[0] == '1') && !iscntrl(page[1])) {
+               /*
+                * If the user enters a PCI device that  doesn't exist
+                * like "0000:01:00.1", we don't want strtobool to think
+                * it's a '0' when it's clearly not what the user wanted.
+                * So we require 0's and 1's to be exactly one character.
+                */
+       } else if (!strtobool(page, use_p2pdma)) {
+               return 0;
+       }
+
+       pr_err("No such PCI device: %.*s\n", (int)strcspn(page, "\n"), page);
+       return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(pci_p2pdma_enable_store);
+
+/**
+ * pci_p2pdma_enable_show - show a configfs/sysfs attribute indicating
+ *             whether p2pdma is enabled
+ * @page: contents of the stored value
+ * @p2p_dev: the selected p2p device (NULL if no device is selected)
+ * @use_p2pdma: whether p2pdme has been enabled
+ *
+ * Attributes that use pci_p2pdma_enable_store() should use this function
+ * to show the value of the attribute.
+ *
+ * Returns 0 on success
+ */
+ssize_t pci_p2pdma_enable_show(char *page, struct pci_dev *p2p_dev,
+                              bool use_p2pdma)
+{
+       if (!use_p2pdma)
+               return sprintf(page, "0\n");
+
+       if (!p2p_dev)
+               return sprintf(page, "1\n");
+
+       return sprintf(page, "%s\n", pci_name(p2p_dev));
+}
+EXPORT_SYMBOL_GPL(pci_p2pdma_enable_show);
index c2ab577050434bb602fd8c4c8252547b90a9ff4f..2a4aa64685794434f9ffdc5c38613035e63038f3 100644 (file)
@@ -519,6 +519,46 @@ static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev)
        return PCI_POWER_ERROR;
 }
 
+static struct acpi_device *acpi_pci_find_companion(struct device *dev);
+
+static bool acpi_pci_bridge_d3(struct pci_dev *dev)
+{
+       const struct fwnode_handle *fwnode;
+       struct acpi_device *adev;
+       struct pci_dev *root;
+       u8 val;
+
+       if (!dev->is_hotplug_bridge)
+               return false;
+
+       /*
+        * Look for a special _DSD property for the root port and if it
+        * is set we know the hierarchy behind it supports D3 just fine.
+        */
+       root = pci_find_pcie_root_port(dev);
+       if (!root)
+               return false;
+
+       adev = ACPI_COMPANION(&root->dev);
+       if (root == dev) {
+               /*
+                * It is possible that the ACPI companion is not yet bound
+                * for the root port so look it up manually here.
+                */
+               if (!adev && !pci_dev_is_added(root))
+                       adev = acpi_pci_find_companion(&root->dev);
+       }
+
+       if (!adev)
+               return false;
+
+       fwnode = acpi_fwnode_handle(adev);
+       if (fwnode_property_read_u8(fwnode, "HotPlugSupportInD3", &val))
+               return false;
+
+       return val == 1;
+}
+
 static bool acpi_pci_power_manageable(struct pci_dev *dev)
 {
        struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
@@ -548,6 +588,7 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
                        error = -EBUSY;
                        break;
                }
+               /* Fall through */
        case PCI_D0:
        case PCI_D1:
        case PCI_D2:
@@ -635,6 +676,7 @@ static bool acpi_pci_need_resume(struct pci_dev *dev)
 }
 
 static const struct pci_platform_pm_ops acpi_pci_platform_pm = {
+       .bridge_d3 = acpi_pci_bridge_d3,
        .is_manageable = acpi_pci_power_manageable,
        .set_state = acpi_pci_set_power_state,
        .get_state = acpi_pci_get_power_state,
@@ -751,10 +793,15 @@ static void pci_acpi_setup(struct device *dev)
 {
        struct pci_dev *pci_dev = to_pci_dev(dev);
        struct acpi_device *adev = ACPI_COMPANION(dev);
+       int node;
 
        if (!adev)
                return;
 
+       node = acpi_get_node(adev->handle);
+       if (node != NUMA_NO_NODE)
+               set_dev_node(dev, node);
+
        pci_acpi_optimize_delay(pci_dev, adev->handle);
 
        pci_acpi_add_pm_notifier(adev, pci_dev);
@@ -762,19 +809,33 @@ static void pci_acpi_setup(struct device *dev)
                return;
 
        device_set_wakeup_capable(dev, true);
+       /*
+        * For bridges that can do D3 we enable wake automatically (as
+        * we do for the power management itself in that case). The
+        * reason is that the bridge may have additional methods such as
+        * _DSW that need to be called.
+        */
+       if (pci_dev->bridge_d3)
+               device_wakeup_enable(dev);
+
        acpi_pci_wakeup(pci_dev, false);
 }
 
 static void pci_acpi_cleanup(struct device *dev)
 {
        struct acpi_device *adev = ACPI_COMPANION(dev);
+       struct pci_dev *pci_dev = to_pci_dev(dev);
 
        if (!adev)
                return;
 
        pci_acpi_remove_pm_notifier(adev);
-       if (adev->wakeup.flags.valid)
+       if (adev->wakeup.flags.valid) {
+               if (pci_dev->bridge_d3)
+                       device_wakeup_disable(dev);
+
                device_set_wakeup_capable(dev, false);
+       }
 }
 
 static bool pci_acpi_bus_match(struct device *dev)
index 1835f3a7aa8d2f5a502a0629bfb0c0cc96420dd4..535c9f974de293f0b6d71fb447d549fdd370cbf7 100644 (file)
@@ -35,6 +35,8 @@
 #include <linux/aer.h>
 #include "pci.h"
 
+DEFINE_MUTEX(pci_slot_mutex);
+
 const char *pci_power_names[] = {
        "error", "D0", "D1", "D2", "D3hot", "D3cold", "unknown",
 };
@@ -196,7 +198,7 @@ EXPORT_SYMBOL_GPL(pci_ioremap_wc_bar);
 /**
  * pci_dev_str_match_path - test if a path string matches a device
  * @dev:    the PCI device to test
- * @p:      string to match the device against
+ * @path:   string to match the device against
  * @endptr: pointer to the string after the match
  *
  * Test if a string (typically from a kernel parameter) formatted as a
@@ -791,6 +793,11 @@ static inline bool platform_pci_need_resume(struct pci_dev *dev)
        return pci_platform_pm ? pci_platform_pm->need_resume(dev) : false;
 }
 
+static inline bool platform_pci_bridge_d3(struct pci_dev *dev)
+{
+       return pci_platform_pm ? pci_platform_pm->bridge_d3(dev) : false;
+}
+
 /**
  * pci_raw_set_power_state - Use PCI PM registers to set the power state of
  *                           given PCI device
@@ -999,7 +1006,7 @@ static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state)
                 * because have already delayed for the bridge.
                 */
                if (dev->runtime_d3cold) {
-                       if (dev->d3cold_delay)
+                       if (dev->d3cold_delay && !dev->imm_ready)
                                msleep(dev->d3cold_delay);
                        /*
                         * When powering on a bridge from D3cold, the
@@ -1284,6 +1291,7 @@ int pci_save_state(struct pci_dev *dev)
        if (i != 0)
                return i;
 
+       pci_save_dpc_state(dev);
        return pci_save_vc_state(dev);
 }
 EXPORT_SYMBOL(pci_save_state);
@@ -1378,6 +1386,7 @@ void pci_restore_state(struct pci_dev *dev)
        pci_restore_ats_state(dev);
        pci_restore_vc_state(dev);
        pci_restore_rebar_state(dev);
+       pci_restore_dpc_state(dev);
 
        pci_cleanup_aer_error_status_regs(dev);
 
@@ -2133,10 +2142,13 @@ static int __pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable
        int ret = 0;
 
        /*
-        * Bridges can only signal wakeup on behalf of subordinate devices,
-        * but that is set up elsewhere, so skip them.
+        * Bridges that are not power-manageable directly only signal
+        * wakeup on behalf of subordinate devices which is set up
+        * elsewhere, so skip them. However, bridges that are
+        * power-manageable may signal wakeup for themselves (for example,
+        * on a hotplug event) and they need to be covered here.
         */
-       if (pci_has_subordinate(dev))
+       if (!pci_power_manageable(dev))
                return 0;
 
        /* Don't do the same thing twice in a row for one device. */
@@ -2511,6 +2523,10 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge)
                if (bridge->is_thunderbolt)
                        return true;
 
+               /* Platform might know better if the bridge supports D3 */
+               if (platform_pci_bridge_d3(bridge))
+                       return true;
+
                /*
                 * Hotplug ports handled natively by the OS were not validated
                 * by vendors for runtime D3 at least until 2018 because there
@@ -2644,6 +2660,7 @@ EXPORT_SYMBOL_GPL(pci_d3cold_disable);
 void pci_pm_init(struct pci_dev *dev)
 {
        int pm;
+       u16 status;
        u16 pmc;
 
        pm_runtime_forbid(&dev->dev);
@@ -2706,6 +2723,10 @@ void pci_pm_init(struct pci_dev *dev)
                /* Disable the PME# generation functionality */
                pci_pme_active(dev, false);
        }
+
+       pci_read_config_word(dev, PCI_STATUS, &status);
+       if (status & PCI_STATUS_IMM_READY)
+               dev->imm_ready = 1;
 }
 
 static unsigned long pci_ea_flags(struct pci_dev *dev, u8 prop)
@@ -4376,6 +4397,9 @@ int pcie_flr(struct pci_dev *dev)
 
        pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR);
 
+       if (dev->imm_ready)
+               return 0;
+
        /*
         * Per PCIe r4.0, sec 6.6.2, a device must complete an FLR within
         * 100ms, but may silently discard requests while the FLR is in
@@ -4417,6 +4441,9 @@ static int pci_af_flr(struct pci_dev *dev, int probe)
 
        pci_write_config_byte(dev, pos + PCI_AF_CTRL, PCI_AF_CTRL_FLR);
 
+       if (dev->imm_ready)
+               return 0;
+
        /*
         * Per Advanced Capabilities for Conventional PCI ECN, 13 April 2006,
         * updated 27 July 2006; a device must complete an FLR within
@@ -4485,21 +4512,42 @@ bool pcie_wait_for_link(struct pci_dev *pdev, bool active)
        bool ret;
        u16 lnk_status;
 
+       /*
+        * Some controllers might not implement link active reporting. In this
+        * case, we wait for 1000 + 100 ms.
+        */
+       if (!pdev->link_active_reporting) {
+               msleep(1100);
+               return true;
+       }
+
+       /*
+        * PCIe r4.0 sec 6.6.1, a component must enter LTSSM Detect within 20ms,
+        * after which we should expect an link active if the reset was
+        * successful. If so, software must wait a minimum 100ms before sending
+        * configuration requests to devices downstream this port.
+        *
+        * If the link fails to activate, either the device was physically
+        * removed or the link is permanently failed.
+        */
+       if (active)
+               msleep(20);
        for (;;) {
                pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
                ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA);
                if (ret == active)
-                       return true;
+                       break;
                if (timeout <= 0)
                        break;
                msleep(10);
                timeout -= 10;
        }
-
-       pci_info(pdev, "Data Link Layer Link Active not %s in 1000 msec\n",
-                active ? "set" : "cleared");
-
-       return false;
+       if (active && ret)
+               msleep(100);
+       else if (ret != active)
+               pci_info(pdev, "Data Link Layer Link Active not %s in 1000 msec\n",
+                       active ? "set" : "cleared");
+       return ret == active;
 }
 
 void pci_reset_secondary_bus(struct pci_dev *dev)
@@ -4571,13 +4619,13 @@ static int pci_reset_hotplug_slot(struct hotplug_slot *hotplug, int probe)
 {
        int rc = -ENOTTY;
 
-       if (!hotplug || !try_module_get(hotplug->ops->owner))
+       if (!hotplug || !try_module_get(hotplug->owner))
                return rc;
 
        if (hotplug->ops->reset_slot)
                rc = hotplug->ops->reset_slot(hotplug, probe);
 
-       module_put(hotplug->ops->owner);
+       module_put(hotplug->owner);
 
        return rc;
 }
@@ -5153,6 +5201,41 @@ static int pci_bus_reset(struct pci_bus *bus, int probe)
        return ret;
 }
 
+/**
+ * pci_bus_error_reset - reset the bridge's subordinate bus
+ * @bridge: The parent device that connects to the bus to reset
+ *
+ * This function will first try to reset the slots on this bus if the method is
+ * available. If slot reset fails or is not available, this will fall back to a
+ * secondary bus reset.
+ */
+int pci_bus_error_reset(struct pci_dev *bridge)
+{
+       struct pci_bus *bus = bridge->subordinate;
+       struct pci_slot *slot;
+
+       if (!bus)
+               return -ENOTTY;
+
+       mutex_lock(&pci_slot_mutex);
+       if (list_empty(&bus->slots))
+               goto bus_reset;
+
+       list_for_each_entry(slot, &bus->slots, list)
+               if (pci_probe_reset_slot(slot))
+                       goto bus_reset;
+
+       list_for_each_entry(slot, &bus->slots, list)
+               if (pci_slot_reset(slot, 0))
+                       goto bus_reset;
+
+       mutex_unlock(&pci_slot_mutex);
+       return 0;
+bus_reset:
+       mutex_unlock(&pci_slot_mutex);
+       return pci_bus_reset(bridge->subordinate, 0);
+}
+
 /**
  * pci_probe_reset_bus - probe whether a PCI bus can be reset
  * @bus: PCI bus to probe
@@ -5690,8 +5773,7 @@ int pci_set_vga_state(struct pci_dev *dev, bool decode,
 void pci_add_dma_alias(struct pci_dev *dev, u8 devfn)
 {
        if (!dev->dma_alias_mask)
-               dev->dma_alias_mask = kcalloc(BITS_TO_LONGS(U8_MAX),
-                                             sizeof(long), GFP_KERNEL);
+               dev->dma_alias_mask = bitmap_zalloc(U8_MAX, GFP_KERNEL);
        if (!dev->dma_alias_mask) {
                pci_warn(dev, "Unable to allocate DMA alias mask\n");
                return;
index 2f1454209257984a18bf764ecd23132cc6866dc4..662b7457db2374d9c7fd039d2e40a54cfda5dc5b 100644 (file)
@@ -35,10 +35,13 @@ int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai,
 
 int pci_probe_reset_function(struct pci_dev *dev);
 int pci_bridge_secondary_bus_reset(struct pci_dev *dev);
+int pci_bus_error_reset(struct pci_dev *dev);
 
 /**
  * struct pci_platform_pm_ops - Firmware PM callbacks
  *
+ * @bridge_d3: Does the bridge allow entering into D3
+ *
  * @is_manageable: returns 'true' if given device is power manageable by the
  *                platform firmware
  *
@@ -60,6 +63,7 @@ int pci_bridge_secondary_bus_reset(struct pci_dev *dev);
  * these callbacks are mandatory.
  */
 struct pci_platform_pm_ops {
+       bool (*bridge_d3)(struct pci_dev *dev);
        bool (*is_manageable)(struct pci_dev *dev);
        int (*set_state)(struct pci_dev *dev, pci_power_t state);
        pci_power_t (*get_state)(struct pci_dev *dev);
@@ -136,6 +140,7 @@ static inline void pci_remove_legacy_files(struct pci_bus *bus) { return; }
 
 /* Lock for read/write access to pci device and bus lists */
 extern struct rw_semaphore pci_bus_sem;
+extern struct mutex pci_slot_mutex;
 
 extern raw_spinlock_t pci_lock;
 
@@ -294,21 +299,71 @@ struct pci_sriov {
        bool            drivers_autoprobe; /* Auto probing of VFs by driver */
 };
 
-/* pci_dev priv_flags */
-#define PCI_DEV_DISCONNECTED 0
-#define PCI_DEV_ADDED 1
+/**
+ * pci_dev_set_io_state - Set the new error state if possible.
+ *
+ * @dev - pci device to set new error_state
+ * @new - the state we want dev to be in
+ *
+ * Must be called with device_lock held.
+ *
+ * Returns true if state has been changed to the requested state.
+ */
+static inline bool pci_dev_set_io_state(struct pci_dev *dev,
+                                       pci_channel_state_t new)
+{
+       bool changed = false;
+
+       device_lock_assert(&dev->dev);
+       switch (new) {
+       case pci_channel_io_perm_failure:
+               switch (dev->error_state) {
+               case pci_channel_io_frozen:
+               case pci_channel_io_normal:
+               case pci_channel_io_perm_failure:
+                       changed = true;
+                       break;
+               }
+               break;
+       case pci_channel_io_frozen:
+               switch (dev->error_state) {
+               case pci_channel_io_frozen:
+               case pci_channel_io_normal:
+                       changed = true;
+                       break;
+               }
+               break;
+       case pci_channel_io_normal:
+               switch (dev->error_state) {
+               case pci_channel_io_frozen:
+               case pci_channel_io_normal:
+                       changed = true;
+                       break;
+               }
+               break;
+       }
+       if (changed)
+               dev->error_state = new;
+       return changed;
+}
 
 static inline int pci_dev_set_disconnected(struct pci_dev *dev, void *unused)
 {
-       set_bit(PCI_DEV_DISCONNECTED, &dev->priv_flags);
+       device_lock(&dev->dev);
+       pci_dev_set_io_state(dev, pci_channel_io_perm_failure);
+       device_unlock(&dev->dev);
+
        return 0;
 }
 
 static inline bool pci_dev_is_disconnected(const struct pci_dev *dev)
 {
-       return test_bit(PCI_DEV_DISCONNECTED, &dev->priv_flags);
+       return dev->error_state == pci_channel_io_perm_failure;
 }
 
+/* pci_dev priv_flags */
+#define PCI_DEV_ADDED 0
+
 static inline void pci_dev_assign_added(struct pci_dev *dev, bool added)
 {
        assign_bit(PCI_DEV_ADDED, &dev->priv_flags, added);
@@ -347,6 +402,14 @@ int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info);
 void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
 #endif /* CONFIG_PCIEAER */
 
+#ifdef CONFIG_PCIE_DPC
+void pci_save_dpc_state(struct pci_dev *dev);
+void pci_restore_dpc_state(struct pci_dev *dev);
+#else
+static inline void pci_save_dpc_state(struct pci_dev *dev) {}
+static inline void pci_restore_dpc_state(struct pci_dev *dev) {}
+#endif
+
 #ifdef CONFIG_PCI_ATS
 void pci_restore_ats_state(struct pci_dev *dev);
 #else
@@ -424,8 +487,8 @@ static inline int pci_dev_specific_disable_acs_redir(struct pci_dev *dev)
 #endif
 
 /* PCI error reporting and recovery */
-void pcie_do_fatal_recovery(struct pci_dev *dev, u32 service);
-void pcie_do_nonfatal_recovery(struct pci_dev *dev);
+void pcie_do_recovery(struct pci_dev *dev, enum pci_channel_state state,
+                     u32 service);
 
 bool pcie_wait_for_link(struct pci_dev *pdev, bool active);
 #ifdef CONFIG_PCIEASPM
index 0a1e9d379bc56e4fa7ed664b140aaaab88928a5a..44742b2e1126f9ecad39dbb4fce3de3162056694 100644 (file)
@@ -36,7 +36,6 @@ config PCIEAER
 config PCIEAER_INJECT
        tristate "PCI Express error injection support"
        depends on PCIEAER
-       default n
        help
          This enables PCI Express Root Port Advanced Error Reporting
          (AER) software error injector.
@@ -84,7 +83,6 @@ config PCIEASPM
 config PCIEASPM_DEBUG
        bool "Debug PCI Express ASPM"
        depends on PCIEASPM
-       default n
        help
          This enables PCI Express ASPM debug support. It will add per-device
          interface to control ASPM.
@@ -129,7 +127,6 @@ config PCIE_PME
 config PCIE_DPC
        bool "PCI Express Downstream Port Containment support"
        depends on PCIEPORTBUS && PCIEAER
-       default n
        help
          This enables PCI Express Downstream Port Containment (DPC)
          driver support.  DPC events from Root and Downstream ports
@@ -139,7 +136,6 @@ config PCIE_DPC
 
 config PCIE_PTM
        bool "PCI Express Precision Time Measurement support"
-       default n
        depends on PCIEPORTBUS
        help
          This enables PCI Express Precision Time Measurement (PTM)
index 83180edd6ed470d5fa57f08833e8178d91a18d92..a90a9194ac4a8304df3076cb40dc32d8bc0605c7 100644 (file)
@@ -30,7 +30,7 @@
 #include "../pci.h"
 #include "portdrv.h"
 
-#define AER_ERROR_SOURCES_MAX          100
+#define AER_ERROR_SOURCES_MAX          128
 
 #define AER_MAX_TYPEOF_COR_ERRS                16      /* as per PCI_ERR_COR_STATUS */
 #define AER_MAX_TYPEOF_UNCOR_ERRS      26      /* as per PCI_ERR_UNCOR_STATUS*/
@@ -42,21 +42,7 @@ struct aer_err_source {
 
 struct aer_rpc {
        struct pci_dev *rpd;            /* Root Port device */
-       struct work_struct dpc_handler;
-       struct aer_err_source e_sources[AER_ERROR_SOURCES_MAX];
-       struct aer_err_info e_info;
-       unsigned short prod_idx;        /* Error Producer Index */
-       unsigned short cons_idx;        /* Error Consumer Index */
-       int isr;
-       spinlock_t e_lock;              /*
-                                        * Lock access to Error Status/ID Regs
-                                        * and error producer/consumer index
-                                        */
-       struct mutex rpc_mutex;         /*
-                                        * only one thread could do
-                                        * recovery on the same
-                                        * root port hierarchy
-                                        */
+       DECLARE_KFIFO(aer_fifo, struct aer_err_source, AER_ERROR_SOURCES_MAX);
 };
 
 /* AER stats for the device */
@@ -866,7 +852,7 @@ void cper_print_aer(struct pci_dev *dev, int aer_severity,
 static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev)
 {
        if (e_info->error_dev_num < AER_MAX_MULTI_ERR_DEVICES) {
-               e_info->dev[e_info->error_dev_num] = dev;
+               e_info->dev[e_info->error_dev_num] = pci_dev_get(dev);
                e_info->error_dev_num++;
                return 0;
        }
@@ -1010,9 +996,12 @@ static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
                                        info->status);
                pci_aer_clear_device_status(dev);
        } else if (info->severity == AER_NONFATAL)
-               pcie_do_nonfatal_recovery(dev);
+               pcie_do_recovery(dev, pci_channel_io_normal,
+                                PCIE_PORT_SERVICE_AER);
        else if (info->severity == AER_FATAL)
-               pcie_do_fatal_recovery(dev, PCIE_PORT_SERVICE_AER);
+               pcie_do_recovery(dev, pci_channel_io_frozen,
+                                PCIE_PORT_SERVICE_AER);
+       pci_dev_put(dev);
 }
 
 #ifdef CONFIG_ACPI_APEI_PCIEAER
@@ -1047,9 +1036,11 @@ static void aer_recover_work_func(struct work_struct *work)
                }
                cper_print_aer(pdev, entry.severity, entry.regs);
                if (entry.severity == AER_NONFATAL)
-                       pcie_do_nonfatal_recovery(pdev);
+                       pcie_do_recovery(pdev, pci_channel_io_normal,
+                                        PCIE_PORT_SERVICE_AER);
                else if (entry.severity == AER_FATAL)
-                       pcie_do_fatal_recovery(pdev, PCIE_PORT_SERVICE_AER);
+                       pcie_do_recovery(pdev, pci_channel_io_frozen,
+                                        PCIE_PORT_SERVICE_AER);
                pci_dev_put(pdev);
        }
 }
@@ -1065,7 +1056,6 @@ static DECLARE_WORK(aer_recover_work, aer_recover_work_func);
 void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
                       int severity, struct aer_capability_regs *aer_regs)
 {
-       unsigned long flags;
        struct aer_recover_entry entry = {
                .bus            = bus,
                .devfn          = devfn,
@@ -1074,13 +1064,12 @@ void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
                .regs           = aer_regs,
        };
 
-       spin_lock_irqsave(&aer_recover_ring_lock, flags);
-       if (kfifo_put(&aer_recover_ring, entry))
+       if (kfifo_in_spinlocked(&aer_recover_ring, &entry, sizeof(entry),
+                                &aer_recover_ring_lock))
                schedule_work(&aer_recover_work);
        else
                pr_err("AER recover: Buffer overflow when recovering AER for %04x:%02x:%02x:%x\n",
                       domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
-       spin_unlock_irqrestore(&aer_recover_ring_lock, flags);
 }
 EXPORT_SYMBOL_GPL(aer_recover_queue);
 #endif
@@ -1115,8 +1104,9 @@ int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
                        &info->mask);
                if (!(info->status & ~info->mask))
                        return 0;
-       } else if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
-               info->severity == AER_NONFATAL) {
+       } else if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
+                  pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM ||
+                  info->severity == AER_NONFATAL) {
 
                /* Link is still healthy for IO reads */
                pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS,
@@ -1170,7 +1160,7 @@ static void aer_isr_one_error(struct aer_rpc *rpc,
                struct aer_err_source *e_src)
 {
        struct pci_dev *pdev = rpc->rpd;
-       struct aer_err_info *e_info = &rpc->e_info;
+       struct aer_err_info e_info;
 
        pci_rootport_aer_stats_incr(pdev, e_src);
 
@@ -1179,83 +1169,57 @@ static void aer_isr_one_error(struct aer_rpc *rpc,
         * uncorrectable error being logged. Report correctable error first.
         */
        if (e_src->status & PCI_ERR_ROOT_COR_RCV) {
-               e_info->id = ERR_COR_ID(e_src->id);
-               e_info->severity = AER_CORRECTABLE;
+               e_info.id = ERR_COR_ID(e_src->id);
+               e_info.severity = AER_CORRECTABLE;
 
                if (e_src->status & PCI_ERR_ROOT_MULTI_COR_RCV)
-                       e_info->multi_error_valid = 1;
+                       e_info.multi_error_valid = 1;
                else
-                       e_info->multi_error_valid = 0;
-               aer_print_port_info(pdev, e_info);
+                       e_info.multi_error_valid = 0;
+               aer_print_port_info(pdev, &e_info);
 
-               if (find_source_device(pdev, e_info))
-                       aer_process_err_devices(e_info);
+               if (find_source_device(pdev, &e_info))
+                       aer_process_err_devices(&e_info);
        }
 
        if (e_src->status & PCI_ERR_ROOT_UNCOR_RCV) {
-               e_info->id = ERR_UNCOR_ID(e_src->id);
+               e_info.id = ERR_UNCOR_ID(e_src->id);
 
                if (e_src->status & PCI_ERR_ROOT_FATAL_RCV)
-                       e_info->severity = AER_FATAL;
+                       e_info.severity = AER_FATAL;
                else
-                       e_info->severity = AER_NONFATAL;
+                       e_info.severity = AER_NONFATAL;
 
                if (e_src->status & PCI_ERR_ROOT_MULTI_UNCOR_RCV)
-                       e_info->multi_error_valid = 1;
+                       e_info.multi_error_valid = 1;
                else
-                       e_info->multi_error_valid = 0;
+                       e_info.multi_error_valid = 0;
 
-               aer_print_port_info(pdev, e_info);
+               aer_print_port_info(pdev, &e_info);
 
-               if (find_source_device(pdev, e_info))
-                       aer_process_err_devices(e_info);
+               if (find_source_device(pdev, &e_info))
+                       aer_process_err_devices(&e_info);
        }
 }
 
-/**
- * get_e_source - retrieve an error source
- * @rpc: pointer to the root port which holds an error
- * @e_src: pointer to store retrieved error source
- *
- * Return 1 if an error source is retrieved, otherwise 0.
- *
- * Invoked by DPC handler to consume an error.
- */
-static int get_e_source(struct aer_rpc *rpc, struct aer_err_source *e_src)
-{
-       unsigned long flags;
-
-       /* Lock access to Root error producer/consumer index */
-       spin_lock_irqsave(&rpc->e_lock, flags);
-       if (rpc->prod_idx == rpc->cons_idx) {
-               spin_unlock_irqrestore(&rpc->e_lock, flags);
-               return 0;
-       }
-
-       *e_src = rpc->e_sources[rpc->cons_idx];
-       rpc->cons_idx++;
-       if (rpc->cons_idx == AER_ERROR_SOURCES_MAX)
-               rpc->cons_idx = 0;
-       spin_unlock_irqrestore(&rpc->e_lock, flags);
-
-       return 1;
-}
-
 /**
  * aer_isr - consume errors detected by root port
  * @work: definition of this work item
  *
  * Invoked, as DPC, when root port records new detected error
  */
-static void aer_isr(struct work_struct *work)
+static irqreturn_t aer_isr(int irq, void *context)
 {
-       struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler);
+       struct pcie_device *dev = (struct pcie_device *)context;
+       struct aer_rpc *rpc = get_service_data(dev);
        struct aer_err_source uninitialized_var(e_src);
 
-       mutex_lock(&rpc->rpc_mutex);
-       while (get_e_source(rpc, &e_src))
+       if (kfifo_is_empty(&rpc->aer_fifo))
+               return IRQ_NONE;
+
+       while (kfifo_get(&rpc->aer_fifo, &e_src))
                aer_isr_one_error(rpc, &e_src);
-       mutex_unlock(&rpc->rpc_mutex);
+       return IRQ_HANDLED;
 }
 
 /**
@@ -1265,56 +1229,26 @@ static void aer_isr(struct work_struct *work)
  *
  * Invoked when Root Port detects AER messages.
  */
-irqreturn_t aer_irq(int irq, void *context)
+static irqreturn_t aer_irq(int irq, void *context)
 {
-       unsigned int status, id;
        struct pcie_device *pdev = (struct pcie_device *)context;
        struct aer_rpc *rpc = get_service_data(pdev);
-       int next_prod_idx;
-       unsigned long flags;
-       int pos;
-
-       pos = pdev->port->aer_cap;
-       /*
-        * Must lock access to Root Error Status Reg, Root Error ID Reg,
-        * and Root error producer/consumer index
-        */
-       spin_lock_irqsave(&rpc->e_lock, flags);
+       struct pci_dev *rp = rpc->rpd;
+       struct aer_err_source e_src = {};
+       int pos = rp->aer_cap;
 
-       /* Read error status */
-       pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status);
-       if (!(status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV))) {
-               spin_unlock_irqrestore(&rpc->e_lock, flags);
+       pci_read_config_dword(rp, pos + PCI_ERR_ROOT_STATUS, &e_src.status);
+       if (!(e_src.status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV)))
                return IRQ_NONE;
-       }
 
-       /* Read error source and clear error status */
-       pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_ERR_SRC, &id);
-       pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status);
+       pci_read_config_dword(rp, pos + PCI_ERR_ROOT_ERR_SRC, &e_src.id);
+       pci_write_config_dword(rp, pos + PCI_ERR_ROOT_STATUS, e_src.status);
 
-       /* Store error source for later DPC handler */
-       next_prod_idx = rpc->prod_idx + 1;
-       if (next_prod_idx == AER_ERROR_SOURCES_MAX)
-               next_prod_idx = 0;
-       if (next_prod_idx == rpc->cons_idx) {
-               /*
-                * Error Storm Condition - possibly the same error occurred.
-                * Drop the error.
-                */
-               spin_unlock_irqrestore(&rpc->e_lock, flags);
+       if (!kfifo_put(&rpc->aer_fifo, e_src))
                return IRQ_HANDLED;
-       }
-       rpc->e_sources[rpc->prod_idx].status =  status;
-       rpc->e_sources[rpc->prod_idx].id = id;
-       rpc->prod_idx = next_prod_idx;
-       spin_unlock_irqrestore(&rpc->e_lock, flags);
-
-       /*  Invoke DPC handler */
-       schedule_work(&rpc->dpc_handler);
 
-       return IRQ_HANDLED;
+       return IRQ_WAKE_THREAD;
 }
-EXPORT_SYMBOL_GPL(aer_irq);
 
 static int set_device_error_reporting(struct pci_dev *dev, void *data)
 {
@@ -1422,33 +1356,6 @@ static void aer_disable_rootport(struct aer_rpc *rpc)
        pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32);
 }
 
-/**
- * aer_alloc_rpc - allocate Root Port data structure
- * @dev: pointer to the pcie_dev data structure
- *
- * Invoked when Root Port's AER service is loaded.
- */
-static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev)
-{
-       struct aer_rpc *rpc;
-
-       rpc = kzalloc(sizeof(struct aer_rpc), GFP_KERNEL);
-       if (!rpc)
-               return NULL;
-
-       /* Initialize Root lock access, e_lock, to Root Error Status Reg */
-       spin_lock_init(&rpc->e_lock);
-
-       rpc->rpd = dev->port;
-       INIT_WORK(&rpc->dpc_handler, aer_isr);
-       mutex_init(&rpc->rpc_mutex);
-
-       /* Use PCIe bus function to store rpc into PCIe device */
-       set_service_data(dev, rpc);
-
-       return rpc;
-}
-
 /**
  * aer_remove - clean up resources
  * @dev: pointer to the pcie_dev data structure
@@ -1459,16 +1366,7 @@ static void aer_remove(struct pcie_device *dev)
 {
        struct aer_rpc *rpc = get_service_data(dev);
 
-       if (rpc) {
-               /* If register interrupt service, it must be free. */
-               if (rpc->isr)
-                       free_irq(dev->irq, dev);
-
-               flush_work(&rpc->dpc_handler);
-               aer_disable_rootport(rpc);
-               kfree(rpc);
-               set_service_data(dev, NULL);
-       }
+       aer_disable_rootport(rpc);
 }
 
 /**
@@ -1481,27 +1379,24 @@ static int aer_probe(struct pcie_device *dev)
 {
        int status;
        struct aer_rpc *rpc;
-       struct device *device = &dev->port->dev;
+       struct device *device = &dev->device;
 
-       /* Alloc rpc data structure */
-       rpc = aer_alloc_rpc(dev);
+       rpc = devm_kzalloc(device, sizeof(struct aer_rpc), GFP_KERNEL);
        if (!rpc) {
                dev_printk(KERN_DEBUG, device, "alloc AER rpc failed\n");
-               aer_remove(dev);
                return -ENOMEM;
        }
+       rpc->rpd = dev->port;
+       set_service_data(dev, rpc);
 
-       /* Request IRQ ISR */
-       status = request_irq(dev->irq, aer_irq, IRQF_SHARED, "aerdrv", dev);
+       status = devm_request_threaded_irq(device, dev->irq, aer_irq, aer_isr,
+                                          IRQF_SHARED, "aerdrv", dev);
        if (status) {
                dev_printk(KERN_DEBUG, device, "request AER IRQ %d failed\n",
                           dev->irq);
-               aer_remove(dev);
                return status;
        }
 
-       rpc->isr = 1;
-
        aer_enable_rootport(rpc);
        dev_info(device, "AER enabled with IRQ %d\n", dev->irq);
        return 0;
@@ -1526,7 +1421,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
        reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
        pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
 
-       rc = pci_bridge_secondary_bus_reset(dev);
+       rc = pci_bus_error_reset(dev);
        pci_printk(KERN_DEBUG, dev, "Root Port link has been reset\n");
 
        /* Clear Root Error Status */
@@ -1541,18 +1436,6 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
        return rc ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
 }
 
-/**
- * aer_error_resume - clean up corresponding error status bits
- * @dev: pointer to Root Port's pci_dev data structure
- *
- * Invoked by Port Bus driver during nonfatal recovery.
- */
-static void aer_error_resume(struct pci_dev *dev)
-{
-       pci_aer_clear_device_status(dev);
-       pci_cleanup_aer_uncorrect_error_status(dev);
-}
-
 static struct pcie_port_service_driver aerdriver = {
        .name           = "aer",
        .port_type      = PCI_EXP_TYPE_ROOT_PORT,
@@ -1560,7 +1443,6 @@ static struct pcie_port_service_driver aerdriver = {
 
        .probe          = aer_probe,
        .remove         = aer_remove,
-       .error_resume   = aer_error_resume,
        .reset_link     = aer_root_reset,
 };
 
@@ -1569,10 +1451,9 @@ static struct pcie_port_service_driver aerdriver = {
  *
  * Invoked when AER root service driver is loaded.
  */
-static int __init aer_service_init(void)
+int __init pcie_aer_init(void)
 {
        if (!pci_aer_available() || aer_acpi_firmware_first())
                return -ENXIO;
        return pcie_port_service_register(&aerdriver);
 }
-device_initcall(aer_service_init);
index 0eb24346cad3057f964d75b7045524bc792df6f3..95d4759664b304b27328b7af656f066aad540ea3 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/irq.h>
 #include <linux/miscdevice.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
@@ -175,14 +176,48 @@ static u32 *find_pci_config_dword(struct aer_error *err, int where,
        return target;
 }
 
+static int aer_inj_read(struct pci_bus *bus, unsigned int devfn, int where,
+                       int size, u32 *val)
+{
+       struct pci_ops *ops, *my_ops;
+       int rv;
+
+       ops = __find_pci_bus_ops(bus);
+       if (!ops)
+               return -1;
+
+       my_ops = bus->ops;
+       bus->ops = ops;
+       rv = ops->read(bus, devfn, where, size, val);
+       bus->ops = my_ops;
+
+       return rv;
+}
+
+static int aer_inj_write(struct pci_bus *bus, unsigned int devfn, int where,
+                        int size, u32 val)
+{
+       struct pci_ops *ops, *my_ops;
+       int rv;
+
+       ops = __find_pci_bus_ops(bus);
+       if (!ops)
+               return -1;
+
+       my_ops = bus->ops;
+       bus->ops = ops;
+       rv = ops->write(bus, devfn, where, size, val);
+       bus->ops = my_ops;
+
+       return rv;
+}
+
 static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn,
                               int where, int size, u32 *val)
 {
        u32 *sim;
        struct aer_error *err;
        unsigned long flags;
-       struct pci_ops *ops;
-       struct pci_ops *my_ops;
        int domain;
        int rv;
 
@@ -203,18 +238,7 @@ static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn,
                return 0;
        }
 out:
-       ops = __find_pci_bus_ops(bus);
-       /*
-        * pci_lock must already be held, so we can directly
-        * manipulate bus->ops.  Many config access functions,
-        * including pci_generic_config_read() require the original
-        * bus->ops be installed to function, so temporarily put them
-        * back.
-        */
-       my_ops = bus->ops;
-       bus->ops = ops;
-       rv = ops->read(bus, devfn, where, size, val);
-       bus->ops = my_ops;
+       rv = aer_inj_read(bus, devfn, where, size, val);
        spin_unlock_irqrestore(&inject_lock, flags);
        return rv;
 }
@@ -226,8 +250,6 @@ static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn,
        struct aer_error *err;
        unsigned long flags;
        int rw1cs;
-       struct pci_ops *ops;
-       struct pci_ops *my_ops;
        int domain;
        int rv;
 
@@ -251,18 +273,7 @@ static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn,
                return 0;
        }
 out:
-       ops = __find_pci_bus_ops(bus);
-       /*
-        * pci_lock must already be held, so we can directly
-        * manipulate bus->ops.  Many config access functions,
-        * including pci_generic_config_write() require the original
-        * bus->ops be installed to function, so temporarily put them
-        * back.
-        */
-       my_ops = bus->ops;
-       bus->ops = ops;
-       rv = ops->write(bus, devfn, where, size, val);
-       bus->ops = my_ops;
+       rv = aer_inj_write(bus, devfn, where, size, val);
        spin_unlock_irqrestore(&inject_lock, flags);
        return rv;
 }
@@ -303,32 +314,13 @@ static int pci_bus_set_aer_ops(struct pci_bus *bus)
        return 0;
 }
 
-static int find_aer_device_iter(struct device *device, void *data)
-{
-       struct pcie_device **result = data;
-       struct pcie_device *pcie_dev;
-
-       if (device->bus == &pcie_port_bus_type) {
-               pcie_dev = to_pcie_device(device);
-               if (pcie_dev->service & PCIE_PORT_SERVICE_AER) {
-                       *result = pcie_dev;
-                       return 1;
-               }
-       }
-       return 0;
-}
-
-static int find_aer_device(struct pci_dev *dev, struct pcie_device **result)
-{
-       return device_for_each_child(&dev->dev, result, find_aer_device_iter);
-}
-
 static int aer_inject(struct aer_error_inj *einj)
 {
        struct aer_error *err, *rperr;
        struct aer_error *err_alloc = NULL, *rperr_alloc = NULL;
        struct pci_dev *dev, *rpdev;
        struct pcie_device *edev;
+       struct device *device;
        unsigned long flags;
        unsigned int devfn = PCI_DEVFN(einj->dev, einj->fn);
        int pos_cap_err, rp_pos_cap_err;
@@ -464,7 +456,9 @@ static int aer_inject(struct aer_error_inj *einj)
        if (ret)
                goto out_put;
 
-       if (find_aer_device(rpdev, &edev)) {
+       device = pcie_port_find_device(rpdev, PCIE_PORT_SERVICE_AER);
+       if (device) {
+               edev = to_pcie_device(device);
                if (!get_service_data(edev)) {
                        dev_warn(&edev->device,
                                 "aer_inject: AER service is not initialized\n");
@@ -474,7 +468,9 @@ static int aer_inject(struct aer_error_inj *einj)
                dev_info(&edev->device,
                         "aer_inject: Injecting errors %08x/%08x into device %s\n",
                         einj->cor_status, einj->uncor_status, pci_name(dev));
-               aer_irq(-1, edev);
+               local_irq_disable();
+               generic_handle_irq(edev->irq);
+               local_irq_enable();
        } else {
                pci_err(rpdev, "aer_inject: AER device not found\n");
                ret = -ENODEV;
index 5326916715d20663ead1c51c30ccf3cc2d0b9ddc..dcb29cb76dc69d1a958f4ae25732048b7532e549 100644 (file)
@@ -895,7 +895,7 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
        struct pcie_link_state *link;
        int blacklist = !!pcie_aspm_sanity_check(pdev);
 
-       if (!aspm_support_enabled)
+       if (!aspm_support_enabled || aspm_disabled)
                return;
 
        if (pdev->link_state)
@@ -991,7 +991,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev)
         * All PCIe functions are in one slot, remove one function will remove
         * the whole slot, so just wait until we are the last function left.
         */
-       if (!list_is_last(&pdev->bus_list, &parent->subordinate->devices))
+       if (!list_empty(&parent->subordinate->devices))
                goto out;
 
        link = parent->link_state;
index f03279fc87cd5978650b0085ad49019047f8bafd..e435d12e61a03bfdf2034f02b92c33db2327b6f9 100644 (file)
@@ -44,6 +44,58 @@ static const char * const rp_pio_error_string[] = {
        "Memory Request Completion Timeout",             /* Bit Position 18 */
 };
 
+static struct dpc_dev *to_dpc_dev(struct pci_dev *dev)
+{
+       struct device *device;
+
+       device = pcie_port_find_device(dev, PCIE_PORT_SERVICE_DPC);
+       if (!device)
+               return NULL;
+       return get_service_data(to_pcie_device(device));
+}
+
+void pci_save_dpc_state(struct pci_dev *dev)
+{
+       struct dpc_dev *dpc;
+       struct pci_cap_saved_state *save_state;
+       u16 *cap;
+
+       if (!pci_is_pcie(dev))
+               return;
+
+       dpc = to_dpc_dev(dev);
+       if (!dpc)
+               return;
+
+       save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_DPC);
+       if (!save_state)
+               return;
+
+       cap = (u16 *)&save_state->cap.data[0];
+       pci_read_config_word(dev, dpc->cap_pos + PCI_EXP_DPC_CTL, cap);
+}
+
+void pci_restore_dpc_state(struct pci_dev *dev)
+{
+       struct dpc_dev *dpc;
+       struct pci_cap_saved_state *save_state;
+       u16 *cap;
+
+       if (!pci_is_pcie(dev))
+               return;
+
+       dpc = to_dpc_dev(dev);
+       if (!dpc)
+               return;
+
+       save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_DPC);
+       if (!save_state)
+               return;
+
+       cap = (u16 *)&save_state->cap.data[0];
+       pci_write_config_word(dev, dpc->cap_pos + PCI_EXP_DPC_CTL, *cap);
+}
+
 static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
 {
        unsigned long timeout = jiffies + HZ;
@@ -67,18 +119,13 @@ static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
 static pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
 {
        struct dpc_dev *dpc;
-       struct pcie_device *pciedev;
-       struct device *devdpc;
-
        u16 cap;
 
        /*
         * DPC disables the Link automatically in hardware, so it has
         * already been reset by the time we get here.
         */
-       devdpc = pcie_port_find_device(pdev, PCIE_PORT_SERVICE_DPC);
-       pciedev = to_pcie_device(devdpc);
-       dpc = get_service_data(pciedev);
+       dpc = to_dpc_dev(pdev);
        cap = dpc->cap_pos;
 
        /*
@@ -93,10 +140,12 @@ static pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
        pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
                              PCI_EXP_DPC_STATUS_TRIGGER);
 
+       if (!pcie_wait_for_link(pdev, true))
+               return PCI_ERS_RESULT_DISCONNECT;
+
        return PCI_ERS_RESULT_RECOVERED;
 }
 
-
 static void dpc_process_rp_pio_error(struct dpc_dev *dpc)
 {
        struct device *dev = &dpc->dev->device;
@@ -169,7 +218,7 @@ static irqreturn_t dpc_handler(int irq, void *context)
 
        reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN) >> 1;
        ext_reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT) >> 5;
-       dev_warn(dev, "DPC %s detected, remove downstream devices\n",
+       dev_warn(dev, "DPC %s detected\n",
                 (reason == 0) ? "unmasked uncorrectable error" :
                 (reason == 1) ? "ERR_NONFATAL" :
                 (reason == 2) ? "ERR_FATAL" :
@@ -186,7 +235,7 @@ static irqreturn_t dpc_handler(int irq, void *context)
        }
 
        /* We configure DPC so it only triggers on ERR_FATAL */
-       pcie_do_fatal_recovery(pdev, PCIE_PORT_SERVICE_DPC);
+       pcie_do_recovery(pdev, pci_channel_io_frozen, PCIE_PORT_SERVICE_DPC);
 
        return IRQ_HANDLED;
 }
@@ -259,6 +308,8 @@ static int dpc_probe(struct pcie_device *dev)
                FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP),
                FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), dpc->rp_log_size,
                FLAG(cap, PCI_EXP_DPC_CAP_DL_ACTIVE));
+
+       pci_add_ext_cap_save_buffer(pdev, PCI_EXT_CAP_ID_DPC, sizeof(u16));
        return status;
 }
 
@@ -282,8 +333,7 @@ static struct pcie_port_service_driver dpcdriver = {
        .reset_link     = dpc_reset_link,
 };
 
-static int __init dpc_service_init(void)
+int __init pcie_dpc_init(void)
 {
        return pcie_port_service_register(&dpcdriver);
 }
-device_initcall(dpc_service_init);
index 708fd3a0d6466063e825383983206ced863adf4a..773197a12568e1e9d2ebcb1dd4e707de059ccec5 100644 (file)
 
 #include <linux/pci.h>
 #include <linux/module.h>
-#include <linux/pci.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/aer.h>
 #include "portdrv.h"
 #include "../pci.h"
 
-struct aer_broadcast_data {
-       enum pci_channel_state state;
-       enum pci_ers_result result;
-};
-
 static pci_ers_result_t merge_result(enum pci_ers_result orig,
                                  enum pci_ers_result new)
 {
@@ -49,66 +43,52 @@ static pci_ers_result_t merge_result(enum pci_ers_result orig,
        return orig;
 }
 
-static int report_error_detected(struct pci_dev *dev, void *data)
+static int report_error_detected(struct pci_dev *dev,
+                                enum pci_channel_state state,
+                                enum pci_ers_result *result)
 {
        pci_ers_result_t vote;
        const struct pci_error_handlers *err_handler;
-       struct aer_broadcast_data *result_data;
-
-       result_data = (struct aer_broadcast_data *) data;
 
        device_lock(&dev->dev);
-       dev->error_state = result_data->state;
-
-       if (!dev->driver ||
+       if (!pci_dev_set_io_state(dev, state) ||
+               !dev->driver ||
                !dev->driver->err_handler ||
                !dev->driver->err_handler->error_detected) {
-               if (result_data->state == pci_channel_io_frozen &&
-                       dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
-                       /*
-                        * In case of fatal recovery, if one of down-
-                        * stream device has no driver. We might be
-                        * unable to recover because a later insmod
-                        * of a driver for this device is unaware of
-                        * its hw state.
-                        */
-                       pci_printk(KERN_DEBUG, dev, "device has %s\n",
-                                  dev->driver ?
-                                  "no AER-aware driver" : "no driver");
-               }
-
                /*
-                * If there's any device in the subtree that does not
-                * have an error_detected callback, returning
-                * PCI_ERS_RESULT_NO_AER_DRIVER prevents calling of
-                * the subsequent mmio_enabled/slot_reset/resume
-                * callbacks of "any" device in the subtree. All the
-                * devices in the subtree are left in the error state
-                * without recovery.
+                * If any device in the subtree does not have an error_detected
+                * callback, PCI_ERS_RESULT_NO_AER_DRIVER prevents subsequent
+                * error callbacks of "any" device in the subtree, and will
+                * exit in the disconnected error state.
                 */
-
                if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE)
                        vote = PCI_ERS_RESULT_NO_AER_DRIVER;
                else
                        vote = PCI_ERS_RESULT_NONE;
        } else {
                err_handler = dev->driver->err_handler;
-               vote = err_handler->error_detected(dev, result_data->state);
-               pci_uevent_ers(dev, PCI_ERS_RESULT_NONE);
+               vote = err_handler->error_detected(dev, state);
        }
-
-       result_data->result = merge_result(result_data->result, vote);
+       pci_uevent_ers(dev, vote);
+       *result = merge_result(*result, vote);
        device_unlock(&dev->dev);
        return 0;
 }
 
+static int report_frozen_detected(struct pci_dev *dev, void *data)
+{
+       return report_error_detected(dev, pci_channel_io_frozen, data);
+}
+
+static int report_normal_detected(struct pci_dev *dev, void *data)
+{
+       return report_error_detected(dev, pci_channel_io_normal, data);
+}
+
 static int report_mmio_enabled(struct pci_dev *dev, void *data)
 {
-       pci_ers_result_t vote;
+       pci_ers_result_t vote, *result = data;
        const struct pci_error_handlers *err_handler;
-       struct aer_broadcast_data *result_data;
-
-       result_data = (struct aer_broadcast_data *) data;
 
        device_lock(&dev->dev);
        if (!dev->driver ||
@@ -118,7 +98,7 @@ static int report_mmio_enabled(struct pci_dev *dev, void *data)
 
        err_handler = dev->driver->err_handler;
        vote = err_handler->mmio_enabled(dev);
-       result_data->result = merge_result(result_data->result, vote);
+       *result = merge_result(*result, vote);
 out:
        device_unlock(&dev->dev);
        return 0;
@@ -126,11 +106,8 @@ static int report_mmio_enabled(struct pci_dev *dev, void *data)
 
 static int report_slot_reset(struct pci_dev *dev, void *data)
 {
-       pci_ers_result_t vote;
+       pci_ers_result_t vote, *result = data;
        const struct pci_error_handlers *err_handler;
-       struct aer_broadcast_data *result_data;
-
-       result_data = (struct aer_broadcast_data *) data;
 
        device_lock(&dev->dev);
        if (!dev->driver ||
@@ -140,7 +117,7 @@ static int report_slot_reset(struct pci_dev *dev, void *data)
 
        err_handler = dev->driver->err_handler;
        vote = err_handler->slot_reset(dev);
-       result_data->result = merge_result(result_data->result, vote);
+       *result = merge_result(*result, vote);
 out:
        device_unlock(&dev->dev);
        return 0;
@@ -151,17 +128,16 @@ static int report_resume(struct pci_dev *dev, void *data)
        const struct pci_error_handlers *err_handler;
 
        device_lock(&dev->dev);
-       dev->error_state = pci_channel_io_normal;
-
-       if (!dev->driver ||
+       if (!pci_dev_set_io_state(dev, pci_channel_io_normal) ||
+               !dev->driver ||
                !dev->driver->err_handler ||
                !dev->driver->err_handler->resume)
                goto out;
 
        err_handler = dev->driver->err_handler;
        err_handler->resume(dev);
-       pci_uevent_ers(dev, PCI_ERS_RESULT_RECOVERED);
 out:
+       pci_uevent_ers(dev, PCI_ERS_RESULT_RECOVERED);
        device_unlock(&dev->dev);
        return 0;
 }
@@ -177,207 +153,86 @@ static pci_ers_result_t default_reset_link(struct pci_dev *dev)
 {
        int rc;
 
-       rc = pci_bridge_secondary_bus_reset(dev);
+       rc = pci_bus_error_reset(dev);
        pci_printk(KERN_DEBUG, dev, "downstream link has been reset\n");
        return rc ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
 }
 
 static pci_ers_result_t reset_link(struct pci_dev *dev, u32 service)
 {
-       struct pci_dev *udev;
        pci_ers_result_t status;
        struct pcie_port_service_driver *driver = NULL;
 
-       if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
-               /* Reset this port for all subordinates */
-               udev = dev;
-       } else {
-               /* Reset the upstream component (likely downstream port) */
-               udev = dev->bus->self;
-       }
-
-       /* Use the aer driver of the component firstly */
-       driver = pcie_port_find_service(udev, service);
-
+       driver = pcie_port_find_service(dev, service);
        if (driver && driver->reset_link) {
-               status = driver->reset_link(udev);
-       } else if (udev->has_secondary_link) {
-               status = default_reset_link(udev);
+               status = driver->reset_link(dev);
+       } else if (dev->has_secondary_link) {
+               status = default_reset_link(dev);
        } else {
                pci_printk(KERN_DEBUG, dev, "no link-reset support at upstream device %s\n",
-                       pci_name(udev));
+                       pci_name(dev));
                return PCI_ERS_RESULT_DISCONNECT;
        }
 
        if (status != PCI_ERS_RESULT_RECOVERED) {
                pci_printk(KERN_DEBUG, dev, "link reset at upstream device %s failed\n",
-                       pci_name(udev));
+                       pci_name(dev));
                return PCI_ERS_RESULT_DISCONNECT;
        }
 
        return status;
 }
 
-/**
- * broadcast_error_message - handle message broadcast to downstream drivers
- * @dev: pointer to from where in a hierarchy message is broadcasted down
- * @state: error state
- * @error_mesg: message to print
- * @cb: callback to be broadcasted
- *
- * Invoked during error recovery process. Once being invoked, the content
- * of error severity will be broadcasted to all downstream drivers in a
- * hierarchy in question.
- */
-static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
-       enum pci_channel_state state,
-       char *error_mesg,
-       int (*cb)(struct pci_dev *, void *))
-{
-       struct aer_broadcast_data result_data;
-
-       pci_printk(KERN_DEBUG, dev, "broadcast %s message\n", error_mesg);
-       result_data.state = state;
-       if (cb == report_error_detected)
-               result_data.result = PCI_ERS_RESULT_CAN_RECOVER;
-       else
-               result_data.result = PCI_ERS_RESULT_RECOVERED;
-
-       if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
-               /*
-                * If the error is reported by a bridge, we think this error
-                * is related to the downstream link of the bridge, so we
-                * do error recovery on all subordinates of the bridge instead
-                * of the bridge and clear the error status of the bridge.
-                */
-               if (cb == report_error_detected)
-                       dev->error_state = state;
-               pci_walk_bus(dev->subordinate, cb, &result_data);
-               if (cb == report_resume) {
-                       pci_aer_clear_device_status(dev);
-                       pci_cleanup_aer_uncorrect_error_status(dev);
-                       dev->error_state = pci_channel_io_normal;
-               }
-       } else {
-               /*
-                * If the error is reported by an end point, we think this
-                * error is related to the upstream link of the end point.
-                * The error is non fatal so the bus is ok; just invoke
-                * the callback for the function that logged the error.
-                */
-               cb(dev, &result_data);
-       }
-
-       return result_data.result;
-}
-
-/**
- * pcie_do_fatal_recovery - handle fatal error recovery process
- * @dev: pointer to a pci_dev data structure of agent detecting an error
- *
- * Invoked when an error is fatal. Once being invoked, removes the devices
- * beneath this AER agent, followed by reset link e.g. secondary bus reset
- * followed by re-enumeration of devices.
- */
-void pcie_do_fatal_recovery(struct pci_dev *dev, u32 service)
+void pcie_do_recovery(struct pci_dev *dev, enum pci_channel_state state,
+                     u32 service)
 {
-       struct pci_dev *udev;
-       struct pci_bus *parent;
-       struct pci_dev *pdev, *temp;
-       pci_ers_result_t result;
-
-       if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
-               udev = dev;
+       pci_ers_result_t status = PCI_ERS_RESULT_CAN_RECOVER;
+       struct pci_bus *bus;
+
+       /*
+        * Error recovery runs on all subordinates of the first downstream port.
+        * If the downstream port detected the error, it is cleared at the end.
+        */
+       if (!(pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
+             pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM))
+               dev = dev->bus->self;
+       bus = dev->subordinate;
+
+       pci_dbg(dev, "broadcast error_detected message\n");
+       if (state == pci_channel_io_frozen)
+               pci_walk_bus(bus, report_frozen_detected, &status);
        else
-               udev = dev->bus->self;
-
-       parent = udev->subordinate;
-       pci_lock_rescan_remove();
-       pci_dev_get(dev);
-       list_for_each_entry_safe_reverse(pdev, temp, &parent->devices,
-                                        bus_list) {
-               pci_dev_get(pdev);
-               pci_dev_set_disconnected(pdev, NULL);
-               if (pci_has_subordinate(pdev))
-                       pci_walk_bus(pdev->subordinate,
-                                    pci_dev_set_disconnected, NULL);
-               pci_stop_and_remove_bus_device(pdev);
-               pci_dev_put(pdev);
-       }
-
-       result = reset_link(udev, service);
+               pci_walk_bus(bus, report_normal_detected, &status);
 
-       if ((service == PCIE_PORT_SERVICE_AER) &&
-           (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)) {
-               /*
-                * If the error is reported by a bridge, we think this error
-                * is related to the downstream link of the bridge, so we
-                * do error recovery on all subordinates of the bridge instead
-                * of the bridge and clear the error status of the bridge.
-                */
-               pci_aer_clear_fatal_status(dev);
-               pci_aer_clear_device_status(dev);
-       }
+       if (state == pci_channel_io_frozen &&
+           reset_link(dev, service) != PCI_ERS_RESULT_RECOVERED)
+               goto failed;
 
-       if (result == PCI_ERS_RESULT_RECOVERED) {
-               if (pcie_wait_for_link(udev, true))
-                       pci_rescan_bus(udev->bus);
-               pci_info(dev, "Device recovery from fatal error successful\n");
-       } else {
-               pci_uevent_ers(dev, PCI_ERS_RESULT_DISCONNECT);
-               pci_info(dev, "Device recovery from fatal error failed\n");
+       if (status == PCI_ERS_RESULT_CAN_RECOVER) {
+               status = PCI_ERS_RESULT_RECOVERED;
+               pci_dbg(dev, "broadcast mmio_enabled message\n");
+               pci_walk_bus(bus, report_mmio_enabled, &status);
        }
 
-       pci_dev_put(dev);
-       pci_unlock_rescan_remove();
-}
-
-/**
- * pcie_do_nonfatal_recovery - handle nonfatal error recovery process
- * @dev: pointer to a pci_dev data structure of agent detecting an error
- *
- * Invoked when an error is nonfatal/fatal. Once being invoked, broadcast
- * error detected message to all downstream drivers within a hierarchy in
- * question and return the returned code.
- */
-void pcie_do_nonfatal_recovery(struct pci_dev *dev)
-{
-       pci_ers_result_t status;
-       enum pci_channel_state state;
-
-       state = pci_channel_io_normal;
-
-       status = broadcast_error_message(dev,
-                       state,
-                       "error_detected",
-                       report_error_detected);
-
-       if (status == PCI_ERS_RESULT_CAN_RECOVER)
-               status = broadcast_error_message(dev,
-                               state,
-                               "mmio_enabled",
-                               report_mmio_enabled);
-
        if (status == PCI_ERS_RESULT_NEED_RESET) {
                /*
                 * TODO: Should call platform-specific
                 * functions to reset slot before calling
                 * drivers' slot_reset callbacks?
                 */
-               status = broadcast_error_message(dev,
-                               state,
-                               "slot_reset",
-                               report_slot_reset);
+               status = PCI_ERS_RESULT_RECOVERED;
+               pci_dbg(dev, "broadcast slot_reset message\n");
+               pci_walk_bus(bus, report_slot_reset, &status);
        }
 
        if (status != PCI_ERS_RESULT_RECOVERED)
                goto failed;
 
-       broadcast_error_message(dev,
-                               state,
-                               "resume",
-                               report_resume);
+       pci_dbg(dev, "broadcast resume message\n");
+       pci_walk_bus(bus, report_resume, &status);
 
+       pci_aer_clear_device_status(dev);
+       pci_cleanup_aer_uncorrect_error_status(dev);
        pci_info(dev, "AER: Device recovery successful\n");
        return;
 
index 3ed67676ea2a16d14f41c9710d7b3a8edfd3b157..0dbcf429089ff00505f94b49abbd833a95392d5d 100644 (file)
@@ -432,6 +432,31 @@ static void pcie_pme_remove(struct pcie_device *srv)
        kfree(get_service_data(srv));
 }
 
+static int pcie_pme_runtime_suspend(struct pcie_device *srv)
+{
+       struct pcie_pme_service_data *data = get_service_data(srv);
+
+       spin_lock_irq(&data->lock);
+       pcie_pme_interrupt_enable(srv->port, false);
+       pcie_clear_root_pme_status(srv->port);
+       data->noirq = true;
+       spin_unlock_irq(&data->lock);
+
+       return 0;
+}
+
+static int pcie_pme_runtime_resume(struct pcie_device *srv)
+{
+       struct pcie_pme_service_data *data = get_service_data(srv);
+
+       spin_lock_irq(&data->lock);
+       pcie_pme_interrupt_enable(srv->port, true);
+       data->noirq = false;
+       spin_unlock_irq(&data->lock);
+
+       return 0;
+}
+
 static struct pcie_port_service_driver pcie_pme_driver = {
        .name           = "pcie_pme",
        .port_type      = PCI_EXP_TYPE_ROOT_PORT,
@@ -439,6 +464,8 @@ static struct pcie_port_service_driver pcie_pme_driver = {
 
        .probe          = pcie_pme_probe,
        .suspend        = pcie_pme_suspend,
+       .runtime_suspend = pcie_pme_runtime_suspend,
+       .runtime_resume = pcie_pme_runtime_resume,
        .resume         = pcie_pme_resume,
        .remove         = pcie_pme_remove,
 };
@@ -446,8 +473,7 @@ static struct pcie_port_service_driver pcie_pme_driver = {
 /**
  * pcie_pme_service_init - Register the PCIe PME service driver.
  */
-static int __init pcie_pme_service_init(void)
+int __init pcie_pme_init(void)
 {
        return pcie_port_service_register(&pcie_pme_driver);
 }
-device_initcall(pcie_pme_service_init);
index d59afa42fc14ba3702b26acb704dc9726442360d..e495f04394d074176d55857d71a20bac2de501d2 100644 (file)
 
 #define PCIE_PORT_DEVICE_MAXSERVICES   4
 
+#ifdef CONFIG_PCIEAER
+int pcie_aer_init(void);
+#else
+static inline int pcie_aer_init(void) { return 0; }
+#endif
+
+#ifdef CONFIG_HOTPLUG_PCI_PCIE
+int pcie_hp_init(void);
+#else
+static inline int pcie_hp_init(void) { return 0; }
+#endif
+
+#ifdef CONFIG_PCIE_PME
+int pcie_pme_init(void);
+#else
+static inline int pcie_pme_init(void) { return 0; }
+#endif
+
+#ifdef CONFIG_PCIE_DPC
+int pcie_dpc_init(void);
+#else
+static inline int pcie_dpc_init(void) { return 0; }
+#endif
+
 /* Port Type */
 #define PCIE_ANY_PORT                  (~0)
 
@@ -52,6 +76,8 @@ struct pcie_port_service_driver {
        int (*suspend) (struct pcie_device *dev);
        int (*resume_noirq) (struct pcie_device *dev);
        int (*resume) (struct pcie_device *dev);
+       int (*runtime_suspend) (struct pcie_device *dev);
+       int (*runtime_resume) (struct pcie_device *dev);
 
        /* Device driver may resume normal operations */
        void (*error_resume)(struct pci_dev *dev);
@@ -85,6 +111,8 @@ int pcie_port_device_register(struct pci_dev *dev);
 int pcie_port_device_suspend(struct device *dev);
 int pcie_port_device_resume_noirq(struct device *dev);
 int pcie_port_device_resume(struct device *dev);
+int pcie_port_device_runtime_suspend(struct device *dev);
+int pcie_port_device_runtime_resume(struct device *dev);
 #endif
 void pcie_port_device_remove(struct pci_dev *dev);
 int __must_check pcie_port_bus_register(void);
@@ -123,10 +151,6 @@ static inline int pcie_aer_get_firmware_first(struct pci_dev *pci_dev)
 }
 #endif
 
-#ifdef CONFIG_PCIEAER
-irqreturn_t aer_irq(int irq, void *context);
-#endif
-
 struct pcie_port_service_driver *pcie_port_find_service(struct pci_dev *dev,
                                                        u32 service);
 struct device *pcie_port_find_device(struct pci_dev *dev, u32 service);
index 7c37d815229e9ee20e7732695dda58344fc77a20..f458ac9cb70c397868a17fa48027ccb21f80e762 100644 (file)
@@ -395,6 +395,26 @@ int pcie_port_device_resume(struct device *dev)
        size_t off = offsetof(struct pcie_port_service_driver, resume);
        return device_for_each_child(dev, &off, pm_iter);
 }
+
+/**
+ * pcie_port_device_runtime_suspend - runtime suspend port services
+ * @dev: PCI Express port to handle
+ */
+int pcie_port_device_runtime_suspend(struct device *dev)
+{
+       size_t off = offsetof(struct pcie_port_service_driver, runtime_suspend);
+       return device_for_each_child(dev, &off, pm_iter);
+}
+
+/**
+ * pcie_port_device_runtime_resume - runtime resume port services
+ * @dev: PCI Express port to handle
+ */
+int pcie_port_device_runtime_resume(struct device *dev)
+{
+       size_t off = offsetof(struct pcie_port_service_driver, runtime_resume);
+       return device_for_each_child(dev, &off, pm_iter);
+}
 #endif /* PM */
 
 static int remove_iter(struct device *dev, void *data)
@@ -466,6 +486,7 @@ struct device *pcie_port_find_device(struct pci_dev *dev,
        device = pdrvs.dev;
        return device;
 }
+EXPORT_SYMBOL_GPL(pcie_port_find_device);
 
 /**
  * pcie_port_device_remove - unregister PCI Express port service devices
index eef22dc29140cd104318de4f332ab1dbb3e88034..0acca3596807afbbcd392fc1537a9dd2048704dc 100644 (file)
@@ -45,12 +45,10 @@ __setup("pcie_ports=", pcie_port_setup);
 #ifdef CONFIG_PM
 static int pcie_port_runtime_suspend(struct device *dev)
 {
-       return to_pci_dev(dev)->bridge_d3 ? 0 : -EBUSY;
-}
+       if (!to_pci_dev(dev)->bridge_d3)
+               return -EBUSY;
 
-static int pcie_port_runtime_resume(struct device *dev)
-{
-       return 0;
+       return pcie_port_device_runtime_suspend(dev);
 }
 
 static int pcie_port_runtime_idle(struct device *dev)
@@ -73,7 +71,7 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = {
        .restore_noirq  = pcie_port_device_resume_noirq,
        .restore        = pcie_port_device_resume,
        .runtime_suspend = pcie_port_runtime_suspend,
-       .runtime_resume = pcie_port_runtime_resume,
+       .runtime_resume = pcie_port_device_runtime_resume,
        .runtime_idle   = pcie_port_runtime_idle,
 };
 
@@ -109,8 +107,8 @@ static int pcie_portdrv_probe(struct pci_dev *dev,
 
        pci_save_state(dev);
 
-       dev_pm_set_driver_flags(&dev->dev, DPM_FLAG_SMART_SUSPEND |
-                                          DPM_FLAG_LEAVE_SUSPENDED);
+       dev_pm_set_driver_flags(&dev->dev, DPM_FLAG_NEVER_SKIP |
+                                          DPM_FLAG_SMART_SUSPEND);
 
        if (pci_bridge_d3_possible(dev)) {
                /*
@@ -146,6 +144,13 @@ static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev,
        return PCI_ERS_RESULT_CAN_RECOVER;
 }
 
+static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev)
+{
+       pci_restore_state(dev);
+       pci_save_state(dev);
+       return PCI_ERS_RESULT_RECOVERED;
+}
+
 static pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev)
 {
        return PCI_ERS_RESULT_RECOVERED;
@@ -185,6 +190,7 @@ static const struct pci_device_id port_pci_ids[] = { {
 
 static const struct pci_error_handlers pcie_portdrv_err_handler = {
        .error_detected = pcie_portdrv_error_detected,
+       .slot_reset = pcie_portdrv_slot_reset,
        .mmio_enabled = pcie_portdrv_mmio_enabled,
        .resume = pcie_portdrv_err_resume,
 };
@@ -226,11 +232,20 @@ static const struct dmi_system_id pcie_portdrv_dmi_table[] __initconst = {
         {}
 };
 
+static void __init pcie_init_services(void)
+{
+       pcie_aer_init();
+       pcie_pme_init();
+       pcie_dpc_init();
+       pcie_hp_init();
+}
+
 static int __init pcie_portdrv_init(void)
 {
        if (pcie_ports_disabled)
                return -EACCES;
 
+       pcie_init_services();
        dmi_check_system(pcie_portdrv_dmi_table);
 
        return pci_register_driver(&pcie_portdriver);
index 575315bb7c4c5519fcec13da1f8d129000c32c17..b1c05b5054a0c5a72385ef122410237f6a02e8ea 100644 (file)
@@ -713,6 +713,7 @@ static void pci_set_bus_speed(struct pci_bus *bus)
 
                pcie_capability_read_dword(bridge, PCI_EXP_LNKCAP, &linkcap);
                bus->max_bus_speed = pcie_link_speed[linkcap & PCI_EXP_LNKCAP_SLS];
+               bridge->link_active_reporting = !!(linkcap & PCI_EXP_LNKCAP_DLLLARC);
 
                pcie_capability_read_word(bridge, PCI_EXP_LNKSTA, &linksta);
                pcie_update_link_speed(bus, linksta);
@@ -2160,7 +2161,7 @@ static void pci_release_dev(struct device *dev)
        pcibios_release_device(pci_dev);
        pci_bus_put(pci_dev->bus);
        kfree(pci_dev->driver_override);
-       kfree(pci_dev->dma_alias_mask);
+       bitmap_free(pci_dev->dma_alias_mask);
        kfree(pci_dev);
 }
 
@@ -2414,8 +2415,8 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
        dev->dev.dma_parms = &dev->dma_parms;
        dev->dev.coherent_dma_mask = 0xffffffffull;
 
-       pci_set_dma_max_seg_size(dev, 65536);
-       pci_set_dma_seg_boundary(dev, 0xffffffff);
+       dma_set_max_seg_size(&dev->dev, 65536);
+       dma_set_seg_boundary(&dev->dev, 0xffffffff);
 
        /* Fix up broken headers */
        pci_fixup_device(pci_fixup_header, dev);
index 6bc27b7fd452ad591626625c69454d85041c1a02..4700d24e5d55a1b456698c4725e8d9d2811c03c7 100644 (file)
@@ -3190,7 +3190,11 @@ static void disable_igfx_irq(struct pci_dev *dev)
 
        pci_iounmap(dev, regs);
 }
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0042, disable_igfx_irq);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0046, disable_igfx_irq);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x004a, disable_igfx_irq);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0102, disable_igfx_irq);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0106, disable_igfx_irq);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0152, disable_igfx_irq);
 
@@ -4987,7 +4991,6 @@ static void quirk_switchtec_ntb_dma_alias(struct pci_dev *pdev)
        void __iomem *mmio;
        struct ntb_info_regs __iomem *mmio_ntb;
        struct ntb_ctrl_regs __iomem *mmio_ctrl;
-       struct sys_info_regs __iomem *mmio_sys_info;
        u64 partition_map;
        u8 partition;
        int pp;
@@ -5008,7 +5011,6 @@ static void quirk_switchtec_ntb_dma_alias(struct pci_dev *pdev)
 
        mmio_ntb = mmio + SWITCHTEC_GAS_NTB_OFFSET;
        mmio_ctrl = (void __iomem *) mmio_ntb + SWITCHTEC_NTB_REG_CTRL_OFFSET;
-       mmio_sys_info = mmio + SWITCHTEC_GAS_SYS_INFO_OFFSET;
 
        partition = ioread8(&mmio_ntb->partition_id);
 
@@ -5057,59 +5059,37 @@ static void quirk_switchtec_ntb_dma_alias(struct pci_dev *pdev)
        pci_iounmap(pdev, mmio);
        pci_disable_device(pdev);
 }
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8531,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8532,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8533,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8534,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8535,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8536,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8543,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8544,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8545,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8546,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8551,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8552,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8553,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8554,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8555,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8556,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8561,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8562,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8563,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8564,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8565,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8566,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8571,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8572,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8573,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8574,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8575,
-                       quirk_switchtec_ntb_dma_alias);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MICROSEMI, 0x8576,
-                       quirk_switchtec_ntb_dma_alias);
+#define SWITCHTEC_QUIRK(vid) \
+       DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_MICROSEMI, vid, \
+               PCI_CLASS_BRIDGE_OTHER, 8, quirk_switchtec_ntb_dma_alias)
+
+SWITCHTEC_QUIRK(0x8531);  /* PFX 24xG3 */
+SWITCHTEC_QUIRK(0x8532);  /* PFX 32xG3 */
+SWITCHTEC_QUIRK(0x8533);  /* PFX 48xG3 */
+SWITCHTEC_QUIRK(0x8534);  /* PFX 64xG3 */
+SWITCHTEC_QUIRK(0x8535);  /* PFX 80xG3 */
+SWITCHTEC_QUIRK(0x8536);  /* PFX 96xG3 */
+SWITCHTEC_QUIRK(0x8541);  /* PSX 24xG3 */
+SWITCHTEC_QUIRK(0x8542);  /* PSX 32xG3 */
+SWITCHTEC_QUIRK(0x8543);  /* PSX 48xG3 */
+SWITCHTEC_QUIRK(0x8544);  /* PSX 64xG3 */
+SWITCHTEC_QUIRK(0x8545);  /* PSX 80xG3 */
+SWITCHTEC_QUIRK(0x8546);  /* PSX 96xG3 */
+SWITCHTEC_QUIRK(0x8551);  /* PAX 24XG3 */
+SWITCHTEC_QUIRK(0x8552);  /* PAX 32XG3 */
+SWITCHTEC_QUIRK(0x8553);  /* PAX 48XG3 */
+SWITCHTEC_QUIRK(0x8554);  /* PAX 64XG3 */
+SWITCHTEC_QUIRK(0x8555);  /* PAX 80XG3 */
+SWITCHTEC_QUIRK(0x8556);  /* PAX 96XG3 */
+SWITCHTEC_QUIRK(0x8561);  /* PFXL 24XG3 */
+SWITCHTEC_QUIRK(0x8562);  /* PFXL 32XG3 */
+SWITCHTEC_QUIRK(0x8563);  /* PFXL 48XG3 */
+SWITCHTEC_QUIRK(0x8564);  /* PFXL 64XG3 */
+SWITCHTEC_QUIRK(0x8565);  /* PFXL 80XG3 */
+SWITCHTEC_QUIRK(0x8566);  /* PFXL 96XG3 */
+SWITCHTEC_QUIRK(0x8571);  /* PFXI 24XG3 */
+SWITCHTEC_QUIRK(0x8572);  /* PFXI 32XG3 */
+SWITCHTEC_QUIRK(0x8573);  /* PFXI 48XG3 */
+SWITCHTEC_QUIRK(0x8574);  /* PFXI 64XG3 */
+SWITCHTEC_QUIRK(0x8575);  /* PFXI 80XG3 */
+SWITCHTEC_QUIRK(0x8576);  /* PFXI 96XG3 */
index 461e7fd2756fb317d147fbebc0ec0ca2bda339c1..e9c6b120cf451331dc294f50a3ac1315cd37c2c3 100644 (file)
@@ -25,9 +25,6 @@ static void pci_stop_dev(struct pci_dev *dev)
 
                pci_dev_assign_added(dev, false);
        }
-
-       if (dev->bus->self)
-               pcie_aspm_exit_link_state(dev);
 }
 
 static void pci_destroy_dev(struct pci_dev *dev)
@@ -41,6 +38,7 @@ static void pci_destroy_dev(struct pci_dev *dev)
        list_del(&dev->bus_list);
        up_write(&pci_bus_sem);
 
+       pcie_aspm_exit_link_state(dev);
        pci_bridge_d3_update(dev);
        pci_free_resources(dev);
        put_device(&dev->dev);
index 79b1824e83b47c058a262185668aff946293bf5e..ed960436df5e84a35b703a96250e2b10f5e22a30 100644 (file)
@@ -811,6 +811,8 @@ static struct resource *find_free_bus_resource(struct pci_bus *bus,
 static resource_size_t calculate_iosize(resource_size_t size,
                resource_size_t min_size,
                resource_size_t size1,
+               resource_size_t add_size,
+               resource_size_t children_add_size,
                resource_size_t old_size,
                resource_size_t align)
 {
@@ -823,15 +825,18 @@ static resource_size_t calculate_iosize(resource_size_t size,
 #if defined(CONFIG_ISA) || defined(CONFIG_EISA)
        size = (size & 0xff) + ((size & ~0xffUL) << 2);
 #endif
-       size = ALIGN(size + size1, align);
+       size = size + size1;
        if (size < old_size)
                size = old_size;
+
+       size = ALIGN(max(size, add_size) + children_add_size, align);
        return size;
 }
 
 static resource_size_t calculate_memsize(resource_size_t size,
                resource_size_t min_size,
-               resource_size_t size1,
+               resource_size_t add_size,
+               resource_size_t children_add_size,
                resource_size_t old_size,
                resource_size_t align)
 {
@@ -841,7 +846,8 @@ static resource_size_t calculate_memsize(resource_size_t size,
                old_size = 0;
        if (size < old_size)
                size = old_size;
-       size = ALIGN(size + size1, align);
+
+       size = ALIGN(max(size, add_size) + children_add_size, align);
        return size;
 }
 
@@ -930,12 +936,10 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
                }
        }
 
-       size0 = calculate_iosize(size, min_size, size1,
+       size0 = calculate_iosize(size, min_size, size1, 0, 0,
                        resource_size(b_res), min_align);
-       if (children_add_size > add_size)
-               add_size = children_add_size;
-       size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 :
-               calculate_iosize(size, min_size, add_size + size1,
+       size1 = (!realloc_head || (realloc_head && !add_size && !children_add_size)) ? size0 :
+               calculate_iosize(size, min_size, size1, add_size, children_add_size,
                        resource_size(b_res), min_align);
        if (!size0 && !size1) {
                if (b_res->start || b_res->end)
@@ -1079,12 +1083,10 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
 
        min_align = calculate_mem_align(aligns, max_order);
        min_align = max(min_align, window_alignment(bus, b_res->flags));
-       size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), min_align);
+       size0 = calculate_memsize(size, min_size, 0, 0, resource_size(b_res), min_align);
        add_align = max(min_align, add_align);
-       if (children_add_size > add_size)
-               add_size = children_add_size;
-       size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 :
-               calculate_memsize(size, min_size, add_size,
+       size1 = (!realloc_head || (realloc_head && !add_size && !children_add_size)) ? size0 :
+               calculate_memsize(size, min_size, add_size, children_add_size,
                                resource_size(b_res), add_align);
        if (!size0 && !size1) {
                if (b_res->start || b_res->end)
index e634229ece8957607b881e4f1e93c6b7028c85fe..c46d5e1ff53692ebeafaee1d4e745d4743a4c78a 100644 (file)
@@ -14,7 +14,6 @@
 
 struct kset *pci_slots_kset;
 EXPORT_SYMBOL_GPL(pci_slots_kset);
-static DEFINE_MUTEX(pci_slot_mutex);
 
 static ssize_t pci_slot_attr_show(struct kobject *kobj,
                                        struct attribute *attr, char *buf)
@@ -371,7 +370,7 @@ void pci_hp_create_module_link(struct pci_slot *pci_slot)
 
        if (!slot || !slot->ops)
                return;
-       kobj = kset_find_obj(module_kset, slot->ops->mod_name);
+       kobj = kset_find_obj(module_kset, slot->mod_name);
        if (!kobj)
                return;
        ret = sysfs_create_link(&pci_slot->kobj, kobj, "module");
index 2d6e272315a82eae024c0fe3f242f097c2787f02..93ee2d5466f8092978f57bf74ca84ee9c3cc1cc8 100644 (file)
@@ -254,7 +254,7 @@ struct asus_wmi {
        int asus_hwmon_num_fans;
        int asus_hwmon_pwm;
 
-       struct hotplug_slot *hotplug_slot;
+       struct hotplug_slot hotplug_slot;
        struct mutex hotplug_lock;
        struct mutex wmi_lock;
        struct workqueue_struct *hotplug_workqueue;
@@ -753,7 +753,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus)
        if (asus->wlan.rfkill)
                rfkill_set_sw_state(asus->wlan.rfkill, blocked);
 
-       if (asus->hotplug_slot) {
+       if (asus->hotplug_slot.ops) {
                bus = pci_find_bus(0, 1);
                if (!bus) {
                        pr_warn("Unable to find PCI bus 1?\n");
@@ -858,7 +858,8 @@ static void asus_unregister_rfkill_notifier(struct asus_wmi *asus, char *node)
 static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot,
                                   u8 *value)
 {
-       struct asus_wmi *asus = hotplug_slot->private;
+       struct asus_wmi *asus = container_of(hotplug_slot,
+                                            struct asus_wmi, hotplug_slot);
        int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
 
        if (result < 0)
@@ -868,8 +869,7 @@ static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot,
        return 0;
 }
 
-static struct hotplug_slot_ops asus_hotplug_slot_ops = {
-       .owner = THIS_MODULE,
+static const struct hotplug_slot_ops asus_hotplug_slot_ops = {
        .get_adapter_status = asus_get_adapter_status,
        .get_power_status = asus_get_adapter_status,
 };
@@ -899,21 +899,9 @@ static int asus_setup_pci_hotplug(struct asus_wmi *asus)
 
        INIT_WORK(&asus->hotplug_work, asus_hotplug_work);
 
-       asus->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
-       if (!asus->hotplug_slot)
-               goto error_slot;
-
-       asus->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
-                                          GFP_KERNEL);
-       if (!asus->hotplug_slot->info)
-               goto error_info;
+       asus->hotplug_slot.ops = &asus_hotplug_slot_ops;
 
-       asus->hotplug_slot->private = asus;
-       asus->hotplug_slot->ops = &asus_hotplug_slot_ops;
-       asus_get_adapter_status(asus->hotplug_slot,
-                               &asus->hotplug_slot->info->adapter_status);
-
-       ret = pci_hp_register(asus->hotplug_slot, bus, 0, "asus-wifi");
+       ret = pci_hp_register(&asus->hotplug_slot, bus, 0, "asus-wifi");
        if (ret) {
                pr_err("Unable to register hotplug slot - %d\n", ret);
                goto error_register;
@@ -922,11 +910,7 @@ static int asus_setup_pci_hotplug(struct asus_wmi *asus)
        return 0;
 
 error_register:
-       kfree(asus->hotplug_slot->info);
-error_info:
-       kfree(asus->hotplug_slot);
-       asus->hotplug_slot = NULL;
-error_slot:
+       asus->hotplug_slot.ops = NULL;
        destroy_workqueue(asus->hotplug_workqueue);
 error_workqueue:
        return ret;
@@ -1054,11 +1038,8 @@ static void asus_wmi_rfkill_exit(struct asus_wmi *asus)
         * asus_unregister_rfkill_notifier()
         */
        asus_rfkill_hotplug(asus);
-       if (asus->hotplug_slot) {
-               pci_hp_deregister(asus->hotplug_slot);
-               kfree(asus->hotplug_slot->info);
-               kfree(asus->hotplug_slot);
-       }
+       if (asus->hotplug_slot.ops)
+               pci_hp_deregister(&asus->hotplug_slot);
        if (asus->hotplug_workqueue)
                destroy_workqueue(asus->hotplug_workqueue);
 
index a4bbf6ecd1f08d185282d348d50f239d8efb305a..e6946a9beb5a352dd2ddbf967ed88ff9cef497eb 100644 (file)
@@ -177,7 +177,7 @@ struct eeepc_laptop {
        struct rfkill *wwan3g_rfkill;
        struct rfkill *wimax_rfkill;
 
-       struct hotplug_slot *hotplug_slot;
+       struct hotplug_slot hotplug_slot;
        struct mutex hotplug_lock;
 
        struct led_classdev tpd_led;
@@ -582,7 +582,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
        mutex_lock(&eeepc->hotplug_lock);
        pci_lock_rescan_remove();
 
-       if (!eeepc->hotplug_slot)
+       if (!eeepc->hotplug_slot.ops)
                goto out_unlock;
 
        port = acpi_get_pci_dev(handle);
@@ -715,8 +715,11 @@ static void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc,
 static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
                                    u8 *value)
 {
-       struct eeepc_laptop *eeepc = hotplug_slot->private;
-       int val = get_acpi(eeepc, CM_ASL_WLAN);
+       struct eeepc_laptop *eeepc;
+       int val;
+
+       eeepc = container_of(hotplug_slot, struct eeepc_laptop, hotplug_slot);
+       val = get_acpi(eeepc, CM_ASL_WLAN);
 
        if (val == 1 || val == 0)
                *value = val;
@@ -726,8 +729,7 @@ static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
        return 0;
 }
 
-static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
-       .owner = THIS_MODULE,
+static const struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
        .get_adapter_status = eeepc_get_adapter_status,
        .get_power_status = eeepc_get_adapter_status,
 };
@@ -742,21 +744,9 @@ static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc)
                return -ENODEV;
        }
 
-       eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
-       if (!eeepc->hotplug_slot)
-               goto error_slot;
+       eeepc->hotplug_slot.ops = &eeepc_hotplug_slot_ops;
 
-       eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
-                                           GFP_KERNEL);
-       if (!eeepc->hotplug_slot->info)
-               goto error_info;
-
-       eeepc->hotplug_slot->private = eeepc;
-       eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
-       eeepc_get_adapter_status(eeepc->hotplug_slot,
-                                &eeepc->hotplug_slot->info->adapter_status);
-
-       ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi");
+       ret = pci_hp_register(&eeepc->hotplug_slot, bus, 0, "eeepc-wifi");
        if (ret) {
                pr_err("Unable to register hotplug slot - %d\n", ret);
                goto error_register;
@@ -765,11 +755,7 @@ static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc)
        return 0;
 
 error_register:
-       kfree(eeepc->hotplug_slot->info);
-error_info:
-       kfree(eeepc->hotplug_slot);
-       eeepc->hotplug_slot = NULL;
-error_slot:
+       eeepc->hotplug_slot.ops = NULL;
        return ret;
 }
 
@@ -830,11 +816,8 @@ static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc)
                eeepc->wlan_rfkill = NULL;
        }
 
-       if (eeepc->hotplug_slot) {
-               pci_hp_deregister(eeepc->hotplug_slot);
-               kfree(eeepc->hotplug_slot->info);
-               kfree(eeepc->hotplug_slot);
-       }
+       if (eeepc->hotplug_slot.ops)
+               pci_hp_deregister(&eeepc->hotplug_slot);
 
        if (eeepc->bluetooth_rfkill) {
                rfkill_unregister(eeepc->bluetooth_rfkill);
index c0631895154e6e7f5b3c7948a66741a302ea669f..f96ec68af2e58aabfa0bdb71dde1f05ff8ad4151 100644 (file)
@@ -515,8 +515,8 @@ static int ism_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        if (ret)
                goto err_unmap;
 
-       pci_set_dma_seg_boundary(pdev, SZ_1M - 1);
-       pci_set_dma_max_seg_size(pdev, SZ_1M);
+       dma_set_seg_boundary(&pdev->dev, SZ_1M - 1);
+       dma_set_max_seg_size(&pdev->dev, SZ_1M);
        pci_set_master(pdev);
 
        ism->smcd = smcd_alloc_dev(&pdev->dev, dev_name(&pdev->dev), &ism_ops,
index 04443577d48b371f37afb057ec5bceb9f2e000f7..2d4e4ddc5acecfe4608a43a4609c2a69405539de 100644 (file)
@@ -1747,7 +1747,7 @@ static int aac_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
                shost->max_sectors = (shost->sg_tablesize * 8) + 112;
        }
 
-       error = pci_set_dma_max_seg_size(pdev,
+       error = dma_set_max_seg_size(&pdev->dev,
                (aac->adapter_info.options & AAC_OPT_NEW_COMM) ?
                        (shost->max_sectors << 9) : 65536);
        if (error)
@@ -2055,8 +2055,6 @@ static void aac_pci_resume(struct pci_dev *pdev)
        struct scsi_device *sdev = NULL;
        struct aac_dev *aac = (struct aac_dev *)shost_priv(shost);
 
-       pci_cleanup_aer_uncorrect_error_status(pdev);
-
        if (aac_adapter_ioremap(aac, aac->base_size)) {
 
                dev_err(&pdev->dev, "aacraid: ioremap failed\n");
index 3660059784f74bdbf9620aa787c5f3ab0c823036..a3019d8a74020bbbeb7165a2a890eb3768bca758 100644 (file)
@@ -5529,7 +5529,6 @@ static pci_ers_result_t beiscsi_eeh_reset(struct pci_dev *pdev)
                return PCI_ERS_RESULT_DISCONNECT;
        }
 
-       pci_cleanup_aer_uncorrect_error_status(pdev);
        return PCI_ERS_RESULT_RECOVERED;
 }
 
index bd7e6a6fc1f184e0b0065408864e7199d0adb46b..911efc98d1fd9f4048d4a25b6fd0cb998bde783a 100644 (file)
@@ -1569,8 +1569,6 @@ bfad_pci_slot_reset(struct pci_dev *pdev)
                if (pci_set_dma_mask(bfad->pcidev, DMA_BIT_MASK(32)) != 0)
                        goto out_disable_device;
 
-       pci_cleanup_aer_uncorrect_error_status(pdev);
-
        if (restart_bfa(bfad) == -1)
                goto out_disable_device;
 
index ed2dae657964be4997934c1efe191fb8b556363f..66b230bee7bcd6b1bd35acfae13af68fb07fde72 100644 (file)
@@ -1102,7 +1102,6 @@ csio_pci_slot_reset(struct pci_dev *pdev)
        pci_set_master(pdev);
        pci_restore_state(pdev);
        pci_save_state(pdev);
-       pci_cleanup_aer_uncorrect_error_status(pdev);
 
        /* Bring HW s/m to ready state.
         * but don't resume IOs.
index f3cae733ae2dffea29e37ff562853c2260f9b681..0503237b814546150fccb85e5d3bd230879a05fb 100644 (file)
@@ -11329,10 +11329,6 @@ lpfc_io_resume_s3(struct pci_dev *pdev)
 
        /* Bring device online, it will be no-op for non-fatal error resume */
        lpfc_online(phba);
-
-       /* Clean up Advanced Error Reporting (AER) if needed */
-       if (phba->hba_flag & HBA_AER_ENABLED)
-               pci_cleanup_aer_uncorrect_error_status(pdev);
 }
 
 /**
@@ -12144,10 +12140,6 @@ lpfc_io_resume_s4(struct pci_dev *pdev)
                /* Bring the device back online */
                lpfc_online(phba);
        }
-
-       /* Clean up Advanced Error Reporting (AER) if needed */
-       if (phba->hba_flag & HBA_AER_ENABLED)
-               pci_cleanup_aer_uncorrect_error_status(pdev);
 }
 
 /**
index 53133cfd420f1d2c7c90dd3e0bdc87bf233feece..86eaa893adfc4fe0fa1faf7647117115f62cf2d5 100644 (file)
@@ -10828,7 +10828,6 @@ scsih_pci_resume(struct pci_dev *pdev)
 
        pr_info(MPT3SAS_FMT "PCI error: resume callback!!\n", ioc->name);
 
-       pci_cleanup_aer_uncorrect_error_status(pdev);
        mpt3sas_base_start_watchdog(ioc);
        scsi_unblock_requests(ioc->shost);
 }
index 42b8f0d3e580da932237826d07c2c36e49bfa7f3..8fe2d7329bfe11bb98cf2a1d6d9f0b691c907703 100644 (file)
@@ -6839,8 +6839,6 @@ qla2xxx_pci_resume(struct pci_dev *pdev)
                    "The device failed to resume I/O from slot/link_reset.\n");
        }
 
-       pci_cleanup_aer_uncorrect_error_status(pdev);
-
        ha->flags.eeh_busy = 0;
 }
 
index 0e13349dce57094b3bc211093ec93a26f2797f11..ab3a924e3e110e4a9d9f12b330a9080a95b395e0 100644 (file)
@@ -9824,7 +9824,6 @@ qla4xxx_pci_resume(struct pci_dev *pdev)
                     __func__);
        }
 
-       pci_cleanup_aer_uncorrect_error_status(pdev);
        clear_bit(AF_EEH_BUSY, &ha->flags);
 }
 
index ba4dd54f2c8214c81af5449088982e604cb8d3aa..cd35e3ce9a8b91ea32fef39de19a9ee7d9a2cc8b 100644 (file)
@@ -346,10 +346,16 @@ struct acpi_device_physical_node {
        bool put_online:1;
 };
 
+struct acpi_device_properties {
+       const guid_t *guid;
+       const union acpi_object *properties;
+       struct list_head list;
+};
+
 /* ACPI Device Specific Data (_DSD) */
 struct acpi_device_data {
        const union acpi_object *pointer;
-       const union acpi_object *properties;
+       struct list_head properties;
        const union acpi_object *of_compatible;
        struct list_head subnodes;
 };
index de8d3d3fa6512e3e9382e23cf4fcd03c36b77097..51e3c29663fed52ca11a1a1f6130f0cff4647650 100644 (file)
@@ -1074,6 +1074,15 @@ static inline int acpi_node_get_property_reference(
                NR_FWNODE_REFERENCE_ARGS, args);
 }
 
+static inline bool acpi_dev_has_props(const struct acpi_device *adev)
+{
+       return !list_empty(&adev->data.properties);
+}
+
+struct acpi_device_properties *
+acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid,
+                   const union acpi_object *properties);
+
 int acpi_node_prop_get(const struct fwnode_handle *fwnode, const char *propname,
                       void **valptr);
 int acpi_dev_prop_read_single(struct acpi_device *adev,
index 6980014357d477793079164ce67393659def6052..c32f7171899be46fb82edae93c6ab78ed19c2a26 100644 (file)
@@ -699,6 +699,7 @@ struct request_queue {
 #define QUEUE_FLAG_SCSI_PASSTHROUGH 27 /* queue supports SCSI commands */
 #define QUEUE_FLAG_QUIESCED    28      /* queue has been quiesced */
 #define QUEUE_FLAG_PREEMPT_ONLY        29      /* only process REQ_PREEMPT requests */
+#define QUEUE_FLAG_PCI_P2PDMA  30      /* device supports PCI p2p requests */
 
 #define QUEUE_FLAG_DEFAULT     ((1 << QUEUE_FLAG_IO_STAT) |            \
                                 (1 << QUEUE_FLAG_SAME_COMP)    |       \
@@ -731,6 +732,8 @@ bool blk_queue_flag_test_and_clear(unsigned int flag, struct request_queue *q);
 #define blk_queue_dax(q)       test_bit(QUEUE_FLAG_DAX, &(q)->queue_flags)
 #define blk_queue_scsi_passthrough(q)  \
        test_bit(QUEUE_FLAG_SCSI_PASSTHROUGH, &(q)->queue_flags)
+#define blk_queue_pci_p2pdma(q)        \
+       test_bit(QUEUE_FLAG_PCI_P2PDMA, &(q)->queue_flags)
 
 #define blk_noretry_request(rq) \
        ((rq)->cmd_flags & (REQ_FAILFAST_DEV|REQ_FAILFAST_TRANSPORT| \
index f91f9e763557b260975edcc3be202a558640f9c0..0ac69ddf5fc461bb907fb28ff11f6a417a5354c3 100644 (file)
@@ -53,11 +53,16 @@ struct vmem_altmap {
  * wakeup event whenever a page is unpinned and becomes idle. This
  * wakeup is used to coordinate physical address space management (ex:
  * fs truncate/hole punch) vs pinned pages (ex: device dma).
+ *
+ * MEMORY_DEVICE_PCI_P2PDMA:
+ * Device memory residing in a PCI BAR intended for use with Peer-to-Peer
+ * transactions.
  */
 enum memory_type {
        MEMORY_DEVICE_PRIVATE = 1,
        MEMORY_DEVICE_PUBLIC,
        MEMORY_DEVICE_FS_DAX,
+       MEMORY_DEVICE_PCI_P2PDMA,
 };
 
 /*
@@ -120,6 +125,7 @@ struct dev_pagemap {
        struct device *dev;
        void *data;
        enum memory_type type;
+       u64 pci_p2pdma_bus_offset;
 };
 
 #ifdef CONFIG_ZONE_DEVICE
index a61ebe8ad4ca92e72e23855c17f8e7c9ad059a54..2055df412a77ec0ebf371d10bac98a320ec0a0d9 100644 (file)
@@ -890,6 +890,19 @@ static inline bool is_device_public_page(const struct page *page)
                page->pgmap->type == MEMORY_DEVICE_PUBLIC;
 }
 
+#ifdef CONFIG_PCI_P2PDMA
+static inline bool is_pci_p2pdma_page(const struct page *page)
+{
+       return is_zone_device_page(page) &&
+               page->pgmap->type == MEMORY_DEVICE_PCI_P2PDMA;
+}
+#else /* CONFIG_PCI_P2PDMA */
+static inline bool is_pci_p2pdma_page(const struct page *page)
+{
+       return false;
+}
+#endif /* CONFIG_PCI_P2PDMA */
+
 #else /* CONFIG_DEV_PAGEMAP_OPS */
 static inline void dev_pagemap_get_ops(void)
 {
@@ -913,6 +926,11 @@ static inline bool is_device_public_page(const struct page *page)
 {
        return false;
 }
+
+static inline bool is_pci_p2pdma_page(const struct page *page)
+{
+       return false;
+}
 #endif /* CONFIG_DEV_PAGEMAP_OPS */
 
 static inline void get_page(struct page *page)
index c3f1b44ade29e9931ae26fa0c7395bacc74a66cf..cb1adf0b78a967bf38ef02b72172249bc7c2f397 100644 (file)
@@ -119,29 +119,11 @@ static inline int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask)
 {
        return dma_set_coherent_mask(&dev->dev, mask);
 }
-
-static inline int pci_set_dma_max_seg_size(struct pci_dev *dev,
-                                          unsigned int size)
-{
-       return dma_set_max_seg_size(&dev->dev, size);
-}
-
-static inline int pci_set_dma_seg_boundary(struct pci_dev *dev,
-                                          unsigned long mask)
-{
-       return dma_set_seg_boundary(&dev->dev, mask);
-}
 #else
 static inline int pci_set_dma_mask(struct pci_dev *dev, u64 mask)
 { return -EIO; }
 static inline int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask)
 { return -EIO; }
-static inline int pci_set_dma_max_seg_size(struct pci_dev *dev,
-                                          unsigned int size)
-{ return -EIO; }
-static inline int pci_set_dma_seg_boundary(struct pci_dev *dev,
-                                          unsigned long mask)
-{ return -EIO; }
 #endif
 
 #endif
diff --git a/include/linux/pci-dma.h b/include/linux/pci-dma.h
deleted file mode 100644 (file)
index 0f7aa73..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _LINUX_PCI_DMA_H
-#define _LINUX_PCI_DMA_H
-
-#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME) DEFINE_DMA_UNMAP_ADDR(ADDR_NAME);
-#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)   DEFINE_DMA_UNMAP_LEN(LEN_NAME);
-#define pci_unmap_addr             dma_unmap_addr
-#define pci_unmap_addr_set         dma_unmap_addr_set
-#define pci_unmap_len              dma_unmap_len
-#define pci_unmap_len_set          dma_unmap_len_set
-
-#endif
diff --git a/include/linux/pci-p2pdma.h b/include/linux/pci-p2pdma.h
new file mode 100644 (file)
index 0000000..bca9bc3
--- /dev/null
@@ -0,0 +1,114 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * PCI Peer 2 Peer DMA support.
+ *
+ * Copyright (c) 2016-2018, Logan Gunthorpe
+ * Copyright (c) 2016-2017, Microsemi Corporation
+ * Copyright (c) 2017, Christoph Hellwig
+ * Copyright (c) 2018, Eideticom Inc.
+ */
+
+#ifndef _LINUX_PCI_P2PDMA_H
+#define _LINUX_PCI_P2PDMA_H
+
+#include <linux/pci.h>
+
+struct block_device;
+struct scatterlist;
+
+#ifdef CONFIG_PCI_P2PDMA
+int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
+               u64 offset);
+int pci_p2pdma_distance_many(struct pci_dev *provider, struct device **clients,
+                            int num_clients, bool verbose);
+bool pci_has_p2pmem(struct pci_dev *pdev);
+struct pci_dev *pci_p2pmem_find_many(struct device **clients, int num_clients);
+void *pci_alloc_p2pmem(struct pci_dev *pdev, size_t size);
+void pci_free_p2pmem(struct pci_dev *pdev, void *addr, size_t size);
+pci_bus_addr_t pci_p2pmem_virt_to_bus(struct pci_dev *pdev, void *addr);
+struct scatterlist *pci_p2pmem_alloc_sgl(struct pci_dev *pdev,
+                                        unsigned int *nents, u32 length);
+void pci_p2pmem_free_sgl(struct pci_dev *pdev, struct scatterlist *sgl);
+void pci_p2pmem_publish(struct pci_dev *pdev, bool publish);
+int pci_p2pdma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+                     enum dma_data_direction dir);
+int pci_p2pdma_enable_store(const char *page, struct pci_dev **p2p_dev,
+                           bool *use_p2pdma);
+ssize_t pci_p2pdma_enable_show(char *page, struct pci_dev *p2p_dev,
+                              bool use_p2pdma);
+#else /* CONFIG_PCI_P2PDMA */
+static inline int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar,
+               size_t size, u64 offset)
+{
+       return -EOPNOTSUPP;
+}
+static inline int pci_p2pdma_distance_many(struct pci_dev *provider,
+       struct device **clients, int num_clients, bool verbose)
+{
+       return -1;
+}
+static inline bool pci_has_p2pmem(struct pci_dev *pdev)
+{
+       return false;
+}
+static inline struct pci_dev *pci_p2pmem_find_many(struct device **clients,
+                                                  int num_clients)
+{
+       return NULL;
+}
+static inline void *pci_alloc_p2pmem(struct pci_dev *pdev, size_t size)
+{
+       return NULL;
+}
+static inline void pci_free_p2pmem(struct pci_dev *pdev, void *addr,
+               size_t size)
+{
+}
+static inline pci_bus_addr_t pci_p2pmem_virt_to_bus(struct pci_dev *pdev,
+                                                   void *addr)
+{
+       return 0;
+}
+static inline struct scatterlist *pci_p2pmem_alloc_sgl(struct pci_dev *pdev,
+               unsigned int *nents, u32 length)
+{
+       return NULL;
+}
+static inline void pci_p2pmem_free_sgl(struct pci_dev *pdev,
+               struct scatterlist *sgl)
+{
+}
+static inline void pci_p2pmem_publish(struct pci_dev *pdev, bool publish)
+{
+}
+static inline int pci_p2pdma_map_sg(struct device *dev,
+               struct scatterlist *sg, int nents, enum dma_data_direction dir)
+{
+       return 0;
+}
+static inline int pci_p2pdma_enable_store(const char *page,
+               struct pci_dev **p2p_dev, bool *use_p2pdma)
+{
+       *use_p2pdma = false;
+       return 0;
+}
+static inline ssize_t pci_p2pdma_enable_show(char *page,
+               struct pci_dev *p2p_dev, bool use_p2pdma)
+{
+       return sprintf(page, "none\n");
+}
+#endif /* CONFIG_PCI_P2PDMA */
+
+
+static inline int pci_p2pdma_distance(struct pci_dev *provider,
+       struct device *client, bool verbose)
+{
+       return pci_p2pdma_distance_many(provider, &client, 1, verbose);
+}
+
+static inline struct pci_dev *pci_p2pmem_find(struct device *client)
+{
+       return pci_p2pmem_find_many(&client, 1);
+}
+
+#endif /* _LINUX_PCI_P2P_H */
index 6925828f9f250fae21e19ef8338d46694621e2a2..f9e04c1703017adbc832dfd5074bdaa745f33c55 100644 (file)
@@ -281,6 +281,7 @@ struct pcie_link_state;
 struct pci_vpd;
 struct pci_sriov;
 struct pci_ats;
+struct pci_p2pdma;
 
 /* The pci_dev structure describes PCI devices */
 struct pci_dev {
@@ -325,6 +326,7 @@ struct pci_dev {
        pci_power_t     current_state;  /* Current operating state. In ACPI,
                                           this is D0-D3, D0 being fully
                                           functional, and D3 being off. */
+       unsigned int    imm_ready:1;    /* Supports Immediate Readiness */
        u8              pm_cap;         /* PM capability offset */
        unsigned int    pme_support:5;  /* Bitmask of states from which PME#
                                           can be generated */
@@ -402,6 +404,7 @@ struct pci_dev {
        unsigned int    has_secondary_link:1;
        unsigned int    non_compliant_bars:1;   /* Broken BARs; ignore them */
        unsigned int    is_probed:1;            /* Device probing in progress */
+       unsigned int    link_active_reporting:1;/* Device capable of reporting link active */
        pci_dev_flags_t dev_flags;
        atomic_t        enable_cnt;     /* pci_enable_device has been called */
 
@@ -438,6 +441,9 @@ struct pci_dev {
 #endif
 #ifdef CONFIG_PCI_PASID
        u16             pasid_features;
+#endif
+#ifdef CONFIG_PCI_P2PDMA
+       struct pci_p2pdma *p2pdma;
 #endif
        phys_addr_t     rom;            /* Physical address if not from BAR */
        size_t          romlen;         /* Length if not from BAR */
@@ -1342,7 +1348,6 @@ int pci_set_vga_state(struct pci_dev *pdev, bool decode,
 
 /* kmem_cache style wrapper around pci_alloc_consistent() */
 
-#include <linux/pci-dma.h>
 #include <linux/dmapool.h>
 
 #define        pci_pool dma_pool
index a6d6650a0490a063c2f221e509979afa6c2fad49..7acc9f91e72b69a965f88da983ce9c6100ff5bdf 100644 (file)
@@ -16,8 +16,6 @@
 
 /**
  * struct hotplug_slot_ops -the callbacks that the hotplug pci core can use
- * @owner: The module owner of this structure
- * @mod_name: The module name (KBUILD_MODNAME) of this structure
  * @enable_slot: Called when the user wants to enable a specific pci slot
  * @disable_slot: Called when the user wants to disable a specific pci slot
  * @set_attention_status: Called to set the specific slot's attention LED to
  * @hardware_test: Called to run a specified hardware test on the specified
  * slot.
  * @get_power_status: Called to get the current power status of a slot.
- *     If this field is NULL, the value passed in the struct hotplug_slot_info
- *     will be used when this value is requested by a user.
  * @get_attention_status: Called to get the current attention status of a slot.
- *     If this field is NULL, the value passed in the struct hotplug_slot_info
- *     will be used when this value is requested by a user.
  * @get_latch_status: Called to get the current latch status of a slot.
- *     If this field is NULL, the value passed in the struct hotplug_slot_info
- *     will be used when this value is requested by a user.
  * @get_adapter_status: Called to get see if an adapter is present in the slot or not.
- *     If this field is NULL, the value passed in the struct hotplug_slot_info
- *     will be used when this value is requested by a user.
  * @reset_slot: Optional interface to allow override of a bus reset for the
  *     slot for cases where a secondary bus reset can result in spurious
  *     hotplug events or where a slot can be reset independent of the bus.
@@ -46,8 +36,6 @@
  * set an LED, enable / disable power, etc.)
  */
 struct hotplug_slot_ops {
-       struct module *owner;
-       const char *mod_name;
        int (*enable_slot)              (struct hotplug_slot *slot);
        int (*disable_slot)             (struct hotplug_slot *slot);
        int (*set_attention_status)     (struct hotplug_slot *slot, u8 value);
@@ -59,38 +47,20 @@ struct hotplug_slot_ops {
        int (*reset_slot)               (struct hotplug_slot *slot, int probe);
 };
 
-/**
- * struct hotplug_slot_info - used to notify the hotplug pci core of the state of the slot
- * @power_status: if power is enabled or not (1/0)
- * @attention_status: if the attention light is enabled or not (1/0)
- * @latch_status: if the latch (if any) is open or closed (1/0)
- * @adapter_status: if there is a pci board present in the slot or not (1/0)
- *
- * Used to notify the hotplug pci core of the status of a specific slot.
- */
-struct hotplug_slot_info {
-       u8      power_status;
-       u8      attention_status;
-       u8      latch_status;
-       u8      adapter_status;
-};
-
 /**
  * struct hotplug_slot - used to register a physical slot with the hotplug pci core
  * @ops: pointer to the &struct hotplug_slot_ops to be used for this slot
- * @info: pointer to the &struct hotplug_slot_info for the initial values for
- * this slot.
- * @private: used by the hotplug pci controller driver to store whatever it
- * needs.
+ * @owner: The module owner of this structure
+ * @mod_name: The module name (KBUILD_MODNAME) of this structure
  */
 struct hotplug_slot {
-       struct hotplug_slot_ops         *ops;
-       struct hotplug_slot_info        *info;
-       void                            *private;
+       const struct hotplug_slot_ops   *ops;
 
        /* Variables below this are for use only by the hotplug pci core. */
        struct list_head                slot_list;
        struct pci_slot                 *pci_slot;
+       struct module                   *owner;
+       const char                      *mod_name;
 };
 
 static inline const char *hotplug_slot_name(const struct hotplug_slot *slot)
@@ -110,9 +80,6 @@ void pci_hp_del(struct hotplug_slot *slot);
 void pci_hp_destroy(struct hotplug_slot *slot);
 void pci_hp_deregister(struct hotplug_slot *slot);
 
-int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
-                                        struct hotplug_slot_info *info);
-
 /* use a define to avoid include chaining to get THIS_MODULE & friends */
 #define pci_hp_register(slot, pbus, devnr, name) \
        __pci_hp_register(slot, pbus, devnr, name, THIS_MODULE, KBUILD_MODNAME)
index d157983b84cf9258fa43b639accc8ee3904a080c..f4e278493f5b4e48fbc16fe6aafb90b27c21313c 100644 (file)
 #define PCI_VENDOR_ID_HUAWEI           0x19e5
 
 #define PCI_VENDOR_ID_NETRONOME                0x19ee
-#define PCI_DEVICE_ID_NETRONOME_NFP3200        0x3200
-#define PCI_DEVICE_ID_NETRONOME_NFP3240        0x3240
 #define PCI_DEVICE_ID_NETRONOME_NFP4000        0x4000
 #define PCI_DEVICE_ID_NETRONOME_NFP5000        0x5000
 #define PCI_DEVICE_ID_NETRONOME_NFP6000        0x6000
index ee556ccc93f48c2e715875d9b94d69e6ce638e72..e1e9888c85e6810aad2e54ab25cbbaf49b72ba57 100644 (file)
@@ -52,6 +52,7 @@
 #define  PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */
 
 #define PCI_STATUS             0x06    /* 16 bits */
+#define  PCI_STATUS_IMM_READY  0x01    /* Immediate Readiness */
 #define  PCI_STATUS_INTERRUPT  0x08    /* Interrupt status */
 #define  PCI_STATUS_CAP_LIST   0x10    /* Support Capability List */
 #define  PCI_STATUS_66MHZ      0x20    /* Support 66 MHz PCI 2.1 bus */