diff options
-rw-r--r-- | arch/sparc/kernel/ldc.c | 153 |
1 files changed, 65 insertions, 88 deletions
diff --git a/arch/sparc/kernel/ldc.c b/arch/sparc/kernel/ldc.c index 274a9f59d95c..d2ae0f70059e 100644 --- a/arch/sparc/kernel/ldc.c +++ b/arch/sparc/kernel/ldc.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/list.h> | 15 | #include <linux/list.h> |
16 | #include <linux/init.h> | 16 | #include <linux/init.h> |
17 | #include <linux/bitmap.h> | 17 | #include <linux/bitmap.h> |
18 | #include <linux/iommu-common.h> | ||
18 | 19 | ||
19 | #include <asm/hypervisor.h> | 20 | #include <asm/hypervisor.h> |
20 | #include <asm/iommu.h> | 21 | #include <asm/iommu.h> |
@@ -27,6 +28,10 @@ | |||
27 | #define DRV_MODULE_VERSION "1.1" | 28 | #define DRV_MODULE_VERSION "1.1" |
28 | #define DRV_MODULE_RELDATE "July 22, 2008" | 29 | #define DRV_MODULE_RELDATE "July 22, 2008" |
29 | 30 | ||
31 | #define COOKIE_PGSZ_CODE 0xf000000000000000ULL | ||
32 | #define COOKIE_PGSZ_CODE_SHIFT 60ULL | ||
33 | |||
34 | |||
30 | static char version[] = | 35 | static char version[] = |
31 | DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; | 36 | DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; |
32 | #define LDC_PACKET_SIZE 64 | 37 | #define LDC_PACKET_SIZE 64 |
@@ -98,10 +103,10 @@ static const struct ldc_mode_ops stream_ops; | |||
98 | int ldom_domaining_enabled; | 103 | int ldom_domaining_enabled; |
99 | 104 | ||
100 | struct ldc_iommu { | 105 | struct ldc_iommu { |
101 | /* Protects arena alloc/free. */ | 106 | /* Protects ldc_unmap. */ |
102 | spinlock_t lock; | 107 | spinlock_t lock; |
103 | struct iommu_arena arena; | ||
104 | struct ldc_mtable_entry *page_table; | 108 | struct ldc_mtable_entry *page_table; |
109 | struct iommu_map_table iommu_map_table; | ||
105 | }; | 110 | }; |
106 | 111 | ||
107 | struct ldc_channel { | 112 | struct ldc_channel { |
@@ -998,31 +1003,59 @@ static void free_queue(unsigned long num_entries, struct ldc_packet *q) | |||
998 | free_pages((unsigned long)q, order); | 1003 | free_pages((unsigned long)q, order); |
999 | } | 1004 | } |
1000 | 1005 | ||
1006 | static unsigned long ldc_cookie_to_index(u64 cookie, void *arg) | ||
1007 | { | ||
1008 | u64 szcode = cookie >> COOKIE_PGSZ_CODE_SHIFT; | ||
1009 | /* struct ldc_iommu *ldc_iommu = (struct ldc_iommu *)arg; */ | ||
1010 | |||
1011 | cookie &= ~COOKIE_PGSZ_CODE; | ||
1012 | |||
1013 | return (cookie >> (13ULL + (szcode * 3ULL))); | ||
1014 | } | ||
1015 | |||
1016 | static void ldc_demap(struct ldc_iommu *iommu, unsigned long id, u64 cookie, | ||
1017 | unsigned long entry, unsigned long npages) | ||
1018 | { | ||
1019 | struct ldc_mtable_entry *base; | ||
1020 | unsigned long i, shift; | ||
1021 | |||
1022 | shift = (cookie >> COOKIE_PGSZ_CODE_SHIFT) * 3; | ||
1023 | base = iommu->page_table + entry; | ||
1024 | for (i = 0; i < npages; i++) { | ||
1025 | if (base->cookie) | ||
1026 | sun4v_ldc_revoke(id, cookie + (i << shift), | ||
1027 | base->cookie); | ||
1028 | base->mte = 0; | ||
1029 | } | ||
1030 | } | ||
1031 | |||
1001 | /* XXX Make this configurable... XXX */ | 1032 | /* XXX Make this configurable... XXX */ |
1002 | #define LDC_IOTABLE_SIZE (8 * 1024) | 1033 | #define LDC_IOTABLE_SIZE (8 * 1024) |
1003 | 1034 | ||
1004 | static int ldc_iommu_init(struct ldc_channel *lp) | 1035 | static int ldc_iommu_init(const char *name, struct ldc_channel *lp) |
1005 | { | 1036 | { |
1006 | unsigned long sz, num_tsb_entries, tsbsize, order; | 1037 | unsigned long sz, num_tsb_entries, tsbsize, order; |
1007 | struct ldc_iommu *iommu = &lp->iommu; | 1038 | struct ldc_iommu *ldc_iommu = &lp->iommu; |
1039 | struct iommu_map_table *iommu = &ldc_iommu->iommu_map_table; | ||
1008 | struct ldc_mtable_entry *table; | 1040 | struct ldc_mtable_entry *table; |
1009 | unsigned long hv_err; | 1041 | unsigned long hv_err; |
1010 | int err; | 1042 | int err; |
1011 | 1043 | ||
1012 | num_tsb_entries = LDC_IOTABLE_SIZE; | 1044 | num_tsb_entries = LDC_IOTABLE_SIZE; |
1013 | tsbsize = num_tsb_entries * sizeof(struct ldc_mtable_entry); | 1045 | tsbsize = num_tsb_entries * sizeof(struct ldc_mtable_entry); |
1014 | 1046 | spin_lock_init(&ldc_iommu->lock); | |
1015 | spin_lock_init(&iommu->lock); | ||
1016 | 1047 | ||
1017 | sz = num_tsb_entries / 8; | 1048 | sz = num_tsb_entries / 8; |
1018 | sz = (sz + 7UL) & ~7UL; | 1049 | sz = (sz + 7UL) & ~7UL; |
1019 | iommu->arena.map = kzalloc(sz, GFP_KERNEL); | 1050 | iommu->map = kzalloc(sz, GFP_KERNEL); |
1020 | if (!iommu->arena.map) { | 1051 | if (!iommu->map) { |
1021 | printk(KERN_ERR PFX "Alloc of arena map failed, sz=%lu\n", sz); | 1052 | printk(KERN_ERR PFX "Alloc of arena map failed, sz=%lu\n", sz); |
1022 | return -ENOMEM; | 1053 | return -ENOMEM; |
1023 | } | 1054 | } |
1024 | 1055 | iommu_tbl_pool_init(iommu, num_tsb_entries, PAGE_SHIFT, | |
1025 | iommu->arena.limit = num_tsb_entries; | 1056 | NULL, false /* no large pool */, |
1057 | 1 /* npools */, | ||
1058 | true /* skip span boundary check */); | ||
1026 | 1059 | ||
1027 | order = get_order(tsbsize); | 1060 | order = get_order(tsbsize); |
1028 | 1061 | ||
@@ -1037,7 +1070,7 @@ static int ldc_iommu_init(struct ldc_channel *lp) | |||
1037 | 1070 | ||
1038 | memset(table, 0, PAGE_SIZE << order); | 1071 | memset(table, 0, PAGE_SIZE << order); |
1039 | 1072 | ||
1040 | iommu->page_table = table; | 1073 | ldc_iommu->page_table = table; |
1041 | 1074 | ||
1042 | hv_err = sun4v_ldc_set_map_table(lp->id, __pa(table), | 1075 | hv_err = sun4v_ldc_set_map_table(lp->id, __pa(table), |
1043 | num_tsb_entries); | 1076 | num_tsb_entries); |
@@ -1049,31 +1082,32 @@ static int ldc_iommu_init(struct ldc_channel *lp) | |||
1049 | 1082 | ||
1050 | out_free_table: | 1083 | out_free_table: |
1051 | free_pages((unsigned long) table, order); | 1084 | free_pages((unsigned long) table, order); |
1052 | iommu->page_table = NULL; | 1085 | ldc_iommu->page_table = NULL; |
1053 | 1086 | ||
1054 | out_free_map: | 1087 | out_free_map: |
1055 | kfree(iommu->arena.map); | 1088 | kfree(iommu->map); |
1056 | iommu->arena.map = NULL; | 1089 | iommu->map = NULL; |
1057 | 1090 | ||
1058 | return err; | 1091 | return err; |
1059 | } | 1092 | } |
1060 | 1093 | ||
1061 | static void ldc_iommu_release(struct ldc_channel *lp) | 1094 | static void ldc_iommu_release(struct ldc_channel *lp) |
1062 | { | 1095 | { |
1063 | struct ldc_iommu *iommu = &lp->iommu; | 1096 | struct ldc_iommu *ldc_iommu = &lp->iommu; |
1097 | struct iommu_map_table *iommu = &ldc_iommu->iommu_map_table; | ||
1064 | unsigned long num_tsb_entries, tsbsize, order; | 1098 | unsigned long num_tsb_entries, tsbsize, order; |
1065 | 1099 | ||
1066 | (void) sun4v_ldc_set_map_table(lp->id, 0, 0); | 1100 | (void) sun4v_ldc_set_map_table(lp->id, 0, 0); |
1067 | 1101 | ||
1068 | num_tsb_entries = iommu->arena.limit; | 1102 | num_tsb_entries = iommu->poolsize * iommu->nr_pools; |
1069 | tsbsize = num_tsb_entries * sizeof(struct ldc_mtable_entry); | 1103 | tsbsize = num_tsb_entries * sizeof(struct ldc_mtable_entry); |
1070 | order = get_order(tsbsize); | 1104 | order = get_order(tsbsize); |
1071 | 1105 | ||
1072 | free_pages((unsigned long) iommu->page_table, order); | 1106 | free_pages((unsigned long) ldc_iommu->page_table, order); |
1073 | iommu->page_table = NULL; | 1107 | ldc_iommu->page_table = NULL; |
1074 | 1108 | ||
1075 | kfree(iommu->arena.map); | 1109 | kfree(iommu->map); |
1076 | iommu->arena.map = NULL; | 1110 | iommu->map = NULL; |
1077 | } | 1111 | } |
1078 | 1112 | ||
1079 | struct ldc_channel *ldc_alloc(unsigned long id, | 1113 | struct ldc_channel *ldc_alloc(unsigned long id, |
@@ -1140,7 +1174,7 @@ struct ldc_channel *ldc_alloc(unsigned long id, | |||
1140 | 1174 | ||
1141 | lp->id = id; | 1175 | lp->id = id; |
1142 | 1176 | ||
1143 | err = ldc_iommu_init(lp); | 1177 | err = ldc_iommu_init(name, lp); |
1144 | if (err) | 1178 | if (err) |
1145 | goto out_free_ldc; | 1179 | goto out_free_ldc; |
1146 | 1180 | ||
@@ -1885,40 +1919,6 @@ int ldc_read(struct ldc_channel *lp, void *buf, unsigned int size) | |||
1885 | } | 1919 | } |
1886 | EXPORT_SYMBOL(ldc_read); | 1920 | EXPORT_SYMBOL(ldc_read); |
1887 | 1921 | ||
1888 | static long arena_alloc(struct ldc_iommu *iommu, unsigned long npages) | ||
1889 | { | ||
1890 | struct iommu_arena *arena = &iommu->arena; | ||
1891 | unsigned long n, start, end, limit; | ||
1892 | int pass; | ||
1893 | |||
1894 | limit = arena->limit; | ||
1895 | start = arena->hint; | ||
1896 | pass = 0; | ||
1897 | |||
1898 | again: | ||
1899 | n = bitmap_find_next_zero_area(arena->map, limit, start, npages, 0); | ||
1900 | end = n + npages; | ||
1901 | if (unlikely(end >= limit)) { | ||
1902 | if (likely(pass < 1)) { | ||
1903 | limit = start; | ||
1904 | start = 0; | ||
1905 | pass++; | ||
1906 | goto again; | ||
1907 | } else { | ||
1908 | /* Scanned the whole thing, give up. */ | ||
1909 | return -1; | ||
1910 | } | ||
1911 | } | ||
1912 | bitmap_set(arena->map, n, npages); | ||
1913 | |||
1914 | arena->hint = end; | ||
1915 | |||
1916 | return n; | ||
1917 | } | ||
1918 | |||
1919 | #define COOKIE_PGSZ_CODE 0xf000000000000000ULL | ||
1920 | #define COOKIE_PGSZ_CODE_SHIFT 60ULL | ||
1921 | |||
1922 | static u64 pagesize_code(void) | 1922 | static u64 pagesize_code(void) |
1923 | { | 1923 | { |
1924 | switch (PAGE_SIZE) { | 1924 | switch (PAGE_SIZE) { |
@@ -1945,23 +1945,14 @@ static u64 make_cookie(u64 index, u64 pgsz_code, u64 page_offset) | |||
1945 | page_offset); | 1945 | page_offset); |
1946 | } | 1946 | } |
1947 | 1947 | ||
1948 | static u64 cookie_to_index(u64 cookie, unsigned long *shift) | ||
1949 | { | ||
1950 | u64 szcode = cookie >> COOKIE_PGSZ_CODE_SHIFT; | ||
1951 | |||
1952 | cookie &= ~COOKIE_PGSZ_CODE; | ||
1953 | |||
1954 | *shift = szcode * 3; | ||
1955 | |||
1956 | return (cookie >> (13ULL + (szcode * 3ULL))); | ||
1957 | } | ||
1958 | 1948 | ||
1959 | static struct ldc_mtable_entry *alloc_npages(struct ldc_iommu *iommu, | 1949 | static struct ldc_mtable_entry *alloc_npages(struct ldc_iommu *iommu, |
1960 | unsigned long npages) | 1950 | unsigned long npages) |
1961 | { | 1951 | { |
1962 | long entry; | 1952 | long entry; |
1963 | 1953 | ||
1964 | entry = arena_alloc(iommu, npages); | 1954 | entry = iommu_tbl_range_alloc(NULL, &iommu->iommu_map_table, |
1955 | npages, NULL, (unsigned long)-1, 0); | ||
1965 | if (unlikely(entry < 0)) | 1956 | if (unlikely(entry < 0)) |
1966 | return NULL; | 1957 | return NULL; |
1967 | 1958 | ||
@@ -2090,7 +2081,7 @@ int ldc_map_sg(struct ldc_channel *lp, | |||
2090 | struct ldc_trans_cookie *cookies, int ncookies, | 2081 | struct ldc_trans_cookie *cookies, int ncookies, |
2091 | unsigned int map_perm) | 2082 | unsigned int map_perm) |
2092 | { | 2083 | { |
2093 | unsigned long i, npages, flags; | 2084 | unsigned long i, npages; |
2094 | struct ldc_mtable_entry *base; | 2085 | struct ldc_mtable_entry *base; |
2095 | struct cookie_state state; | 2086 | struct cookie_state state; |
2096 | struct ldc_iommu *iommu; | 2087 | struct ldc_iommu *iommu; |
@@ -2109,9 +2100,7 @@ int ldc_map_sg(struct ldc_channel *lp, | |||
2109 | 2100 | ||
2110 | iommu = &lp->iommu; | 2101 | iommu = &lp->iommu; |
2111 | 2102 | ||
2112 | spin_lock_irqsave(&iommu->lock, flags); | ||
2113 | base = alloc_npages(iommu, npages); | 2103 | base = alloc_npages(iommu, npages); |
2114 | spin_unlock_irqrestore(&iommu->lock, flags); | ||
2115 | 2104 | ||
2116 | if (!base) | 2105 | if (!base) |
2117 | return -ENOMEM; | 2106 | return -ENOMEM; |
@@ -2136,7 +2125,7 @@ int ldc_map_single(struct ldc_channel *lp, | |||
2136 | struct ldc_trans_cookie *cookies, int ncookies, | 2125 | struct ldc_trans_cookie *cookies, int ncookies, |
2137 | unsigned int map_perm) | 2126 | unsigned int map_perm) |
2138 | { | 2127 | { |
2139 | unsigned long npages, pa, flags; | 2128 | unsigned long npages, pa; |
2140 | struct ldc_mtable_entry *base; | 2129 | struct ldc_mtable_entry *base; |
2141 | struct cookie_state state; | 2130 | struct cookie_state state; |
2142 | struct ldc_iommu *iommu; | 2131 | struct ldc_iommu *iommu; |
@@ -2152,9 +2141,7 @@ int ldc_map_single(struct ldc_channel *lp, | |||
2152 | 2141 | ||
2153 | iommu = &lp->iommu; | 2142 | iommu = &lp->iommu; |
2154 | 2143 | ||
2155 | spin_lock_irqsave(&iommu->lock, flags); | ||
2156 | base = alloc_npages(iommu, npages); | 2144 | base = alloc_npages(iommu, npages); |
2157 | spin_unlock_irqrestore(&iommu->lock, flags); | ||
2158 | 2145 | ||
2159 | if (!base) | 2146 | if (!base) |
2160 | return -ENOMEM; | 2147 | return -ENOMEM; |
@@ -2172,35 +2159,25 @@ int ldc_map_single(struct ldc_channel *lp, | |||
2172 | } | 2159 | } |
2173 | EXPORT_SYMBOL(ldc_map_single); | 2160 | EXPORT_SYMBOL(ldc_map_single); |
2174 | 2161 | ||
2162 | |||
2175 | static void free_npages(unsigned long id, struct ldc_iommu *iommu, | 2163 | static void free_npages(unsigned long id, struct ldc_iommu *iommu, |
2176 | u64 cookie, u64 size) | 2164 | u64 cookie, u64 size) |
2177 | { | 2165 | { |
2178 | struct iommu_arena *arena = &iommu->arena; | 2166 | unsigned long npages, entry; |
2179 | unsigned long i, shift, index, npages; | ||
2180 | struct ldc_mtable_entry *base; | ||
2181 | 2167 | ||
2182 | npages = PAGE_ALIGN(((cookie & ~PAGE_MASK) + size)) >> PAGE_SHIFT; | 2168 | npages = PAGE_ALIGN(((cookie & ~PAGE_MASK) + size)) >> PAGE_SHIFT; |
2183 | index = cookie_to_index(cookie, &shift); | ||
2184 | base = iommu->page_table + index; | ||
2185 | 2169 | ||
2186 | BUG_ON(index > arena->limit || | 2170 | entry = ldc_cookie_to_index(cookie, iommu); |
2187 | (index + npages) > arena->limit); | 2171 | ldc_demap(iommu, id, cookie, entry, npages); |
2188 | 2172 | iommu_tbl_range_free(&iommu->iommu_map_table, cookie, npages, entry); | |
2189 | for (i = 0; i < npages; i++) { | ||
2190 | if (base->cookie) | ||
2191 | sun4v_ldc_revoke(id, cookie + (i << shift), | ||
2192 | base->cookie); | ||
2193 | base->mte = 0; | ||
2194 | __clear_bit(index + i, arena->map); | ||
2195 | } | ||
2196 | } | 2173 | } |
2197 | 2174 | ||
2198 | void ldc_unmap(struct ldc_channel *lp, struct ldc_trans_cookie *cookies, | 2175 | void ldc_unmap(struct ldc_channel *lp, struct ldc_trans_cookie *cookies, |
2199 | int ncookies) | 2176 | int ncookies) |
2200 | { | 2177 | { |
2201 | struct ldc_iommu *iommu = &lp->iommu; | 2178 | struct ldc_iommu *iommu = &lp->iommu; |
2202 | unsigned long flags; | ||
2203 | int i; | 2179 | int i; |
2180 | unsigned long flags; | ||
2204 | 2181 | ||
2205 | spin_lock_irqsave(&iommu->lock, flags); | 2182 | spin_lock_irqsave(&iommu->lock, flags); |
2206 | for (i = 0; i < ncookies; i++) { | 2183 | for (i = 0; i < ncookies; i++) { |