if (multipage) {
ctx->pt = sif_pt_create(sdev, sif_mem_get_sgl(mem),
- ctx->base, ctx->size, page_shift, false, false);
+ ctx->base, ctx->size, page_shift, false, false, false);
if (!ctx->pt)
return -ENOMEM;
set_ctx(sdev, ctx, sif_pt_root_table_level(ctx->pt), sif_pt_dma_root(ctx->pt));
hw_ctx->translation_type = MMU_GVA2GPA_MODE;
if (multipage) {
- ctx->pt = sif_pt_create_for_mem(mem, ctx->base, 12, true, true);
+ ctx->pt = sif_pt_create_for_mem(mem, ctx->base, 12, true, true, false);
if (!ctx->pt)
return -ENOMEM;
set_ctx(sdev, ctx, sif_pt_root_table_level(ctx->pt), sif_pt_dma_root(ctx->pt));
set_ctx(sdev, ctx, leaf_level, aligned_dma_addr);
} else if (!ctx->pt) {
- ctx->pt = sif_pt_create_for_mem(mem, ctx->base, page_shift, true, false);
+ ctx->pt = sif_pt_create_for_mem(mem, ctx->base, page_shift, true, false, false);
if (!ctx->pt)
return -ENOMEM;
set_ctx(sdev, ctx, sif_pt_root_table_level(ctx->pt), sif_pt_dma_root(ctx->pt));
/* some utilities: */
+
+/* Abstract locking functions:
+ * TBD: In principle this allows locking to be skipped if the page table
+ * does not need to be enforcing thread safeness.
+ * Right now use locking to trap if two threads are trying to modify a
+ * table that is assumed to be accessed serialized:
+ * This is a measure to avoid memory overwrites or corruction
+ * if used like we assume is the case in Orabug: 24655978.
+ *
+ */
+static inline void pt_lock(struct sif_pt *pt)
+{
+ if (pt->thread_safe)
+ mutex_lock(&pt->lock);
+ else
+ BUG_ON(!mutex_trylock(&pt->lock));
+}
+
+static inline void pt_unlock(struct sif_pt *pt)
+{
+ mutex_unlock(&pt->lock);
+}
+
+
/* Find the optimal page size (represented by the leaf level)
* to use based on device capabilities, configuration and a max_shift
* value (typically based on continuousness of memory.
*/
struct sif_pt *sif_pt_create(struct sif_dev *sdev, struct scatterlist *sg,
u64 vstart, size_t size, u32 page_shift,
- bool modifiable, bool fixed_top)
+ bool modifiable, bool fixed_top, bool thread_safe)
{
int ret = 0;
int i;
pt->sdev = sdev;
pt->fixed_top = fixed_top;
pt->modifiable = modifiable;
+ pt->thread_safe = thread_safe;
ret = find_optimal_leaf_level(sdev, page_shift,
vstart, dma_start, size,
struct sif_pt *sif_pt_create_for_mem(struct sif_mem *mem,
- u64 vstart, u32 page_shift, bool modifiable, bool fixed_top)
+ u64 vstart, u32 page_shift,
+ bool modifiable, bool fixed_top, bool thread_safe)
{
int ret = 0;
int i;
pt->sdev = sdev;
pt->fixed_top = fixed_top;
pt->modifiable = modifiable;
+ pt->thread_safe = thread_safe;
ret = find_optimal_leaf_level(sdev, page_shift,
vstart, sif_mem_dma(mem, 0), size,
&pt->leaf_level, &pt->pte_ext_shift);
if (map_mt == SIFMT_2M)
page_shift += sdev->mi.level_shift;
- pt = sif_pt_create(sdev, NULL, vstart, 0, page_shift, true, map_mt == SIFMT_CS);
+ pt = sif_pt_create(sdev, NULL, vstart, 0, page_shift, true, map_mt == SIFMT_CS,
+ map_mt == SIFMT_CS);
if (!pt)
return NULL;
u8 level;
int i, ip;
- mutex_lock(&pt->lock);
+ pt_lock(pt);
level = pt->leaf_level;
va_up = vaddr & ~level_to_pagemask(pt, level);
pt_shift = level_to_pageshift(pt, level-1);
sif_log(sdev, SIF_MMU_V, "Page at vaddr %llx not found", va_up);
ret = -EINVAL;
}
- mutex_unlock(&pt->lock);
+ pt_unlock(pt);
return ret;
}
sif_log(pt->sdev, SIF_MMU, "** vstart %llx size %lx page size %llx leaf_level %d **",
vstart, size, page_mask + 1, pt->leaf_level);
- mutex_lock(&pt->lock);
+
+ pt_lock(pt);
/* Calculate a good size of each sg table in the kmem object: */
if (!pt->top) {
pt->vstart = new_start;
pt->vsize = new_size;
- mutex_unlock(&pt->lock);
+ pt_unlock(pt);
return ret;
populate_failed:
kref_put(&pt->refcnt, sif_pt_release);
kmem_ext_failed:
sif_kmem_free(pt->sdev, &pt->m);
- mutex_unlock(&pt->lock);
+ pt_unlock(pt);
return ret;
}
sif_log(pt->sdev, SIF_MMU, "** vstart %llx size %lx page size %llx leaf level %d **",
vstart, size, page_mask + 1, pt->leaf_level);
- mutex_lock(&pt->lock);
+ pt_lock(pt);
/* Calculate a good size of each sg table in the kmem object: */
if (!pt->top) {
/* This is a blank pt - allocate and set up the initial structures */
pt->vstart = new_start;
pt->vsize = new_size;
- mutex_unlock(&pt->lock);
+ pt_unlock(pt);
return ret;
kmem_ext_failed:
sif_kmem_free(pt->sdev, &pt->m);
- mutex_unlock(&pt->lock);
+ pt_unlock(pt);
return ret;
}
sif_log(pt->sdev, SIF_PT_V, "** vstart %llx -> %llx, size %lx **", vstart, va, size);
page_size = level_to_pagesize(pt, level - 1);
- mutex_lock(&pt->lock);
+
+ pt_lock(pt);
p = find_page(pt, level, va_up);
if (!p) {
sif_log(pt->sdev, SIF_INFO, "vaddr %llx not found at level %d",
*/
dma_sync_sg_for_device(pt->sdev->ib_dev.dma_device, pt->m.sg, pt->m.sg_max, DMA_TO_DEVICE);
- mutex_unlock(&pt->lock);
+ pt_unlock(pt);
return kref_put(&pt->refcnt, sif_pt_release);
failed:
- mutex_unlock(&pt->lock);
+ pt_unlock(pt);
return ret;
}
}
sif_log(pt->sdev, SIF_MMU_V, "** vstart %llx size %llx **", vstart, mem->size);
- mutex_lock(&pt->lock);
+ pt_lock(pt);
/* Fast path: Repopulate ptes directly - all ref.cnts are kept as is: */
ret = populate_pt_from_mem(pt, mem, vstart, true);
*/
if (!ret)
dma_sync_sg_for_device(pt->sdev->ib_dev.dma_device, pt->m.sg, pt->m.sg_max, DMA_TO_DEVICE);
- mutex_unlock(&pt->lock);
+ pt_unlock(pt);
return ret;
}
struct sif_dev *sdev; /* Device this mapping is valid for */
bool fixed_top; /* If set, pt guarantees that the top node remains constant */
bool modifiable; /* Set if this page table should support modification */
+ bool thread_safe; /* Set if multiple threads may modify the page table */
u8 top_level; /* Page table level of top node, 0 means no table */
u8 leaf_level; /* Page table level of leaf node */
u8 pte_ext_shift; /* Only populate every (1 << pte_ext_shift) pte */
* Set @modifiable to allow the table to be extended and shrinked
* Set @fixed_top to have pt guarantee that the top node remains constant
* in which case it will always be a level 4 tree.
+ * set @thread_safe if the page table can be modified in parallel by multiple threads,
+ * which requires the mutex lock to be held. Such page tables cannot be
+ * called from interrupt context due to locking needs.
*/
struct sif_pt *sif_pt_create(struct sif_dev *sdev, struct scatterlist *sg,
u64 vstart, size_t mapsize,
- u32 page_shift, bool modifiable, bool fixed_top);
+ u32 page_shift, bool modifiable, bool fixed_top,
+ bool thread_safe);
/* Create a sif page table from a mem object:
* Set @fixed_top to prepare for a table where the top node is fixed:
* (will always be a level 4 tree)
*/
struct sif_pt *sif_pt_create_for_mem(struct sif_mem *mem, u64 vstart,
- u32 page_shift, bool modifiable, bool fixed_top);
+ u32 page_shift, bool modifiable,
+ bool fixed_top, bool thread_safe);
/* Remap the (remappable) page table to be used starting at vstart for the range of mem
* eg. replace the current mapping with a new one, preserving the top node