#include <linux/compiler.h>
#include <linux/types.h>
#include <unistd.h>
#include "tests.h"
#include "debug.h"
#include "machine.h"
#include "event.h"
#include "unwind.h"
#include "perf_regs.h"
#include "map.h"
#include "thread.h"
#include "callchain.h"

#if defined (__x86_64__) || defined (__i386__) || defined (__powerpc__)
#include "arch-tests.h"
#endif

/* For bsearch. We try to unwind functions in shared object. */
#include <stdlib.h>

static int mmap_handler(struct perf_tool *tool __maybe_unused,
			union perf_event *event,
			struct perf_sample *sample,
			struct machine *machine)
{
	return machine__process_mmap2_event(machine, event, sample);
}

static int init_live_machine(struct machine *machine)
{
	union perf_event event;
	pid_t pid = getpid();

	return perf_event__synthesize_mmap_events(NULL, &event, pid, pid,
						  mmap_handler, machine, true, 500);
}

#define MAX_STACK 8

static int unwind_entry(struct unwind_entry *entry, void *arg)
{
	unsigned long *cnt = (unsigned long *) arg;
	char *symbol = entry->sym ? entry->sym->name : NULL;
	static const char *funcs[MAX_STACK] = {
		"test__arch_unwind_sample",
		"unwind_thread",
		"compare",
		"bsearch",
		"krava_3",
		"krava_2",
		"krava_1",
		"test__dwarf_unwind"
	};
	/*
	 * The funcs[MAX_STACK] array index, based on the
	 * callchain order setup.
	 */
	int idx = callchain_param.order == ORDER_CALLER ?
		  MAX_STACK - *cnt - 1 : *cnt;

	if (*cnt >= MAX_STACK) {
		pr_debug("failed: crossed the max stack value %d\n", MAX_STACK);
		return -1;
	}

	if (!symbol) {
		pr_debug("failed: got unresolved address 0x%" PRIx64 "\n",
			 entry->ip);
		return -1;
	}

	(*cnt)++;
	pr_debug("got: %s 0x%" PRIx64 ", expecting %s\n",
		 symbol, entry->ip, funcs[idx]);
	return strcmp((const char *) symbol, funcs[idx]);
}

__attribute__ ((noinline))
static int unwind_thread(struct thread *thread)
{
	struct perf_sample sample;
	unsigned long cnt = 0;
	int err = -1;

	memset(&sample, 0, sizeof(sample));

	if (test__arch_unwind_sample(&sample, thread)) {
		pr_debug("failed to get unwind sample\n");
		goto out;
	}

	err = unwind__get_entries(unwind_entry, &cnt, thread,
				  &sample, MAX_STACK);
	if (err)
		pr_debug("unwind failed\n");
	else if (cnt != MAX_STACK) {
		pr_debug("got wrong number of stack entries %lu != %d\n",
			 cnt, MAX_STACK);
		err = -1;
	}

 out:
	free(sample.user_stack.data);
	free(sample.user_regs.regs);
	return err;
}

static int global_unwind_retval = -INT_MAX;

__attribute__ ((noinline))
static int compare(void *p1, void *p2)
{
	/* Any possible value should be 'thread' */
	struct thread *thread = *(struct thread **)p1;

	if (global_unwind_retval == -INT_MAX) {
		/* Call unwinder twice for both callchain orders. */
		callchain_param.order = ORDER_CALLER;

		global_unwind_retval = unwind_thread(thread);
		if (!global_unwind_retval) {
			callchain_param.order = ORDER_CALLEE;
			global_unwind_retval = unwind_thread(thread);
		}
	}

	return p1 - p2;
}

__attribute__ ((noinline))
static int krava_3(struct thread *thread)
{
	struct thread *array[2] = {thread, thread};
	void *fp = &bsearch;
	/*
	 * make _bsearch a volatile function pointer to
	 * prevent potential optimization, which may expand
	 * bsearch and call compare directly from this function,
	 * instead of libc shared object.
	 */
	void *(*volatile _bsearch)(void *, void *, size_t,
			size_t, int (*)(void *, void *));

	_bsearch = fp;
	_bsearch(array, &thread, 2, sizeof(struct thread **), compare);
	return global_unwind_retval;
}

__attribute__ ((noinline))
static int krava_2(struct thread *thread)
{
	return krava_3(thread);
}

__attribute__ ((noinline))
static int krava_1(struct thread *thread)
{
	return krava_2(thread);
}

