Skip to content

Commit

Permalink
PCI: Add implementation for PRI capability
Browse files Browse the repository at this point in the history
Implement the necessary functions to handle PRI capabilities
on PCIe devices. With PRI devices behind an IOMMU can signal
page fault conditions to software and recover from such
faults.

Reviewed-by: Bjorn Helgaas <bhelgaas@google.com>
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
  • Loading branch information
Joerg Roedel authored and Jesse Barnes committed Oct 14, 2011
1 parent d4c0636 commit c320b97
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 0 deletions.
9 changes: 9 additions & 0 deletions drivers/pci/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ config PCI_IOV

If unsure, say N.

config PCI_PRI
bool "PCI PRI support"
select PCI_ATS
help
PRI is the PCI Page Request Interface. It allows PCI devices that are
behind an IOMMU to recover from page faults.

If unsure, say N.

config PCI_IOAPIC
bool
depends on PCI
Expand Down
167 changes: 167 additions & 0 deletions drivers/pci/ats.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
* drivers/pci/ats.c
*
* Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
* Copyright (C) 2011 Advanced Micro Devices,
*
* PCI Express I/O Virtualization (IOV) support.
* Address Translation Service 1.0
* Page Request Interface added by Joerg Roedel <joerg.roedel@amd.com>
*/

#include <linux/pci-ats.h>
Expand Down Expand Up @@ -156,3 +158,168 @@ int pci_ats_queue_depth(struct pci_dev *dev)
PCI_ATS_MAX_QDEP;
}
EXPORT_SYMBOL_GPL(pci_ats_queue_depth);

#ifdef CONFIG_PCI_PRI
/**
* pci_enable_pri - Enable PRI capability
* @ pdev: PCI device structure
*
* Returns 0 on success, negative value on error
*/
int pci_enable_pri(struct pci_dev *pdev, u32 reqs)
{
u16 control, status;
u32 max_requests;
int pos;

pos = pci_find_ext_capability(pdev, PCI_PRI_CAP);
if (!pos)
return -EINVAL;

pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control);
pci_read_config_word(pdev, pos + PCI_PRI_STATUS_OFF, &status);
if ((control & PCI_PRI_ENABLE) || !(status & PCI_PRI_STATUS_STOPPED))
return -EBUSY;

pci_read_config_dword(pdev, pos + PCI_PRI_MAX_REQ_OFF, &max_requests);
reqs = min(max_requests, reqs);
pci_write_config_dword(pdev, pos + PCI_PRI_ALLOC_REQ_OFF, reqs);

control |= PCI_PRI_ENABLE;
pci_write_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, control);

return 0;
}
EXPORT_SYMBOL_GPL(pci_enable_pri);

/**
* pci_disable_pri - Disable PRI capability
* @pdev: PCI device structure
*
* Only clears the enabled-bit, regardless of its former value
*/
void pci_disable_pri(struct pci_dev *pdev)
{
u16 control;
int pos;

pos = pci_find_ext_capability(pdev, PCI_PRI_CAP);
if (!pos)
return;

pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control);
control &= ~PCI_PRI_ENABLE;
pci_write_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, control);
}
EXPORT_SYMBOL_GPL(pci_disable_pri);

/**
* pci_pri_enabled - Checks if PRI capability is enabled
* @pdev: PCI device structure
*
* Returns true if PRI is enabled on the device, false otherwise
*/
bool pci_pri_enabled(struct pci_dev *pdev)
{
u16 control;
int pos;

pos = pci_find_ext_capability(pdev, PCI_PRI_CAP);
if (!pos)
return false;

pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control);

return (control & PCI_PRI_ENABLE) ? true : false;
}
EXPORT_SYMBOL_GPL(pci_pri_enabled);

/**
* pci_reset_pri - Resets device's PRI state
* @pdev: PCI device structure
*
* The PRI capability must be disabled before this function is called.
* Returns 0 on success, negative value on error.
*/
int pci_reset_pri(struct pci_dev *pdev)
{
u16 control;
int pos;

pos = pci_find_ext_capability(pdev, PCI_PRI_CAP);
if (!pos)
return -EINVAL;

pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control);
if (control & PCI_PRI_ENABLE)
return -EBUSY;

control |= PCI_PRI_RESET;

pci_write_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, control);

return 0;
}
EXPORT_SYMBOL_GPL(pci_reset_pri);

/**
* pci_pri_stopped - Checks whether the PRI capability is stopped
* @pdev: PCI device structure
*
* Returns true if the PRI capability on the device is disabled and the
* device has no outstanding PRI requests, false otherwise. The device
* indicates this via the STOPPED bit in the status register of the
* capability.
* The device internal state can be cleared by resetting the PRI state
* with pci_reset_pri(). This can force the capability into the STOPPED
* state.
*/
bool pci_pri_stopped(struct pci_dev *pdev)
{
u16 control, status;
int pos;

pos = pci_find_ext_capability(pdev, PCI_PRI_CAP);
if (!pos)
return true;

pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control);
pci_read_config_word(pdev, pos + PCI_PRI_STATUS_OFF, &status);

