From 0723a2850bdd1077c70bbdd8872087e13e4cc39c Mon Sep 17 00:00:00 2001 From: Sepherosa Ziehau Date: Mon, 7 Dec 2015 14:33:51 +0800 Subject: [PATCH] inpcb: Split portinfo token into tokens for porthash head And use pooled token for porthash head. This avoids another 10K/s ~20K/s contention during local port selection. --- sys/netinet/in_pcb.c | 101 +++++++++++++++++++-------------------- sys/netinet/in_pcb.h | 40 ++++++++-------- sys/netinet/ip_divert.c | 2 +- sys/netinet/raw_ip.c | 2 +- sys/netinet/tcp_subr.c | 2 +- sys/netinet/udp_usrreq.c | 2 +- sys/netinet6/in6_pcb.c | 19 ++++---- sys/netinet6/in6_pcb.h | 2 +- sys/netinet6/in6_src.c | 13 +++-- 9 files changed, 89 insertions(+), 94 deletions(-) diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index 43b3531b87..dea655ab48 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -120,6 +120,10 @@ #define INP_LOCALGROUP_SIZMIN 8 #define INP_LOCALGROUP_SIZMAX 256 +static struct inpcb *in_pcblookup_local(struct inpcbporthead *porthash, + struct in_addr laddr, u_int lport_arg, int wild_okay, + struct ucred *cred); + struct in_addr zeroin_addr; /* @@ -363,21 +367,24 @@ static boolean_t in_pcbporthash_update(struct inpcbportinfo *portinfo, struct inpcb *inp, u_short lport, struct ucred *cred, int wild) { + struct inpcbporthead *porthash; + /* * This has to be atomic. If the porthash is shared across multiple * protocol threads, e.g. tcp and udp, then the token must be held. */ - GET_PORT_TOKEN(portinfo); + porthash = in_pcbporthash_head(portinfo, lport); + GET_PORTHASH_TOKEN(porthash); - if (in_pcblookup_local(portinfo, inp->inp_laddr, lport, + if (in_pcblookup_local(porthash, inp->inp_laddr, lport, wild, cred) != NULL) { - REL_PORT_TOKEN(portinfo); + REL_PORTHASH_TOKEN(porthash); return FALSE; } inp->inp_lport = lport; - in_pcbinsporthash(portinfo, inp); + in_pcbinsporthash(porthash, inp); - REL_PORT_TOKEN(portinfo); + REL_PORTHASH_TOKEN(porthash); return TRUE; } @@ -508,6 +515,7 @@ in_pcbbind(struct inpcb *inp, struct sockaddr *nam, struct thread *td) struct sockaddr_in *sin = (struct sockaddr_in *)nam; struct inpcbinfo *pcbinfo; struct inpcbportinfo *portinfo; + struct inpcbporthead *porthash; struct inpcb *t; u_short lport, lport_ho; int reuseport = (so->so_options & SO_REUSEPORT); @@ -579,14 +587,15 @@ in_pcbbind(struct inpcb *inp, struct sockaddr *nam, struct thread *td) /* * This has to be atomic. If the porthash is shared across - * multiple protocol threads (aka tcp) then the token must - * be held. + * multiple protocol threads, e.g. tcp and udp then the token + * must be held. */ - GET_PORT_TOKEN(portinfo); + porthash = in_pcbporthash_head(portinfo, lport); + GET_PORTHASH_TOKEN(porthash); if (so->so_cred->cr_uid != 0 && !IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) { - t = in_pcblookup_local(portinfo, sin->sin_addr, lport, + t = in_pcblookup_local(porthash, sin->sin_addr, lport, INPLOOKUP_WILDCARD, cred); if (t && (so->so_cred->cr_uid != @@ -601,7 +610,7 @@ in_pcbbind(struct inpcb *inp, struct sockaddr *nam, struct thread *td) error = EADDRNOTAVAIL; goto done; } - t = in_pcblookup_local(portinfo, sin->sin_addr, lport, + t = in_pcblookup_local(porthash, sin->sin_addr, lport, wild, cred); if (t && !(reuseport & t->inp_socket->so_options)) { inp->inp_laddr.s_addr = INADDR_ANY; @@ -609,10 +618,10 @@ in_pcbbind(struct inpcb *inp, struct sockaddr *nam, struct thread *td) goto done; } inp->inp_lport = lport; - in_pcbinsporthash(portinfo, inp); + in_pcbinsporthash(porthash, inp); error = 0; done: - REL_PORT_TOKEN(portinfo); + REL_PORTHASH_TOKEN(porthash); return (error); } else { jsin.sin_family = AF_INET; @@ -628,11 +637,10 @@ done: } static struct inpcb * -in_pcblookup_localremote(struct inpcbportinfo *portinfo, struct in_addr laddr, +in_pcblookup_localremote(struct inpcbporthead *porthash, struct in_addr laddr, u_short lport, struct in_addr faddr, u_short fport, struct ucred *cred) { struct inpcb *inp; - struct inpcbporthead *porthash; struct inpcbport *phd; struct inpcb *match = NULL; @@ -640,7 +648,7 @@ in_pcblookup_localremote(struct inpcbportinfo *portinfo, struct in_addr laddr, * If the porthashbase is shared across several cpus, it must * have been locked. */ - ASSERT_PORT_TOKEN_HELD(portinfo); + ASSERT_PORTHASH_TOKEN_HELD(porthash); /* * Best fit PCB lookup. @@ -648,8 +656,6 @@ in_pcblookup_localremote(struct inpcbportinfo *portinfo, struct in_addr laddr, * First see if this local port is in use by looking on the * port hash list. */ - porthash = &portinfo->porthashbase[ - INP_PCBPORTHASH(lport, portinfo->porthashmask)]; LIST_FOREACH(phd, porthash, phd_hash) { if (phd->phd_port == lport) break; @@ -687,21 +693,24 @@ in_pcbporthash_update4(struct inpcbportinfo *portinfo, struct inpcb *inp, u_short lport, const struct sockaddr_in *sin, struct ucred *cred) { + struct inpcbporthead *porthash; + /* * This has to be atomic. If the porthash is shared across multiple * protocol threads, e.g. tcp and udp, then the token must be held. */ - GET_PORT_TOKEN(portinfo); + porthash = in_pcbporthash_head(portinfo, lport); + GET_PORTHASH_TOKEN(porthash); - if (in_pcblookup_localremote(portinfo, inp->inp_laddr, + if (in_pcblookup_localremote(porthash, inp->inp_laddr, lport, sin->sin_addr, sin->sin_port, cred) != NULL) { - REL_PORT_TOKEN(portinfo); + REL_PORTHASH_TOKEN(porthash); return FALSE; } inp->inp_lport = lport; - in_pcbinsporthash(portinfo, inp); + in_pcbinsporthash(porthash, inp); - REL_PORT_TOKEN(portinfo); + REL_PORTHASH_TOKEN(porthash); return TRUE; } @@ -1372,14 +1381,13 @@ in_rtchange(struct inpcb *inp, int err) /* * Lookup a PCB based on the local address and port. */ -struct inpcb * -in_pcblookup_local(struct inpcbportinfo *portinfo, struct in_addr laddr, +static struct inpcb * +in_pcblookup_local(struct inpcbporthead *porthash, struct in_addr laddr, u_int lport_arg, int wild_okay, struct ucred *cred) { struct inpcb *inp; int matchwild = 3, wildcard; u_short lport = lport_arg; - struct inpcbporthead *porthash; struct inpcbport *phd; struct inpcb *match = NULL; @@ -1387,7 +1395,7 @@ in_pcblookup_local(struct inpcbportinfo *portinfo, struct in_addr laddr, * If the porthashbase is shared across several cpus, it must * have been locked. */ - ASSERT_PORT_TOKEN_HELD(portinfo); + ASSERT_PORTHASH_TOKEN_HELD(porthash); /* * Best fit PCB lookup. @@ -1395,8 +1403,6 @@ in_pcblookup_local(struct inpcbportinfo *portinfo, struct in_addr laddr, * First see if this local port is in use by looking on the * port hash list. */ - porthash = &portinfo->porthashbase[ - INP_PCBPORTHASH(lport, portinfo->porthashmask)]; LIST_FOREACH(phd, porthash, phd_hash) { if (phd->phd_port == lport) break; @@ -1725,23 +1731,20 @@ in_pcbremconnhash(struct inpcb *inp) * Insert PCB into port hash table. */ void -in_pcbinsporthash(struct inpcbportinfo *portinfo, struct inpcb *inp) +in_pcbinsporthash(struct inpcbporthead *pcbporthash, struct inpcb *inp) { struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; - struct inpcbporthead *pcbporthash; struct inpcbport *phd; /* * If the porthashbase is shared across several cpus, it must * have been locked. */ - ASSERT_PORT_TOKEN_HELD(portinfo); + ASSERT_PORTHASH_TOKEN_HELD(pcbporthash); /* * Insert into the port hash table. */ - pcbporthash = &portinfo->porthashbase[ - INP_PCBPORTHASH(inp->inp_lport, portinfo->porthashmask)]; /* Go through port list and look for a head for this lport. */ LIST_FOREACH(phd, pcbporthash, phd_hash) { @@ -1759,7 +1762,7 @@ in_pcbinsporthash(struct inpcbportinfo *portinfo, struct inpcb *inp) LIST_INSERT_HEAD(pcbporthash, phd, phd_hash); } - inp->inp_portinfo = portinfo; + inp->inp_porthash = pcbporthash; inp->inp_phd = phd; LIST_INSERT_HEAD(&phd->phd_pcblist, inp, inp_portlist); @@ -1780,6 +1783,7 @@ in_pcbinsporthash_lport(struct inpcb *inp) { struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; struct inpcbportinfo *portinfo; + struct inpcbporthead *porthash; u_short lport_ho; /* Locate the proper portinfo based on lport */ @@ -1787,28 +1791,24 @@ in_pcbinsporthash_lport(struct inpcb *inp) portinfo = &pcbinfo->portinfo[lport_ho & pcbinfo->portinfo_mask]; KKASSERT((lport_ho & pcbinfo->portinfo_mask) == portinfo->offset); - GET_PORT_TOKEN(portinfo); - in_pcbinsporthash(portinfo, inp); - REL_PORT_TOKEN(portinfo); + porthash = in_pcbporthash_head(portinfo, inp->inp_lport); + GET_PORTHASH_TOKEN(porthash); + in_pcbinsporthash(porthash, inp); + REL_PORTHASH_TOKEN(porthash); } void in_pcbremporthash(struct inpcb *inp) { - struct inpcbportinfo *portinfo; + struct inpcbporthead *porthash; struct inpcbport *phd; if (inp->inp_phd == NULL) return; KASSERT(inp->inp_lport != 0, ("inpcb has no lport")); - /* - * NOTE: - * inp->inp_portinfo is _not_ necessary same as - * inp->inp_pcbinfo->portinfo. - */ - portinfo = inp->inp_portinfo; - GET_PORT_TOKEN(portinfo); + porthash = inp->inp_porthash; + GET_PORTHASH_TOKEN(porthash); phd = inp->inp_phd; LIST_REMOVE(inp, inp_portlist); @@ -1817,9 +1817,10 @@ in_pcbremporthash(struct inpcb *inp) kfree(phd, M_PCB); } - REL_PORT_TOKEN(portinfo); + REL_PORTHASH_TOKEN(porthash); inp->inp_phd = NULL; + /* NOTE: Don't whack inp_lport, which may be used later */ } static struct inp_localgroup * @@ -2308,7 +2309,7 @@ in_savefaddr(struct socket *so, const struct sockaddr *faddr) void in_pcbportinfo_init(struct inpcbportinfo *portinfo, int hashsize, - boolean_t shared, u_short offset) + u_short offset) { memset(portinfo, 0, sizeof(*portinfo)); @@ -2319,12 +2320,6 @@ in_pcbportinfo_init(struct inpcbportinfo *portinfo, int hashsize, portinfo->porthashbase = hashinit(hashsize, M_PCB, &portinfo->porthashmask); - - if (shared) { - portinfo->porttoken = kmalloc(sizeof(struct lwkt_token), - M_PCB, M_WAITOK); - lwkt_token_init(portinfo->porttoken, "porttoken"); - } } void diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index 59f1fd9524..b7b023c94b 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -238,7 +238,7 @@ struct inpcb { u_int8_t inp6_hlim; } inp_depend6; LIST_ENTRY(inpcb) inp_portlist; - struct inpcbportinfo *inp_portinfo; + struct inpcbporthead *inp_porthash; struct inpcbport *inp_phd; /* head of this list */ inp_gen_t inp_gencnt; /* generation count of this instance */ #define in6p_faddr inp_inc.inc6_faddr @@ -292,7 +292,6 @@ struct inpcbport { struct lwkt_token; struct inpcbportinfo { - struct lwkt_token *porttoken; /* if this inpcbportinfo is shared */ struct inpcbporthead *porthashbase; u_long porthashmask; u_short offset; @@ -421,27 +420,29 @@ struct baddynamicports { #ifdef _KERNEL -#define GET_PORT_TOKEN(portinfo) \ +static __inline struct inpcbporthead * +in_pcbporthash_head(struct inpcbportinfo *portinfo, u_short lport) +{ + return &portinfo->porthashbase[ + INP_PCBPORTHASH(lport, portinfo->porthashmask)]; +} + +#define GET_PORTHASH_TOKEN(porthashhead) \ do { \ - if ((portinfo)->porttoken) \ - lwkt_gettoken((portinfo)->porttoken); \ + lwkt_getpooltoken((porthashhead)); \ } while (0) -#define REL_PORT_TOKEN(portinfo) \ +#define REL_PORTHASH_TOKEN(porthashhead) \ do { \ - if ((portinfo)->porttoken) \ - lwkt_reltoken((portinfo)->porttoken); \ + lwkt_relpooltoken((porthashhead)); \ } while (0) #ifdef INVARIANTS -#define ASSERT_PORT_TOKEN_HELD(portinfo) \ -do { \ - if ((portinfo)->porttoken) \ - ASSERT_LWKT_TOKEN_HELD((portinfo)->porttoken); \ -} while (0) -#else /* !INVARIANTS */ -#define ASSERT_PORT_TOKEN_HELD(portinfo) -#endif /* INVARIANTS */ +#define ASSERT_PORTHASH_TOKEN_HELD(pcbhashhead) \ + ASSERT_LWKT_TOKEN_HELD(lwkt_token_pool_lookup((pcbhashhead))) +#else +#define ASSERT_PORTHASH_TOKEN_HELD(pcbhashhead) +#endif #define GET_PCBINFO_TOKEN(pcbinfo) \ do { \ @@ -496,7 +497,7 @@ void in_pcbpurgeif0 (struct inpcbinfo *, struct ifnet *); void in_losing (struct inpcb *); void in_rtchange (struct inpcb *, int); void in_pcbinfo_init (struct inpcbinfo *, int, boolean_t); -void in_pcbportinfo_init (struct inpcbportinfo *, int, boolean_t, u_short); +void in_pcbportinfo_init (struct inpcbportinfo *, int, u_short); int in_pcballoc (struct socket *, struct inpcbinfo *); void in_pcbunlink (struct inpcb *, struct inpcbinfo *); void in_pcbunlink_flags (struct inpcb *, struct inpcbinfo *, int); @@ -513,16 +514,13 @@ void in_pcbdisconnect (struct inpcb *); void in_pcbinswildcardhash(struct inpcb *inp); void in_pcbinswildcardhash_oncpu(struct inpcb *, struct inpcbinfo *); void in_pcbinsconnhash(struct inpcb *inp); -void in_pcbinsporthash (struct inpcbportinfo *, struct inpcb *); +void in_pcbinsporthash(struct inpcbporthead *, struct inpcb *); void in_pcbinsporthash_lport (struct inpcb *); void in_pcbremporthash (struct inpcb *); int in_pcbladdr (struct inpcb *, struct sockaddr *, struct sockaddr_in **, struct thread *); int in_pcbladdr_find (struct inpcb *, struct sockaddr *, struct sockaddr_in **, struct thread *, int); -struct inpcb * - in_pcblookup_local (struct inpcbportinfo *, struct in_addr, u_int, int, - struct ucred *); struct inpcb * in_pcblookup_hash (struct inpcbinfo *, struct in_addr, u_int, struct in_addr, u_int, diff --git a/sys/netinet/ip_divert.c b/sys/netinet/ip_divert.c index c0c528ee08..85fa421dbc 100644 --- a/sys/netinet/ip_divert.c +++ b/sys/netinet/ip_divert.c @@ -126,7 +126,7 @@ void div_init(void) { in_pcbinfo_init(&divcbinfo, 0, FALSE); - in_pcbportinfo_init(&divcbportinfo, 1, FALSE, 0); + in_pcbportinfo_init(&divcbportinfo, 1, 0); /* * XXX We don't use the hash list for divert IP, but it's easier * to allocate a one entry hash list than it is to check all diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c index 010aca0fc6..d977235243 100644 --- a/sys/netinet/raw_ip.c +++ b/sys/netinet/raw_ip.c @@ -129,7 +129,7 @@ void rip_init(void) { in_pcbinfo_init(&ripcbinfo, 0, FALSE); - in_pcbportinfo_init(&ripcbportinfo, 1, FALSE, 0); + in_pcbportinfo_init(&ripcbportinfo, 1, 0); /* * XXX We don't use the hash list for raw IP, but it's easier * to allocate a one entry hash list than it is to check all diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index 15eae3813c..b1f4fd04b8 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -393,7 +393,7 @@ tcp_init(void) in_pcbinfo_init(ticb, cpu, FALSE); ticb->hashbase = hashinit(hashsize, M_PCB, &ticb->hashmask); - in_pcbportinfo_init(&portinfo[cpu], hashsize, TRUE, cpu); + in_pcbportinfo_init(&portinfo[cpu], hashsize, cpu); ticb->portinfo = portinfo; ticb->portinfo_mask = ncpus2_mask; ticb->wildcardhashbase = hashinit(hashsize, M_PCB, diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c index bbc96b7caf..807a12c7e0 100644 --- a/sys/netinet/udp_usrreq.c +++ b/sys/netinet/udp_usrreq.c @@ -221,7 +221,7 @@ udp_init(void) in_pcbinfo_init(uicb, cpu, TRUE); uicb->hashbase = hashinit(UDBHASHSIZE, M_PCB, &uicb->hashmask); - in_pcbportinfo_init(&portinfo[cpu], UDBHASHSIZE, TRUE, cpu); + in_pcbportinfo_init(&portinfo[cpu], UDBHASHSIZE, cpu); uicb->portinfo = portinfo; uicb->portinfo_mask = ncpus2_mask; diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index 98d06489f0..92a07d47b8 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -138,6 +138,7 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr *nam, struct thread *td) struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam; struct inpcbinfo *pcbinfo; struct inpcbportinfo *portinfo; + struct inpcbporthead *porthash; int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); struct ucred *cred = NULL; struct inpcb *t; @@ -230,11 +231,12 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr *nam, struct thread *td) * multiple protocol threads (aka tcp) then the token must * be held. */ - GET_PORT_TOKEN(portinfo); + porthash = in_pcbporthash_head(portinfo, lport); + GET_PORTHASH_TOKEN(porthash); if (so->so_cred->cr_uid != 0 && !IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { - t = in6_pcblookup_local(portinfo, + t = in6_pcblookup_local(porthash, &sin6->sin6_addr, lport, INPLOOKUP_WILDCARD, cred); if (t && (so->so_cred->cr_uid != @@ -250,7 +252,7 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr *nam, struct thread *td) error = EADDRNOTAVAIL; goto done; } - t = in6_pcblookup_local(portinfo, &sin6->sin6_addr, lport, + t = in6_pcblookup_local(porthash, &sin6->sin6_addr, lport, wild, cred); if (t && (reuseport & t->inp_socket->so_options) == 0) { inp->in6p_laddr = kin6addr_any; @@ -259,10 +261,10 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr *nam, struct thread *td) } inp->inp_lport = lport; - in_pcbinsporthash(portinfo, inp); + in_pcbinsporthash(porthash, inp); error = 0; done: - REL_PORT_TOKEN(portinfo); + REL_PORTHASH_TOKEN(porthash); return (error); } else { auto_select: @@ -634,14 +636,13 @@ do_notify: * Lookup a PCB based on the local address and port. */ struct inpcb * -in6_pcblookup_local(struct inpcbportinfo *portinfo, +in6_pcblookup_local(struct inpcbporthead *porthash, const struct in6_addr *laddr, u_int lport_arg, int wild_okay, struct ucred *cred) { struct inpcb *inp; int matchwild = 3, wildcard; u_short lport = lport_arg; - struct inpcbporthead *porthash; struct inpcbport *phd; struct inpcb *match = NULL; @@ -649,7 +650,7 @@ in6_pcblookup_local(struct inpcbportinfo *portinfo, * If the porthashbase is shared across several cpus, it must * have been locked. */ - ASSERT_PORT_TOKEN_HELD(portinfo); + ASSERT_PORTHASH_TOKEN_HELD(porthash); /* * Best fit PCB lookup. @@ -657,8 +658,6 @@ in6_pcblookup_local(struct inpcbportinfo *portinfo, * First see if this local port is in use by looking on the * port hash list. */ - porthash = &portinfo->porthashbase[ - INP_PCBPORTHASH(lport, portinfo->porthashmask)]; LIST_FOREACH(phd, porthash, phd_hash) { if (phd->phd_port == lport) break; diff --git a/sys/netinet6/in6_pcb.h b/sys/netinet6/in6_pcb.h index aea91fdacd..f0d2b2679d 100644 --- a/sys/netinet6/in6_pcb.h +++ b/sys/netinet6/in6_pcb.h @@ -110,7 +110,7 @@ void in6_pcbdisconnect (struct inpcb *); int in6_pcbladdr (struct inpcb *, struct sockaddr *, struct in6_addr **, struct thread *); struct inpcb * - in6_pcblookup_local (struct inpcbportinfo *, const struct in6_addr *, + in6_pcblookup_local (struct inpcbporthead *, const struct in6_addr *, u_int, int, struct ucred *); struct inpcb * in6_pcblookup_hash (struct inpcbinfo *, diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c index 10d5459f5d..fbf9bb68c5 100644 --- a/sys/netinet6/in6_src.c +++ b/sys/netinet6/in6_src.c @@ -380,21 +380,24 @@ static boolean_t in6_pcbporthash_update(struct inpcbportinfo *portinfo, struct inpcb *inp, u_short lport, struct ucred *cred, int wild) { + struct inpcbporthead *porthash; + /* * This has to be atomic. If the porthash is shared across multiple * protocol threads, e.g. tcp and udp, then the token must be held. */ - GET_PORT_TOKEN(portinfo); + porthash = in_pcbporthash_head(portinfo, lport); + GET_PORTHASH_TOKEN(porthash); - if (in6_pcblookup_local(portinfo, &inp->in6p_laddr, lport, + if (in6_pcblookup_local(porthash, &inp->in6p_laddr, lport, wild, cred) != NULL) { - REL_PORT_TOKEN(portinfo); + REL_PORTHASH_TOKEN(porthash); return FALSE; } inp->inp_lport = lport; - in_pcbinsporthash(portinfo, inp); + in_pcbinsporthash(porthash, inp); - REL_PORT_TOKEN(portinfo); + REL_PORTHASH_TOKEN(porthash); return TRUE; } -- 2.41.0