|
@@ -330,6 +330,21 @@ bool __tlb_remove_page_size(struct mmu_gather *tlb, struct page *page, int page_
|
|
* See the comment near struct mmu_table_batch.
|
|
* See the comment near struct mmu_table_batch.
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * If we want tlb_remove_table() to imply TLB invalidates.
|
|
|
|
+ */
|
|
|
|
+static inline void tlb_table_invalidate(struct mmu_gather *tlb)
|
|
|
|
+{
|
|
|
|
+#ifdef CONFIG_HAVE_RCU_TABLE_INVALIDATE
|
|
|
|
+ /*
|
|
|
|
+ * Invalidate page-table caches used by hardware walkers. Then we still
|
|
|
|
+ * need to RCU-sched wait while freeing the pages because software
|
|
|
|
+ * walkers can still be in-flight.
|
|
|
|
+ */
|
|
|
|
+ tlb_flush_mmu_tlbonly(tlb);
|
|
|
|
+#endif
|
|
|
|
+}
|
|
|
|
+
|
|
static void tlb_remove_table_smp_sync(void *arg)
|
|
static void tlb_remove_table_smp_sync(void *arg)
|
|
{
|
|
{
|
|
/* Simply deliver the interrupt */
|
|
/* Simply deliver the interrupt */
|
|
@@ -366,6 +381,7 @@ void tlb_table_flush(struct mmu_gather *tlb)
|
|
struct mmu_table_batch **batch = &tlb->batch;
|
|
struct mmu_table_batch **batch = &tlb->batch;
|
|
|
|
|
|
if (*batch) {
|
|
if (*batch) {
|
|
|
|
+ tlb_table_invalidate(tlb);
|
|
call_rcu_sched(&(*batch)->rcu, tlb_remove_table_rcu);
|
|
call_rcu_sched(&(*batch)->rcu, tlb_remove_table_rcu);
|
|
*batch = NULL;
|
|
*batch = NULL;
|
|
}
|
|
}
|
|
@@ -378,11 +394,13 @@ void tlb_remove_table(struct mmu_gather *tlb, void *table)
|
|
if (*batch == NULL) {
|
|
if (*batch == NULL) {
|
|
*batch = (struct mmu_table_batch *)__get_free_page(GFP_NOWAIT | __GFP_NOWARN);
|
|
*batch = (struct mmu_table_batch *)__get_free_page(GFP_NOWAIT | __GFP_NOWARN);
|
|
if (*batch == NULL) {
|
|
if (*batch == NULL) {
|
|
|
|
+ tlb_table_invalidate(tlb);
|
|
tlb_remove_table_one(table);
|
|
tlb_remove_table_one(table);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
(*batch)->nr = 0;
|
|
(*batch)->nr = 0;
|
|
}
|
|
}
|
|
|
|
+
|
|
(*batch)->tables[(*batch)->nr++] = table;
|
|
(*batch)->tables[(*batch)->nr++] = table;
|
|
if ((*batch)->nr == MAX_TABLE_BATCH)
|
|
if ((*batch)->nr == MAX_TABLE_BATCH)
|
|
tlb_table_flush(tlb);
|
|
tlb_table_flush(tlb);
|