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);

}

猜你喜欢

转载自blog.csdn.net/eeeeety6208/article/details/127029797