sparc: Revert generic IOMMU allocator.

I applied the wrong version of this patch series, V4 instead
of V10, due to a patchwork bundling snafu.

Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/arch/sparc/kernel/ldc.c b/arch/sparc/kernel/ldc.c
index d485697..274a9f5 100644
--- a/arch/sparc/kernel/ldc.c
+++ b/arch/sparc/kernel/ldc.c
@@ -15,8 +15,6 @@
 #include <linux/list.h>
 #include <linux/init.h>
 #include <linux/bitmap.h>
-#include <linux/hash.h>
-#include <linux/iommu-common.h>
 
 #include <asm/hypervisor.h>
 #include <asm/iommu.h>
@@ -29,11 +27,6 @@
 #define DRV_MODULE_VERSION	"1.1"
 #define DRV_MODULE_RELDATE	"July 22, 2008"
 
-#define COOKIE_PGSZ_CODE	0xf000000000000000ULL
-#define COOKIE_PGSZ_CODE_SHIFT	60ULL
-
-static DEFINE_PER_CPU(unsigned int, ldc_pool_hash);
-
 static char version[] =
 	DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
 #define LDC_PACKET_SIZE		64
@@ -105,10 +98,10 @@
 int ldom_domaining_enabled;
 
 struct ldc_iommu {
-	/* Protects ldc_unmap.  */
+	/* Protects arena alloc/free.  */
 	spinlock_t			lock;
+	struct iommu_arena		arena;
 	struct ldc_mtable_entry		*page_table;
-	struct iommu_table		iommu_table;
 };
 
 struct ldc_channel {
@@ -1005,85 +998,31 @@
 	free_pages((unsigned long)q, order);
 }
 
-static unsigned long ldc_cookie_to_index(u64 cookie, void *arg)
-{
-	u64 szcode = cookie >> COOKIE_PGSZ_CODE_SHIFT;
-	/* struct ldc_iommu *ldc_iommu = (struct ldc_iommu *)arg; */
-
-	cookie &= ~COOKIE_PGSZ_CODE;
-
-	return (cookie >> (13ULL + (szcode * 3ULL)));
-}
-
-struct ldc_demap_arg {
-	struct ldc_iommu *ldc_iommu;
-	u64 cookie;
-	unsigned long id;
-};
-
-static void ldc_demap(void *arg, unsigned long entry, unsigned long npages)
-{
-	struct ldc_demap_arg *ldc_demap_arg = arg;
-	struct ldc_iommu *iommu = ldc_demap_arg->ldc_iommu;
-	unsigned long id = ldc_demap_arg->id;
-	u64 cookie = ldc_demap_arg->cookie;
-	struct ldc_mtable_entry *base;
-	unsigned long i, shift;
-
-	shift = (cookie >> COOKIE_PGSZ_CODE_SHIFT) * 3;
-	base = iommu->page_table + entry;
-	for (i = 0; i < npages; i++) {
-		if (base->cookie)
-			sun4v_ldc_revoke(id, cookie + (i << shift),
-					 base->cookie);
-		base->mte = 0;
-	}
-}
-
 /* XXX Make this configurable... XXX */
 #define LDC_IOTABLE_SIZE	(8 * 1024)
 
