Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1426196 > unrolled thread
| Started by | Byungchul Park <byungchul.park@lge.com> |
|---|---|
| First post | 2016-06-20 07:20 +0200 |
| Last post | 2016-07-06 10:20 +0200 |
| Articles | 20 on this page of 23 — 5 participants |
Back to article view | Back to linux.kernel
[RFC 00/12] lockdep: Implement crossrelease feature Byungchul Park <byungchul.park@lge.com> - 2016-06-20 07:20 +0200
[RFC 08/12] lockdep: Apply crossrelease to PG_locked lock Byungchul Park <byungchul.park@lge.com> - 2016-06-20 07:20 +0200
Re: [RFC 08/12] lockdep: Apply crossrelease to PG_locked lock Peter Zijlstra <peterz@infradead.org> - 2016-06-30 15:10 +0200
Re: [RFC 08/12] lockdep: Apply crossrelease to PG_locked lock Byungchul Park <byungchul.park@lge.com> - 2016-07-01 01:30 +0200
Re: [RFC 08/12] lockdep: Apply crossrelease to PG_locked lock Peter Zijlstra <peterz@infradead.org> - 2016-07-01 10:20 +0200
Re: [RFC 08/12] lockdep: Apply crossrelease to PG_locked lock "Kirill A. Shutemov" <kirill@shutemov.name> - 2016-07-01 13:30 +0200
Re: [RFC 08/12] lockdep: Apply crossrelease to PG_locked lock Byungchul Park <byungchul.park@lge.com> - 2016-07-04 06:40 +0200
[RFC 09/12] cifs/file.c: Remove trailing white space Byungchul Park <byungchul.park@lge.com> - 2016-06-20 07:20 +0200
[RFC 04/12] lockdep: Make save_trace can copy from other stack_trace Byungchul Park <byungchul.park@lge.com> - 2016-06-20 07:20 +0200
[RFC 07/12] pagemap.h: Remove trailing white space Byungchul Park <byungchul.park@lge.com> - 2016-06-20 07:20 +0200
Re: [RFC 00/12] lockdep: Implement crossrelease feature Byungchul Park <byungchul.park@lge.com> - 2016-06-24 01:40 +0200
Re: [RFC 00/12] lockdep: Implement crossrelease feature Peter Zijlstra <peterz@infradead.org> - 2016-06-24 09:10 +0200
Re: [RFC 00/12] lockdep: Implement crossrelease feature Byungchul Park <byungchul.park@lge.com> - 2016-06-24 13:20 +0200
Re: [RFC 00/12] lockdep: Implement crossrelease feature Nikolay Borisov <kernel@kyup.com> - 2016-06-24 13:30 +0200
Re: [RFC 00/12] lockdep: Implement crossrelease feature Byungchul Park <byungchul.park@lge.com> - 2016-06-27 03:40 +0200
[PATCH] lockdep: Add a document describing crossrelease feature Byungchul Park <byungchul.park@lge.com> - 2016-07-01 06:20 +0200
Re: [PATCH] lockdep: Add a document describing crossrelease feature Peter Zijlstra <peterz@infradead.org> - 2016-07-01 12:50 +0200
Re: [PATCH] lockdep: Add a document describing crossrelease feature Byungchul Park <byungchul.park@lge.com> - 2016-07-04 08:50 +0200
Re: [PATCH] lockdep: Add a document describing crossrelease feature Boqun Feng <boqun.feng@gmail.com> - 2016-07-06 02:50 +0200
Re: [PATCH] lockdep: Add a document describing crossrelease feature Byungchul Park <byungchul.park@lge.com> - 2016-07-06 04:20 +0200
Re: [PATCH] lockdep: Add a document describing crossrelease feature Byungchul Park <byungchul.park@lge.com> - 2016-07-06 07:40 +0200
Re: [PATCH] lockdep: Add a document describing crossrelease feature Peter Zijlstra <peterz@infradead.org> - 2016-07-06 10:00 +0200
Re: [PATCH] lockdep: Add a document describing crossrelease feature Byungchul Park <byungchul.park@lge.com> - 2016-07-06 10:20 +0200
Page 1 of 2 [1] 2 Next page →
| From | Byungchul Park <byungchul.park@lge.com> |
|---|---|
| Date | 2016-06-20 07:20 +0200 |
| Subject | [RFC 00/12] lockdep: Implement crossrelease feature |
| Message-ID | <rLZMR-42g-3@gated-at.bofh.it> |
Crossrelease feature calls a lock which is releasable by a different context from the context having acquired the lock, crosslock. For crosslock, all locks having been held in the context unlocking the crosslock, until eventually the crosslock will be unlocked, have dependency with the crosslock. That's a key idea to implement crossrelease feature. Crossrelease feature introduces 2 new data structures. 1. pend_lock (== plock) This is for keeping locks waiting to commit those so that an actual dependency chain is built, when commiting a crosslock. Every task_struct has an array of this pending lock to keep those locks. These pending locks will be added whenever lock_acquire() is called for normal(non-crosslock) lock and will be flushed(committed) at proper time. 2. cross_lock (== xlock) This keeps some additional data only for crosslock. There is one cross_lock per one lockdep_map for crosslock. lockdep_init_map_crosslock() should be used instead of lockdep_init_map() to use the lock as a crosslock. Acquiring and releasing sequence for crossrelease feature: 1. Acquire All validation check is performed for all locks. 1) For non-crosslock (normal lock) The hlock will be added not only to held_locks of the current's task_struct, but also to pend_lock array of the task_struct, so that a dependency chain can be built with the lock when doing commit. 2) For crosslock The hlock will be added only to the cross_lock of the lock's lockdep_map instead of held_locks, so that a dependency chain can be built with the lock when doing commit. And this lock is added to the xlocks_head list. 2. Commit (only for crosslock) This establishes a dependency chain between the lock unlocking it now and all locks having held in the context unlocking it since the lock was held, even though it tries to avoid building a chain unnecessarily as far as possible. 3. Release 1) For non-crosslock (normal lock) No change. 2) For crosslock Just Remove the lock from xlocks_head list. Release operation should be used with commit operation together for crosslock, in order to build a dependency chain properly. Byungchul Park (12): lockdep: Refactor lookup_chain_cache() lockdep: Add a function building a chain between two hlocks lockdep: Make check_prev_add can use a stack_trace of other context lockdep: Make save_trace can copy from other stack_trace lockdep: Implement crossrelease feature lockdep: Apply crossrelease to completion pagemap.h: Remove trailing white space lockdep: Apply crossrelease to PG_locked lock cifs/file.c: Remove trailing white space mm/swap_state.c: Remove trailing white space lockdep: Call lock_acquire(release) when accessing PG_locked manually x86/dumpstack: Optimize save_stack_trace arch/x86/include/asm/stacktrace.h | 1 + arch/x86/kernel/dumpstack.c | 2 + arch/x86/kernel/dumpstack_32.c | 2 + arch/x86/kernel/stacktrace.c | 7 + fs/cifs/file.c | 6 +- include/linux/completion.h | 121 +++++- include/linux/irqflags.h | 16 +- include/linux/lockdep.h | 139 +++++++ include/linux/mm_types.h | 9 + include/linux/pagemap.h | 104 ++++- include/linux/sched.h | 5 + kernel/fork.c | 4 + kernel/locking/lockdep.c | 846 +++++++++++++++++++++++++++++++++++--- kernel/sched/completion.c | 55 +-- lib/Kconfig.debug | 30 ++ mm/filemap.c | 10 +- mm/ksm.c | 1 + mm/migrate.c | 1 + mm/page_alloc.c | 3 + mm/shmem.c | 2 + mm/swap_state.c | 12 +- mm/vmscan.c | 1 + 22 files changed, 1255 insertions(+), 122 deletions(-) -- 1.9.1
[toc] | [next] | [standalone]
| From | Byungchul Park <byungchul.park@lge.com> |
|---|---|
| Date | 2016-06-20 07:20 +0200 |
| Subject | [RFC 08/12] lockdep: Apply crossrelease to PG_locked lock |
| Message-ID | <rLZMS-42g-33@gated-at.bofh.it> |
| In reply to | #1426196 |
lock_page() and its family can cause deadlock. Nevertheless, it cannot
use the lock correctness validator becasue unlock_page() can be called
in different context from the context calling lock_page(), which
violates original lockdep's assumption.
However, thanks to CONFIG_LOCKDEP_CROSSRELEASE, we can apply the lockdep
detector to lock_page() using PG_locked. Applied it.
Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
include/linux/mm_types.h | 9 +++++
include/linux/pagemap.h | 95 +++++++++++++++++++++++++++++++++++++++++++++---
lib/Kconfig.debug | 9 +++++
mm/filemap.c | 4 +-
mm/page_alloc.c | 3 ++
5 files changed, 112 insertions(+), 8 deletions(-)
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 624b78b..ab33ee3 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -15,6 +15,10 @@
#include <asm/page.h>
#include <asm/mmu.h>
+#ifdef CONFIG_LOCKDEP_PAGELOCK
+#include <linux/lockdep.h>
+#endif
+
#ifndef AT_VECTOR_SIZE_ARCH
#define AT_VECTOR_SIZE_ARCH 0
#endif
@@ -215,6 +219,11 @@ struct page {
#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
int _last_cpupid;
#endif
+
+#ifdef CONFIG_LOCKDEP_PAGELOCK
+ struct lockdep_map map;
+ struct cross_lock xlock;
+#endif
}
/*
* The struct page can be forced to be double word aligned so that atomic ops
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index c0049d9..2fc4af1 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -14,6 +14,9 @@
#include <linux/bitops.h>
#include <linux/hardirq.h> /* for in_interrupt() */
#include <linux/hugetlb_inline.h>
+#ifdef CONFIG_LOCKDEP_PAGELOCK
+#include <linux/lockdep.h>
+#endif
/*
* Bits in mapping->flags. The lower __GFP_BITS_SHIFT bits are the page
@@ -441,26 +444,81 @@ static inline pgoff_t linear_page_index(struct vm_area_struct *vma,
return pgoff >> (PAGE_CACHE_SHIFT - PAGE_SHIFT);
}
+#ifdef CONFIG_LOCKDEP_PAGELOCK
+#define lock_page_init(p) \
+do { \
+ static struct lock_class_key __key; \
+ lockdep_init_map_crosslock(&(p)->map, &(p)->xlock, \
+ "(PG_locked)" #p, &__key, 0); \
+} while (0)
+
+static inline void lock_page_acquire(struct page *page, int try)
+{
+ page = compound_head(page);
+ lock_acquire_exclusive(&page->map, 0, try, NULL, _RET_IP_);
+}
+
+static inline void lock_page_release(struct page *page)
+{
+ page = compound_head(page);
+ /*
+ * Calling lock_commit_crosslock() is necessary
+ * for cross-releasable lock when the lock is
+ * releasing before calling lock_release().
+ */
+ lock_commit_crosslock(&page->map);
+ lock_release(&page->map, 0, _RET_IP_);
+}
+#else
+static inline void lock_page_init(struct page *page) {}
+static inline void lock_page_free(struct page *page) {}
+static inline void lock_page_acquire(struct page *page, int try) {}
+static inline void lock_page_release(struct page *page) {}
+#endif
+
extern void __lock_page(struct page *page);
extern int __lock_page_killable(struct page *page);
extern int __lock_page_or_retry(struct page *page, struct mm_struct *mm,
unsigned int flags);
-extern void unlock_page(struct page *page);
+extern void do_raw_unlock_page(struct page *page);
-static inline int trylock_page(struct page *page)
+static inline void unlock_page(struct page *page)
+{
+ lock_page_release(page);
+ do_raw_unlock_page(page);
+}
+
+static inline int do_raw_trylock_page(struct page *page)
{
page = compound_head(page);
return (likely(!test_and_set_bit_lock(PG_locked, &page->flags)));
}
+static inline int trylock_page(struct page *page)
+{
+ if (do_raw_trylock_page(page)) {
+ lock_page_acquire(page, 1);
+ return 1;
+ }
+ return 0;
+}
+
/*
* lock_page may only be called if we have the page's inode pinned.
*/
static inline void lock_page(struct page *page)
{
might_sleep();
- if (!trylock_page(page))
+
+ if (!do_raw_trylock_page(page))
__lock_page(page);
+ /*
+ * The acquire function must be after actual lock operation
+ * for crossrelease lock, because the lock instance is
+ * searched by release operation in any context and more
+ * than two instances acquired make it confused.
+ */
+ lock_page_acquire(page, 0);
}
/*
@@ -470,9 +528,22 @@ static inline void lock_page(struct page *page)
*/
static inline int lock_page_killable(struct page *page)
{
+ int ret;
+
might_sleep();
- if (!trylock_page(page))
- return __lock_page_killable(page);
+
+ if (!do_raw_trylock_page(page)) {
+ ret = __lock_page_killable(page);
+ if (ret)
+ return ret;
+ }
+ /*
+ * The acquire function must be after actual lock operation
+ * for crossrelease lock, because the lock instance is
+ * searched by release operation in any context and more
+ * than two instances acquired make it confused.
+ */
+ lock_page_acquire(page, 0);
return 0;
}
@@ -487,7 +558,19 @@ static inline int lock_page_or_retry(struct page *page, struct mm_struct *mm,
unsigned int flags)
{
might_sleep();
- return trylock_page(page) || __lock_page_or_retry(page, mm, flags);
+
+ if (do_raw_trylock_page(page) || __lock_page_or_retry(page, mm, flags)) {
+ /*
+ * The acquire function must be after actual lock operation
+ * for crossrelease lock, because the lock instance is
+ * searched by release operation in any context and more
+ * than two instances acquired make it confused.
+ */
+ lock_page_acquire(page, 0);
+ return 1;
+ }
+
+ return 0;
}
/*
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index dd314d3..73942b5 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1024,6 +1024,15 @@ config LOCKDEP_COMPLETE
A deadlock caused by wait and complete can be detected by lockdep
using crossrelease feature.
+config LOCKDEP_PAGELOCK
+ bool "Lock debugging: allow PG_locked lock to use deadlock detector"
+ select LOCKDEP_CROSSRELEASE
+ default n
+ help
+ PG_locked lock is a kind of cross-released lock. This makes
+ PG_locked lock possible to use deadlock detector, using
+ crossrelease feature.
+
config PROVE_LOCKING
bool "Lock debugging: prove locking correctness"
depends on DEBUG_KERNEL && TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT
diff --git a/mm/filemap.c b/mm/filemap.c
index 3461d97..47fc5c0 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -814,7 +814,7 @@ EXPORT_SYMBOL_GPL(add_page_wait_queue);
* The mb is necessary to enforce ordering between the clear_bit and the read
* of the waitqueue (to avoid SMP races with a parallel wait_on_page_locked()).
*/
-void unlock_page(struct page *page)
+void do_raw_unlock_page(struct page *page)
{
page = compound_head(page);
VM_BUG_ON_PAGE(!PageLocked(page), page);
@@ -822,7 +822,7 @@ void unlock_page(struct page *page)
smp_mb__after_atomic();
wake_up_page(page, PG_locked);
}
-EXPORT_SYMBOL(unlock_page);
+EXPORT_SYMBOL(do_raw_unlock_page);
/**
* end_page_writeback - end writeback against a page
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 838ca8bb..17ed9a8 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -4538,6 +4538,9 @@ void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone,
} else {
__init_single_pfn(pfn, zone, nid);
}
+#ifdef CONFIG_LOCKDEP_PAGELOCK
+ lock_page_init(pfn_to_page(pfn));
+#endif
}
}
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Peter Zijlstra <peterz@infradead.org> |
|---|---|
| Date | 2016-06-30 15:10 +0200 |
| Subject | Re: [RFC 08/12] lockdep: Apply crossrelease to PG_locked lock |
| Message-ID | <rPJTc-1y9-15@gated-at.bofh.it> |
| In reply to | #1426197 |
On Mon, Jun 20, 2016 at 01:55:23PM +0900, Byungchul Park wrote:
> @@ -215,6 +219,11 @@ struct page {
> #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
> int _last_cpupid;
> #endif
> +
> +#ifdef CONFIG_LOCKDEP_PAGELOCK
> + struct lockdep_map map;
> + struct cross_lock xlock;
> +#endif
> }
So that's 32+64=96 bytes (CONFIG_LOCK_STAT=n) added to struct page,
really!?
[toc] | [prev] | [next] | [standalone]
| From | Byungchul Park <byungchul.park@lge.com> |
|---|---|
| Date | 2016-07-01 01:30 +0200 |
| Subject | Re: [RFC 08/12] lockdep: Apply crossrelease to PG_locked lock |
| Message-ID | <rPTzb-7lm-7@gated-at.bofh.it> |
| In reply to | #1434537 |
On Thu, Jun 30, 2016 at 03:04:58PM +0200, Peter Zijlstra wrote:
> On Mon, Jun 20, 2016 at 01:55:23PM +0900, Byungchul Park wrote:
> > @@ -215,6 +219,11 @@ struct page {
> > #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
> > int _last_cpupid;
> > #endif
> > +
> > +#ifdef CONFIG_LOCKDEP_PAGELOCK
> > + struct lockdep_map map;
> > + struct cross_lock xlock;
> > +#endif
> > }
>
> So that's 32+64=96 bytes (CONFIG_LOCK_STAT=n) added to struct page,
> really!?
Yes... I concerned it at first, but I thought it would be ok since
CONFIG_LOCKDEP_PAGE is a debug feature. Anyway, I will try to reduce
the size of struct cross_lock which is only thing I can do to reduce
it, since we cannot avoid using lockdep_map if we want to make
lock_page() participate in the lockdep play.
[toc] | [prev] | [next] | [standalone]
| From | Peter Zijlstra <peterz@infradead.org> |
|---|---|
| Date | 2016-07-01 10:20 +0200 |
| Subject | Re: [RFC 08/12] lockdep: Apply crossrelease to PG_locked lock |
| Message-ID | <rQ1Q6-4fw-31@gated-at.bofh.it> |
| In reply to | #1434906 |
On Fri, Jul 01, 2016 at 08:21:21AM +0900, Byungchul Park wrote:
> On Thu, Jun 30, 2016 at 03:04:58PM +0200, Peter Zijlstra wrote:
> > On Mon, Jun 20, 2016 at 01:55:23PM +0900, Byungchul Park wrote:
> > > @@ -215,6 +219,11 @@ struct page {
> > > #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
> > > int _last_cpupid;
> > > #endif
> > > +
> > > +#ifdef CONFIG_LOCKDEP_PAGELOCK
> > > + struct lockdep_map map;
> > > + struct cross_lock xlock;
> > > +#endif
> > > }
> >
> > So that's 32+64=96 bytes (CONFIG_LOCK_STAT=n) added to struct page,
> > really!?
>
> Yes... I concerned it at first, but I thought it would be ok since
> CONFIG_LOCKDEP_PAGE is a debug feature.
Right, but still, that's 0.75 GB of memory on my desktop (32GB total)
just for a debug feature. It grows struct page from 1.5% to 3.9% of
total memory, that is immense.
We've avoided doing this for ptl; which was doable because typically
only a small number of pages ends up being a pagetable.
In any case, I feel PG_locked is special enough to fudge. After all, the
content of all these lockdep_map thingies would basically be the same,
which is a massive waste of space.
I still need to bend my brain around this xlock stuff, that just didn't
want to parse when I looked at it last night.
[toc] | [prev] | [next] | [standalone]
| From | "Kirill A. Shutemov" <kirill@shutemov.name> |
|---|---|
| Date | 2016-07-01 13:30 +0200 |
| Subject | Re: [RFC 08/12] lockdep: Apply crossrelease to PG_locked lock |
| Message-ID | <rQ4NX-60Z-1@gated-at.bofh.it> |
| In reply to | #1434906 |
On Fri, Jul 01, 2016 at 08:21:21AM +0900, Byungchul Park wrote:
> On Thu, Jun 30, 2016 at 03:04:58PM +0200, Peter Zijlstra wrote:
> > On Mon, Jun 20, 2016 at 01:55:23PM +0900, Byungchul Park wrote:
> > > @@ -215,6 +219,11 @@ struct page {
> > > #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
> > > int _last_cpupid;
> > > #endif
> > > +
> > > +#ifdef CONFIG_LOCKDEP_PAGELOCK
> > > + struct lockdep_map map;
> > > + struct cross_lock xlock;
> > > +#endif
> > > }
> >
> > So that's 32+64=96 bytes (CONFIG_LOCK_STAT=n) added to struct page,
> > really!?
>
> Yes... I concerned it at first, but I thought it would be ok since
> CONFIG_LOCKDEP_PAGE is a debug feature. Anyway, I will try to reduce
> the size of struct cross_lock which is only thing I can do to reduce
> it, since we cannot avoid using lockdep_map if we want to make
> lock_page() participate in the lockdep play.
Please use page_ext instead. With boottime switch to enable.
This way we can have this compile-time debug option enabled on more
machines without unnecessary runtime overhead.
And, please, CC linux-mm next time.
--
Kirill A. Shutemov
[toc] | [prev] | [next] | [standalone]
| From | Byungchul Park <byungchul.park@lge.com> |
|---|---|
| Date | 2016-07-04 06:40 +0200 |
| Subject | Re: [RFC 08/12] lockdep: Apply crossrelease to PG_locked lock |
| Message-ID | <rR3PP-158-1@gated-at.bofh.it> |
| In reply to | #1435264 |
On Fri, Jul 01, 2016 at 02:18:46PM +0300, Kirill A. Shutemov wrote:
> On Fri, Jul 01, 2016 at 08:21:21AM +0900, Byungchul Park wrote:
> > On Thu, Jun 30, 2016 at 03:04:58PM +0200, Peter Zijlstra wrote:
> > > On Mon, Jun 20, 2016 at 01:55:23PM +0900, Byungchul Park wrote:
> > > > @@ -215,6 +219,11 @@ struct page {
> > > > #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
> > > > int _last_cpupid;
> > > > #endif
> > > > +
> > > > +#ifdef CONFIG_LOCKDEP_PAGELOCK
> > > > + struct lockdep_map map;
> > > > + struct cross_lock xlock;
> > > > +#endif
> > > > }
> > >
> > > So that's 32+64=96 bytes (CONFIG_LOCK_STAT=n) added to struct page,
> > > really!?
> >
> > Yes... I concerned it at first, but I thought it would be ok since
> > CONFIG_LOCKDEP_PAGE is a debug feature. Anyway, I will try to reduce
> > the size of struct cross_lock which is only thing I can do to reduce
> > it, since we cannot avoid using lockdep_map if we want to make
> > lock_page() participate in the lockdep play.
>
> Please use page_ext instead. With boottime switch to enable.
>
> This way we can have this compile-time debug option enabled on more
> machines without unnecessary runtime overhead.
Thank you for advice.
I also think it's one of good candidates except the fact that it have to
depend on page_ext additionally.
>
> And, please, CC linux-mm next time.
>
> --
> Kirill A. Shutemov
[toc] | [prev] | [next] | [standalone]
| From | Byungchul Park <byungchul.park@lge.com> |
|---|---|
| Date | 2016-06-20 07:20 +0200 |
| Subject | [RFC 09/12] cifs/file.c: Remove trailing white space |
| Message-ID | <rLZMS-42g-31@gated-at.bofh.it> |
| In reply to | #1426196 |
Trailing white space is not accepted in kernel coding style. Remove them. Signed-off-by: Byungchul Park <byungchul.park@lge.com> --- fs/cifs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index ff882ae..bcf9ead 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -3851,7 +3851,7 @@ void cifs_oplock_break(struct work_struct *work) * In the non-cached mode (mount with cache=none), we shunt off direct read and write requests * so this method should never be called. * - * Direct IO is not yet supported in the cached mode. + * Direct IO is not yet supported in the cached mode. */ static ssize_t cifs_direct_io(struct kiocb *iocb, struct iov_iter *iter, loff_t pos) -- 1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Byungchul Park <byungchul.park@lge.com> |
|---|---|
| Date | 2016-06-20 07:20 +0200 |
| Subject | [RFC 04/12] lockdep: Make save_trace can copy from other stack_trace |
| Message-ID | <rLZMS-42g-39@gated-at.bofh.it> |
| In reply to | #1426196 |
Currently, save_trace() can only save current context's stack trace.
However, it would be useful if it can save(copy from) another context's
stack trace. Especially, it can be used by crossrelease feature.
Signed-off-by: Byungchul Park <byungchul.park@lge.com>
---
kernel/locking/lockdep.c | 22 ++++++++++++++--------
1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index c596bef..b03014b 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -389,7 +389,7 @@ static void print_lockdep_off(const char *bug_msg)
#endif
}
-static int save_trace(struct stack_trace *trace)
+static int save_trace(struct stack_trace *trace, struct stack_trace *copy)
{
trace->nr_entries = 0;
trace->max_entries = MAX_STACK_TRACE_ENTRIES - nr_stack_trace_entries;
@@ -397,7 +397,13 @@ static int save_trace(struct stack_trace *trace)
trace->skip = 3;
- save_stack_trace(trace);
+ if (copy) {
+ trace->nr_entries = min(copy->nr_entries, trace->max_entries);
+ trace->skip = copy->skip;
+ memcpy(trace->entries, copy->entries,
+ trace->nr_entries * sizeof(unsigned long));
+ } else
+ save_stack_trace(trace);
/*
* Some daft arches put -1 at the end to indicate its a full trace.
@@ -1201,7 +1207,7 @@ static noinline int print_circular_bug(struct lock_list *this,
if (!debug_locks_off_graph_unlock() || debug_locks_silent)
return 0;
- if (!save_trace(&this->trace))
+ if (!save_trace(&this->trace, NULL))
return 0;
depth = get_lock_depth(target);
@@ -1547,13 +1553,13 @@ print_bad_irq_dependency(struct task_struct *curr,
printk("\nthe dependencies between %s-irq-safe lock", irqclass);
printk(" and the holding lock:\n");
- if (!save_trace(&prev_root->trace))
+ if (!save_trace(&prev_root->trace, NULL))
return 0;
print_shortest_lock_dependencies(backwards_entry, prev_root);
printk("\nthe dependencies between the lock to be acquired");
printk(" and %s-irq-unsafe lock:\n", irqclass);
- if (!save_trace(&next_root->trace))
+ if (!save_trace(&next_root->trace, NULL))
return 0;
print_shortest_lock_dependencies(forwards_entry, next_root);
@@ -1885,7 +1891,7 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev,
}
if (!own_trace && stack_saved && !*stack_saved) {
- if (!save_trace(&trace))
+ if (!save_trace(&trace, NULL))
return 0;
*stack_saved = 1;
}
@@ -2436,7 +2442,7 @@ print_irq_inversion_bug(struct task_struct *curr,
lockdep_print_held_locks(curr);
printk("\nthe shortest dependencies between 2nd lock and 1st lock:\n");
- if (!save_trace(&root->trace))
+ if (!save_trace(&root->trace, NULL))
return 0;
print_shortest_lock_dependencies(other, root);
@@ -3015,7 +3021,7 @@ static int mark_lock(struct task_struct *curr, struct held_lock *this,
hlock_class(this)->usage_mask |= new_mask;
- if (!save_trace(hlock_class(this)->usage_traces + new_bit))
+ if (!save_trace(hlock_class(this)->usage_traces + new_bit, NULL))
return 0;
switch (new_bit) {
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Byungchul Park <byungchul.park@lge.com> |
|---|---|
| Date | 2016-06-20 07:20 +0200 |
| Subject | [RFC 07/12] pagemap.h: Remove trailing white space |
| Message-ID | <rLZMS-42g-41@gated-at.bofh.it> |
| In reply to | #1426196 |
Trailing white space is not accepted in kernel coding style. Remove them. Signed-off-by: Byungchul Park <byungchul.park@lge.com> --- include/linux/pagemap.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 92395a0..c0049d9 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -513,7 +513,7 @@ static inline void wake_up_page(struct page *page, int bit) __wake_up_bit(page_waitqueue(page), &page->flags, bit); } -/* +/* * Wait for a page to be unlocked. * * This must be called with the caller "holding" the page, @@ -526,7 +526,7 @@ static inline void wait_on_page_locked(struct page *page) wait_on_page_bit(compound_head(page), PG_locked); } -/* +/* * Wait for a page to complete writeback */ static inline void wait_on_page_writeback(struct page *page) -- 1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Byungchul Park <byungchul.park@lge.com> |
|---|---|
| Date | 2016-06-24 01:40 +0200 |
| Message-ID | <rNmo1-it-5@gated-at.bofh.it> |
| In reply to | #1426196 |
On Mon, Jun 20, 2016 at 01:55:15PM +0900, Byungchul Park wrote: Hello, I have a plan to resend this patchset after reinforcement of documentation. However I am wondering what you think about the main concept of this. A main motivation is to be able to detect several problems which I describes with examples below. ex.1) PROCESS X PROCESS Y --------- --------- mutext_lock A lock_page B lock_page B mutext_lock A // DEADLOCK unlock_page B mutext_unlock A mutex_unlock A unlock_page B ex.2) PROCESS X PROCESS Y PROCESS Z --------- --------- --------- lock_page B mutex_lock A lock_page B mutext_lock A // DEADLOCK mutext_unlock A unlock_page B mutex_unlock A ex.3) PROCESS X PROCESS Y --------- --------- mutex_lock A mutex_lock A mutex_unlock A wait_for_complete B // DEADLOCK complete B mutex_unlock A and so on... Whatever lockdep can detect can be detected by my implementation except AA deadlock in a context, which is of course not a deadlock by nature, for locks releasable by difference context. Fortunately, current kernel code is robust enough not to be detected on my machine, I am sure this can be a good navigator to developers. Thank you. Byungchul > Crossrelease feature calls a lock which is releasable by a > different context from the context having acquired the lock, > crosslock. For crosslock, all locks having been held in the > context unlocking the crosslock, until eventually the crosslock > will be unlocked, have dependency with the crosslock. That's a > key idea to implement crossrelease feature. > > Crossrelease feature introduces 2 new data structures. > > 1. pend_lock (== plock) > > This is for keeping locks waiting to commit those so > that an actual dependency chain is built, when commiting > a crosslock. > > Every task_struct has an array of this pending lock to > keep those locks. These pending locks will be added > whenever lock_acquire() is called for normal(non-crosslock) > lock and will be flushed(committed) at proper time. > > 2. cross_lock (== xlock) > > This keeps some additional data only for crosslock. There > is one cross_lock per one lockdep_map for crosslock. > lockdep_init_map_crosslock() should be used instead of > lockdep_init_map() to use the lock as a crosslock. > > Acquiring and releasing sequence for crossrelease feature: > > 1. Acquire > > All validation check is performed for all locks. > > 1) For non-crosslock (normal lock) > > The hlock will be added not only to held_locks > of the current's task_struct, but also to > pend_lock array of the task_struct, so that > a dependency chain can be built with the lock > when doing commit. > > 2) For crosslock > > The hlock will be added only to the cross_lock > of the lock's lockdep_map instead of held_locks, > so that a dependency chain can be built with > the lock when doing commit. And this lock is > added to the xlocks_head list. > > 2. Commit (only for crosslock) > > This establishes a dependency chain between the lock > unlocking it now and all locks having held in the context > unlocking it since the lock was held, even though it tries > to avoid building a chain unnecessarily as far as possible. > > 3. Release > > 1) For non-crosslock (normal lock) > > No change. > > 2) For crosslock > > Just Remove the lock from xlocks_head list. Release > operation should be used with commit operation > together for crosslock, in order to build a > dependency chain properly. > > Byungchul Park (12): > lockdep: Refactor lookup_chain_cache() > lockdep: Add a function building a chain between two hlocks > lockdep: Make check_prev_add can use a stack_trace of other context > lockdep: Make save_trace can copy from other stack_trace > lockdep: Implement crossrelease feature > lockdep: Apply crossrelease to completion > pagemap.h: Remove trailing white space > lockdep: Apply crossrelease to PG_locked lock > cifs/file.c: Remove trailing white space > mm/swap_state.c: Remove trailing white space > lockdep: Call lock_acquire(release) when accessing PG_locked manually > x86/dumpstack: Optimize save_stack_trace > > arch/x86/include/asm/stacktrace.h | 1 + > arch/x86/kernel/dumpstack.c | 2 + > arch/x86/kernel/dumpstack_32.c | 2 + > arch/x86/kernel/stacktrace.c | 7 + > fs/cifs/file.c | 6 +- > include/linux/completion.h | 121 +++++- > include/linux/irqflags.h | 16 +- > include/linux/lockdep.h | 139 +++++++ > include/linux/mm_types.h | 9 + > include/linux/pagemap.h | 104 ++++- > include/linux/sched.h | 5 + > kernel/fork.c | 4 + > kernel/locking/lockdep.c | 846 +++++++++++++++++++++++++++++++++++--- > kernel/sched/completion.c | 55 +-- > lib/Kconfig.debug | 30 ++ > mm/filemap.c | 10 +- > mm/ksm.c | 1 + > mm/migrate.c | 1 + > mm/page_alloc.c | 3 + > mm/shmem.c | 2 + > mm/swap_state.c | 12 +- > mm/vmscan.c | 1 + > 22 files changed, 1255 insertions(+), 122 deletions(-) > > -- > 1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Peter Zijlstra <peterz@infradead.org> |
|---|---|
| Date | 2016-06-24 09:10 +0200 |
| Message-ID | <rNtpw-4ZY-7@gated-at.bofh.it> |
| In reply to | #1430244 |
On Fri, Jun 24, 2016 at 08:37:13AM +0900, Byungchul Park wrote: > On Mon, Jun 20, 2016 at 01:55:15PM +0900, Byungchul Park wrote: > > Hello, > > I have a plan to resend this patchset after reinforcement of > documentation. However I am wondering what you think about the > main concept of this. I have not had time to look at this at all..
[toc] | [prev] | [next] | [standalone]
| From | Byungchul Park <byungchul.park@lge.com> |
|---|---|
| Date | 2016-06-24 13:20 +0200 |
| Message-ID | <rNxjr-7kU-1@gated-at.bofh.it> |
| In reply to | #1430410 |
On Fri, Jun 24, 2016 at 09:08:44AM +0200, Peter Zijlstra wrote: > On Fri, Jun 24, 2016 at 08:37:13AM +0900, Byungchul Park wrote: > > On Mon, Jun 20, 2016 at 01:55:15PM +0900, Byungchul Park wrote: > > > > Hello, > > > > I have a plan to resend this patchset after reinforcement of > > documentation. However I am wondering what you think about the > > main concept of this. > > I have not had time to look at this at all.. I can wait until you become available. Thank you for letting me know that.
[toc] | [prev] | [next] | [standalone]
| From | Nikolay Borisov <kernel@kyup.com> |
|---|---|
| Date | 2016-06-24 13:30 +0200 |
| Message-ID | <rNxt8-7os-29@gated-at.bofh.it> |
| In reply to | #1430244 |
On 06/24/2016 02:37 AM, Byungchul Park wrote: > On Mon, Jun 20, 2016 at 01:55:15PM +0900, Byungchul Park wrote: > > Hello, > > I have a plan to resend this patchset after reinforcement of > documentation. However I am wondering what you think about the > main concept of this. A main motivation is to be able to detect > several problems which I describes with examples below. > > ex.1) > > PROCESS X PROCESS Y > --------- --------- > mutext_lock A > lock_page B > lock_page B > mutext_lock A // DEADLOCK > unlock_page B > mutext_unlock A > mutex_unlock A > unlock_page B > > ex.2) > > PROCESS X PROCESS Y PROCESS Z > --------- --------- --------- > lock_page B mutex_lock A > > lock_page B > mutext_lock A // DEADLOCK > mutext_unlock A > unlock_page B > mutex_unlock A Am I correct in assuming that in ex2 PROCESS Z holds page B lock? If so can you make it a bit more explicit if this is going to go into the documentation, that is. > > ex.3) > > PROCESS X PROCESS Y > --------- --------- > mutex_lock A > mutex_lock A > mutex_unlock A wait_for_complete B // DEADLOCK > > complete B > mutex_unlock A > > and so on... > > Whatever lockdep can detect can be detected by my implementation > except AA deadlock in a context, which is of course not a deadlock > by nature, for locks releasable by difference context. Fortunately, > current kernel code is robust enough not to be detected on my machine, > I am sure this can be a good navigator to developers. > > Thank you. > Byungchul > >> Crossrelease feature calls a lock which is releasable by a >> different context from the context having acquired the lock, >> crosslock. For crosslock, all locks having been held in the >> context unlocking the crosslock, until eventually the crosslock >> will be unlocked, have dependency with the crosslock. That's a >> key idea to implement crossrelease feature. >> >> Crossrelease feature introduces 2 new data structures. >> >> 1. pend_lock (== plock) >> >> This is for keeping locks waiting to commit those so >> that an actual dependency chain is built, when commiting >> a crosslock. >> >> Every task_struct has an array of this pending lock to >> keep those locks. These pending locks will be added >> whenever lock_acquire() is called for normal(non-crosslock) >> lock and will be flushed(committed) at proper time. >> >> 2. cross_lock (== xlock) >> >> This keeps some additional data only for crosslock. There >> is one cross_lock per one lockdep_map for crosslock. >> lockdep_init_map_crosslock() should be used instead of >> lockdep_init_map() to use the lock as a crosslock. >> >> Acquiring and releasing sequence for crossrelease feature: >> >> 1. Acquire >> >> All validation check is performed for all locks. >> >> 1) For non-crosslock (normal lock) >> >> The hlock will be added not only to held_locks >> of the current's task_struct, but also to >> pend_lock array of the task_struct, so that >> a dependency chain can be built with the lock >> when doing commit. >> >> 2) For crosslock >> >> The hlock will be added only to the cross_lock >> of the lock's lockdep_map instead of held_locks, >> so that a dependency chain can be built with >> the lock when doing commit. And this lock is >> added to the xlocks_head list. >> >> 2. Commit (only for crosslock) >> >> This establishes a dependency chain between the lock >> unlocking it now and all locks having held in the context >> unlocking it since the lock was held, even though it tries >> to avoid building a chain unnecessarily as far as possible. >> >> 3. Release >> >> 1) For non-crosslock (normal lock) >> >> No change. >> >> 2) For crosslock >> >> Just Remove the lock from xlocks_head list. Release >> operation should be used with commit operation >> together for crosslock, in order to build a >> dependency chain properly. >> >> Byungchul Park (12): >> lockdep: Refactor lookup_chain_cache() >> lockdep: Add a function building a chain between two hlocks >> lockdep: Make check_prev_add can use a stack_trace of other context >> lockdep: Make save_trace can copy from other stack_trace >> lockdep: Implement crossrelease feature >> lockdep: Apply crossrelease to completion >> pagemap.h: Remove trailing white space >> lockdep: Apply crossrelease to PG_locked lock >> cifs/file.c: Remove trailing white space >> mm/swap_state.c: Remove trailing white space >> lockdep: Call lock_acquire(release) when accessing PG_locked manually >> x86/dumpstack: Optimize save_stack_trace >> >> arch/x86/include/asm/stacktrace.h | 1 + >> arch/x86/kernel/dumpstack.c | 2 + >> arch/x86/kernel/dumpstack_32.c | 2 + >> arch/x86/kernel/stacktrace.c | 7 + >> fs/cifs/file.c | 6 +- >> include/linux/completion.h | 121 +++++- >> include/linux/irqflags.h | 16 +- >> include/linux/lockdep.h | 139 +++++++ >> include/linux/mm_types.h | 9 + >> include/linux/pagemap.h | 104 ++++- >> include/linux/sched.h | 5 + >> kernel/fork.c | 4 + >> kernel/locking/lockdep.c | 846 +++++++++++++++++++++++++++++++++++--- >> kernel/sched/completion.c | 55 +-- >> lib/Kconfig.debug | 30 ++ >> mm/filemap.c | 10 +- >> mm/ksm.c | 1 + >> mm/migrate.c | 1 + >> mm/page_alloc.c | 3 + >> mm/shmem.c | 2 + >> mm/swap_state.c | 12 +- >> mm/vmscan.c | 1 + >> 22 files changed, 1255 insertions(+), 122 deletions(-) >> >> -- >> 1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Byungchul Park <byungchul.park@lge.com> |
|---|---|
| Date | 2016-06-27 03:40 +0200 |
| Message-ID | <rOtGN-1ZG-9@gated-at.bofh.it> |
| In reply to | #1430589 |
On Fri, Jun 24, 2016 at 02:26:45PM +0300, Nikolay Borisov wrote: > > > On 06/24/2016 02:37 AM, Byungchul Park wrote: > > On Mon, Jun 20, 2016 at 01:55:15PM +0900, Byungchul Park wrote: > > > > Hello, > > > > I have a plan to resend this patchset after reinforcement of > > documentation. However I am wondering what you think about the > > main concept of this. A main motivation is to be able to detect > > several problems which I describes with examples below. > > > > ex.1) > > > > PROCESS X PROCESS Y > > --------- --------- > > mutext_lock A > > lock_page B > > lock_page B > > mutext_lock A // DEADLOCK > > unlock_page B > > mutext_unlock A > > mutex_unlock A > > unlock_page B > > > > ex.2) > > > > PROCESS X PROCESS Y PROCESS Z > > --------- --------- --------- > > lock_page B mutex_lock A > > > > lock_page B > > mutext_lock A // DEADLOCK > > mutext_unlock A > > unlock_page B > > mutex_unlock A > > Am I correct in assuming that in ex2 PROCESS Z holds page B lock? If so In this example, PROCESS Z does not hold page B lock. The page B lock being unlocked by PROCESS Z was held by PROCESS X. > can you make it a bit more explicit if this is going to go into the > documentation, that is. > > > > > ex.3) > > > > PROCESS X PROCESS Y > > --------- --------- > > mutex_lock A > > mutex_lock A > > mutex_unlock A wait_for_complete B // DEADLOCK > > > > complete B > > mutex_unlock A > > > > and so on... > > > > Whatever lockdep can detect can be detected by my implementation > > except AA deadlock in a context, which is of course not a deadlock > > by nature, for locks releasable by difference context. Fortunately, > > current kernel code is robust enough not to be detected on my machine, > > I am sure this can be a good navigator to developers. > > > > Thank you. > > Byungchul > > > >> Crossrelease feature calls a lock which is releasable by a > >> different context from the context having acquired the lock, > >> crosslock. For crosslock, all locks having been held in the > >> context unlocking the crosslock, until eventually the crosslock > >> will be unlocked, have dependency with the crosslock. That's a > >> key idea to implement crossrelease feature. > >> > >> Crossrelease feature introduces 2 new data structures. > >> > >> 1. pend_lock (== plock) > >> > >> This is for keeping locks waiting to commit those so > >> that an actual dependency chain is built, when commiting > >> a crosslock. > >> > >> Every task_struct has an array of this pending lock to > >> keep those locks. These pending locks will be added > >> whenever lock_acquire() is called for normal(non-crosslock) > >> lock and will be flushed(committed) at proper time. > >> > >> 2. cross_lock (== xlock) > >> > >> This keeps some additional data only for crosslock. There > >> is one cross_lock per one lockdep_map for crosslock. > >> lockdep_init_map_crosslock() should be used instead of > >> lockdep_init_map() to use the lock as a crosslock. > >> > >> Acquiring and releasing sequence for crossrelease feature: > >> > >> 1. Acquire > >> > >> All validation check is performed for all locks. > >> > >> 1) For non-crosslock (normal lock) > >> > >> The hlock will be added not only to held_locks > >> of the current's task_struct, but also to > >> pend_lock array of the task_struct, so that > >> a dependency chain can be built with the lock > >> when doing commit. > >> > >> 2) For crosslock > >> > >> The hlock will be added only to the cross_lock > >> of the lock's lockdep_map instead of held_locks, > >> so that a dependency chain can be built with > >> the lock when doing commit. And this lock is > >> added to the xlocks_head list. > >> > >> 2. Commit (only for crosslock) > >> > >> This establishes a dependency chain between the lock > >> unlocking it now and all locks having held in the context > >> unlocking it since the lock was held, even though it tries > >> to avoid building a chain unnecessarily as far as possible. > >> > >> 3. Release > >> > >> 1) For non-crosslock (normal lock) > >> > >> No change. > >> > >> 2) For crosslock > >> > >> Just Remove the lock from xlocks_head list. Release > >> operation should be used with commit operation > >> together for crosslock, in order to build a > >> dependency chain properly. > >> > >> Byungchul Park (12): > >> lockdep: Refactor lookup_chain_cache() > >> lockdep: Add a function building a chain between two hlocks > >> lockdep: Make check_prev_add can use a stack_trace of other context > >> lockdep: Make save_trace can copy from other stack_trace > >> lockdep: Implement crossrelease feature > >> lockdep: Apply crossrelease to completion > >> pagemap.h: Remove trailing white space > >> lockdep: Apply crossrelease to PG_locked lock > >> cifs/file.c: Remove trailing white space > >> mm/swap_state.c: Remove trailing white space > >> lockdep: Call lock_acquire(release) when accessing PG_locked manually > >> x86/dumpstack: Optimize save_stack_trace > >> > >> arch/x86/include/asm/stacktrace.h | 1 + > >> arch/x86/kernel/dumpstack.c | 2 + > >> arch/x86/kernel/dumpstack_32.c | 2 + > >> arch/x86/kernel/stacktrace.c | 7 + > >> fs/cifs/file.c | 6 +- > >> include/linux/completion.h | 121 +++++- > >> include/linux/irqflags.h | 16 +- > >> include/linux/lockdep.h | 139 +++++++ > >> include/linux/mm_types.h | 9 + > >> include/linux/pagemap.h | 104 ++++- > >> include/linux/sched.h | 5 + > >> kernel/fork.c | 4 + > >> kernel/locking/lockdep.c | 846 +++++++++++++++++++++++++++++++++++--- > >> kernel/sched/completion.c | 55 +-- > >> lib/Kconfig.debug | 30 ++ > >> mm/filemap.c | 10 +- > >> mm/ksm.c | 1 + > >> mm/migrate.c | 1 + > >> mm/page_alloc.c | 3 + > >> mm/shmem.c | 2 + > >> mm/swap_state.c | 12 +- > >> mm/vmscan.c | 1 + > >> 22 files changed, 1255 insertions(+), 122 deletions(-) > >> > >> -- > >> 1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Byungchul Park <byungchul.park@lge.com> |
|---|---|
| Date | 2016-07-01 06:20 +0200 |
| Subject | [PATCH] lockdep: Add a document describing crossrelease feature |
| Message-ID | <rPY5P-1V8-1@gated-at.bofh.it> |
| In reply to | #1426196 |
Crossrelease feature introduces new concept and data structure. Thus a document helping understand it is necessary. So added it. Signed-off-by: Byungchul Park <byungchul.park@lge.com> --- Documentation/locking/crossrelease.txt | 276 +++++++++++++++++++++++++++++++++ 1 file changed, 276 insertions(+) create mode 100644 Documentation/locking/crossrelease.txt diff --git a/Documentation/locking/crossrelease.txt b/Documentation/locking/crossrelease.txt new file mode 100644 index 0000000..98851ef --- /dev/null +++ b/Documentation/locking/crossrelease.txt @@ -0,0 +1,276 @@ +Crossrelease lock dependency check +================================== + +Started by Byungchul Park <byungchul.park@lge.com> + +Contents: + + (*) What is a problem? + + - Original lockdep's assumptions. + - Original lockdep's limitation. + + (*) How to solve the problem. + + - What causes deadlock? + - Relax the assumptions. + - Introduce "crosslock". + - Introduce "commit" stage. + - Acquire vs commit vs release + + (*) Implementation. + + - Data structures. + - Optimizations. + + +================= +What is a problem +================= + +Can we detect deadlocks descriped below with original lockdep? +No. + +Example 1) + + PROCESS X PROCESS Y + -------------- -------------- + mutext_lock A + lock_page B + lock_page B + mutext_lock A // DEADLOCK + unlock_page B + mutext_unlock A + mutex_unlock A + unlock_page B + +We are currently not checking lock dependency for lock_page(), which is +for exclusive access to pages. + +Example 2) + + PROCESS X PROCESS Y PROCESS Z + -------------- -------------- -------------- + mutex_lock A + lock_page B + lock_page B + mutext_lock A // DEADLOCK + mutext_unlock A + unlock_page B + (B was held by PROCESS X) + unlock_page B + mutex_unlock A + +We cannot detect this kind of deadlock with original lockdep, even +though we enable lock dependency check on lock_page(). + +Example 3) + + PROCESS X PROCESS Y + -------------- -------------- + mutex_lock A + mutex_lock A + mutex_unlock A + wait_for_complete B // DEADLOCK + complete B + mutex_unlock A + +wait_for_complete() and complete() also can cause a deadlock, however +we cannot detect it with original lockdep, either. + + +Original lockdep's assumptions +------------------------------ + +Original lockdep (not crossrelease featured lockdep) assumes that, + +1. A lock will be unlocked within the context holding the lock. +2. A lock has dependency with all locks already held in held_locks. +2. Acquiring is more important than releasing, to check its dependency. + + +Original lockdep's limitation +----------------------------- + +Therefore, the original lockdep has limitations. It can be applied only +to typical lock operations, e.g. spin_lock, mutex, semaphore and the +like. Even though lock_page() can be considered as a lock, it cannot be +used with lockdep because it violates assumptions of original lockdep. +In the view point of original lockdep, a lock must be released within +the context having held the lock, however, a lock using lock_page() can +be released by different context from the context having held the lock. +wait_for_complete() is also the case by nature, in which original +lockdep cannot deal with it. + + +======================== +How to solve the problem +======================== + +What causes deadlock +-------------------- + +Not only lock operations, but also any operations causing to wait or +spin it e.g. all wait operations for an event, lock_page() and so on +can cause deadlock unless it's eventually released by someone. The most +important point here is that the waiting or spinning must be *released* +by someone. In other words, we have to focus whether the waiting and +spinning can be *released* or not to avoid deadlock, rather than +waiting or spinning it itself. + + +Relax the assumptions +--------------------- + +We can relax the assumtions the original lockdep has, which is not +necessary to check dependency and detect a deadlock. + +1. A lock can be unlocked in any context, unless the context itself + causes a deadlock e.g. acquiring a lock in irq-safe context before + releasing the lock in irq-unsafe context. + +2. A lock has dependency with all locks in the releasing context, having + been held since the lock was held. Thus we can check the dependency + only after we identify the releasing context at first. Of course, + if we consider only typical lock e.g. spin lock, mutex, semaphore + and so on, then we can identify the releasing context at the time + acquiring a lock because the releasing context is same as the + releasing context for the typical lock. However, generally we have to + wait until the lock having been held will be eventually released to + identify the releasing context. We can say that the original lockdep + is a special case among all cases this crossrelease feature can deal + with. + +3. Releasing is more important than acquiring to check its dependency. + Compare to the third assumption of original lockdep. + + +Introduce "crosslock" +--------------------- + +Crossrelease feature names a lock "crosslock" if it is releasable by a +different context from the context having acquired the lock. All locks +having been held in the context unlocking the crosslock until +eventually the crosslock will be unlocked, have dependency with the +crosslock. That's the key idea to implement crossrelease feature. + + +Introduce "commit" stage +------------------------ + +Crossrelease feature names it "commit", to check dependency and build +the dependency tree and chain. That is, the original lockdep is already +doing the so-called commit, when acquiring it. In the strict sense, the +checking and building must be done in the releasing context, as +described in the "What causes a deadlock" subsection above. However, it +will work no matter which context is used for typical lock, since it's +guarrented that the acquiring context is same as the releasing context +as described above. So we can commit it in the acquiring context for +typical lock. + +How the original lockdep works: + + acquire (including commit operation) -> release + +What if we consider a crosslock? For crosslock, the way lockdep works +must be changed so that the releasing context is considered instead. +Again, the releasing context is more important than the acquiring +context, to check dependency and detect a deadlock. Thus checking +dependency and building the dependency tree and chain, namely commit +must be done in the releasing context, especially for crosslock. + +How the crossrelease lockdep works for crosslock: + + acquire -> (context may be changed) -> commit -> release + + +Acquire vs commit vs release +---------------------------- + +The things to do when acquiring and releasing a lock will be slightly +changed in other to make lockdep can work even for crosslock. And an +additional stage, commit, is placed between acquire and release. + +1. Acquire + + 1) For typical lock + + The lock will be added not only to held_locks of the + current's task_struct, but also to additional structure + so that the commit stage can check dependency and build + the dependency tree and chain with that later. + + 2) For crosslock + + The lock will be added to a global linked list so that + the commit stage can check dependency and build the + dependency tree and chain with that later. + +2. Commit + + 1) For typical lock + + N/A. + + 2) For crosslock + + It checks dependency and builds the dependency tree and + chain with data saved in the acquire stage. Here, we + establish dependency between the crosslock we are + unlocking now and all locks in the context unlocking it, + having been held since the lock was held. Of course, + it avoids unnecessary checking and building as far as + possible. + +3. Release + + 1) For typical lock + + No change. + + 2) For crosslock + + Just Remove the target crosslock from the global linked + list, to which the crosslock was added at acquire stage. + Release operation should be used with commit operation + together for crosslock, in order to build a dependency + chain properly. + + +============== +Implementation +============== + +Data structures +--------------- + +Crossrelease feature introduces two new data structures. + +1. pend_lock (== plock) + + This is for keeping locks waiting to be committed so that the + actual dependency tree and chain is built in the commit stage. + Every task_struct has an pend_lock array to keep those locks. + pend_lock entry will be consumed and filled whenever + lock_acquire() is called for typical lock and will be flushed, + namely committed at proper time. + +2. cross_lock (== xlock) + + This keeps some additional data only for crosslock. One + cross_lock exists per one lockdep_map. + lockdep_init_map_crosslock() should be used instead of + lockdep_init_map() to use a lock as a crosslock. + + +Optimizations +------------- + +Adding a pend_lock is an operation very frequently happened because it +happens whenever a typical lock is acquired. So the operation is +implemented locklessly using rcu mechanism unless the xlock instance +can be freed or destroyed unpredictably e.g. the instance is on stack. + +And chain cache for crosslock is also used to avoid unnecessary checking +and building dependency, like how the original lockdep is doing for that +purpose. -- 1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Peter Zijlstra <peterz@infradead.org> |
|---|---|
| Date | 2016-07-01 12:50 +0200 |
| Subject | Re: [PATCH] lockdep: Add a document describing crossrelease feature |
| Message-ID | <rQ4bf-5yT-3@gated-at.bofh.it> |
| In reply to | #1435002 |
So I really could not understand your initial changelogs, this text seem to be somewhat better, so let me try and comment on this. On Fri, Jul 01, 2016 at 01:15:38PM +0900, Byungchul Park wrote: > +++ b/Documentation/locking/crossrelease.txt > @@ -0,0 +1,276 @@ > +Crossrelease lock dependency check > +================================== > + > +Started by Byungchul Park <byungchul.park@lge.com> > + > +Contents: > + > + (*) What is a problem? > + > + - Original lockdep's assumptions. > + - Original lockdep's limitation. Their form doesn't make sense if we ever commit this. Nobody knows or cares about an 'original' lockdep. There is only now. > +Original lockdep's assumptions > +------------------------------ > + > +Original lockdep (not crossrelease featured lockdep) assumes that, > + > +1. A lock will be unlocked within the context holding the lock. This is lock owner semantics; that is, each lock has a clear owner. Which is a sane assumption, and a hard requirement for PI. Remember, all this comes from the RT tree. !owner locks cannot do PI and thus cannot be used for code you want to provide deterministic behaviour with. > +2. A lock has dependency with all locks already held in held_locks. That's not really an assumption, given 1, this is a fact. An owner lock can only depend on locks currently held. This is a corner stone of proving things. > +2. Acquiring is more important than releasing, to check its dependency. s/2/3/ That's not an assumption; that's a hard requirement. Since the acquire is the one blocking, you _have_ to check for cycles before you block, otherwise you'll hit the deadlock and not get a report, because you're deadlocked. > +Original lockdep's limitation > +----------------------------- > + > +Therefore, the original lockdep has limitations. It can be applied only > +to typical lock operations, e.g. spin_lock, mutex, semaphore and the This is wrong, semaphores are very much not covered by lockdep since they do not have owner semantics (what you call crossmuck). (this is the distinction between a binary semaphore and a mutex) > +What causes deadlock > +-------------------- > + > +Not only lock operations, but also any operations causing to wait or > +spin it e.g. all wait operations for an event, lock_page() and so on > +can cause deadlock unless it's eventually released by someone. The most > +important point here is that the waiting or spinning must be *released* > +by someone. In other words, we have to focus whether the waiting and > +spinning can be *released* or not to avoid deadlock, rather than > +waiting or spinning it itself. But since its the blocking that _is_ the deadlock, you'll never get your report. IOW, you rely on future behaviour to tell if now can make forwards progress. This already implies a well formed program. You're inverting causality. > +Relax the assumptions > +--------------------- > + > +We can relax the assumtions the original lockdep has, which is not > +necessary to check dependency and detect a deadlock. > + > +1. A lock can be unlocked in any context, unless the context itself > + causes a deadlock e.g. acquiring a lock in irq-safe context before > + releasing the lock in irq-unsafe context. You fail to say how this preserves correctness. By relaxing this you loose the held_lock dependencies and you destroy the entire proof that currently underpins lockdep. > +2. A lock has dependency with all locks in the releasing context, having > + been held since the lock was held. But you cannot tell this. The 'since the lock was held' thing fully depends on timing and is not fundamentally correct. lock(A) unlock(A) lock(A) wait_for(B) unlock(A) wake(B) Between the wait_for(B) and wake(B), _nothing_ has been held, yet still there's the deadlock potential. And note that if the timing was 'right', you would never get to wake(B) because deadlock, so you'd never establish that there would be a deadlock. > Thus we can check the dependency > + only after we identify the releasing context at first. Of course, > + if we consider only typical lock e.g. spin lock, mutex, semaphore > + and so on, then we can identify the releasing context at the time > + acquiring a lock because the releasing context is same as the > + releasing context for the typical lock. However, generally we have to > + wait until the lock having been held will be eventually released to > + identify the releasing context. We can say that the original lockdep > + is a special case among all cases this crossrelease feature can deal > + with. I'm not sure you can say this at all; you've no proof of correctness from which this special case flows. > +3. Releasing is more important than acquiring to check its dependency. > + Compare to the third assumption of original lockdep. Again, you're inverting causality afaict. You depend on the future happening to say now is correct. > +Introduce "crosslock" > +--------------------- > + > +Crossrelease feature names a lock "crosslock" if it is releasable by a > +different context from the context having acquired the lock. All locks > +having been held in the context unlocking the crosslock until > +eventually the crosslock will be unlocked, have dependency with the > +crosslock. That's the key idea to implement crossrelease feature. _all_ locks? That implies infinite storage, which is hardly feasible. If you limit it, the limit would seem arbitrary and you loose your proof (in so far as I can see, because you're not actually giving any). Please, give a coherent, mathematical proof of correctness. Because I'm not seeing how this thing would work.
[toc] | [prev] | [next] | [standalone]
| From | Byungchul Park <byungchul.park@lge.com> |
|---|---|
| Date | 2016-07-04 08:50 +0200 |
| Subject | Re: [PATCH] lockdep: Add a document describing crossrelease feature |
| Message-ID | <rR5RE-2gx-9@gated-at.bofh.it> |
| In reply to | #1435251 |
On Fri, Jul 01, 2016 at 12:45:21PM +0200, Peter Zijlstra wrote: > > +Crossrelease lock dependency check > > +================================== > > + > > +Started by Byungchul Park <byungchul.park@lge.com> > > + > > +Contents: > > + > > + (*) What is a problem? > > + > > + - Original lockdep's assumptions. > > + - Original lockdep's limitation. > > Their form doesn't make sense if we ever commit this. Nobody knows or > cares about an 'original' lockdep. There is only now. Right. I wonder what word would be proper. Basic? Or just lockdep? > > +Original lockdep's assumptions > > +------------------------------ > > + > > +Original lockdep (not crossrelease featured lockdep) assumes that, > > + > > +1. A lock will be unlocked within the context holding the lock. > > This is lock owner semantics; that is, each lock has a clear owner. > Which is a sane assumption, and a hard requirement for PI. Remember, all > this comes from the RT tree. I agree it's hard requirment for PI. > > !owner locks cannot do PI and thus cannot be used for code you want to > provide deterministic behaviour with. I am sorry for that I don't understand this sentence. Could you explain it more? > > +2. A lock has dependency with all locks already held in held_locks. > > That's not really an assumption, given 1, this is a fact. An owner lock > can only depend on locks currently held. This is a corner stone of > proving things. Yes, given 1, it's a fact. I need to modify this assumption section. > > > +2. Acquiring is more important than releasing, to check its dependency. > > s/2/3/ > > That's not an assumption; that's a hard requirement. Since the acquire > is the one blocking, you _have_ to check for cycles before you block, > otherwise you'll hit the deadlock and not get a report, because you're > deadlocked. I don't think that's a *hard* requirement, even though that's required in order to be able to report the deadlock before hitting it actually. I agree that we can report the actual deadlock only if we check it before the blocking operation is actually executed, as the current lockdep can report it before the actual deadlock happens. It's very good. However, there's another valuable thing lockdep mechanism can provides. It can report a deadlock possibility based on the dependency built even though actual deadlock does not happen. Of cource it would be the best if it can report both the actual deadlock and the deadlock possibility. So I also think we should focus acquiring rather than releasing for typical lock since we can always report the deadlock. However, for any other locks which can be released by different context for the context it was held by, we cannot detect any deadlock focusing acquiring because we don't know where it will be released. However we can detect the deadlock possibility if we consider the releasing so that we can identify the releasing context, even though this way we cannot report the actual deadlock for the crosslock. I think, detecting the deadlock possibility is more valuable than doing nothing, unless it harms original lockdep's current capability. > > +Original lockdep's limitation > > +----------------------------- > > + > > +Therefore, the original lockdep has limitations. It can be applied only > > +to typical lock operations, e.g. spin_lock, mutex, semaphore and the > > This is wrong, semaphores are very much not covered by lockdep since > they do not have owner semantics (what you call crossmuck). (this is > the distinction between a binary semaphore and a mutex) Sorry for missing it. And please don't use the word like muck. > > +What causes deadlock > > +-------------------- > > + > > +Not only lock operations, but also any operations causing to wait or > > +spin it e.g. all wait operations for an event, lock_page() and so on > > +can cause deadlock unless it's eventually released by someone. The most > > +important point here is that the waiting or spinning must be *released* > > +by someone. In other words, we have to focus whether the waiting and > > +spinning can be *released* or not to avoid deadlock, rather than > > +waiting or spinning it itself. > > But since its the blocking that _is_ the deadlock, you'll never get your > report. Yes right. So I also think it is better to leave current implementation unchanged for typical lock. However it would be better to detect the deadlock possibility than doing nothing for crosslock. > > +Relax the assumptions > > +--------------------- > > + > > +We can relax the assumtions the original lockdep has, which is not > > +necessary to check dependency and detect a deadlock. > > + > > +1. A lock can be unlocked in any context, unless the context itself > > + causes a deadlock e.g. acquiring a lock in irq-safe context before > > + releasing the lock in irq-unsafe context. > > You fail to say how this preserves correctness. By relaxing this you Yes, I have to add more description about that. > loose the held_lock dependencies and you destroy the entire proof that > currently underpins lockdep. I've never touch the current proof the lockdep uses. Just *added* additional detection capability to detect the deadlock possibility for crosslock for which currently we are doing nothing. > > +2. A lock has dependency with all locks in the releasing context, having > > + been held since the lock was held. > > But you cannot tell this. The 'since the lock was held' thing fully > depends on timing and is not fundamentally correct. > > lock(A) > unlock(A) > lock(A) > wait_for(B) > unlock(A) > wake(B) > > Between the wait_for(B) and wake(B), _nothing_ has been held, yet still > there's the deadlock potential. Crossreleas feature can detect this situation as a deadlock. wait_for() is not an actual lock, but we can make it detectable by using acquring and releasing semantics on wait_for() and wake(). > And note that if the timing was 'right', you would never get to wake(B) > because deadlock, so you'd never establish that there would be a > deadlock. If a deadlock actually happens, then we cannot establish it as you said. Remind that current lockdep does nothing for this situation. But at least crossrelease feature can detect this deadlock possibility at the time the dependency tree(graph) is built, which is better than doing nothing. > > Thus we can check the dependency > > + only after we identify the releasing context at first. Of course, > > + if we consider only typical lock e.g. spin lock, mutex, semaphore > > + and so on, then we can identify the releasing context at the time > > + acquiring a lock because the releasing context is same as the > > + releasing context for the typical lock. However, generally we have to > > + wait until the lock having been held will be eventually released to > > + identify the releasing context. We can say that the original lockdep > > + is a special case among all cases this crossrelease feature can deal > > + with. > > I'm not sure you can say this at all; you've no proof of correctness > from which this special case flows. I need to modify and reinforce this description so that does not make you confused. > > +Introduce "crosslock" > > +--------------------- > > + > > +Crossrelease feature names a lock "crosslock" if it is releasable by a > > +different context from the context having acquired the lock. All locks > > +having been held in the context unlocking the crosslock until > > +eventually the crosslock will be unlocked, have dependency with the > > +crosslock. That's the key idea to implement crossrelease feature. > > _all_ locks? That implies infinite storage, which is hardly feasible. If Right. Basically it tries to consider all lock, but crossfeature builds cache chain and check if the consideration is duplicated or not, and only consider necessary ones. At least, on my qemu machine it works well without any problem and detect problematic deadlock situation I mentioned. > you limit it, the limit would seem arbitrary and you loose your proof > (in so far as I can see, because you're not actually giving any). I will give more description about implementation next spin.
[toc] | [prev] | [next] | [standalone]
| From | Boqun Feng <boqun.feng@gmail.com> |
|---|---|
| Date | 2016-07-06 02:50 +0200 |
| Subject | Re: [PATCH] lockdep: Add a document describing crossrelease feature |
| Message-ID | <rRJcl-1UL-5@gated-at.bofh.it> |
| In reply to | #1436127 |
[Multipart message — attachments visible in raw view] — view raw
On Mon, Jul 04, 2016 at 03:42:59PM +0900, Byungchul Park wrote: [snip] > > > +2. A lock has dependency with all locks in the releasing context, having > > > + been held since the lock was held. > > > > But you cannot tell this. The 'since the lock was held' thing fully > > depends on timing and is not fundamentally correct. > > > > lock(A) > > unlock(A) > > lock(A) > > wait_for(B) > > unlock(A) > > wake(B) > > > > Between the wait_for(B) and wake(B), _nothing_ has been held, yet still > > there's the deadlock potential. > > Crossreleas feature can detect this situation as a deadlock. wait_for() > is not an actual lock, but we can make it detectable by using acquring and > releasing semantics on wait_for() and wake(). > > > And note that if the timing was 'right', you would never get to wake(B) > > because deadlock, so you'd never establish that there would be a > > deadlock. > > If a deadlock actually happens, then we cannot establish it as you said. > Remind that current lockdep does nothing for this situation. But at least > crossrelease feature can detect this deadlock possibility at the time the > dependency tree(graph) is built, which is better than doing nothing. > Confused, how? Say the sequence of events is as follow: (two tasks are initially with no lock held) Task 1 Task 2 ============= ==================== lock(A) unlock(A) lock(A) wait_for(B) // acquire wake(B) // commit + release unlock(A) by the time, the commit are called, the dependency tree will be built, and we will find there is _no_ lock held before wake(B). Therefore at the release stage, you will end up only adding dependency chain A->B in the lockdep, right? And it looks like neither Task1 or Task2 will break the dependency chain A->B. So how can crossrelease detect the potential deadlock? It will be better, that you could provide some samples that crossrelease can detect after your confirmation. Regards, Boqun
[toc] | [prev] | [next] | [standalone]
| From | Byungchul Park <byungchul.park@lge.com> |
|---|---|
| Date | 2016-07-06 04:20 +0200 |
| Subject | Re: [PATCH] lockdep: Add a document describing crossrelease feature |
| Message-ID | <rRKBr-38O-11@gated-at.bofh.it> |
| In reply to | #1437347 |
On Wed, Jul 06, 2016 at 08:49:43AM +0800, Boqun Feng wrote: > On Mon, Jul 04, 2016 at 03:42:59PM +0900, Byungchul Park wrote: > [snip] > > > > +2. A lock has dependency with all locks in the releasing context, having > > > > + been held since the lock was held. > > > > > > But you cannot tell this. The 'since the lock was held' thing fully > > > depends on timing and is not fundamentally correct. > > > > > > lock(A) > > > unlock(A) > > > lock(A) > > > wait_for(B) > > > unlock(A) > > > wake(B) > > > > > > Between the wait_for(B) and wake(B), _nothing_ has been held, yet still > > > there's the deadlock potential. I mis-understood this sentence. However, anyway this does not cause deadlock. Of course it's deadlock if an example below actually happens. We should not presume that the below can happen, once the above happened because some dependencies between these contexts may prevent the below. Therefore we should decide to check only when the actual problematic sequence happens like below. lock(A) wait_for(B) ~~~~~~~~~~~~~~~~~~~~~~~~ <- serialized by atomic operation lock(A) unlock(A) wake(B) unlock(A) So I meant crossrelease can detect this deadlock if actually deadlock causable sequence happens at least once. I tried to avoid false positive detection, in other words, I made lockdep's detection stronger only with true positive ones. Of course I want this crosslock to be stronger than current implementation. However I have no idea to make even what peterz's example can be detected regarding all dependency with avoiding false positive detection. It should be a future work. But it will be not simple or impossible. > > > > Crossreleas feature can detect this situation as a deadlock. wait_for() > > is not an actual lock, but we can make it detectable by using acquring and > > releasing semantics on wait_for() and wake(). > > > > > And note that if the timing was 'right', you would never get to wake(B) > > > because deadlock, so you'd never establish that there would be a > > > deadlock. > > > > If a deadlock actually happens, then we cannot establish it as you said. > > Remind that current lockdep does nothing for this situation. But at least > > crossrelease feature can detect this deadlock possibility at the time the > > dependency tree(graph) is built, which is better than doing nothing. > > > > Confused, how? And I am sorry for making you confused. > > Say the sequence of events is as follow: > > (two tasks are initially with no lock held) > > Task 1 Task 2 > ============= ==================== > lock(A) > unlock(A) > lock(A) > wait_for(B) // acquire > wake(B) // commit + release > unlock(A) > As you know this is not actual deadlock. > by the time, the commit are called, the dependency tree will be built, > and we will find there is _no_ lock held before wake(B). Therefore at > the release stage, you will end up only adding dependency chain A->B in > the lockdep, right? And it looks like neither Task1 or Task2 will break > the dependency chain A->B. So how can crossrelease detect the potential > deadlock? It's similar to how the crossrelease work, but a little bit different. I will reinforce and resend the document later. > > It will be better, that you could provide some samples that crossrelease > can detect after your confirmation. Yes I will. Thank you, Byungchul > > Regards, > Boqun
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | linux.kernel
csiph-web