Post by Alexander BluhmHi,
One of the regression tests triggers this panic in the pf purge
thread. I am not sure which test it is as the panic may be delayed.
It happens on a remote machine, the test machine connects to it in
a multi machine setup.
Using a pool for 'radix_node_head' should fix the problem. pf_table
seems to be the only place where such nodes are freed. I converted
rn_inithead() to be able to audit all consumers of this API.
Diff below.
Post by Alexander Bluhmlogin: panic: kernel diagnostic assertion "_kernel_lock_held()" failed: file "/usr/src/sys/kern/kern_malloc.c", line 373
Stopped at db_enter+0x7: leave
TID PID UID PRFLAGS PFLAGS CPU COMMAND
*284526 76934 0 0x14000 0x200 0 pfpurge
db_enter(d0b626ba,f5480d38,d0a1f6c4,f5480d38,d06dce8d) at db_enter+0x7
panic(d0a1f6c4,d09ca4f6,d09d052d,d09e933c,175) at panic+0x71
__assert(d09ca4f6,d09e933c,175,d09d052d,d8247280) at __assert+0x2e
free(d8247200,5,0,0,d0c51668) at free+0x251
pfr_destroy_ktable(db6a5420,1,f5480e5c,d03665a8,d0be0f80) at pfr_destroy_ktable+0x49
pfr_setflags_ktable(db6a5420,0,f5480e5c,d037c2b2,d75fda8c) at pfr_setflags_ktable+0xe3
pfr_setflags_ktable(db6a4f18,6,f5480eac,d036c8a9,d71c8980,0,8a796f54,f5480e9c,d04281c5) at pfr_setflags_ktable+0x141
pfr_detach_table(db6a4f18,0,d63f0234,d036c985,d71c8938) at pfr_detach_table+0x3c
pf_tbladdr_remove(db75c4f0,2,f5480efc,d036c8dd,db75c7b4) at pf_tbladdr_remove+0x25
pf_rm_rule(0,db75c4c0,f5480f5c,d0635d28,d0bd150c) at pf_rm_rule+0x224
pf_free_state(d75fdb64,ffffffff,40,0,1) at pf_free_state+0x176
pf_purge_expired_states(5,d0bd150c,20,d09f81dd,64) at pf_purge_expired_states+0x62
pf_purge_thread(d774c2cc) at pf_purge_thread+0x6e
Index: kern/vfs_subr.c
===================================================================
RCS file: /cvs/src/sys/kern/vfs_subr.c,v
retrieving revision 1.259
diff -u -p -r1.259 vfs_subr.c
--- kern/vfs_subr.c 20 Apr 2017 14:13:00 -0000 1.259
+++ kern/vfs_subr.c 4 Jul 2017 09:36:53 -0000
@@ -1405,7 +1405,7 @@ vfs_hang_addrlist(struct mount *mp, stru
switch (i) {
case AF_INET:
if ((rnh = nep->ne_rtable_inet) == NULL) {
- if (!rn_inithead((void **)&nep->ne_rtable_inet,
+ if (!rn_inithead(&nep->ne_rtable_inet,
offsetof(struct sockaddr_in, sin_addr))) {
error = ENOBUFS;
goto out;
Index: net/pf_table.c
===================================================================
RCS file: /cvs/src/sys/net/pf_table.c,v
retrieving revision 1.126
diff -u -p -r1.126 pf_table.c
--- net/pf_table.c 8 May 2017 20:24:03 -0000 1.126
+++ net/pf_table.c 4 Jul 2017 09:36:32 -0000
@@ -2010,9 +2010,9 @@ pfr_create_ktable(struct pfr_table *tbl,
rs->tables++;
}
- if (!rn_inithead((void **)&kt->pfrkt_ip4,
+ if (!rn_inithead(&kt->pfrkt_ip4,
offsetof(struct sockaddr_in, sin_addr)) ||
- !rn_inithead((void **)&kt->pfrkt_ip6,
+ !rn_inithead(&kt->pfrkt_ip6,
offsetof(struct sockaddr_in6, sin6_addr))) {
pfr_destroy_ktable(kt, 0);
return (NULL);
@@ -2046,10 +2046,8 @@ pfr_destroy_ktable(struct pfr_ktable *kt
pfr_clean_node_mask(kt, &addrq);
pfr_destroy_kentries(&addrq);
}
- if (kt->pfrkt_ip4 != NULL)
- free((caddr_t)kt->pfrkt_ip4, M_RTABLE, 0);
- if (kt->pfrkt_ip6 != NULL)
- free((caddr_t)kt->pfrkt_ip6, M_RTABLE, 0);
+ rn_freehead(kt->pfrkt_ip4);
+ rn_freehead(kt->pfrkt_ip6);
if (kt->pfrkt_shadow != NULL)
pfr_destroy_ktable(kt->pfrkt_shadow, flushaddr);
if (kt->pfrkt_rs != NULL) {
Index: net/pipex.c
===================================================================
RCS file: /cvs/src/sys/net/pipex.c,v
retrieving revision 1.102
diff -u -p -r1.102 pipex.c
--- net/pipex.c 6 Jun 2017 13:07:22 -0000 1.102
+++ net/pipex.c 4 Jul 2017 09:39:08 -0000
@@ -151,12 +151,12 @@ pipex_iface_init(struct pipex_iface_cont
pipex_iface->ifnet_this = ifp;
if (pipex_rd_head4 == NULL) {
- if (!rn_inithead((void **)&pipex_rd_head4,
+ if (!rn_inithead(&pipex_rd_head4,
offsetof(struct sockaddr_in, sin_addr)))
panic("rn_inithead() failed on pipex_init()");
}
if (pipex_rd_head6 == NULL) {
- if (!rn_inithead((void **)&pipex_rd_head6,
+ if (!rn_inithead(&pipex_rd_head6,
offsetof(struct sockaddr_in6, sin6_addr)))
panic("rn_inithead() failed on pipex_init()");
}
Index: net/radix.h
===================================================================
RCS file: /cvs/src/sys/net/radix.h,v
retrieving revision 1.30
diff -u -p -r1.30 radix.h
--- net/radix.h 19 Jun 2017 09:42:45 -0000 1.30
+++ net/radix.h 4 Jul 2017 09:36:09 -0000
@@ -97,7 +97,8 @@ struct radix_node_head {
};
void rn_init(unsigned int);
-int rn_inithead(void **, int);
+int rn_inithead(struct radix_node_head **, int);
+void rn_freehead(struct radix_node_head *);
int rn_walktree(struct radix_node_head *,
int (*)(struct radix_node *, void *, u_int), void *);
Index: net/radix.c
===================================================================
RCS file: /cvs/src/sys/net/radix.c,v
retrieving revision 1.58
diff -u -p -r1.58 radix.c
--- net/radix.c 20 Jun 2017 09:03:39 -0000 1.58
+++ net/radix.c 4 Jul 2017 09:35:59 -0000
@@ -60,7 +60,8 @@ static unsigned int max_keylen; /* size
struct radix_node_head *mask_rnhead; /* head of shared mask tree */
-struct pool rtmask_pool; /* pool for radix_mask structures */
+struct pool rthead_pool; /* pool for radix_node_head structs */
+struct pool rtmask_pool; /* pool for radix_mask structs */
static inline int rn_satisfies_leaf(char *, struct radix_node *, int);
static inline int rn_lexobetter(void *, void *);
@@ -1097,7 +1098,7 @@ rn_initmask(void)
KASSERT(max_keylen > 0);
- mask_rnhead = malloc(sizeof(*mask_rnhead), M_RTABLE, M_NOWAIT);
+ mask_rnhead = pool_get(&rthead_pool, PR_NOWAIT);
if (mask_rnhead == NULL)
return (1);
@@ -1106,7 +1107,7 @@ rn_initmask(void)
}
int
-rn_inithead(void **head, int off)
+rn_inithead(struct radix_node_head **head, int off)
{
struct radix_node_head *rnh;
@@ -1116,7 +1117,7 @@ rn_inithead(void **head, int off)
if (rn_initmask())
panic("failed to initialize the mask tree");
- rnh = malloc(sizeof(*rnh), M_RTABLE, M_NOWAIT);
+ rnh = pool_get(&rthead_pool, PR_NOWAIT);
if (rnh == NULL)
return (0);
*head = rnh;
@@ -1124,6 +1125,14 @@ rn_inithead(void **head, int off)
return (1);
}
+void
+rn_freehead(struct radix_node_head *rnh)
+{
+ if (rnh == NULL)
+ return;
+ pool_put(&rthead_pool, rnh);
+}
+
int
rn_inithead0(struct radix_node_head *rnh, int offset)
{
@@ -1158,6 +1167,8 @@ rn_init(unsigned int keylen)
if (max_keylen == 0) {
pool_init(&rtmask_pool, sizeof(struct radix_mask), 0,
IPL_SOFTNET, 0, "rtmask", NULL);
+ pool_init(&rthead_pool, sizeof(struct radix_node_head), 0,
+ IPL_SOFTNET, 0, "rthead", NULL);
}
if (keylen <= max_keylen)
Index: netinet/ip_spd.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_spd.c,v
retrieving revision 1.92
diff -u -p -r1.92 ip_spd.c
--- netinet/ip_spd.c 6 Apr 2017 14:25:18 -0000 1.92
+++ netinet/ip_spd.c 4 Jul 2017 09:37:19 -0000
@@ -98,7 +98,7 @@ spd_table_add(unsigned int rtableid)
}
if (spd_tables[rdomain] == NULL) {
- if (rn_inithead((void **)&rnh,
+ if (rn_inithead(&rnh,
offsetof(struct sockaddr_encap, sen_type)) == 0)
rnh = NULL;
spd_tables[rdomain] = rnh;