From patchwork Tue Jan 22 07:34:23 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Srivatsa S. Bhat" X-Patchwork-Id: 2015681 Return-Path: X-Original-To: patchwork-linux-pm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id 06A2F3FCDE for ; Tue, 22 Jan 2013 07:36:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754326Ab3AVHgh (ORCPT ); Tue, 22 Jan 2013 02:36:37 -0500 Received: from e23smtp06.au.ibm.com ([202.81.31.148]:36233 "EHLO e23smtp06.au.ibm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754023Ab3AVHge (ORCPT ); Tue, 22 Jan 2013 02:36:34 -0500 Received: from /spool/local by e23smtp06.au.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Tue, 22 Jan 2013 17:32:33 +1000 Received: from d23dlp01.au.ibm.com (202.81.31.203) by e23smtp06.au.ibm.com (202.81.31.212) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Tue, 22 Jan 2013 17:32:30 +1000 Received: from d23relay05.au.ibm.com (d23relay05.au.ibm.com [9.190.235.152]) by d23dlp01.au.ibm.com (Postfix) with ESMTP id 1EB5E2CE8050; Tue, 22 Jan 2013 18:36:28 +1100 (EST) Received: from d23av04.au.ibm.com (d23av04.au.ibm.com [9.190.235.139]) by d23relay05.au.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id r0M7OXxp4850016; Tue, 22 Jan 2013 18:24:34 +1100 Received: from d23av04.au.ibm.com (loopback [127.0.0.1]) by d23av04.au.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id r0M7aPBv004708; Tue, 22 Jan 2013 18:36:27 +1100 Received: from srivatsabhat.in.ibm.com (srivatsabhat.in.ibm.com [9.124.35.112]) by d23av04.au.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id r0M7a8kK004305; Tue, 22 Jan 2013 18:36:09 +1100 From: "Srivatsa S. Bhat" Subject: [PATCH v5 06/45] percpu_rwlock: Allow writers to be readers, and add lockdep annotations To: tglx@linutronix.de, peterz@infradead.org, tj@kernel.org, oleg@redhat.com, paulmck@linux.vnet.ibm.com, rusty@rustcorp.com.au, mingo@kernel.org, akpm@linux-foundation.org, namhyung@kernel.org Cc: rostedt@goodmis.org, wangyun@linux.vnet.ibm.com, xiaoguangrong@linux.vnet.ibm.com, rjw@sisk.pl, sbw@mit.edu, fweisbec@gmail.com, linux@arm.linux.org.uk, nikunj@linux.vnet.ibm.com, srivatsa.bhat@linux.vnet.ibm.com, linux-pm@vger.kernel.org, linux-arch@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linuxppc-dev@lists.ozlabs.org, netdev@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org Date: Tue, 22 Jan 2013 13:04:23 +0530 Message-ID: <20130122073416.13822.96504.stgit@srivatsabhat.in.ibm.com> In-Reply-To: <20130122073210.13822.50434.stgit@srivatsabhat.in.ibm.com> References: <20130122073210.13822.50434.stgit@srivatsabhat.in.ibm.com> User-Agent: StGIT/0.14.3 MIME-Version: 1.0 X-Content-Scanned: Fidelis XPS MAILER x-cbid: 13012207-7014-0000-0000-0000027FF79E Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org CPU hotplug (which will be the first user of per-CPU rwlocks) has a special requirement with respect to locking: the writer, after acquiring the per-CPU rwlock for write, must be allowed to take the same lock for read, without deadlocking and without getting complaints from lockdep. In comparison, this is similar to what get_online_cpus()/put_online_cpus() does today: it allows a hotplug writer (who holds the cpu_hotplug.lock mutex) to invoke it without locking issues, because it silently returns if the caller is the hotplug writer itself. This can be easily achieved with per-CPU rwlocks as well (even without a "is this a writer?" check) by incrementing the per-CPU refcount of the writer immediately after taking the global rwlock for write, and then decrementing the per-CPU refcount before releasing the global rwlock. This ensures that any reader that comes along on that CPU while the writer is active (on that same CPU), notices the non-zero value of the nested counter and assumes that it is a nested read-side critical section and proceeds by just incrementing the refcount. Thus we prevent the reader from taking the global rwlock for read, which prevents the writer from deadlocking itself. Add that support and teach lockdep about this special locking scheme so that it knows that this sort of usage is valid. Also add the required lockdep annotations to enable it to detect common locking problems with per-CPU rwlocks. Cc: David Howells Signed-off-by: Srivatsa S. Bhat --- lib/percpu-rwlock.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) -- To unsubscribe from this list: send the line "unsubscribe linux-pm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/lib/percpu-rwlock.c b/lib/percpu-rwlock.c index a8d177a..054a50a 100644 --- a/lib/percpu-rwlock.c +++ b/lib/percpu-rwlock.c @@ -84,6 +84,10 @@ void percpu_read_lock_irqsafe(struct percpu_rwlock *pcpu_rwlock) if (likely(!writer_active(pcpu_rwlock))) { this_cpu_inc(*pcpu_rwlock->reader_refcnt); + + /* Pretend that we take global_rwlock for lockdep */ + rwlock_acquire_read(&pcpu_rwlock->global_rwlock.dep_map, + 0, 0, _RET_IP_); } else { /* Writer is active, so switch to global rwlock. */ @@ -108,6 +112,12 @@ void percpu_read_lock_irqsafe(struct percpu_rwlock *pcpu_rwlock) if (!writer_active(pcpu_rwlock)) { this_cpu_inc(*pcpu_rwlock->reader_refcnt); read_unlock(&pcpu_rwlock->global_rwlock); + + /* + * Pretend that we take global_rwlock for lockdep + */ + rwlock_acquire_read(&pcpu_rwlock->global_rwlock.dep_map, + 0, 0, _RET_IP_); } } } @@ -128,6 +138,14 @@ void percpu_read_unlock_irqsafe(struct percpu_rwlock *pcpu_rwlock) if (reader_nested_percpu(pcpu_rwlock)) { this_cpu_dec(*pcpu_rwlock->reader_refcnt); smp_wmb(); /* Paired with smp_rmb() in sync_reader() */ + + /* + * If this is the last decrement, then it is time to pretend + * to lockdep that we are releasing the read lock. + */ + if (!reader_nested_percpu(pcpu_rwlock)) + rwlock_release(&pcpu_rwlock->global_rwlock.dep_map, + 1, _RET_IP_); } else { read_unlock(&pcpu_rwlock->global_rwlock); } @@ -205,11 +223,14 @@ void percpu_write_lock_irqsave(struct percpu_rwlock *pcpu_rwlock, announce_writer_active(pcpu_rwlock); sync_all_readers(pcpu_rwlock); write_lock_irqsave(&pcpu_rwlock->global_rwlock, *flags); + this_cpu_inc(*pcpu_rwlock->reader_refcnt); } void percpu_write_unlock_irqrestore(struct percpu_rwlock *pcpu_rwlock, unsigned long *flags) { + this_cpu_dec(*pcpu_rwlock->reader_refcnt); + /* * Inform all readers that we are done, so that they can switch back * to their per-cpu refcounts. (We don't need to wait for them to