if (control & PCI_PRI_ENABLE)
return false;

return (status & PCI_PRI_STATUS_STOPPED) ? true : false;
}
EXPORT_SYMBOL_GPL(pci_pri_stopped);

/**
* pci_pri_status - Request PRI status of a device
* @pdev: PCI device structure
*
* Returns negative value on failure, status on success. The status can
* be checked against status-bits. Supported bits are currently:
* PCI_PRI_STATUS_RF: Response failure
* PCI_PRI_STATUS_UPRGI: Unexpected Page Request Group Index
* PCI_PRI_STATUS_STOPPED: PRI has stopped
*/
int pci_pri_status(struct pci_dev *pdev)
{
u16 status, control;
int pos;

pos = pci_find_ext_capability(pdev, PCI_PRI_CAP);
if (!pos)
return -EINVAL;

pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control);
pci_read_config_word(pdev, pos + PCI_PRI_STATUS_OFF, &status);

/* Stopped bit is undefined when enable == 1, so clear it */
if (control & PCI_PRI_ENABLE)
status &= ~PCI_PRI_STATUS_STOPPED;

return status;
}
EXPORT_SYMBOL_GPL(pci_pri_status);
#endif /* CONFIG_PCI_PRI */
42 changes: 42 additions & 0 deletions include/linux/pci-ats.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ struct pci_ats {
extern int pci_enable_ats(struct pci_dev *dev, int ps);
extern void pci_disable_ats(struct pci_dev *dev);
extern int pci_ats_queue_depth(struct pci_dev *dev);

/**
* pci_ats_enabled - query the ATS status
* @dev: the PCI device
Expand Down Expand Up @@ -51,4 +52,45 @@ static inline int pci_ats_enabled(struct pci_dev *dev)

#endif /* CONFIG_PCI_IOV */

#ifdef CONFIG_PCI_PRI

extern int pci_enable_pri(struct pci_dev *pdev, u32 reqs);
extern void pci_disable_pri(struct pci_dev *pdev);
extern bool pci_pri_enabled(struct pci_dev *pdev);
extern int pci_reset_pri(struct pci_dev *pdev);
extern bool pci_pri_stopped(struct pci_dev *pdev);
extern int pci_pri_status(struct pci_dev *pdev);

#else /* CONFIG_PCI_PRI */

static inline int pci_enable_pri(struct pci_dev *pdev, u32 reqs)
{
return -ENODEV;
}

static inline void pci_disable_pri(struct pci_dev *pdev)
{
}

static inline bool pci_pri_enabled(struct pci_dev *pdev)
{
return false;
}

static inline int pci_reset_pri(struct pci_dev *pdev)
{
return -ENODEV;
}

static inline bool pci_pri_stopped(struct pci_dev *pdev)
{
return true;
}

static inline int pci_pri_status(struct pci_dev *pdev)
{
return -ENODEV;
}
#endif /* CONFIG_PCI_PRI */

#endif /* LINUX_PCI_ATS_H*/
12 changes: 12 additions & 0 deletions include/linux/pci_regs.h
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,18 @@
#define PCI_ATS_CTRL_STU(x) ((x) & 0x1f) /* Smallest Translation Unit */
#define PCI_ATS_MIN_STU 12 /* shift of minimum STU block */

/* Page Request Interface */
#define PCI_PRI_CAP 0x13 /* PRI capability ID */
#define PCI_PRI_CONTROL_OFF 0x04 /* Offset of control register */
#define PCI_PRI_STATUS_OFF 0x06 /* Offset of status register */
#define PCI_PRI_ENABLE 0x0001 /* Enable mask */
#define PCI_PRI_RESET 0x0002 /* Reset bit mask */
#define PCI_PRI_STATUS_RF 0x0001 /* Request Failure */
#define PCI_PRI_STATUS_UPRGI 0x0002 /* Unexpected PRG index */
#define PCI_PRI_STATUS_STOPPED 0x0100 /* PRI Stopped */
#define PCI_PRI_MAX_REQ_OFF 0x08 /* Cap offset for max reqs supported */
#define PCI_PRI_ALLOC_REQ_OFF 0x0c /* Cap offset for max reqs allowed */

/* Single Root I/O Virtualization */
#define PCI_SRIOV_CAP 0x04 /* SR-IOV Capabilities */
#define PCI_SRIOV_CAP_VFM 0x01 /* VF Migration Capable */
Expand Down

0 comments on commit c320b97

Please sign in to comment.