• Linux邻居协议 学习笔记 之四 通用邻居项创建、查找、删除等相关的函数


    首先介绍neigh_create函数,分析如下

    1、调用neigh_alloc,申请一个邻居项缓存

    2、设置邻居项的primary_key、dev值,并增加dev的引用计数

    3、调用邻居表的constructor,初始化邻居表协议相关的参数

    4、如果存在n->parms->neigh_setup,则调用neigh_setup进行初始化(对于arp,该函数

    为NULL)

    5、设置confirmed时间

    6、如果创建的邻居项总数超过了邻居表定义的最大值,则调用neigh_hash_grow扩充

    邻居项的hash表容量

    7、在将创建的邻居项插入到邻居项hash表之前,需要再次查找hash表里的邻居项:

           a)如果该邻居项已经存在hash表里,则增加对该邻居项的引用计数,并释放已

    申请的邻居项缓存

           b)如果该邻居项还没有在hash表里,则将该邻居项插入的hash表里。

    struct neighbour*neigh_create(struct neigh_table *tbl, const void *pkey,

                                struct net_device *dev)

    {

           u32 hash_val;

           int key_len = tbl->key_len;

           int error;

           struct neighbour *n1, *rc, *n =neigh_alloc(tbl);

           if (!n) {

                  rc = ERR_PTR(-ENOBUFS);

                  goto out;

           }

           memcpy(n->primary_key, pkey, key_len);

           n->dev = dev;

           dev_hold(dev);

           /* Protocol specific setup. */

           if (tbl->constructor && (error = tbl->constructor(n)) < 0) {

                  rc = ERR_PTR(error);

                  goto out_neigh_release;

           }

           /* Device specific setup. */

           if (n->parms->neigh_setup&&

              (error = n->parms->neigh_setup(n)) < 0) {

                  rc = ERR_PTR(error);

                  goto out_neigh_release;

           }

           n->confirmed = jiffies -(n->parms->base_reachable_time << 1);

           write_lock_bh(&tbl->lock);

           if (atomic_read(&tbl->entries)> (tbl->hash_mask + 1))

                  neigh_hash_grow(tbl,(tbl->hash_mask + 1) << 1);

           hash_val = tbl->hash(pkey, dev) &tbl->hash_mask;

           if (n->parms->dead) {

                  rc = ERR_PTR(-EINVAL);

                  goto out_tbl_unlock;

           }

           for (n1 = tbl->hash_buckets[hash_val];n1; n1 = n1->next) {

                  if (dev == n1->dev &&!memcmp(n1->primary_key, pkey, key_len)) {

                         neigh_hold(n1);

                         rc = n1;

                         goto out_tbl_unlock;

                  }

           }

           n->next =tbl->hash_buckets[hash_val];

           tbl->hash_buckets[hash_val] = n;

           n->dead = 0;

           neigh_hold(n);

           write_unlock_bh(&tbl->lock);

           NEIGH_PRINTK2("neigh %p iscreated.\n", n);

           rc = n;

    out:

           return rc;

    out_tbl_unlock:

           write_unlock_bh(&tbl->lock);

    out_neigh_release:

           neigh_release(n);

           goto out;

    }

    由于该函数调用了neigh_alloc,下面我们分析函数neigh_alloc

    从邻居表的slab缓存里,申请一个邻居项,并进行通用邻居层的初始化

    static structneighbour *neigh_alloc(struct neigh_table *tbl)

    {

           struct neighbour *n = NULL;

           unsigned long now = jiffies;

           int entries;

           entries =atomic_inc_return(&tbl->entries) - 1;

           if (entries >= tbl->gc_thresh3 ||

              (entries >= tbl->gc_thresh2 &&

               time_after(now, tbl->last_flush + 5 * HZ))) {

                  if (!neigh_forced_gc(tbl)&&

                     entries >= tbl->gc_thresh3)

                         goto out_entries;

           }

           n =kmem_cache_zalloc(tbl->kmem_cachep, GFP_ATOMIC);

           if (!n)

                  goto out_entries;

           skb_queue_head_init(&n->arp_queue);

           rwlock_init(&n->lock);

           n->updated       = n->used = now;

           n->nud_state     = NUD_NONE;

           n->output         = neigh_blackhole;

           n->parms   =neigh_parms_clone(&tbl->parms);//直接从邻居表中克隆

           setup_timer(&n->timer,neigh_timer_handler, (unsigned long)n);//创建定时器

           NEIGH_CACHE_STAT_INC(tbl, allocs);

           n->tbl              = tbl;

           atomic_set(&n->refcnt, 1);

           n->dead            = 1;

    out:

           return n;

    out_entries:

           atomic_dec(&tbl->entries);

           goto out;

    }

    下面分析通用邻居项的查找函数,主要是有neigh_lookup、neigh_lookup_nodev、__neigh_lookup、__neigh_lookup_errno

    函数neigh_lookup的功能是根据关键字net_device与pkey在邻居表里查找一个邻居项

    1、根据关键字net_device与pkey,计算hash值

    2、通过hash值,在邻居项hash数组中,找到指定的hash链表

    3、遍历指定的hash链表,比较net_device与pkey值,查找符合条件的邻居项。

    struct neighbour*neigh_lookup(struct neigh_table *tbl, const void *pkey,

                                struct net_device *dev)

    {

           struct neighbour *n;

           int key_len = tbl->key_len;

           u32 hash_val;

           NEIGH_CACHE_STAT_INC(tbl, lookups);

           read_lock_bh(&tbl->lock);

           hash_val = tbl->hash(pkey, dev);

           for (n = tbl->hash_buckets[hash_val& tbl->hash_mask]; n; n = n->next) {

                  if (dev == n->dev &&!memcmp(n->primary_key, pkey, key_len)) {

                         neigh_hold(n);

                         NEIGH_CACHE_STAT_INC(tbl,hits);

                         break;

                  }

           }

           read_unlock_bh(&tbl->lock);

           return n;

    }

    函数neigh_lookup_nodev功能是根据关键字net与pkey在邻居表里查找一个邻居项

    该函数的功能与neigh_lookup类似

    struct neighbour*neigh_lookup_nodev(struct neigh_table *tbl, struct net *net,

                                     const void *pkey)

    {

           struct neighbour *n;

           int key_len = tbl->key_len;

           u32 hash_val;

           NEIGH_CACHE_STAT_INC(tbl, lookups);

           read_lock_bh(&tbl->lock);

           hash_val = tbl->hash(pkey, NULL);

           for (n = tbl->hash_buckets[hash_val& tbl->hash_mask]; n; n = n->next) {

                  if (!memcmp(n->primary_key,pkey, key_len) &&

                     net_eq(dev_net(n->dev), net)) {

                         neigh_hold(n);

                         NEIGH_CACHE_STAT_INC(tbl,hits);

                         break;

                  }

           }

           read_unlock_bh(&tbl->lock);

           return n;

    }

    函数__neigh_lookup的功能是同时实现查找邻居项与决定是否创建邻居项,该函数通常被其他协议子层调用。

    static inlinestruct neighbour *

    __neigh_lookup(structneigh_table *tbl, const void *pkey, struct net_device *dev, int creat)

    {

           struct neighbour *n = neigh_lookup(tbl,pkey, dev);

           if (n || !creat)

                  return n;

           n = neigh_create(tbl, pkey, dev);

           return IS_ERR(n) ? NULL : n;

    }

    该函数与函数__neigh_lookup相比,其在查找不到邻居项的情况下,即会默认创建一个新的邻居项。

    static inlinestruct neighbour *

    __neigh_lookup_errno(structneigh_table *tbl, const void *pkey,

      struct net_device *dev)

    {

           struct neighbour *n = neigh_lookup(tbl,pkey, dev);

           if (n)

                  return n;

           return neigh_create(tbl, pkey, dev);

    }

    下面分析一下邻居项hash表容量的扩容函数neigh_hash_grow

    功能:邻居项散列表的扩容

    1、为邻居表申请一个hash bucket数组指针,

    2、然后将原hash bucket中所有的邻居项,重新计算hash值后,存放在新的hash链表

    3、释放原hash bucket数组所占用的缓存。

    static voidneigh_hash_grow(struct neigh_table *tbl, unsigned long new_entries)

    {

           struct neighbour **new_hash, **old_hash;

           unsigned int i, new_hash_mask,old_entries;

           NEIGH_CACHE_STAT_INC(tbl, hash_grows);

           BUG_ON(!is_power_of_2(new_entries));

           new_hash = neigh_hash_alloc(new_entries);

           if (!new_hash)

                  return;

           old_entries = tbl->hash_mask + 1;

           new_hash_mask = new_entries - 1;

           old_hash = tbl->hash_buckets;

           get_random_bytes(&tbl->hash_rnd,sizeof(tbl->hash_rnd));

           for (i = 0; i < old_entries; i++) {

                  struct neighbour *n, *next;

                  for (n = old_hash[i]; n; n = next){

                         unsigned int hash_val =tbl->hash(n->primary_key, n->dev);

                         hash_val &=new_hash_mask;

                         next = n->next;

                         n->next =new_hash[hash_val];

                         new_hash[hash_val] = n;

                  }

           }

           tbl->hash_buckets = new_hash;

           tbl->hash_mask = new_hash_mask;

           neigh_hash_free(old_hash, old_entries);

    }

    下面分析邻居项的删除函数neigh_destroy

    1、判断该邻居项是否可以删除 dead==1,则可以删除

    2、删除该邻居项的定时器

    3、对于可以删除的邻居项, 将邻居项所关联的所有二层缓存头的hh_output设置

           为neigh_blackhole,即直接丢弃数据包

    4、调用skb_queue_purge,丢弃队列中所有待发送的数据包

    5、取消对net_dev、neigh_parms的引用

    6、最后调用kmem_cache_free,将邻居项的缓存释放给邻居表的slab缓存中。

    voidneigh_destroy(struct neighbour *neigh)

    {

           struct hh_cache *hh;

           NEIGH_CACHE_STAT_INC(neigh->tbl,destroys);

           if (!neigh->dead) {

                  printk(KERN_WARNING

                         "Destroying alive neighbour%p\n", neigh);

                  dump_stack();

                  return;

           }

           if (neigh_del_timer(neigh))

                  printk(KERN_WARNING"Impossible event.\n");

           while ((hh = neigh->hh) != NULL) {

                  neigh->hh = hh->hh_next;

                  hh->hh_next = NULL;

                  write_seqlock_bh(&hh->hh_lock);

                  hh->hh_output =neigh_blackhole;

                  write_sequnlock_bh(&hh->hh_lock);

                  if(atomic_dec_and_test(&hh->hh_refcnt))

                         kfree(hh);

           }

           skb_queue_purge(&neigh->arp_queue);

           dev_put(neigh->dev);

           neigh_parms_put(neigh->parms);

           NEIGH_PRINTK2("neigh %p isdestroyed.\n", neigh);

           atomic_dec(&neigh->tbl->entries);

           kmem_cache_free(neigh->tbl->kmem_cachep,neigh);

    }

    函数neigh_release是neigh_destroy的包裹函数,增加了对neigh->refcnt的判断。

    如果邻居项的引用计数为1时,则调用neigh_destroy释放该邻居项所对应的缓存

    static inlinevoid neigh_release(struct neighbour *neigh)

    {

           if(atomic_dec_and_test(&neigh->refcnt))

                  neigh_destroy(neigh);

    }

    下面分析一下一些小函数

    neigh_clone,该函数只是增加对邻居项的引用,而并不clone一个邻居项

    static inlinestruct neighbour * neigh_clone(struct neighbour *neigh)

    {

           if (neigh)

                  atomic_inc(&neigh->refcnt);

           return neigh;

    }

    设置邻居项的confirmed值为当前时间

    static inlinevoid neigh_confirm(struct neighbour *neigh)

    {

           if (neigh)

                  neigh->confirmed = jiffies;

    }

    /*

           从hh_cache中拷贝二层头部,并调用hh_cache->output输出skb

    */

    static inlineint neigh_hh_output(struct hh_cache *hh, struct sk_buff *skb)

    {

           unsigned seq;

           int hh_len;

           do {

                  int hh_alen;

                  seq =read_seqbegin(&hh->hh_lock);

                  hh_len = hh->hh_len;

                  hh_alen = HH_DATA_ALIGN(hh_len);

                  memcpy(skb->data - hh_alen,hh->hh_data, hh_alen);

           } while(read_seqretry(&hh->hh_lock, seq));

           skb_push(skb, hh_len);

           return hh->hh_output(skb);

    }

  • 相关阅读:
    5G专网在工业场景中的应用及仿真
    wasm-在浏览器中使用python
    windbg分析进程卡死
    LLM探索:为ChatGLM2的gRPC后端增加连续对话功能
    【Vue五分钟】五分钟了解组件之间的通信方式
    JVM的基础了解
    移动app测试的7个关键点,建议收藏
    Nginx 学习(五)Tomcat 服务器
    tkinter控件样式
    【C++ 学习 ㉝】- C++11 使用 using 定义别名
  • 原文地址:https://blog.csdn.net/eeeeety6208/article/details/127029797