diff --git a/sys/netlink/netlink_message_writer.c b/sys/netlink/netlink_message_writer.c --- a/sys/netlink/netlink_message_writer.c +++ b/sys/netlink/netlink_message_writer.c @@ -79,6 +79,64 @@ * change. It happens transparently to the caller. */ +static uma_zone_t nlmsg_zone; + +#define NLMSGSIZE 2048 + +static void +m_free_nl_mbuf(struct mbuf *m) +{ + uma_zfree(nlmsg_zone, m->m_ext.ext_buf); +} + +static int +m_ctor_nl_mbuf(void *mem, int size, void *arg, int how) +{ + struct mbuf *m = (struct mbuf *)arg; + + if (m != NULL) + m_extadd(m, mem, size, m_free_nl_mbuf, NULL, NULL, 0, EXT_MOD_TYPE); + + return (0); +} + +static struct mbuf * +m_get_nl_mbuf(int size, int malloc_flags) +{ + struct mbuf *m, *n; + + if (size <= MHLEN) + return (m_get2(size, malloc_flags, MT_DATA, M_PKTHDR)); + + if (__predict_false(size > NLMSGSIZE)) + return (NULL); + + m = m_gethdr(malloc_flags, MT_DATA); + if (m == NULL) + return (NULL); + + n = uma_zalloc_arg(nlmsg_zone, m, malloc_flags); + if (n == NULL) { + m_free_raw(m); + return (NULL); + } + + return (m); +} + +void +nl_init_msg_zone(void) +{ + nlmsg_zone = uma_zcreate("netlink_msg", NLMSGSIZE, m_ctor_nl_mbuf, + NULL, NULL, NULL, UMA_ALIGN_PTR, 0); +} + +void +nl_destroy_msg_zone(void) +{ + uma_zdestroy(nlmsg_zone); +} + typedef bool nlwriter_op_init(struct nl_writer *nw, int size, bool waitok); typedef bool nlwriter_op_write(struct nl_writer *nw, void *buf, int buflen, int cnt); @@ -196,17 +254,16 @@ * This is the most efficient mechanism as it avoids double-copying. * * Allocates a single mbuf suitable to store up to @size bytes of data. - * If size < MHLEN (around 160 bytes), allocates mbuf with pkghdr - * If size <= MCLBYTES (2k), allocate a single mbuf cluster - * Otherwise, return NULL. + * If size < MHLEN (around 160 bytes), allocates mbuf with pkghdr. + * If the size <= NLMSGSIZE (2k), allocate mbuf+storage out of nlmsg_zone. + * Returns NULL on greater size or the allocation failure. */ static bool nlmsg_get_ns_mbuf(struct nl_writer *nw, int size, bool waitok) { - struct mbuf *m; - int mflag = waitok ? M_WAITOK : M_NOWAIT; - m = m_get2(size, mflag, MT_DATA, M_PKTHDR); + struct mbuf *m = m_get_nl_mbuf(size, mflag); + if (__predict_false(m == NULL)) return (false); nw->alloc_len = M_TRAILINGSPACE(m); diff --git a/sys/netlink/netlink_module.c b/sys/netlink/netlink_module.c --- a/sys/netlink/netlink_module.c +++ b/sys/netlink/netlink_module.c @@ -223,6 +223,7 @@ switch (what) { case MOD_LOAD: NL_LOG(LOG_DEBUG2, "Loading"); + nl_init_msg_zone(); nl_osd_register(); #if !defined(NETLINK) && defined(NETLINK_MODULE) nl_set_functions(&nl_module); @@ -238,6 +239,7 @@ nl_set_functions(NULL); #endif nl_osd_unregister(); + nl_destroy_msg_zone(); } else ret = EBUSY; break; diff --git a/sys/netlink/netlink_var.h b/sys/netlink/netlink_var.h --- a/sys/netlink/netlink_var.h +++ b/sys/netlink/netlink_var.h @@ -152,6 +152,10 @@ void nl_set_source_metadata(struct mbuf *m, int num_messages); void nl_add_msg_info(struct mbuf *m); +/* netlink_message_writer.c */ +void nl_init_msg_zone(void); +void nl_destroy_msg_zone(void); + /* netlink_generic.c */ struct genl_family { const char *family_name;