[PATCH 4/9] syslog_ns: make syslog handling per namespace

From: Rui Xiang
Date: Sun Jul 28 2013 - 22:34:26 EST


This patch makes syslog buf and other fields per
namespace.

Here use ns->log_buf(log_buf_len, logbuf_lock,
log_first_seq, logbuf_lock, and so on) fields
instead of global ones to handle syslog.

Syslog interfaces such as /dev/kmsg, /proc/kmsg,
and syslog syscall are all containerized for
container users.

Signed-off-by: Rui Xiang <rui.xiang@xxxxxxxxxx>
---
fs/proc/kmsg.c | 17 +-
include/linux/printk.h | 1 -
include/linux/syslog.h | 3 +-
kernel/printk.c | 507 +++++++++++++++++++++++++------------------------
kernel/sysctl.c | 3 +-
5 files changed, 273 insertions(+), 258 deletions(-)

diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c
index bdfabda..cb98431 100644
--- a/fs/proc/kmsg.c
+++ b/fs/proc/kmsg.c
@@ -13,6 +13,8 @@
#include <linux/proc_fs.h>
#include <linux/fs.h>
#include <linux/syslog.h>
+#include <linux/cred.h>
+#include <linux/user_namespace.h>

#include <asm/uaccess.h>
#include <asm/io.h>
@@ -21,12 +23,14 @@ extern wait_queue_head_t log_wait;

static int kmsg_open(struct inode * inode, struct file * file)
{
- return do_syslog(SYSLOG_ACTION_OPEN, NULL, 0, SYSLOG_FROM_PROC);
+ return do_syslog(SYSLOG_ACTION_OPEN, NULL, 0, SYSLOG_FROM_PROC,
+ file->f_cred->user_ns->syslog_ns);
}

static int kmsg_release(struct inode * inode, struct file * file)
{
- (void) do_syslog(SYSLOG_ACTION_CLOSE, NULL, 0, SYSLOG_FROM_PROC);
+ (void) do_syslog(SYSLOG_ACTION_CLOSE, NULL, 0, SYSLOG_FROM_PROC,
+ file->f_cred->user_ns->syslog_ns);
return 0;
}

@@ -34,15 +38,18 @@ static ssize_t kmsg_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
if ((file->f_flags & O_NONBLOCK) &&
- !do_syslog(SYSLOG_ACTION_SIZE_UNREAD, NULL, 0, SYSLOG_FROM_PROC))
+ !do_syslog(SYSLOG_ACTION_SIZE_UNREAD, NULL, 0, SYSLOG_FROM_PROC,
+ file->f_cred->user_ns->syslog_ns))
return -EAGAIN;
- return do_syslog(SYSLOG_ACTION_READ, buf, count, SYSLOG_FROM_PROC);
+ return do_syslog(SYSLOG_ACTION_READ, buf, count, SYSLOG_FROM_PROC,
+ file->f_cred->user_ns->syslog_ns);
}

static unsigned int kmsg_poll(struct file *file, poll_table *wait)
{
poll_wait(file, &log_wait, wait);
- if (do_syslog(SYSLOG_ACTION_SIZE_UNREAD, NULL, 0, SYSLOG_FROM_PROC))
+ if (do_syslog(SYSLOG_ACTION_SIZE_UNREAD, NULL, 0, SYSLOG_FROM_PROC,
+ file->f_cred->user_ns->syslog_ns))
return POLLIN | POLLRDNORM;
return 0;
}
diff --git a/include/linux/printk.h b/include/linux/printk.h
index 22c7052..29e3f85 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -139,7 +139,6 @@ extern bool printk_timed_ratelimit(unsigned long *caller_jiffies,
unsigned int interval_msec);

extern int printk_delay_msec;
-extern int dmesg_restrict;
extern int kptr_restrict;

extern void wake_up_klogd(void);
diff --git a/include/linux/syslog.h b/include/linux/syslog.h
index 363bc56..fbf0cb6 100644
--- a/include/linux/syslog.h
+++ b/include/linux/syslog.h
@@ -120,7 +120,8 @@ static inline void put_syslog_ns(struct syslog_namespace *ns)
kref_put(&ns->kref, free_syslog_ns);
}

-int do_syslog(int type, char __user *buf, int count, bool from_file);
+int do_syslog(int type, char __user *buf, int count, bool from_file,
+ struct syslog_namespace *ns);

extern struct syslog_namespace init_syslog_ns;
#endif /* _LINUX_SYSLOG_H */
diff --git a/kernel/printk.c b/kernel/printk.c
index fd83ec1..846fef5 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -213,29 +213,8 @@ static DEFINE_RAW_SPINLOCK(logbuf_lock);

#ifdef CONFIG_PRINTK
DECLARE_WAIT_QUEUE_HEAD(log_wait);
-/* the next printk record to read by syslog(READ) or /proc/kmsg */
-static u64 syslog_seq;
-static u32 syslog_idx;
-static enum log_flags syslog_prev;
-static size_t syslog_partial;
-
-/* index and sequence number of the first record stored in the buffer */
-static u64 log_first_seq;
-static u32 log_first_idx;
-
-/* index and sequence number of the next record to store in the buffer */
-static u64 log_next_seq;
-static u32 log_next_idx;
-
-/* the next printk record to write to the console */
-static u64 console_seq;
-static u32 console_idx;
static enum log_flags console_prev;

-/* the next printk record to read after the last 'clear' command */
-static u64 clear_seq;
-static u32 clear_idx;
-
#define PREFIX_MAX 32
#define LOG_LINE_MAX 1024 - PREFIX_MAX

@@ -246,12 +225,8 @@ static u32 clear_idx;
#define LOG_ALIGN __alignof__(struct log)
#endif
#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
+/* this buf only for init_syslog_ns */
static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);
-static char *log_buf = __log_buf;
-static u32 log_buf_len = __LOG_BUF_LEN;
-
-/* cpu currently holding logbuf_lock */
-static volatile unsigned int logbuf_cpu = UINT_MAX;

