Commit 8a1d8246 authored by Lu Baolu's avatar Lu Baolu Committed by Joerg Roedel

iommu/vt-d: Multiple descriptors per qi_submit_sync()

Current qi_submit_sync() only supports single invalidation descriptor
per submission and appends wait descriptor after each submission to
poll the hardware completion. This extends the qi_submit_sync() helper
to support multiple descriptors, and add an option so that the caller
could specify the Page-request Drain (PD) bit in the wait descriptor.
Signed-off-by: default avatarJacob Pan <jacob.jun.pan@linux.intel.com>
Signed-off-by: default avatarLu Baolu <baolu.lu@linux.intel.com>
Reviewed-by: default avatarKevin Tian <kevin.tian@intel.com>
Link: https://lore.kernel.org/r/20200516062101.29541-13-baolu.lu@linux.intel.comSigned-off-by: default avatarJoerg Roedel <jroedel@suse.de>
parent 064a57d7
...@@ -1157,12 +1157,11 @@ static inline void reclaim_free_desc(struct q_inval *qi) ...@@ -1157,12 +1157,11 @@ static inline void reclaim_free_desc(struct q_inval *qi)
} }
} }
static int qi_check_fault(struct intel_iommu *iommu, int index) static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
{ {
u32 fault; u32 fault;
int head, tail; int head, tail;
struct q_inval *qi = iommu->qi; struct q_inval *qi = iommu->qi;
int wait_index = (index + 1) % QI_LENGTH;
int shift = qi_shift(iommu); int shift = qi_shift(iommu);
if (qi->desc_status[wait_index] == QI_ABORT) if (qi->desc_status[wait_index] == QI_ABORT)
...@@ -1225,17 +1224,21 @@ static int qi_check_fault(struct intel_iommu *iommu, int index) ...@@ -1225,17 +1224,21 @@ static int qi_check_fault(struct intel_iommu *iommu, int index)
} }
/* /*
* Submit the queued invalidation descriptor to the remapping * Function to submit invalidation descriptors of all types to the queued
* hardware unit and wait for its completion. * invalidation interface(QI). Multiple descriptors can be submitted at a
* time, a wait descriptor will be appended to each submission to ensure
* hardware has completed the invalidation before return. Wait descriptors
* can be part of the submission but it will not be polled for completion.
*/ */
int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu) int qi_submit_sync(struct intel_iommu *iommu, struct qi_desc *desc,
unsigned int count, unsigned long options)
{ {
int rc;
struct q_inval *qi = iommu->qi; struct q_inval *qi = iommu->qi;
int offset, shift, length;
struct qi_desc wait_desc; struct qi_desc wait_desc;
int wait_index, index; int wait_index, index;
unsigned long flags; unsigned long flags;
int offset, shift;
int rc, i;
if (!qi) if (!qi)
return 0; return 0;
...@@ -1244,32 +1247,41 @@ int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu) ...@@ -1244,32 +1247,41 @@ int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu)
rc = 0; rc = 0;
raw_spin_lock_irqsave(&qi->q_lock, flags); raw_spin_lock_irqsave(&qi->q_lock, flags);
while (qi->free_cnt < 3) { /*
* Check if we have enough empty slots in the queue to submit,
* the calculation is based on:
* # of desc + 1 wait desc + 1 space between head and tail
*/
while (qi->free_cnt < count + 2) {
raw_spin_unlock_irqrestore(&qi->q_lock, flags); raw_spin_unlock_irqrestore(&qi->q_lock, flags);
cpu_relax(); cpu_relax();
raw_spin_lock_irqsave(&qi->q_lock, flags); raw_spin_lock_irqsave(&qi->q_lock, flags);
} }
index = qi->free_head; index = qi->free_head;
wait_index = (index + 1) % QI_LENGTH; wait_index = (index + count) % QI_LENGTH;
shift = qi_shift(iommu); shift = qi_shift(iommu);
length = 1 << shift;
qi->desc_status[index] = qi->desc_status[wait_index] = QI_IN_USE; for (i = 0; i < count; i++) {
offset = ((index + i) % QI_LENGTH) << shift;
memcpy(qi->desc + offset, &desc[i], 1 << shift);
qi->desc_status[(index + i) % QI_LENGTH] = QI_IN_USE;
}
qi->desc_status[wait_index] = QI_IN_USE;
offset = index << shift;
memcpy(qi->desc + offset, desc, length);
wait_desc.qw0 = QI_IWD_STATUS_DATA(QI_DONE) | wait_desc.qw0 = QI_IWD_STATUS_DATA(QI_DONE) |
QI_IWD_STATUS_WRITE | QI_IWD_TYPE; QI_IWD_STATUS_WRITE | QI_IWD_TYPE;
if (options & QI_OPT_WAIT_DRAIN)
wait_desc.qw0 |= QI_IWD_PRQ_DRAIN;
wait_desc.qw1 = virt_to_phys(&qi->desc_status[wait_index]); wait_desc.qw1 = virt_to_phys(&qi->desc_status[wait_index]);
wait_desc.qw2 = 0; wait_desc.qw2 = 0;
wait_desc.qw3 = 0; wait_desc.qw3 = 0;
offset = wait_index << shift; offset = wait_index << shift;
memcpy(qi->desc + offset, &wait_desc, length); memcpy(qi->desc + offset, &wait_desc, 1 << shift);
qi->free_head = (qi->free_head + 2) % QI_LENGTH; qi->free_head = (qi->free_head + count + 1) % QI_LENGTH;
qi->free_cnt -= 2; qi->free_cnt -= count + 1;
/* /*
* update the HW tail register indicating the presence of * update the HW tail register indicating the presence of
...@@ -1285,7 +1297,7 @@ int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu) ...@@ -1285,7 +1297,7 @@ int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu)
* a deadlock where the interrupt context can wait indefinitely * a deadlock where the interrupt context can wait indefinitely
* for free slots in the queue. * for free slots in the queue.
*/ */
rc = qi_check_fault(iommu, index); rc = qi_check_fault(iommu, index, wait_index);
if (rc) if (rc)
break; break;
...@@ -1294,7 +1306,8 @@ int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu) ...@@ -1294,7 +1306,8 @@ int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu)
raw_spin_lock(&qi->q_lock); raw_spin_lock(&qi->q_lock);
} }
qi->desc_status[index] = QI_DONE; for (i = 0; i < count; i++)
qi->desc_status[(index + i) % QI_LENGTH] = QI_DONE;
reclaim_free_desc(qi); reclaim_free_desc(qi);
raw_spin_unlock_irqrestore(&qi->q_lock, flags); raw_spin_unlock_irqrestore(&qi->q_lock, flags);
...@@ -1318,7 +1331,7 @@ void qi_global_iec(struct intel_iommu *iommu) ...@@ -1318,7 +1331,7 @@ void qi_global_iec(struct intel_iommu *iommu)
desc.qw3 = 0; desc.qw3 = 0;
/* should never fail */ /* should never fail */
qi_submit_sync(&desc, iommu); qi_submit_sync(iommu, &desc, 1, 0);
} }
void qi_flush_context(struct intel_iommu *iommu, u16 did, u16 sid, u8 fm, void qi_flush_context(struct intel_iommu *iommu, u16 did, u16 sid, u8 fm,
...@@ -1332,7 +1345,7 @@ void qi_flush_context(struct intel_iommu *iommu, u16 did, u16 sid, u8 fm, ...@@ -1332,7 +1345,7 @@ void qi_flush_context(struct intel_iommu *iommu, u16 did, u16 sid, u8 fm,
desc.qw2 = 0; desc.qw2 = 0;
desc.qw3 = 0; desc.qw3 = 0;
qi_submit_sync(&desc, iommu); qi_submit_sync(iommu, &desc, 1, 0);
} }
void qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr, void qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr,
...@@ -1356,7 +1369,7 @@ void qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr, ...@@ -1356,7 +1369,7 @@ void qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr,
desc.qw2 = 0; desc.qw2 = 0;
desc.qw3 = 0; desc.qw3 = 0;
qi_submit_sync(&desc, iommu); qi_submit_sync(iommu, &desc, 1, 0);
} }
void qi_flush_dev_iotlb(struct intel_iommu *iommu, u16 sid, u16 pfsid, void qi_flush_dev_iotlb(struct intel_iommu *iommu, u16 sid, u16 pfsid,
...@@ -1378,7 +1391,7 @@ void qi_flush_dev_iotlb(struct intel_iommu *iommu, u16 sid, u16 pfsid, ...@@ -1378,7 +1391,7 @@ void qi_flush_dev_iotlb(struct intel_iommu *iommu, u16 sid, u16 pfsid,
desc.qw2 = 0; desc.qw2 = 0;
desc.qw3 = 0; desc.qw3 = 0;
qi_submit_sync(&desc, iommu); qi_submit_sync(iommu, &desc, 1, 0);
} }
/* PASID-based IOTLB invalidation */ /* PASID-based IOTLB invalidation */
...@@ -1419,7 +1432,7 @@ void qi_flush_piotlb(struct intel_iommu *iommu, u16 did, u32 pasid, u64 addr, ...@@ -1419,7 +1432,7 @@ void qi_flush_piotlb(struct intel_iommu *iommu, u16 did, u32 pasid, u64 addr,
QI_EIOTLB_AM(mask); QI_EIOTLB_AM(mask);
} }
qi_submit_sync(&desc, iommu); qi_submit_sync(iommu, &desc, 1, 0);
} }
/* PASID-based device IOTLB Invalidate */ /* PASID-based device IOTLB Invalidate */
...@@ -1448,7 +1461,7 @@ void qi_flush_dev_iotlb_pasid(struct intel_iommu *iommu, u16 sid, u16 pfsid, ...@@ -1448,7 +1461,7 @@ void qi_flush_dev_iotlb_pasid(struct intel_iommu *iommu, u16 sid, u16 pfsid,
if (size_order) if (size_order)
desc.qw1 |= QI_DEV_EIOTLB_SIZE; desc.qw1 |= QI_DEV_EIOTLB_SIZE;
qi_submit_sync(&desc, iommu); qi_submit_sync(iommu, &desc, 1, 0);
} }
void qi_flush_pasid_cache(struct intel_iommu *iommu, u16 did, void qi_flush_pasid_cache(struct intel_iommu *iommu, u16 did,
...@@ -1458,7 +1471,7 @@ void qi_flush_pasid_cache(struct intel_iommu *iommu, u16 did, ...@@ -1458,7 +1471,7 @@ void qi_flush_pasid_cache(struct intel_iommu *iommu, u16 did,
desc.qw0 = QI_PC_PASID(pasid) | QI_PC_DID(did) | desc.qw0 = QI_PC_PASID(pasid) | QI_PC_DID(did) |
QI_PC_GRAN(granu) | QI_PC_TYPE; QI_PC_GRAN(granu) | QI_PC_TYPE;
qi_submit_sync(&desc, iommu); qi_submit_sync(iommu, &desc, 1, 0);
} }
/* /*
......
...@@ -438,7 +438,7 @@ pasid_cache_invalidation_with_pasid(struct intel_iommu *iommu, ...@@ -438,7 +438,7 @@ pasid_cache_invalidation_with_pasid(struct intel_iommu *iommu,
desc.qw2 = 0; desc.qw2 = 0;
desc.qw3 = 0; desc.qw3 = 0;
qi_submit_sync(&desc, iommu); qi_submit_sync(iommu, &desc, 1, 0);
} }
static void static void
...@@ -452,7 +452,7 @@ iotlb_invalidation_with_pasid(struct intel_iommu *iommu, u16 did, u32 pasid) ...@@ -452,7 +452,7 @@ iotlb_invalidation_with_pasid(struct intel_iommu *iommu, u16 did, u32 pasid)
desc.qw2 = 0; desc.qw2 = 0;
desc.qw3 = 0; desc.qw3 = 0;
qi_submit_sync(&desc, iommu); qi_submit_sync(iommu, &desc, 1, 0);
} }
static void static void
......
...@@ -138,7 +138,7 @@ static void intel_flush_svm_range_dev (struct intel_svm *svm, struct intel_svm_d ...@@ -138,7 +138,7 @@ static void intel_flush_svm_range_dev (struct intel_svm *svm, struct intel_svm_d
} }
desc.qw2 = 0; desc.qw2 = 0;
desc.qw3 = 0; desc.qw3 = 0;
qi_submit_sync(&desc, svm->iommu); qi_submit_sync(svm->iommu, &desc, 1, 0);
if (sdev->dev_iotlb) { if (sdev->dev_iotlb) {
desc.qw0 = QI_DEV_EIOTLB_PASID(svm->pasid) | desc.qw0 = QI_DEV_EIOTLB_PASID(svm->pasid) |
...@@ -162,7 +162,7 @@ static void intel_flush_svm_range_dev (struct intel_svm *svm, struct intel_svm_d ...@@ -162,7 +162,7 @@ static void intel_flush_svm_range_dev (struct intel_svm *svm, struct intel_svm_d
} }
desc.qw2 = 0; desc.qw2 = 0;
desc.qw3 = 0; desc.qw3 = 0;
qi_submit_sync(&desc, svm->iommu); qi_submit_sync(svm->iommu, &desc, 1, 0);
} }
} }
...@@ -846,7 +846,7 @@ static irqreturn_t prq_event_thread(int irq, void *d) ...@@ -846,7 +846,7 @@ static irqreturn_t prq_event_thread(int irq, void *d)
sizeof(req->priv_data)); sizeof(req->priv_data));
resp.qw2 = 0; resp.qw2 = 0;
resp.qw3 = 0; resp.qw3 = 0;
qi_submit_sync(&resp, iommu); qi_submit_sync(iommu, &resp, 1, 0);
} }
head = (head + sizeof(*req)) & PRQ_RING_MASK; head = (head + sizeof(*req)) & PRQ_RING_MASK;
} }
......
...@@ -151,7 +151,7 @@ static int qi_flush_iec(struct intel_iommu *iommu, int index, int mask) ...@@ -151,7 +151,7 @@ static int qi_flush_iec(struct intel_iommu *iommu, int index, int mask)
desc.qw2 = 0; desc.qw2 = 0;
desc.qw3 = 0; desc.qw3 = 0;
return qi_submit_sync(&desc, iommu); return qi_submit_sync(iommu, &desc, 1, 0);
} }
static int modify_irte(struct irq_2_iommu *irq_iommu, static int modify_irte(struct irq_2_iommu *irq_iommu,
......
...@@ -333,6 +333,7 @@ enum { ...@@ -333,6 +333,7 @@ enum {
#define QI_IWD_STATUS_DATA(d) (((u64)d) << 32) #define QI_IWD_STATUS_DATA(d) (((u64)d) << 32)
#define QI_IWD_STATUS_WRITE (((u64)1) << 5) #define QI_IWD_STATUS_WRITE (((u64)1) << 5)
#define QI_IWD_PRQ_DRAIN (((u64)1) << 7)
#define QI_IOTLB_DID(did) (((u64)did) << 16) #define QI_IOTLB_DID(did) (((u64)did) << 16)
#define QI_IOTLB_DR(dr) (((u64)dr) << 7) #define QI_IOTLB_DR(dr) (((u64)dr) << 7)
...@@ -702,7 +703,13 @@ void qi_flush_dev_iotlb_pasid(struct intel_iommu *iommu, u16 sid, u16 pfsid, ...@@ -702,7 +703,13 @@ void qi_flush_dev_iotlb_pasid(struct intel_iommu *iommu, u16 sid, u16 pfsid,
void qi_flush_pasid_cache(struct intel_iommu *iommu, u16 did, u64 granu, void qi_flush_pasid_cache(struct intel_iommu *iommu, u16 did, u64 granu,
int pasid); int pasid);
extern int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu); int qi_submit_sync(struct intel_iommu *iommu, struct qi_desc *desc,
unsigned int count, unsigned long options);
/*
* Options used in qi_submit_sync:
* QI_OPT_WAIT_DRAIN - Wait for PRQ drain completion, spec 6.5.2.8.
*/
#define QI_OPT_WAIT_DRAIN BIT(0)
extern int dmar_ir_support(void); extern int dmar_ir_support(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment