DO NOT MERGE: Luserspace control knob for the ITS cache

Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index 9a50771..775625c 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -315,6 +315,7 @@
 #define   KVM_DEV_ARM_ITS_RESTORE_TABLES        2
 #define   KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES	3
 #define   KVM_DEV_ARM_ITS_CTRL_RESET		4
+#define   KVM_DEV_ARM_VGIC_PCPU_LPI_CACHE_SIZE	5
 
 /* Device Control API on vcpu fd */
 #define KVM_ARM_VCPU_PMU_V3_CTRL	0
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 298cfc3..eef69b5 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -251,6 +251,7 @@
 
 	/* LPI translation cache */
 	struct list_head	lpi_translation_cache;
+	u32			lpi_pcpu_cache_size;
 
 	/* used by vgic-debug */
 	struct vgic_state_iter *iter;
diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index 2be6b66b..5f2ad74 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -1827,13 +1827,16 @@
 void vgic_lpi_translation_cache_init(struct kvm *kvm)
 {
 	struct vgic_dist *dist = &kvm->arch.vgic;
-	unsigned int sz;
+	unsigned int sz = dist->lpi_pcpu_cache_size;
 	int i;
 
 	if (!list_empty(&dist->lpi_translation_cache))
 		return;
 
-	sz = atomic_read(&kvm->online_vcpus) * LPI_DEFAULT_PCPU_CACHE_SIZE;
+	if (!sz)
+		sz = LPI_DEFAULT_PCPU_CACHE_SIZE;
+
+	sz *= atomic_read(&kvm->online_vcpus);
 
 	for (i = 0; i < sz; i++) {
 		struct vgic_translation_cache_entry *cte;
diff --git a/virt/kvm/arm/vgic/vgic-kvm-device.c b/virt/kvm/arm/vgic/vgic-kvm-device.c
index 4441967..45e8033 100644
--- a/virt/kvm/arm/vgic/vgic-kvm-device.c
+++ b/virt/kvm/arm/vgic/vgic-kvm-device.c
@@ -642,6 +642,21 @@
 			unlock_all_vcpus(dev->kvm);
 			mutex_unlock(&dev->kvm->lock);
 			return ret;
+
+		case KVM_DEV_ARM_VGIC_PCPU_LPI_CACHE_SIZE: {
+			u32 __user *uaddr = (u32 __user *)(long)attr->addr;
+			u32 val;
+
+			if (get_user(val, uaddr))
+				return -EFAULT;
+
+			/* YAAL: Yet Another Arbitrary Limit */
+			if (val > 256)
+				return -E2BIG;
+
+			dev->kvm->arch.vgic.lpi_pcpu_cache_size = val;
+			return 0;
+		}
 		}
 		break;
 	}
@@ -691,6 +706,17 @@
 		tmp32 = reg;
 		return put_user(tmp32, uaddr);
 	}
+	case KVM_DEV_ARM_VGIC_GRP_CTRL: {
+		switch (attr->attr) {
+		case KVM_DEV_ARM_VGIC_PCPU_LPI_CACHE_SIZE: {
+			u32 __user *uaddr = (u32 __user *)(long)attr->addr;
+
+			return put_user(dev->kvm->arch.vgic.lpi_pcpu_cache_size,
+					uaddr);
+		}
+		}
+		break;
+	}
 	}
 	return -ENXIO;
 }
@@ -726,6 +752,8 @@
 			return 0;
 		case KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES:
 			return 0;
+		case KVM_DEV_ARM_VGIC_PCPU_LPI_CACHE_SIZE:
+			return 0;
 		}
 	}
 	return -ENXIO;