void                    *lookup_ptr;
        void                    *result;
        struct fib_rule         *rule;
+       int                     flags;
+#define FIB_LOOKUP_NOREF       1
 };
 
 struct fib_rules_ops {
 
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
        int                     fib_power;
 #endif
+       struct rcu_head         rcu;
        struct fib_nh           fib_nh[0];
 #define fib_dev                fib_nh[0].nh_dev
 };
 };
 
 extern int fib_table_lookup(struct fib_table *tb, const struct flowi *flp,
-                           struct fib_result *res);
+                           struct fib_result *res, int fib_flags);
 extern int fib_table_insert(struct fib_table *, struct fib_config *);
 extern int fib_table_delete(struct fib_table *, struct fib_config *);
 extern int fib_table_dump(struct fib_table *table, struct sk_buff *skb,
        struct fib_table *table;
 
        table = fib_get_table(net, RT_TABLE_LOCAL);
-       if (!fib_table_lookup(table, flp, res))
+       if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF))
                return 0;
 
        table = fib_get_table(net, RT_TABLE_MAIN);
-       if (!fib_table_lookup(table, flp, res))
+       if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF))
                return 0;
        return -ENETUNREACH;
 }
                free_fib_info(fi);
 }
 
-static inline void fib_res_put(struct fib_result *res)
-{
-       if (res->fi)
-               fib_info_put(res->fi);
-#ifdef CONFIG_IP_MULTIPLE_TABLES
-       if (res->r)
-               fib_rule_put(res->r);
-#endif
-}
-
 #ifdef CONFIG_PROC_FS
 extern int __net_init  fib_proc_init(struct net *net);
 extern void __net_exit fib_proc_exit(struct net *net);
 
                        err = ops->action(rule, fl, flags, arg);
 
                if (err != -EAGAIN) {
-                       if (likely(atomic_inc_not_zero(&rule->refcnt))) {
+                       if ((arg->flags & FIB_LOOKUP_NOREF) ||
+                           likely(atomic_inc_not_zero(&rule->refcnt))) {
                                arg->rule = rule;
                                goto out;
                        }
 
        struct fib_result res = { 0 };
        struct net_device *dev = NULL;
 
-       if (fib_lookup(net, &fl, &res))
+       rcu_read_lock();
+       if (fib_lookup(net, &fl, &res)) {
+               rcu_read_unlock();
                return NULL;
+       }
        if (res.type != RTN_LOCAL)
                goto out;
        dev = FIB_RES_DEV(res);
        if (dev && devref)
                dev_hold(dev);
 out:
-       fib_res_put(&res);
+       rcu_read_unlock();
        return dev;
 }
 EXPORT_SYMBOL(__ip_dev_find);
        local_table = fib_get_table(net, RT_TABLE_LOCAL);
        if (local_table) {
                ret = RTN_UNICAST;
-               if (!fib_table_lookup(local_table, &fl, &res)) {
+               rcu_read_lock();
+               if (!fib_table_lookup(local_table, &fl, &res, FIB_LOOKUP_NOREF)) {
                        if (!dev || dev == res.fi->fib_dev)
                                ret = res.type;
-                       fib_res_put(&res);
                }
+               rcu_read_unlock();
        }
        return ret;
 }
  * - figure out what "logical" interface this packet arrived
  *   and calculate "specific destination" address.
  * - check, that packet arrived from expected physical interface.
+ * called with rcu_read_lock()
  */
 int fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif,
                        struct net_device *dev, __be32 *spec_dst,
        struct net *net;
 
        no_addr = rpf = accept_local = 0;
-       rcu_read_lock();
        in_dev = __in_dev_get_rcu(dev);
        if (in_dev) {
                no_addr = in_dev->ifa_list == NULL;
                if (mark && !IN_DEV_SRC_VMARK(in_dev))
                        fl.mark = 0;
        }
-       rcu_read_unlock();
 
        if (in_dev == NULL)
                goto e_inval;
                goto last_resort;
        if (res.type != RTN_UNICAST) {
                if (res.type != RTN_LOCAL || !accept_local)
-                       goto e_inval_res;
+                       goto e_inval;
        }
        *spec_dst = FIB_RES_PREFSRC(res);
        fib_combine_itag(itag, &res);
 #endif
        if (dev_match) {
                ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
-               fib_res_put(&res);
                return ret;
        }
-       fib_res_put(&res);
        if (no_addr)
                goto last_resort;
        if (rpf == 1)
                        *spec_dst = FIB_RES_PREFSRC(res);
                        ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
                }