int test__dwarf_unwind(int subtest __maybe_unused)
{
	struct machine *machine;
	struct thread *thread;
	int err = -1;

	machine = machine__new_host();
	if (!machine) {
		pr_err("Could not get machine\n");
		return -1;
	}

	if (machine__create_kernel_maps(machine)) {
		pr_err("Failed to create kernel maps\n");
		return -1;
	}

	callchain_param.record_mode = CALLCHAIN_DWARF;

	if (init_live_machine(machine)) {
		pr_err("Could not init machine\n");
		goto out;
	}

	if (verbose > 1)
		machine__fprintf(machine, stderr);

	thread = machine__find_thread(machine, getpid(), getpid());
	if (!thread) {
		pr_err("Could not get thread\n");
		goto out;
	}

	err = krava_1(thread);
	thread__put(thread);

 out:
	machine__delete_threads(machine);
	machine__delete(machine);
	return err;
}
' onchange='this.form.submit();'><option value='0' selected='selected'>include</option><option value='1'>ignore</option></select></td></tr><tr><td class='label'>mode:</td><td class='ctrl'><select name='dt' onchange='this.form.submit();'><option value='0' selected='selected'>unified</option><option value='1'>ssdiff</option><option value='2'>stat only</option></select></td></tr><tr><td/><td class='ctrl'><noscript><input type='submit' value='reload'/></noscript></td></tr></table></form></div><div class='diffstat-header'><a href='/cgit.cgi/linux/net-next.git/diff/?id=205321f0927ad2303e7f71767d402e0ff36a9a87&amp;id2=1001354ca34179f3db924eb66672442a173147dc'>Diffstat</a> (limited to 'include/drm/drm_fixed.h')</div><table summary='diffstat' class='diffstat'>b6672d5a37e'>98fe974ee4e20121253de7f61fc8d01bdb3821c1</a> /<a href='/cgit.cgi/linux/net-next.git/tree/net/sched/sch_red.c?id=39cb2c9a316e77f6dfba96c543e55b6672d5a37e'>net/sched/sch_red.c</a></td></tr>
<tr><th>parent</th><td colspan='2' class='oid'><a href='/cgit.cgi/linux/net-next.git/commit/net/sched/sch_red.c?id=2c5d9555d6d937966d79d4c6529a5f7b9206e405'>2c5d9555d6d937966d79d4c6529a5f7b9206e405</a> (<a href='/cgit.cgi/linux/net-next.git/diff/net/sched/sch_red.c?id=39cb2c9a316e77f6dfba96c543e55b6672d5a37e&amp;id2=2c5d9555d6d937966d79d4c6529a5f7b9206e405'>diff</a>)</td></tr></table>
<div class='commit-subject'>drm/i915: Check for NULL i915_vma in intel_unpin_fb_obj()</div><div class='commit-msg'>I've seen this trigger twice now, where the i915_gem_object_to_ggtt()
call in intel_unpin_fb_obj() returns NULL, resulting in an oops
immediately afterwards as the (inlined) call to i915_vma_unpin_fence()
tries to dereference it.

It seems to be some race condition where the object is going away at
shutdown time, since both times happened when shutting down the X
server.  The call chains were different:

 - VT ioctl(KDSETMODE, KD_TEXT):

    intel_cleanup_plane_fb+0x5b/0xa0 [i915]
    drm_atomic_helper_cleanup_planes+0x6f/0x90 [drm_kms_helper]
    intel_atomic_commit_tail+0x749/0xfe0 [i915]
    intel_atomic_commit+0x3cb/0x4f0 [i915]
    drm_atomic_commit+0x4b/0x50 [drm]
    restore_fbdev_mode+0x14c/0x2a0 [drm_kms_helper]
    drm_fb_helper_restore_fbdev_mode_unlocked+0x34/0x80 [drm_kms_helper]
    drm_fb_helper_set_par+0x2d/0x60 [drm_kms_helper]
    intel_fbdev_set_par+0x18/0x70 [i915]
    fb_set_var+0x236/0x460
    fbcon_blank+0x30f/0x350
    do_unblank_screen+0xd2/0x1a0
    vt_ioctl+0x507/0x12a0
    tty_ioctl+0x355/0xc30
    do_vfs_ioctl+0xa3/0x5e0
    SyS_ioctl+0x79/0x90
    entry_SYSCALL_64_fastpath+0x13/0x94

 - i915 unpin_work workqueue:

    intel_unpin_work_fn+0x58/0x140 [i915]
    process_one_work+0x1f1/0x480
    worker_thread+0x48/0x4d0
    kthread+0x101/0x140

and this patch purely papers over the issue by adding a NULL pointer
check and a WARN_ON_ONCE() to avoid the oops that would then generally
make the machine unresponsive.

Other callers of i915_gem_object_to_ggtt() seem to also check for the
returned pointer being NULL and warn about it, so this clearly has
happened before in other places.

[ Reported it originally to the i915 developers on Jan 8, applying the
  ugly workaround on my own now after triggering the problem for the
  second time with no feedback.

  This is likely to be the same bug reported as

     https://bugs.freedesktop.org/show_bug.cgi?id=98829
     https://bugs.freedesktop.org/show_bug.cgi?id=99134

  which has a patch for the underlying problem, but it hasn't gotten to
  me, so I'm applying the workaround. ]

Cc: Daniel Vetter &lt;daniel.vetter@intel.com&gt;
Cc: Jani Nikula &lt;jani.nikula@linux.intel.com&gt;
Cc: Ville Syrjälä &lt;ville.syrjala@linux.intel.com&gt;
Cc: Chris Wilson &lt;chris@chris-wilson.co.uk&gt;
Cc: Maarten Lankhorst &lt;maarten.lankhorst@linux.intel.com&gt;
Cc: Tvrtko Ursulin &lt;tvrtko.ursulin@intel.com&gt;
Cc: Imre Deak &lt;imre.deak@intel.com&gt;
Signed-off-by: Linus Torvalds &lt;torvalds@linux-foundation.org&gt;
</div><div class='diffstat-header'><a href='/cgit.cgi/linux/net-next.git/diff/?id=39cb2c9a316e77f6dfba96c543e55b6672d5a37e'>Diffstat</a> (limited to 'net/sched/sch_red.c')</div><table summary='diffstat' class='diffstat'>