diff options
| -rw-r--r-- | drivers/gpu/drm/i915/i915_gem_gtt.c | 146 | ||||
| -rw-r--r-- | drivers/gpu/drm/i915/i915_gem_gtt.h | 2 |
2 files changed, 129 insertions, 19 deletions
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index a322e31655b5..641d3f45aa1e 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c | |||
| @@ -346,7 +346,7 @@ static struct i915_page_table_entry *alloc_pt_single(struct drm_device *dev) | |||
| 346 | if (!pt->used_ptes) | 346 | if (!pt->used_ptes) |
| 347 | goto fail_bitmap; | 347 | goto fail_bitmap; |
| 348 | 348 | ||
| 349 | pt->page = alloc_page(GFP_KERNEL | __GFP_ZERO); | 349 | pt->page = alloc_page(GFP_KERNEL); |
| 350 | if (!pt->page) | 350 | if (!pt->page) |
| 351 | goto fail_page; | 351 | goto fail_page; |
| 352 | 352 | ||
| @@ -381,7 +381,7 @@ fail_bitmap: | |||
| 381 | * Return: 0 if allocation succeeded. | 381 | * Return: 0 if allocation succeeded. |
| 382 | */ | 382 | */ |
| 383 | static int alloc_pt_range(struct i915_page_directory_entry *pd, uint16_t pde, size_t count, | 383 | static int alloc_pt_range(struct i915_page_directory_entry *pd, uint16_t pde, size_t count, |
| 384 | struct drm_device *dev) | 384 | struct drm_device *dev) |
| 385 | { | 385 | { |
| 386 | int i, ret; | 386 | int i, ret; |
| 387 | 387 | ||
| @@ -1165,13 +1165,70 @@ static inline void mark_tlbs_dirty(struct i915_hw_ppgtt *ppgtt) | |||
| 1165 | ppgtt->pd_dirty_rings = INTEL_INFO(ppgtt->base.dev)->ring_mask; | 1165 | ppgtt->pd_dirty_rings = INTEL_INFO(ppgtt->base.dev)->ring_mask; |
| 1166 | } | 1166 | } |
| 1167 | 1167 | ||
| 1168 | static void gen6_initialize_pt(struct i915_address_space *vm, | ||
| 1169 | struct i915_page_table_entry *pt) | ||
| 1170 | { | ||
| 1171 | gen6_pte_t *pt_vaddr, scratch_pte; | ||
| 1172 | int i; | ||
| 1173 | |||
| 1174 | WARN_ON(vm->scratch.addr == 0); | ||
| 1175 | |||
| 1176 | scratch_pte = vm->pte_encode(vm->scratch.addr, | ||
| 1177 | I915_CACHE_LLC, true, 0); | ||
| 1178 | |||
| 1179 | pt_vaddr = kmap_atomic(pt->page); | ||
| 1180 | |||
| 1181 | for (i = 0; i < GEN6_PTES; i++) | ||
| 1182 | pt_vaddr[i] = scratch_pte; | ||
| 1183 | |||
| 1184 | kunmap_atomic(pt_vaddr); | ||
| 1185 | } | ||
| 1186 | |||
| 1168 | static int gen6_alloc_va_range(struct i915_address_space *vm, | 1187 | static int gen6_alloc_va_range(struct i915_address_space *vm, |
| 1169 | uint64_t start, uint64_t length) | 1188 | uint64_t start, uint64_t length) |
| 1170 | { | 1189 | { |
| 1190 | DECLARE_BITMAP(new_page_tables, I915_PDES); | ||
| 1191 | struct drm_device *dev = vm->dev; | ||
| 1192 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
| 1171 | struct i915_hw_ppgtt *ppgtt = | 1193 | struct i915_hw_ppgtt *ppgtt = |
| 1172 | container_of(vm, struct i915_hw_ppgtt, base); | 1194 | container_of(vm, struct i915_hw_ppgtt, base); |
| 1173 | struct i915_page_table_entry *pt; | 1195 | struct i915_page_table_entry *pt; |
| 1196 | const uint32_t start_save = start, length_save = length; | ||
| 1174 | uint32_t pde, temp; | 1197 | uint32_t pde, temp; |
| 1198 | int ret; | ||
| 1199 | |||
| 1200 | WARN_ON(upper_32_bits(start)); | ||
| 1201 | |||
| 1202 | bitmap_zero(new_page_tables, I915_PDES); | ||
| 1203 | |||
| 1204 | /* The allocation is done in two stages so that we can bail out with | ||
| 1205 | * minimal amount of pain. The first stage finds new page tables that | ||
| 1206 | * need allocation. The second stage marks use ptes within the page | ||
| 1207 | * tables. | ||
| 1208 | */ | ||
| 1209 | gen6_for_each_pde(pt, &ppgtt->pd, start, length, temp, pde) { | ||
| 1210 | if (pt != ppgtt->scratch_pt) { | ||
| 1211 | WARN_ON(bitmap_empty(pt->used_ptes, GEN6_PTES)); | ||
| 1212 | continue; | ||
| 1213 | } | ||
| 1214 | |||
| 1215 | /* We've already allocated a page table */ | ||
| 1216 | WARN_ON(!bitmap_empty(pt->used_ptes, GEN6_PTES)); | ||
| 1217 | |||
| 1218 | pt = alloc_pt_single(dev); | ||
| 1219 | if (IS_ERR(pt)) { | ||
| 1220 | ret = PTR_ERR(pt); | ||
| 1221 | goto unwind_out; | ||
| 1222 | } | ||
| 1223 | |||
| 1224 | gen6_initialize_pt(vm, pt); | ||
| 1225 | |||
| 1226 | ppgtt->pd.page_table[pde] = pt; | ||
| 1227 | set_bit(pde, new_page_tables); | ||
| 1228 | } | ||
| 1229 | |||
| 1230 | start = start_save; | ||
| 1231 | length = length_save; | ||
| 1175 | 1232 | ||
| 1176 | gen6_for_each_pde(pt, &ppgtt->pd, start, length, temp, pde) { | 1233 | gen6_for_each_pde(pt, &ppgtt->pd, start, length, temp, pde) { |
| 1177 | DECLARE_BITMAP(tmp_bitmap, GEN6_PTES); | 1234 | DECLARE_BITMAP(tmp_bitmap, GEN6_PTES); |
| @@ -1180,21 +1237,46 @@ static int gen6_alloc_va_range(struct i915_address_space *vm, | |||
| 1180 | bitmap_set(tmp_bitmap, gen6_pte_index(start), | 1237 | bitmap_set(tmp_bitmap, gen6_pte_index(start), |
| 1181 | gen6_pte_count(start, length)); | 1238 | gen6_pte_count(start, length)); |
| 1182 | 1239 | ||
| 1183 | bitmap_or(pt->used_ptes, pt->used_ptes, tmp_bitmap, | 1240 | if (test_and_clear_bit(pde, new_page_tables)) |
| 1241 | gen6_write_pde(&ppgtt->pd, pde, pt); | ||
| 1242 | |||
| 1243 | bitmap_or(pt->used_ptes, tmp_bitmap, pt->used_ptes, | ||
| 1184 | GEN6_PTES); | 1244 | GEN6_PTES); |
| 1185 | } | 1245 | } |
| 1186 | 1246 | ||
| 1247 | WARN_ON(!bitmap_empty(new_page_tables, I915_PDES)); | ||
| 1248 | |||
| 1249 | /* Make sure write is complete before other code can use this page | ||
| 1250 | * table. Also require for WC mapped PTEs */ | ||
| 1251 | readl(dev_priv->gtt.gsm); | ||
| 1252 | |||
| 1187 | mark_tlbs_dirty(ppgtt); | 1253 | mark_tlbs_dirty(ppgtt); |
| 1188 | return 0; | 1254 | return 0; |
| 1255 | |||
| 1256 | unwind_out: | ||
| 1257 | for_each_set_bit(pde, new_page_tables, I915_PDES) { | ||
| 1258 | struct i915_page_table_entry *pt = ppgtt->pd.page_table[pde]; | ||
| 1259 | |||
| 1260 | ppgtt->pd.page_table[pde] = ppgtt->scratch_pt; | ||
| 1261 | unmap_and_free_pt(pt, vm->dev); | ||
| 1262 | } | ||
| 1263 | |||
| 1264 | mark_tlbs_dirty(ppgtt); | ||
| 1265 | return ret; | ||
| 1189 | } | 1266 | } |
| 1190 | 1267 | ||
| 1191 | static void gen6_ppgtt_free(struct i915_hw_ppgtt *ppgtt) | 1268 | static void gen6_ppgtt_free(struct i915_hw_ppgtt *ppgtt) |
| 1192 | { | 1269 | { |
| 1193 | int i; | 1270 | int i; |
| 1194 | 1271 | ||
| 1195 | for (i = 0; i < ppgtt->num_pd_entries; i++) | 1272 | for (i = 0; i < ppgtt->num_pd_entries; i++) { |
| 1196 | unmap_and_free_pt(ppgtt->pd.page_table[i], ppgtt->base.dev); | 1273 | struct i915_page_table_entry *pt = ppgtt->pd.page_table[i]; |
| 1274 | |||
| 1275 | if (pt != ppgtt->scratch_pt) | ||
| 1276 | unmap_and_free_pt(ppgtt->pd.page_table[i], ppgtt->base.dev); | ||
| 1277 | } | ||
| 1197 | 1278 | ||
| 1279 | unmap_and_free_pt(ppgtt->scratch_pt, ppgtt->base.dev); | ||
| 1198 | unmap_and_free_pd(&ppgtt->pd); | 1280 | unmap_and_free_pd(&ppgtt->pd); |
| 1199 | } | 1281 | } |
| 1200 | 1282 | ||
| @@ -1220,6 +1302,12 @@ static int gen6_ppgtt_allocate_page_directories(struct i915_hw_ppgtt *ppgtt) | |||
| 1220 | * size. We allocate at the top of the GTT to avoid fragmentation. | 1302 | * size. We allocate at the top of the GTT to avoid fragmentation. |
| 1221 | */ | 1303 | */ |
| 1222 | BUG_ON(!drm_mm_initialized(&dev_priv->gtt.base.mm)); | 1304 | BUG_ON(!drm_mm_initialized(&dev_priv->gtt.base.mm)); |
| 1305 | ppgtt->scratch_pt = alloc_pt_single(ppgtt->base.dev); | ||
| 1306 | if (IS_ERR(ppgtt->scratch_pt)) | ||
| 1307 | return PTR_ERR(ppgtt->scratch_pt); | ||
| 1308 | |||
| 1309 | gen6_initialize_pt(&ppgtt->base, ppgtt->scratch_pt); | ||
| 1310 | |||
| 1223 | alloc: | 1311 | alloc: |
| 1224 | ret = drm_mm_insert_node_in_range_generic(&dev_priv->gtt.base.mm, | 1312 | ret = drm_mm_insert_node_in_range_generic(&dev_priv->gtt.base.mm, |
| 1225 | &ppgtt->node, GEN6_PD_SIZE, | 1313 | &ppgtt->node, GEN6_PD_SIZE, |
| @@ -1250,6 +1338,7 @@ alloc: | |||
| 1250 | return 0; | 1338 | return 0; |
| 1251 | 1339 | ||
| 1252 | err_out: | 1340 | err_out: |
| 1341 | unmap_and_free_pt(ppgtt->scratch_pt, ppgtt->base.dev); | ||
| 1253 | return ret; | 1342 | return ret; |
| 1254 | } | 1343 | } |
| 1255 | 1344 | ||
| @@ -1261,18 +1350,20 @@ static int gen6_ppgtt_alloc(struct i915_hw_ppgtt *ppgtt) | |||
| 1261 | if (ret) | 1350 | if (ret) |
| 1262 | return ret; | 1351 | return ret; |
| 1263 | 1352 | ||
| 1264 | ret = alloc_pt_range(&ppgtt->pd, 0, ppgtt->num_pd_entries, | 1353 | return 0; |
| 1265 | ppgtt->base.dev); | 1354 | } |
| 1266 | 1355 | ||
| 1267 | if (ret) { | 1356 | static void gen6_scratch_va_range(struct i915_hw_ppgtt *ppgtt, |
| 1268 | drm_mm_remove_node(&ppgtt->node); | 1357 | uint64_t start, uint64_t length) |
| 1269 | return ret; | 1358 | { |
| 1270 | } | 1359 | struct i915_page_table_entry *unused; |
| 1360 | uint32_t pde, temp; | ||
| 1271 | 1361 | ||
| 1272 | return 0; | 1362 | gen6_for_each_pde(unused, &ppgtt->pd, start, length, temp, pde) |
| 1363 | ppgtt->pd.page_table[pde] = ppgtt->scratch_pt; | ||
| 1273 | } | 1364 | } |
| 1274 | 1365 | ||
| 1275 | static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt) | 1366 | static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt, bool aliasing) |
| 1276 | { | 1367 | { |
| 1277 | struct drm_device *dev = ppgtt->base.dev; | 1368 | struct drm_device *dev = ppgtt->base.dev; |
| 1278 | struct drm_i915_private *dev_priv = dev->dev_private; | 1369 | struct drm_i915_private *dev_priv = dev->dev_private; |
| @@ -1295,6 +1386,17 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt) | |||
| 1295 | if (ret) | 1386 | if (ret) |
| 1296 | return ret; | 1387 | return ret; |
| 1297 | 1388 | ||
| 1389 | if (aliasing) { | ||
| 1390 | /* preallocate all pts */ | ||
| 1391 | ret = alloc_pt_range(&ppgtt->pd, 0, ppgtt->num_pd_entries, | ||
| 1392 | ppgtt->base.dev); | ||
| 1393 | |||
| 1394 | if (ret) { | ||
| 1395 | gen6_ppgtt_cleanup(&ppgtt->base); | ||
| 1396 | return ret; | ||
| 1397 | } | ||
| 1398 | } | ||
| 1399 | |||
| 1298 | ppgtt->base.allocate_va_range = gen6_alloc_va_range; | 1400 | ppgtt->base.allocate_va_range = gen6_alloc_va_range; |
| 1299 | ppgtt->base.clear_range = gen6_ppgtt_clear_range; | 1401 | ppgtt->base.clear_range = gen6_ppgtt_clear_range; |
| 1300 | ppgtt->base.insert_entries = gen6_ppgtt_insert_entries; | 1402 | ppgtt->base.insert_entries = gen6_ppgtt_insert_entries; |
| @@ -1309,7 +1411,10 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt) | |||
| 1309 | ppgtt->pd_addr = (gen6_pte_t __iomem *)dev_priv->gtt.gsm + | 1411 | ppgtt->pd_addr = (gen6_pte_t __iomem *)dev_priv->gtt.gsm + |
| 1310 | ppgtt->pd.pd_offset / sizeof(gen6_pte_t); | 1412 | ppgtt->pd.pd_offset / sizeof(gen6_pte_t); |
| 1311 | 1413 | ||
| 1312 | ppgtt->base.clear_range(&ppgtt->base, 0, ppgtt->base.total, true); | 1414 | if (aliasing) |
| 1415 | ppgtt->base.clear_range(&ppgtt->base, 0, ppgtt->base.total, true); | ||
| 1416 | else | ||
| 1417 | gen6_scratch_va_range(ppgtt, 0, ppgtt->base.total); | ||
| 1313 | 1418 | ||
| 1314 | gen6_write_page_range(dev_priv, &ppgtt->pd, 0, ppgtt->base.total); | 1419 | gen6_write_page_range(dev_priv, &ppgtt->pd, 0, ppgtt->base.total); |
| 1315 | 1420 | ||
| @@ -1323,7 +1428,8 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt) | |||
| 1323 | return 0; | 1428 | return 0; |
| 1324 | } | 1429 | } |
| 1325 | 1430 | ||
| 1326 | static int __hw_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt) | 1431 | static int __hw_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt, |
| 1432 | bool aliasing) | ||
| 1327 | { | 1433 | { |
| 1328 | struct drm_i915_private *dev_priv = dev->dev_private; | 1434 | struct drm_i915_private *dev_priv = dev->dev_private; |
| 1329 | 1435 | ||
| @@ -1331,7 +1437,7 @@ static int __hw_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt) | |||
| 1331 | ppgtt->base.scratch = dev_priv->gtt.base.scratch; | 1437 | ppgtt->base.scratch = dev_priv->gtt.base.scratch; |
| 1332 | 1438 | ||
| 1333 | if (INTEL_INFO(dev)->gen < 8) | 1439 | if (INTEL_INFO(dev)->gen < 8) |
| 1334 | return gen6_ppgtt_init(ppgtt); | 1440 | return gen6_ppgtt_init(ppgtt, aliasing); |
| 1335 | else | 1441 | else |
| 1336 | return gen8_ppgtt_init(ppgtt, dev_priv->gtt.base.total); | 1442 | return gen8_ppgtt_init(ppgtt, dev_priv->gtt.base.total); |
| 1337 | } | 1443 | } |
| @@ -1340,7 +1446,7 @@ int i915_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt) | |||
| 1340 | struct drm_i915_private *dev_priv = dev->dev_private; | 1446 | struct drm_i915_private *dev_priv = dev->dev_private; |
| 1341 | int ret = 0; | 1447 | int ret = 0; |
| 1342 | 1448 | ||
| 1343 | ret = __hw_ppgtt_init(dev, ppgtt); | 1449 | ret = __hw_ppgtt_init(dev, ppgtt, false); |
| 1344 | if (ret == 0) { | 1450 | if (ret == 0) { |
| 1345 | kref_init(&ppgtt->ref); | 1451 | kref_init(&ppgtt->ref); |
| 1346 | drm_mm_init(&ppgtt->base.mm, ppgtt->base.start, | 1452 | drm_mm_init(&ppgtt->base.mm, ppgtt->base.start, |
| @@ -1975,9 +2081,11 @@ static int i915_gem_setup_global_gtt(struct drm_device *dev, | |||
| 1975 | if (!ppgtt) | 2081 | if (!ppgtt) |
| 1976 | return -ENOMEM; | 2082 | return -ENOMEM; |
| 1977 | 2083 | ||
| 1978 | ret = __hw_ppgtt_init(dev, ppgtt); | 2084 | ret = __hw_ppgtt_init(dev, ppgtt, true); |
| 1979 | if (ret != 0) | 2085 | if (ret) { |
| 2086 | kfree(ppgtt); | ||
| 1980 | return ret; | 2087 | return ret; |
| 2088 | } | ||
| 1981 | 2089 | ||
| 1982 | dev_priv->mm.aliasing_ppgtt = ppgtt; | 2090 | dev_priv->mm.aliasing_ppgtt = ppgtt; |
| 1983 | } | 2091 | } |
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h index 3d873467377e..3f0ad9f25441 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.h +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h | |||
| @@ -320,6 +320,8 @@ struct i915_hw_ppgtt { | |||
| 320 | struct i915_page_directory_entry pd; | 320 | struct i915_page_directory_entry pd; |
| 321 | }; | 321 | }; |
| 322 | 322 | ||
| 323 | struct i915_page_table_entry *scratch_pt; | ||
| 324 | |||
| 323 | struct drm_i915_file_private *file_priv; | 325 | struct drm_i915_file_private *file_priv; |
| 324 | 326 | ||
| 325 | gen6_pte_t __iomem *pd_addr; | 327 | gen6_pte_t __iomem *pd_addr; |
