Date: Wed, 16 Jun 2004 17:00:13 -0700 (PDT) From: Aaron Fabbri <fabbri@isilon.com> To: FreeBSD-gnats-submit@FreeBSD.org Subject: kern/68026: [patch] add net_remove_domain, domain refcounts Message-ID: <20040617000013.955551A6D1@lily.isilon.com> Resent-Message-ID: <200406170000.i5H00iQg014542@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 68026 >Category: kern >Synopsis: [patch] add net_remove_domain, domain refcounts >Confidential: no >Severity: non-critical >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: update >Submitter-Id: current-users >Arrival-Date: Thu Jun 17 00:00:44 GMT 2004 >Closed-Date: >Last-Modified: >Originator: Aaron Fabbri >Release: FreeBSD-CURRENT >Organization: Isilon Systems >Environment: System: FreeBSD -CURRENT as of Jun 15, i386 >Description: There is currently a net_add_domain(), but no net_remove_domain(). This means you can't safely unload a module which implements a protocol domain. >How-To-Repeat: n/a >Fix: Refcount domains and provide a safe net_remove_domain() function. - Add net_ref_domain() and net_unref_domain(). - Add new net_remove_domain() function which allows removal of a domain iff its refcount is < 1. - Changed pffindtype() and pffindproto() to "pfgettype" and "pfgetproto". On success, these functions now ref the domain which owns the protosw returned. - Added pfput which unref's the domain which owns the supplied protosw. Patch tested on an older branch of FreeBSD (__FreeBSD_version 500028). This diff is against -current, but I have not compiled/tested it there. Patch: diff -ru ../../freebsd-current/src/sys/kern/uipc_domain.c ./sys/kern/uipc_domain.c --- ../../freebsd-current/src/sys/kern/uipc_domain.c 2004-04-05 14:03:36.000000000 -0700 +++ ./sys/kern/uipc_domain.c 2004-06-16 15:34:16.000000000 -0700 @@ -70,6 +70,29 @@ MTX_SYSINIT(domain, &dom_mtx, "domain list", MTX_DEF); /* + * Ref domain. net_add_domain() adds 1 ref, as do sockets of the domain via + * socreate(). + */ +static void +net_ref_domain(struct domain *dom) +{ + mtx_assert(MA_OWNED, &dom_mtx) + dom->dom_refcnt++; +} + +/* + * Remove ref on domain. net_remove_domain() removes the last ref. + */ +static void +net_unref_domain(struct domain *dom) +{ + mtx_assert(MA_OWNED, &dom_mtx); + if (dom->dom_refcnt <= 0) + panic("Too many unrefs on domain %s.", dom->dom_name); + dom->dom_refcnt--; +} + +/* * Add a new protocol domain to the list of supported domains * Note: you cant unload it again because a socket may be using it. * XXX can't fail at this time. @@ -110,10 +133,47 @@ mtx_lock(&dom_mtx); dp->dom_next = domains; domains = dp; + net_ref_domain(dp); mtx_unlock(&dom_mtx); net_init_domain(dp); } +/* + * Remove a domain. Returns ENOENT if 'dp' cannot be found, or EBUSY if 'dp' is + * in use by a socket. + */ +int +net_remove_domain(struct domain *dp) +{ + int error = ENOENT; + struct domain *idp, *dpprev; + + mtx_lock(&dom_mtx); + + dpprev = NULL; + idp = domains; + while (idp != NULL) { + if (idp == dp) { + if (dp->dom_refcnt > 1) { + error = EBUSY; + break; + } + if (dpprev == NULL) + domains = idp->dom_next; + else + dpprev->dom_next = idp->dom_next; + net_unref_domain(dp); + error = 0; + break; + } + dpprev = idp; + idp = idp->dom_next; + } + + mtx_unlock(&dom_mtx); + return (error); +} + /* ARGSUSED*/ static void domaininit(void *dummy) @@ -143,51 +203,99 @@ } +/* + * Find a protocol switch by family and type. If found, ref the domain. + */ struct protosw * -pffindtype(family, type) +pfgettype(family, type) int family; int type; { register struct domain *dp; - register struct protosw *pr; + register struct protosw *pr = NULL; + int found_fam = 0, found = 0; + + mtx_lock(&dom_mtx); for (dp = domains; dp; dp = dp->dom_next) - if (dp->dom_family == family) - goto found; - return (0); -found: - for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) - if (pr->pr_type && pr->pr_type == type) - return (pr); - return (0); + if (dp->dom_family == family) { + found_fam = 1; + break; + } + + if (found_fam) { + for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) + if (pr->pr_type && pr->pr_type == type) { + found = 1; + break; + } + } + if (found) + net_ref_domain(dp); + else + pr = NULL; + + mtx_unlock(&dom_mtx); + return (pr); } +/* + * Find a protocol switch by family, protocol and type. If found, ref the + * domain. + */ struct protosw * -pffindproto(family, protocol, type) +pfgetproto(family, protocol, type) int family; int protocol; int type; { register struct domain *dp; - register struct protosw *pr; + register struct protosw *pr = NULL; struct protosw *maybe = 0; + int found_fam = 0, found = 0; if (family == 0) return (0); + + mtx_lock(&dom_mtx); + for (dp = domains; dp; dp = dp->dom_next) - if (dp->dom_family == family) - goto found; - return (0); -found: - for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) { - if ((pr->pr_protocol == protocol) && (pr->pr_type == type)) - return (pr); - - if (type == SOCK_RAW && pr->pr_type == SOCK_RAW && - pr->pr_protocol == 0 && maybe == (struct protosw *)0) - maybe = pr; + if (dp->dom_family == family) { + found_fam = 1; + break; + } + + if (found_fam) { + for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) { + if ((pr->pr_protocol == protocol) && + (pr->pr_type == type)) { + found = 1; + break; + } + if (type == SOCK_RAW && pr->pr_type == SOCK_RAW && + pr->pr_protocol == 0 && maybe == (struct protosw *)0) + maybe = pr; + } } - return (maybe); + + if (found) + net_ref_domain(dp); + else if (maybe != NULL) { + net_ref_domain(dp); + pr = maybe; + } else + pr = NULL; + + mtx_unlock(&dom_mtx); + return (pr); +} + +void +pfput(struct protosw* pr) +{ + mtx_lock(&dom_mtx); + net_unref_domain(pr->pr_domain); + mtx_unlock(&dom_mtx); } void diff -ru ../../freebsd-current/src/sys/kern/uipc_socket.c ./sys/kern/uipc_socket.c --- ../../freebsd-current/src/sys/kern/uipc_socket.c 2004-06-14 11:16:19.000000000 -0700 +++ ./sys/kern/uipc_socket.c 2004-06-16 15:37:32.000000000 -0700 @@ -173,25 +173,32 @@ int error; if (proto) - prp = pffindproto(dom, proto, type); + prp = pfgetproto(dom, proto, type); else - prp = pffindtype(dom, type); + prp = pfgettype(dom, type); - if (prp == NULL || prp->pr_usrreqs->pru_attach == NULL) - return (EPROTONOSUPPORT); + if (prp == NULL || prp->pr_usrreqs->pru_attach == NULL) { + error = EPROTONOSUPPORT; + goto out; + } if (jailed(cred) && jail_socket_unixiproute_only && prp->pr_domain->dom_family != PF_LOCAL && prp->pr_domain->dom_family != PF_INET && prp->pr_domain->dom_family != PF_ROUTE) { - return (EPROTONOSUPPORT); + error = EPROTONOSUPPORT; + goto out; } - if (prp->pr_type != type) - return (EPROTOTYPE); + if (prp->pr_type != type) { + error = EPROTOTYPE; + goto out; + } so = soalloc(M_WAITOK); - if (so == NULL) - return (ENOBUFS); + if (so == NULL) { + error = ENOBUFS; + goto out; + } TAILQ_INIT(&so->so_incomp); TAILQ_INIT(&so->so_comp); @@ -209,10 +216,13 @@ SOCK_LOCK(so); so->so_state |= SS_NOFDREF; sorele(so); - return (error); } - *aso = so; - return (0); +out: + if (error && prp != NULL) + pfput(prp); + else if (error == 0) + *aso = so; + return (error); } int @@ -414,6 +424,8 @@ int error2 = (*so->so_proto->pr_usrreqs->pru_detach)(so); if (error == 0) error = error2; + if (error2 == 0) + pfput(so->so_proto); } discard: SOCK_LOCK(so); diff -ru ../../freebsd-current/src/sys/netinet/ip_input.c ./sys/netinet/ip_input.c --- ../../freebsd-current/src/sys/netinet/ip_input.c 2004-06-13 10:29:09.000000000 -0700 +++ ./sys/netinet/ip_input.c 2004-06-16 15:30:42.000000000 -0700 @@ -254,7 +254,7 @@ TAILQ_INIT(&in_ifaddrhead); in_ifaddrhashtbl = hashinit(INADDR_NHASH, M_IFADDR, &in_ifaddrhmask); - pr = pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW); + pr = pfgetproto(PF_INET, IPPROTO_RAW, SOCK_RAW); if (pr == 0) panic("ip_init"); for (i = 0; i < IPPROTO_MAX; i++) diff -ru ../../freebsd-current/src/sys/netinet6/ip6_input.c ./sys/netinet6/ip6_input.c --- ../../freebsd-current/src/sys/netinet6/ip6_input.c 2004-06-13 10:29:10.000000000 -0700 +++ ./sys/netinet6/ip6_input.c 2004-06-16 15:30:42.000000000 -0700 @@ -173,7 +173,7 @@ if (sizeof(struct protosw) != sizeof(struct ip6protosw)) panic("sizeof(protosw) != sizeof(ip6protosw)"); #endif - pr = (struct ip6protosw *)pffindproto(PF_INET6, IPPROTO_RAW, SOCK_RAW); + pr = (struct ip6protosw *)pfgetproto(PF_INET6, IPPROTO_RAW, SOCK_RAW); if (pr == 0) panic("ip6_init"); for (i = 0; i < IPPROTO_MAX; i++) diff -ru ../../freebsd-current/src/sys/sys/domain.h ./sys/sys/domain.h --- ../../freebsd-current/src/sys/sys/domain.h 2004-04-06 21:19:49.000000000 -0700 +++ ./sys/sys/domain.h 2004-06-16 15:42:18.000000000 -0700 @@ -61,12 +61,14 @@ void *(*dom_ifattach)(struct ifnet *); oid (*dom_ifdetach)(struct ifnet *, void *); /* af-dependent data on ifnet */ + u_long dom_refcnt; /* sockets ref domains */ }; #ifdef _KERNEL extern struct domain *domains; extern struct domain localdomain; extern void net_add_domain(void *); +extern int net_remove_domain(struct domain *); #define DOMAIN_SET(name) \ SYSINIT(domain_ ## name, SI_SUB_PROTO_DOMAIN, SI_ORDER_SECOND, net_add_domain, & name ## domain) diff -ru ../../freebsd-current/src/sys/sys/protosw.h ./sys/sys/protosw.h --- ../../freebsd-current/src/sys/sys/protosw.h 2004-04-06 21:19:49.000000000 -0700 +++ ./sys/sys/protosw.h 2004-06-16 15:30:42.000000000 -0700 @@ -317,8 +317,9 @@ #ifdef _KERNEL void pfctlinput(int, struct sockaddr *); void pfctlinput2(int, struct sockaddr *, void *); -struct protosw *pffindproto(int family, int protocol, int type); -struct protosw *pffindtype(int family, int type); +struct protosw *pfgetproto(int family, int protocol, int type); +struct protosw *pfgettype(int family, int type); +void pfput(struct protosw *); #endif #endif >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20040617000013.955551A6D1>