Mercurial > illumos > illumos-gate
changeset 3292:f1a92dc5bc08
6486273 bad mutex panic within find_tpc()
author | kp158701 |
---|---|
date | Wed, 20 Dec 2006 06:50:33 -0800 |
parents | a0d6d28506cf |
children | 11ac3dc768d8 |
files | usr/src/uts/common/inet/ip/tnet.c usr/src/uts/common/os/labelsys.c usr/src/uts/common/sys/tsol/tnet.h |
diffstat | 3 files changed, 195 insertions(+), 133 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/uts/common/inet/ip/tnet.c Wed Dec 20 03:52:51 2006 -0800 +++ b/usr/src/uts/common/inet/ip/tnet.c Wed Dec 20 06:50:33 2006 -0800 @@ -1307,10 +1307,7 @@ /* We've found a gateway address to do the template lookup */ if (paddr != NULL) { ASSERT(gw_rhc == NULL); - if (ire->ire_ipversion == IPV4_VERSION) - gw_rhc = find_rhc_v4(paddr); - else - gw_rhc = find_rhc_v6(paddr); + gw_rhc = find_rhc(paddr, ire->ire_ipversion, B_FALSE); if (gw_rhc != NULL) { /* * Note that if the lookup above returned an @@ -1850,8 +1847,7 @@ * real one. */ if (paddr != NULL) { - attrp->igsa_rhc = (ipversion == IPV4_VERSION) ? - find_rhc_v4(paddr) : find_rhc_v6(paddr); + attrp->igsa_rhc = find_rhc(paddr, ipversion, B_FALSE); } if (exists)
--- a/usr/src/uts/common/os/labelsys.c Wed Dec 20 03:52:51 2006 -0800 +++ b/usr/src/uts/common/os/labelsys.c Wed Dec 20 06:50:33 2006 -0800 @@ -57,7 +57,7 @@ * tnrhc_table and tnrhc_table_v6 are similar to the IP forwarding tables * in organization and search. The tnrhc_table[_v6] is an array of 33/129 * pointers to the 33/129 tnrhc tables indexed by the prefix length. - * A largest prefix match search is done by find_rhc_v[46] and it walks the + * A largest prefix match search is done by find_rhc and it walks the * tables from the most specific to the least specific table. Table 0 * corresponds to the single entry for 0.0.0.0/0 or ::0/0. */ @@ -329,81 +329,103 @@ * * Return 0 for success, error code for failure. */ -int -tnrh_load(const tsol_rhent_t *rhent) +static int +tnrh_hash_add(tsol_tnrhc_t *new, short prefix) { tsol_tnrhc_t **rhp; - tsol_tnrhc_t *rh, *new; - tsol_tpc_t *tpc; + tsol_tnrhc_t *rh; ipaddr_t tmpmask; in6_addr_t tmpmask_v6; tnrhc_hash_t *tnrhc_hash; /* Find the existing entry, if any, leaving the hash locked */ - if (rhent->rh_address.ta_family == AF_INET) { - if (rhent->rh_prefix < 0 || rhent->rh_prefix > IP_ABITS) + if (new->rhc_host.ta_family == AF_INET) { + if (prefix < 0 || prefix > IP_ABITS) return (EINVAL); - if (tnrhc_table[rhent->rh_prefix] == NULL && - !tnrhc_init_table(tnrhc_table, rhent->rh_prefix, + if (tnrhc_table[prefix] == NULL && + !tnrhc_init_table(tnrhc_table, prefix, KM_NOSLEEP)) return (ENOMEM); - tmpmask = tsol_plen_to_mask(rhent->rh_prefix); - tnrhc_hash = &tnrhc_table[rhent->rh_prefix][ - TSOL_ADDR_HASH(rhent->rh_address.ta_addr_v4.s_addr & + tmpmask = tsol_plen_to_mask(prefix); + tnrhc_hash = &tnrhc_table[prefix][ + TSOL_ADDR_HASH(new->rhc_host.ta_addr_v4.s_addr & tmpmask, TNRHC_SIZE)]; mutex_enter(&tnrhc_hash->tnrh_lock); for (rhp = &tnrhc_hash->tnrh_list; (rh = *rhp) != NULL; rhp = &rh->rhc_next) { ASSERT(rh->rhc_host.ta_family == AF_INET); if (((rh->rhc_host.ta_addr_v4.s_addr ^ - rhent->rh_address.ta_addr_v4.s_addr) & tmpmask) == + new->rhc_host.ta_addr_v4.s_addr) & tmpmask) == 0) break; } - } else if (rhent->rh_address.ta_family == AF_INET6) { - if (rhent->rh_prefix < 0 || rhent->rh_prefix > IPV6_ABITS) + } else if (new->rhc_host.ta_family == AF_INET6) { + if (prefix < 0 || prefix > IPV6_ABITS) return (EINVAL); - if (tnrhc_table_v6[rhent->rh_prefix] == NULL && - !tnrhc_init_table(tnrhc_table_v6, rhent->rh_prefix, + if (tnrhc_table_v6[prefix] == NULL && + !tnrhc_init_table(tnrhc_table_v6, prefix, KM_NOSLEEP)) return (ENOMEM); - tsol_plen_to_mask_v6(rhent->rh_prefix, &tmpmask_v6); - tnrhc_hash = &tnrhc_table_v6[rhent->rh_prefix][ - TSOL_ADDR_MASK_HASH_V6(rhent->rh_address.ta_addr_v6, + tsol_plen_to_mask_v6(prefix, &tmpmask_v6); + tnrhc_hash = &tnrhc_table_v6[prefix][ + TSOL_ADDR_MASK_HASH_V6(new->rhc_host.ta_addr_v6, tmpmask_v6, TNRHC_SIZE)]; mutex_enter(&tnrhc_hash->tnrh_lock); for (rhp = &tnrhc_hash->tnrh_list; (rh = *rhp) != NULL; rhp = &rh->rhc_next) { ASSERT(rh->rhc_host.ta_family == AF_INET6); if (V6_MASK_EQ_2(rh->rhc_host.ta_addr_v6, tmpmask_v6, - rhent->rh_address.ta_addr_v6)) + new->rhc_host.ta_addr_v6)) break; } } else { return (EAFNOSUPPORT); } - if ((new = kmem_zalloc(sizeof (*new), KM_NOSLEEP)) == NULL) { - mutex_exit(&tnrhc_hash->tnrh_lock); - return (ENOMEM); - } - - /* Find and bump the reference count on the named template */ - if ((tpc = tnrhtp_find(rhent->rh_template, tpc_name_hash)) == NULL) { - mutex_exit(&tnrhc_hash->tnrh_lock); - kmem_free(new, sizeof (*new)); - return (EINVAL); - } - /* Clobber the old remote host entry. */ if (rh != NULL) { ASSERT(!rh->rhc_invalid); rh->rhc_invalid = 1; *rhp = rh->rhc_next; rh->rhc_next = NULL; + DTRACE_PROBE1(tx__tndb__l2__tnrhhashadd__invalidaterh, + tsol_tnrhc_t *, rh); TNRHC_RELE(rh); } + TNRHC_HOLD(new); + new->rhc_next = tnrhc_hash->tnrh_list; + tnrhc_hash->tnrh_list = new; + DTRACE_PROBE1(tx__tndb__l2__tnrhhashadd__addedrh, tsol_tnrhc_t *, new); + mutex_exit(&tnrhc_hash->tnrh_lock); + + return (0); +} + +/* + * Load a remote host entry into kernel cache. + * + * Return 0 for success, error code for failure. + */ +int +tnrh_load(const tsol_rhent_t *rhent) +{ + tsol_tnrhc_t *new; + tsol_tpc_t *tpc; + int status; + + /* Find and bump the reference count on the named template */ + if ((tpc = tnrhtp_find(rhent->rh_template, tpc_name_hash)) == NULL) { + return (EINVAL); + } + ASSERT(tpc->tpc_tp.host_type == UNLABELED || + tpc->tpc_tp.host_type == SUN_CIPSO); + + if ((new = kmem_zalloc(sizeof (*new), KM_NOSLEEP)) == NULL) { + TPC_RELE(tpc); + return (ENOMEM); + } + /* Initialize the new entry. */ mutex_init(&new->rhc_lock, NULL, MUTEX_DEFAULT, NULL); new->rhc_host = rhent->rh_address; @@ -411,16 +433,17 @@ /* The rhc now owns this tpc reference, so no TPC_RELE past here */ new->rhc_tpc = tpc; - ASSERT(tpc->tpc_tp.host_type == UNLABELED || - tpc->tpc_tp.host_type == SUN_CIPSO); - + /* + * tnrh_hash_add handles the tnrh entry ref count for hash + * table inclusion. The ref count is incremented and decremented + * here to trigger deletion of the new hash table entry in the + * event that tnrh_hash_add fails. + */ TNRHC_HOLD(new); - new->rhc_next = tnrhc_hash->tnrh_list; - tnrhc_hash->tnrh_list = new; - DTRACE_PROBE(tx__tndb__l2__tnrhload__addedrh); - mutex_exit(&tnrhc_hash->tnrh_lock); + status = tnrh_hash_add(new, rhent->rh_prefix); + TNRHC_RELE(new); - return (0); + return (status); } static int @@ -1015,81 +1038,145 @@ * The returned rhc's refcnt is incremented. */ tsol_tnrhc_t * -find_rhc_v4(const in_addr_t *in4) +find_rhc(const void *addr, uchar_t version, boolean_t staleok) { tsol_tnrhc_t *rh = NULL; + tsol_tnrhc_t *new; + tsol_tpc_t *tpc; tnrhc_hash_t *tnrhc_hash; ipaddr_t tmpmask; + in_addr_t *in4 = (in_addr_t *)addr; + in6_addr_t *in6 = (in6_addr_t *)addr; + in_addr_t tmpin4; + in6_addr_t tmpmask6; int i; - - for (i = (TSOL_MASK_TABLE_SIZE - 1); i >= 0; i--) { - - if ((tnrhc_table[i]) == NULL) - continue; - - tmpmask = tsol_plen_to_mask(i); - tnrhc_hash = &tnrhc_table[i][ - TSOL_ADDR_HASH(*in4 & tmpmask, TNRHC_SIZE)]; + int prefix; - mutex_enter(&tnrhc_hash->tnrh_lock); - for (rh = tnrhc_hash->tnrh_list; rh != NULL; - rh = rh->rhc_next) { - if ((rh->rhc_host.ta_family == AF_INET) && - ((rh->rhc_host.ta_addr_v4.s_addr & tmpmask) == - (*in4 & tmpmask))) { - TNRHC_HOLD(rh); - mutex_exit(&tnrhc_hash->tnrh_lock); - return (rh); - } - } - mutex_exit(&tnrhc_hash->tnrh_lock); + /* + * An IPv4-mapped IPv6 address is really an IPv4 address + * in IPv6 format. + */ + if (version == IPV6_VERSION && + IN6_IS_ADDR_V4MAPPED(in6)) { + IN6_V4MAPPED_TO_IPADDR(in6, tmpin4); + version = IPV4_VERSION; + in4 = &tmpin4; } - return (NULL); -} + /* + * Search the tnrh hash table for each prefix length, + * starting at longest prefix length, until a matching + * rhc entry is found. + */ + if (version == IPV4_VERSION) { + for (i = (TSOL_MASK_TABLE_SIZE - 1); i >= 0; i--) { + + if ((tnrhc_table[i]) == NULL) + continue; + + tmpmask = tsol_plen_to_mask(i); + tnrhc_hash = &tnrhc_table[i][ + TSOL_ADDR_HASH(*in4 & tmpmask, TNRHC_SIZE)]; -/* - * Returns a tnrhc matching the addr address. - * The returned rhc's refcnt is incremented. - */ -tsol_tnrhc_t * -find_rhc_v6(const in6_addr_t *in6) -{ - tsol_tnrhc_t *rh = NULL; - tnrhc_hash_t *tnrhc_hash; - in6_addr_t tmpmask; - int i; + mutex_enter(&tnrhc_hash->tnrh_lock); + for (rh = tnrhc_hash->tnrh_list; rh != NULL; + rh = rh->rhc_next) { + if ((rh->rhc_host.ta_family == AF_INET) && + ((rh->rhc_host.ta_addr_v4.s_addr & + tmpmask) == (*in4 & tmpmask))) { + prefix = i; + TNRHC_HOLD(rh); + break; + } + } + mutex_exit(&tnrhc_hash->tnrh_lock); + if (rh != NULL) + break; + } + if (rh == NULL) + DTRACE_PROBE1(tx__tndb__l1__findrhc__norhv4ent, + in_addr_t *, in4); + } else { + for (i = (TSOL_MASK_TABLE_SIZE_V6 - 1); i >= 0; i--) { + if ((tnrhc_table_v6[i]) == NULL) + continue; - if (IN6_IS_ADDR_V4MAPPED(in6)) { - in_addr_t in4; + tsol_plen_to_mask_v6(i, &tmpmask6); + tnrhc_hash = &tnrhc_table_v6[i][ + TSOL_ADDR_MASK_HASH_V6(*in6, tmpmask6, TNRHC_SIZE)]; - IN6_V4MAPPED_TO_IPADDR(in6, in4); - return (find_rhc_v4(&in4)); + mutex_enter(&tnrhc_hash->tnrh_lock); + for (rh = tnrhc_hash->tnrh_list; rh != NULL; + rh = rh->rhc_next) { + if ((rh->rhc_host.ta_family == AF_INET6) && + V6_MASK_EQ_2(rh->rhc_host.ta_addr_v6, + tmpmask6, *in6)) { + prefix = i; + TNRHC_HOLD(rh); + break; + } + } + mutex_exit(&tnrhc_hash->tnrh_lock); + if (rh != NULL) + break; + } + if (rh == NULL) + DTRACE_PROBE1(tx__tndb__l1__findrhc__norhv6ent, + in6_addr_t *, in6); } - for (i = (TSOL_MASK_TABLE_SIZE_V6 - 1); i >= 0; i--) { - if ((tnrhc_table_v6[i]) == NULL) - continue; - - tsol_plen_to_mask_v6(i, &tmpmask); - tnrhc_hash = &tnrhc_table_v6[i][ - TSOL_ADDR_MASK_HASH_V6(*in6, tmpmask, TNRHC_SIZE)]; + /* + * Does the tnrh entry point to a stale template? + * This can happen any time the user deletes or modifies + * a template that has existing tnrh entries pointing + * to it. Try to find a new version of the template. + * If there is no template, then just give up. + * If the template exists, reload the tnrh entry. + */ + if (rh != NULL && rh->rhc_tpc->tpc_invalid) { + tpc = tnrhtp_find(rh->rhc_tpc->tpc_tp.name, tpc_name_hash); + if (tpc == NULL) { + if (!staleok) { + DTRACE_PROBE2(tx__tndb__l1__findrhc__staletpc, + tsol_tnrhc_t *, rh, tsol_tpc_t *, + rh->rhc_tpc); + TNRHC_RELE(rh); + rh = NULL; + } + } else { + ASSERT(tpc->tpc_tp.host_type == UNLABELED || + tpc->tpc_tp.host_type == SUN_CIPSO); - mutex_enter(&tnrhc_hash->tnrh_lock); - for (rh = tnrhc_hash->tnrh_list; rh != NULL; - rh = rh->rhc_next) { - if ((rh->rhc_host.ta_family == AF_INET6) && - V6_MASK_EQ_2(rh->rhc_host.ta_addr_v6, tmpmask, - *in6)) { - TNRHC_HOLD(rh); - mutex_exit(&tnrhc_hash->tnrh_lock); - return (rh); + if ((new = kmem_zalloc(sizeof (*new), + KM_NOSLEEP)) == NULL) { + DTRACE_PROBE(tx__tndb__l1__findrhc__nomem); + TNRHC_RELE(rh); + TPC_RELE(tpc); + return (NULL); + } + + mutex_init(&new->rhc_lock, NULL, MUTEX_DEFAULT, NULL); + new->rhc_host = rh->rhc_host; + new->rhc_tpc = tpc; + new->rhc_isbcast = rh->rhc_isbcast; + new->rhc_local = rh->rhc_local; + TNRHC_RELE(rh); + rh = new; + + /* + * This function increments the tnrh entry ref count + * for the pointer returned to the caller. + * tnrh_hash_add increments the tnrh entry ref count + * for the pointer in the hash table. + */ + TNRHC_HOLD(rh); + if (tnrh_hash_add(new, prefix) != 0) { + TNRHC_RELE(rh); + rh = NULL; } } - mutex_exit(&tnrhc_hash->tnrh_lock); } - - return (NULL); + return (rh); } tsol_tpc_t * @@ -1098,33 +1185,13 @@ tsol_tpc_t *tpc; tsol_tnrhc_t *rhc; - if (version == IPV4_VERSION) - rhc = find_rhc_v4(addr); - else - rhc = find_rhc_v6(addr); + if ((rhc = find_rhc(addr, version, staleok)) == NULL) + return (NULL); - if (rhc != NULL) { - tpc = rhc->rhc_tpc; - if (!staleok && tpc->tpc_invalid) { - /* - * This should not happen unless the user deletes - * templates without recreating them. Try to find the - * new version of template. If there is none, then - * just give up. - */ - tpc = tnrhtp_find(tpc->tpc_tp.name, tpc_name_hash); - if (tpc != NULL) { - TPC_RELE(rhc->rhc_tpc); - rhc->rhc_tpc = tpc; - } - } - if (tpc != NULL) - TPC_HOLD(tpc); - TNRHC_RELE(rhc); - return (tpc); - } - DTRACE_PROBE(tx__tndb__l1__findtpc__notemplate); - return (NULL); + tpc = rhc->rhc_tpc; + TPC_HOLD(tpc); + TNRHC_RELE(rhc); + return (tpc); } /*
--- a/usr/src/uts/common/sys/tsol/tnet.h Wed Dec 20 03:52:51 2006 -0800 +++ b/usr/src/uts/common/sys/tsol/tnet.h Wed Dec 20 06:50:33 2006 -0800 @@ -47,8 +47,7 @@ #define TSOL_MAX_IPV6_OPTION (8 + IP_MAX_OPT_LENGTH) extern int tsol_tnrh_chk(tsol_tpent_t *, bslabel_t *, int); -extern tsol_tnrhc_t *find_rhc_v4(const in_addr_t *); -extern tsol_tnrhc_t *find_rhc_v6(const in6_addr_t *); +extern tsol_tnrhc_t *find_rhc(const void *, uchar_t, boolean_t); extern int tsol_compute_label(const cred_t *, ipaddr_t, uchar_t *, boolean_t); extern int tsol_compute_label_v6(const cred_t *, const in6_addr_t *, uchar_t *, boolean_t);