[Intel-gfx] [PATCH 07/25] drm/i915: Cache the error string
Joonas Lahtinen
joonas.lahtinen at linux.intel.com
Fri Nov 23 12:52:28 UTC 2018
Quoting Chris Wilson (2018-11-02 18:12:14)
> Currently, we convert the error state into a string every time we read
> from sysfs (and sysfs reads in page size (4KiB) chunks). We do try to
> window the string and only capture the portion that is being read, but
> that means that we must always convert up to the window to find the
> start. For a very large error state bordering on EXEC_OBJECT_CAPTURE
> abuse, this is noticeable as it degrades to O(N^2)!
>
> As we do not have a convenient hook for sysfs open(), and we would like
> to keep the lazy conversion into a string, do the conversion of the
> whole string on the first read and keep the string until the error state
> is freed.
>
> v2: Don't double advance simple_read_from_buffer
> v3: Due to extreme pain of lack of vrealloc, use a scatterlist
> v4: Keep the forward iterator loosely cached
>
> Reported-by: Jason Ekstrand <jason at jlekstrand.net>
> Testcase: igt/gem_exec_capture/many*
> Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
> Cc: Mika Kuoppala <mika.kuoppala at linux.intel.com>
> Cc: Jason Ekstrand <jason at jlekstrand.net>
<SNIP>
> @@ -943,30 +943,28 @@ static int i915_gem_fence_regs_info(struct seq_file *m, void *data)
> static ssize_t gpu_state_read(struct file *file, char __user *ubuf,
> size_t count, loff_t *pos)
> {
> - struct i915_gpu_state *error = file->private_data;
> - struct drm_i915_error_state_buf str;
> + struct i915_gpu_state *error;
> ssize_t ret;
> - loff_t tmp;
> + void *buf;
>
> + error = file->private_data;
> if (!error)
> return 0;
>
> - ret = i915_error_state_buf_init(&str, error->i915, count, *pos);
> - if (ret)
> - return ret;
> -
> - ret = i915_error_state_to_str(&str, error);
> - if (ret)
> - goto out;
> + /* Bounce buffer required because of kernfs __user API convenience. */
> + buf = kmalloc(count, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
>
> - tmp = 0;
> - ret = simple_read_from_buffer(ubuf, count, &tmp, str.buf, str.bytes);
> - if (ret < 0)
> - goto out;
> + ret = i915_gpu_state_copy_to_buffer(error, buf, *pos, count);
> + if (ret > 0) {
if (ret <= 0)
goto out;
One can never onion too early.
> + if (!copy_to_user(ubuf, buf, ret))
> + *pos += ret;
> + else
> + ret = -EFAULT;
> + }
>
> - *pos = str.start + ret;
> -out:
> - i915_error_state_buf_release(&str);
> + kfree(buf);
> return ret;
> }
<SNIP>
> -static bool __i915_error_ok(struct drm_i915_error_state_buf *e)
> +static void __sg_set_buf(struct scatterlist *sg,
> + void *addr, unsigned int len, loff_t it)
> {
> -
> - if (!e->err && WARN(e->bytes > (e->size - 1), "overflow")) {
> - e->err = -ENOSPC;
> - return false;
> - }
> -
> - if (e->bytes == e->size - 1 || e->err)
> - return false;
> -
> - return true;
> + sg->page_link = (unsigned long)virt_to_page(addr);
> + sg->offset = offset_in_page(addr);
> + sg->length = len;
> + sg->dma_address = it;
> }
The least we can do is to extract the sg functions as a follow-up patch
and try to haggle them to scatterlist.h.
>
> -static bool __i915_error_seek(struct drm_i915_error_state_buf *e,
> - unsigned len)
> +static bool __i915_error_grow(struct drm_i915_error_state_buf *e, size_t len)
> {
> - if (e->pos + len <= e->start) {
> - e->pos += len;
> + if (!len)
> return false;
> - }
>
> - /* First vsnprintf needs to fit in its entirety for memmove */
> - if (len >= e->size) {
> - e->err = -EIO;
> - return false;
> - }
> + if (e->bytes + len + 1 > e->size) {
if (e->bytes + len + 1 <= e->size)
return true;
Indent reduced somewhat.
> + if (e->bytes) {
> + __sg_set_buf(e->cur++, e->buf, e->bytes, e->iter);
> + e->iter += e->bytes;
> + e->buf = NULL;
> + e->bytes = 0;
> + }
>
> - return true;
> -}
> + if (e->cur == e->end) {
> + struct scatterlist *sgl;
>
<SNIP>
> @@ -268,6 +268,8 @@ static int compress_page(struct compress *c,
>
> if (zlib_deflate(zstream, Z_NO_FLUSH) != Z_OK)
> return -EIO;
> +
> + touch_nmi_watchdog();
Lost hunk looking for a good home.
> } while (zstream->avail_in);
>
> /* Fallback to uncompressed if we increase size? */
> @@ -635,36 +637,53 @@ static void err_print_uc(struct drm_i915_error_state_buf *m,
> print_error_obj(m, NULL, "GuC log buffer", error_uc->guc_log);
> }
>
> -int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
> - const struct i915_gpu_state *error)
> +static void err_free_sgl(struct scatterlist *sgl)
> {
> - struct drm_i915_private *dev_priv = m->i915;
> + while (sgl) {
> + struct scatterlist *sg;
> +
> + for (sg = sgl; !sg_is_chain(sg); sg++) {
> + kfree(sg_virt(sg));
> + if (sg_is_last(sg))
> + break;
> + }
> +
> + sg = sg_is_last(sg) ? NULL : sg_chain_ptr(sg);
> + free_page((unsigned long)sgl);
> + sgl = sg;
> + }
> +}
> +
> +static int err_print_to_sgl(struct i915_gpu_state *error)
> +{
> + struct drm_i915_error_state_buf m;
> struct drm_i915_error_object *obj;
> struct timespec64 ts;
> int i, j;
>
> - if (!error) {
> - err_printf(m, "No error state collected\n");
> + if (READ_ONCE(error->sgl))
> return 0;
> - }
> +
> + memset(&m, 0, sizeof(m));
> + m.i915 = error->i915;
I'm almost tempted to suggest to wrap the following code with
__err_print_to_sgl to protect from unnecessary motion? I tried
to look through it and it seems OK. But the next time type of
m changes it's going to be huge.
This is (with the assumption that sg innards are extracted as
a follow-up);
Reviewed-by: Joonas Lahtinen <joonas.lahtinen at linux.intel.com>
Regards, Joonas
More information about the Intel-gfx
mailing list