'context imbalance' warnings

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

This is the continuation of last week thread.

I've added a few more simplifications for selects & compares on top
of those from last week. Alas, none of these makes any real difference:
in my usual tests I have just one warning less (total number is now 995):
   * fs/dcache.c:1226:24: warning: context imbalance in 'dentry_lru_isolate_shrink' - different lock contexts for basic block

But well, these simplifications are good and can help.

So, I've taken again a look at the origin of these warnings (I already
did this 2-3 years ago). Here is what I found for the 6 first ones:

1) intel_{start,stop}_scheduling()
   - take/release a lock conditionally
   - but intel_start_scheduling() returns void ...

2) get_locked_pte()
   - it's a conditional lock
   - some code assume it never fails
   - some code protect it by a [VM_]BUG_ON()
   - sparse complains

3) lock_vector_lock()
   - OK, but the annotation is missing, I'll send a fix

4) raw_spin_trylock()
   - on UP, raw_spin_trylock() takes the context twice ...
   - I'll see what can be fixed there

5) acpi_os_read_memory()
   the usage pattern is 'interesting':

	flag = 0;
	lock();
	addr = lookup();
	if (!addr) {
		unlock()
		addr = map();
		if (!addr)
			return ERROR;
		flag = true;
	}

	// sparse complains here

	... use addr ...

	if (flag)
		unmap(addr);
	else
		unlock();
	return 0;

   I see the pattern and I suppose the locking is correct but I
   think that sparse's warning is justified. Is it needed to do
   the early unlock() when the lookup fails?

6) clk_enable_lock() (@ drivers/clk/clk.c)
   This one is really interesting and shows one of Sparse's shortcoming

	if (!spin_trylock_irqsave(&lock, flags)) {
		if (some_test) {
			__acquire;
			local_save_flags(flags);
			return flags;
		}
		spin_lock_irqsave(&enable_lock, flags);
	}
	...
	return;

   So the code is correct but what sparse really sees is:

	flags = __raw_save_flags();
	cli;
	tmp = _raw_spin_try_lock();
	if (tmp)
		__acquire;
	else
		__raw_restore_flags(flags);

	// sparse complains here

	if (!tmp) {
		if (some_test) {
			__acquire;
			flags = __raw_save_flags();
			return flags;
		}
		flags = raw_spin_lock_irqsave();
	}
	...
	return flags;

   Now sparse could merge the two branches depending on tmp,
   as if the code would have been written as:

	flags = __raw_save_flags();
	cli;
	tmp = _raw_spin_try_lock();
	if (tmp)
		__acquire;
	else {
		__raw_restore_flags(flags);
		if (some_test) {
			__acquire;
			flags = __raw_save_flags();
			return flags;
		}
		flags = raw_spin_lock_irqsave();
	}
	...
	return flags;

   But the corresponding IR is:

	... %r9(flags) <- ...			// flags = __raw_save_flags();
	...					// cli();
	call.32	%r10(tmp) <- _raw_spin_trylock	// tmp = _raw_spin_try_lock();
	cbr	%r10(tmp), .L1, .L2		// if (tmp)
.L1:	context	1				//   __acquire;
	br	.L3
.L2:	<restore interrupts>			//   __raw_restore_flags(flags);
	br	.L3
.L3:	phisrc.32 %phi1(flags) <- %r9(flags)
	cbr	%r10(tmp), .L4, .L5		// if (!tmp)

   The presence of the phisrc at .L3 blocks the BB merging.
   But this is only part of the problem. First:
   1) The phisrc (and maybe the associated phi-node) could
      be moved/adjusted when merging the BBs
   2) phisrcs are not really needed anyway (they're just a
      mean to track the BB of each phi-node's arguments).

   But even without these phi-sources, some merging that could
   happen doesn't.  For example, code like:
	#define __acquire	__context__(X, 1)
	int  def(void);
	void use(int);
	int foo(int a)
	{
		int tmp = def();
		if (a)
			__acquire;
		else
			use(tmp);
		if (!a) {
			def();
			__acquire;
		}
		use(0);
		__release;
		return tmp;
	}

   produces:
	foo:
	.L0:	call.32     %r1 <- def
		cbr         %arg1, .L1, .L2
	.L1:	context     1
		br          .L3
	.L2:	call        use, %r1
		br          .L3
	.L3:	cbr         %arg1, .L5, .L4	;; false imbalance here
	.L4:	call.32     %r7 <- def
		context     1
		br          .L5
	.L5:	call        use, $0
		context     -1
		ret.32      %r1

   There is no phi-source here and the CBR at .L3 could be merged
   with the one at .L0, removing the false imbalance but it's not.
   I thought that sparse was doing this sort of branch simplification
   but it doesn't, or at least it doesn't in this (simple) situation.

   I'll begin to look at this. I'm not sure about a general solution
   but for a simple case like here (just a 'diamond') it seems simple
   enough to handle.

-- Luc



[Index of Archives]     [Newbies FAQ]     [LKML]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Trinity Fuzzer Tool]

  Powered by Linux