u32 table_id, struct fib_info *fi,
int *fa_index, int fa_start, unsigned int flags);
+void fnhe_update_pmtu(struct fib_nh_exception *fnhe, u32 new, u32 orig);
+
static inline void ip_rt_put(struct rtable *rt)
{
/* dst_release() accepts a NULL parameter.
return NOTIFY_DONE;
}
-/* Update the PMTU of exceptions when:
- * - the new MTU of the first hop becomes smaller than the PMTU
- * - the old MTU was the same as the PMTU, and it limited discovery of
- * larger MTUs on the path. With that limit raised, we can now
- * discover larger MTUs
- * A special case is locked exceptions, for which the PMTU is smaller
- * than the minimal accepted PMTU:
- * - if the new MTU is greater than the PMTU, don't make any change
- * - otherwise, unlock and set PMTU
+/* Walk the exceptions of a nexthop after its first hop MTU changed. The
+ * chain is RCU protected here, while fnhe_update_pmtu() takes fnhe_lock
+ * for the update of each entry.
*/
void fib_nhc_update_mtu(struct fib_nh_common *nhc, u32 new, u32 orig)
{
struct fnhe_hash_bucket *bucket;
int i;
- bucket = rcu_dereference_protected(nhc->nhc_exceptions, 1);
+ rcu_read_lock();
+ bucket = rcu_dereference(nhc->nhc_exceptions);
if (!bucket)
- return;
+ goto out;
for (i = 0; i < FNHE_HASH_SIZE; i++) {
struct fib_nh_exception *fnhe;
- for (fnhe = rcu_dereference_protected(bucket[i].chain, 1);
+ for (fnhe = rcu_dereference(bucket[i].chain);
fnhe;
- fnhe = rcu_dereference_protected(fnhe->fnhe_next, 1)) {
- if (fnhe->fnhe_mtu_locked) {
- if (new <= fnhe->fnhe_pmtu) {
- fnhe->fnhe_pmtu = new;
- fnhe->fnhe_mtu_locked = false;
- }
- } else if (new < fnhe->fnhe_pmtu ||
- orig == fnhe->fnhe_pmtu) {
- fnhe->fnhe_pmtu = new;
- }
- }
+ fnhe = rcu_dereference(fnhe->fnhe_next))
+ fnhe_update_pmtu(fnhe, new, orig);
}
+out:
+ rcu_read_unlock();
}
void fib_sync_mtu(struct net_device *dev, u32 orig_mtu)
spin_unlock_bh(&fnhe_lock);
}
+/* Update the PMTU of an exception when:
+ * - the new MTU of the first hop becomes smaller than the PMTU
+ * - the old MTU was the same as the PMTU, and it limited discovery of
+ * larger MTUs on the path. With that limit raised, we can now
+ * discover larger MTUs
+ * A special case is locked exceptions, for which the PMTU is smaller
+ * than the minimal accepted PMTU:
+ * - if the new MTU is greater than the PMTU, don't make any change
+ * - otherwise, unlock and set PMTU
+ *
+ * fnhe_lock keeps fnhe_pmtu and fnhe_mtu_locked consistent against
+ * update_or_create_fnhe(), which sets both under the same lock.
+ */
+void fnhe_update_pmtu(struct fib_nh_exception *fnhe, u32 new, u32 orig)
+{
+ spin_lock_bh(&fnhe_lock);
+
+ if (fnhe->fnhe_mtu_locked) {
+ if (new <= fnhe->fnhe_pmtu) {
+ fnhe->fnhe_pmtu = new;
+ fnhe->fnhe_mtu_locked = false;
+ }
+ } else if (new < fnhe->fnhe_pmtu || orig == fnhe->fnhe_pmtu) {
+ fnhe->fnhe_pmtu = new;
+ }
+
+ spin_unlock_bh(&fnhe_lock);
+}
+
static void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flowi4 *fl4,
bool kill_route)
{