-struct iommu_tbl_ops ldc_iommu_ops = {
-	.cookie_to_index = ldc_cookie_to_index,
-	.demap = ldc_demap,
-};
-
-static void setup_ldc_pool_hash(void)
-{
-	unsigned int i;
-	static bool do_once;
-
-	if (do_once)
-		return;
-	do_once = true;
-	for_each_possible_cpu(i)
-		per_cpu(ldc_pool_hash, i) = hash_32(i, IOMMU_POOL_HASHBITS);
-}
-
-
-static int ldc_iommu_init(const char *name, struct ldc_channel *lp)
+static int ldc_iommu_init(struct ldc_channel *lp)
 {
 	unsigned long sz, num_tsb_entries, tsbsize, order;
-	struct ldc_iommu *ldc_iommu = &lp->iommu;
-	struct iommu_table *iommu = &ldc_iommu->iommu_table;
+	struct ldc_iommu *iommu = &lp->iommu;
 	struct ldc_mtable_entry *table;
 	unsigned long hv_err;
 	int err;
 
 	num_tsb_entries = LDC_IOTABLE_SIZE;
 	tsbsize = num_tsb_entries * sizeof(struct ldc_mtable_entry);
-	setup_ldc_pool_hash();
-	spin_lock_init(&ldc_iommu->lock);
+
+	spin_lock_init(&iommu->lock);
 
 	sz = num_tsb_entries / 8;
 	sz = (sz + 7UL) & ~7UL;
-	iommu->map = kzalloc(sz, GFP_KERNEL);
-	if (!iommu->map) {
+	iommu->arena.map = kzalloc(sz, GFP_KERNEL);
+	if (!iommu->arena.map) {
 		printk(KERN_ERR PFX "Alloc of arena map failed, sz=%lu\n", sz);
 		return -ENOMEM;
 	}
-	iommu_tbl_pool_init(iommu, num_tsb_entries, PAGE_SHIFT,
-			    &ldc_iommu_ops, false, 1);
+
+	iommu->arena.limit = num_tsb_entries;
 
 	order = get_order(tsbsize);
 
@@ -1098,7 +1037,7 @@
 
 	memset(table, 0, PAGE_SIZE << order);
 
-	ldc_iommu->page_table = table;
+	iommu->page_table = table;
 
 	hv_err = sun4v_ldc_set_map_table(lp->id, __pa(table),
 					 num_tsb_entries);
@@ -1110,32 +1049,31 @@
 
 out_free_table:
 	free_pages((unsigned long) table, order);
-	ldc_iommu->page_table = NULL;
+	iommu->page_table = NULL;
 
 out_free_map:
-	kfree(iommu->map);
-	iommu->map = NULL;
+	kfree(iommu->arena.map);
+	iommu->arena.map = NULL;
 
 	return err;
 }
 
 static void ldc_iommu_release(struct ldc_channel *lp)
 {
-	struct ldc_iommu *ldc_iommu = &lp->iommu;
-	struct iommu_table *iommu = &ldc_iommu->iommu_table;
+	struct ldc_iommu *iommu = &lp->iommu;
 	unsigned long num_tsb_entries, tsbsize, order;
 
 	(void) sun4v_ldc_set_map_table(lp->id, 0, 0);
 
-	num_tsb_entries = iommu->poolsize * iommu->nr_pools;
+	num_tsb_entries = iommu->arena.limit;
 	tsbsize = num_tsb_entries * sizeof(struct ldc_mtable_entry);
 	order = get_order(tsbsize);
 
-	free_pages((unsigned long) ldc_iommu->page_table, order);
-	ldc_iommu->page_table = NULL;
+	free_pages((unsigned long) iommu->page_table, order);
+	iommu->page_table = NULL;
 
-	kfree(iommu->map);
-	iommu->map = NULL;
+	kfree(iommu->arena.map);
+	iommu->arena.map = NULL;
 }
 
 struct ldc_channel *ldc_alloc(unsigned long id,
@@ -1202,7 +1140,7 @@
 
 	lp->id = id;
 
-	err = ldc_iommu_init(name, lp);
+	err = ldc_iommu_init(lp);
 	if (err)
 		goto out_free_ldc;
 
@@ -1947,6 +1885,40 @@
 }
 EXPORT_SYMBOL(ldc_read);
 
+static long arena_alloc(struct ldc_iommu *iommu, unsigned long npages)
+{
+	struct iommu_arena *arena = &iommu->arena;
+	unsigned long n, start, end, limit;
+	int pass;
+
+	limit = arena->limit;
+	start = arena->hint;
+	pass = 0;
+
+again:
+	n = bitmap_find_next_zero_area(arena->map, limit, start, npages, 0);
+	end = n + npages;
+	if (unlikely(end >= limit)) {
+		if (likely(pass < 1)) {
+			limit = start;
+			start = 0;
+			pass++;
+			goto again;
+		} else {
+			/* Scanned the whole thing, give up. */
+			return -1;
+		}
+	}
+	bitmap_set(arena->map, n, npages);
+
+	arena->hint = end;
+
+	return n;
+}
+
+#define COOKIE_PGSZ_CODE	0xf000000000000000ULL
+#define COOKIE_PGSZ_CODE_SHIFT	60ULL
+
 static u64 pagesize_code(void)
 {
 	switch (PAGE_SIZE) {
@@ -1973,14 +1945,23 @@
 		page_offset);
 }
 
+static u64 cookie_to_index(u64 cookie, unsigned long *shift)
+{
+	u64 szcode = cookie >> COOKIE_PGSZ_CODE_SHIFT;
+
+	cookie &= ~COOKIE_PGSZ_CODE;
+
+	*shift = szcode * 3;
+
+	return (cookie >> (13ULL + (szcode * 3ULL)));
+}
 
 static struct ldc_mtable_entry *alloc_npages(struct ldc_iommu *iommu,
 					     unsigned long npages)
 {
 	long entry;
 
-	entry = iommu_tbl_range_alloc(NULL, &iommu->iommu_table, npages,
-				     NULL, __this_cpu_read(ldc_pool_hash));
+	entry = arena_alloc(iommu, npages);
 	if (unlikely(entry < 0))
 		return NULL;
 
@@ -2109,7 +2090,7 @@
 	       struct ldc_trans_cookie *cookies, int ncookies,
 	       unsigned int map_perm)
 {
-	unsigned long i, npages;
+	unsigned long i, npages, flags;
 	struct ldc_mtable_entry *base;
 	struct cookie_state state;
 	struct ldc_iommu *iommu;
@@ -2128,7 +2109,9 @@
 
 	iommu = &lp->iommu;
 
+	spin_lock_irqsave(&iommu->lock, flags);
 	base = alloc_npages(iommu, npages);
+	spin_unlock_irqrestore(&iommu->lock, flags);
 
 	if (!base)
 		return -ENOMEM;
@@ -2153,7 +2136,7 @@
 		   struct ldc_trans_cookie *cookies, int ncookies,
 		   unsigned int map_perm)
 {
-	unsigned long npages, pa;
+	unsigned long npages, pa, flags;
 	struct ldc_mtable_entry *base;
 	struct cookie_state state;
 	struct ldc_iommu *iommu;
@@ -2169,7 +2152,9 @@
 
 	iommu = &lp->iommu;
 
+	spin_lock_irqsave(&iommu->lock, flags);
 	base = alloc_npages(iommu, npages);
+	spin_unlock_irqrestore(&iommu->lock, flags);
 
 	if (!base)
 		return -ENOMEM;
@@ -2187,29 +2172,35 @@
 }
 EXPORT_SYMBOL(ldc_map_single);
 
-
 static void free_npages(unsigned long id, struct ldc_iommu *iommu,
 			u64 cookie, u64 size)
 {
-	unsigned long npages;
-	struct ldc_demap_arg demap_arg;
-
-	demap_arg.ldc_iommu = iommu;
-	demap_arg.cookie = cookie;
-	demap_arg.id = id;
+	struct iommu_arena *arena = &iommu->arena;
+	unsigned long i, shift, index, npages;
+	struct ldc_mtable_entry *base;
 
 	npages = PAGE_ALIGN(((cookie & ~PAGE_MASK) + size)) >> PAGE_SHIFT;
-	iommu_tbl_range_free(&iommu->iommu_table, cookie, npages, true,
-			     &demap_arg);
+	index = cookie_to_index(cookie, &shift);
+	base = iommu->page_table + index;
 
+	BUG_ON(index > arena->limit ||
+	       (index + npages) > arena->limit);
+
+	for (i = 0; i < npages; i++) {
+		if (base->cookie)
+			sun4v_ldc_revoke(id, cookie + (i << shift),
+					 base->cookie);
+		base->mte = 0;
+		__clear_bit(index + i, arena->map);
+	}
 }
 
 void ldc_unmap(struct ldc_channel *lp, struct ldc_trans_cookie *cookies,
 	       int ncookies)
 {
 	struct ldc_iommu *iommu = &lp->iommu;
-	int i;
 	unsigned long flags;
+	int i;
 
 	spin_lock_irqsave(&iommu->lock, flags);
 	for (i = 0; i < ncookies; i++) {