-               fib_res_put(&res);
        }
        return ret;
 
        *itag = 0;
        return 0;
 
-e_inval_res:
-       fib_res_put(&res);
 e_inval:
        return -EINVAL;
 e_rpf:
                local_bh_disable();
 
                frn->tb_id = tb->tb_id;
-               frn->err = fib_table_lookup(tb, &fl, &res);
+               rcu_read_lock();
+               frn->err = fib_table_lookup(tb, &fl, &res, FIB_LOOKUP_NOREF);
 
                if (!frn->err) {
                        frn->prefixlen = res.prefixlen;
                        frn->nh_sel = res.nh_sel;
                        frn->type = res.type;
                        frn->scope = res.scope;
-                       fib_res_put(&res);
                }
+               rcu_read_unlock();
                local_bh_enable();
        }
 }
 
 }
 
 int fib_table_lookup(struct fib_table *tb,
-                    const struct flowi *flp, struct fib_result *res)
+                    const struct flowi *flp, struct fib_result *res,
+                    int fib_flags)
 {
        int err;
        struct fn_zone *fz;
 
                        err = fib_semantic_match(&f->fn_alias,
                                                 flp, res,
-                                                fz->fz_order);
+                                                fz->fz_order, fib_flags);
                        if (err <= 0)
                                goto out;
                }
 
 /* Exported by fib_semantics.c */
 extern int fib_semantic_match(struct list_head *head,
                              const struct flowi *flp,
-                             struct fib_result *res, int prefixlen);
+                             struct fib_result *res, int prefixlen, int fib_flags);
 extern void fib_release_info(struct fib_info *);
 extern struct fib_info *fib_create_info(struct fib_config *cfg);
 extern int fib_nh_match(struct fib_config *cfg, struct fib_info *fi);
 
 {
        struct fib_lookup_arg arg = {
                .result = res,
+               .flags = FIB_LOOKUP_NOREF,
        };
        int err;
 
        if (!tbl)
                goto errout;
 
-       err = fib_table_lookup(tbl, flp, (struct fib_result *) arg->result);
+       err = fib_table_lookup(tbl, flp, (struct fib_result *) arg->result, arg->flags);
        if (err > 0)
                err = -EAGAIN;
 errout:
 
 
 /* Release a nexthop info record */
 
+static void free_fib_info_rcu(struct rcu_head *head)
+{
+       struct fib_info *fi = container_of(head, struct fib_info, rcu);
+
+       kfree(fi);
+}
+
 void free_fib_info(struct fib_info *fi)
 {
        if (fi->fib_dead == 0) {
        } endfor_nexthops(fi);
        fib_info_cnt--;
        release_net(fi->fib_net);
-       kfree(fi);
+       call_rcu(&fi->rcu, free_fib_info_rcu);
 }
 
 void fib_release_info(struct fib_info *fi)
                        nh->nh_scope = RT_SCOPE_LINK;
                        return 0;
                }
