aboutsummaryrefslogtreecommitdiffstats
path: root/include/linux/seqlock.h
diff options
context:
space:
mode:
authorWaiman Long <Waiman.Long@hp.com>2013-09-12 10:55:34 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-09-12 12:25:23 -0400
commit1370e97bb2eb1ef2df7355204e5a4ba13e12b861 (patch)
tree9e3c2e9b0e0a7f67e50898e8ff5ecb462f260625 /include/linux/seqlock.h
parentdecf7abcc97444ecd2d3cf278f5cc8093f33f49a (diff)
seqlock: Add a new locking reader type
The sequence lock (seqlock) was originally designed for the cases where the readers do not need to block the writers by making the readers retry the read operation when the data change. Since then, the use cases have been expanded to include situations where a thread does not need to change the data (effectively a reader) at all but have to take the writer lock because it can't tolerate changes to the protected structure. Some examples are the d_path() function and the getcwd() syscall in fs/dcache.c where the functions take the writer lock on rename_lock even though they don't need to change anything in the protected data structure at all. This is inefficient as a reader is now blocking other sequence number reading readers from moving forward by pretending to be a writer. This patch tries to eliminate this inefficiency by introducing a new type of locking reader to the seqlock locking mechanism. This new locking reader will try to take an exclusive lock preventing other writers and locking readers from going forward. However, it won't affect the progress of the other sequence number reading readers as the sequence number won't be changed. Signed-off-by: Waiman Long <Waiman.Long@hp.com> Cc: Alexander Viro <viro@zeniv.linux.org.uk> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'include/linux/seqlock.h')
-rw-r--r--include/linux/seqlock.h68
1 files changed, 63 insertions, 5 deletions
diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h
index 18299057402f..21a209336e79 100644
--- a/include/linux/seqlock.h
+++ b/include/linux/seqlock.h
@@ -3,15 +3,21 @@
3/* 3/*
4 * Reader/writer consistent mechanism without starving writers. This type of 4 * Reader/writer consistent mechanism without starving writers. This type of
5 * lock for data where the reader wants a consistent set of information 5 * lock for data where the reader wants a consistent set of information
6 * and is willing to retry if the information changes. Readers never 6 * and is willing to retry if the information changes. There are two types
7 * block but they may have to retry if a writer is in 7 * of readers:
8 * progress. Writers do not wait for readers. 8 * 1. Sequence readers which never block a writer but they may have to retry
9 * if a writer is in progress by detecting change in sequence number.
10 * Writers do not wait for a sequence reader.
11 * 2. Locking readers which will wait if a writer or another locking reader
12 * is in progress. A locking reader in progress will also block a writer
13 * from going forward. Unlike the regular rwlock, the read lock here is
14 * exclusive so that only one locking reader can get it.
9 * 15 *
10 * This is not as cache friendly as brlock. Also, this will not work 16 * This is not as cache friendly as brlock. Also, this may not work well
11 * for data that contains pointers, because any writer could 17 * for data that contains pointers, because any writer could
12 * invalidate a pointer that a reader was following. 18 * invalidate a pointer that a reader was following.
13 * 19 *
14 * Expected reader usage: 20 * Expected non-blocking reader usage:
15 * do { 21 * do {
16 * seq = read_seqbegin(&foo); 22 * seq = read_seqbegin(&foo);
17 * ... 23 * ...
@@ -268,4 +274,56 @@ write_sequnlock_irqrestore(seqlock_t *sl, unsigned long flags)
268 spin_unlock_irqrestore(&sl->lock, flags); 274 spin_unlock_irqrestore(&sl->lock, flags);
269} 275}
270 276
277/*
278 * A locking reader exclusively locks out other writers and locking readers,
279 * but doesn't update the sequence number. Acts like a normal spin_lock/unlock.
280 * Don't need preempt_disable() because that is in the spin_lock already.
281 */
282static inline void read_seqlock_excl(seqlock_t *sl)
283{
284 spin_lock(&sl->lock);
285}
286
287static inline void read_sequnlock_excl(seqlock_t *sl)
288{
289 spin_unlock(&sl->lock);
290}
291
292static inline void read_seqlock_excl_bh(seqlock_t *sl)
293{
294 spin_lock_bh(&sl->lock);
295}
296
297static inline void read_sequnlock_excl_bh(seqlock_t *sl)
298{
299 spin_unlock_bh(&sl->lock);
300}
301
302static inline void read_seqlock_excl_irq(seqlock_t *sl)
303{
304 spin_lock_irq(&sl->lock);
305}
306
307static inline void read_sequnlock_excl_irq(seqlock_t *sl)
308{
309 spin_unlock_irq(&sl->lock);
310}
311
312static inline unsigned long __read_seqlock_excl_irqsave(seqlock_t *sl)
313{
314 unsigned long flags;
315
316 spin_lock_irqsave(&sl->lock, flags);
317 return flags;
318}
319
320#define read_seqlock_excl_irqsave(lock, flags) \
321 do { flags = __read_seqlock_excl_irqsave(lock); } while (0)
322
323static inline void
324read_sequnlock_excl_irqrestore(seqlock_t *sl, unsigned long flags)
325{
326 spin_unlock_irqrestore(&sl->lock, flags);
327}
328
271#endif /* __LINUX_SEQLOCK_H */ 329#endif /* __LINUX_SEQLOCK_H */