struct syslog_namespace init_syslog_ns = {
.kref = {
@@ -282,23 +257,23 @@ static char *log_dict(const struct log *msg)
}

/* get record by index; idx must point to valid msg */
-static struct log *log_from_idx(u32 idx)
+static struct log *log_from_idx(u32 idx, struct syslog_namespace *ns)
{
- struct log *msg = (struct log *)(log_buf + idx);
+ struct log *msg = (struct log *)(ns->log_buf + idx);

/*
* A length == 0 record is the end of buffer marker. Wrap around and
* read the message at the start of the buffer.
*/
if (!msg->len)
- return (struct log *)log_buf;
+ return (struct log *)ns->log_buf;
return msg;
}

/* get next record; idx must point to valid msg */
-static u32 log_next(u32 idx)
+static u32 log_next(u32 idx, struct syslog_namespace *ns)
{
- struct log *msg = (struct log *)(log_buf + idx);
+ struct log *msg = (struct log *)(ns->log_buf + idx);

/* length == 0 indicates the end of the buffer; wrap */
/*
@@ -307,7 +282,7 @@ static u32 log_next(u32 idx)
* return the one after that.
*/
if (!msg->len) {
- msg = (struct log *)log_buf;
+ msg = (struct log *)ns->log_buf;
return msg->len;
}
return idx + msg->len;
@@ -317,7 +292,8 @@ static u32 log_next(u32 idx)
static void log_store(int facility, int level,
enum log_flags flags, u64 ts_nsec,
const char *dict, u16 dict_len,
- const char *text, u16 text_len)
+ const char *text, u16 text_len,
+ struct syslog_namespace *ns)
{
struct log *msg;
u32 size, pad_len;
@@ -327,34 +303,40 @@ static void log_store(int facility, int level,
pad_len = (-size) & (LOG_ALIGN - 1);
size += pad_len;

- while (log_first_seq < log_next_seq) {
+ while (ns->log_first_seq < ns->log_next_seq) {
u32 free;

- if (log_next_idx > log_first_idx)
- free = max(log_buf_len - log_next_idx, log_first_idx);
+ if (ns->log_next_idx > ns->log_first_idx)
+ free = max(ns->log_buf_len -
+ ns->log_next_idx,
+ ns->log_first_idx);
else
- free = log_first_idx - log_next_idx;
+ free = ns->log_first_idx -
+ ns->log_next_idx;

if (free > size + sizeof(struct log))
break;

/* drop old messages until we have enough contiuous space */
- log_first_idx = log_next(log_first_idx);
- log_first_seq++;
+ ns->log_first_idx =
+ log_next(ns->log_first_idx, ns);
+ ns->log_first_seq++;
}

- if (log_next_idx + size + sizeof(struct log) >= log_buf_len) {
+ if (ns->log_next_idx + size + sizeof(struct log) >=
+ ns->log_buf_len) {
/*
* This message + an additional empty header does not fit
* at the end of the buffer. Add an empty header with len == 0
* to signify a wrap around.
*/
- memset(log_buf + log_next_idx, 0, sizeof(struct log));
- log_next_idx = 0;
+ memset(ns->log_buf + ns->log_next_idx,
+ 0, sizeof(struct log));
+ ns->log_next_idx = 0;
}

/* fill message */
- msg = (struct log *)(log_buf + log_next_idx);
+ msg = (struct log *)(ns->log_buf + ns->log_next_idx);
memcpy(log_text(msg), text, text_len);
msg->text_len = text_len;
memcpy(log_dict(msg), dict, dict_len);
@@ -370,19 +352,14 @@ static void log_store(int facility, int level,
msg->len = sizeof(struct log) + text_len + dict_len + pad_len;

/* insert message */
- log_next_idx += msg->len;
- log_next_seq++;
+ ns->log_next_idx += msg->len;
+ ns->log_next_seq++;
}

-#ifdef CONFIG_SECURITY_DMESG_RESTRICT
-int dmesg_restrict = 1;
-#else
-int dmesg_restrict;
-#endif
-
-static int syslog_action_restricted(int type)
+static int syslog_action_restricted(int type,
+ struct syslog_namespace *ns)
{
- if (dmesg_restrict)
+ if (ns->dmesg_restrict)
return 1;
/*
* Unless restricted, we allow "read all" and "get buffer size"
@@ -392,7 +369,8 @@ static int syslog_action_restricted(int type)
type != SYSLOG_ACTION_SIZE_BUFFER;
}

-static int check_syslog_permissions(int type, bool from_file)
+static int check_syslog_permissions(int type, bool from_file,
+ struct syslog_namespace *ns)
{
/*
* If this is from /proc/kmsg and we've already opened it, then we've
@@ -401,7 +379,7 @@ static int check_syslog_permissions(int type, bool from_file)
if (from_file && type != SYSLOG_ACTION_OPEN)
return 0;

- if (syslog_action_restricted(type)) {
+ if (syslog_action_restricted(type, ns)) {
if (capable(CAP_SYSLOG))
return 0;
/*
@@ -496,6 +474,8 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
char cont = '-';
size_t len;
ssize_t ret;
+ struct syslog_namespace *ns =
+ file->f_cred->user_ns->syslog_ns;

if (!user)
return -EBADF;
@@ -503,32 +483,32 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
ret = mutex_lock_interruptible(&user->lock);
if (ret)
return ret;
- raw_spin_lock_irq(&logbuf_lock);
- while (user->seq == log_next_seq) {
+ raw_spin_lock_irq(&ns->logbuf_lock);
+ while (user->seq == ns->log_next_seq) {
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
- raw_spin_unlock_irq(&logbuf_lock);
+ raw_spin_unlock_irq(&ns->logbuf_lock);
goto out;
}

- raw_spin_unlock_irq(&logbuf_lock);
+ raw_spin_unlock_irq(&ns->logbuf_lock);
ret = wait_event_interruptible(log_wait,
- user->seq != log_next_seq);
+ user->seq != ns->log_next_seq);
if (ret)
goto out;
- raw_spin_lock_irq(&logbuf_lock);
+ raw_spin_lock_irq(&ns->logbuf_lock);
}

- if (user->seq < log_first_seq) {
+ if (user->seq < ns->log_first_seq) {
/* our last seen message is gone, return error and reset */
- user->idx = log_first_idx;
- user->seq = log_first_seq;
+ user->idx = ns->log_first_idx;
+ user->seq = ns->log_first_seq;
ret = -EPIPE;
- raw_spin_unlock_irq(&logbuf_lock);
+ raw_spin_unlock_irq(&ns->logbuf_lock);
goto out;
}

- msg = log_from_idx(user->idx);
+ msg = log_from_idx(user->idx, ns);
ts_usec = msg->ts_nsec;
do_div(ts_usec, 1000);

@@ -589,9 +569,9 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
user->buf[len++] = '\n';
}

- user->idx = log_next(user->idx);
+ user->idx = log_next(user->idx, ns);
user->seq++;
- raw_spin_unlock_irq(&logbuf_lock);
+ raw_spin_unlock_irq(&ns->logbuf_lock);

if (len > count) {
ret = -EINVAL;
@@ -612,18 +592,19 @@ static loff_t devkmsg_llseek(struct file *file, loff_t offset, int whence)
{
struct devkmsg_user *user = file->private_data;
loff_t ret = 0;
+ struct syslog_namespace *ns = file->f_cred->user_ns->syslog_ns;

if (!user)
return -EBADF;
if (offset)
return -ESPIPE;

- raw_spin_lock_irq(&logbuf_lock);
+ raw_spin_lock_irq(&ns->logbuf_lock);
switch (whence) {
case SEEK_SET:
/* the first record */
- user->idx = log_first_idx;
- user->seq = log_first_seq;
+ user->idx = ns->log_first_idx;
+ user->seq = ns->log_first_seq;
break;
case SEEK_DATA:
/*
@@ -631,18 +612,18 @@ static loff_t devkmsg_llseek(struct file *file, loff_t offset, int whence)
* like issued by 'dmesg -c'. Reading /dev/kmsg itself
* changes no global state, and does not clear anything.
*/
- user->idx = clear_idx;
- user->seq = clear_seq;
+ user->idx = ns->clear_idx;
+ user->seq = ns->clear_seq;
break;
case SEEK_END:
/* after the last record */
- user->idx = log_next_idx;
- user->seq = log_next_seq;
+ user->idx = ns->log_next_idx;
+ user->seq = ns->log_next_seq;
break;
default:
ret = -EINVAL;
}
- raw_spin_unlock_irq(&logbuf_lock);
+ raw_spin_unlock_irq(&ns->logbuf_lock);
return ret;
}

@@ -650,21 +631,22 @@ static unsigned int devkmsg_poll(struct file *file, poll_table *wait)
{
struct devkmsg_user *user = file->private_data;
int ret = 0;
+ struct syslog_namespace *ns = file->f_cred->user_ns->syslog_ns;

if (!user)
return POLLERR|POLLNVAL;

poll_wait(file, &log_wait, wait);

- raw_spin_lock_irq(&logbuf_lock);
- if (user->seq < log_next_seq) {
+ raw_spin_lock_irq(&ns->logbuf_lock);
+ if (user->seq < ns->log_next_seq) {
/* return error when data has vanished underneath us */
- if (user->seq < log_first_seq)
+ if (user->seq < ns->log_first_seq)
ret = POLLIN|POLLRDNORM|POLLERR|POLLPRI;
else
ret = POLLIN|POLLRDNORM;
}
- raw_spin_unlock_irq(&logbuf_lock);
+ raw_spin_unlock_irq(&ns->logbuf_lock);

return ret;
}
@@ -673,13 +655,14 @@ static int devkmsg_open(struct inode *inode, struct file *file)
{
struct devkmsg_user *user;
int err;
+ struct syslog_namespace *ns = file->f_cred->user_ns->syslog_ns;

/* write-only does not need any file context */
if ((file->f_flags & O_ACCMODE) == O_WRONLY)
return 0;

err = check_syslog_permissions(SYSLOG_ACTION_READ_ALL,
- SYSLOG_FROM_READER);
+ SYSLOG_FROM_READER, ns);
if (err)
return err;

@@ -689,10 +672,10 @@ static int devkmsg_open(struct inode *inode, struct file *file)

mutex_init(&user->lock);

- raw_spin_lock_irq(&logbuf_lock);
- user->idx = log_first_idx;
- user->seq = log_first_seq;
- raw_spin_unlock_irq(&logbuf_lock);
+ raw_spin_lock_irq(&ns->logbuf_lock);
+ user->idx = ns->log_first_idx;
+ user->seq = ns->log_first_seq;
+ raw_spin_unlock_irq(&ns->logbuf_lock);

file->private_data = user;
return 0;
@@ -730,10 +713,11 @@ const struct file_operations kmsg_fops = {
*/
void log_buf_kexec_setup(void)
{
- VMCOREINFO_SYMBOL(log_buf);
- VMCOREINFO_SYMBOL(log_buf_len);
- VMCOREINFO_SYMBOL(log_first_idx);
- VMCOREINFO_SYMBOL(log_next_idx);
+ struct syslog_namespace *ns = &init_syslog_ns;
+ VMCOREINFO_SYMBOL(ns->log_buf);
+ VMCOREINFO_SYMBOL(ns->log_buf_len);
+ VMCOREINFO_SYMBOL(ns->log_first_idx);
+ VMCOREINFO_SYMBOL(ns->log_next_idx);
/*
* Export struct log size and field offsets. User space tools can
* parse it and detect any changes to structure down the line.
@@ -753,10 +737,11 @@ static unsigned long __initdata new_log_buf_len;
static int __init log_buf_len_setup(char *str)
{
unsigned size = memparse(str, &str);
+ struct syslog_namespace *ns = &init_syslog_ns;

if (size)
size = roundup_pow_of_two(size);
- if (size > log_buf_len)
+ if (size > ns->log_buf_len)
new_log_buf_len = size;

return 0;
@@ -768,6 +753,7 @@ void __init setup_log_buf(int early)
unsigned long flags;
char *new_log_buf;
int free;
+ struct syslog_namespace *ns = &init_syslog_ns;

if (!new_log_buf_len)
return;
@@ -789,15 +775,15 @@ void __init setup_log_buf(int early)
return;
}

- raw_spin_lock_irqsave(&logbuf_lock, flags);
- log_buf_len = new_log_buf_len;
- log_buf = new_log_buf;
+ raw_spin_lock_irqsave(&ns->logbuf_lock, flags);
+ memcpy(new_log_buf, ns->log_buf, __LOG_BUF_LEN);
+ ns->log_buf_len = new_log_buf_len;
+ ns->log_buf = new_log_buf;
new_log_buf_len = 0;
- free = __LOG_BUF_LEN - log_next_idx;
- memcpy(log_buf, __log_buf, __LOG_BUF_LEN);
- raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+ free = __LOG_BUF_LEN - ns->log_next_idx;
+ raw_spin_unlock_irqrestore(&ns->logbuf_lock, flags);

- pr_info("log_buf_len: %d\n", log_buf_len);
+ pr_info("log_buf_len: %d\n", ns->log_buf_len);
pr_info("early log buf free: %d(%d%%)\n",
free, (free * 100) / __LOG_BUF_LEN);
}
@@ -977,7 +963,8 @@ static size_t msg_print_text(const struct log *msg, enum log_flags prev,
return len;
}

-static int syslog_print(char __user *buf, int size)
+static int syslog_print(char __user *buf, int size,
+ struct syslog_namespace *ns)
{
char *text;
struct log *msg;
@@ -991,37 +978,38 @@ static int syslog_print(char __user *buf, int size)
size_t n;
size_t skip;

- raw_spin_lock_irq(&logbuf_lock);
- if (syslog_seq < log_first_seq) {
+ raw_spin_lock_irq(&ns->logbuf_lock);
+ if (ns->syslog_seq < ns->log_first_seq) {
/* messages are gone, move to first one */
- syslog_seq = log_first_seq;
- syslog_idx = log_first_idx;
- syslog_prev = 0;
- syslog_partial = 0;
+ ns->syslog_seq = ns->log_first_seq;
+ ns->syslog_idx = ns->log_first_idx;
+ ns->syslog_prev = 0;
+ ns->syslog_partial = 0;
}
- if (syslog_seq == log_next_seq) {
- raw_spin_unlock_irq(&logbuf_lock);
+ if (ns->syslog_seq == ns->log_next_seq) {
+ raw_spin_unlock_irq(&ns->logbuf_lock);
break;
}

- skip = syslog_partial;
- msg = log_from_idx(syslog_idx);
- n = msg_print_text(msg, syslog_prev, true, text,
+ skip = ns->syslog_partial;
+ msg = log_from_idx(ns->syslog_idx, ns);
+ n = msg_print_text(msg, ns->syslog_prev, true, text,
LOG_LINE_MAX + PREFIX_MAX);
- if (n - syslog_partial <= size) {
+ if (n - ns->syslog_partial <= size) {
/* message fits into buffer, move forward */
- syslog_idx = log_next(syslog_idx);
- syslog_seq++;
- syslog_prev = msg->flags;
- n -= syslog_partial;
- syslog_partial = 0;
+ ns->syslog_idx =
+ log_next(ns->syslog_idx, ns);
+ ns->syslog_seq++;
+ ns->syslog_prev = msg->flags;
+ n -= ns->syslog_partial;
+ ns->syslog_partial = 0;
} else if (!len){
/* partial read(), remember position */
n = size;
- syslog_partial += n;
+ ns->syslog_partial += n;
} else
n = 0;
- raw_spin_unlock_irq(&logbuf_lock);
+ raw_spin_unlock_irq(&ns->logbuf_lock);

if (!n)
break;
@@ -1041,7 +1029,8 @@ static int syslog_print(char __user *buf, int size)
return len;
}

-static int syslog_print_all(char __user *buf, int size, bool clear)
+static int syslog_print_all(char __user *buf, int size, bool clear,
+ struct syslog_namespace *ns)
{
char *text;
int len = 0;
@@ -1050,55 +1039,55 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
if (!text)
return -ENOMEM;

- raw_spin_lock_irq(&logbuf_lock);
+ raw_spin_lock_irq(&ns->logbuf_lock);
if (buf) {
u64 next_seq;
u64 seq;
u32 idx;
enum log_flags prev;

- if (clear_seq < log_first_seq) {
+ if (ns->clear_seq < ns->log_first_seq) {
/* messages are gone, move to first available one */
- clear_seq = log_first_seq;
- clear_idx = log_first_idx;
+ ns->clear_seq = ns->log_first_seq;
+ ns->clear_idx = ns->log_first_idx;
}

/*
* Find first record that fits, including all following records,
* into the user-provided buffer for this dump.
*/
- seq = clear_seq;
- idx = clear_idx;
+ seq = ns->clear_seq;
+ idx = ns->clear_idx;
prev = 0;
- while (seq < log_next_seq) {
- struct log *msg = log_from_idx(idx);
+ while (seq < ns->log_next_seq) {
+ struct log *msg = log_from_idx(idx, ns);

len += msg_print_text(msg, prev, true, NULL, 0);
prev = msg->flags;
- idx = log_next(idx);
+ idx = log_next(idx, ns);
seq++;
}

/* move first record forward until length fits into the buffer */
- seq = clear_seq;
- idx = clear_idx;
+ seq = ns->clear_seq;
+ idx = ns->clear_idx;
prev = 0;
- while (len > size && seq < log_next_seq) {
- struct log *msg = log_from_idx(idx);
+ while (len > size && seq < ns->log_next_seq) {
+ struct log *msg = log_from_idx(idx, ns);

len -= msg_print_text(msg, prev, true, NULL, 0);
prev = msg->flags;
- idx = log_next(idx);
+ idx = log_next(idx, ns);
seq++;
}

/* last message fitting into this dump */
- next_seq = log_next_seq;
+ next_seq = ns->log_next_seq;

len = 0;
prev = 0;
while (len >= 0 && seq < next_seq) {
- struct log *msg = log_from_idx(idx);
+ struct log *msg = log_from_idx(idx, ns);
int textlen;

textlen = msg_print_text(msg, prev, true, text,
@@ -1107,43 +1096,44 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
len = textlen;
break;
}
- idx = log_next(idx);
+ idx = log_next(idx, ns);
seq++;
prev = msg->flags;

- raw_spin_unlock_irq(&logbuf_lock);
+ raw_spin_unlock_irq(&ns->logbuf_lock);
if (copy_to_user(buf + len, text, textlen))
len = -EFAULT;
else
len += textlen;
- raw_spin_lock_irq(&logbuf_lock);
+ raw_spin_lock_irq(&ns->logbuf_lock);

- if (seq < log_first_seq) {
+ if (seq < ns->log_first_seq) {
/* messages are gone, move to next one */
- seq = log_first_seq;
- idx = log_first_idx;
+ seq = ns->log_first_seq;
+ idx = ns->log_first_idx;
prev = 0;
}
}
}

if (clear) {
- clear_seq = log_next_seq;
- clear_idx = log_next_idx;
+ ns->clear_seq = ns->log_next_seq;
+ ns->clear_idx = ns->log_next_idx;
}
- raw_spin_unlock_irq(&logbuf_lock);
+ raw_spin_unlock_irq(&ns->logbuf_lock);

kfree(text);
return len;
}

-int do_syslog(int type, char __user *buf, int len, bool from_file)
+int do_syslog(int type, char __user *buf, int len, bool from_file,
+ struct syslog_namespace *ns)
{
bool clear = false;
static int saved_console_loglevel = -1;
int error;

- error = check_syslog_permissions(type, from_file);
+ error = check_syslog_permissions(type, from_file, ns);
if (error)
goto out;

@@ -1168,10 +1158,10 @@ int do_syslog(int type, char __user *buf, int len, bool from_file)
goto out;
}
error = wait_event_interruptible(log_wait,
- syslog_seq != log_next_seq);
+ ns->syslog_seq != ns->log_next_seq);
if (error)
goto out;
- error = syslog_print(buf, len);
+ error = syslog_print(buf, len, ns);
break;
/* Read/clear last kernel messages */
case SYSLOG_ACTION_READ_CLEAR:
@@ -1189,11 +1179,11 @@ int do_syslog(int type, char __user *buf, int len, bool from_file)
error = -EFAULT;
goto out;
}
- error = syslog_print_all(buf, len, clear);
+ error = syslog_print_all(buf, len, clear, ns);
break;
/* Clear ring buffer */
case SYSLOG_ACTION_CLEAR:
- syslog_print_all(NULL, 0, true);
+ syslog_print_all(NULL, 0, true, ns);
break;
/* Disable logging to console */
case SYSLOG_ACTION_CONSOLE_OFF:
@@ -1222,13 +1212,13 @@ int do_syslog(int type, char __user *buf, int len, bool from_file)
break;
/* Number of chars in the log buffer */
case SYSLOG_ACTION_SIZE_UNREAD:
- raw_spin_lock_irq(&logbuf_lock);
- if (syslog_seq < log_first_seq) {
+ raw_spin_lock_irq(&ns->logbuf_lock);
+ if (ns->syslog_seq < ns->log_first_seq) {
/* messages are gone, move to first one */
- syslog_seq = log_first_seq;
- syslog_idx = log_first_idx;
- syslog_prev = 0;
- syslog_partial = 0;
+ ns->syslog_seq = ns->log_first_seq;
+ ns->syslog_idx = ns->log_first_idx;
+ ns->syslog_prev = 0;
+ ns->syslog_partial = 0;
}
if (from_file) {
/*
@@ -1236,28 +1226,28 @@ int do_syslog(int type, char __user *buf, int len, bool from_file)
* for pending data, not the size; return the count of
* records, not the length.
*/
- error = log_next_idx - syslog_idx;
+ error = ns->log_next_idx - ns->syslog_idx;
} else {
- u64 seq = syslog_seq;
- u32 idx = syslog_idx;
- enum log_flags prev = syslog_prev;
+ u64 seq = ns->syslog_seq;
+ u32 idx = ns->syslog_idx;
+ enum log_flags prev = ns->syslog_prev;

error = 0;
- while (seq < log_next_seq) {
- struct log *msg = log_from_idx(idx);
+ while (seq < ns->log_next_seq) {
+ struct log *msg = log_from_idx(idx, ns);

error += msg_print_text(msg, prev, true, NULL, 0);
- idx = log_next(idx);
+ idx = log_next(idx, ns);
seq++;
prev = msg->flags;
}
- error -= syslog_partial;
+ error -= ns->syslog_partial;
}
- raw_spin_unlock_irq(&logbuf_lock);
+ raw_spin_unlock_irq(&ns->logbuf_lock);
break;
/* Size of the log buffer */
case SYSLOG_ACTION_SIZE_BUFFER:
- error = log_buf_len;
+ error = ns->log_buf_len;
break;
default:
error = -EINVAL;
@@ -1269,7 +1259,8 @@ out:

SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len)
{
- return do_syslog(type, buf, len, SYSLOG_FROM_READER);
+ return do_syslog(type, buf, len, SYSLOG_FROM_READER,
+ current_user_ns()->syslog_ns);
}

/*
@@ -1307,7 +1298,7 @@ static void call_console_drivers(int level, const char *text, size_t len)
* every 10 seconds, to leave time for slow consoles to print a
* full oops.
*/
-static void zap_locks(void)
+static void zap_locks(struct syslog_namespace *ns)
{
static unsigned long oops_timestamp;

@@ -1319,7 +1310,7 @@ static void zap_locks(void)

debug_locks_off();
/* If a crash is occurring, make sure we can't deadlock */
- raw_spin_lock_init(&logbuf_lock);
+ raw_spin_lock_init(&ns->logbuf_lock);
/* And make sure that we print immediately */
sema_init(&console_sem, 1);
}
@@ -1359,8 +1350,9 @@ static inline int can_use_console(unsigned int cpu)
* interrupts disabled. It should return with 'lockbuf_lock'
* released but interrupts still disabled.
*/
-static int console_trylock_for_printk(unsigned int cpu)
- __releases(&logbuf_lock)
+static int console_trylock_for_printk(unsigned int cpu,
+ struct syslog_namespace *ns)
+ __releases(&ns->logbuf_lock)
{
int retval = 0, wake = 0;

@@ -1379,8 +1371,8 @@ static int console_trylock_for_printk(unsigned int cpu)
retval = 0;
}
}
- logbuf_cpu = UINT_MAX;
- raw_spin_unlock(&logbuf_lock);
+ ns->logbuf_cpu = UINT_MAX;
+ raw_spin_unlock(&ns->logbuf_lock);
if (wake)
up(&console_sem);
return retval;
@@ -1418,7 +1410,7 @@ static struct cont {
bool flushed:1; /* buffer sealed and committed */
} cont;

-static void cont_flush(enum log_flags flags)
+static void cont_flush(enum log_flags flags, struct syslog_namespace *ns)
{
if (cont.flushed)
return;
@@ -1432,7 +1424,7 @@ static void cont_flush(enum log_flags flags)
* line. LOG_NOCONS suppresses a duplicated output.
*/
log_store(cont.facility, cont.level, flags | LOG_NOCONS,
- cont.ts_nsec, NULL, 0, cont.buf, cont.len);
+ cont.ts_nsec, NULL, 0, cont.buf, cont.len, ns);
cont.flags = flags;
cont.flushed = true;
} else {
@@ -1441,19 +1433,20 @@ static void cont_flush(enum log_flags flags)
* just submit it to the store and free the buffer.
*/
log_store(cont.facility, cont.level, flags, 0,
- NULL, 0, cont.buf, cont.len);
+ NULL, 0, cont.buf, cont.len, ns);
cont.len = 0;
}
}

-static bool cont_add(int facility, int level, const char *text, size_t len)
+static bool cont_add(int facility, int level, const char *text, size_t len,
+ struct syslog_namespace *ns)
{
if (cont.len && cont.flushed)
return false;

if (cont.len + len > sizeof(cont.buf)) {
/* the line gets too long, split it up in separate records */
- cont_flush(LOG_CONT);
+ cont_flush(LOG_CONT, ns);
return false;
}

@@ -1471,7 +1464,7 @@ static bool cont_add(int facility, int level, const char *text, size_t len)
cont.len += len;

if (cont.len > (sizeof(cont.buf) * 80) / 100)
- cont_flush(LOG_CONT);
+ cont_flush(LOG_CONT, ns);

return true;
}
@@ -1516,6 +1509,7 @@ asmlinkage int vprintk_emit(int facility, int level,
unsigned long flags;
int this_cpu;
int printed_len = 0;
+ struct syslog_namespace *ns = &init_syslog_ns;

boot_delay_msec(level);
printk_delay();
@@ -1527,7 +1521,7 @@ asmlinkage int vprintk_emit(int facility, int level,
/*
* Ouch, printk recursed into itself!
*/
- if (unlikely(logbuf_cpu == this_cpu)) {
+ if (unlikely(ns->logbuf_cpu == this_cpu)) {
/*
* If a crash is occurring during printk() on this CPU,
* then try to get the crash message out but make sure
@@ -1539,12 +1533,12 @@ asmlinkage int vprintk_emit(int facility, int level,
recursion_bug = 1;
goto out_restore_irqs;
}
- zap_locks();
+ zap_locks(ns);
}

lockdep_off();
- raw_spin_lock(&logbuf_lock);
- logbuf_cpu = this_cpu;
+ raw_spin_lock(&ns->logbuf_lock);
+ ns->logbuf_cpu = this_cpu;

if (recursion_bug) {
static const char recursion_msg[] =
@@ -1554,7 +1548,7 @@ asmlinkage int vprintk_emit(int facility, int level,
printed_len += strlen(recursion_msg);
/* emit KERN_CRIT message */
log_store(0, 2, LOG_PREFIX|LOG_NEWLINE, 0,
- NULL, 0, recursion_msg, printed_len);
+ NULL, 0, recursion_msg, printed_len, ns);
}

/*
@@ -1601,12 +1595,12 @@ asmlinkage int vprintk_emit(int facility, int level,
* or another task also prints continuation lines.
*/
if (cont.len && (lflags & LOG_PREFIX || cont.owner != current))
- cont_flush(LOG_NEWLINE);
+ cont_flush(LOG_NEWLINE, ns);

/* buffer line if possible, otherwise store it right away */
- if (!cont_add(facility, level, text, text_len))
+ if (!cont_add(facility, level, text, text_len, ns))
log_store(facility, level, lflags | LOG_CONT, 0,
- dict, dictlen, text, text_len);
+ dict, dictlen, text, text_len, ns);
} else {
bool stored = false;

@@ -1618,13 +1612,14 @@ asmlinkage int vprintk_emit(int facility, int level,
*/
if (cont.len && cont.owner == current) {
if (!(lflags & LOG_PREFIX))
- stored = cont_add(facility, level, text, text_len);
- cont_flush(LOG_NEWLINE);
+ stored = cont_add(facility, level, text,
+ text_len, ns);
+ cont_flush(LOG_NEWLINE, ns);
}

if (!stored)
log_store(facility, level, lflags, 0,
- dict, dictlen, text, text_len);
+ dict, dictlen, text, text_len, ns);
}
printed_len += text_len;

@@ -1636,7 +1631,7 @@ asmlinkage int vprintk_emit(int facility, int level,
* The console_trylock_for_printk() function will release 'logbuf_lock'
* regardless of whether it actually gets the console semaphore or not.
*/
- if (console_trylock_for_printk(this_cpu))
+ if (console_trylock_for_printk(this_cpu, ns))
console_unlock();

lockdep_on();
@@ -1995,12 +1990,13 @@ int is_console_locked(void)
return console_locked;
}

-static void console_cont_flush(char *text, size_t size)
+static void console_cont_flush(char *text, size_t size,
+ struct syslog_namespace *ns)
{
unsigned long flags;
size_t len;

- raw_spin_lock_irqsave(&logbuf_lock, flags);
+ raw_spin_lock_irqsave(&ns->logbuf_lock, flags);

if (!cont.len)
goto out;
@@ -2010,18 +2006,18 @@ static void console_cont_flush(char *text, size_t size)
* busy. The earlier ones need to be printed before this one, we
* did not flush any fragment so far, so just let it queue up.
*/
- if (console_seq < log_next_seq && !cont.cons)
+ if (ns->console_seq < ns->log_next_seq && !cont.cons)
goto out;

len = cont_print_text(text, size);
- raw_spin_unlock(&logbuf_lock);
+ raw_spin_unlock(&ns->logbuf_lock);
stop_critical_timings();
call_console_drivers(cont.level, text, len);
start_critical_timings();
local_irq_restore(flags);
return;
out:
- raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+ raw_spin_unlock_irqrestore(&ns->logbuf_lock, flags);
}

/**
@@ -2045,6 +2041,7 @@ void console_unlock(void)
unsigned long flags;
bool wake_klogd = false;
bool retry;
+ struct syslog_namespace *ns = &init_syslog_ns;

if (console_suspended) {
up(&console_sem);
@@ -2054,37 +2051,38 @@ void console_unlock(void)
console_may_schedule = 0;

/* flush buffered message fragment immediately to console */
- console_cont_flush(text, sizeof(text));
+ console_cont_flush(text, sizeof(text), ns);
again:
for (;;) {
struct log *msg;
size_t len;
int level;

- raw_spin_lock_irqsave(&logbuf_lock, flags);
- if (seen_seq != log_next_seq) {
+ raw_spin_lock_irqsave(&ns->logbuf_lock, flags);
+ if (seen_seq != ns->log_next_seq) {
wake_klogd = true;
- seen_seq = log_next_seq;
+ seen_seq = ns->log_next_seq;
}

- if (console_seq < log_first_seq) {
+ if (ns->console_seq < ns->log_first_seq) {
/* messages are gone, move to first one */
- console_seq = log_first_seq;
- console_idx = log_first_idx;
+ ns->console_seq = ns->log_first_seq;
+ ns->console_idx = ns->log_first_idx;
console_prev = 0;
}
skip:
- if (console_seq == log_next_seq)
+ if (ns->console_seq == ns->log_next_seq)
break;

- msg = log_from_idx(console_idx);
+ msg = log_from_idx(ns->console_idx, ns);
if (msg->flags & LOG_NOCONS) {
/*
* Skip record we have buffered and already printed
* directly to the console when we received it.
*/
- console_idx = log_next(console_idx);
- console_seq++;
+ ns->console_idx =
+ log_next(ns->console_idx, ns);
+ ns->console_seq++;
/*
* We will get here again when we register a new
* CON_PRINTBUFFER console. Clear the flag so we
@@ -2098,10 +2096,11 @@ skip:
level = msg->level;
len = msg_print_text(msg, console_prev, false,
text, sizeof(text));
- console_idx = log_next(console_idx);
- console_seq++;
+ ns->console_idx =
+ log_next(ns->console_idx, ns);
+ ns->console_seq++;
console_prev = msg->flags;
- raw_spin_unlock(&logbuf_lock);
+ raw_spin_unlock(&ns->logbuf_lock);

stop_critical_timings(); /* don't trace print latency */
call_console_drivers(level, text, len);
@@ -2115,7 +2114,7 @@ skip:
if (unlikely(exclusive_console))
exclusive_console = NULL;

- raw_spin_unlock(&logbuf_lock);
+ raw_spin_unlock(&ns->logbuf_lock);

up(&console_sem);

@@ -2125,9 +2124,9 @@ skip:
* there's a new owner and the console_unlock() from them will do the
* flush, no worries.
*/
- raw_spin_lock(&logbuf_lock);
- retry = console_seq != log_next_seq;
- raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+ raw_spin_lock(&ns->logbuf_lock);
+ retry = ns->console_seq != ns->log_next_seq;
+ raw_spin_unlock_irqrestore(&ns->logbuf_lock, flags);

if (retry && console_trylock())
goto again;
@@ -2252,6 +2251,7 @@ void register_console(struct console *newcon)
int i;
unsigned long flags;
struct console *bcon = NULL;
+ struct syslog_namespace *ns = &init_syslog_ns;

/*
* before we register a new CON_BOOT console, make sure we don't
@@ -2361,11 +2361,11 @@ void register_console(struct console *newcon)
* console_unlock(); will print out the buffered messages
* for us.
*/
- raw_spin_lock_irqsave(&logbuf_lock, flags);
- console_seq = syslog_seq;
- console_idx = syslog_idx;
- console_prev = syslog_prev;
- raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+ raw_spin_lock_irqsave(&ns->logbuf_lock, flags);
+ ns->console_seq = ns->syslog_seq;
+ ns->console_idx = ns->syslog_idx;
+ console_prev = ns->syslog_prev;
+ raw_spin_unlock_irqrestore(&ns->logbuf_lock, flags);
/*
* We're about to replay the log buffer. Only do this to the
* just-registered console to avoid excessive message spam to
@@ -2627,6 +2627,7 @@ void kmsg_dump(enum kmsg_dump_reason reason)
{
struct kmsg_dumper *dumper;
unsigned long flags;
+ struct syslog_namespace *ns = &init_syslog_ns;

if ((reason > KMSG_DUMP_OOPS) && !always_kmsg_dump)
return;
@@ -2639,12 +2640,12 @@ void kmsg_dump(enum kmsg_dump_reason reason)
/* initialize iterator with data about the stored records */
dumper->active = true;

- raw_spin_lock_irqsave(&logbuf_lock, flags);
- dumper->cur_seq = clear_seq;
- dumper->cur_idx = clear_idx;
- dumper->next_seq = log_next_seq;
- dumper->next_idx = log_next_idx;
- raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+ raw_spin_lock_irqsave(&ns->logbuf_lock, flags);
+ dumper->cur_seq = ns->clear_seq;
+ dumper->cur_idx = ns->clear_idx;
+ dumper->next_seq = ns->log_next_seq;
+ dumper->next_idx = ns->log_next_idx;
+ raw_spin_unlock_irqrestore(&ns->logbuf_lock, flags);

/* invoke dumper which will iterate over records */
dumper->dump(dumper, reason);
@@ -2680,24 +2681,25 @@ bool kmsg_dump_get_line_nolock(struct kmsg_dumper *dumper, bool syslog,
struct log *msg;
size_t l = 0;
bool ret = false;
+ struct syslog_namespace *ns = &init_syslog_ns;

if (!dumper->active)
goto out;

- if (dumper->cur_seq < log_first_seq) {
+ if (dumper->cur_seq < ns->log_first_seq) {
/* messages are gone, move to first available one */
- dumper->cur_seq = log_first_seq;
- dumper->cur_idx = log_first_idx;
+ dumper->cur_seq = ns->log_first_seq;
+ dumper->cur_idx = ns->log_first_idx;
}

/* last entry */
- if (dumper->cur_seq >= log_next_seq)
+ if (dumper->cur_seq >= ns->log_next_seq)
goto out;

- msg = log_from_idx(dumper->cur_idx);
+ msg = log_from_idx(dumper->cur_idx, ns);
l = msg_print_text(msg, 0, syslog, line, size);

- dumper->cur_idx = log_next(dumper->cur_idx);
+ dumper->cur_idx = log_next(dumper->cur_idx, ns);
dumper->cur_seq++;
ret = true;
out:
@@ -2728,10 +2730,11 @@ bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
{
unsigned long flags;
bool ret;
+ struct syslog_namespace *ns = &init_syslog_ns;

- raw_spin_lock_irqsave(&logbuf_lock, flags);
+ raw_spin_lock_irqsave(&ns->logbuf_lock, flags);
ret = kmsg_dump_get_line_nolock(dumper, syslog, line, size, len);
- raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+ raw_spin_unlock_irqrestore(&ns->logbuf_lock, flags);

return ret;
}
@@ -2767,20 +2770,21 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
enum log_flags prev;
size_t l = 0;
bool ret = false;
+ struct syslog_namespace *ns = &init_syslog_ns;

if (!dumper->active)
goto out;

- raw_spin_lock_irqsave(&logbuf_lock, flags);
- if (dumper->cur_seq < log_first_seq) {
+ raw_spin_lock_irqsave(&ns->logbuf_lock, flags);
+ if (dumper->cur_seq < ns->log_first_seq) {
/* messages are gone, move to first available one */
- dumper->cur_seq = log_first_seq;
- dumper->cur_idx = log_first_idx;
+ dumper->cur_seq = ns->log_first_seq;
+ dumper->cur_idx = ns->log_first_idx;
}

/* last entry */
if (dumper->cur_seq >= dumper->next_seq) {
- raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+ raw_spin_unlock_irqrestore(&ns->logbuf_lock, flags);
goto out;
}

@@ -2789,10 +2793,10 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
idx = dumper->cur_idx;
prev = 0;
while (seq < dumper->next_seq) {
- struct log *msg = log_from_idx(idx);
+ struct log *msg = log_from_idx(idx, ns);

l += msg_print_text(msg, prev, true, NULL, 0);
- idx = log_next(idx);
+ idx = log_next(idx, ns);
seq++;
prev = msg->flags;
}
@@ -2802,10 +2806,10 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
idx = dumper->cur_idx;
prev = 0;
while (l > size && seq < dumper->next_seq) {
- struct log *msg = log_from_idx(idx);
+ struct log *msg = log_from_idx(idx, ns);

l -= msg_print_text(msg, prev, true, NULL, 0);
- idx = log_next(idx);
+ idx = log_next(idx, ns);
seq++;
prev = msg->flags;
}
@@ -2817,10 +2821,10 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
l = 0;
prev = 0;
while (seq < dumper->next_seq) {
- struct log *msg = log_from_idx(idx);
+ struct log *msg = log_from_idx(idx, ns);

l += msg_print_text(msg, prev, syslog, buf + l, size - l);
- idx = log_next(idx);
+ idx = log_next(idx, ns);
seq++;
prev = msg->flags;
}
@@ -2828,7 +2832,7 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
dumper->next_seq = next_seq;
dumper->next_idx = next_idx;
ret = true;
- raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+ raw_spin_unlock_irqrestore(&ns->logbuf_lock, flags);
out:
if (len)
*len = l;
@@ -2848,10 +2852,12 @@ EXPORT_SYMBOL_GPL(kmsg_dump_get_buffer);
*/
void kmsg_dump_rewind_nolock(struct kmsg_dumper *dumper)
{
- dumper->cur_seq = clear_seq;
- dumper->cur_idx = clear_idx;
- dumper->next_seq = log_next_seq;
- dumper->next_idx = log_next_idx;
+ struct syslog_namespace *ns = &init_syslog_ns;
+
+ dumper->cur_seq = ns->clear_seq;
+ dumper->cur_idx = ns->clear_idx;
+ dumper->next_seq = ns->log_next_seq;
+ dumper->next_idx = ns->log_next_idx;
}

/**
@@ -2865,10 +2871,11 @@ void kmsg_dump_rewind_nolock(struct kmsg_dumper *dumper)
void kmsg_dump_rewind(struct kmsg_dumper *dumper)
{
unsigned long flags;
+ struct syslog_namespace *ns = &init_syslog_ns;

- raw_spin_lock_irqsave(&logbuf_lock, flags);
+ raw_spin_lock_irqsave(&ns->logbuf_lock, flags);
kmsg_dump_rewind_nolock(dumper);
- raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+ raw_spin_unlock_irqrestore(&ns->logbuf_lock, flags);
}
EXPORT_SYMBOL_GPL(kmsg_dump_rewind);

diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index ac09d98..0954b09 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -62,6 +62,7 @@
#include <linux/capability.h>
#include <linux/binfmts.h>
#include <linux/sched/sysctl.h>
+#include <linux/syslog.h>

#include <asm/uaccess.h>
#include <asm/processor.h>
@@ -773,7 +774,7 @@ static struct ctl_table kern_table[] = {
},
{
.procname = "dmesg_restrict",
- .data = &dmesg_restrict,
+ .data = &init_syslog_ns.dmesg_restrict,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax_sysadmin,
--
1.8.2.2


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/