+               rcu_read_lock();
                {
                        struct flowi fl = {
                                .nl_u = {
                        if (fl.fl4_scope < RT_SCOPE_LINK)
                                fl.fl4_scope = RT_SCOPE_LINK;
                        err = fib_lookup(net, &fl, &res);
-                       if (err)
+                       if (err) {
+                               rcu_read_unlock();
                                return err;
+                       }
                }
                err = -EINVAL;
                if (res.type != RTN_UNICAST && res.type != RTN_LOCAL)
                        goto out;
                err = 0;
 out:
-               fib_res_put(&res);
+               rcu_read_unlock();
                return err;
        } else {
                struct in_device *in_dev;
 
 /* Note! fib_semantic_match intentionally uses  RCU list functions. */
 int fib_semantic_match(struct list_head *head, const struct flowi *flp,
-                      struct fib_result *res, int prefixlen)
+                      struct fib_result *res, int prefixlen, int fib_flags)
 {
        struct fib_alias *fa;
        int nh_sel = 0;
        res->type = fa->fa_type;
        res->scope = fa->fa_scope;
        res->fi = fa->fa_info;
-       atomic_inc(&res->fi->fib_clntref);
+       if (!(fib_flags & FIB_LOOKUP_NOREF))
+               atomic_inc(&res->fi->fib_clntref);
        return 0;
 }
 
 
 /* should be called with rcu_read_lock */
 static int check_leaf(struct trie *t, struct leaf *l,
                      t_key key,  const struct flowi *flp,
-                     struct fib_result *res)
+                     struct fib_result *res, int fib_flags)
 {
        struct leaf_info *li;
        struct hlist_head *hhead = &l->list;
                if (l->key != (key & ntohl(mask)))
                        continue;
 
-               err = fib_semantic_match(&li->falh, flp, res, plen);
+               err = fib_semantic_match(&li->falh, flp, res, plen, fib_flags);
 
 #ifdef CONFIG_IP_FIB_TRIE_STATS
                if (err <= 0)
 }
 
 int fib_table_lookup(struct fib_table *tb, const struct flowi *flp,
-                    struct fib_result *res)
+                    struct fib_result *res, int fib_flags)
 {
        struct trie *t = (struct trie *) tb->tb_data;
        int ret;
 
        /* Just a leaf? */
        if (IS_LEAF(n)) {
-               ret = check_leaf(t, (struct leaf *)n, key, flp, res);
+               ret = check_leaf(t, (struct leaf *)n, key, flp, res, fib_flags);
                goto found;
        }
 
                }
 
                if (IS_LEAF(n)) {
-                       ret = check_leaf(t, (struct leaf *)n, key, flp, res);
+                       ret = check_leaf(t, (struct leaf *)n, key, flp, res, fib_flags);
                        if (ret > 0)
                                goto backtrace;
                        goto found;
 
 
        if (rt->fl.iif == 0)
                src = rt->rt_src;
-       else if (fib_lookup(dev_net(rt->dst.dev), &rt->fl, &res) == 0) {
-               src = FIB_RES_PREFSRC(res);
-               fib_res_put(&res);
-       } else
-               src = inet_select_addr(rt->dst.dev, rt->rt_gateway,
+       else {
+               rcu_read_lock();
+               if (fib_lookup(dev_net(rt->dst.dev), &rt->fl, &res) == 0)
+                       src = FIB_RES_PREFSRC(res);
+               else
+                       src = inet_select_addr(rt->dst.dev, rt->rt_gateway,
                                        RT_SCOPE_UNIVERSE);
+               rcu_read_unlock();
+       }
        memcpy(addr, &src, 4);
 }
 
  *     Such approach solves two big problems:
  *     1. Not simplex devices are handled properly.
  *     2. IP spoofing attempts are filtered with 100% of guarantee.
+ *     called with rcu_read_lock()
  */
 
 static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
        unsigned        hash;
        __be32          spec_dst;
        int             err = -EINVAL;
-       int             free_res = 0;
        struct net    * net = dev_net(dev);
 
        /* IP on this device is disabled. */
        /*
         *      Now we are ready to route packet.
         */
-       if ((err = fib_lookup(net, &fl, &res)) != 0) {
+       err = fib_lookup(net, &fl, &res);
+       if (err != 0) {
                if (!IN_DEV_FORWARD(in_dev))
                        goto e_hostunreach;
                goto no_route;
        }
-       free_res = 1;
 
        RT_CACHE_STAT_INC(in_slow_tot);
 
 
        if (res.type == RTN_LOCAL) {
                err = fib_validate_source(saddr, daddr, tos,
-                                            net->loopback_dev->ifindex,
-                                            dev, &spec_dst, &itag, skb->mark);
+                                         net->loopback_dev->ifindex,
+                                         dev, &spec_dst, &itag, skb->mark);
                if (err < 0)
                        goto martian_source_keep_err;
                if (err)
                goto martian_destination;
 
        err = ip_mkroute_input(skb, &res, &fl, in_dev, daddr, saddr, tos);
-done:
-       if (free_res)
-               fib_res_put(&res);
 out:   return err;
 
 brd_input:
        rth->rt_type    = res.type;
        hash = rt_hash(daddr, saddr, fl.iif, rt_genid(net));
        err = rt_intern_hash(hash, rth, NULL, skb, fl.iif);
-       goto done;
+       goto out;
 
 no_route:
        RT_CACHE_STAT_INC(in_no_route);
 
 e_hostunreach:
        err = -EHOSTUNREACH;
-       goto done;
+       goto out;
 
 e_inval:
        err = -EINVAL;
-       goto done;
+       goto out;
 
 e_nobufs:
        err = -ENOBUFS;
-       goto done;
+       goto out;
 
 martian_source:
        err = -EINVAL;
 martian_source_keep_err:
        ip_handle_martian_source(dev, in_dev, skb, daddr, saddr);
-       goto done;
+       goto out;
 }
 
 int ip_route_input_common(struct sk_buff *skb, __be32 daddr, __be32 saddr,
 }
 EXPORT_SYMBOL(ip_route_input_common);
 
+/* called with rcu_read_lock() */
 static int __mkroute_output(struct rtable **result,
                            struct fib_result *res,
                            const struct flowi *fl,
        if (dev_out->flags & IFF_LOOPBACK)
                flags |= RTCF_LOCAL;
 
-       rcu_read_lock();
        in_dev = __in_dev_get_rcu(dev_out);
-       if (!in_dev) {
-               rcu_read_unlock();
+       if (!in_dev)
                return -EINVAL;
-       }
+
        if (res->type == RTN_BROADCAST) {
                flags |= RTCF_BROADCAST | RTCF_LOCAL;
-               if (res->fi) {
-                       fib_info_put(res->fi);
-                       res->fi = NULL;
-               }
+               res->fi = NULL;
        } else if (res->type == RTN_MULTICAST) {
                flags |= RTCF_MULTICAST | RTCF_LOCAL;
                if (!ip_check_mc(in_dev, oldflp->fl4_dst, oldflp->fl4_src,
                 * default one, but do not gateway in this case.
                 * Yes, it is hack.
                 */
-               if (res->fi && res->prefixlen < 4) {
-                       fib_info_put(res->fi);
+               if (res->fi && res->prefixlen < 4)
                        res->fi = NULL;
-               }
        }
 
 
        return 0;
 }
 
+/* called with rcu_read_lock() */
 static int ip_mkroute_output(struct rtable **rp,
                             struct fib_result *res,
                             const struct flowi *fl,
        struct fib_result res;
        unsigned int flags = 0;
        struct net_device *dev_out = NULL;
-       int free_res = 0;
        int err;
 
 
                err = -ENETUNREACH;
                goto out;
        }
-       free_res = 1;
 
        if (res.type == RTN_LOCAL) {
                if (!fl.fl4_src)
                        fl.fl4_src = fl.fl4_dst;
                dev_out = net->loopback_dev;
                fl.oif = dev_out->ifindex;
-               if (res.fi)
-                       fib_info_put(res.fi);
                res.fi = NULL;
                flags |= RTCF_LOCAL;
                goto make_route;
 make_route:
        err = ip_mkroute_output(rp, &res, &fl, oldflp, dev_out, flags);
 
-       if (free_res)
-               fib_res_put(&res);
 out:   return err;
 }