Module Name: src Committed By: ozaki-r Date: Tue Aug 8 04:17:34 UTC 2017
Modified Files: src/sys/netipsec: key.c keydb.h Log Message: MP-ify SAD (key_sad.sahlist and sah entries) localcount(9) is used to protect key_sad.sahlist and sah entries as well as SPD (and will be used for SAD sav). Please read the locking notes of SAD for more details. To generate a diff of this commit: cvs rdiff -u -r1.215 -r1.216 src/sys/netipsec/key.c cvs rdiff -u -r1.18 -r1.19 src/sys/netipsec/keydb.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/netipsec/key.c diff -u src/sys/netipsec/key.c:1.215 src/sys/netipsec/key.c:1.216 --- src/sys/netipsec/key.c:1.215 Tue Aug 8 01:56:49 2017 +++ src/sys/netipsec/key.c Tue Aug 8 04:17:34 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: key.c,v 1.215 2017/08/08 01:56:49 ozaki-r Exp $ */ +/* $NetBSD: key.c,v 1.216 2017/08/08 04:17:34 ozaki-r Exp $ */ /* $FreeBSD: src/sys/netipsec/key.c,v 1.3.2.3 2004/02/14 22:23:23 bms Exp $ */ /* $KAME: key.c,v 1.191 2001/06/27 10:46:49 sakane Exp $ */ @@ -32,7 +32,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: key.c,v 1.215 2017/08/08 01:56:49 ozaki-r Exp $"); +__KERNEL_RCSID(0, "$NetBSD: key.c,v 1.216 2017/08/08 04:17:34 ozaki-r Exp $"); /* * This code is referd to RFC 2367 @@ -198,12 +198,35 @@ static u_int32_t acq_seq = 0; * until GC by the timer */ /* + * Locking notes on SAD: + * - Data structures + * - SAs are managed by the list called key_sad.sahlist and sav lists of sah + * entries + * - A sah has sav lists for each SA state + * - Multiple sahs with the same saidx can exist + * - Only one entry has MATURE state and others should be DEAD + * - DEAD entries are just ignored from searching + * - Modifications to the key_sad.sahlist must be done with holding key_sad.lock + * which is a adaptive mutex + * - Read accesses to the key_sad.sahlist must be in pserialize(9) read sections + * - sah's lifetime is managed by localcount(9) + * - Getting an sah entry + * - We get an SP from the key_spd.splist + * - Must iterate the list and increment the reference count of a found sah + * (by key_sah_ref) in a pserialize read section + * - A gotten sah must be released after use by key_sah_unref + * - An sah is destroyed when its state become DEAD and no sav is + * listed to the sah + * - The destruction is done only in the timer (see key_timehandler_sad) + */ +/* * Locking notes on misc data: * - All lists of key_misc are protected by key_misc.lock * - key_misc.lock must be held even for read accesses */ -static pserialize_t key_psz __read_mostly; +static pserialize_t key_spd_psz __read_mostly; +static pserialize_t key_sad_psz __read_mostly; /* SPD */ static struct { @@ -220,6 +243,7 @@ static struct { /* SAD */ static struct { kmutex_t lock; + kcondvar_t cv; struct pslist_head sahlist; } key_sad __cacheline_aligned; @@ -614,13 +638,18 @@ static struct mbuf *key_setdumpsp (struc static u_int key_getspreqmsglen (const struct secpolicy *); static int key_spdexpire (struct secpolicy *); static struct secashead *key_newsah (const struct secasindex *); -static void key_delsah (struct secashead *); +static void key_unlink_sah(struct secashead *); +static void key_destroy_sah(struct secashead *); +static bool key_sah_has_sav(struct secashead *); +static void key_sah_ref(struct secashead *); +static void key_sah_unref(struct secashead *); static struct secasvar *key_newsav(struct mbuf *, const struct sadb_msghdr *, int *, const char*, int); #define KEY_NEWSAV(m, sadb, e) \ key_newsav(m, sadb, e, __func__, __LINE__) static void key_delsav (struct secasvar *); static struct secashead *key_getsah(const struct secasindex *, int); +static struct secashead *key_getsah_ref(const struct secasindex *, int); static bool key_checkspidup(const struct secasindex *, u_int32_t); static struct secasvar *key_getsavbyspi (struct secashead *, u_int32_t); static int key_setsaval (struct secasvar *, struct mbuf *, @@ -800,7 +829,7 @@ key_unlink_sp(struct secpolicy *sp) #ifdef NET_MPSAFE KASSERT(mutex_ownable(softnet_lock)); - pserialize_perform(key_psz); + pserialize_perform(key_spd_psz); #endif localcount_drain(&sp->localcount, &key_spd.cv, &key_spd.lock); @@ -2954,9 +2983,13 @@ key_newsah(const struct secasindex *said PSLIST_INIT(&newsah->savlist[i]); newsah->saidx = *saidx; - /* add to saidxtree */ - newsah->state = SADB_SASTATE_MATURE; + localcount_init(&newsah->localcount); + /* Take a reference for the caller */ + localcount_acquire(&newsah->localcount); + + /* Add to the sah list */ SAHLIST_ENTRY_INIT(newsah); + newsah->state = SADB_SASTATE_MATURE; mutex_enter(&key_sad.lock); SAHLIST_WRITER_INSERT_HEAD(newsah); mutex_exit(&key_sad.lock); @@ -2964,51 +2997,55 @@ key_newsah(const struct secasindex *said return newsah; } -/* - * delete SA index and all SA registerd. - */ -static void -key_delsah(struct secashead *sah) +static bool +key_sah_has_sav(struct secashead *sah) { - struct secasvar *sav; u_int state; - int s; - int zombie = 0; - KASSERT(!cpu_softintr_p()); - KASSERT(sah != NULL); - - s = splsoftnet(); + KASSERT(mutex_owned(&key_sad.lock)); - /* searching all SA registerd in the secindex. */ SASTATE_ANY_FOREACH(state) { - SAVLIST_READER_FOREACH(sav, sah, state) { - /* give up to delete this sa */ - zombie++; - } + if (!SAVLIST_WRITER_EMPTY(sah, state)) + return true; } - /* don't delete sah only if there are savs. */ - if (zombie) { - splx(s); - return; - } + return false; +} - rtcache_free(&sah->sa_route); +static void +key_unlink_sah(struct secashead *sah) +{ - /* remove from tree of SA index */ + KASSERT(!cpu_softintr_p()); + KASSERT(mutex_owned(&key_sad.lock)); + KASSERT(sah->state == SADB_SASTATE_DEAD); + + /* Remove from the sah list */ SAHLIST_WRITER_REMOVE(sah); +#ifdef NET_MPSAFE + KASSERT(mutex_ownable(softnet_lock)); + pserialize_perform(key_sad_psz); +#endif + + localcount_drain(&sah->localcount, &key_sad.cv, &key_sad.lock); +} + +static void +key_destroy_sah(struct secashead *sah) +{ + + rtcache_free(&sah->sa_route); + + SAHLIST_ENTRY_DESTROY(sah); + localcount_fini(&sah->localcount); + if (sah->idents != NULL) kmem_free(sah->idents, sah->idents_len); if (sah->identd != NULL) kmem_free(sah->identd, sah->identd_len); - SAHLIST_ENTRY_DESTROY(sah); kmem_free(sah, sizeof(*sah)); - - splx(s); - return; } /* @@ -3136,7 +3173,32 @@ key_delsav(struct secasvar *sav) } /* - * search SAD. + * Must be called in a pserialize read section. A held sah + * must be released by key_sah_unref after use. + */ +static void +key_sah_ref(struct secashead *sah) +{ + + localcount_acquire(&sah->localcount); +} + +/* + * Must be called without holding key_sad.lock because the lock + * would be held in localcount_release. + */ +static void +key_sah_unref(struct secashead *sah) +{ + + KDASSERT(mutex_ownable(&key_sad.lock)); + + localcount_release(&sah->localcount, &key_sad.cv, &key_sad.lock); +} + +/* + * Search SAD and return sah. Must be called in a pserialize + * read section. * OUT: * NULL : not found * others : found, pointer to a SA. @@ -3157,6 +3219,28 @@ key_getsah(const struct secasindex *said } /* + * Search SAD and return sah. If sah is returned, the caller must call + * key_sah_unref to releaset a reference. + * OUT: + * NULL : not found + * others : found, pointer to a SA. + */ +static struct secashead * +key_getsah_ref(const struct secasindex *saidx, int flag) +{ + struct secashead *sah; + int s; + + s = pserialize_read_enter(); + sah = key_getsah(saidx, flag); + if (sah != NULL) + key_sah_ref(sah); + pserialize_read_exit(s); + + return sah; +} + +/* * check not to be duplicated SPI. * NOTE: this function is too slow due to searching all SAD. * OUT: @@ -3168,6 +3252,7 @@ key_checkspidup(const struct secasindex { struct secashead *sah; struct secasvar *sav; + int s; /* check address family */ if (saidx->src.sa.sa_family != saidx->dst.sa.sa_family) { @@ -3176,15 +3261,18 @@ key_checkspidup(const struct secasindex } /* check all SAD */ + s = pserialize_read_enter(); SAHLIST_READER_FOREACH(sah) { if (!key_ismyaddr((struct sockaddr *)&sah->saidx.dst)) continue; sav = key_getsavbyspi(sah, spi); if (sav != NULL) { + pserialize_read_exit(s); KEY_SA_UNREF(&sav); return true; } } + pserialize_read_exit(s); return false; } @@ -4594,15 +4682,28 @@ static void key_timehandler_sad(time_t now) { struct secashead *sah; - struct secasvar *sav; + int s; restart: + mutex_enter(&key_sad.lock); SAHLIST_WRITER_FOREACH(sah) { - /* if sah has been dead, then delete it and process next sah. */ - if (sah->state == SADB_SASTATE_DEAD) { - key_delsah(sah); + /* If sah has been dead and has no sav, then delete it */ + if (sah->state == SADB_SASTATE_DEAD && + !key_sah_has_sav(sah)) { + key_unlink_sah(sah); + mutex_exit(&key_sad.lock); + key_destroy_sah(sah); goto restart; } + } + mutex_exit(&key_sad.lock); + + s = pserialize_read_enter(); + SAHLIST_READER_FOREACH(sah) { + struct secasvar *sav; + + key_sah_ref(sah); + pserialize_read_exit(s); /* if LARVAL entry doesn't become MATURE, delete it. */ restart_sav_LARVAL: @@ -4727,7 +4828,11 @@ restart: * (such as from SPD). */ } + + s = pserialize_read_enter(); + key_sah_unref(sah); } + pserialize_read_exit(s); } static void @@ -4984,7 +5089,7 @@ key_api_getspi(struct socket *so, struct return key_senderror(so, m, EINVAL); /* get a SA index */ - sah = key_getsah(&saidx, CMP_REQID); + sah = key_getsah_ref(&saidx, CMP_REQID); if (sah == NULL) { /* create a new SA index */ sah = key_newsah(&saidx); @@ -4998,6 +5103,7 @@ key_api_getspi(struct socket *so, struct /* XXX rewrite */ newsav = KEY_NEWSAV(m, mhp, &error); if (newsav == NULL) { + key_sah_unref(sah); /* XXX don't free new SA index allocated in above. */ return key_senderror(so, m, error); } @@ -5015,6 +5121,8 @@ key_api_getspi(struct socket *so, struct mutex_exit(&key_sad.lock); key_validate_savlist(sah, SADB_SASTATE_LARVAL); + key_sah_unref(sah); + #ifndef IPSEC_NONBLOCK_ACQUIRE /* delete the entry in key_misc.acqlist */ if (mhp->msg->sadb_msg_seq != 0) { @@ -5362,7 +5470,7 @@ key_api_update(struct socket *so, struct return key_senderror(so, m, EINVAL); /* get a SA header */ - sah = key_getsah(&saidx, CMP_REQID); + sah = key_getsah_ref(&saidx, CMP_REQID); if (sah == NULL) { IPSECLOG(LOG_DEBUG, "no SA index found.\n"); return key_senderror(so, m, ENOENT); @@ -5372,7 +5480,7 @@ key_api_update(struct socket *so, struct /* XXX rewrite */ error = key_setident(sah, m, mhp); if (error) - return key_senderror(so, m, error); + goto error_sah; /* find a SA with sequence number. */ #ifdef IPSEC_DOSEQCHECK @@ -5382,7 +5490,8 @@ key_api_update(struct socket *so, struct IPSECLOG(LOG_DEBUG, "no larval SA with sequence %u exists.\n", mhp->msg->sadb_msg_seq); - return key_senderror(so, m, ENOENT); + error = ENOENT; + goto error_sah; } } #else @@ -5390,7 +5499,8 @@ key_api_update(struct socket *so, struct if (sav == NULL) { IPSECLOG(LOG_DEBUG, "no such a SA found (spi:%u)\n", (u_int32_t)ntohl(sa0->sadb_sa_spi)); - return key_senderror(so, m, EINVAL); + error = EINVAL; + goto error_sah; } #endif @@ -5461,6 +5571,9 @@ key_api_update(struct socket *so, struct KEY_FREESAV(&sav); KEY_FREESAV(&sav); + key_sah_unref(sah); + sah = NULL; + { struct mbuf *n; @@ -5476,6 +5589,8 @@ key_api_update(struct socket *so, struct } error: KEY_SA_UNREF(&sav); +error_sah: + key_sah_unref(sah); return key_senderror(so, m, error); } @@ -5593,7 +5708,7 @@ key_api_add(struct socket *so, struct mb return key_senderror(so, m, EINVAL); /* get a SA header */ - sah = key_getsah(&saidx, CMP_REQID); + sah = key_getsah_ref(&saidx, CMP_REQID); if (sah == NULL) { /* create a new SA header */ sah = key_newsah(&saidx); @@ -5606,9 +5721,8 @@ key_api_add(struct socket *so, struct mb /* set spidx if there */ /* XXX rewrite */ error = key_setident(sah, m, mhp); - if (error) { - return key_senderror(so, m, error); - } + if (error) + goto error; { struct secasvar *sav; @@ -5618,27 +5732,28 @@ key_api_add(struct socket *so, struct mb if (sav != NULL) { KEY_SA_UNREF(&sav); IPSECLOG(LOG_DEBUG, "SA already exists.\n"); - return key_senderror(so, m, EEXIST); + error = EEXIST; + goto error; } } /* create new SA entry. */ newsav = KEY_NEWSAV(m, mhp, &error); - if (newsav == NULL) { - return key_senderror(so, m, error); - } + if (newsav == NULL) + goto error; newsav->sah = sah; error = key_handle_natt_info(newsav, mhp); if (error != 0) { key_delsav(newsav); - return key_senderror(so, m, EINVAL); + error = EINVAL; + goto error; } error = key_init_xform(newsav); if (error != 0) { key_delsav(newsav); - return key_senderror(so, m, error); + goto error; } /* add to satree */ @@ -5650,6 +5765,9 @@ key_api_add(struct socket *so, struct mb mutex_exit(&key_sad.lock); key_validate_savlist(sah, SADB_SASTATE_MATURE); + key_sah_unref(sah); + sah = NULL; + /* * don't call key_freesav() here, as we would like to keep the SA * in the database on success. @@ -5668,6 +5786,9 @@ key_api_add(struct socket *so, struct mb m_freem(m); return key_sendup_mbuf(so, n, KEY_SENDUP_ALL); } +error: + key_sah_unref(sah); + return key_senderror(so, m, error); } /* m is retained */ @@ -5853,10 +5974,11 @@ key_api_delete(struct socket *so, struct return key_senderror(so, m, EINVAL); /* get a SA header */ - sah = key_getsah(&saidx, CMP_HEAD); + sah = key_getsah_ref(&saidx, CMP_HEAD); if (sah != NULL) { /* get a SA with SPI. */ sav = key_getsavbyspi(sah, sa0->sadb_sa_spi); + key_sah_unref(sah); } if (sav == NULL) { @@ -5911,7 +6033,7 @@ key_delete_all(struct socket *so, struct if (error != 0) return key_senderror(so, m, EINVAL); - sah = key_getsah(&saidx, CMP_HEAD); + sah = key_getsah_ref(&saidx, CMP_HEAD); if (sah != NULL) { /* Delete all non-LARVAL SAs. */ SASTATE_ALIVE_FOREACH(state) { @@ -5933,6 +6055,7 @@ key_delete_all(struct socket *so, struct goto restart; } } + key_sah_unref(sah); } { struct mbuf *n; @@ -5971,7 +6094,6 @@ key_api_get(struct socket *so, struct mb struct sadb_sa *sa0; const struct sockaddr *src, *dst; struct secasindex saidx; - struct secashead *sah; struct secasvar *sav = NULL; u_int16_t proto; int error; @@ -6008,11 +6130,17 @@ key_api_get(struct socket *so, struct mb return key_senderror(so, m, EINVAL); /* get a SA header */ + { + struct secashead *sah; + int s = pserialize_read_enter(); + sah = key_getsah(&saidx, CMP_HEAD); if (sah != NULL) { /* get a SA with SPI. */ sav = key_getsavbyspi(sah, sa0->sadb_sa_spi); } + pserialize_read_exit(s); + } if (sav == NULL) { IPSECLOG(LOG_DEBUG, "no SA found.\n"); return key_senderror(so, m, ENOENT); @@ -6023,7 +6151,7 @@ key_api_get(struct socket *so, struct mb u_int8_t satype; /* map proto to satype */ - satype = key_proto2satype(sah->saidx.proto); + satype = key_proto2satype(sav->sah->saidx.proto); if (satype == 0) { KEY_SA_UNREF(&sav); IPSECLOG(LOG_DEBUG, "there was invalid proto in SAD.\n"); @@ -6623,7 +6751,6 @@ key_api_acquire(struct socket *so, struc { const struct sockaddr *src, *dst; struct secasindex saidx; - struct secashead *sah; u_int16_t proto; int error; @@ -6703,11 +6830,18 @@ key_api_acquire(struct socket *so, struc return key_senderror(so, m, EINVAL); /* get a SA index */ + { + struct secashead *sah; + int s = pserialize_read_enter(); + sah = key_getsah(&saidx, CMP_MODE_REQID); if (sah != NULL) { + pserialize_read_exit(s); IPSECLOG(LOG_DEBUG, "a SA exists already.\n"); return key_senderror(so, m, EEXIST); } + pserialize_read_exit(s); + } error = key_acquire(&saidx, NULL); if (error != 0) { @@ -7051,6 +7185,7 @@ key_api_flush(struct socket *so, struct struct secasvar *sav; u_int16_t proto; u_int8_t state; + int s; /* map satype to proto */ proto = key_satype2proto(mhp->msg->sadb_msg_satype); @@ -7060,11 +7195,15 @@ key_api_flush(struct socket *so, struct } /* no SATYPE specified, i.e. flushing all SA. */ + s = pserialize_read_enter(); SAHLIST_READER_FOREACH(sah) { if (mhp->msg->sadb_msg_satype != SADB_SATYPE_UNSPEC && proto != sah->saidx.proto) continue; + key_sah_ref(sah); + pserialize_read_exit(s); + SASTATE_ALIVE_FOREACH(state) { restart: SAVLIST_WRITER_FOREACH(sav, sah, state) { @@ -7074,8 +7213,11 @@ key_api_flush(struct socket *so, struct } } + s = pserialize_read_enter(); sah->state = SADB_SASTATE_DEAD; + key_sah_unref(sah); } + pserialize_read_exit(s); if (m->m_len < sizeof(struct sadb_msg) || sizeof(struct sadb_msg) > m->m_len + M_TRAILINGSPACE(m)) { @@ -7784,10 +7926,12 @@ key_do_init(void) int i, error; mutex_init(&key_misc.lock, MUTEX_DEFAULT, IPL_NONE); - key_psz = pserialize_create(); + key_spd_psz = pserialize_create(); mutex_init(&key_spd.lock, MUTEX_DEFAULT, IPL_NONE); cv_init(&key_spd.cv, "key_sp"); + key_sad_psz = pserialize_create(); mutex_init(&key_sad.lock, MUTEX_DEFAULT, IPL_NONE); + cv_init(&key_sad.cv, "key_sa"); pfkeystat_percpu = percpu_alloc(sizeof(uint64_t) * PFKEY_NSTATS); @@ -7982,16 +8126,26 @@ void key_sa_routechange(struct sockaddr *dst) { struct secashead *sah; - struct route *ro; - const struct sockaddr *sa; + int s; + s = pserialize_read_enter(); SAHLIST_READER_FOREACH(sah) { + struct route *ro; + const struct sockaddr *sa; + + key_sah_ref(sah); + pserialize_read_exit(s); + ro = &sah->sa_route; sa = rtcache_getdst(ro); if (sa != NULL && dst->sa_len == sa->sa_len && memcmp(dst, sa, dst->sa_len) == 0) rtcache_free(ro); + + s = pserialize_read_enter(); + key_sah_unref(sah); } + pserialize_read_exit(s); return; } Index: src/sys/netipsec/keydb.h diff -u src/sys/netipsec/keydb.h:1.18 src/sys/netipsec/keydb.h:1.19 --- src/sys/netipsec/keydb.h:1.18 Mon Aug 7 03:21:59 2017 +++ src/sys/netipsec/keydb.h Tue Aug 8 04:17:34 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: keydb.h,v 1.18 2017/08/07 03:21:59 ozaki-r Exp $ */ +/* $NetBSD: keydb.h,v 1.19 2017/08/08 04:17:34 ozaki-r Exp $ */ /* $FreeBSD: src/sys/netipsec/keydb.h,v 1.1.4.1 2003/01/24 05:11:36 sam Exp $ */ /* $KAME: keydb.h,v 1.14 2000/08/02 17:58:26 sakane Exp $ */ @@ -36,6 +36,8 @@ #ifdef _KERNEL +#include <sys/localcount.h> + #include <netipsec/key_var.h> #include <net/route.h> #include <netinet/in.h> @@ -66,6 +68,7 @@ struct secasindex { /* Security Association Data Base */ struct secashead { struct pslist_entry pslist_entry; + struct localcount localcount; /* reference count */ struct secasindex saidx;