/* * Copyright 1997 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD: src/sys/net/hostcache.c,v 1.6.2.1 2002/04/14 21:41:48 luigi Exp $ * $DragonFly: src/sys/net/Attic/hostcache.c,v 1.3 2004/09/15 20:38:36 joerg Exp $ */ #include #include #include #include #include #include #include MALLOC_DEFINE(M_HOSTCACHE, "hostcache", "per-host cache structure"); static struct hctable hctable[AF_MAX]; static int hc_timeout_interval = 120; static int hc_maxidle = 1800; static struct callout hc_timeout_h; static int cmpsa(const struct sockaddr *sa1, const struct sockaddr *sa2); static void hc_timeout(void *xhct); static void maybe_bump_hash(struct hctable *hct); int hc_init(int af, struct hccallback *hccb, int init_nelem, int primes) { struct hctable *hct; struct hchead *heads; u_long nelem; hct = &hctable[af]; nelem = init_nelem; if (hct->hct_nentries) return 0; if (primes) { heads = phashinit(init_nelem, M_HOSTCACHE, &nelem); } else { int i; MALLOC(heads, struct hchead *, nelem * sizeof *heads, M_HOSTCACHE, M_WAITOK); for (i = 0; i < nelem; i++) { LIST_INIT(&heads[i]); } } hct->hct_heads = heads; hct->hct_nentries = nelem; hct->hct_primes = primes; callout_init(&hc_timeout_h); callout_reset(&hc_timeout_h, hc_timeout_interval * hz, hc_timeout, hct); return 0; } struct hcentry * hc_get(struct sockaddr *sa) { u_long hash; struct hcentry *hc; struct hctable *hct; int s; hct = &hctable[sa->sa_family]; if (hct->hct_nentries == 0) return 0; hash = hct->hct_cb->hccb_hash(sa, hct->hct_nentries); hc = hct->hct_heads[hash].lh_first; for (; hc; hc = hc->hc_link.le_next) { if (cmpsa(hc->hc_host, sa) == 0) break; } if (hc == 0) return 0; s = splnet(); if (hc->hc_rt && (hc->hc_rt->rt_flags & RTF_UP) == 0) { RTFREE(hc->hc_rt); hc->hc_rt = 0; } if (hc->hc_rt == 0) { hc->hc_rt = rtalloc1(hc->hc_host, 1, 0); } hc_ref(hc); splx(s); /* XXX move to front of list? */ return hc; } void hc_ref(struct hcentry *hc) { int s = splnet(); if (hc->hc_refcnt++ == 0) { hc->hc_hct->hct_idle--; hc->hc_hct->hct_active++; } splx(s); } void hc_rele(struct hcentry *hc) { int s = splnet(); #ifdef DIAGNOSTIC printf("hc_rele: %p: negative refcnt!\n", (void *)hc); #endif hc->hc_refcnt--; if (hc->hc_refcnt == 0) { hc->hc_hct->hct_idle++; hc->hc_hct->hct_active--; hc->hc_idlesince = mono_time; /* XXX right one? */ } splx(s); } /* * The user is expected to initialize hc_host with the address and everything * else to the appropriate form of `0'. */ int hc_insert(struct hcentry *hc) { struct hcentry *hc2; struct hctable *hct; u_long hash; int s; hct = &hctable[hc->hc_host->sa_family]; hash = hct->hct_cb->hccb_hash(hc->hc_host, hct->hct_nentries); hc2 = hct->hct_heads[hash].lh_first; for (; hc2; hc2 = hc2->hc_link.le_next) { if (cmpsa(hc2->hc_host, hc->hc_host) == 0) break; } if (hc2 != 0) return EEXIST; hc->hc_hct = hct; s = splnet(); LIST_INSERT_HEAD(&hct->hct_heads[hash], hc, hc_link); hct->hct_idle++; /* * If the table is now more than 75% full, consider bumping it. */ if (100 * (hct->hct_idle + hct->hct_active) > 75 * hct->hct_nentries) maybe_bump_hash(hct); splx(s); return 0; } /* * It's not clear to me how much sense this makes as an external interface, * since it is expected that the deletion will normally be handled by * the cache timeout. */ int hc_delete(struct hcentry *hc) { struct hctable *hct; int error, s; if (hc->hc_refcnt > 0) return 0; hct = hc->hc_hct; error = hct->hct_cb->hccb_delete(hc); if (error) return 0; s = splnet(); LIST_REMOVE(hc, hc_link); hc->hc_hct->hct_idle--; splx(s); free(hc, M_HOSTCACHE); return 0; } static void hc_timeout(void *xhct) { struct hcentry *hc; struct hctable *hct; int j, s; time_t start; hct = xhct; start = mono_time.tv_sec; /* for simplicity */ if (hct->hct_idle == 0) return; for (j = 0; j < hct->hct_nentries; j++) { for (hc = hct->hct_heads[j].lh_first; hc; hc = hc->hc_link.le_next) { if (hc->hc_refcnt > 0) continue; if (hc->hc_idlesince.tv_sec + hc_maxidle <= start) { if (hct->hct_cb->hccb_delete(hc)) continue; s = splnet(); LIST_REMOVE(hc, hc_link); hct->hct_idle--; splx(s); } } } /* * Fiddle something here based on tot_idle... */ callout_reset(&hc_timeout_h, hc_timeout_interval * hz, hc_timeout, xhct); } static int cmpsa(const struct sockaddr *sa1, const struct sockaddr *sa2) { if (sa1->sa_len != sa2->sa_len) return ((int)sa1->sa_len - sa2->sa_len); return bcmp(sa1, sa2, sa1->sa_len); } static void maybe_bump_hash(struct hctable *hct) { ; /* XXX fill me in */ }