iommu/vt-d: Support page request in scalable mode

VT-d Rev3.0 has made a few changes to the page request interface,

1. widened PRQ descriptor from 128 bits to 256 bits;
2. removed streaming response type;
3. introduced private data that requires page response even the
   request is not last request in group (LPIG).

This is a supplement to commit 1c4f88b7f1 ("iommu/vt-d: Shared
virtual address in scalable mode") and makes the svm code compliant
with VT-d Rev3.0.

Cc: Ashok Raj <ashok.raj@intel.com>
Cc: Liu Yi L <yi.l.liu@intel.com>
Cc: Kevin Tian <kevin.tian@intel.com>
Signed-off-by: Jacob Pan <jacob.jun.pan@linux.intel.com>
Fixes: 1c4f88b7f1 ("iommu/vt-d: Shared virtual address in scalable mode")
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
This commit is contained in:
Jacob Pan 2019-01-11 13:04:57 +08:00 committed by Joerg Roedel
parent bfeffd1552
commit 5b438f4ba3
3 changed files with 55 additions and 45 deletions

View File

@ -470,20 +470,31 @@ EXPORT_SYMBOL_GPL(intel_svm_is_pasid_valid);
/* Page request queue descriptor */ /* Page request queue descriptor */
struct page_req_dsc { struct page_req_dsc {
u64 srr:1; union {
u64 bof:1; struct {
u64 pasid_present:1; u64 type:8;
u64 lpig:1; u64 pasid_present:1;
u64 pasid:20; u64 priv_data_present:1;
u64 bus:8; u64 rsvd:6;
u64 private:23; u64 rid:16;
u64 prg_index:9; u64 pasid:20;
u64 rd_req:1; u64 exe_req:1;
u64 wr_req:1; u64 pm_req:1;
u64 exe_req:1; u64 rsvd2:10;
u64 priv_req:1; };
u64 devfn:8; u64 qw_0;
u64 addr:52; };
union {
struct {
u64 rd_req:1;
u64 wr_req:1;
u64 lpig:1;
u64 prg_index:9;
u64 addr:52;
};
u64 qw_1;
};
u64 priv_data[2];
}; };
#define PRQ_RING_MASK ((0x1000 << PRQ_ORDER) - 0x10) #define PRQ_RING_MASK ((0x1000 << PRQ_ORDER) - 0x10)
@ -596,7 +607,7 @@ static irqreturn_t prq_event_thread(int irq, void *d)
/* Accounting for major/minor faults? */ /* Accounting for major/minor faults? */
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(sdev, &svm->devs, list) { list_for_each_entry_rcu(sdev, &svm->devs, list) {
if (sdev->sid == PCI_DEVID(req->bus, req->devfn)) if (sdev->sid == req->rid)
break; break;
} }
/* Other devices can go away, but the drivers are not permitted /* Other devices can go away, but the drivers are not permitted
@ -609,33 +620,35 @@ static irqreturn_t prq_event_thread(int irq, void *d)
if (sdev && sdev->ops && sdev->ops->fault_cb) { if (sdev && sdev->ops && sdev->ops->fault_cb) {
int rwxp = (req->rd_req << 3) | (req->wr_req << 2) | int rwxp = (req->rd_req << 3) | (req->wr_req << 2) |
(req->exe_req << 1) | (req->priv_req); (req->exe_req << 1) | (req->pm_req);
sdev->ops->fault_cb(sdev->dev, req->pasid, req->addr, req->private, rwxp, result); sdev->ops->fault_cb(sdev->dev, req->pasid, req->addr,
req->priv_data, rwxp, result);
} }
/* We get here in the error case where the PASID lookup failed, /* We get here in the error case where the PASID lookup failed,
and these can be NULL. Do not use them below this point! */ and these can be NULL. Do not use them below this point! */
sdev = NULL; sdev = NULL;
svm = NULL; svm = NULL;
no_pasid: no_pasid:
if (req->lpig) { if (req->lpig || req->priv_data_present) {
/* Page Group Response */ /*
* Per VT-d spec. v3.0 ch7.7, system software must
* respond with page group response if private data
* is present (PDP) or last page in group (LPIG) bit
* is set. This is an additional VT-d feature beyond
* PCI ATS spec.
*/
resp.qw0 = QI_PGRP_PASID(req->pasid) | resp.qw0 = QI_PGRP_PASID(req->pasid) |
QI_PGRP_DID((req->bus << 8) | req->devfn) | QI_PGRP_DID(req->rid) |
QI_PGRP_PASID_P(req->pasid_present) | QI_PGRP_PASID_P(req->pasid_present) |
QI_PGRP_PDP(req->pasid_present) |
QI_PGRP_RESP_CODE(result) |
QI_PGRP_RESP_TYPE; QI_PGRP_RESP_TYPE;
resp.qw1 = QI_PGRP_IDX(req->prg_index) | resp.qw1 = QI_PGRP_IDX(req->prg_index) |
QI_PGRP_PRIV(req->private) | QI_PGRP_LPIG(req->lpig);
QI_PGRP_RESP_CODE(result);
} else if (req->srr) { if (req->priv_data_present)
/* Page Stream Response */ memcpy(&resp.qw2, req->priv_data,
resp.qw0 = QI_PSTRM_IDX(req->prg_index) | sizeof(req->priv_data));
QI_PSTRM_PRIV(req->private) |
QI_PSTRM_BUS(req->bus) |
QI_PSTRM_PASID(req->pasid) |
QI_PSTRM_RESP_TYPE;
resp.qw1 = QI_PSTRM_ADDR(address) |
QI_PSTRM_DEVFN(req->devfn) |
QI_PSTRM_RESP_CODE(result);
} }
resp.qw2 = 0; resp.qw2 = 0;
resp.qw3 = 0; resp.qw3 = 0;

View File

@ -374,20 +374,17 @@ enum {
#define QI_DEV_EIOTLB_PFSID(pfsid) (((u64)(pfsid & 0xf) << 12) | ((u64)(pfsid & 0xfff) << 52)) #define QI_DEV_EIOTLB_PFSID(pfsid) (((u64)(pfsid & 0xf) << 12) | ((u64)(pfsid & 0xfff) << 52))
#define QI_DEV_EIOTLB_MAX_INVS 32 #define QI_DEV_EIOTLB_MAX_INVS 32
#define QI_PGRP_IDX(idx) (((u64)(idx)) << 55) /* Page group response descriptor QW0 */
#define QI_PGRP_PRIV(priv) (((u64)(priv)) << 32)
#define QI_PGRP_RESP_CODE(res) ((u64)(res))
#define QI_PGRP_PASID(pasid) (((u64)(pasid)) << 32)
#define QI_PGRP_DID(did) (((u64)(did)) << 16)
#define QI_PGRP_PASID_P(p) (((u64)(p)) << 4) #define QI_PGRP_PASID_P(p) (((u64)(p)) << 4)
#define QI_PGRP_PDP(p) (((u64)(p)) << 5)
#define QI_PGRP_RESP_CODE(res) (((u64)(res)) << 12)
#define QI_PGRP_DID(rid) (((u64)(rid)) << 16)
#define QI_PGRP_PASID(pasid) (((u64)(pasid)) << 32)
/* Page group response descriptor QW1 */
#define QI_PGRP_LPIG(x) (((u64)(x)) << 2)
#define QI_PGRP_IDX(idx) (((u64)(idx)) << 3)
#define QI_PSTRM_ADDR(addr) (((u64)(addr)) & VTD_PAGE_MASK)
#define QI_PSTRM_DEVFN(devfn) (((u64)(devfn)) << 4)
#define QI_PSTRM_RESP_CODE(res) ((u64)(res))
#define QI_PSTRM_IDX(idx) (((u64)(idx)) << 55)
#define QI_PSTRM_PRIV(priv) (((u64)(priv)) << 32)
#define QI_PSTRM_BUS(bus) (((u64)(bus)) << 24)
#define QI_PSTRM_PASID(pasid) (((u64)(pasid)) << 4)
#define QI_RESP_SUCCESS 0x0 #define QI_RESP_SUCCESS 0x0
#define QI_RESP_INVALID 0x1 #define QI_RESP_INVALID 0x1

View File

@ -20,7 +20,7 @@ struct device;
struct svm_dev_ops { struct svm_dev_ops {
void (*fault_cb)(struct device *dev, int pasid, u64 address, void (*fault_cb)(struct device *dev, int pasid, u64 address,
u32 private, int rwxp, int response); void *private, int rwxp, int response);
}; };
/* Values for rxwp in fault_cb callback */ /* Values for rxwp in fault_cb callback */