Re: [PATCH v5 06/13] lockdep: Implement crossrelease feature

Peter Zijlstra
2017-02-28T15:15:30Z

On Wed, Jan 18, 2017 at 10:17:32PM +0900, Byungchul Park wrote:
> + /*
> + * If the previous in held_locks can create a proper dependency
> + * with a target crosslock, then we can skip commiting this,
> + * since "the target crosslock -> the previous lock" and
> + * "the previous lock -> this lock" can cover the case. So we
> + * keep the previous's gen_id to make the decision.
> + */
> + unsigned int prev_gen_id;

> +static void add_xhlock(struct held_lock *hlock, unsigned int prev_gen_id)
> +{
> + struct hist_lock *xhlock;
> +
> + xhlock = alloc_xhlock();
> +
> + /* Initialize hist_lock's members */
> + xhlock->hlock = *hlock;
> + xhlock->nmi = !!(preempt_count() & NMI_MASK);
> + /*
> + * prev_gen_id is used to skip adding dependency at commit step,
> + * when the previous lock in held_locks can do that instead.
> + */
> + xhlock->prev_gen_id = prev_gen_id;
> + xhlock->work_id = current->work_id;
> +
> + xhlock->trace.nr_entries = 0;
> + xhlock->trace.max_entries = MAX_XHLOCK_TRACE_ENTRIES;
> + xhlock->trace.entries = xhlock->trace_entries;
> + xhlock->trace.skip = 3;
> + save_stack_trace(&xhlock->trace);
> +}

> +static void check_add_xhlock(struct held_lock *hlock)
> +{
> + struct held_lock *prev;
> + struct held_lock *start;
> + unsigned int gen_id;
> + unsigned int gen_id_invalid;
> +
> + if (!current->xhlocks || !depend_before(hlock))
> + return;
> +
> + gen_id = (unsigned int)atomic_read(&cross_gen_id);
> + /*
> + * gen_id_invalid must be too old to be valid. That means
> + * current hlock should not be skipped but should be
> + * considered at commit step.
> + */
> + gen_id_invalid = gen_id - (UINT_MAX / 4);
> + start = current->held_locks;
> +
> + for (prev = hlock - 1; prev >= start &&
> + !depend_before(prev); prev--);
> +
> + if (prev < start)
> + add_xhlock(hlock, gen_id_invalid);
> + else if (prev->gen_id != gen_id)
> + add_xhlock(hlock, prev->gen_id);
> +}

> +static int commit_xhlocks(struct cross_lock *xlock)
> +{
> + struct task_struct *curr = current;
> + struct hist_lock *xhlock_c = xhlock_curr(curr);
> + struct hist_lock *xhlock = xhlock_c;
> +
> + do {
> + xhlock = xhlock_prev(curr, xhlock);
> +
> + if (!xhlock_used(xhlock))
> + break;
> +
> + if (before(xhlock->hlock.gen_id, xlock->hlock.gen_id))
> + break;
> +
> + if (same_context_xhlock(xhlock) &&
> + before(xhlock->prev_gen_id, xlock->hlock.gen_id) &&
> + !commit_xhlock(xlock, xhlock))
> + return 0;
> + } while (xhlock_c != xhlock);
> +
> + return 1;
> +}

So I'm still struggling with prev_gen_id; is it an optimization or is it
required for correctness?