* [PATCH 0/6] defer: Employ new scheme for code snippets (cont.)
@ 2018-12-03 15:33 Akira Yokosawa
2018-12-03 15:35 ` [PATCH 1/6] defer: Employ new scheme for 'lst:defer:Hazard-Pointer Storage and Erasure' Akira Yokosawa
` (6 more replies)
0 siblings, 7 replies; 8+ messages in thread
From: Akira Yokosawa @ 2018-12-03 15:33 UTC (permalink / raw)
To: Paul E. McKenney; +Cc: perfbook, Akira Yokosawa
Hi Paul,
This is a followup patch set to update remaining code snippets
in chapter "defer".
My first thought was that patches #5 and #6 might conflict with
upcoming updates to reflect consolidation of RCU flavors, but it
looks like these code snippets are unlikely to be affected.
Patch #1 assumes that you intentionally presented a different
set of hazptr API from the one used in hazptr.h.
Thanks, Akira
--
Akira Yokosawa (6):
defer: Employ new scheme for 'lst:defer:Hazard-Pointer Storage and
Erasure'
defer: Employ new scheme for snippets of route_hazptr.c
defer: Employ new scheme for snippet of seqlock.h
defer: Employ new scheme for snippets of route_seqlock.c
defer: Employ new scheme for snippets in rcuintro and rcufundamental
defer: Employ new scheme for snippets of route_rcu.c
CodeSamples/defer/route_hazptr.c | 50 ++--
CodeSamples/defer/route_rcu.c | 75 +++---
CodeSamples/defer/route_seqlock.c | 56 ++--
CodeSamples/defer/seqlock.h | 55 ++--
defer/hazptr.tex | 168 +++---------
defer/rcufundamental.tex | 381 +++++++++++++--------------
defer/rcuintro.tex | 13 +-
defer/rcuusage.tex | 523 ++++++++++++++++----------------------
defer/seqlock.tex | 259 ++++++-------------
9 files changed, 637 insertions(+), 943 deletions(-)
--
2.7.4
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/6] defer: Employ new scheme for 'lst:defer:Hazard-Pointer Storage and Erasure'
2018-12-03 15:33 [PATCH 0/6] defer: Employ new scheme for code snippets (cont.) Akira Yokosawa
@ 2018-12-03 15:35 ` Akira Yokosawa
2018-12-03 15:37 ` [PATCH 2/6] defer: Employ new scheme for snippets of route_hazptr.c Akira Yokosawa
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Akira Yokosawa @ 2018-12-03 15:35 UTC (permalink / raw)
To: Paul E. McKenney; +Cc: perfbook, Akira Yokosawa
From a526b98e4fe052223c82dbd3cf961d40bc27f294 Mon Sep 17 00:00:00 2001
From: Akira Yokosawa <akiyks@gmail.com>
Date: Tue, 4 Dec 2018 00:13:47 +0900
Subject: [PATCH 1/6] defer: Employ new scheme for 'lst:defer:Hazard-Pointer Storage and Erasure'
Signed-off-by: Akira Yokosawa <akiyks@gmail.com>
---
defer/hazptr.tex | 58 ++++++++++++++++++++++++++++----------------------------
1 file changed, 29 insertions(+), 29 deletions(-)
diff --git a/defer/hazptr.tex b/defer/hazptr.tex
index 1aca813..588f603 100644
--- a/defer/hazptr.tex
+++ b/defer/hazptr.tex
@@ -22,33 +22,30 @@ and there are no longer any hazard pointers referencing it, that element
may safely be freed.
\begin{listing}[btp]
-{ \scriptsize
-\begin{verbbox}
- 1 int hp_store(void **p, void **hp)
- 2 {
- 3 void *tmp;
- 4
- 5 tmp = READ_ONCE(*p);
- 6 WRITE_ONCE(*hp, tmp);
- 7 smp_mb();
- 8 if (tmp != READ_ONCE(*p) ||
- 9 tmp == HAZPTR_POISON) {
-10 WRITE_ONCE(*hp, NULL);
-11 return 0;
-12 }
-13 return 1;
-14 }
-15
-16 void hp_erase(void **hp)
-17 {
-18 smp_mb();
-19 WRITE_ONCE(*hp, NULL);
-20 hp_free(hp);
-21 }
-\end{verbbox}
-}
-\centering
-\theverbbox
+\begin{linelabel}[ln:defer:Hazard-Pointer Storage and Erasure]
+\begin{VerbatimL}[commandchars=\\\[\]]
+int hp_store(void **p, void **hp) \lnlbl[store:b]
+{
+ void *tmp;
+
+ tmp = READ_ONCE(*p);
+ WRITE_ONCE(*hp, tmp);
+ smp_mb();
+ if (tmp != READ_ONCE(*p) || tmp == HAZPTR_POISON) {
+ WRITE_ONCE(*hp, NULL);
+ return 0;
+ }
+ return 1;
+} \lnlbl[store:e]
+
+void hp_erase(void **hp) \lnlbl[erase:b]
+{
+ smp_mb();
+ WRITE_ONCE(*hp, NULL);
+ hp_free(hp);
+} \lnlbl[erase:e]
+\end{VerbatimL}
+\end{linelabel}
\caption{Hazard-Pointer Storage and Erasure}
\label{lst:defer:Hazard-Pointer Storage and Erasure}
\end{listing}
@@ -56,13 +53,16 @@ may safely be freed.
Of course, this means that hazard-pointer acquisition must be carried
out quite carefully in order to avoid destructive races with concurrent
deletion.
+\begin{lineref}[ln:defer:Hazard-Pointer Storage and Erasure]
One implementation is shown in
Listing~\ref{lst:defer:Hazard-Pointer Storage and Erasure},
-which shows \co{hp_store()} on lines~1-14 and \co{hp_erase()} on
-lines~16-21.
+which shows \co{hp_store()} on
+lines~\lnref{store:b}-\lnref{store:e} and \co{hp_erase()} on
+lines~\lnref{erase:b}-\lnref{erase:e}.
The \co{smp_mb()} primitive will be described in detail in
Chapter~\ref{chp:Advanced Synchronization: Memory Ordering}, but may be ignored for
the purposes of this brief overview.
+\end{lineref}
The \co{hp_store()} function records a hazard pointer at \co{hp} for the data
element whose pointer is referenced by \co{p}, while checking for
--
2.7.4
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/6] defer: Employ new scheme for snippets of route_hazptr.c
2018-12-03 15:33 [PATCH 0/6] defer: Employ new scheme for code snippets (cont.) Akira Yokosawa
2018-12-03 15:35 ` [PATCH 1/6] defer: Employ new scheme for 'lst:defer:Hazard-Pointer Storage and Erasure' Akira Yokosawa
@ 2018-12-03 15:37 ` Akira Yokosawa
2018-12-03 15:39 ` [PATCH 3/6] defer: Employ new scheme for snippet of seqlock.h Akira Yokosawa
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Akira Yokosawa @ 2018-12-03 15:37 UTC (permalink / raw)
To: Paul E. McKenney; +Cc: perfbook, Akira Yokosawa
From be603d83f1555cea7c2e1e6d1306c613ceb79b21 Mon Sep 17 00:00:00 2001
From: Akira Yokosawa <akiyks@gmail.com>
Date: Tue, 4 Dec 2018 00:15:18 +0900
Subject: [PATCH 2/6] defer: Employ new scheme for snippets of route_hazptr.c
Signed-off-by: Akira Yokosawa <akiyks@gmail.com>
---
CodeSamples/defer/route_hazptr.c | 50 ++++++++++--------
defer/hazptr.tex | 110 +++++----------------------------------
2 files changed, 40 insertions(+), 120 deletions(-)
diff --git a/CodeSamples/defer/route_hazptr.c b/CodeSamples/defer/route_hazptr.c
index 49e6e4a..c9285c6 100644
--- a/CodeSamples/defer/route_hazptr.c
+++ b/CodeSamples/defer/route_hazptr.c
@@ -23,58 +23,61 @@
#include "hazptr.h"
/* Route-table entry to be included in the routing list. */
+//\begin{snippet}[labelbase=ln:defer:route_hazptr:lookup,commandchars=\\\[\]]
struct route_entry {
- struct hazptr_head hh;
+ struct hazptr_head hh; //\lnlbl{hh}
struct route_entry *re_next;
unsigned long addr;
unsigned long iface;
- int re_freed;
+ int re_freed; //\lnlbl{re_freed}
};
-
+ //\fcvexclude
struct route_entry route_list;
DEFINE_SPINLOCK(routelock);
-
-/* This thread's fixed-sized set of hazard pointers. */
+ //\fcvexclude
+/* This thread's fixed-sized set of hazard pointers. */ //\fcvexclude
hazard_pointer __thread *my_hazptr;
-/*
- * Look up a route entry, return the corresponding interface.
- */
+/* \fcvexclude
+ * Look up a route entry, return the corresponding interface. \fcvexclude
+ */ //\fcvexclude
unsigned long route_lookup(unsigned long addr)
{
int offset = 0;
struct route_entry *rep;
struct route_entry **repp;
-retry:
+retry: //\lnlbl{retry}
repp = &route_list.re_next;
do {
rep = READ_ONCE(*repp);
if (rep == NULL)
return ULONG_MAX;
- if (rep == (struct route_entry *)HAZPTR_POISON)
+ if (rep == (struct route_entry *)HAZPTR_POISON) //\lnlbl{acq:b}
goto retry; /* element deleted. */
-
- /* Store a hazard pointer. */
+ //\fcvexclude
+ /* Store a hazard pointer. */ //\fcvexclude
my_hazptr[offset].p = &rep->hh;
offset = !offset;
smp_mb(); /* Force pointer loads in order. */
-
- /* Recheck the hazard pointer against the original. */
+ //\fcvexclude
+ /* Recheck the hazard pointer against the original. */ //\fcvexclude
if (READ_ONCE(*repp) != rep)
- goto retry;
-
- /* Advance to next. */
+ goto retry; //\lnlbl{acq:e}
+ //\fcvexclude
+ /* Advance to next. */ //\fcvexclude
repp = &rep->re_next;
} while (rep->addr != addr);
if (READ_ONCE(rep->re_freed))
abort();
return rep->iface;
}
+//\end{snippet}
/*
* Add an element to the route table.
*/
+//\begin{snippet}[labelbase=ln:defer:route_hazptr:add_del,commandchars=\\\[\]]
int route_add(unsigned long addr, unsigned long interface)
{
struct route_entry *rep;
@@ -84,7 +87,7 @@ int route_add(unsigned long addr, unsigned long interface)
return -ENOMEM;
rep->addr = addr;
rep->iface = interface;
- rep->re_freed = 0;
+ rep->re_freed = 0; //\lnlbl{init_freed}
spin_lock(&routelock);
rep->re_next = route_list.re_next;
route_list.re_next = rep;
@@ -92,9 +95,9 @@ int route_add(unsigned long addr, unsigned long interface)
return 0;
}
-/*
- * Remove the specified element from the route table.
- */
+/* \fcvexclude
+ * Remove the specified element from the route table. \fcvexclude
+ */ //\fcvexclude
int route_del(unsigned long addr)
{
struct route_entry *rep;
@@ -108,9 +111,9 @@ int route_del(unsigned long addr)
break;
if (rep->addr == addr) {
*repp = rep->re_next;
- rep->re_next = (struct route_entry *)HAZPTR_POISON;
+ rep->re_next = (struct route_entry *)HAZPTR_POISON; //\lnlbl{poison}
spin_unlock(&routelock);
- hazptr_free_later(&rep->hh);
+ hazptr_free_later(&rep->hh); //\lnlbl{free_later}
return 0;
}
repp = &rep->re_next;
@@ -118,6 +121,7 @@ int route_del(unsigned long addr)
spin_unlock(&routelock);
return -ENOENT;
}
+//\end{snippet}
/*
* Clear all elements from the route table.
diff --git a/defer/hazptr.tex b/defer/hazptr.tex
index 588f603..98ba002 100644
--- a/defer/hazptr.tex
+++ b/defer/hazptr.tex
@@ -181,101 +181,13 @@ Chapter~\ref{chp:Data Structures}
and in other publications~\cite{ThomasEHart2007a,McKenney:2013:SDS:2483852.2483867,MagedMichael04a}.
\begin{listing}[tbp]
-{ \scriptsize
-\begin{verbbox}
- 1 struct route_entry {
- 2 struct hazptr_head hh;
- 3 struct route_entry *re_next;
- 4 unsigned long addr;
- 5 unsigned long iface;
- 6 int re_freed;
- 7 };
- 8 struct route_entry route_list;
- 9 DEFINE_SPINLOCK(routelock);
-10 hazard_pointer __thread *my_hazptr;
-11
-12 unsigned long route_lookup(unsigned long addr)
-13 {
-14 int offset = 0;
-15 struct route_entry *rep;
-16 struct route_entry **repp;
-17
-18 retry:
-19 repp = &route_list.re_next;
-20 do {
-21 rep = READ_ONCE(*repp);
-22 if (rep == NULL)
-23 return ULONG_MAX;
-24 if (rep == (struct route_entry *)HAZPTR_POISON)
-25 goto retry;
-26 my_hazptr[offset].p = &rep->hh;
-27 offset = !offset;
-28 smp_mb();
-29 if (READ_ONCE(*repp) != rep)
-30 goto retry;
-31 repp = &rep->re_next;
-32 } while (rep->addr != addr);
-33 if (READ_ONCE(rep->re_freed))
-34 abort();
-35 return rep->iface;
-36 }
-\end{verbbox}
-}
-\centering
-\theverbbox
+\input{CodeSamples/defer/route_hazptr@lookup.fcv}
\caption{Hazard-Pointer Pre-BSD Routing Table Lookup}
\label{lst:defer:Hazard-Pointer Pre-BSD Routing Table Lookup}
\end{listing}
\begin{listing}[tbp]
-{ \scriptsize
-\begin{verbbox}
- 1 int route_add(unsigned long addr,
- 2 unsigned long interface)
- 3 {
- 4 struct route_entry *rep;
- 5
- 6 rep = malloc(sizeof(*rep));
- 7 if (!rep)
- 8 return -ENOMEM;
- 9 rep->addr = addr;
-10 rep->iface = interface;
-11 rep->re_freed = 0;
-12 spin_lock(&routelock);
-13 rep->re_next = route_list.re_next;
-14 route_list.re_next = rep;
-15 spin_unlock(&routelock);
-16 return 0;
-17 }
-18
-19 int route_del(unsigned long addr)
-20 {
-21 struct route_entry *rep;
-22 struct route_entry **repp;
-23
-24 spin_lock(&routelock);
-25 repp = &route_list.re_next;
-26 for (;;) {
-27 rep = *repp;
-28 if (rep == NULL)
-29 break;
-30 if (rep->addr == addr) {
-31 *repp = rep->re_next;
-32 rep->re_next =
-33 (struct route_entry *)HAZPTR_POISON;
-34 spin_unlock(&routelock);
-35 hazptr_free_later(&rep->hh);
-36 return 0;
-37 }
-38 repp = &rep->re_next;
-39 }
-40 spin_unlock(&routelock);
-41 return -ENOENT;
-42 }
-\end{verbbox}
-}
-\centering
-\theverbbox
+\input{CodeSamples/defer/route_hazptr@add_del.fcv}
\caption{Hazard-Pointer Pre-BSD Routing Table Add/Delete}
\label{lst:defer:Hazard-Pointer Pre-BSD Routing Table Add/Delete}
\end{listing}
@@ -293,24 +205,28 @@ on
page~\pageref{lst:defer:Sequential Pre-BSD Routing Table},
so only differences will be discussed.
+\begin{lineref}[ln:defer:route_hazptr:lookup]
Starting with
Listing~\ref{lst:defer:Hazard-Pointer Pre-BSD Routing Table Lookup},
-line~2 shows the \co{->hh} field used to queue objects pending
+line~\lnref{hh} shows the \co{->hh} field used to queue objects pending
hazard-pointer free,
-line~6 shows the \co{->re_freed} field used to detect use-after-free bugs,
-and lines~24-30 attempt to acquire a hazard pointer, branching
-to line~18's \co{retry} label on failure.
+line~\lnref{re_freed} shows the \co{->re_freed} field used to detect use-after-free bugs,
+and lines~\lnref{acq:b}-\lnref{acq:e} attempt to acquire a hazard pointer, branching
+to line~\lnref{retry}'s \co{retry} label on failure.
+\end{lineref}
+\begin{lineref}[ln:defer:route_hazptr:add_del]
In
Listing~\ref{lst:defer:Hazard-Pointer Pre-BSD Routing Table Add/Delete},
-line~11 initializes \co{->re_freed},
-lines~32 and~33 poison the \co{->re_next} field of the newly removed
+line~\lnref{init_freed} initializes \co{->re_freed},
+line~\lnref{poison} poisons the \co{->re_next} field of the newly removed
object, and
-line~35 passes that object to the hazard pointers's
+line~\lnref{free_later} passes that object to the hazard pointers's
\co{hazptr_free_later()} function, which will free that object once it
is safe to do so.
The spinlocks work the same as in
Listing~\ref{lst:defer:Reference-Counted Pre-BSD Routing Table Add/Delete}.
+\end{lineref}
\begin{figure}[tb]
\centering
--
2.7.4
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 3/6] defer: Employ new scheme for snippet of seqlock.h
2018-12-03 15:33 [PATCH 0/6] defer: Employ new scheme for code snippets (cont.) Akira Yokosawa
2018-12-03 15:35 ` [PATCH 1/6] defer: Employ new scheme for 'lst:defer:Hazard-Pointer Storage and Erasure' Akira Yokosawa
2018-12-03 15:37 ` [PATCH 2/6] defer: Employ new scheme for snippets of route_hazptr.c Akira Yokosawa
@ 2018-12-03 15:39 ` Akira Yokosawa
2018-12-03 15:41 ` [PATCH 4/6] defer: Employ new scheme for snippets of route_seqlock.c Akira Yokosawa
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Akira Yokosawa @ 2018-12-03 15:39 UTC (permalink / raw)
To: Paul E. McKenney; +Cc: perfbook, Akira Yokosawa
From 6a5f865a117ceea5b924506b9bd55f622b95365f Mon Sep 17 00:00:00 2001
From: Akira Yokosawa <akiyks@gmail.com>
Date: Tue, 4 Dec 2018 00:15:45 +0900
Subject: [PATCH 3/6] defer: Employ new scheme for snippet of seqlock.h
Also convert inline snippets in hazptr.tex.
Signed-off-by: Akira Yokosawa <akiyks@gmail.com>
---
CodeSamples/defer/seqlock.h | 55 +++++++++--------
defer/seqlock.tex | 146 +++++++++++++++++---------------------------
2 files changed, 85 insertions(+), 116 deletions(-)
diff --git a/CodeSamples/defer/seqlock.h b/CodeSamples/defer/seqlock.h
index c994285..dece371 100644
--- a/CodeSamples/defer/seqlock.h
+++ b/CodeSamples/defer/seqlock.h
@@ -18,50 +18,53 @@
* Copyright (c) 2008 Paul E. McKenney, IBM Corporation.
*/
-typedef struct {
- unsigned long seq;
+//\begin{snippet}[labelbase=ln:defer:seqlock:impl,commandchars=\\\[\]]
+typedef struct { //\lnlbl{typedef:b}
+ unsigned long seq; //\lnlbl{typedef:seq}
spinlock_t lock;
-} seqlock_t;
+} seqlock_t; //\lnlbl{typedef:e}
-#define DEFINE_SEQ_LOCK(name) seqlock_t name = { \
- .seq = 0, \
- .lock = __SPIN_LOCK_UNLOCKED(name.lock), \
-};
-
-static inline void seqlock_init(seqlock_t *slp)
+#define DEFINE_SEQ_LOCK(name) seqlock_t name = { /* \fcvexclude */ \
+ .seq = 0, /* \fcvexclude */ \
+ .lock = __SPIN_LOCK_UNLOCKED(name.lock), /* \fcvexclude */ \
+}; /* \fcvexclude */
+ /* \fcvexclude */
+static inline void seqlock_init(seqlock_t *slp) //\lnlbl{init:b}
{
slp->seq = 0;
spin_lock_init(&slp->lock);
-}
+} //\lnlbl{init:e}
-static inline unsigned long read_seqbegin(seqlock_t *slp)
+static inline unsigned long read_seqbegin(seqlock_t *slp) //\lnlbl{read_seqbegin:b}
{
unsigned long s;
- s = READ_ONCE(slp->seq);
- smp_mb();
- return s & ~0x1UL;
-}
+ s = READ_ONCE(slp->seq); //\lnlbl{read_seqbegin:fetch}
+ smp_mb(); //\lnlbl{read_seqbegin:mb}
+ return s & ~0x1UL; //\lnlbl{read_seqbegin:ret}
+} //\lnlbl{read_seqbegin:e}
-static inline int read_seqretry(seqlock_t *slp, unsigned long oldseq)
+static inline int read_seqretry(seqlock_t *slp, //\lnlbl{read_seqretry:b}
+ unsigned long oldseq)
{
unsigned long s;
- smp_mb();
- s = READ_ONCE(slp->seq);
- return s != oldseq;
-}
+ smp_mb(); //\lnlbl{read_seqretry:mb}
+ s = READ_ONCE(slp->seq); //\lnlbl{read_seqretry:fetch}
+ return s != oldseq; //\lnlbl{read_seqretry:ret}
+} //\lnlbl{read_seqretry:e}
-static inline void write_seqlock(seqlock_t *slp)
+static inline void write_seqlock(seqlock_t *slp) //\lnlbl{write_seqlock:b}
{
spin_lock(&slp->lock);
++slp->seq;
smp_mb();
-}
+} //\lnlbl{write_seqlock:e}
-static inline void write_sequnlock(seqlock_t *slp)
+static inline void write_sequnlock(seqlock_t *slp) //\lnlbl{write_sequnlock:b}
{
- smp_mb();
- ++slp->seq;
+ smp_mb(); //\lnlbl{write_sequnlock:mb}
+ ++slp->seq; //\lnlbl{write_sequnlock:inc}
spin_unlock(&slp->lock);
-}
+} //\lnlbl{write_sequnlock:e}
+//\end{snippet}
diff --git a/defer/seqlock.tex b/defer/seqlock.tex
index 84da3c0..7fabc35 100644
--- a/defer/seqlock.tex
+++ b/defer/seqlock.tex
@@ -50,30 +50,22 @@ very rarely need to retry.
} \QuickQuizEnd
\begin{listing}[bp]
-{ \scriptsize
-\begin{verbbox}
- 1 do {
- 2 seq = read_seqbegin(&test_seqlock);
- 3 /* read-side access. */
- 4 } while (read_seqretry(&test_seqlock, seq));
-\end{verbbox}
-}
-\centering
-\theverbbox
+\begin{VerbatimL}
+do {
+ seq = read_seqbegin(&test_seqlock);
+ /* read-side access. */
+} while (read_seqretry(&test_seqlock, seq));
+\end{VerbatimL}
\caption{Sequence-Locking Reader}
\label{lst:defer:Sequence-Locking Reader}
\end{listing}
\begin{listing}[bp]
-{ \scriptsize
-\begin{verbbox}
- 1 write_seqlock(&test_seqlock);
- 2 /* Update */
- 3 write_sequnlock(&test_seqlock);
-\end{verbbox}
-}
-\centering
-\theverbbox
+\begin{VerbatimL}
+write_seqlock(&test_seqlock);
+/* Update */
+write_sequnlock(&test_seqlock);
+\end{VerbatimL}
\caption{Sequence-Locking Writer}
\label{lst:defer:Sequence-Locking Writer}
\end{listing}
@@ -101,55 +93,7 @@ quantities used for timekeeping.
It is also used in pathname traversal to detect concurrent rename operations.
\begin{listing}[tb]
-{ \scriptsize
-\begin{verbbox}
- 1 typedef struct {
- 2 unsigned long seq;
- 3 spinlock_t lock;
- 4 } seqlock_t;
- 5
- 6 static void seqlock_init(seqlock_t *slp)
- 7 {
- 8 slp->seq = 0;
- 9 spin_lock_init(&slp->lock);
-10 }
-11
-12 static unsigned long read_seqbegin(seqlock_t *slp)
-13 {
-14 unsigned long s;
-15
-16 s = READ_ONCE(slp->seq);
-17 smp_mb();
-18 return s & ~0x1UL;
-19 }
-20
-21 static int read_seqretry(seqlock_t *slp,
-22 unsigned long oldseq)
-23 {
-24 unsigned long s;
-25
-26 smp_mb();
-27 s = READ_ONCE(slp->seq);
-28 return s != oldseq;
-29 }
-30
-31 static void write_seqlock(seqlock_t *slp)
-32 {
-33 spin_lock(&slp->lock);
-34 ++slp->seq;
-35 smp_mb();
-36 }
-37
-38 static void write_sequnlock(seqlock_t *slp)
-39 {
-40 smp_mb();
-41 ++slp->seq;
-42 spin_unlock(&slp->lock);
-43 }
-\end{verbbox}
-}
-\centering
-\theverbbox
+\input{CodeSamples/defer/seqlock@impl.fcv}
\caption{Sequence-Locking Implementation}
\label{lst:defer:Sequence-Locking Implementation}
\end{listing}
@@ -157,18 +101,26 @@ It is also used in pathname traversal to detect concurrent rename operations.
A simple implementation of sequence locks is shown in
Listing~\ref{lst:defer:Sequence-Locking Implementation}
(\path{seqlock.h}).
-The \co{seqlock_t} data structure is shown on lines~1-4, and contains
+\begin{lineref}[ln:defer:seqlock:impl:typedef]
+The \co{seqlock_t} data structure is shown on
+lines~\lnref{b}-\lnref{e}, and contains
the sequence number along with a lock to serialize writers.
-Lines~6-10 show \co{seqlock_init()}, which, as the name indicates,
+\end{lineref}
+\begin{lineref}[ln:defer:seqlock:impl:init]
+Lines~\lnref{b}-\lnref{e} show \co{seqlock_init()}, which, as the name indicates,
initializes a \co{seqlock_t}.
+\end{lineref}
-Lines~12-19 show \co{read_seqbegin()}, which begins a sequence-lock
+\begin{lineref}[ln:defer:seqlock:impl:read_seqbegin]
+Lines~\lnref{b}-\lnref{e} show \co{read_seqbegin()}, which begins a sequence-lock
read-side critical section.
-Line~16 takes a snapshot of the sequence counter, and line~17 orders
+Line~\lnref{fetch} takes a snapshot of the sequence counter, and
+line~\lnref{mb} orders
this snapshot operation before the caller's critical section.
-Finally, line~18 returns the value of the snapshot (with the least-significant
+Finally, line~\lnref{ret} returns the value of the snapshot (with the least-significant
bit cleared), which the caller
will pass to a later call to \co{read_seqretry()}.
+\end{lineref}
\QuickQuiz{}
Why not have \co{read_seqbegin()} in
@@ -185,17 +137,20 @@ will pass to a later call to \co{read_seqretry()}.
check internal to \co{read_seqbegin()} might be preferable.
} \QuickQuizEnd
-Lines~21-29 show \co{read_seqretry()}, which returns true if there
+\begin{lineref}[ln:defer:seqlock:impl:read_seqretry]
+Lines~\lnref{b}-\lnref{e} show \co{read_seqretry()}, which returns true if there
was at least one writer since the time of the corresponding
call to \co{read_seqbegin()}.
-Line~26 orders the caller's prior critical section before line~27's
+Line~\lnref{mb} orders the caller's prior critical section before line~\lnref{fetch}'s
fetch of the new snapshot of the sequence counter.
-Finally, line~28 checks whether the sequence counter has changed,
+Finally, line~\lnref{ret} checks whether the sequence counter has changed,
in other words, whether there has been at least one writer, and returns
true if so.
+\end{lineref}
\QuickQuiz{}
- Why is the \co{smp_mb()} on line~26 of
+ Why is the \co{smp_mb()} on
+ line~\ref{ln:defer:seqlock:impl:read_seqretry:mb} of
Listing~\ref{lst:defer:Sequence-Locking Implementation}
needed?
\QuickQuizAnswer{
@@ -213,19 +168,23 @@ true if so.
\QuickQuizAnswer{
In older versions of the Linux kernel, no.
- In very new versions of the Linux kernel, line~16 could use
+ \begin{lineref}[ln:defer:seqlock:impl]
+ In very new versions of the Linux kernel,
+ line~\lnref{read_seqbegin:fetch} could use
\co{smp_load_acquire()} instead of \co{READ_ONCE()}, which
- in turn would allow the \co{smp_mb()} on line~17 to be dropped.
- Similarly, line~41 could use an \co{smp_store_release()}, for
+ in turn would allow the \co{smp_mb()} on
+ line~\lnref{read_seqbegin:mb} to be dropped.
+ Similarly, line~\lnref{write_sequnlock:inc} could use an
+ \co{smp_store_release()}, for
example, as follows:
-\begin{minipage}[c][5ex][c]{\columnwidth}\scriptsize
-\begin{verbatim}
+\begin{VerbatimU}
smp_store_release(&slp->seq, READ_ONCE(slp->seq) + 1);
-\end{verbatim}
-\end{minipage}
+\end{VerbatimU}
- This would allow the \co{smp_mb()} on line~40 to be dropped.
+ This would allow the \co{smp_mb()} on
+ line~\lnref{write_sequnlock:mb} to be dropped.
+ \end{lineref}
} \QuickQuizEnd
\QuickQuiz{}
@@ -239,12 +198,16 @@ smp_store_release(&slp->seq, READ_ONCE(slp->seq) + 1);
situation, in which case, go wild with the sequence-locking updates!
} \QuickQuizEnd
-Lines~31-36 show \co{write_seqlock()}, which simply acquires the lock,
+\begin{lineref}[ln:defer:seqlock:impl:write_seqlock]
+Lines~\lnref{b}-\lnref{e} show \co{write_seqlock()}, which simply acquires the lock,
increments the sequence number, and executes a memory barrier to ensure
that this increment is ordered before the caller's critical section.
-Lines~38-43 show \co{write_sequnlock()}, which executes a memory barrier
+\end{lineref}
+\begin{lineref}[ln:defer:seqlock:impl:write_sequnlock]
+Lines~\lnref{b}-\lnref{e} show \co{write_sequnlock()}, which executes a memory barrier
to ensure that the caller's critical section is ordered before the
-increment of the sequence number on line~44, then releases the lock.
+increment of the sequence number on line~\lnref{inc}, then releases the lock.
+\end{lineref}
\QuickQuiz{}
What if something else serializes writers, so that the lock
@@ -255,7 +218,8 @@ increment of the sequence number on line~44, then releases the lock.
} \QuickQuizEnd
\QuickQuiz{}
- Why isn't \co{seq} on line~2 of
+ Why isn't \co{seq} on
+ line~\ref{ln:defer:seqlock:impl:typedef:seq} of
Listing~\ref{lst:defer:Sequence-Locking Implementation}
\co{unsigned} rather than \co{unsigned long}?
After all, if \co{unsigned} is good enough for the Linux
@@ -266,7 +230,9 @@ increment of the sequence number on line~44, then releases the lock.
it to ignore the following sequence of events:
\begin{enumerate}
\item Thread~0 executes \co{read_seqbegin()}, picking up
- \co{->seq} in line~16, noting that the value is even,
+ \co{->seq} in
+ line~\ref{ln:defer:seqlock:impl:read_seqbegin:fetch},
+ noting that the value is even,
and thus returning to the caller.
\item Thread~0 starts executing its read-side critical section,
but is then preempted for a long time.
--
2.7.4
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 4/6] defer: Employ new scheme for snippets of route_seqlock.c
2018-12-03 15:33 [PATCH 0/6] defer: Employ new scheme for code snippets (cont.) Akira Yokosawa
` (2 preceding siblings ...)
2018-12-03 15:39 ` [PATCH 3/6] defer: Employ new scheme for snippet of seqlock.h Akira Yokosawa
@ 2018-12-03 15:41 ` Akira Yokosawa
2018-12-03 15:42 ` [PATCH 5/6] defer: Employ new scheme for snippets in rcuintro and rcufundamental Akira Yokosawa
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Akira Yokosawa @ 2018-12-03 15:41 UTC (permalink / raw)
To: Paul E. McKenney; +Cc: perfbook, Akira Yokosawa
From 8232875eee86903a48912cacdd5d2268e5ff8d64 Mon Sep 17 00:00:00 2001
From: Akira Yokosawa <akiyks@gmail.com>
Date: Tue, 4 Dec 2018 00:16:17 +0900
Subject: [PATCH 4/6] defer: Employ new scheme for snippets of route_seqlock.c
Signed-off-by: Akira Yokosawa <akiyks@gmail.com>
---
CodeSamples/defer/route_seqlock.c | 56 ++++++++++---------
defer/seqlock.tex | 113 ++++++--------------------------------
2 files changed, 48 insertions(+), 121 deletions(-)
diff --git a/CodeSamples/defer/route_seqlock.c b/CodeSamples/defer/route_seqlock.c
index 8224682..969704b 100644
--- a/CodeSamples/defer/route_seqlock.c
+++ b/CodeSamples/defer/route_seqlock.c
@@ -23,19 +23,20 @@
#include "seqlock.h"
/* Route-table entry to be included in the routing list. */
+//\begin{snippet}[labelbase=ln:defer:route_seqlock:lookup,commandchars=\\\[\]]
struct route_entry {
struct route_entry *re_next;
unsigned long addr;
unsigned long iface;
- int re_freed;
+ int re_freed; //\lnlbl{struct:re_freed}
};
-
+ //\fcvexclude
struct route_entry route_list;
-DEFINE_SEQ_LOCK(sl);
+DEFINE_SEQ_LOCK(sl); //\lnlbl{struct:sl}
-/*
- * Look up a route entry, return the corresponding interface.
- */
+/* \fcvexclude
+ * Look up a route entry, return the corresponding interface. \fcvexclude
+ */ //\fcvexclude
unsigned long route_lookup(unsigned long addr)
{
struct route_entry *rep;
@@ -43,31 +44,33 @@ unsigned long route_lookup(unsigned long addr)
unsigned long ret;
unsigned long s;
-retry:
- s = read_seqbegin(&sl);
+retry: //\lnlbl{lookup:retry}
+ s = read_seqbegin(&sl); //\lnlbl{lookup:r_sqbegin}
repp = &route_list.re_next;
do {
rep = READ_ONCE(*repp);
if (rep == NULL) {
- if (read_seqretry(&sl, s))
- goto retry;
+ if (read_seqretry(&sl, s)) //\lnlbl{lookup:r_sqretry1}
+ goto retry; //\lnlbl{lookup:goto_retry1}
return ULONG_MAX;
}
-
- /* Advance to next. */
+ //\fcvexclude
+ /* Advance to next. */ //\fcvexclude
repp = &rep->re_next;
} while (rep->addr != addr);
- if (READ_ONCE(rep->re_freed))
- abort();
+ if (READ_ONCE(rep->re_freed)) //\lnlbl{lookup:chk_freed}
+ abort(); //\lnlbl{lookup:abort}
ret = rep->iface;
- if (read_seqretry(&sl, s))
- goto retry;
+ if (read_seqretry(&sl, s)) //\lnlbl{lookup:r_sqretry2}
+ goto retry; //\lnlbl{lookup:goto_retry2}
return ret;
}
+//\end{snippet}
/*
* Add an element to the route table.
*/
+//\begin{snippet}[labelbase=ln:defer:route_seqlock:add_del,commandchars=\\\[\]]
int route_add(unsigned long addr, unsigned long interface)
{
struct route_entry *rep;
@@ -77,23 +80,23 @@ int route_add(unsigned long addr, unsigned long interface)
return -ENOMEM;
rep->addr = addr;
rep->iface = interface;
- rep->re_freed = 0;
- write_seqlock(&sl);
+ rep->re_freed = 0; //\lnlbl{add:clr_freed}
+ write_seqlock(&sl); //\lnlbl{add:w_sqlock}
rep->re_next = route_list.re_next;
route_list.re_next = rep;
- write_sequnlock(&sl);
+ write_sequnlock(&sl); //\lnlbl{add:w_squnlock}
return 0;
}
-/*
- * Remove the specified element from the route table.
- */
+/* \fcvexclude
+ * Remove the specified element from the route table. \fcvexclude
+ */ //\fcvexclude
int route_del(unsigned long addr)
{
struct route_entry *rep;
struct route_entry **repp;
- write_seqlock(&sl);
+ write_seqlock(&sl); //\lnlbl{del:w_sqlock}
repp = &route_list.re_next;
for (;;) {
rep = *repp;
@@ -101,17 +104,18 @@ int route_del(unsigned long addr)
break;
if (rep->addr == addr) {
*repp = rep->re_next;
- write_sequnlock(&sl);
+ write_sequnlock(&sl); //\lnlbl{del:w_squnlock1}
smp_mb();
- rep->re_freed = 1;
+ rep->re_freed = 1; //\lnlbl{del:set_freed}
free(rep);
return 0;
}
repp = &rep->re_next;
}
- write_sequnlock(&sl);
+ write_sequnlock(&sl); //\lnlbl{del:w_squnlock2}
return -ENOENT;
}
+//\end{snippet}
/*
* Clear all elements from the route table.
diff --git a/defer/seqlock.tex b/defer/seqlock.tex
index 7fabc35..3eabf31 100644
--- a/defer/seqlock.tex
+++ b/defer/seqlock.tex
@@ -263,100 +263,13 @@ increment of the sequence number on line~\lnref{inc}, then releases the lock.
} \QuickQuizEnd
\begin{listing}[tbp]
-{ \scriptsize
-\begin{verbbox}
- 1 struct route_entry {
- 2 struct route_entry *re_next;
- 3 unsigned long addr;
- 4 unsigned long iface;
- 5 int re_freed;
- 6 };
- 7 struct route_entry route_list;
- 8 DEFINE_SEQ_LOCK(sl);
- 9
-10 unsigned long route_lookup(unsigned long addr)
-11 {
-12 struct route_entry *rep;
-13 struct route_entry **repp;
-14 unsigned long ret;
-15 unsigned long s;
-16
-17 retry:
-18 s = read_seqbegin(&sl);
-19 repp = &route_list.re_next;
-20 do {
-21 rep = READ_ONCE(*repp);
-22 if (rep == NULL) {
-23 if (read_seqretry(&sl, s))
-24 goto retry;
-25 return ULONG_MAX;
-26 }
-27 repp = &rep->re_next;
-28 } while (rep->addr != addr);
-29 if (READ_ONCE(rep->re_freed))
-30 abort();
-31 ret = rep->iface;
-32 if (read_seqretry(&sl, s))
-33 goto retry;
-34 return ret;
-35 }
-\end{verbbox}
-}
-\centering
-\theverbbox
+\input{CodeSamples/defer/route_seqlock@lookup.fcv}
\caption{Sequence-Locked Pre-BSD Routing Table Lookup (BUGGY!!!)}
\label{lst:defer:Sequence-Locked Pre-BSD Routing Table Lookup}
\end{listing}
\begin{listing}[tbp]
-{ \scriptsize
-\begin{verbbox}
- 1 int route_add(unsigned long addr,
- 2 unsigned long interface)
- 3 {
- 4 struct route_entry *rep;
- 5
- 6 rep = malloc(sizeof(*rep));
- 7 if (!rep)
- 8 return -ENOMEM;
- 9 rep->addr = addr;
-10 rep->iface = interface;
-11 rep->re_freed = 0;
-12 write_seqlock(&sl);
-13 rep->re_next = route_list.re_next;
-14 route_list.re_next = rep;
-15 write_sequnlock(&sl);
-16 return 0;
-17 }
-18
-19 int route_del(unsigned long addr)
-20 {
-21 struct route_entry *rep;
-22 struct route_entry **repp;
-23
-24 write_seqlock(&sl);
-25 repp = &route_list.re_next;
-26 for (;;) {
-27 rep = *repp;
-28 if (rep == NULL)
-29 break;
-30 if (rep->addr == addr) {
-31 *repp = rep->re_next;
-32 write_sequnlock(&sl);
-33 smp_mb();
-34 rep->re_freed = 1;
-35 free(rep);
-36 return 0;
-37 }
-38 repp = &rep->re_next;
-39 }
-40 write_sequnlock(&sl);
-41 return -ENOENT;
-42 }
-\end{verbbox}
-}
-\centering
-\theverbbox
+\input{CodeSamples/defer/route_seqlock@add_del.fcv}
\caption{Sequence-Locked Pre-BSD Routing Table Add/Delete (BUGGY!!!)}
\label{lst:defer:Sequence-Locked Pre-BSD Routing Table Add/Delete}
\end{listing}
@@ -370,19 +283,29 @@ shows \co{route_add()} and \co{route_del()} (\path{route_seqlock.c}).
This implementation is once again similar to its counterparts in earlier
sections, so only the differences will be highlighted.
+\begin{lineref}[ln:defer:route_seqlock:lookup]
In
Listing~\ref{lst:defer:Sequence-Locked Pre-BSD Routing Table Lookup},
-line~5 adds \co{->re_freed}, which is checked on lines~29 and~30.
-Line~8 adds a sequence lock, which is used by \co{route_lookup()}
-on lines~18, 23, and~32, with lines~24 and~33 branching back to
-the \co{retry} label on line~17.
+line~\lnref{struct:re_freed} adds \co{->re_freed}, which is checked on
+lines~\lnref{lookup:chk_freed} and~\lnref{lookup:abort}.
+Line~\lnref{struct:sl} adds a sequence lock, which is used by \co{route_lookup()}
+\end{lineref}
+\begin{lineref}[ln:defer:route_seqlock:lookup:lookup]
+on lines~\lnref{r_sqbegin}, \lnref{r_sqretry1}, and~\lnref{r_sqretry2},
+with lines~\lnref{goto_retry1} and~\lnref{goto_retry2} branching back to
+the \co{retry} label on line~\lnref{retry}.
The effect is to retry any lookup that runs concurrently with an update.
+\end{lineref}
+\begin{lineref}[ln:defer:route_seqlock:add_del]
In
Listing~\ref{lst:defer:Sequence-Locked Pre-BSD Routing Table Add/Delete},
-lines~12, 15, 24, and~40 acquire and release the sequence lock,
-while lines~11, 33, and~44 handle \co{->re_freed}.
+lines~\lnref{add:w_sqlock}, \lnref{add:w_squnlock}, \lnref{del:w_sqlock},
+\lnref{del:w_squnlock1}, and~\lnref{del:w_squnlock2}
+acquire and release the sequence lock,
+while lines~\lnref{add:clr_freed} and~\lnref{del:set_freed} handle \co{->re_freed}.
This implementation is therefore quite straightforward.
+\end{lineref}
\begin{figure}[tb]
\centering
--
2.7.4
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 5/6] defer: Employ new scheme for snippets in rcuintro and rcufundamental
2018-12-03 15:33 [PATCH 0/6] defer: Employ new scheme for code snippets (cont.) Akira Yokosawa
` (3 preceding siblings ...)
2018-12-03 15:41 ` [PATCH 4/6] defer: Employ new scheme for snippets of route_seqlock.c Akira Yokosawa
@ 2018-12-03 15:42 ` Akira Yokosawa
2018-12-03 15:44 ` [PATCH 6/6] defer: Employ new scheme for snippets of route_rcu.c Akira Yokosawa
2018-12-03 17:23 ` [PATCH 0/6] defer: Employ new scheme for code snippets (cont.) Paul E. McKenney
6 siblings, 0 replies; 8+ messages in thread
From: Akira Yokosawa @ 2018-12-03 15:42 UTC (permalink / raw)
To: Paul E. McKenney; +Cc: perfbook, Akira Yokosawa
From 7bd805e63060278c9222b39c8c238227835f04a4 Mon Sep 17 00:00:00 2001
From: Akira Yokosawa <akiyks@gmail.com>
Date: Tue, 4 Dec 2018 00:16:42 +0900
Subject: [PATCH 5/6] defer: Employ new scheme for snippets in rcuintro and rcufundamental
Signed-off-by: Akira Yokosawa <akiyks@gmail.com>
---
defer/rcufundamental.tex | 381 ++++++++++++++++++++++-------------------------
defer/rcuintro.tex | 13 +-
2 files changed, 179 insertions(+), 215 deletions(-)
diff --git a/defer/rcufundamental.tex b/defer/rcufundamental.tex
index 2660e7d..3a5fad2 100644
--- a/defer/rcufundamental.tex
+++ b/defer/rcufundamental.tex
@@ -69,26 +69,22 @@ summarizes RCU fundamentals.
\label{sec:defer:Publish-Subscribe Mechanism}
\begin{listing}[tbp]
-{ \scriptsize
-\begin{verbbox}
- 1 struct foo {
- 2 int a;
- 3 int b;
- 4 int c;
- 5 };
- 6 struct foo *gp = NULL;
- 7
- 8 /* . . . */
- 9
- 10 p = kmalloc(sizeof(*p), GFP_KERNEL);
- 11 p->a = 1;
- 12 p->b = 2;
- 13 p->c = 3;
- 14 gp = p;
-\end{verbbox}
-}
-\centering
-\theverbbox
+\begin{VerbatimL}
+struct foo {
+ int a;
+ int b;
+ int c;
+};
+struct foo *gp = NULL;
+
+/* . . . */
+
+p = kmalloc(sizeof(*p), GFP_KERNEL);
+p->a = 1;
+p->b = 2;
+p->c = 3;
+gp = p;
+\end{VerbatimL}
\caption{Data Structure Publication (Unsafe)}
\label{lst:defer:Data Structure Publication (Unsafe)}
\end{listing}
@@ -116,17 +112,12 @@ We therefore encapsulate them into a primitive
\co{rcu_assign_pointer()} that has publication semantics.
The last four lines would then be as follows:
-\vspace{5pt}
-\begin{minipage}[t]{\columnwidth}
-\scriptsize
-\begin{verbatim}
- 1 p->a = 1;
- 2 p->b = 2;
- 3 p->c = 3;
- 4 rcu_assign_pointer(gp, p);
-\end{verbatim}
-\end{minipage}
-\vspace{5pt}
+\begin{VerbatimN}
+p->a = 1;
+p->b = 2;
+p->c = 3;
+rcu_assign_pointer(gp, p);
+\end{VerbatimN}
The \co{rcu_assign_pointer()}
would \emph{publish} the new structure, forcing both the compiler
@@ -137,17 +128,12 @@ However, it is not sufficient to only enforce ordering at the
updater, as the reader must enforce proper ordering as well.
Consider for example the following code fragment:
-\vspace{5pt}
-\begin{minipage}[t]{\columnwidth}
-\scriptsize
-\begin{verbatim}
- 1 p = gp;
- 2 if (p != NULL) {
- 3 do_something_with(p->a, p->b, p->c);
- 4 }
-\end{verbatim}
-\end{minipage}
-\vspace{5pt}
+\begin{VerbatimN}
+p = gp;
+if (p != NULL) {
+ do_something_with(p->a, p->b, p->c);
+}
+\end{VerbatimN}
Although this code fragment might well seem immune to misordering,
unfortunately, the
@@ -177,19 +163,14 @@ directives are required for this purpose:\footnote{
\co{memory_order_acquire}, thus emitting a needless memory-barrier
instruction on weakly ordered systems.)}
-\vspace{5pt}
-\begin{minipage}[t]{\columnwidth}
-\scriptsize
-\begin{verbatim}
- 1 rcu_read_lock();
- 2 p = rcu_dereference(gp);
- 3 if (p != NULL) {
- 4 do_something_with(p->a, p->b, p->c);
- 5 }
- 6 rcu_read_unlock();
-\end{verbatim}
-\end{minipage}
-\vspace{5pt}
+\begin{VerbatimN}
+rcu_read_lock();
+p = rcu_dereference(gp);
+if (p != NULL) {
+ do_something_with(p->a, p->b, p->c);
+}
+rcu_read_unlock();
+\end{VerbatimN}
The \co{rcu_dereference()} primitive can thus be thought of
as \emph{subscribing} to a given value of the specified pointer,
@@ -240,27 +221,25 @@ Figure~\ref{fig:defer:Linux Linked List Abbreviated},
which shows only the non-header (blue) elements.
\begin{listing}[tbp]
-{ \scriptsize
-\begin{verbbox}
- 1 struct foo {
- 2 struct list_head *list;
- 3 int a;
- 4 int b;
- 5 int c;
- 6 };
- 7 LIST_HEAD(head);
- 8
- 9 /* . . . */
- 10
- 11 p = kmalloc(sizeof(*p), GFP_KERNEL);
- 12 p->a = 1;
- 13 p->b = 2;
- 14 p->c = 3;
- 15 list_add_rcu(&p->list, &head);
-\end{verbbox}
-}
-\centering
-\theverbbox
+\begin{linelabel}[ln:defer:RCU Data Structure Publication]
+\begin{VerbatimL}[commandchars=\\\[\]]
+struct foo {
+ struct list_head *list;
+ int a;
+ int b;
+ int c;
+};
+LIST_HEAD(head);
+
+/* . . . */
+
+p = kmalloc(sizeof(*p), GFP_KERNEL);
+p->a = 1;
+p->b = 2;
+p->c = 3;
+list_add_rcu(&p->list, &head); \lnlbl[add_rcu]
+\end{VerbatimL}
+\end{linelabel}
\caption{RCU Data Structure Publication}
\label{lst:defer:RCU Data Structure Publication}
\end{listing}
@@ -269,7 +248,8 @@ Adapting the pointer-publish example for the linked list results in
the code shown in
Listing~\ref{lst:defer:RCU Data Structure Publication}.
-Line~15 must be protected by some synchronization mechanism (most
+Line~\ref{ln:defer:RCU Data Structure Publication:add_rcu}
+must be protected by some synchronization mechanism (most
commonly some sort of lock) to prevent multiple \co{list_add_rcu()}
instances from executing concurrently.
However, such synchronization does not prevent this \co{list_add()}
@@ -277,18 +257,13 @@ instance from executing concurrently with RCU readers.
Subscribing to an RCU-protected list is straightforward:
-\vspace{5pt}
-\begin{minipage}[t]{\columnwidth}
-\scriptsize
-\begin{verbatim}
- 1 rcu_read_lock();
- 2 list_for_each_entry_rcu(p, head, list) {
- 3 do_something_with(p->a, p->b, p->c);
- 4 }
- 5 rcu_read_unlock();
-\end{verbatim}
-\end{minipage}
-\vspace{5pt}
+\begin{VerbatimN}
+rcu_read_lock();
+list_for_each_entry_rcu(p, head, list) {
+ do_something_with(p->a, p->b, p->c);
+}
+rcu_read_unlock();
+\end{VerbatimN}
The \co{list_add_rcu()} primitive publishes an entry, inserting it at
the head of the specified list, guaranteeing that the corresponding
@@ -331,27 +306,25 @@ in the same way lists are, as shown in
Figure~\ref{fig:defer:Linux Linked List Abbreviated}.
\begin{listing}[tbp]
-{ \scriptsize
-\begin{verbbox}
- 1 struct foo {
- 2 struct hlist_node *list;
- 3 int a;
- 4 int b;
- 5 int c;
- 6 };
- 7 HLIST_HEAD(head);
- 8
- 9 /* . . . */
- 10
- 11 p = kmalloc(sizeof(*p), GFP_KERNEL);
- 12 p->a = 1;
- 13 p->b = 2;
- 14 p->c = 3;
- 15 hlist_add_head_rcu(&p->list, &head);
-\end{verbbox}
-}
-\centering
-\theverbbox
+\begin{linelabel}[ln:defer:RCU hlist Publication]
+\begin{VerbatimL}[commandchars=\\\[\]]
+struct foo {
+ struct hlist_node *list;
+ int a;
+ int b;
+ int c;
+};
+HLIST_HEAD(head);
+
+/* . . . */
+
+p = kmalloc(sizeof(*p), GFP_KERNEL);
+p->a = 1;
+p->b = 2;
+p->c = 3;
+hlist_add_head_rcu(&p->list, &head); \lnlbl[add_head]
+\end{VerbatimL}
+\end{linelabel}
\caption{RCU {\tt hlist} Publication}
\label{lst:defer:RCU hlist Publication}
\end{listing}
@@ -360,24 +333,20 @@ Publishing a new element to an RCU-protected hlist is quite similar
to doing so for the circular list,
as shown in Listing~\ref{lst:defer:RCU hlist Publication}.
-As before, line~15 must be protected by some sort of synchronization
+As before, line~\ref{ln:defer:RCU hlist Publication:add_head}
+must be protected by some sort of synchronization
mechanism, for example, a lock.
Subscribing to an RCU-protected hlist is also similar to the
circular list:
-\vspace{5pt}
-\begin{minipage}[t]{\columnwidth}
-\scriptsize
-\begin{verbatim}
- 1 rcu_read_lock();
- 2 hlist_for_each_entry_rcu(p, head, list) {
- 3 do_something_with(p->a, p->b, p->c);
- 4 }
- 5 rcu_read_unlock();
-\end{verbatim}
-\end{minipage}
-\vspace{5pt}
+\begin{VerbatimN}
+rcu_read_lock();
+hlist_for_each_entry_rcu(p, head, list) {
+ do_something_with(p->a, p->b, p->c);
+}
+rcu_read_unlock();
+\end{VerbatimN}
\begin{table*}[tb]
\renewcommand*{\arraystretch}{1.2}
@@ -488,33 +457,31 @@ RCU to wait for readers:
\end{enumerate}
\begin{listing}[tbp]
-{ \scriptsize
-\begin{verbbox}
- 1 struct foo {
- 2 struct list_head *list;
- 3 int a;
- 4 int b;
- 5 int c;
- 6 };
- 7 LIST_HEAD(head);
- 8
- 9 /* . . . */
- 10
- 11 p = search(head, key);
- 12 if (p == NULL) {
- 13 /* Take appropriate action, unlock, & return. */
- 14 }
- 15 q = kmalloc(sizeof(*p), GFP_KERNEL);
- 16 *q = *p;
- 17 q->b = 2;
- 18 q->c = 3;
- 19 list_replace_rcu(&p->list, &q->list);
- 20 synchronize_rcu();
- 21 kfree(p);
-\end{verbbox}
+\begin{linelabel}[ln:defer:Canonical RCU Replacement Example]
+\begin{VerbatimL}[commandchars=\\\[\]]
+struct foo {
+ struct list_head *list;
+ int a;
+ int b;
+ int c;
+};
+LIST_HEAD(head);
+
+/* . . . */
+
+p = search(head, key); \lnlbl[search]
+if (p == NULL) {
+ /* Take appropriate action, unlock, & return. */
}
-\centering
-\theverbbox
+q = kmalloc(sizeof(*p), GFP_KERNEL);
+*q = *p; \lnlbl[copy]
+q->b = 2; \lnlbl[update]
+q->c = 3;
+list_replace_rcu(&p->list, &q->list); \lnlbl[replace]
+synchronize_rcu(); \lnlbl[sync_rcu]
+kfree(p); \lnlbl[kfree]
+\end{VerbatimL}
+\end{linelabel}
\caption{Canonical RCU Replacement Example}
\label{lst:defer:Canonical RCU Replacement Example}
\end{listing}
@@ -524,10 +491,15 @@ Listing~\ref{lst:defer:Canonical RCU Replacement Example},
adapted from those in Section~\ref{sec:defer:Publish-Subscribe Mechanism},
demonstrates this process, with field \co{a} being the search key.
-Lines~19, 20, and~21 implement the three steps called out above.
-Lines~16-19 gives RCU (``read-copy update'') its name: while permitting
-concurrent \emph{reads}, line~16 \emph{copies} and lines~17-19
+\begin{lineref}[ln:defer:Canonical RCU Replacement Example]
+Lines~\lnref{replace}, \lnref{sync_rcu}, and~\lnref{kfree}
+implement the three steps called out above.
+Lines~\lnref{copy}-\lnref{replace}
+gives RCU (``read-copy update'') its name: while permitting
+concurrent \emph{reads}, line~\lnref{copy} \emph{copies} and
+lines~\lnref{update}-\lnref{replace}
do an \emph{update}.
+\end{lineref}
As discussed in Section~\ref{sec:defer:Introduction to RCU},
the \co{synchronize_rcu()} primitive can be quite simple
@@ -563,24 +535,24 @@ We can now revisit the deletion example from
Section~\ref{sec:defer:Introduction to RCU},
but now with the benefit of a firm understanding of the fundamental
concepts underlying RCU.
+\begin{lineref}[ln:defer:Canonical RCU Replacement Example]
To begin this new version of the deletion example,
-we will modify lines~11-21 in
+we will modify
+lines~\lnref{search}-\lnref{kfree} in
Listing~\ref{lst:defer:Canonical RCU Replacement Example}
to read as follows:
-
-\vspace{5pt}
-\begin{minipage}[t]{\columnwidth}
-\scriptsize
-\begin{verbatim}
- 1 p = search(head, key);
- 2 if (p != NULL) {
- 3 list_del_rcu(&p->list);
- 4 synchronize_rcu();
- 5 kfree(p);
- 6 }
-\end{verbatim}
-\end{minipage}
-\vspace{5pt}
+\end{lineref}
+
+\begin{linelabel}[ln:defer:RCU Deletion From Linked List]
+\begin{VerbatimN}[commandchars=\\\[\]]
+p = search(head, key);
+if (p != NULL) {
+ list_del_rcu(&p->list); \lnlbl[del_rcu]
+ synchronize_rcu(); \lnlbl[sync_rcu]
+ kfree(p);
+}
+\end{VerbatimN}
+\end{linelabel}
\begin{figure}[tb]
\centering
@@ -601,8 +573,9 @@ Please note that
we have omitted the backwards pointers and the link from the tail
of the list to the head for clarity.
+\begin{lineref}[ln:defer:RCU Deletion From Linked List]
After the \co{list_del_rcu()} on
-line~3 has completed, the \co{5,6,7}~element
+line~\lnref{del_rcu} has completed, the \co{5,6,7}~element
has been removed from the list, as shown in the second row of
Figure~\ref{fig:defer:RCU Deletion From Linked List}.
Since readers do not synchronize directly with updaters,
@@ -626,12 +599,13 @@ element~\co{5,6,7} after exiting from their RCU read-side
critical sections.
Therefore,
once the \co{synchronize_rcu()} on
-line~4 completes, so that all pre-existing readers are
+line~\lnref{sync_rcu} completes, so that all pre-existing readers are
guaranteed to have completed,
there can be no more readers referencing this
element, as indicated by its green shading on the third row of
Figure~\ref{fig:defer:RCU Deletion From Linked List}.
We are thus back to a single version of the list.
+\end{lineref}
At this point, the \co{5,6,7}~element may safely be
freed, as shown on the final row of
@@ -648,20 +622,17 @@ here are the last few lines of the
example shown in
Listing~\ref{lst:defer:Canonical RCU Replacement Example}:
-\vspace{5pt}
-\begin{minipage}[t]{\columnwidth}
-\scriptsize
-\begin{verbatim}
- 1 q = kmalloc(sizeof(*p), GFP_KERNEL);
- 2 *q = *p;
- 3 q->b = 2;
- 4 q->c = 3;
- 5 list_replace_rcu(&p->list, &q->list);
- 6 synchronize_rcu();
- 7 kfree(p);
-\end{verbatim}
-\end{minipage}
-\vspace{5pt}
+\begin{linelabel}[ln:defer:Canonical RCU Replacement Example (2nd)]
+\begin{VerbatimN}[commandchars=\\\[\],firstnumber=15]
+q = kmalloc(sizeof(*p), GFP_KERNEL); \lnlbl[kmalloc]
+*q = *p; \lnlbl[copy]
+q->b = 2; \lnlbl[update1]
+q->c = 3; \lnlbl[update2]
+list_replace_rcu(&p->list, &q->list); \lnlbl[replace]
+synchronize_rcu(); \lnlbl[sync_rcu]
+kfree(p); \lnlbl[kfree]
+\end{VerbatimN}
+\end{linelabel}
\begin{figure}[tbp]
\centering
@@ -689,24 +660,26 @@ The following text describes how to replace the \co{5,6,7} element
with \co{5,2,3} in such a way that any given reader sees one of these
two values.
-Line~1 \co{kmalloc()}s a replacement element, as follows,
+\begin{lineref}[ln:defer:Canonical RCU Replacement Example (2nd)]
+Line~\lnref{kmalloc} \co{kmalloc()}s a replacement element, as follows,
resulting in the state as shown in the second row of
Figure~\ref{fig:defer:RCU Replacement in Linked List}.
At this point, no reader can hold a reference to the newly allocated
element (as indicated by its green shading), and it is uninitialized
(as indicated by the question marks).
-Line~2 copies the old element to the new one, resulting in the
+Line~\lnref{copy} copies the old element to the new one, resulting in the
state as shown in the third row of
Figure~\ref{fig:defer:RCU Replacement in Linked List}.
The newly allocated element still cannot be referenced by readers, but
it is now initialized.
-Line~3 updates \co{q->b} to the value ``2'', and
-line~4 updates \co{q->c} to the value ``3'', as shown on the fourth row of
+Line~\lnref{update1} updates \co{q->b} to the value ``2'', and
+line~\lnref{update2} updates \co{q->c} to the value ``3'',
+as shown on the fourth row of
Figure~\ref{fig:defer:RCU Replacement in Linked List}.
-Now, line~5 does the replacement, so that the new element is
+Now, line~\lnref{replace} does the replacement, so that the new element is
finally visible to readers, and hence is shaded red, as shown on
the fifth row of
Figure~\ref{fig:defer:RCU Replacement in Linked List}.
@@ -716,7 +689,7 @@ therefore now shaded yellow), but
new readers will instead see the \co{5,2,3} element.
But any given reader is guaranteed to see some well-defined list.
-After the \co{synchronize_rcu()} on line~6 returns,
+After the \co{synchronize_rcu()} on line~\lnref{sync_rcu} returns,
a grace period will have elapsed, and so all reads that started before the
\co{list_replace_rcu()} will have completed.
In particular, any readers that might have been holding references
@@ -729,9 +702,10 @@ Figure~\ref{fig:defer:RCU Replacement in Linked List}.
As far as the readers are concerned, we are back to having a single version
of the list, but with the new element in place of the old.
-After the \co{kfree()} on line~7 completes, the list will
+After the \co{kfree()} on line~\lnref{kfree} completes, the list will
appear as shown on the final row of
Figure~\ref{fig:defer:RCU Replacement in Linked List}.
+\end{lineref}
Despite the fact that RCU was named after the replacement case,
the vast majority of RCU usage within the Linux kernel relies on
@@ -753,23 +727,18 @@ versions of the list active at a given time.
Listing~\ref{lst:defer:Concurrent RCU Deletion}.
\begin{listing}[htbp]
-\scriptsize
-{
-\begin{verbbox}
- 1 spin_lock(&mylock);
- 2 p = search(head, key);
- 3 if (p == NULL)
- 4 spin_unlock(&mylock);
- 5 else {
- 6 list_del_rcu(&p->list);
- 7 spin_unlock(&mylock);
- 8 synchronize_rcu();
- 9 kfree(p);
- 10 }
-\end{verbbox}
+\begin{VerbatimL}
+spin_lock(&mylock);
+p = search(head, key);
+if (p == NULL)
+ spin_unlock(&mylock);
+else {
+ list_del_rcu(&p->list);
+ spin_unlock(&mylock);
+ synchronize_rcu();
+ kfree(p);
}
-\centering
-\theverbbox
+\end{VerbatimL}
\caption{Concurrent RCU Deletion}
\label{lst:defer:Concurrent RCU Deletion}
\end{listing}
diff --git a/defer/rcuintro.tex b/defer/rcuintro.tex
index 6e909c9..3259a19 100644
--- a/defer/rcuintro.tex
+++ b/defer/rcuintro.tex
@@ -146,15 +146,10 @@ with time advancing from the top of the figure to the bottom.
Although production-quality implementations of this approach can be
quite complex, a toy implementation is exceedingly simple:
-\vspace{5pt}
-\begin{minipage}[t]{\columnwidth}
-\scriptsize
-\begin{verbatim}
- 1 for_each_online_cpu(cpu)
- 2 run_on(cpu);
-\end{verbatim}
-\end{minipage}
-\vspace{5pt}
+\begin{VerbatimN}
+for_each_online_cpu(cpu)
+ run_on(cpu);
+\end{VerbatimN}
The \co{for_each_online_cpu()} primitive iterates over all CPUs, and
the \co{run_on()} function causes the current thread to execute on the
--
2.7.4
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 6/6] defer: Employ new scheme for snippets of route_rcu.c
2018-12-03 15:33 [PATCH 0/6] defer: Employ new scheme for code snippets (cont.) Akira Yokosawa
` (4 preceding siblings ...)
2018-12-03 15:42 ` [PATCH 5/6] defer: Employ new scheme for snippets in rcuintro and rcufundamental Akira Yokosawa
@ 2018-12-03 15:44 ` Akira Yokosawa
2018-12-03 17:23 ` [PATCH 0/6] defer: Employ new scheme for code snippets (cont.) Paul E. McKenney
6 siblings, 0 replies; 8+ messages in thread
From: Akira Yokosawa @ 2018-12-03 15:44 UTC (permalink / raw)
To: Paul E. McKenney; +Cc: perfbook, Akira Yokosawa
From 955dcb167cc6d8136c693ad193e0cd711a495150 Mon Sep 17 00:00:00 2001
From: Akira Yokosawa <akiyks@gmail.com>
Date: Tue, 4 Dec 2018 00:17:12 +0900
Subject: [PATCH 6/6] defer: Employ new scheme for snippets of route_rcu.c
Also convert inline snippets in rcuusage.tex.
Signed-off-by: Akira Yokosawa <akiyks@gmail.com>
---
CodeSamples/defer/route_rcu.c | 75 +++---
defer/rcuusage.tex | 523 +++++++++++++++++-------------------------
2 files changed, 256 insertions(+), 342 deletions(-)
diff --git a/CodeSamples/defer/route_rcu.c b/CodeSamples/defer/route_rcu.c
index 1fd69ea..168eead 100644
--- a/CodeSamples/defer/route_rcu.c
+++ b/CodeSamples/defer/route_rcu.c
@@ -31,48 +31,51 @@
#include "../api.h"
/* Route-table entry to be included in the routing list. */
+//\begin{snippet}[labelbase=ln:defer:route_rcu:lookup,commandchars=\\\[\]]
struct route_entry {
- struct rcu_head rh;
+ struct rcu_head rh; //\lnlbl{rh}
struct cds_list_head re_next;
unsigned long addr;
unsigned long iface;
- int re_freed;
+ int re_freed; //\lnlbl{re_freed}
};
-
+ //\fcvexclude
CDS_LIST_HEAD(route_list);
DEFINE_SPINLOCK(routelock);
-static void re_free(struct route_entry *rep)
-{
- WRITE_ONCE(rep->re_freed, 1);
- free(rep);
-}
-
-/*
- * Look up a route entry, return the corresponding interface.
- */
+static void re_free(struct route_entry *rep) //\fcvexclude
+{ //\fcvexclude
+ WRITE_ONCE(rep->re_freed, 1); //\fcvexclude
+ free(rep); //\fcvexclude
+} //\fcvexclude
+ //\fcvexclude
+/* \fcvexclude
+ * Look up a route entry, return the corresponding interface. \fcvexclude
+ */ //\fcvexclude
unsigned long route_lookup(unsigned long addr)
{
struct route_entry *rep;
unsigned long ret;
- rcu_read_lock();
+ rcu_read_lock(); //\lnlbl{lock}
cds_list_for_each_entry_rcu(rep, &route_list, re_next) {
if (rep->addr == addr) {
ret = rep->iface;
- if (READ_ONCE(rep->re_freed))
- abort();
- rcu_read_unlock();
+ if (READ_ONCE(rep->re_freed)) //\lnlbl{chk_freed}
+ abort(); //\lnlbl{abort}
+ rcu_read_unlock(); //\lnlbl{unlock1}
return ret;
}
}
- rcu_read_unlock();
+ rcu_read_unlock(); //\lnlbl{unlock2}
return ULONG_MAX;
}
+//\end{snippet}
/*
* Add an element to the route table.
*/
+//\begin{snippet}[labelbase=ln:defer:route_rcu:add_del,commandchars=\\\[\]]
int route_add(unsigned long addr, unsigned long interface)
{
struct route_entry *rep;
@@ -83,38 +86,46 @@ int route_add(unsigned long addr, unsigned long interface)
rep->addr = addr;
rep->iface = interface;
rep->re_freed = 0;
- spin_lock(&routelock);
- cds_list_add_rcu(&rep->re_next, &route_list);
- spin_unlock(&routelock);
+ spin_lock(&routelock); //\lnlbl{add:lock}
+ cds_list_add_rcu(&rep->re_next, &route_list); //\lnlbl{add:add_rcu}
+ spin_unlock(&routelock); //\lnlbl{add:unlock}
return 0;
}
-static void route_cb(struct rcu_head *rhp)
+static void route_cb(struct rcu_head *rhp) //\lnlbl{cb:b}
{
- struct route_entry *rep = container_of(rhp, struct route_entry, rh);
+ struct route_entry *rep = container_of(rhp, struct route_entry, rh); //\fcvexclude
+ //\fcvexclude
+ re_free(rep); //\fcvexclude
+/* --- Alternative code for code snippet: begin --- \fcvexclude
+ struct route_entry *rep;
- re_free(rep);
-}
+ rep = container_of(rhp, struct route_entry, rh);
+ WRITE_ONCE(rep->re_freed, 1);
+ free(rep);
+ --- Alternative code for code snippet: end --- */ //\fcvexclude
+} //\lnlbl{cb:e}
-/*
- * Remove the specified element from the route table.
- */
+/* \fcvexclude
+ * Remove the specified element from the route table. \fcvexclude
+ */ //\fcvexclude
int route_del(unsigned long addr)
{
struct route_entry *rep;
- spin_lock(&routelock);
+ spin_lock(&routelock); //\lnlbl{del:lock}
cds_list_for_each_entry(rep, &route_list, re_next) {
if (rep->addr == addr) {
- cds_list_del_rcu(&rep->re_next);
- spin_unlock(&routelock);
- call_rcu(&rep->rh, route_cb);
+ cds_list_del_rcu(&rep->re_next);//\lnlbl{del:del_rcu}
+ spin_unlock(&routelock); //\lnlbl{del:unlock1}
+ call_rcu(&rep->rh, route_cb); //\lnlbl{del:call_rcu}
return 0;
}
}
- spin_unlock(&routelock);
+ spin_unlock(&routelock); //\lnlbl{del:unlock2}
return -ENOENT;
}
+//\end{snippet}
/*
* Clear all elements from the route table.
diff --git a/defer/rcuusage.tex b/defer/rcuusage.tex
index ed0c8be..a6772a4 100644
--- a/defer/rcuusage.tex
+++ b/defer/rcuusage.tex
@@ -44,95 +44,13 @@ Section~\ref{sec:defer:RCU Usage Summary} provides a summary.
\label{sec:defer:RCU for Pre-BSD Routing}
\begin{listing}[tbp]
-{ \scriptsize
-\begin{verbbox}
- 1 struct route_entry {
- 2 struct rcu_head rh;
- 3 struct cds_list_head re_next;
- 4 unsigned long addr;
- 5 unsigned long iface;
- 6 int re_freed;
- 7 };
- 8 CDS_LIST_HEAD(route_list);
- 9 DEFINE_SPINLOCK(routelock);
-10
-11 unsigned long route_lookup(unsigned long addr)
-12 {
-13 struct route_entry *rep;
-14 unsigned long ret;
-15
-16 rcu_read_lock();
-17 cds_list_for_each_entry_rcu(rep, &route_list,
-18 re_next) {
-19 if (rep->addr == addr) {
-20 ret = rep->iface;
-21 if (READ_ONCE(rep->re_freed))
-22 abort();
-23 rcu_read_unlock();
-24 return ret;
-25 }
-26 }
-27 rcu_read_unlock();
-28 return ULONG_MAX;
-29 }
-\end{verbbox}
-}
-\centering
-\theverbbox
+\input{CodeSamples/defer/route_rcu@lookup.fcv}
\caption{RCU Pre-BSD Routing Table Lookup}
\label{lst:defer:RCU Pre-BSD Routing Table Lookup}
\end{listing}
\begin{listing}[tbp]
-{ \scriptsize
-\begin{verbbox}
- 1 int route_add(unsigned long addr,
- 2 unsigned long interface)
- 3 {
- 4 struct route_entry *rep;
- 5
- 6 rep = malloc(sizeof(*rep));
- 7 if (!rep)
- 8 return -ENOMEM;
- 9 rep->addr = addr;
-10 rep->iface = interface;
-11 rep->re_freed = 0;
-12 spin_lock(&routelock);
-13 cds_list_add_rcu(&rep->re_next, &route_list);
-14 spin_unlock(&routelock);
-15 return 0;
-16 }
-17
-18 static void route_cb(struct rcu_head *rhp)
-19 {
-20 struct route_entry *rep;
-21
-22 rep = container_of(rhp, struct route_entry, rh);
-23 WRITE_ONCE(rep->re_freed, 1);
-24 free(rep);
-25 }
-26
-27 int route_del(unsigned long addr)
-28 {
-29 struct route_entry *rep;
-30
-31 spin_lock(&routelock);
-32 cds_list_for_each_entry(rep, &route_list,
-33 re_next) {
-34 if (rep->addr == addr) {
-35 cds_list_del_rcu(&rep->re_next);
-36 spin_unlock(&routelock);
-37 call_rcu(&rep->rh, route_cb);
-38 return 0;
-39 }
-40 }
-41 spin_unlock(&routelock);
-42 return -ENOENT;
-43 }
-\end{verbbox}
-}
-\centering
-\theverbbox
+\input{CodeSamples/defer/route_rcu@add_del.fcv}
\caption{RCU Pre-BSD Routing Table Add/Delete}
\label{lst:defer:RCU Pre-BSD Routing Table Add/Delete}
\end{listing}
@@ -144,17 +62,24 @@ show code for an RCU-protected Pre-BSD routing table
The former shows data structures and \co{route_lookup()},
and the latter shows \co{route_add()} and \co{route_del()}.
+\begin{lineref}[ln:defer:route_rcu:lookup]
In Listing~\ref{lst:defer:RCU Pre-BSD Routing Table Lookup},
-line~2 adds the \co{->rh} field used by RCU reclamation,
-line~6 adds the \co{->re_freed} use-after-free-check field,
-lines~16, 17, 23, and~27 add RCU read-side protection,
-and lines~21 and~22 add the use-after-free check.
+line~\lnref{rh} adds the \co{->rh} field used by RCU reclamation,
+line~\lnref{re_freed} adds the \co{->re_freed} use-after-free-check field,
+lines~\lnref{lock}, \lnref{unlock1}, and~\lnref{unlock2}
+add RCU read-side protection,
+and lines~\lnref{chk_freed} and~\lnref{abort} add the use-after-free check.
+\end{lineref}
+\begin{lineref}[ln:defer:route_rcu:add_del]
In Listing~\ref{lst:defer:RCU Pre-BSD Routing Table Add/Delete},
-lines~12, 14, 31, 36, and~41 add update-side locking,
-lines~13 and~35 add RCU update-side protection,
-line~37 causes \co{route_cb()} to be invoked after a grace period elapses,
-and lines~18-25 define \co{route_cb()}.
+lines~\lnref{add:lock}, \lnref{add:unlock}, \lnref{del:lock},
+\lnref{del:unlock1}, and~\lnref{del:unlock2} add update-side locking,
+lines~\lnref{add:add_rcu} and~\lnref{del:del_rcu} add RCU update-side protection,
+line~\lnref{del:call_rcu} causes \co{route_cb()} to be invoked after
+a grace period elapses,
+and lines~\lnref{cb:b}-\lnref{cb:e} define \co{route_cb()}.
This is minimal added code for a working concurrent implementation.
+\end{lineref}
\begin{figure}[tb]
\centering
@@ -279,42 +204,27 @@ Figure~\ref{fig:defer:Performance Advantage of RCU Over Reader-Writer Locking}.
First, consider that the inner loop used to
take this measurement is as follows:
-\vspace{5pt}
-\begin{minipage}[t]{\columnwidth}
-\scriptsize
-\begin{verbatim}
- 1 for (i = 0; i < CSCOUNT_SCALE; i++) {
- 2 rcu_read_lock();
- 3 rcu_read_unlock();
- 4 }
-\end{verbatim}
-\end{minipage}
-\vspace{5pt}
+\begin{VerbatimN}
+for (i = 0; i < CSCOUNT_SCALE; i++) {
+ rcu_read_lock();
+ rcu_read_unlock();
+}
+\end{VerbatimN}
Next, consider the effective definitions of \co{rcu_read_lock()}
and \co{rcu_read_unlock()}:
-\vspace{5pt}
-\begin{minipage}[t]{\columnwidth}
-\scriptsize
-\begin{verbatim}
- 1 #define rcu_read_lock() do { } while (0)
- 2 #define rcu_read_unlock() do { } while (0)
-\end{verbatim}
-\end{minipage}
-\vspace{5pt}
+\begin{VerbatimN}
+#define rcu_read_lock() do { } while (0)
+#define rcu_read_unlock() do { } while (0)
+\end{VerbatimN}
Consider also that the compiler does simple optimizations,
allowing it to replace the loop with:
-\vspace{5pt}
-\begin{minipage}[t]{\columnwidth}
-\scriptsize
-\begin{verbatim}
+\begin{VerbatimN}
i = CSCOUNT_SCALE;
-\end{verbatim}
-\end{minipage}
-\vspace{5pt}
+\end{VerbatimN}
So the ``measurement'' of 100 femtoseconds is simply the fixed
overhead of the timing measurements divided by the number of
@@ -407,16 +317,11 @@ cycle.
RCU read-side primitives is via the following (illegal) sequence
of statements:
-\vspace{5pt}
-\begin{minipage}[t]{\columnwidth}
-\small
-\begin{verbatim}
+\begin{VerbatimU}
rcu_read_lock();
synchronize_rcu();
rcu_read_unlock();
-\end{verbatim}
-\end{minipage}
-\vspace{5pt}
+\end{VerbatimU}
The \co{synchronize_rcu()} cannot return until all
pre-existing RCU read-side critical sections complete, but
@@ -445,23 +350,18 @@ Attempting to do such an upgrade with reader-writer locking results
in deadlock.
A sample code fragment that does an RCU read-to-update upgrade follows:
-\vspace{5pt}
-\begin{minipage}[t]{\columnwidth}
-\scriptsize
-\begin{verbatim}
- 1 rcu_read_lock();
- 2 list_for_each_entry_rcu(p, &head, list_field) {
- 3 do_something_with(p);
- 4 if (need_update(p)) {
- 5 spin_lock(my_lock);
- 6 do_update(p);
- 7 spin_unlock(&my_lock);
- 8 }
- 9 }
- 10 rcu_read_unlock();
-\end{verbatim}
-\end{minipage}
-\vspace{5pt}
+\begin{VerbatimN}
+rcu_read_lock();
+list_for_each_entry_rcu(p, &head, list_field) {
+ do_something_with(p);
+ if (need_update(p)) {
+ spin_lock(my_lock);
+ do_update(p);
+ spin_unlock(&my_lock);
+ }
+}
+rcu_read_unlock();
+\end{VerbatimN}
Note that \co{do_update()} is executed under
the protection of the lock \emph{and} under RCU read-side protection.
@@ -691,17 +591,12 @@ the RCU read-side primitives may be used as a restricted
reference-counting mechanism.
For example, consider the following code fragment:
-\vspace{5pt}
-\begin{minipage}[t]{\columnwidth}
-\scriptsize
-\begin{verbatim}
- 1 rcu_read_lock(); /* acquire reference. */
- 2 p = rcu_dereference(head);
- 3 /* do something with p. */
- 4 rcu_read_unlock(); /* release reference. */
-\end{verbatim}
-\end{minipage}
-\vspace{5pt}
+\begin{VerbatimN}
+rcu_read_lock(); /* acquire reference. */
+p = rcu_dereference(head);
+/* do something with p. */
+rcu_read_unlock(); /* release reference. */
+\end{VerbatimN}
The \co{rcu_read_lock()} primitive can be thought of as
acquiring a reference to \co{p}, because a grace period
@@ -716,20 +611,15 @@ from one task to another.
Regardless of these restrictions,
the following code can safely delete \co{p}:
-\vspace{5pt}
-\begin{minipage}[t]{\columnwidth}
-\scriptsize
-\begin{verbatim}
- 1 spin_lock(&mylock);
- 2 p = head;
- 3 rcu_assign_pointer(head, NULL);
- 4 spin_unlock(&mylock);
- 5 /* Wait for all references to be released. */
- 6 synchronize_rcu();
- 7 kfree(p);
-\end{verbatim}
-\end{minipage}
-\vspace{5pt}
+\begin{VerbatimN}
+spin_lock(&mylock);
+p = head;
+rcu_assign_pointer(head, NULL);
+spin_unlock(&mylock);
+/* Wait for all references to be released. */
+synchronize_rcu();
+kfree(p);
+\end{VerbatimN}
The assignment to \co{head} prevents any future references
to \co{p} from being acquired, and the \co{synchronize_rcu()}
@@ -877,55 +767,57 @@ guaranteed to remain in existence for the duration of that RCU
read-side critical section.
\begin{listing}[tbp]
-{ \scriptsize
-\begin{verbbox}
- 1 int delete(int key)
- 2 {
- 3 struct element *p;
- 4 int b;
- 5
- 6 b = hashfunction(key);
- 7 rcu_read_lock();
- 8 p = rcu_dereference(hashtable[b]);
- 9 if (p == NULL || p->key != key) {
- 10 rcu_read_unlock();
- 11 return 0;
- 12 }
- 13 spin_lock(&p->lock);
- 14 if (hashtable[b] == p && p->key == key) {
- 15 rcu_read_unlock();
- 16 rcu_assign_pointer(hashtable[b], NULL);
- 17 spin_unlock(&p->lock);
- 18 synchronize_rcu();
- 19 kfree(p);
- 20 return 1;
- 21 }
- 22 spin_unlock(&p->lock);
- 23 rcu_read_unlock();
- 24 return 0;
- 25 }
-\end{verbbox}
+\begin{linelabel}[ln:defer:Existence Guarantees Enable Per-Element Locking]
+\begin{VerbatimL}[commandchars=\\\@\$]
+int delete(int key)
+{
+ struct element *p;
+ int b;
+
+ b = hashfunction(key); \lnlbl@hash$
+ rcu_read_lock(); \lnlbl@rdlock$
+ p = rcu_dereference(hashtable[b]);
+ if (p == NULL || p->key != key) { \lnlbl@chkkey$
+ rcu_read_unlock(); \lnlbl@rdunlock1$
+ return 0; \lnlbl@ret_0:a$
+ }
+ spin_lock(&p->lock); \lnlbl@acq$
+ if (hashtable[b] == p && p->key == key) {\lnlbl@chkkey2$
+ rcu_read_unlock(); \lnlbl@rdunlock2$
+ rcu_assign_pointer(hashtable[b], NULL);\lnlbl@remove$
+ spin_unlock(&p->lock); \lnlbl@rel1$
+ synchronize_rcu(); \lnlbl@sync_rcu$
+ kfree(p); \lnlbl@kfree$
+ return 1; \lnlbl@ret_1$
+ }
+ spin_unlock(&p->lock); \lnlbl@rel2$
+ rcu_read_unlock(); \lnlbl@rdunlock3$
+ return 0; \lnlbl@ret_0:b$
}
-\centering
-\theverbbox
+\end{VerbatimL}
+\end{linelabel}
\caption{Existence Guarantees Enable Per-Element Locking}
\label{lst:defer:Existence Guarantees Enable Per-Element Locking}
\end{listing}
+\begin{lineref}[ln:defer:Existence Guarantees Enable Per-Element Locking]
Listing~\ref{lst:defer:Existence Guarantees Enable Per-Element Locking}
demonstrates how RCU-based existence guarantees can enable
per-element locking via a function that deletes an element from
a hash table.
-Line~6 computes a hash function, and line~7 enters an RCU
+Line~\lnref{hash} computes a hash function, and line~\lnref{rdlock} enters an RCU
read-side critical section.
-If line~9 finds that the corresponding bucket of the hash table is
+If line~\lnref{chkkey} finds that the corresponding bucket of the hash table is
empty or that the element present is not the one we wish to delete,
-then line~10 exits the RCU read-side critical section and line~11
+then line~\lnref{rdunlock1} exits the RCU read-side critical section and
+line~\lnref{ret_0:a}
indicates failure.
+\end{lineref}
\QuickQuiz{}
What if the element we need to delete is not the first element
- of the list on line~9 of
+ of the list on
+ line~\ref{ln:defer:Existence Guarantees Enable Per-Element Locking:chkkey} of
Listing~\ref{lst:defer:Existence Guarantees Enable Per-Element Locking}?
\QuickQuizAnswer{
As with
@@ -936,24 +828,29 @@ indicates failure.
full chaining.
} \QuickQuizEnd
-Otherwise, line~13 acquires the update-side spinlock, and
-line~14 then checks that the element is still the one that we want.
-If so, line~15 leaves the RCU read-side critical section,
-line~16 removes it from the table, line~17 releases
-the lock, line~18 waits for all pre-existing RCU read-side critical
-sections to complete, line~19 frees the newly removed element,
-and line~20 indicates success.
-If the element is no longer the one we want, line~22 releases
-the lock, line~23 leaves the RCU read-side critical section,
-and line~24 indicates failure to delete the specified key.
+\begin{lineref}[ln:defer:Existence Guarantees Enable Per-Element Locking]
+Otherwise, line~\lnref{acq} acquires the update-side spinlock, and
+line~\lnref{chkkey2} then checks that the element is still the one that we want.
+If so, line~\lnref{rdunlock2} leaves the RCU read-side critical section,
+line~\lnref{remove} removes it from the table, line~\lnref{rel1} releases
+the lock, line~\lnref{sync_rcu} waits for all pre-existing RCU read-side critical
+sections to complete, line~\lnref{kfree} frees the newly removed element,
+and line~\lnref{ret_1} indicates success.
+If the element is no longer the one we want, line~\lnref{rel2} releases
+the lock, line~\lnref{rdunlock3} leaves the RCU read-side critical section,
+and line~\lnref{ret_0:b} indicates failure to delete the specified key.
+\end{lineref}
\QuickQuiz{}
+ \begin{lineref}[ln:defer:Existence Guarantees Enable Per-Element Locking]
Why is it OK to exit the RCU read-side critical section on
- line~15 of
+ line~\lnref{rdunlock2} of
Listing~\ref{lst:defer:Existence Guarantees Enable Per-Element Locking}
- before releasing the lock on line~17?
+ before releasing the lock on line~\lnref{rel1}?
+ \end{lineref}
\QuickQuizAnswer{
- First, please note that the second check on line~14 is
+ \begin{lineref}[ln:defer:Existence Guarantees Enable Per-Element Locking]
+ First, please note that the second check on line~\lnref{chkkey2} is
necessary because some other
CPU might have removed this element while we were waiting
to acquire the lock.
@@ -970,42 +867,48 @@ and line~24 indicates failure to delete the specified key.
% A re-check is necessary if the key can mutate or if it is
% necessary to reject deleted entries (in cases where deletion
% is recorded by mutating the key.
+ \end{lineref}
} \QuickQuizEnd
\QuickQuiz{}
+ \begin{lineref}[ln:defer:Existence Guarantees Enable Per-Element Locking]
Why not exit the RCU read-side critical section on
- line~23 of
+ line~\lnref{rdunlock3} of
Listing~\ref{lst:defer:Existence Guarantees Enable Per-Element Locking}
- before releasing the lock on line~22?
+ before releasing the lock on line~\lnref{rel2}?
+ \end{lineref}
\QuickQuizAnswer{
+ \begin{lineref}[ln:defer:Existence Guarantees Enable Per-Element Locking]
Suppose we reverse the order of these two lines.
Then this code is vulnerable to the following sequence of
events:
\begin{enumerate}
\item CPU~0 invokes \co{delete()}, and finds the element
- to be deleted, executing through line~15.
+ to be deleted, executing through line~\lnref{rdunlock2}.
It has not yet actually deleted the element, but
is about to do so.
\item CPU~1 concurrently invokes \co{delete()}, attempting
to delete this same element.
However, CPU~0 still holds the lock, so CPU~1 waits
- for it at line~13.
- \item CPU~0 executes lines~16 and 17, and blocks at
- line~18 waiting for CPU~1 to exit its RCU read-side
- critical section.
- \item CPU~1 now acquires the lock, but the test on line~14
+ for it at line~\lnref{acq}.
+ \item CPU~0 executes lines~\lnref{remove} and~\lnref{rel1},
+ and blocks at line~\lnref{sync_rcu} waiting for CPU~1
+ to exit its RCU read-side critical section.
+ \item CPU~1 now acquires the lock, but the test on line~\lnref{chkkey2}
fails because CPU~0 has already removed the element.
- CPU~1 now executes line~22 (which we switched with line~23
+ CPU~1 now executes line~\lnref{rel2}
+ (which we switched with line~\lnref{rdunlock3}
for the purposes of this Quick Quiz)
and exits its RCU read-side critical section.
\item CPU~0 can now return from \co{synchronize_rcu()},
- and thus executes line~19, sending the element to
+ and thus executes line~\lnref{kfree}, sending the element to
the freelist.
\item CPU~1 now attempts to release a lock for an element
that has been freed, and, worse yet, possibly
reallocated as some other type of data structure.
This is a fatal memory-corruption error.
\end{enumerate}
+ \end{lineref}
} \QuickQuizEnd
Alert readers will recognize this as only a slight variation on
@@ -1127,84 +1030,88 @@ A simplified version of this code is shown
Listing~\ref{lst:defer:Using RCU to Wait for NMIs to Finish}.
\begin{listing}[tbp]
-{ \scriptsize
-\begin{verbbox}
- 1 struct profile_buffer {
- 2 long size;
- 3 atomic_t entry[0];
- 4 };
- 5 static struct profile_buffer *buf = NULL;
- 6
- 7 void nmi_profile(unsigned long pcvalue)
- 8 {
- 9 struct profile_buffer *p = rcu_dereference(buf);
- 10
- 11 if (p == NULL)
- 12 return;
- 13 if (pcvalue >= p->size)
- 14 return;
- 15 atomic_inc(&p->entry[pcvalue]);
- 16 }
- 17
- 18 void nmi_stop(void)
- 19 {
- 20 struct profile_buffer *p = buf;
- 21
- 22 if (p == NULL)
- 23 return;
- 24 rcu_assign_pointer(buf, NULL);
- 25 synchronize_sched();
- 26 kfree(p);
- 27 }
-\end{verbbox}
-}
-\centering
-\theverbbox
+\begin{linelabel}[ln:defer:Using RCU to Wait for NMIs to Finish]
+\begin{VerbatimL}[commandchars=\\\@\$]
+struct profile_buffer { \lnlbl@struct:b$
+ long size;
+ atomic_t entry[0];
+}; \lnlbl@struct:e$
+static struct profile_buffer *buf = NULL; \lnlbl@struct:buf$
+
+void nmi_profile(unsigned long pcvalue) \lnlbl@nmi_profile:b$
+{
+ struct profile_buffer *p = rcu_dereference(buf);\lnlbl@nmi_profile:rcu_deref$
+
+ if (p == NULL) \lnlbl@nmi_profile:if_NULL$
+ return; \lnlbl@nmi_profile:ret:a$
+ if (pcvalue >= p->size) \lnlbl@nmi_profile:if_oor$
+ return; \lnlbl@nmi_profile:ret:b$
+ atomic_inc(&p->entry[pcvalue]); \lnlbl@nmi_profile:inc$
+} \lnlbl@nmi_profile:e$
+
+void nmi_stop(void) \lnlbl@nmi_stop:b$
+{
+ struct profile_buffer *p = buf; \lnlbl@nmi_stop:fetch$
+
+ if (p == NULL) \lnlbl@nmi_stop:if_NULL$
+ return; \lnlbl@nmi_stop:ret$
+ rcu_assign_pointer(buf, NULL); \lnlbl@nmi_stop:NULL$
+ synchronize_sched(); \lnlbl@nmi_stop:sync_sched$
+ kfree(p); \lnlbl@nmi_stop:kfree$
+} \lnlbl@nmi_stop:e$
+\end{VerbatimL}
+\end{linelabel}
\caption{Using RCU to Wait for NMIs to Finish}
\label{lst:defer:Using RCU to Wait for NMIs to Finish}
\end{listing}
-Lines~1-4 define a \co{profile_buffer} structure, containing a
+\begin{lineref}[ln:defer:Using RCU to Wait for NMIs to Finish:struct]
+Lines~\lnref{b}-\lnref{e} define a \co{profile_buffer} structure, containing a
size and an indefinite array of entries.
-Line~5 defines a pointer to a profile buffer, which is
+Line~\lnref{buf} defines a pointer to a profile buffer, which is
presumably initialized elsewhere to point to a dynamically allocated
region of memory.
+\end{lineref}
-Lines~7-16 define the \co{nmi_profile()} function,
+\begin{lineref}[ln:defer:Using RCU to Wait for NMIs to Finish:nmi_profile]
+Lines~\lnref{b}-\lnref{e} define the \co{nmi_profile()} function,
which is called from within an NMI handler.
As such, it cannot be preempted, nor can it be interrupted by a normal
interrupts handler, however, it is still subject to delays due to cache misses,
ECC errors, and cycle stealing by other hardware threads within the same
core.
-Line~9 gets a local pointer to the profile buffer using the
+Line~\lnref{rcu_deref} gets a local pointer to the profile buffer using the
\co{rcu_dereference()} primitive to ensure memory ordering on
DEC Alpha, and
-lines~11 and~12 exit from this function if there is no
-profile buffer currently allocated, while lines~13 and~14
+lines~\lnref{if_NULL} and~\lnref{ret:a} exit from this function if there is no
+profile buffer currently allocated, while lines~\lnref{if_oor} and~\lnref{ret:b}
exit from this function if the \co{pcvalue} argument
is out of range.
-Otherwise, line~15 increments the profile-buffer entry indexed
+Otherwise, line~\lnref{inc} increments the profile-buffer entry indexed
by the \co{pcvalue} argument.
Note that storing the size with the buffer guarantees that the
range check matches the buffer, even if a large buffer is suddenly
replaced by a smaller one.
+\end{lineref}
-Lines~18-27 define the \co{nmi_stop()} function,
+\begin{lineref}[ln:defer:Using RCU to Wait for NMIs to Finish:nmi_stop]
+Lines~\lnref{b}-\lnref{e} define the \co{nmi_stop()} function,
where the caller is responsible for mutual exclusion (for example,
holding the correct lock).
-Line~20 fetches a pointer to the profile buffer, and
-lines~22 and~23 exit the function if there is no buffer.
-Otherwise, line~24 \co{NULL}s out the profile-buffer pointer
+Line~\lnref{fetch} fetches a pointer to the profile buffer, and
+lines~\lnref{if_NULL} and~\lnref{ret} exit the function if there is no buffer.
+Otherwise, line~\lnref{NULL} \co{NULL}s out the profile-buffer pointer
(using the \co{rcu_assign_pointer()} primitive to maintain
memory ordering on weakly ordered machines),
-and line~25 waits for an RCU Sched grace period to elapse,
+and line~\lnref{sync_sched} waits for an RCU Sched grace period to elapse,
in particular, waiting for all non-preemptible regions of code,
including NMI handlers, to complete.
-Once execution continues at line~26, we are guaranteed that
+Once execution continues at line~\lnref{kfree}, we are guaranteed that
any instance of \co{nmi_profile()} that obtained a
pointer to the old buffer has returned.
It is therefore safe to free the buffer, in this case using the
\co{kfree()} primitive.
+\end{lineref}
\QuickQuiz{}
Suppose that the \co{nmi_profile()} function was preemptible.
@@ -1218,46 +1125,42 @@ It is therefore safe to free the buffer, in this case using the
Listing~\ref{lst:defer:Using RCU to Wait for Mythical Preemptible NMIs to Finish}.
%
\begin{listing}[tb]
-{\scriptsize
-\begin{verbbox}
- 1 struct profile_buffer {
- 2 long size;
- 3 atomic_t entry[0];
- 4 };
- 5 static struct profile_buffer *buf = NULL;
- 6
- 7 void nmi_profile(unsigned long pcvalue)
- 8 {
- 9 struct profile_buffer *p;
- 10
- 11 rcu_read_lock();
- 12 p = rcu_dereference(buf);
- 13 if (p == NULL) {
- 14 rcu_read_unlock();
- 15 return;
- 16 }
- 17 if (pcvalue >= p->size) {
- 18 rcu_read_unlock();
- 19 return;
- 20 }
- 21 atomic_inc(&p->entry[pcvalue]);
- 22 rcu_read_unlock();
- 23 }
- 24
- 25 void nmi_stop(void)
- 26 {
- 27 struct profile_buffer *p = buf;
- 28
- 29 if (p == NULL)
- 30 return;
- 31 rcu_assign_pointer(buf, NULL);
- 32 synchronize_rcu();
- 33 kfree(p);
- 34 }
-\end{verbbox}
+\begin{VerbatimL}
+struct profile_buffer {
+ long size;
+ atomic_t entry[0];
+};
+static struct profile_buffer *buf = NULL;
+
+void nmi_profile(unsigned long pcvalue)
+{
+ struct profile_buffer *p;
+
+ rcu_read_lock();
+ p = rcu_dereference(buf);
+ if (p == NULL) {
+ rcu_read_unlock();
+ return;
+ }
+ if (pcvalue >= p->size) {
+ rcu_read_unlock();
+ return;
+ }
+ atomic_inc(&p->entry[pcvalue]);
+ rcu_read_unlock();
}
-\centering
-\theverbbox
+
+void nmi_stop(void)
+{
+ struct profile_buffer *p = buf;
+
+ if (p == NULL)
+ return;
+ rcu_assign_pointer(buf, NULL);
+ synchronize_rcu();
+ kfree(p);
+}
+\end{VerbatimL}
\caption{Using RCU to Wait for Mythical Preemptible NMIs to Finish}
\label{lst:defer:Using RCU to Wait for Mythical Preemptible NMIs to Finish}
\end{listing}
--
2.7.4
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 0/6] defer: Employ new scheme for code snippets (cont.)
2018-12-03 15:33 [PATCH 0/6] defer: Employ new scheme for code snippets (cont.) Akira Yokosawa
` (5 preceding siblings ...)
2018-12-03 15:44 ` [PATCH 6/6] defer: Employ new scheme for snippets of route_rcu.c Akira Yokosawa
@ 2018-12-03 17:23 ` Paul E. McKenney
6 siblings, 0 replies; 8+ messages in thread
From: Paul E. McKenney @ 2018-12-03 17:23 UTC (permalink / raw)
To: Akira Yokosawa; +Cc: perfbook
On Tue, Dec 04, 2018 at 12:33:32AM +0900, Akira Yokosawa wrote:
> Hi Paul,
>
> This is a followup patch set to update remaining code snippets
> in chapter "defer".
>
> My first thought was that patches #5 and #6 might conflict with
> upcoming updates to reflect consolidation of RCU flavors, but it
> looks like these code snippets are unlikely to be affected.
>
> Patch #1 assumes that you intentionally presented a different
> set of hazptr API from the one used in hazptr.h.
>
> Thanks, Akira
Applied and pushed, thank you!
Thanx, Paul
> --
> Akira Yokosawa (6):
> defer: Employ new scheme for 'lst:defer:Hazard-Pointer Storage and
> Erasure'
> defer: Employ new scheme for snippets of route_hazptr.c
> defer: Employ new scheme for snippet of seqlock.h
> defer: Employ new scheme for snippets of route_seqlock.c
> defer: Employ new scheme for snippets in rcuintro and rcufundamental
> defer: Employ new scheme for snippets of route_rcu.c
>
> CodeSamples/defer/route_hazptr.c | 50 ++--
> CodeSamples/defer/route_rcu.c | 75 +++---
> CodeSamples/defer/route_seqlock.c | 56 ++--
> CodeSamples/defer/seqlock.h | 55 ++--
> defer/hazptr.tex | 168 +++---------
> defer/rcufundamental.tex | 381 +++++++++++++--------------
> defer/rcuintro.tex | 13 +-
> defer/rcuusage.tex | 523 ++++++++++++++++----------------------
> defer/seqlock.tex | 259 ++++++-------------
> 9 files changed, 637 insertions(+), 943 deletions(-)
>
> --
> 2.7.4
>
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2018-12-03 17:23 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-12-03 15:33 [PATCH 0/6] defer: Employ new scheme for code snippets (cont.) Akira Yokosawa
2018-12-03 15:35 ` [PATCH 1/6] defer: Employ new scheme for 'lst:defer:Hazard-Pointer Storage and Erasure' Akira Yokosawa
2018-12-03 15:37 ` [PATCH 2/6] defer: Employ new scheme for snippets of route_hazptr.c Akira Yokosawa
2018-12-03 15:39 ` [PATCH 3/6] defer: Employ new scheme for snippet of seqlock.h Akira Yokosawa
2018-12-03 15:41 ` [PATCH 4/6] defer: Employ new scheme for snippets of route_seqlock.c Akira Yokosawa
2018-12-03 15:42 ` [PATCH 5/6] defer: Employ new scheme for snippets in rcuintro and rcufundamental Akira Yokosawa
2018-12-03 15:44 ` [PATCH 6/6] defer: Employ new scheme for snippets of route_rcu.c Akira Yokosawa
2018-12-03 17:23 ` [PATCH 0/6] defer: Employ new scheme for code snippets (cont.) Paul E. McKenney
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox