]> rtime.felk.cvut.cz Git - zynq/linux.git/commitdiff
seqlock: Prevent rt starvation
authorThomas Gleixner <tglx@linutronix.de>
Wed, 22 Feb 2012 11:03:30 +0000 (12:03 +0100)
committerMichal Sojka <sojka@merica.cz>
Sun, 13 Sep 2015 07:47:38 +0000 (09:47 +0200)
If a low prio writer gets preempted while holding the seqlock write
locked, a high prio reader spins forever on RT.

To prevent this let the reader grab the spinlock, so it blocks and
eventually boosts the writer. This way the writer can proceed and
endless spinning is prevented.

For seqcount writers we disable preemption over the update code
path. Thaanks to Al Viro for distangling some VFS code to make that
possible.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: stable-rt@vger.kernel.org
include/linux/seqlock.h
include/net/dst.h
include/net/neighbour.h

index f5df8f687b4d097dd7855e3670a2b790cb380d5d..de343cda4680944cbbff807de3a621b990d1c389 100644 (file)
@@ -219,20 +219,30 @@ static inline int read_seqcount_retry(const seqcount_t *s, unsigned start)
        return __read_seqcount_retry(s, start);
 }
 
-
-
-static inline void raw_write_seqcount_begin(seqcount_t *s)
+static inline void __raw_write_seqcount_begin(seqcount_t *s)
 {
        s->sequence++;
        smp_wmb();
 }
 
-static inline void raw_write_seqcount_end(seqcount_t *s)
+static inline void raw_write_seqcount_begin(seqcount_t *s)
+{
+       preempt_disable_rt();
+       __raw_write_seqcount_begin(s);
+}
+
+static inline void __raw_write_seqcount_end(seqcount_t *s)
 {
        smp_wmb();
        s->sequence++;
 }
 
+static inline void raw_write_seqcount_end(seqcount_t *s)
+{
+       __raw_write_seqcount_end(s);
+       preempt_enable_rt();
+}
+
 /*
  * raw_write_seqcount_latch - redirect readers to even/odd copy
  * @s: pointer to seqcount_t
@@ -305,10 +315,33 @@ typedef struct {
 /*
  * Read side functions for starting and finalizing a read side section.
  */
+#ifndef CONFIG_PREEMPT_RT_FULL
 static inline unsigned read_seqbegin(const seqlock_t *sl)
 {
        return read_seqcount_begin(&sl->seqcount);
 }
+#else
+/*
+ * Starvation safe read side for RT
+ */
+static inline unsigned read_seqbegin(seqlock_t *sl)
+{
+       unsigned ret;
+
+repeat:
+       ret = ACCESS_ONCE(sl->seqcount.sequence);
+       if (unlikely(ret & 1)) {
+               /*
+                * Take the lock and let the writer proceed (i.e. evtl
+                * boost it), otherwise we could loop here forever.
+                */
+               spin_lock(&sl->lock);
+               spin_unlock(&sl->lock);
+               goto repeat;
+       }
+       return ret;
+}
+#endif
 
 static inline unsigned read_seqretry(const seqlock_t *sl, unsigned start)
 {
@@ -323,36 +356,36 @@ static inline unsigned read_seqretry(const seqlock_t *sl, unsigned start)
 static inline void write_seqlock(seqlock_t *sl)
 {
        spin_lock(&sl->lock);
-       write_seqcount_begin(&sl->seqcount);
+       __write_seqcount_begin(&sl->seqcount);
 }
 
 static inline void write_sequnlock(seqlock_t *sl)
 {
-       write_seqcount_end(&sl->seqcount);
+       __raw_write_seqcount_end(&sl->seqcount);
        spin_unlock(&sl->lock);
 }
 
 static inline void write_seqlock_bh(seqlock_t *sl)
 {
        spin_lock_bh(&sl->lock);
-       write_seqcount_begin(&sl->seqcount);
+       __write_seqcount_begin(&sl->seqcount);
 }
 
 static inline void write_sequnlock_bh(seqlock_t *sl)
 {
-       write_seqcount_end(&sl->seqcount);
+       __raw_write_seqcount_end(&sl->seqcount);
        spin_unlock_bh(&sl->lock);
 }
 
 static inline void write_seqlock_irq(seqlock_t *sl)
 {
        spin_lock_irq(&sl->lock);
-       write_seqcount_begin(&sl->seqcount);
+       __write_seqcount_begin(&sl->seqcount);
 }
 
 static inline void write_sequnlock_irq(seqlock_t *sl)
 {
-       write_seqcount_end(&sl->seqcount);
+       __raw_write_seqcount_end(&sl->seqcount);
        spin_unlock_irq(&sl->lock);
 }
 
@@ -361,7 +394,7 @@ static inline unsigned long __write_seqlock_irqsave(seqlock_t *sl)
        unsigned long flags;
 
        spin_lock_irqsave(&sl->lock, flags);
-       write_seqcount_begin(&sl->seqcount);
+       __write_seqcount_begin(&sl->seqcount);
        return flags;
 }
 
@@ -371,7 +404,7 @@ static inline unsigned long __write_seqlock_irqsave(seqlock_t *sl)
 static inline void
 write_sequnlock_irqrestore(seqlock_t *sl, unsigned long flags)
 {
-       write_seqcount_end(&sl->seqcount);
+       __raw_write_seqcount_end(&sl->seqcount);
        spin_unlock_irqrestore(&sl->lock, flags);
 }
 
index 0fb99a26e97372613da1851d9ee4d9bb432aba3f..b6a790646729c4fe8064c5f387b0b292df18d514 100644 (file)
@@ -403,7 +403,7 @@ static inline void dst_confirm(struct dst_entry *dst)
 static inline int dst_neigh_output(struct dst_entry *dst, struct neighbour *n,
                                   struct sk_buff *skb)
 {
-       const struct hh_cache *hh;
+       struct hh_cache *hh;
 
        if (dst->pending_confirm) {
                unsigned long now = jiffies;
index 76f708486aaec76031a24ee5ff1d02f126185304..b865ae95c06ef70a5b0fb1bcf94257255a9683ec 100644 (file)
@@ -394,7 +394,7 @@ static inline int neigh_hh_bridge(struct hh_cache *hh, struct sk_buff *skb)
 }
 #endif
 
-static inline int neigh_hh_output(const struct hh_cache *hh, struct sk_buff *skb)
+static inline int neigh_hh_output(struct hh_cache *hh, struct sk_buff *skb)
 {
        unsigned int seq;
        int hh_len;
@@ -449,7 +449,7 @@ struct neighbour_cb {
 
 #define NEIGH_CB(skb)  ((struct neighbour_cb *)(skb)->cb)
 
-static inline void neigh_ha_snapshot(char *dst, const struct neighbour *n,
+static inline void neigh_ha_snapshot(char *dst, struct neighbour *n,
                                     const struct net_device *dev)
 {
        unsigned int seq;