diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h index 1ccfa3779..fef46a4c7 100644 --- a/include/linux/cgroup-defs.h +++ b/include/linux/cgroup-defs.h @@ -21,6 +21,7 @@ #include #include #include +#include #ifdef CONFIG_CGROUPS @@ -487,6 +488,9 @@ struct cgroup { /* Used to store internal freezer state */ struct cgroup_freezer_state freezer; + /* cgroup log subsys*/ + struct mbuf_slot *mbuf; + /* ids of the ancestors at each level including self */ int ancestor_ids[]; }; diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 202852383..7541951d6 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -959,4 +959,5 @@ static inline void cgroup_bpf_put(struct cgroup *cgrp) {} #endif /* CONFIG_CGROUP_BPF */ +ssize_t mbuf_print(struct cgroup *cgrp, const char *fmt, ...); #endif /* _LINUX_CGROUP_H */ diff --git a/include/linux/mbuf.h b/include/linux/mbuf.h new file mode 100644 index 000000000..10d19870b --- /dev/null +++ b/include/linux/mbuf.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2021 bauerchen + */ + +#include +#include + +#ifndef _CGROUP_MBUF_H +#define _CGROUP_MBUF_H + +struct mbuf_struct { + u32 mbuf_len; + u32 mbuf_max_slots; + u32 mbuf_frees; + u32 mbuf_next_id; + u32 mbuf_size_per_cg; + spinlock_t mbuf_lock; + char *mbuf; + unsigned long *mbuf_bitmap; +}; + +struct mbuf_ring_desc { + /* timestamp of this message */ + u64 ts_ns; + /* message total len, ring_item + ->len = next_item */ + u16 len; + /* text len text_len + sizeof(ring) = len */ + u16 text_len; +}; + +struct mbuf_ring { + u32 base_idx; + u32 first_idx; + u64 first_seq; + u32 next_idx; + u64 next_seq; + u32 end_idx; +}; + +struct mbuf_user_desc { + u64 user_seq; + u32 user_idx; + char buf[1024]; +}; + +/* each cgroup has a mbuf_slot struct */ +struct mbuf_slot { + u32 idx; + /* write op must hold this lock */ + spinlock_t slot_lock; + /* rate limit */ + struct ratelimit_state ratelimit; + struct cgroup *owner; + const struct mbuf_operations *ops; + struct mbuf_ring *mring; + struct mbuf_user_desc *udesc; +}; + +struct mbuf_operations { + /* read message */ + ssize_t (*read) (struct mbuf_slot *, struct mbuf_user_desc *); + /* get next available idx */ + u32 (*next) (struct mbuf_ring *, u32); + /* write message */ + ssize_t (*write) (struct cgroup *, const char *, va_list); +} ____cacheline_aligned; + + +void __init mbuf_bmap_init(void); +void __init setup_mbuf(void); +struct mbuf_slot *mbuf_slot_alloc(struct cgroup *cg); +void mbuf_free(struct cgroup *cg); +#endif diff --git a/init/main.c b/init/main.c index fef9e610b..2677324e7 100644 --- a/init/main.c +++ b/init/main.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -623,11 +624,13 @@ asmlinkage __visible void __init start_kernel(void) * kmem_cache_init() */ setup_log_buf(0); + setup_mbuf(); vfs_caches_init_early(); sort_main_extable(); trap_init(); mm_init(); + mbuf_bmap_init(); ftrace_init(); /* trace_printk can be enabled here */ diff --git a/kernel/cgroup/Makefile b/kernel/cgroup/Makefile index 5d7a76bfb..0244c31c1 100644 --- a/kernel/cgroup/Makefile +++ b/kernel/cgroup/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_CGROUP_PIDS) += pids.o obj-$(CONFIG_CGROUP_RDMA) += rdma.o obj-$(CONFIG_CPUSETS) += cpuset.o obj-$(CONFIG_CGROUP_DEBUG) += debug.o +obj-y += mbuf.o diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c index 79682c234..c7a9d086f 100644 --- a/kernel/cgroup/cgroup-v1.c +++ b/kernel/cgroup/cgroup-v1.c @@ -15,7 +15,6 @@ #include #include #include - #include #define cg_invalf(fc, fmt, ...) invalf(fc, fmt, ## __VA_ARGS__) diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 37db8eba1..a9f33ad42 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -58,6 +58,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include @@ -180,6 +181,8 @@ static u16 cgrp_dfl_threaded_ss_mask; LIST_HEAD(cgroup_roots); static int cgroup_root_count; +int sysctl_qos_mbuf_enable = 0; + /* hierarchy ID allocation and mapping, protected by cgroup_mutex */ static DEFINE_IDR(cgroup_hierarchy_idr); @@ -3683,6 +3686,107 @@ static void cgroup_pressure_release(struct kernfs_open_file *of) } #endif /* CONFIG_PSI */ +static void *mbuf_start(struct seq_file *s, loff_t *pos) +{ + struct cgroup *cgrp = seq_css(s)->cgroup; + struct mbuf_slot *mb = cgrp->mbuf; + u32 index; + + if (!mb) + return NULL; + + index = *pos; + /* If already reach end, just return */ + if (index && index == mb->mring->next_idx) + return NULL; + + if (!mb->udesc) { + mb->udesc = kmalloc(sizeof(struct mbuf_user_desc), GFP_KERNEL); + + if (!mb->udesc) + goto out; + + mb->udesc->user_idx = mb->mring->first_idx; + mb->udesc->user_seq = mb->mring->first_seq; + } + + /* Maybe reach end or empty */ + if (mb->udesc->user_idx == mb->mring->next_idx) + return NULL; + +out: + return mb->udesc; +} + +static void *mbuf_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct mbuf_user_desc *udesc = (struct mbuf_user_desc *)v; + struct cgroup *cgrp = seq_css(s)->cgroup; + struct mbuf_slot *mb = cgrp->mbuf; + + udesc->user_idx = mb->ops->next(mb->mring, udesc->user_idx); + *pos = udesc->user_idx; + + if (udesc->user_idx == mb->mring->next_idx) + return NULL; + + return udesc; +} + +static void mbuf_stop(struct seq_file *s, void *v) +{ + struct cgroup *cgrp = seq_css(s)->cgroup; + struct mbuf_user_desc *desc; + + if (cgrp->mbuf) { + desc = cgrp->mbuf->udesc; + if(desc && desc->user_idx == cgrp->mbuf->mring->next_idx) { + kfree(cgrp->mbuf->udesc); + cgrp->mbuf->udesc = NULL; + } + } +} + +static int mbuf_show(struct seq_file *s, void *v) +{ + ssize_t ret; + struct mbuf_user_desc *udesc = (struct mbuf_user_desc *)v; + struct cgroup *cgrp = seq_css(s)->cgroup; + struct mbuf_slot *mb = cgrp->mbuf; + + memset(udesc->buf, 0, sizeof(udesc->buf)); + ret = mb->ops->read(mb, udesc); + + if (ret > 0) + seq_printf(s, "%s", udesc->buf); + + return 0; +} + +ssize_t mbuf_print(struct cgroup *cgrp, const char *fmt, ...) +{ + struct mbuf_slot *mb; + va_list args; + + mb = cgrp->mbuf; + if(!sysctl_qos_mbuf_enable || !mb) + goto out; + + if(!__ratelimit(&mb->ratelimit)) { + goto out; + } + + if (mb->ops) { + va_start(args, fmt); + mb->ops->write(cgrp, fmt, args); + va_end(args); + } + +out: + return 0; +} +EXPORT_SYMBOL(mbuf_print); + static int cgroup_freeze_show(struct seq_file *seq, void *v) { struct cgroup *cgrp = seq_css(seq)->cgroup; @@ -4941,6 +5045,14 @@ static struct cftype cgroup_base_files[] = { .release = cgroup_pressure_release, }, #endif /* CONFIG_PSI */ + { + .name = "mbuf", + .flags = CFTYPE_NOT_ON_ROOT, + .seq_show = mbuf_show, + .seq_start = mbuf_start, + .seq_next = mbuf_next, + .seq_stop = mbuf_stop, + }, { } /* terminate */ }; @@ -5258,6 +5370,7 @@ static struct cgroup *cgroup_create(struct cgroup *parent) cgrp->self.parent = &parent->self; cgrp->root = root; cgrp->level = level; + cgrp->mbuf = NULL; ret = psi_cgroup_alloc(cgrp); if (ret) @@ -5368,6 +5481,33 @@ fail: return ret; } +/* mbuf only support cpu/memory/io/net cgroup */ +static inline bool cgroup_need_mbuf(struct cgroup *cgrp) +{ +#if IS_ENABLED(CONFIG_CGROUP_SCHED) + if (cgroup_css(cgrp, cgroup_subsys[cpu_cgrp_id])) + return true; +#endif + +#if IS_ENABLED(CONFIG_BLK_CGROUP) + if (cgroup_css(cgrp, cgroup_subsys[io_cgrp_id])) + return true; +#endif + +#if IS_ENABLED(CONFIG_MEMCG) + if (cgroup_css(cgrp, cgroup_subsys[memory_cgrp_id])) + return true; +#endif + +#if IS_ENABLED(CONFIG_CGROUP_NET_CLASSID) + if (cgroup_css(cgrp, cgroup_subsys[net_cls_cgrp_id])) + return true; +#endif + + return false; +} + + int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, umode_t mode) { struct cgroup *parent, *cgrp; @@ -5406,7 +5546,6 @@ int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, umode_t mode) * that @cgrp->kn is always accessible. */ kernfs_get(kn); - ret = cgroup_kn_set_ugid(kn); if (ret) goto out_destroy; @@ -5419,6 +5558,9 @@ int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, umode_t mode) if (ret) goto out_destroy; + if(sysctl_qos_mbuf_enable && cgroup_on_dfl(cgrp) && cgroup_need_mbuf(cgrp)) + cgrp->mbuf = mbuf_slot_alloc(cgrp); + TRACE_CGROUP_PATH(mkdir, cgrp); /* let's create and online css's */ @@ -5601,6 +5743,9 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) cgroup_bpf_offline(cgrp); + if (cgrp->mbuf) + mbuf_free(cgrp); + /* put the base reference */ percpu_ref_kill(&cgrp->self.refcnt); diff --git a/kernel/cgroup/mbuf.c b/kernel/cgroup/mbuf.c new file mode 100644 index 000000000..aee4aec6a --- /dev/null +++ b/kernel/cgroup/mbuf.c @@ -0,0 +1,419 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Quality Monitor Buffer + * Aim to provide backup buffer for RQM to record critical message. + * Could be used to catch critical context when abnormal jitters occur. + * + * Author: bauerchen + * Copyright (C) 2021 Tencent, Inc + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Define max mbuf len is 8M, and min is 2M */ +#define MBUF_LEN_MAX (1 << 23) +#define MBUF_LEN_MIN (1 << 21) +#define MBUF_LEN_DEF MBUF_LEN_MIN + +#define MBUF_MSG_LEN_MAX 1024 + +/* Monitor buffer support max 1024 items */ +#define MBUF_SLOTS_MAX 1024 +#define MBUF_SLOTS_MIN 256 +#define MBUF_SLOTS_DEF 512 + +/* Global mbuf metadata struct */ +static struct mbuf_struct g_mbuf = { + .mbuf_len = MBUF_LEN_DEF, + .mbuf_max_slots = MBUF_SLOTS_DEF, + .mbuf_next_id = 0, + .mbuf_size_per_cg = 0, + .mbuf = NULL, + .mbuf_bitmap = NULL, +}; + +static void __init mbuf_len_update(u64 size) +{ + if (size) + size = roundup_pow_of_two(size); + + if (size > MBUF_LEN_MAX) { + size = (u64)MBUF_LEN_MAX; + pr_warn("mbuf: monitor buffer over [ %llu ] is not supported.\n", + (u64)MBUF_LEN_MAX); + } + + if (size < MBUF_LEN_MIN){ + size = (u64) MBUF_LEN_MIN; + pr_warn("mbuf: monitor buffer less [ %llu ] is not supported.\n", + (u64) MBUF_LEN_MIN); + } + + g_mbuf.mbuf_len = size; +} + +static int __init mbuf_len_setup(char *str) +{ + + u64 size; + + if (!str) + return -EINVAL; + + size = memparse(str, &str); + + mbuf_len_update(size); + + return 0; + +} +early_param("mbuf_len", mbuf_len_setup); + +static int __init mbuf_max_items_setup(char *str) +{ + int num; + + if (!str) + return -EINVAL; + + if (!get_option(&str, &num)) + return -EINVAL; + + if (num) + num = roundup_pow_of_two(num); + + if (num > MBUF_SLOTS_MAX) + num = MBUF_SLOTS_MAX; + + if (num < MBUF_SLOTS_MIN) + num = MBUF_SLOTS_MIN; + + g_mbuf.mbuf_max_slots = num; + + return 0; +} +early_param("mbuf_max_items", mbuf_max_items_setup); + +/* Alloc mbuf global bitmap, each bit stands for a mbuf slot. */ +void __init mbuf_bmap_init(void) +{ + size_t alloc_size; + void *mbuf_bitmap; + + alloc_size = max_t(size_t, g_mbuf.mbuf_max_slots / BITS_PER_BYTE + 1, + L1_CACHE_BYTES); + mbuf_bitmap = kmalloc(alloc_size, __GFP_HIGH|__GFP_ZERO); + + if(!mbuf_bitmap){ + pr_err("mbuf: alloc mbuf_bitmap failed!\n"); + return; + } + g_mbuf.mbuf_bitmap = mbuf_bitmap; + g_mbuf.mbuf_size_per_cg = g_mbuf.mbuf_len / g_mbuf.mbuf_max_slots; +} + +/* Called by start_kernel() */ +void __init setup_mbuf(void) +{ + /* mbuf has been alloced */ + if (g_mbuf.mbuf) + return; + + g_mbuf.mbuf = memblock_alloc(g_mbuf.mbuf_len, PAGE_SIZE); + if (unlikely(!g_mbuf.mbuf)) { + pr_err("mbuf: memblock_alloc [ %u ] bytes failed\n", + g_mbuf.mbuf_len); + return; + } + + g_mbuf.mbuf_frees = g_mbuf.mbuf_max_slots; + spin_lock_init(&g_mbuf.mbuf_lock); + + pr_info("mbuf: mbuf_len:%u\n", g_mbuf.mbuf_len); +} + +/* Get mbuf ring desc text pointer */ +static char *mbuf_text(struct mbuf_ring_desc *desc) +{ + return (char *)desc + sizeof(struct mbuf_ring_desc); +} + +/* Get next mbuf_slot idx */ +static u32 mbuf_next(struct mbuf_ring *mring, u32 curr_idx) +{ + struct mbuf_ring_desc *cdesc, *ndesc; + u32 frees, next_idx; + + cdesc = (struct mbuf_ring_desc *)(g_mbuf.mbuf + curr_idx); + next_idx = curr_idx + cdesc->len; + /* + * If frees are not enough to store mbuf_ring_desc struct, + * just goto head + */ + frees = mring->end_idx - next_idx; + if(frees < sizeof(struct mbuf_ring_desc)){ + next_idx = mring->base_idx; + goto next; + } + + ndesc = (struct mbuf_ring_desc *)(g_mbuf.mbuf + next_idx); + if (!ndesc->len && next_idx != mring->next_idx) + next_idx = mring->base_idx; + +next: + return next_idx; +} + +static inline struct mbuf_ring_desc *get_ring_desc_from_idx( + struct mbuf_ring *ring, u32 idx) +{ + return (struct mbuf_ring_desc *)(g_mbuf.mbuf + idx); +} + +/* Read mbuf message according to its idx */ +static ssize_t mbuf_read(struct mbuf_slot *mb, struct mbuf_user_desc *udesc) +{ + struct mbuf_ring *mring; + struct mbuf_ring_desc *desc; + ssize_t ret; + size_t i, len, tbuf_len; + + tbuf_len = sizeof(udesc->buf); + mring = mb->mring; + + if (udesc->user_seq < mring->first_seq) { + udesc->user_seq = mring->first_seq; + udesc->user_idx = mring->first_idx; + ret = -1; + goto out; + } + + desc = get_ring_desc_from_idx(mring, udesc->user_idx); + + len = sprintf(udesc->buf, "%llu:", desc->ts_ns); + for (i = 0; i < desc->text_len; i++) { + unsigned char c = mbuf_text(desc)[i]; + + if (c < ' ' || c >= 127 || c == '\\') + continue; + else + udesc->buf[len++] = c; + + if (len >= tbuf_len) + break; + } + + len = len >= tbuf_len ? tbuf_len - 1 : len; + udesc->buf[len] = '\n'; + udesc->user_seq++; + ret = len; + +out: + return ret; +} + +static int mbuf_prepare(struct mbuf_ring *mring, u32 msg_size) +{ + u32 frees; + + if (unlikely(msg_size > MBUF_MSG_LEN_MAX)) { + return -ENOMEM; + } + + while (mring->first_seq < mring->next_seq) { + + if (mring->first_idx < mring->next_idx) + frees = max(mring->end_idx - mring->next_idx, + mring->first_idx - mring->base_idx); + else + frees = mring->first_idx - mring->next_idx; + + if (frees > msg_size) + break; + + /* Drop old message until se have enough contiguous space */ + mring->first_idx = mbuf_next(mring, mring->first_idx); + mring->first_seq++; + } + return 0; +} + +/* Write monitor buffer message */ +static ssize_t do_mbuf_write(struct cgroup *cg, char *buffer, size_t size) +{ + struct mbuf_ring *mring; + struct mbuf_ring_desc *desc; + size_t len; + unsigned long flags; + + if (size >= g_mbuf.mbuf_size_per_cg){ + pr_err("mbuf: write message need less than [ %u ] bytes\n", + g_mbuf.mbuf_size_per_cg); + return 0; + } + + mring = cg->mbuf->mring; + len = sizeof(struct mbuf_ring_desc) + size; + + spin_lock_irqsave(&cg->mbuf->slot_lock, flags); + + if (mbuf_prepare(mring, len)){ + spin_unlock_irqrestore(&cg->mbuf->slot_lock, flags); + pr_err("mbuf: Can not find enough space.\n"); + return 0; + } + + if (mring->next_idx + len >= mring->end_idx) { + /* Set remain buffer to 0 if we go to head */ + memset(g_mbuf.mbuf + mring->next_idx, 0, mring->end_idx - mring->next_idx); + mring->next_idx = mring->base_idx; + } + + desc = (struct mbuf_ring_desc *)(g_mbuf.mbuf + mring->next_idx); + memcpy(mbuf_text(desc), buffer, size); + desc->len = size + sizeof(struct mbuf_ring_desc); + desc->text_len = size; + desc->ts_ns = local_clock(); + mring->next_idx += desc->len; + mring->next_seq++; + + spin_unlock_irqrestore(&cg->mbuf->slot_lock, flags); + + return size; +} + +void mbuf_reset(struct mbuf_ring *mring) +{ + mring->first_idx = mring->base_idx; + mring->first_seq = 0; + mring->next_idx = mring->base_idx; + mring->next_seq = 0; +} + +static ssize_t mbuf_write(struct cgroup *cg, const char *fmt, va_list args) +{ + static char buf[MBUF_MSG_LEN_MAX]; + char *text = buf; + size_t t_len; + ssize_t ret; + /* Store string to buffer */ + t_len = vscnprintf(text, sizeof(buf), fmt, args); + + /* Write string to mbuf */ + ret = do_mbuf_write(cg, text, t_len); + + return ret; +} + +const struct mbuf_operations mbuf_ops = { + .read = mbuf_read, + .next = mbuf_next, + .write = mbuf_write, +}; + +static int get_next_mbuf_id(unsigned long *addr, u32 start) +{ + u32 index; + + index = find_next_zero_bit(addr, g_mbuf.mbuf_max_slots, start); + if (unlikely(index >= g_mbuf.mbuf_max_slots)) + return g_mbuf.mbuf_max_slots; + + return index; +} + +static void mbuf_slot_init(struct mbuf_slot *mb, struct cgroup *cg, u32 index) +{ + mb->owner = cg; + mb->idx = index; + mb->ops = &mbuf_ops; + spin_lock_init(&mb->slot_lock); + ratelimit_default_init(&mb->ratelimit); + + mb->mring = (struct mbuf_ring *)((char *)mb + sizeof(struct mbuf_slot)); + mb->mring->base_idx = index * + g_mbuf.mbuf_size_per_cg + sizeof(struct mbuf_slot) + sizeof(struct mbuf_ring); + mb->mring->end_idx = (index + 1) * g_mbuf.mbuf_size_per_cg - 1; + + mbuf_reset(mb->mring); +} + +struct mbuf_slot *mbuf_slot_alloc(struct cgroup *cg) +{ + struct mbuf_slot *mb; + u32 index = 0; + u32 try_times; + unsigned long *m_bitmap; + unsigned long flags; + + /* If mbuf_bitmap or mbuf not ready, just return NULL. */ + if (!g_mbuf.mbuf_bitmap || !g_mbuf.mbuf) { + pr_warn_ratelimited("mbuf: mbuf bitmap or mbuf pointer is NULL, alloc failed\n"); + return NULL; + } + + spin_lock_irqsave(&g_mbuf.mbuf_lock, flags); + + if (g_mbuf.mbuf_frees == 0) { + pr_warn_ratelimited("mbuf: reached max num, alloc failed\n"); + spin_unlock_irqrestore(&g_mbuf.mbuf_lock, flags); + return NULL; + } + + /* Alloc a free mbuf_slot from global mbuf, according mbuf_bitmap */ + m_bitmap = g_mbuf.mbuf_bitmap; + + try_times = 1; +again: + index = get_next_mbuf_id(m_bitmap, g_mbuf.mbuf_next_id); + + /* Rescan next avail idx from head if current idx reach end */ + if (index == g_mbuf.mbuf_max_slots && try_times--) { + g_mbuf.mbuf_next_id = 0; + goto again; + } + + if (unlikely(index == g_mbuf.mbuf_max_slots)) { + /* + * Just a protection mechanism, its must be a bug + * if function reached here. + */ + pr_warn_ratelimited("mbuf: frees and bitmap not coincident, just return\n"); + spin_unlock_irqrestore(&g_mbuf.mbuf_lock, flags); + return NULL; + } + + __set_bit(index, m_bitmap); + g_mbuf.mbuf_next_id = index; + + mb = (struct mbuf_slot *)(g_mbuf.mbuf + index * g_mbuf.mbuf_size_per_cg); + mbuf_slot_init(mb, cg, index); + g_mbuf.mbuf_frees--; + + spin_unlock_irqrestore(&g_mbuf.mbuf_lock, flags); + + return mb; +} + +void mbuf_free(struct cgroup *cg) +{ + unsigned long flags; + + spin_lock_irqsave(&g_mbuf.mbuf_lock, flags); + + /* Make current idx the next available buffer */ + g_mbuf.mbuf_next_id = cg->mbuf->idx; + __clear_bit(g_mbuf.mbuf_next_id, g_mbuf.mbuf_bitmap); + + g_mbuf.mbuf_frees++; + spin_unlock_irqrestore(&g_mbuf.mbuf_lock, flags); +} + diff --git a/kernel/sysctl.c b/kernel/sysctl.c index de69ac536..102f05417 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -121,6 +121,7 @@ extern unsigned int sysctl_nr_open_min, sysctl_nr_open_max; extern int sysctl_nr_trim_pages; #endif +extern int sysctl_qos_mbuf_enable; extern int sysctl_min_epoll_wait_time; extern unsigned int sysctl_softirq_accel_target; extern int sysctl_softirq_accel_mask; @@ -1448,6 +1449,15 @@ static struct ctl_table kern_table[] = { .extra1 = SYSCTL_ZERO, .extra2 = SYSCTL_ONE, }, + { + .procname = "qos_mbuf_enable", + .data = &sysctl_qos_mbuf_enable, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }, { } };