diff options
| author | Chris Wilson <chris@chris-wilson.co.uk> | 2018-10-03 04:24:22 -0400 |
|---|---|---|
| committer | Rodrigo Vivi <rodrigo.vivi@intel.com> | 2018-10-03 11:02:42 -0400 |
| commit | 4c9613ce556fdeb671e779668b627ea3a2b61728 (patch) | |
| tree | 0b79560066e47e1b1b08f00164883824d77a0dc5 | |
| parent | 4ca8ca9fe7dc792000c3762de5081a4d6dc33667 (diff) | |
drm/i915: Handle incomplete Z_FINISH for compressed error states
The final call to zlib_deflate(Z_FINISH) may require more output
space to be allocated and so needs to re-invoked. Failure to do so in
the current code leads to incomplete zlib streams (albeit intact due to
the use of Z_SYNC_FLUSH) resulting in the occasional short object
capture.
v2: Check against overrunning our pre-allocated page array
v3: Drop Z_SYNC_FLUSH entirely
Testcase: igt/i915-error-capture.js
Fixes: 0a97015d45ee ("drm/i915: Compress GPU objects in error state")
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Cc: <stable@vger.kernel.org> # v4.10+
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20181003082422.23214-1-chris@chris-wilson.co.uk
(cherry picked from commit 83bc0f5b432f60394466deef16fc753e27371d0b)
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
| -rw-r--r-- | drivers/gpu/drm/i915/i915_gpu_error.c | 88 | ||||
| -rw-r--r-- | drivers/gpu/drm/i915/i915_gpu_error.h | 1 |
2 files changed, 64 insertions, 25 deletions
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index f7f2aa71d8d9..a262a64f5625 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c | |||
| @@ -232,6 +232,20 @@ static bool compress_init(struct compress *c) | |||
| 232 | return true; | 232 | return true; |
| 233 | } | 233 | } |
| 234 | 234 | ||
| 235 | static void *compress_next_page(struct drm_i915_error_object *dst) | ||
| 236 | { | ||
| 237 | unsigned long page; | ||
| 238 | |||
| 239 | if (dst->page_count >= dst->num_pages) | ||
| 240 | return ERR_PTR(-ENOSPC); | ||
| 241 | |||
| 242 | page = __get_free_page(GFP_ATOMIC | __GFP_NOWARN); | ||
| 243 | if (!page) | ||
| 244 | return ERR_PTR(-ENOMEM); | ||
| 245 | |||
| 246 | return dst->pages[dst->page_count++] = (void *)page; | ||
| 247 | } | ||
| 248 | |||
| 235 | static int compress_page(struct compress *c, | 249 | static int compress_page(struct compress *c, |
| 236 | void *src, | 250 | void *src, |
| 237 | struct drm_i915_error_object *dst) | 251 | struct drm_i915_error_object *dst) |
| @@ -245,19 +259,14 @@ static int compress_page(struct compress *c, | |||
| 245 | 259 | ||
| 246 | do { | 260 | do { |
| 247 | if (zstream->avail_out == 0) { | 261 | if (zstream->avail_out == 0) { |
| 248 | unsigned long page; | 262 | zstream->next_out = compress_next_page(dst); |
| 249 | 263 | if (IS_ERR(zstream->next_out)) | |
| 250 | page = __get_free_page(GFP_ATOMIC | __GFP_NOWARN); | 264 | return PTR_ERR(zstream->next_out); |
| 251 | if (!page) | ||
| 252 | return -ENOMEM; | ||
| 253 | 265 | ||
| 254 | dst->pages[dst->page_count++] = (void *)page; | ||
| 255 | |||
| 256 | zstream->next_out = (void *)page; | ||
| 257 | zstream->avail_out = PAGE_SIZE; | 266 | zstream->avail_out = PAGE_SIZE; |
| 258 | } | 267 | } |
| 259 | 268 | ||
| 260 | if (zlib_deflate(zstream, Z_SYNC_FLUSH) != Z_OK) | 269 | if (zlib_deflate(zstream, Z_NO_FLUSH) != Z_OK) |
| 261 | return -EIO; | 270 | return -EIO; |
| 262 | } while (zstream->avail_in); | 271 | } while (zstream->avail_in); |
| 263 | 272 | ||
| @@ -268,19 +277,42 @@ static int compress_page(struct compress *c, | |||
| 268 | return 0; | 277 | return 0; |
| 269 | } | 278 | } |
| 270 | 279 | ||
| 271 | static void compress_fini(struct compress *c, | 280 | static int compress_flush(struct compress *c, |
| 272 | struct drm_i915_error_object *dst) | 281 | struct drm_i915_error_object *dst) |
| 273 | { | 282 | { |
| 274 | struct z_stream_s *zstream = &c->zstream; | 283 | struct z_stream_s *zstream = &c->zstream; |
| 275 | 284 | ||
| 276 | if (dst) { | 285 | do { |
| 277 | zlib_deflate(zstream, Z_FINISH); | 286 | switch (zlib_deflate(zstream, Z_FINISH)) { |
| 278 | dst->unused = zstream->avail_out; | 287 | case Z_OK: /* more space requested */ |
| 279 | } | 288 | zstream->next_out = compress_next_page(dst); |
| 289 | if (IS_ERR(zstream->next_out)) | ||
| 290 | return PTR_ERR(zstream->next_out); | ||
| 291 | |||
| 292 | zstream->avail_out = PAGE_SIZE; | ||
| 293 | break; | ||
| 294 | |||
| 295 | case Z_STREAM_END: | ||
| 296 | goto end; | ||
| 297 | |||
| 298 | default: /* any error */ | ||
| 299 | return -EIO; | ||
| 300 | } | ||
| 301 | } while (1); | ||
| 302 | |||
| 303 | end: | ||
| 304 | memset(zstream->next_out, 0, zstream->avail_out); | ||
| 305 | dst->unused = zstream->avail_out; | ||
| 306 | return 0; | ||
| 307 | } | ||
| 308 | |||
| 309 | static void compress_fini(struct compress *c, | ||
| 310 | struct drm_i915_error_object *dst) | ||
| 311 | { | ||
| 312 | struct z_stream_s *zstream = &c->zstream; | ||
| 280 | 313 | ||
| 281 | zlib_deflateEnd(zstream); | 314 | zlib_deflateEnd(zstream); |
| 282 | kfree(zstream->workspace); | 315 | kfree(zstream->workspace); |
| 283 | |||
| 284 | if (c->tmp) | 316 | if (c->tmp) |
| 285 | free_page((unsigned long)c->tmp); | 317 | free_page((unsigned long)c->tmp); |
| 286 | } | 318 | } |
| @@ -319,6 +351,12 @@ static int compress_page(struct compress *c, | |||
| 319 | return 0; | 351 | return 0; |
| 320 | } | 352 | } |
| 321 | 353 | ||
| 354 | static int compress_flush(struct compress *c, | ||
| 355 | struct drm_i915_error_object *dst) | ||
| 356 | { | ||
| 357 | return 0; | ||
| 358 | } | ||
| 359 | |||
| 322 | static void compress_fini(struct compress *c, | 360 | static void compress_fini(struct compress *c, |
| 323 | struct drm_i915_error_object *dst) | 361 | struct drm_i915_error_object *dst) |
| 324 | { | 362 | { |
| @@ -917,6 +955,7 @@ i915_error_object_create(struct drm_i915_private *i915, | |||
| 917 | unsigned long num_pages; | 955 | unsigned long num_pages; |
| 918 | struct sgt_iter iter; | 956 | struct sgt_iter iter; |
| 919 | dma_addr_t dma; | 957 | dma_addr_t dma; |
| 958 | int ret; | ||
| 920 | 959 | ||
| 921 | if (!vma) | 960 | if (!vma) |
| 922 | return NULL; | 961 | return NULL; |
| @@ -930,6 +969,7 @@ i915_error_object_create(struct drm_i915_private *i915, | |||
| 930 | 969 | ||
| 931 | dst->gtt_offset = vma->node.start; | 970 | dst->gtt_offset = vma->node.start; |
| 932 | dst->gtt_size = vma->node.size; | 971 | dst->gtt_size = vma->node.size; |
| 972 | dst->num_pages = num_pages; | ||
| 933 | dst->page_count = 0; | 973 | dst->page_count = 0; |
| 934 | dst->unused = 0; | 974 | dst->unused = 0; |
| 935 | 975 | ||
| @@ -938,28 +978,26 @@ i915_error_object_create(struct drm_i915_private *i915, | |||
| 938 | return NULL; | 978 | return NULL; |
| 939 | } | 979 | } |
| 940 | 980 | ||
| 981 | ret = -EINVAL; | ||
| 941 | for_each_sgt_dma(dma, iter, vma->pages) { | 982 | for_each_sgt_dma(dma, iter, vma->pages) { |
| 942 | void __iomem *s; | 983 | void __iomem *s; |
| 943 | int ret; | ||
| 944 | 984 | ||
| 945 | ggtt->vm.insert_page(&ggtt->vm, dma, slot, I915_CACHE_NONE, 0); | 985 | ggtt->vm.insert_page(&ggtt->vm, dma, slot, I915_CACHE_NONE, 0); |
| 946 | 986 | ||
| 947 | s = io_mapping_map_atomic_wc(&ggtt->iomap, slot); | 987 | s = io_mapping_map_atomic_wc(&ggtt->iomap, slot); |
| 948 | ret = compress_page(&compress, (void __force *)s, dst); | 988 | ret = compress_page(&compress, (void __force *)s, dst); |
| 949 | io_mapping_unmap_atomic(s); | 989 | io_mapping_unmap_atomic(s); |
| 950 | |||
| 951 | if (ret) | 990 | if (ret) |
| 952 | goto unwind; | 991 | break; |
| 953 | } | 992 | } |
| 954 | goto out; | ||
| 955 | 993 | ||
| 956 | unwind: | 994 | if (ret || compress_flush(&compress, dst)) { |
| 957 | while (dst->page_count--) | 995 | while (dst->page_count--) |
| 958 | free_page((unsigned long)dst->pages[dst->page_count]); | 996 | free_page((unsigned long)dst->pages[dst->page_count]); |
| 959 | kfree(dst); | 997 | kfree(dst); |
| 960 | dst = NULL; | 998 | dst = NULL; |
| 999 | } | ||
| 961 | 1000 | ||
| 962 | out: | ||
| 963 | compress_fini(&compress, dst); | 1001 | compress_fini(&compress, dst); |
| 964 | ggtt->vm.clear_range(&ggtt->vm, slot, PAGE_SIZE); | 1002 | ggtt->vm.clear_range(&ggtt->vm, slot, PAGE_SIZE); |
| 965 | return dst; | 1003 | return dst; |
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.h b/drivers/gpu/drm/i915/i915_gpu_error.h index f893a4e8b783..8710fb18ed74 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.h +++ b/drivers/gpu/drm/i915/i915_gpu_error.h | |||
| @@ -135,6 +135,7 @@ struct i915_gpu_state { | |||
| 135 | struct drm_i915_error_object { | 135 | struct drm_i915_error_object { |
| 136 | u64 gtt_offset; | 136 | u64 gtt_offset; |
| 137 | u64 gtt_size; | 137 | u64 gtt_size; |
| 138 | int num_pages; | ||
| 138 | int page_count; | 139 | int page_count; |
| 139 | int unused; | 140 | int unused; |
| 140 | u32 *pages[0]; | 141 | u32 *pages[0]; |
