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 @@ -53,7 +53,7 @@ * The goal of this file is to provide convenient message writing KPI on top of * different storage methods (mbufs, uio, temporary memory chunks). * - * The main KPI guarantee is the the (last) message always resides in the contiguous + * The main KPI guarantee is that the (last) message always resides in the contiguous * memory buffer, so one is able to update the header after writing the entire message. * * This guarantee comes with a side effect of potentially reallocating underlying @@ -79,6 +79,71 @@ * change. It happens transparently to the caller. */ +/* + * Uma zone for the mbuf-based Netlink storage + */ +static uma_zone_t nlmsg_zone; + +static void +nl_free_mbuf_storage(struct mbuf *m) +{ + uma_zfree(nlmsg_zone, m->m_ext.ext_buf); +} + +static int +nl_setup_mbuf_storage(void *mem, int size, void *arg, int how __unused) +{ + struct mbuf *m = (struct mbuf *)arg; + + if (m != NULL) + m_extadd(m, mem, size, nl_free_mbuf_storage, NULL, NULL, 0, EXT_MOD_TYPE); + + return (0); +} + +static struct mbuf * +nl_get_mbuf_flags(int size, int malloc_flags, int mbuf_flags) +{ + struct mbuf *m, *m_storage; + + if (size <= MHLEN) + return (m_get2(size, malloc_flags, MT_DATA, mbuf_flags)); + + if (__predict_false(size > NLMBUFSIZE)) + return (NULL); + + m = m_gethdr(malloc_flags, MT_DATA); + if (m == NULL) + return (NULL); + + m_storage = uma_zalloc_arg(nlmsg_zone, m, malloc_flags); + if (m_storage == NULL) { + m_free_raw(m); + return (NULL); + } + + return (m); +} + +static struct mbuf * +nl_get_mbuf(int size, int malloc_flags) +{ + return (nl_get_mbuf_flags(size, malloc_flags, M_PKTHDR)); +} + +void +nl_init_msg_zone(void) +{ + nlmsg_zone = uma_zcreate("netlink", NLMBUFSIZE, nl_setup_mbuf_storage, + 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 +261,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 <= NLMBUFSIZE (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 = nl_get_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 @@ -36,8 +36,10 @@ #include #include -#define NLSNDQ 65536 /* Default socket sendspace */ -#define NLRCVQ 65536 /* Default socket recvspace */ +#define NLSNDQ 65536 /* Default socket sendspace */ +#define NLRCVQ 65536 /* Default socket recvspace */ + +#define NLMBUFSIZE 2048 /* External storage size for Netlink mbufs */ struct ucred; @@ -152,6 +154,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;