Home | History | Annotate | Line # | Download | only in dns
      1  1.9  christos /*	$NetBSD: badcache.c,v 1.9 2025/01/26 16:25:21 christos Exp $	*/
      2  1.1  christos 
      3  1.1  christos /*
      4  1.1  christos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  1.1  christos  *
      6  1.6  christos  * SPDX-License-Identifier: MPL-2.0
      7  1.6  christos  *
      8  1.1  christos  * This Source Code Form is subject to the terms of the Mozilla Public
      9  1.1  christos  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  1.5  christos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  1.1  christos  *
     12  1.1  christos  * See the COPYRIGHT file distributed with this work for additional
     13  1.1  christos  * information regarding copyright ownership.
     14  1.1  christos  */
     15  1.1  christos 
     16  1.1  christos /*! \file */
     17  1.1  christos 
     18  1.3  christos #include <inttypes.h>
     19  1.3  christos #include <stdbool.h>
     20  1.3  christos 
     21  1.9  christos #include <isc/async.h>
     22  1.1  christos #include <isc/buffer.h>
     23  1.4  christos #include <isc/hash.h>
     24  1.1  christos #include <isc/log.h>
     25  1.9  christos #include <isc/loop.h>
     26  1.1  christos #include <isc/mem.h>
     27  1.1  christos #include <isc/mutex.h>
     28  1.4  christos #include <isc/rwlock.h>
     29  1.9  christos #include <isc/spinlock.h>
     30  1.9  christos #include <isc/stdtime.h>
     31  1.1  christos #include <isc/string.h>
     32  1.9  christos #include <isc/thread.h>
     33  1.1  christos #include <isc/time.h>
     34  1.9  christos #include <isc/urcu.h>
     35  1.1  christos #include <isc/util.h>
     36  1.1  christos 
     37  1.1  christos #include <dns/badcache.h>
     38  1.8  christos #include <dns/fixedname.h>
     39  1.1  christos #include <dns/name.h>
     40  1.1  christos #include <dns/rdatatype.h>
     41  1.1  christos #include <dns/types.h>
     42  1.1  christos 
     43  1.1  christos typedef struct dns_bcentry dns_bcentry_t;
     44  1.1  christos 
     45  1.9  christos typedef struct dns_bckey {
     46  1.9  christos 	const dns_name_t *name;
     47  1.9  christos 	dns_rdatatype_t type;
     48  1.9  christos } dns__bckey_t;
     49  1.9  christos 
     50  1.1  christos struct dns_badcache {
     51  1.4  christos 	unsigned int magic;
     52  1.4  christos 	isc_mem_t *mctx;
     53  1.9  christos 	struct cds_lfht *ht;
     54  1.9  christos 	struct cds_list_head *lru;
     55  1.9  christos 	uint32_t nloops;
     56  1.1  christos };
     57  1.1  christos 
     58  1.4  christos #define BADCACHE_MAGIC	  ISC_MAGIC('B', 'd', 'C', 'a')
     59  1.4  christos #define VALID_BADCACHE(m) ISC_MAGIC_VALID(m, BADCACHE_MAGIC)
     60  1.1  christos 
     61  1.9  christos #define BADCACHE_INIT_SIZE (1 << 10) /* Must be power of 2 */
     62  1.9  christos #define BADCACHE_MIN_SIZE  (1 << 8)  /* Must be power of 2 */
     63  1.9  christos 
     64  1.1  christos struct dns_bcentry {
     65  1.9  christos 	isc_loop_t *loop;
     66  1.9  christos 	isc_stdtime_t expire;
     67  1.9  christos 	uint32_t flags;
     68  1.9  christos 
     69  1.9  christos 	struct cds_lfht_node ht_node;
     70  1.9  christos 	struct rcu_head rcu_head;
     71  1.9  christos 	struct cds_list_head lru_head;
     72  1.9  christos 
     73  1.9  christos 	dns_name_t name;
     74  1.4  christos 	dns_rdatatype_t type;
     75  1.1  christos };
     76  1.1  christos 
     77  1.4  christos static void
     78  1.9  christos bcentry_print(dns_bcentry_t *bad, isc_stdtime_t now, FILE *fp);
     79  1.1  christos 
     80  1.9  christos static void
     81  1.9  christos bcentry_destroy(struct rcu_head *rcu_head);
     82  1.1  christos 
     83  1.9  christos static bool
     84  1.9  christos bcentry_alive(struct cds_lfht *ht, dns_bcentry_t *bad, isc_stdtime_t now);
     85  1.1  christos 
     86  1.9  christos dns_badcache_t *
     87  1.9  christos dns_badcache_new(isc_mem_t *mctx, isc_loopmgr_t *loopmgr) {
     88  1.9  christos 	REQUIRE(loopmgr != NULL);
     89  1.9  christos 
     90  1.9  christos 	uint32_t nloops = isc_loopmgr_nloops(loopmgr);
     91  1.9  christos 	dns_badcache_t *bc = isc_mem_get(mctx, sizeof(*bc));
     92  1.9  christos 	*bc = (dns_badcache_t){
     93  1.9  christos 		.magic = BADCACHE_MAGIC,
     94  1.9  christos 		.nloops = nloops,
     95  1.9  christos 	};
     96  1.9  christos 
     97  1.9  christos 	bc->ht = cds_lfht_new(BADCACHE_INIT_SIZE, BADCACHE_MIN_SIZE, 0,
     98  1.9  christos 			      CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
     99  1.9  christos 	INSIST(bc->ht != NULL);
    100  1.9  christos 
    101  1.9  christos 	bc->lru = isc_mem_cget(mctx, bc->nloops, sizeof(bc->lru[0]));
    102  1.9  christos 	for (size_t i = 0; i < bc->nloops; i++) {
    103  1.9  christos 		CDS_INIT_LIST_HEAD(&bc->lru[i]);
    104  1.9  christos 	}
    105  1.1  christos 
    106  1.1  christos 	isc_mem_attach(mctx, &bc->mctx);
    107  1.1  christos 
    108  1.9  christos 	return bc;
    109  1.1  christos }
    110  1.1  christos 
    111  1.1  christos void
    112  1.1  christos dns_badcache_destroy(dns_badcache_t **bcp) {
    113  1.9  christos 	REQUIRE(bcp != NULL && *bcp != NULL);
    114  1.9  christos 	REQUIRE(VALID_BADCACHE(*bcp));
    115  1.1  christos 
    116  1.9  christos 	dns_badcache_t *bc = *bcp;
    117  1.4  christos 	*bcp = NULL;
    118  1.9  christos 	bc->magic = 0;
    119  1.9  christos 
    120  1.9  christos 	dns_bcentry_t *bad = NULL;
    121  1.9  christos 	struct cds_lfht_iter iter;
    122  1.9  christos 	cds_lfht_for_each_entry(bc->ht, &iter, bad, ht_node) {
    123  1.9  christos 		INSIST(!cds_lfht_del(bc->ht, &bad->ht_node));
    124  1.9  christos 		bcentry_destroy(&bad->rcu_head);
    125  1.9  christos 	}
    126  1.9  christos 	RUNTIME_CHECK(!cds_lfht_destroy(bc->ht, NULL));
    127  1.1  christos 
    128  1.9  christos 	isc_mem_cput(bc->mctx, bc->lru, bc->nloops, sizeof(bc->lru[0]));
    129  1.1  christos 
    130  1.1  christos 	isc_mem_putanddetach(&bc->mctx, bc, sizeof(dns_badcache_t));
    131  1.1  christos }
    132  1.1  christos 
    133  1.9  christos static int
    134  1.9  christos bcentry_match(struct cds_lfht_node *ht_node, const void *key0) {
    135  1.9  christos 	const dns__bckey_t *key = key0;
    136  1.9  christos 	dns_bcentry_t *bad = caa_container_of(ht_node, dns_bcentry_t, ht_node);
    137  1.9  christos 
    138  1.9  christos 	return (bad->type == key->type) &&
    139  1.9  christos 	       dns_name_equal(&bad->name, key->name);
    140  1.9  christos }
    141  1.9  christos 
    142  1.9  christos static uint32_t
    143  1.9  christos bcentry_hash(const dns__bckey_t *key) {
    144  1.9  christos 	isc_hash32_t state;
    145  1.9  christos 	isc_hash32_init(&state);
    146  1.9  christos 	isc_hash32_hash(&state, key->name->ndata, key->name->length, false);
    147  1.9  christos 	isc_hash32_hash(&state, &key->type, sizeof(key->type), true);
    148  1.9  christos 	return isc_hash32_finalize(&state);
    149  1.9  christos }
    150  1.9  christos 
    151  1.9  christos static dns_bcentry_t *
    152  1.9  christos bcentry_lookup(struct cds_lfht *ht, uint32_t hashval, dns__bckey_t *key) {
    153  1.9  christos 	struct cds_lfht_iter iter;
    154  1.9  christos 
    155  1.9  christos 	cds_lfht_lookup(ht, hashval, bcentry_match, key, &iter);
    156  1.9  christos 
    157  1.9  christos 	return cds_lfht_entry(cds_lfht_iter_get_node(&iter), dns_bcentry_t,
    158  1.9  christos 			      ht_node);
    159  1.9  christos }
    160  1.9  christos 
    161  1.9  christos static dns_bcentry_t *
    162  1.9  christos bcentry_new(isc_loop_t *loop, const dns_name_t *name,
    163  1.9  christos 	    const dns_rdatatype_t type, const uint32_t flags,
    164  1.9  christos 	    const isc_stdtime_t expire) {
    165  1.9  christos 	isc_mem_t *mctx = isc_loop_getmctx(loop);
    166  1.9  christos 	dns_bcentry_t *bad = isc_mem_get(mctx, sizeof(*bad));
    167  1.9  christos 	*bad = (dns_bcentry_t){
    168  1.9  christos 		.type = type,
    169  1.9  christos 		.flags = flags,
    170  1.9  christos 		.expire = expire,
    171  1.9  christos 		.loop = isc_loop_ref(loop),
    172  1.9  christos 		.lru_head = CDS_LIST_HEAD_INIT(bad->lru_head),
    173  1.9  christos 	};
    174  1.9  christos 
    175  1.9  christos 	dns_name_init(&bad->name, NULL);
    176  1.9  christos 	dns_name_dup(name, mctx, &bad->name);
    177  1.9  christos 
    178  1.9  christos 	return bad;
    179  1.9  christos }
    180  1.9  christos 
    181  1.4  christos static void
    182  1.9  christos bcentry_destroy(struct rcu_head *rcu_head) {
    183  1.9  christos 	dns_bcentry_t *bad = caa_container_of(rcu_head, dns_bcentry_t,
    184  1.9  christos 					      rcu_head);
    185  1.9  christos 	isc_loop_t *loop = bad->loop;
    186  1.9  christos 	isc_mem_t *mctx = isc_loop_getmctx(loop);
    187  1.9  christos 
    188  1.9  christos 	dns_name_free(&bad->name, mctx);
    189  1.9  christos 	isc_mem_put(mctx, bad, sizeof(*bad));
    190  1.9  christos 
    191  1.9  christos 	isc_loop_unref(loop);
    192  1.9  christos }
    193  1.9  christos 
    194  1.9  christos static void
    195  1.9  christos bcentry_evict_async(void *arg) {
    196  1.9  christos 	dns_bcentry_t *bad = arg;
    197  1.9  christos 
    198  1.9  christos 	RUNTIME_CHECK(bad->loop == isc_loop());
    199  1.9  christos 
    200  1.9  christos 	cds_list_del(&bad->lru_head);
    201  1.9  christos 	call_rcu(&bad->rcu_head, bcentry_destroy);
    202  1.9  christos }
    203  1.9  christos 
    204  1.9  christos static void
    205  1.9  christos bcentry_evict(struct cds_lfht *ht, dns_bcentry_t *bad) {
    206  1.9  christos 	if (!cds_lfht_del(ht, &bad->ht_node)) {
    207  1.9  christos 		if (bad->loop == isc_loop()) {
    208  1.9  christos 			bcentry_evict_async(bad);
    209  1.9  christos 			return;
    210  1.1  christos 		}
    211  1.9  christos 
    212  1.9  christos 		isc_async_run(bad->loop, bcentry_evict_async, bad);
    213  1.9  christos 	}
    214  1.9  christos }
    215  1.9  christos 
    216  1.9  christos static bool
    217  1.9  christos bcentry_alive(struct cds_lfht *ht, dns_bcentry_t *bad, isc_stdtime_t now) {
    218  1.9  christos 	if (cds_lfht_is_node_deleted(&bad->ht_node)) {
    219  1.9  christos 		return false;
    220  1.9  christos 	} else if (bad->expire < now) {
    221  1.9  christos 		bcentry_evict(ht, bad);
    222  1.9  christos 		return false;
    223  1.1  christos 	}
    224  1.1  christos 
    225  1.9  christos 	return true;
    226  1.9  christos }
    227  1.4  christos 
    228  1.9  christos #define cds_lfht_for_each_entry_next(ht, iter, pos, member)     \
    229  1.9  christos 	for (cds_lfht_next(ht, iter),                           \
    230  1.9  christos 	     pos = cds_lfht_entry(cds_lfht_iter_get_node(iter), \
    231  1.9  christos 				  __typeof__(*(pos)), member);  \
    232  1.9  christos 	     pos != NULL; /**/                                  \
    233  1.9  christos 	     cds_lfht_next(ht, iter),                           \
    234  1.9  christos 	     pos = cds_lfht_entry(cds_lfht_iter_get_node(iter), \
    235  1.9  christos 				  __typeof__(*(pos)), member))
    236  1.1  christos 
    237  1.9  christos static void
    238  1.9  christos bcentry_purge(struct cds_lfht *ht, struct cds_list_head *lru,
    239  1.9  christos 	      isc_stdtime_t now) {
    240  1.9  christos 	size_t count = 10;
    241  1.9  christos 	dns_bcentry_t *bad;
    242  1.9  christos 	cds_list_for_each_entry_rcu(bad, lru, lru_head) {
    243  1.9  christos 		if (bcentry_alive(ht, bad, now)) {
    244  1.9  christos 			break;
    245  1.9  christos 		}
    246  1.9  christos 		if (--count == 0) {
    247  1.9  christos 			break;
    248  1.9  christos 		}
    249  1.9  christos 	}
    250  1.1  christos }
    251  1.1  christos 
    252  1.1  christos void
    253  1.1  christos dns_badcache_add(dns_badcache_t *bc, const dns_name_t *name,
    254  1.9  christos 		 dns_rdatatype_t type, uint32_t flags, isc_stdtime_t expire) {
    255  1.1  christos 	REQUIRE(VALID_BADCACHE(bc));
    256  1.1  christos 	REQUIRE(name != NULL);
    257  1.1  christos 
    258  1.9  christos 	isc_loop_t *loop = isc_loop();
    259  1.9  christos 	uint32_t tid = isc_tid();
    260  1.9  christos 	struct cds_list_head *lru = &bc->lru[tid];
    261  1.9  christos 
    262  1.9  christos 	isc_stdtime_t now = isc_stdtime_now();
    263  1.9  christos 	if (expire < now) {
    264  1.9  christos 		expire = now;
    265  1.9  christos 	}
    266  1.9  christos 
    267  1.9  christos 	rcu_read_lock();
    268  1.9  christos 	struct cds_lfht *ht = rcu_dereference(bc->ht);
    269  1.9  christos 	INSIST(ht != NULL);
    270  1.9  christos 
    271  1.9  christos 	dns__bckey_t key = {
    272  1.9  christos 		.name = name,
    273  1.9  christos 		.type = type,
    274  1.9  christos 	};
    275  1.9  christos 	uint32_t hashval = bcentry_hash(&key);
    276  1.9  christos 
    277  1.9  christos 	/* struct cds_lfht_iter iter; */
    278  1.9  christos 	dns_bcentry_t *bad = bcentry_new(loop, name, type, flags, expire);
    279  1.9  christos 	struct cds_lfht_node *ht_node;
    280  1.9  christos 	do {
    281  1.9  christos 		ht_node = cds_lfht_add_unique(ht, hashval, bcentry_match, &key,
    282  1.9  christos 					      &bad->ht_node);
    283  1.9  christos 		if (ht_node != &bad->ht_node) {
    284  1.9  christos 			dns_bcentry_t *found = caa_container_of(
    285  1.9  christos 				ht_node, dns_bcentry_t, ht_node);
    286  1.9  christos 			bcentry_evict(ht, found);
    287  1.9  christos 		}
    288  1.9  christos 	} while (ht_node != &bad->ht_node);
    289  1.1  christos 
    290  1.9  christos 	/* No locking, instead we are using per-thread lists */
    291  1.9  christos 	cds_list_add_tail_rcu(&bad->lru_head, lru);
    292  1.1  christos 
    293  1.9  christos 	bcentry_purge(ht, lru, now);
    294  1.1  christos 
    295  1.9  christos 	rcu_read_unlock();
    296  1.1  christos }
    297  1.1  christos 
    298  1.9  christos isc_result_t
    299  1.1  christos dns_badcache_find(dns_badcache_t *bc, const dns_name_t *name,
    300  1.9  christos 		  dns_rdatatype_t type, uint32_t *flagp, isc_stdtime_t now) {
    301  1.1  christos 	REQUIRE(VALID_BADCACHE(bc));
    302  1.1  christos 	REQUIRE(name != NULL);
    303  1.1  christos 
    304  1.9  christos 	isc_result_t result = ISC_R_NOTFOUND;
    305  1.9  christos 
    306  1.9  christos 	rcu_read_lock();
    307  1.9  christos 	struct cds_lfht *ht = rcu_dereference(bc->ht);
    308  1.9  christos 	INSIST(ht != NULL);
    309  1.9  christos 
    310  1.9  christos 	dns__bckey_t key = {
    311  1.9  christos 		.name = name,
    312  1.9  christos 		.type = type,
    313  1.9  christos 	};
    314  1.9  christos 	uint32_t hashval = bcentry_hash(&key);
    315  1.1  christos 
    316  1.9  christos 	dns_bcentry_t *found = bcentry_lookup(ht, hashval, &key);
    317  1.1  christos 
    318  1.9  christos 	if (found != NULL && bcentry_alive(ht, found, now)) {
    319  1.9  christos 		result = ISC_R_SUCCESS;
    320  1.9  christos 		if (flagp != NULL) {
    321  1.9  christos 			*flagp = found->flags;
    322  1.1  christos 		}
    323  1.1  christos 	}
    324  1.1  christos 
    325  1.9  christos 	uint32_t tid = isc_tid();
    326  1.9  christos 	struct cds_list_head *lru = &bc->lru[tid];
    327  1.9  christos 	bcentry_purge(ht, lru, now);
    328  1.9  christos 
    329  1.9  christos 	rcu_read_unlock();
    330  1.1  christos 
    331  1.9  christos 	return result;
    332  1.1  christos }
    333  1.1  christos 
    334  1.1  christos void
    335  1.1  christos dns_badcache_flush(dns_badcache_t *bc) {
    336  1.1  christos 	REQUIRE(VALID_BADCACHE(bc));
    337  1.1  christos 
    338  1.9  christos 	rcu_read_lock();
    339  1.9  christos 	struct cds_lfht *ht = rcu_dereference(bc->ht);
    340  1.9  christos 	INSIST(ht != NULL);
    341  1.9  christos 
    342  1.9  christos 	/* Flush the hash table */
    343  1.9  christos 	dns_bcentry_t *bad;
    344  1.9  christos 	struct cds_lfht_iter iter;
    345  1.9  christos 	cds_lfht_for_each_entry(ht, &iter, bad, ht_node) {
    346  1.9  christos 		bcentry_evict(ht, bad);
    347  1.1  christos 	}
    348  1.9  christos 
    349  1.9  christos 	rcu_read_unlock();
    350  1.1  christos }
    351  1.1  christos 
    352  1.1  christos void
    353  1.1  christos dns_badcache_flushname(dns_badcache_t *bc, const dns_name_t *name) {
    354  1.1  christos 	REQUIRE(VALID_BADCACHE(bc));
    355  1.1  christos 	REQUIRE(name != NULL);
    356  1.1  christos 
    357  1.9  christos 	isc_stdtime_t now = isc_stdtime_now();
    358  1.1  christos 
    359  1.9  christos 	rcu_read_lock();
    360  1.9  christos 	struct cds_lfht *ht = rcu_dereference(bc->ht);
    361  1.9  christos 	INSIST(ht != NULL);
    362  1.9  christos 
    363  1.9  christos 	dns_bcentry_t *bad;
    364  1.9  christos 	struct cds_lfht_iter iter;
    365  1.9  christos 	cds_lfht_for_each_entry(ht, &iter, bad, ht_node) {
    366  1.9  christos 		if (dns_name_equal(&bad->name, name)) {
    367  1.9  christos 			bcentry_evict(ht, bad);
    368  1.9  christos 			continue;
    369  1.4  christos 		}
    370  1.9  christos 
    371  1.9  christos 		/* Flush all the expired entries */
    372  1.9  christos 		(void)bcentry_alive(ht, bad, now);
    373  1.1  christos 	}
    374  1.1  christos 
    375  1.9  christos 	rcu_read_unlock();
    376  1.1  christos }
    377  1.1  christos 
    378  1.1  christos void
    379  1.1  christos dns_badcache_flushtree(dns_badcache_t *bc, const dns_name_t *name) {
    380  1.1  christos 	REQUIRE(VALID_BADCACHE(bc));
    381  1.1  christos 	REQUIRE(name != NULL);
    382  1.1  christos 
    383  1.9  christos 	isc_stdtime_t now = isc_stdtime_now();
    384  1.9  christos 
    385  1.9  christos 	rcu_read_lock();
    386  1.9  christos 	struct cds_lfht *ht = rcu_dereference(bc->ht);
    387  1.9  christos 	INSIST(ht != NULL);
    388  1.9  christos 
    389  1.9  christos 	dns_bcentry_t *bad;
    390  1.9  christos 	struct cds_lfht_iter iter;
    391  1.9  christos 	cds_lfht_for_each_entry(ht, &iter, bad, ht_node) {
    392  1.9  christos 		if (dns_name_issubdomain(&bad->name, name)) {
    393  1.9  christos 			bcentry_evict(ht, bad);
    394  1.9  christos 			continue;
    395  1.1  christos 		}
    396  1.9  christos 
    397  1.9  christos 		/* Flush all the expired entries */
    398  1.9  christos 		(void)bcentry_alive(ht, bad, now);
    399  1.1  christos 	}
    400  1.1  christos 
    401  1.9  christos 	rcu_read_unlock();
    402  1.9  christos }
    403  1.9  christos 
    404  1.9  christos static void
    405  1.9  christos bcentry_print(dns_bcentry_t *bad, isc_stdtime_t now, FILE *fp) {
    406  1.9  christos 	char namebuf[DNS_NAME_FORMATSIZE];
    407  1.9  christos 	char typebuf[DNS_RDATATYPE_FORMATSIZE];
    408  1.9  christos 
    409  1.9  christos 	dns_name_format(&bad->name, namebuf, sizeof(namebuf));
    410  1.9  christos 	dns_rdatatype_format(bad->type, typebuf, sizeof(typebuf));
    411  1.9  christos 	fprintf(fp, "; %s/%s [ttl %" PRIu32 "]\n", namebuf, typebuf,
    412  1.9  christos 		bad->expire - now);
    413  1.1  christos }
    414  1.1  christos 
    415  1.1  christos void
    416  1.1  christos dns_badcache_print(dns_badcache_t *bc, const char *cachename, FILE *fp) {
    417  1.9  christos 	dns_bcentry_t *bad;
    418  1.9  christos 	isc_stdtime_t now = isc_stdtime_now();
    419  1.1  christos 
    420  1.1  christos 	REQUIRE(VALID_BADCACHE(bc));
    421  1.1  christos 	REQUIRE(fp != NULL);
    422  1.1  christos 
    423  1.1  christos 	fprintf(fp, ";\n; %s\n;\n", cachename);
    424  1.1  christos 
    425  1.9  christos 	rcu_read_lock();
    426  1.9  christos 	struct cds_lfht *ht = rcu_dereference(bc->ht);
    427  1.9  christos 	INSIST(ht != NULL);
    428  1.9  christos 
    429  1.9  christos 	struct cds_lfht_iter iter;
    430  1.9  christos 	cds_lfht_for_each_entry(ht, &iter, bad, ht_node) {
    431  1.9  christos 		if (bcentry_alive(ht, bad, now)) {
    432  1.9  christos 			bcentry_print(bad, now, fp);
    433  1.1  christos 		}
    434  1.1  christos 	}
    435  1.9  christos 
    436  1.9  christos 	rcu_read_unlock();
    437  1.1  christos }
    438