Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1256658 > unrolled thread

[RFC v5 0/8] Additional kmsg devices

Started byPaul Osmialowski <p.osmialowsk@samsung.com>
First post2015-10-27 11:30 +0100
Last post2015-10-27 11:40 +0100
Articles 6 — 1 participant

Back to article view | Back to linux.kernel


Contents

  [RFC v5 0/8] Additional kmsg devices Paul Osmialowski <p.osmialowsk@samsung.com> - 2015-10-27 11:30 +0100
    [RFC v5 8/8] kmsg: selftests Paul Osmialowski <p.osmialowsk@samsung.com> - 2015-10-27 11:30 +0100
    [RFC v5 6/8] kmsg: add ioctl for adding and deleting kmsg* devices Paul Osmialowski <p.osmialowsk@samsung.com> - 2015-10-27 11:30 +0100
    [RFC v5 7/8] kmsg: add ioctl for kmsg* devices operating on buffers Paul Osmialowski <p.osmialowsk@samsung.com> - 2015-10-27 11:40 +0100
    [RFC v5 5/8] kmsg: add function for adding and deleting additional  buffers Paul Osmialowski <p.osmialowsk@samsung.com> - 2015-10-27 11:40 +0100
    [RFC v5 4/8] kmsg: add additional buffers support to memory class Paul Osmialowski <p.osmialowsk@samsung.com> - 2015-10-27 11:40 +0100

#1256658 — [RFC v5 0/8] Additional kmsg devices

FromPaul Osmialowski <p.osmialowsk@samsung.com>
Date2015-10-27 11:30 +0100
Subject[RFC v5 0/8] Additional kmsg devices
Message-ID<qo9FU-422-19@gated-at.bofh.it>
Dear All,

This is the fifth iteration of Marcin Niesluchowski's serie of patches
extending kmsg interface with ability to dynamically create (and destroy)
kmsg-like devices which can be used by userspace for logging.

This iteration introduces two changes:

1. selftests are rearranged to use kselftest.h API

2. A disputed  patch "add predefined _PID, _TID, _COMM keywords to kmsg*
   log dict" is removed - no chance it will be ever accepted. It is not
   critical for this patchset as a whole.

Best regards,
Paul

Marcin Niesluchowski (6):
  printk: add one function for storing log in proper format
  kmsg: introduce additional kmsg devices support
  kmsg: add additional buffers support to memory class
  kmsg: add function for adding and deleting additional buffers
  kmsg: add ioctl for adding and deleting kmsg* devices
  kmsg: add ioctl for kmsg* devices operating on buffers

Paul Osmialowski (2):
  printk: extract kmsg-related routines from printk.c to kmsg.c
  kmsg: selftests

 Documentation/ioctl/ioctl-number.txt               |    1 +
 drivers/char/mem.c                                 |   27 +-
 fs/proc/kmsg.c                                     |    4 +-
 include/linux/printk.h                             |   48 +
 include/uapi/linux/Kbuild                          |    1 +
 include/uapi/linux/kmsg_ioctl.h                    |   45 +
 kernel/printk/Makefile                             |    1 +
 kernel/printk/kmsg.c                               | 1022 ++++++++++++++++
 kernel/printk/printk.c                             | 1251 +++++---------------
 kernel/printk/printk.h                             |  256 ++++
 samples/kmsg/kmsg-api.h                            |   44 +
 tools/testing/selftests/Makefile                   |    1 +
 tools/testing/selftests/kmsg/.gitignore            |    1 +
 tools/testing/selftests/kmsg/Makefile              |   30 +
 tools/testing/selftests/kmsg/kmsg-test.c           |  344 ++++++
 tools/testing/selftests/kmsg/kmsg-test.h           |   28 +
 tools/testing/selftests/kmsg/test-buffer-add-del.c |   78 ++
 .../kmsg/test-buffer-add-write-read-del.c          |  163 +++
 .../kmsg/test-buffer-buf-multithreaded-torture.c   |  201 ++++
 .../selftests/kmsg/test-buffer-buf-torture.c       |  141 +++
 20 files changed, 2722 insertions(+), 965 deletions(-)
 create mode 100644 include/uapi/linux/kmsg_ioctl.h
 create mode 100644 kernel/printk/kmsg.c
 create mode 100644 kernel/printk/printk.h
 create mode 100644 samples/kmsg/kmsg-api.h
 create mode 100644 tools/testing/selftests/kmsg/.gitignore
 create mode 100644 tools/testing/selftests/kmsg/Makefile
 create mode 100644 tools/testing/selftests/kmsg/kmsg-test.c
 create mode 100644 tools/testing/selftests/kmsg/kmsg-test.h
 create mode 100644 tools/testing/selftests/kmsg/test-buffer-add-del.c
 create mode 100644 tools/testing/selftests/kmsg/test-buffer-add-write-read-del.c
 create mode 100644 tools/testing/selftests/kmsg/test-buffer-buf-multithreaded-torture.c
 create mode 100644 tools/testing/selftests/kmsg/test-buffer-buf-torture.c

-- 
1.9.1

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

[toc] | [next] | [standalone]


#1256659 — [RFC v5 8/8] kmsg: selftests

FromPaul Osmialowski <p.osmialowsk@samsung.com>
Date2015-10-27 11:30 +0100
Subject[RFC v5 8/8] kmsg: selftests
Message-ID<qo9FU-422-27@gated-at.bofh.it>
In reply to#1256658
This patch adds selftests framework and four test scenarios for kmsg.

The framework shape and code was inspired by similar selftests framework
for kdbus.

Signed-off-by: Paul Osmialowski <p.osmialowsk@samsung.com>
---
 samples/kmsg/kmsg-api.h                            |  44 +++
 tools/testing/selftests/Makefile                   |   1 +
 tools/testing/selftests/kmsg/.gitignore            |   1 +
 tools/testing/selftests/kmsg/Makefile              |  30 ++
 tools/testing/selftests/kmsg/kmsg-test.c           | 344 +++++++++++++++++++++
 tools/testing/selftests/kmsg/kmsg-test.h           |  28 ++
 tools/testing/selftests/kmsg/test-buffer-add-del.c |  78 +++++
 .../kmsg/test-buffer-add-write-read-del.c          | 163 ++++++++++
 .../kmsg/test-buffer-buf-multithreaded-torture.c   | 201 ++++++++++++
 .../selftests/kmsg/test-buffer-buf-torture.c       | 141 +++++++++
 10 files changed, 1031 insertions(+)
 create mode 100644 samples/kmsg/kmsg-api.h
 create mode 100644 tools/testing/selftests/kmsg/.gitignore
 create mode 100644 tools/testing/selftests/kmsg/Makefile
 create mode 100644 tools/testing/selftests/kmsg/kmsg-test.c
 create mode 100644 tools/testing/selftests/kmsg/kmsg-test.h
 create mode 100644 tools/testing/selftests/kmsg/test-buffer-add-del.c
 create mode 100644 tools/testing/selftests/kmsg/test-buffer-add-write-read-del.c
 create mode 100644 tools/testing/selftests/kmsg/test-buffer-buf-multithreaded-torture.c
 create mode 100644 tools/testing/selftests/kmsg/test-buffer-buf-torture.c

diff --git a/samples/kmsg/kmsg-api.h b/samples/kmsg/kmsg-api.h
new file mode 100644
index 0000000..9004acd
--- /dev/null
+++ b/samples/kmsg/kmsg-api.h
@@ -0,0 +1,44 @@
+#ifndef KMSG_API_H
+#define KMSG_API_H
+
+#include <stdint.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <linux/kmsg_ioctl.h>
+
+static inline int kmsg_cmd_buffer_add(int fd, struct kmsg_cmd_buffer_add *cmd)
+{
+	int ret = ioctl(fd, KMSG_CMD_BUFFER_ADD, cmd);
+
+	return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+static inline int kmsg_cmd_buffer_del(int fd, int *minor)
+{
+	int ret = ioctl(fd, KMSG_CMD_BUFFER_DEL, minor);
+
+	return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+static inline int kmsg_cmd_get_buf_size(int fd, uint32_t *size)
+{
+	int ret = ioctl(fd, KMSG_CMD_GET_BUF_SIZE, size);
+
+	return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+static inline int kmsg_cmd_get_read_size_max(int fd, uint32_t *max_size)
+{
+	int ret = ioctl(fd, KMSG_CMD_GET_READ_SIZE_MAX, max_size);
+
+	return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+static inline int kmsg_cmd_clear(int fd)
+{
+	int ret = ioctl(fd, KMSG_CMD_CLEAR);
+
+	return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+#endif /* KMSG_API_H */
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index bf4ece6..b7bdf58 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -7,6 +7,7 @@ TARGETS += ftrace
 TARGETS += futex
 TARGETS += kcmp
 TARGETS += kdbus
+TARGETS += kmsg
 TARGETS += lib
 TARGETS += membarrier
 TARGETS += memfd
diff --git a/tools/testing/selftests/kmsg/.gitignore b/tools/testing/selftests/kmsg/.gitignore
new file mode 100644
index 0000000..687d517
--- /dev/null
+++ b/tools/testing/selftests/kmsg/.gitignore
@@ -0,0 +1 @@
+kmsg-test
diff --git a/tools/testing/selftests/kmsg/Makefile b/tools/testing/selftests/kmsg/Makefile
new file mode 100644
index 0000000..cee2e2b
--- /dev/null
+++ b/tools/testing/selftests/kmsg/Makefile
@@ -0,0 +1,30 @@
+CFLAGS += -I../../../../usr/include/
+CFLAGS += -I../../../../samples/kmsg/
+CFLAGS += -I../../../../include/uapi/
+CFLAGS += -std=gnu99 -Wall
+CFLAGS += -DKBUILD_MODNAME=\"kmsg\" -D_GNU_SOURCE
+CFLAGS += -pthread
+LDLIBS += -pthread
+
+OBJS= \
+	kmsg-test.o				\
+	test-buffer-add-del.o			\
+	test-buffer-add-write-read-del.o	\
+	test-buffer-buf-torture.o		\
+	test-buffer-buf-multithreaded-torture.o
+
+all: kmsg-test
+
+include ../lib.mk
+
+%.o: %.c kmsg-test.h
+	$(CC) $(CFLAGS) -c $< -o $@
+
+kmsg-test: $(OBJS)
+	$(CC) $(CFLAGS) $^ $(LDLIBS) -o $@
+
+run_tests:
+	./kmsg-test
+
+clean:
+	rm -f *.o kmsg-test
diff --git a/tools/testing/selftests/kmsg/kmsg-test.c b/tools/testing/selftests/kmsg/kmsg-test.c
new file mode 100644
index 0000000..282ec1f
--- /dev/null
+++ b/tools/testing/selftests/kmsg/kmsg-test.c
@@ -0,0 +1,344 @@
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include "../kselftest.h"
+
+#include "kmsg-test.h"
+
+struct kmsg_test {
+	const char	*name;
+	const char	*desc;
+	int (*func)(const struct kmsg_test_args *args);
+};
+
+static const struct kmsg_test tests[] = {
+	{
+		.name	= "buffer-add-del",
+		.desc	= "create and delete kmsg devices",
+		.func	= kmsg_test_buffer_add_del,
+	}, {
+		.name	= "buffer-add-write-read-del",
+		.desc	= "create w/r and del kmsg device",
+		.func	= kmsg_test_buffer_add_write_read_del,
+	}, {
+		.name	= "buffer-buf-torture",
+		.desc	= "fill more than whole buffer can hold",
+		.func	= kmsg_test_buffer_buf_torture,
+	}, {
+		.name	= "buffer-buf-multithreaded-torture",
+		.desc	= "fill from many threads",
+		.func	= kmsg_test_buffer_buf_multithreaded_torture,
+	},
+};
+
+#define N_TESTS ARRAY_SIZE(tests)
+
+FILE *kmsg_get_device(int minor, const char *mode)
+{
+	char path[80] = "";
+	dev_t dev = makedev(1, minor);
+
+	if (minor < 0) {
+		printf("Invalid minor number %d\n", minor);
+		return NULL;
+	}
+
+	snprintf(path, sizeof(path), "/tmp/kmsg-%d", minor);
+
+	if (access(path, F_OK) < 0) {
+		if (mknod(path, S_IFCHR | 0600, dev)) {
+			printf("Cannot create device %s with minor %d\n",
+								path, minor);
+			return NULL;
+		}
+	}
+
+	if (access(path, F_OK) < 0) {
+		printf("Cannot access device %s\n", path);
+		return NULL;
+	}
+
+	return fopen(path, mode);
+}
+
+int kmsg_drop_device(int minor)
+{
+	char path[80] = "";
+
+	if (minor < 0) {
+		printf("Invalid minor number %d\n", minor);
+		return -1;
+	}
+
+	snprintf(path, sizeof(path), "/tmp/kmsg-%d", minor);
+
+	return unlink(path);
+}
+
+static void usage(const char *argv0)
+{
+	unsigned int i, j;
+
+	printf("Usage: %s [options]\n"
+	       "Options:\n"
+	       "\t-x, --loop		Run in a loop\n"
+	       "\t-f, --fork		Fork before running a test\n"
+	       "\t-h, --help		Print this help\n"
+	       "\t-t, --test <test-id>	Run one specific test only\n"
+	       "\t-w, --wait <secs>	Wait <secs> before actually starting test\n"
+	       "\n", argv0);
+
+	printf("By default, all test are run once, and a summary is printed.\n"
+	       "Available tests for --test:\n\n");
+
+	for (i = 0; i < N_TESTS; i++) {
+		const struct kmsg_test *t = tests + i;
+
+		printf("\t%s", t->name);
+
+		for (j = 0; j < 60 - strlen(t->name); j++)
+			printf(" ");
+
+		printf("Test %s\n", t->desc);
+	}
+
+	printf("\n");
+	printf("Note that some tests may, if run specifically by --test, ");
+	printf("behave differently, and not terminate by themselves.\n");
+}
+
+static void print_test_result(int ret)
+{
+	switch (ret) {
+	case KSFT_PASS:
+		printf("OK");
+		break;
+	case KSFT_SKIP:
+		printf("SKIPPED");
+		break;
+	case KSFT_FAIL:
+		printf("ERROR");
+		break;
+	}
+}
+
+static int test_run(const struct kmsg_test *t,
+		    const struct kmsg_test_args *kmsg_args,
+		    int wait)
+{
+	int ret;
+
+	if (wait > 0) {
+		printf("Sleeping %d seconds before running test ...\n", wait);
+		sleep(wait);
+	}
+
+	ret = t->func(kmsg_args);
+	return ret;
+}
+
+static int test_run_forked(const struct kmsg_test *t,
+			   const struct kmsg_test_args *kmsg_args,
+			   int wait)
+{
+	int ret;
+	pid_t pid;
+
+	pid = fork();
+	if (pid < 0) {
+		return KSFT_FAIL;
+	} else if (pid == 0) {
+		ret = test_run(t, kmsg_args, wait);
+		_exit(ret);
+	}
+
+	pid = waitpid(pid, &ret, 0);
+	if (pid <= 0)
+		return KSFT_FAIL;
+	else if (!WIFEXITED(ret))
+		return KSFT_FAIL;
+	else
+		return WEXITSTATUS(ret);
+}
+
+static int start_all_tests(const struct kmsg_test_args *kmsg_args)
+{
+	int retval;
+	int ret = KSFT_PASS;
+	unsigned int i, n;
+	const struct kmsg_test *t;
+
+	for (i = 0; i < N_TESTS; i++) {
+		t = tests + i;
+
+		printf("Testing %s (%s) ", t->desc, t->name);
+		for (n = 0; n < 60 - strlen(t->desc) - strlen(t->name); n++)
+			printf(".");
+		printf(" ");
+
+		retval = test_run_forked(t, kmsg_args, 0);
+		switch (retval) {
+		case KSFT_PASS:
+			ksft_inc_pass_cnt();
+			break;
+		case KSFT_SKIP:
+			ksft_inc_xskip_cnt();
+			break;
+		case KSFT_FAIL:
+		default:
+			ret = KSFT_FAIL;
+			ksft_inc_fail_cnt();
+			break;
+		}
+
+		print_test_result(retval);
+		printf("\n");
+	}
+
+	return ret;
+}
+
+static int start_one_test(const struct kmsg_test_args *kmsg_args)
+{
+	int i, ret = KSFT_PASS;
+	bool test_found = false;
+	const struct kmsg_test *t;
+
+	for (i = 0; i < N_TESTS; i++) {
+		t = tests + i;
+
+		if (strcmp(t->name, kmsg_args->test))
+			continue;
+
+		do {
+			test_found = true;
+			if (kmsg_args->fork)
+				ret = test_run_forked(t, kmsg_args,
+						      kmsg_args->wait);
+			else
+				ret = test_run(t, kmsg_args,
+					       kmsg_args->wait);
+
+			printf("Testing %s: ", t->desc);
+			print_test_result(ret);
+			printf("\n");
+
+			if ((ret != KSFT_PASS) && (ret != KSFT_SKIP))
+				break;
+		} while (kmsg_args->loop);
+
+		return ret;
+	}
+
+	if (!test_found) {
+		printf("Unknown test-id '%s'\n", kmsg_args->test);
+		return KSFT_FAIL;
+	}
+
+	return ret;
+}
+
+static int start_tests(const struct kmsg_test_args *kmsg_args)
+{
+	int retval;
+	int ret = KSFT_PASS;
+
+	if (kmsg_args->test) {
+		retval = start_one_test(kmsg_args);
+		switch (retval) {
+		case KSFT_PASS:
+			ksft_inc_pass_cnt();
+			break;
+		case KSFT_SKIP:
+			ksft_inc_xskip_cnt();
+			break;
+		case KSFT_FAIL:
+		default:
+			ret = KSFT_FAIL;
+			ksft_inc_fail_cnt();
+			break;
+		}
+	} else  {
+		do {
+			ret = start_all_tests(kmsg_args);
+			if ((ret != KSFT_PASS) && (ret != KSFT_SKIP))
+				break;
+		} while (kmsg_args->loop);
+	}
+
+	return ret;
+}
+
+int main(int argc, char *argv[])
+{
+	int t, ret = 0;
+	struct kmsg_test_args *kmsg_args;
+	char *exec = basename(argv[0]);
+
+	kmsg_args = malloc(sizeof(*kmsg_args));
+	if (!kmsg_args) {
+		printf("unable to malloc() kmsg_args\n");
+		return ksft_exit_fail();
+	}
+
+	memset(kmsg_args, 0, sizeof(*kmsg_args));
+
+	static const struct option options[] = {
+		{ "loop",	no_argument,		NULL, 'x' },
+		{ "help",	no_argument,		NULL, 'h' },
+		{ "test",	required_argument,	NULL, 't' },
+		{ "wait",	required_argument,	NULL, 'w' },
+		{ "fork",	no_argument,		NULL, 'f' },
+		{}
+	};
+
+	if (strcmp(exec, "kmsg-test") != 0)
+		kmsg_args->test = exec;
+
+	while ((t = getopt_long(argc, argv, "hxfm:r:t:b:w:a",
+						options, NULL)) >= 0) {
+		switch (t) {
+		case 'x':
+			kmsg_args->loop = 1;
+			break;
+
+		case 't':
+			kmsg_args->test = optarg;
+			break;
+
+		case 'w':
+			kmsg_args->wait = strtol(optarg, NULL, 10);
+			break;
+
+		case 'f':
+			kmsg_args->fork = 1;
+			break;
+
+		default:
+		case 'h':
+			usage(argv[0]);
+			return ksft_exit_fail();
+		}
+	}
+
+	ret = start_tests(kmsg_args);
+
+	free(kmsg_args);
+
+	ksft_print_cnts();
+
+	if ((ret != KSFT_PASS) && (ret != KSFT_SKIP))
+		return ksft_exit_fail();
+
+	return ksft_exit_pass();
+}
diff --git a/tools/testing/selftests/kmsg/kmsg-test.h b/tools/testing/selftests/kmsg/kmsg-test.h
new file mode 100644
index 0000000..d9f770c
--- /dev/null
+++ b/tools/testing/selftests/kmsg/kmsg-test.h
@@ -0,0 +1,28 @@
+#ifndef _KMSG_TEST_H_
+#define _KMSG_TEST_H_
+
+#include <stdio.h>
+
+#define DEV_KMSG "/dev/kmsg"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+
+#define KMSG_REQUESTED_BUF_SIZE (1024 * 256)
+
+struct kmsg_test_args {
+	int loop;
+	int wait;
+	int fork;
+	const char *test;
+};
+
+FILE *kmsg_get_device(int minor, const char *mode);
+int kmsg_drop_device(int minor);
+
+int kmsg_test_buffer_add_del(const struct kmsg_test_args *args);
+int kmsg_test_buffer_add_write_read_del(const struct kmsg_test_args *args);
+int kmsg_test_buffer_buf_torture(const struct kmsg_test_args *args);
+int kmsg_test_buffer_buf_multithreaded_torture(
+					const struct kmsg_test_args *args);
+
+#endif /* _KMSG_TEST_H_ */
diff --git a/tools/testing/selftests/kmsg/test-buffer-add-del.c b/tools/testing/selftests/kmsg/test-buffer-add-del.c
new file mode 100644
index 0000000..4acef53
--- /dev/null
+++ b/tools/testing/selftests/kmsg/test-buffer-add-del.c
@@ -0,0 +1,78 @@
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <kmsg-api.h>
+
+#include "../kselftest.h"
+
+#include "kmsg-test.h"
+
+int kmsg_test_buffer_add_del(const struct kmsg_test_args *args)
+{
+	int i;
+	int fd = open(DEV_KMSG, O_RDWR);
+	struct kmsg_cmd_buffer_add cmd = { 0 };
+	int minors[] = { -1, -1, -1, -1 };
+	FILE *fds[ARRAY_SIZE(minors)];
+	int retval = KSFT_PASS;
+	uint32_t size;
+
+	if (fd < 0) {
+		printf("Failed: cannot open %s\n", DEV_KMSG);
+		return KSFT_FAIL;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(minors); i++) {
+		fds[i] = NULL;
+		cmd.size = KMSG_REQUESTED_BUF_SIZE;
+		cmd.mode = 0662;
+		if (kmsg_cmd_buffer_add(fd, &cmd)) {
+			printf("Failed to add buffer\n");
+			goto error;
+		}
+		if (cmd.minor < 0) {
+			printf("Minor number < 0\n");
+			goto error;
+		}
+		minors[i] = cmd.minor;
+		fds[i] = kmsg_get_device(minors[i], "r");
+		if (!fds[i]) {
+			printf("Cannot get device %d\n", i);
+			goto error;
+		}
+		size = 0;
+		if (kmsg_cmd_get_buf_size(fileno(fds[i]), &size)) {
+			printf("Cannot get buf size on defice %d\n", i);
+			goto error;
+		}
+		if (size != KMSG_REQUESTED_BUF_SIZE) {
+			printf("Invalid buf size on device %d\n", i);
+			goto error;
+		}
+	}
+
+	goto cleanup;
+
+error:
+	retval = KSFT_FAIL;
+
+cleanup:
+	for (i = 0; i < ARRAY_SIZE(minors); i++) {
+		if (minors[i] < 0)
+			continue;
+		if (fds[i])
+			fclose(fds[i]);
+		if (kmsg_drop_device(minors[i])) {
+			printf("Failed to delete device file %d\n", i);
+			retval = KSFT_FAIL;
+		}
+		if (kmsg_cmd_buffer_del(fd, &minors[i])) {
+			printf("Failed to delete buffer %d\n", i);
+			retval = KSFT_FAIL;
+		}
+	}
+	close(fd);
+	return retval;
+}
diff --git a/tools/testing/selftests/kmsg/test-buffer-add-write-read-del.c b/tools/testing/selftests/kmsg/test-buffer-add-write-read-del.c
new file mode 100644
index 0000000..2f21bce
--- /dev/null
+++ b/tools/testing/selftests/kmsg/test-buffer-add-write-read-del.c
@@ -0,0 +1,163 @@
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <kmsg-api.h>
+
+#include "../kselftest.h"
+
+#include "kmsg-test.h"
+
+static const char *message(char *buff, size_t size, int i, int j)
+{
+	snprintf(buff, size, "Test message (%d, %d)", i, j);
+	return buff;
+}
+
+int kmsg_test_buffer_add_write_read_del(const struct kmsg_test_args *args)
+{
+	int i, j;
+	int fd = open(DEV_KMSG, O_RDWR);
+	struct kmsg_cmd_buffer_add cmd = { 0 };
+	int minors[] = { -1, -1, -1, -1 };
+	FILE *fds[ARRAY_SIZE(minors)];
+	FILE *log[ARRAY_SIZE(minors)];
+	int logfd;
+	int retval = KSFT_PASS;
+	uint32_t size;
+	char txt[80] = "";
+	char *buff = NULL;
+	const char *msg;
+	char *msgend;
+
+	if (fd < 0) {
+		printf("Failed: cannot open %s\n", DEV_KMSG);
+		return KSFT_FAIL;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(minors); i++) {
+		fds[i] = NULL;
+		log[i] = NULL;
+		cmd.size = KMSG_REQUESTED_BUF_SIZE;
+		cmd.mode = 0662;
+		if (kmsg_cmd_buffer_add(fd, &cmd)) {
+			printf("Failed to add buffer\n");
+			goto error;
+		}
+		if (cmd.minor < 0) {
+			printf("Minor number < 0\n");
+			goto error;
+		}
+		minors[i] = cmd.minor;
+
+		fds[i] = kmsg_get_device(minors[i], "w");
+		if (!fds[i]) {
+			printf("Cannot get device %d for write\n", i);
+			goto error;
+		}
+		size = 0;
+		if (kmsg_cmd_get_buf_size(fileno(fds[i]), &size)) {
+			printf("Cannot get buf size on defice %d\n", i);
+			goto error;
+		}
+		if (size != KMSG_REQUESTED_BUF_SIZE) {
+			printf("Invalid buf size on device %d\n", i);
+			goto error;
+		}
+		log[i] = kmsg_get_device(minors[i], "r");
+		if (!log[i]) {
+			printf("Cannot get device %d for read\n", i);
+			goto error;
+		}
+		size = 0;
+		if (kmsg_cmd_get_buf_size(fileno(log[i]), &size)) {
+			printf("Cannot get buf size on defice %d\n", i);
+			goto error;
+		}
+		if (size != KMSG_REQUESTED_BUF_SIZE) {
+			printf("Invalid buf size on device %d\n", i);
+			goto error;
+		}
+
+		for (j = 0; j <= i; j++) {
+			if (kmsg_cmd_clear(fileno(fds[j]))) {
+				printf("Cannot clear buffer on device %d\n", j);
+				goto error;
+			}
+			fprintf(fds[j], "%s\n", message(txt, ARRAY_SIZE(txt),
+									i, j));
+			fflush(fds[j]);
+		}
+
+		for (j = 0; j <= i; j++) {
+			logfd = fileno(log[j]);
+			size = 0;
+			if (kmsg_cmd_get_read_size_max(logfd, &size)) {
+				printf("Cannot get buf size on device %d\n", j);
+				goto error;
+			}
+			if (!size) {
+				printf("Expected non-zero buf size on %d\n", j);
+				goto error;
+			}
+			buff = malloc(size);
+			if (!buff) {
+				printf("Out of memory\n");
+				goto error;
+			}
+			if (read(logfd, buff, size) <= 0) {
+				printf("Could not read from buffer %d\n", j);
+				goto error;
+			}
+			msg = strchr(buff, ';');
+			msgend = strchr(buff, '\n');
+			if ((!msg) || (!msgend)) {
+				printf("Could not read stored log on %d\n", j);
+				goto error;
+			}
+			msg++;
+			*msgend = 0;
+			if (strcmp(msg, message(txt, ARRAY_SIZE(txt), i, j))) {
+				printf("Messages do not match on %d\n", j);
+				goto error;
+			}
+			free(buff);
+			buff = NULL;
+		}
+	}
+
+	goto cleanup;
+
+error:
+	retval = KSFT_FAIL;
+
+cleanup:
+	for (i = 0; i < ARRAY_SIZE(minors); i++) {
+		if (minors[i] < 0)
+			continue;
+		if (fds[i])
+			fclose(fds[i]);
+		if (log[i]) {
+			if (kmsg_cmd_clear(fileno(log[i]))) {
+				printf("Failed to clear device %d\n", i);
+				retval = KSFT_FAIL;
+			}
+			fclose(log[i]);
+		}
+		if (kmsg_drop_device(minors[i])) {
+			printf("Failed to delete device file %d\n", i);
+			retval = KSFT_FAIL;
+		}
+		if (kmsg_cmd_buffer_del(fd, &minors[i])) {
+			printf("Failed to delete buffer %d\n", i);
+			retval = KSFT_FAIL;
+		}
+	}
+	close(fd);
+	if (buff)
+		free(buff);
+	return retval;
+}
diff --git a/tools/testing/selftests/kmsg/test-buffer-buf-multithreaded-torture.c b/tools/testing/selftests/kmsg/test-buffer-buf-multithreaded-torture.c
new file mode 100644
index 0000000..512a05e
--- /dev/null
+++ b/tools/testing/selftests/kmsg/test-buffer-buf-multithreaded-torture.c
@@ -0,0 +1,201 @@
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <kmsg-api.h>
+
+#include "../kselftest.h"
+
+#include "kmsg-test.h"
+
+#define SOME_BUFF_SIZE 4096
+#define THREADS_PER_DEVICE 10
+
+static bool ok = true;
+static bool nok = !true;
+
+static void *kmsg_test_thread_func(void *data)
+{
+	char buff[SOME_BUFF_SIZE];
+	int minor = *((int *)data);
+	FILE *f = kmsg_get_device(minor, "w");
+	int fd;
+	void *retval = &ok;
+	int iter;
+	ssize_t s;
+	uint32_t size, done;
+	uint32_t max_size;
+
+	memset(buff, 'A', ARRAY_SIZE(buff));
+	buff[ARRAY_SIZE(buff) - 1] = 0;
+
+	if (!f) {
+		printf("Cannot get device for write\n");
+		return &nok;
+	}
+	fd = fileno(f);
+
+	size = 0;
+	if (kmsg_cmd_get_buf_size(fd, &size)) {
+		printf("Cannot get buf size\n");
+		goto error;
+	}
+	if (size != KMSG_REQUESTED_BUF_SIZE) {
+		printf("Invalid buf size\n");
+		goto error;
+	}
+
+	if (kmsg_cmd_clear(fd)) {
+		printf("Cannot clear buffer\n");
+		goto error;
+	}
+
+	iter = 0;
+	while (done < (KMSG_REQUESTED_BUF_SIZE * 2)) {
+		s = write(fd, buff, ARRAY_SIZE(buff));
+		if (s < 0) {
+			printf("Cannot write iteration %d\n", iter);
+			goto error;
+		}
+		done += s;
+
+		max_size = 0;
+		if (kmsg_cmd_get_read_size_max(fd, &max_size)) {
+			printf("Cannot get max_size\n");
+			goto error;
+		}
+		if (!max_size) {
+			printf("Expected non-zero max_size\n");
+			goto error;
+		}
+
+		iter++;
+	}
+
+	goto cleanup;
+
+error:
+	retval = &nok;
+
+cleanup:
+	fclose(f);
+
+	return retval;
+}
+
+int kmsg_test_buffer_buf_multithreaded_torture(
+					const struct kmsg_test_args *args)
+{
+	int i, j;
+	int fd = open(DEV_KMSG, O_RDWR);
+	struct kmsg_cmd_buffer_add cmd = { 0 };
+	int minors[] = { -1, -1, -1, -1 };
+	FILE *log[ARRAY_SIZE(minors)];
+	int retval = KSFT_PASS;
+	pthread_t threads[ARRAY_SIZE(minors)][THREADS_PER_DEVICE];
+	bool started[ARRAY_SIZE(minors)][THREADS_PER_DEVICE];
+	uint32_t size;
+	uint32_t max_size;
+	void *retptr;
+
+	for (i = 0; i < ARRAY_SIZE(minors); i++)
+		for (j = 0; j < THREADS_PER_DEVICE; j++)
+			started[i][j] = false;
+
+	if (fd < 0) {
+		printf("Failed: cannot open %s\n", DEV_KMSG);
+		return KSFT_FAIL;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(minors); i++) {
+		log[i] = NULL;
+		cmd.size = KMSG_REQUESTED_BUF_SIZE;
+		cmd.mode = 0662;
+		if (kmsg_cmd_buffer_add(fd, &cmd)) {
+			printf("Failed to add buffer\n");
+			goto error;
+		}
+		if (cmd.minor < 0) {
+			printf("Minor number < 0\n");
+			goto error;
+		}
+		minors[i] = cmd.minor;
+
+		log[i] = kmsg_get_device(minors[i], "r");
+		if (!log[i]) {
+			printf("Cannot get device %d for read\n", i);
+			goto error;
+		}
+		size = 0;
+		if (kmsg_cmd_get_buf_size(fileno(log[i]), &size)) {
+			printf("Cannot get buf size on defice %d\n", i);
+			goto error;
+		}
+		if (size != KMSG_REQUESTED_BUF_SIZE) {
+			printf("Invalid buf size on device %d\n", i);
+			goto error;
+		}
+
+		for (j = 0; j < THREADS_PER_DEVICE; j++) {
+			if (pthread_create(&threads[i][j], NULL,
+					  kmsg_test_thread_func, &minors[i])) {
+				printf("Cannot create thread %d for dev %d\n",
+									j, i);
+				goto error;
+			}
+			started[i][j] = true;
+		}
+	}
+
+	goto cleanup;
+
+error:
+	retval = KSFT_FAIL;
+
+cleanup:
+	for (i = 0; i < ARRAY_SIZE(minors); i++) {
+		for (j = 0; j < THREADS_PER_DEVICE; j++)
+			if (started[i][j]) {
+				if (pthread_join(threads[i][j], &retptr)) {
+					printf("pthread_join() failed %d:%d\n",
+									i, j);
+					retval = KSFT_FAIL;
+				}
+				if (!(*((bool *)retptr)))
+					retval = KSFT_FAIL;
+			}
+		if (minors[i] < 0)
+			continue;
+		if (log[i]) {
+			max_size = 0;
+			if (kmsg_cmd_get_read_size_max(fileno(log[i]),
+								&max_size)) {
+				printf("Cannot get max_size\n");
+				retval = KSFT_FAIL;
+			}
+			if (!max_size) {
+				printf("Expected non-zero max_size\n");
+				retval = KSFT_FAIL;
+			}
+			if (kmsg_cmd_clear(fileno(log[i]))) {
+				printf("Failed to clear device %d\n", i);
+				retval = KSFT_FAIL;
+			}
+			fclose(log[i]);
+		}
+		if (kmsg_drop_device(minors[i])) {
+			printf("Failed to delete device file %d\n", i);
+			retval = KSFT_FAIL;
+		}
+		if (kmsg_cmd_buffer_del(fd, &minors[i])) {
+			printf("Failed to delete buffer %d\n", i);
+			retval = KSFT_FAIL;
+		}
+	}
+	close(fd);
+	return retval;
+}
diff --git a/tools/testing/selftests/kmsg/test-buffer-buf-torture.c b/tools/testing/selftests/kmsg/test-buffer-buf-torture.c
new file mode 100644
index 0000000..829b342
--- /dev/null
+++ b/tools/testing/selftests/kmsg/test-buffer-buf-torture.c
@@ -0,0 +1,141 @@
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <kmsg-api.h>
+
+#include "../kselftest.h"
+
+#include "kmsg-test.h"
+
+#define SOME_BUFF_SIZE 4096
+
+int kmsg_test_buffer_buf_torture(const struct kmsg_test_args *args)
+{
+	int i, iter;
+	int fd = open(DEV_KMSG, O_RDWR);
+	struct kmsg_cmd_buffer_add cmd = { 0 };
+	int minors[] = { -1, -1, -1, -1 };
+	FILE *fds[ARRAY_SIZE(minors)];
+	FILE *log[ARRAY_SIZE(minors)];
+	int retval = KSFT_PASS;
+	char buff[SOME_BUFF_SIZE];
+	ssize_t s;
+	int logfd;
+	uint32_t size, done;
+	uint32_t max_size;
+
+	memset(buff, 'A', ARRAY_SIZE(buff));
+	buff[ARRAY_SIZE(buff) - 1] = 0;
+
+	if (fd < 0) {
+		printf("Failed: cannot open %s\n", DEV_KMSG);
+		return KSFT_FAIL;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(minors); i++) {
+		fds[i] = NULL;
+		log[i] = NULL;
+		cmd.size = KMSG_REQUESTED_BUF_SIZE;
+		cmd.mode = 0662;
+		if (kmsg_cmd_buffer_add(fd, &cmd)) {
+			printf("Failed to add buffer\n");
+			goto error;
+		}
+		if (cmd.minor < 0) {
+			printf("Minor number < 0\n");
+			goto error;
+		}
+		minors[i] = cmd.minor;
+
+		fds[i] = kmsg_get_device(minors[i], "w");
+		if (!fds[i]) {
+			printf("Cannot get device %d for write\n", i);
+			goto error;
+		}
+		size = 0;
+		if (kmsg_cmd_get_buf_size(fileno(fds[i]), &size)) {
+			printf("Cannot get buf size on defice %d\n", i);
+			goto error;
+		}
+		if (size != KMSG_REQUESTED_BUF_SIZE) {
+			printf("Invalid buf size on device %d\n", i);
+			goto error;
+		}
+		log[i] = kmsg_get_device(minors[i], "r");
+		if (!log[i]) {
+			printf("Cannot get device %d for read\n", i);
+			goto error;
+		}
+		size = 0;
+		if (kmsg_cmd_get_buf_size(fileno(log[i]), &size)) {
+			printf("Cannot get buf size on defice %d\n", i);
+			goto error;
+		}
+		if (size != KMSG_REQUESTED_BUF_SIZE) {
+			printf("Invalid buf size on device %d\n", i);
+			goto error;
+		}
+
+		logfd = fileno(fds[i]);
+		if (kmsg_cmd_clear(logfd)) {
+			printf("Cannot clear buffer on device %d\n", i);
+			goto error;
+		}
+
+		iter = 0;
+		while (done < (KMSG_REQUESTED_BUF_SIZE * 2)) {
+			s = write(logfd, buff, ARRAY_SIZE(buff));
+			if (s < 0) {
+				printf("Cannot write %d to device %d, %s\n",
+						    iter, i, strerror(errno));
+				goto error;
+			}
+			done += s;
+
+			max_size = 0;
+			if (kmsg_cmd_get_read_size_max(logfd, &max_size)) {
+				printf("Cannot get max_size on device %d\n", i);
+				goto error;
+			}
+			if (!max_size) {
+				printf("Expected non-zero max_size on %d\n", i);
+				goto error;
+			}
+
+			iter++;
+		}
+	}
+
+	goto cleanup;
+
+error:
+	retval = KSFT_FAIL;
+
+cleanup:
+	for (i = 0; i < ARRAY_SIZE(minors); i++) {
+		if (minors[i] < 0)
+			continue;
+		if (fds[i])
+			fclose(fds[i]);
+		if (log[i]) {
+			if (kmsg_cmd_clear(fileno(log[i]))) {
+				printf("Failed to clear device %d\n", i);
+				retval = KSFT_FAIL;
+			}
+			fclose(log[i]);
+		}
+		if (kmsg_drop_device(minors[i])) {
+			printf("Failed to delete device file %d\n", i);
+			retval = KSFT_FAIL;
+		}
+		if (kmsg_cmd_buffer_del(fd, &minors[i])) {
+			printf("Failed to delete buffer %d\n", i);
+			retval = KSFT_FAIL;
+		}
+	}
+	close(fd);
+	return retval;
+}
-- 
1.9.1

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

[toc] | [prev] | [next] | [standalone]


#1256664 — [RFC v5 6/8] kmsg: add ioctl for adding and deleting kmsg* devices

FromPaul Osmialowski <p.osmialowsk@samsung.com>
Date2015-10-27 11:30 +0100
Subject[RFC v5 6/8] kmsg: add ioctl for adding and deleting kmsg* devices
Message-ID<qo9FV-422-45@gated-at.bofh.it>
In reply to#1256658
From: Marcin Niesluchowski <m.niesluchow@samsung.com>

There is no possibility to add/delete kmsg* buffers from userspace.

Adds following ioctl for main kmsg device adding and deleting
additional kmsg devices:
* KMSG_CMD_BUFFER_ADD
* KMSG_CMD_BUFFER_DEL

Signed-off-by: Marcin Niesluchowski <m.niesluchow@samsung.com>
Signed-off-by: Paul Osmialowski <p.osmialowsk@samsung.com>
---
 Documentation/ioctl/ioctl-number.txt |   1 +
 drivers/char/mem.c                   |   2 +-
 include/linux/printk.h               |   7 ++
 include/uapi/linux/Kbuild            |   1 +
 include/uapi/linux/kmsg_ioctl.h      |  30 +++++++++
 kernel/printk/kmsg.c                 | 122 +++++++++++++++++++++++++++++++++++
 6 files changed, 162 insertions(+), 1 deletion(-)
 create mode 100644 include/uapi/linux/kmsg_ioctl.h

diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 43e6923..76dec8b 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -319,6 +319,7 @@ Code  Seq#(hex)	Include File		Comments
 					<mailto:vgo@ratio.de>
 0xB1	00-1F	PPPoX			<mailto:mostrows@styx.uwaterloo.ca>
 0xB3	00	linux/mmc/ioctl.h
+0xBB	00-02	uapi/linux/kmsg_ioctl.h
 0xC0	00-0F	linux/usb/iowarrior.h
 0xCA	00-0F	uapi/misc/cxl.h
 0xCA	80-8F	uapi/scsi/cxlflash_ioctl.h
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 7d46234..ac824de 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -808,7 +808,7 @@ static int memory_open(struct inode *inode, struct file *filp)
 
 	minor = iminor(inode);
 	if (minor >= ARRAY_SIZE(devlist))
-		return kmsg_memory_open(inode, filp);
+		return kmsg_memory_open_ext(inode, filp);
 
 	dev = &devlist[minor];
 	if (!dev->fops)
diff --git a/include/linux/printk.h b/include/linux/printk.h
index 513fa6f..ebacfa6 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -427,9 +427,11 @@ struct inode;
 extern struct class *mem_class;
 
 extern const struct file_operations kmsg_fops;
+extern const struct file_operations kmsg_fops_ext;
 
 extern struct device *init_kmsg(int minor, umode_t mode);
 extern int kmsg_memory_open(struct inode *inode, struct file *filp);
+extern int kmsg_memory_open_ext(struct inode *inode, struct file *filp);
 extern int kmsg_mode(int minor, umode_t *mode);
 extern int kmsg_sys_buffer_add(size_t size, umode_t mode);
 extern void kmsg_sys_buffer_del(int minor);
@@ -446,6 +448,11 @@ static inline int kmsg_memory_open(struct inode *inode, struct file *filp)
 	return -ENXIO;
 }
 
+static inline int kmsg_memory_open_ext(struct inode *inode, struct file *filp)
+{
+	return -ENXIO;
+}
+
 static inline int kmsg_mode(int minor, umode_t *mode)
 {
 	return -ENXIO;
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index e777078..d998999 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -225,6 +225,7 @@ header-y += kernel-page-flags.h
 header-y += kexec.h
 header-y += keyboard.h
 header-y += keyctl.h
+header-y += kmsg_ioctl.h
 
 ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/include/uapi/asm/kvm.h \
 		  $(srctree)/arch/$(SRCARCH)/include/asm/kvm.h),)
diff --git a/include/uapi/linux/kmsg_ioctl.h b/include/uapi/linux/kmsg_ioctl.h
new file mode 100644
index 0000000..96e7930
--- /dev/null
+++ b/include/uapi/linux/kmsg_ioctl.h
@@ -0,0 +1,30 @@
+/*
+ * This is ioctl include for kmsg* devices
+ */
+
+#ifndef _KMSG_IOCTL_H_
+#define _KMSG_IOCTL_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+struct kmsg_cmd_buffer_add {
+	__u64 size;
+	__u32 mode;
+	__u32 minor;
+};
+
+#define KMSG_IOCTL_MAGIC	0xBB
+
+/*
+ * A ioctl interface for kmsg device.
+ *
+ * KMSG_CMD_BUFFER_ADD:	Creates additional kmsg device based on its size
+ *			and mode. Minor of created device is put.
+ * KMSG_CMD_BUFFER_DEL:	Removes additional kmsg device based on its minor
+ */
+#define KMSG_CMD_BUFFER_ADD		_IOWR(KMSG_IOCTL_MAGIC, 0x00, \
+					      struct kmsg_cmd_buffer_add)
+#define KMSG_CMD_BUFFER_DEL		_IOW(KMSG_IOCTL_MAGIC, 0x01, int)
+
+#endif
diff --git a/kernel/printk/kmsg.c b/kernel/printk/kmsg.c
index 9222fdc..62bb4d5 100644
--- a/kernel/printk/kmsg.c
+++ b/kernel/printk/kmsg.c
@@ -23,8 +23,12 @@
 
 #include <asm/uaccess.h>
 
+#include <uapi/linux/kmsg_ioctl.h>
+
 #include "printk.h"
 
+#define KMSG_MAX_MINOR_LEN	20
+
 /* /dev/kmsg - userspace message inject/listen interface */
 struct devkmsg_user {
 	u64 seq;
@@ -408,6 +412,117 @@ const struct file_operations kmsg_fops = {
 	.release = devkmsg_release,
 };
 
+static int kmsg_open_ext(struct inode *inode, struct file *file)
+{
+	return kmsg_fops.open(inode, file);
+}
+
+static ssize_t kmsg_write_iter_ext(struct kiocb *iocb, struct iov_iter *from)
+{
+	return kmsg_fops.write_iter(iocb, from);
+}
+
+static ssize_t kmsg_read_ext(struct file *file, char __user *buf,
+			     size_t count, loff_t *ppos)
+{
+	return kmsg_fops.read(file, buf, count, ppos);
+}
+
+static loff_t kmsg_llseek_ext(struct file *file, loff_t offset, int whence)
+{
+	return kmsg_fops.llseek(file, offset, whence);
+}
+
+static unsigned int kmsg_poll_ext(struct file *file,
+				  struct poll_table_struct *wait)
+{
+	return kmsg_fops.poll(file, wait);
+}
+
+static long kmsg_ioctl_buffers(struct file *file, unsigned int cmd,
+			       unsigned long arg)
+{
+	void __user *argp = (void __user *)arg;
+	struct kmsg_cmd_buffer_add cmd_buffer_add;
+	char name[4 + KMSG_MAX_MINOR_LEN + 1];
+	struct device *dev;
+	int minor;
+
+	if (iminor(file->f_inode) != log_buf.minor)
+		return -ENOTTY;
+
+	switch (cmd) {
+	case KMSG_CMD_BUFFER_ADD:
+		if (copy_from_user(&cmd_buffer_add, argp,
+					sizeof(struct kmsg_cmd_buffer_add)))
+			return -EFAULT;
+		minor = kmsg_sys_buffer_add(cmd_buffer_add.size,
+						cmd_buffer_add.mode);
+		if (minor < 0)
+			return minor;
+		sprintf(name, "kmsg%d", minor);
+		dev = device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor),
+				    NULL, name);
+		if (IS_ERR(dev)) {
+			kmsg_sys_buffer_del(minor);
+			return PTR_ERR(dev);
+		}
+		cmd_buffer_add.minor = minor;
+		if (copy_to_user(argp, &cmd_buffer_add,
+					sizeof(struct kmsg_cmd_buffer_add))) {
+			device_destroy(mem_class, MKDEV(MEM_MAJOR, minor));
+			kmsg_sys_buffer_del(minor);
+			return -EFAULT;
+		}
+		return 0;
+	case KMSG_CMD_BUFFER_DEL:
+		if (copy_from_user(&minor, argp, sizeof(minor)))
+			return -EFAULT;
+		if (minor <= log_buf.minor)
+			return -EINVAL;
+		device_destroy(mem_class, MKDEV(MEM_MAJOR, minor));
+		kmsg_sys_buffer_del(minor);
+		return 0;
+	}
+	return -ENOTTY;
+}
+
+static long kmsg_unlocked_ioctl_ext(struct file *file, unsigned int cmd,
+				    unsigned long arg)
+{
+	long ret = kmsg_ioctl_buffers(file, cmd, arg);
+
+	if (ret == -ENOTTY)
+		return kmsg_fops.unlocked_ioctl(file, cmd, arg);
+	return ret;
+}
+
+static long kmsg_compat_ioctl_ext(struct file *file, unsigned int cmd,
+				  unsigned long arg)
+{
+	long ret = kmsg_ioctl_buffers(file, cmd, arg);
+
+	if (ret == -ENOTTY)
+		return kmsg_fops.compat_ioctl(file, cmd, arg);
+	return ret;
+}
+
+static int kmsg_release_ext(struct inode *inode, struct file *file)
+{
+	return kmsg_fops.release(inode, file);
+}
+
+const struct file_operations kmsg_fops_ext = {
+	.open		= kmsg_open_ext,
+	.read		= kmsg_read_ext,
+	.write_iter	= kmsg_write_iter_ext,
+	.llseek		= kmsg_llseek_ext,
+	.poll		= kmsg_poll_ext,
+	.unlocked_ioctl	= kmsg_unlocked_ioctl_ext,
+	.compat_ioctl	= kmsg_compat_ioctl_ext,
+	.release	= kmsg_release_ext,
+};
+
 /* Should be used for device registration */
 struct device *init_kmsg(int minor, umode_t mode)
 {
@@ -424,6 +539,13 @@ int kmsg_memory_open(struct inode *inode, struct file *filp)
 	return kmsg_fops.open(inode, filp);
 }
 
+int kmsg_memory_open_ext(struct inode *inode, struct file *filp)
+{
+	filp->f_op = &kmsg_fops_ext;
+
+	return kmsg_fops_ext.open(inode, filp);
+}
+
 int kmsg_mode(int minor, umode_t *mode)
 {
 	int ret = -ENXIO;
-- 
1.9.1

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

[toc] | [prev] | [next] | [standalone]


#1256665 — [RFC v5 7/8] kmsg: add ioctl for kmsg* devices operating on buffers

FromPaul Osmialowski <p.osmialowsk@samsung.com>
Date2015-10-27 11:40 +0100
Subject[RFC v5 7/8] kmsg: add ioctl for kmsg* devices operating on buffers
Message-ID<qo9Pz-455-1@gated-at.bofh.it>
In reply to#1256658
From: Marcin Niesluchowski <m.niesluchow@samsung.com>

There is no possibility to clear additional kmsg buffers,
get size of them or know what size should be passed to read
file operation (too small size causes it to retrun -EINVAL).

Add following ioctls which solve those issues:
* KMSG_CMD_GET_BUF_SIZE
* KMSG_CMD_GET_READ_SIZE_MAX
* KMSG_CMD_CLEAR

Signed-off-by: Marcin Niesluchowski <m.niesluchow@samsung.com>
Signed-off-by: Paul Osmialowski <p.osmialowsk@samsung.com>
---
 Documentation/ioctl/ioctl-number.txt |  2 +-
 include/uapi/linux/kmsg_ioctl.h      | 15 ++++++++++
 kernel/printk/kmsg.c                 | 57 ++++++++++++++++++++++++++++++++++--
 3 files changed, 71 insertions(+), 3 deletions(-)

diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 76dec8b..d36bb04 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -319,7 +319,7 @@ Code  Seq#(hex)	Include File		Comments
 					<mailto:vgo@ratio.de>
 0xB1	00-1F	PPPoX			<mailto:mostrows@styx.uwaterloo.ca>
 0xB3	00	linux/mmc/ioctl.h
-0xBB	00-02	uapi/linux/kmsg_ioctl.h
+0xBB	00-83	uapi/linux/kmsg_ioctl.h
 0xC0	00-0F	linux/usb/iowarrior.h
 0xCA	00-0F	uapi/misc/cxl.h
 0xCA	80-8F	uapi/scsi/cxlflash_ioctl.h
diff --git a/include/uapi/linux/kmsg_ioctl.h b/include/uapi/linux/kmsg_ioctl.h
index 96e7930..bfd9cd3 100644
--- a/include/uapi/linux/kmsg_ioctl.h
+++ b/include/uapi/linux/kmsg_ioctl.h
@@ -27,4 +27,19 @@ struct kmsg_cmd_buffer_add {
 					      struct kmsg_cmd_buffer_add)
 #define KMSG_CMD_BUFFER_DEL		_IOW(KMSG_IOCTL_MAGIC, 0x01, int)
 
+/*
+ * A ioctl interface for kmsg* devices.
+ *
+ * KMSG_CMD_GET_BUF_SIZE:	Retrieve cyclic log buffer size associated with
+ *				device.
+ * KMSG_CMD_GET_READ_SIZE_MAX:	Retrieve max size of data read by kmsg read
+ *				operation.
+ * KMSG_CMD_CLEAR:		Clears cyclic log buffer. After that operation
+ *				there is no data to read from buffer unless
+ *				logs are written.
+ */
+#define KMSG_CMD_GET_BUF_SIZE		_IOR(KMSG_IOCTL_MAGIC, 0x80, __u32)
+#define KMSG_CMD_GET_READ_SIZE_MAX	_IOR(KMSG_IOCTL_MAGIC, 0x81, __u32)
+#define KMSG_CMD_CLEAR			_IO(KMSG_IOCTL_MAGIC, 0x82)
+
 #endif
diff --git a/kernel/printk/kmsg.c b/kernel/printk/kmsg.c
index 62bb4d5..bcf0801 100644
--- a/kernel/printk/kmsg.c
+++ b/kernel/printk/kmsg.c
@@ -247,8 +247,9 @@ static loff_t kmsg_llseek(struct log_buffer *log_b, struct file *file,
 		}
 		/*
 		 * The first record after the last SYSLOG_ACTION_CLEAR,
-		 * like issued by 'dmesg -c'. Reading /dev/kmsg itself
-		 * changes no global state, and does not clear anything.
+		 * like issued by 'dmesg -c' or KMSG_CMD_CLEAR ioctl
+		 * command. Reading /dev/kmsg itself changes no global
+		 * state, and does not clear anything.
 		 */
 		user->idx = log_b->clear_idx;
 		user->seq = log_b->clear_seq;
@@ -391,6 +392,56 @@ static int devkmsg_open(struct inode *inode, struct file *file)
 	return ret;
 }
 
+static long kmsg_ioctl(struct log_buffer *log_b, unsigned int cmd,
+		       unsigned long arg)
+{
+	void __user *argp = (void __user *)arg;
+	static const u32 read_size_max = CONSOLE_EXT_LOG_MAX;
+
+	switch (cmd) {
+	case KMSG_CMD_GET_BUF_SIZE:
+		if (copy_to_user(argp, &log_b->len, sizeof(u32)))
+			return -EFAULT;
+		break;
+	case KMSG_CMD_GET_READ_SIZE_MAX:
+		if (copy_to_user(argp, &read_size_max, sizeof(u32)))
+			return -EFAULT;
+		break;
+	case KMSG_CMD_CLEAR:
+		if (!capable(CAP_SYSLOG))
+			return -EPERM;
+		raw_spin_lock_irq(&log_b->lock);
+		log_b->clear_seq = log_b->next_seq;
+		log_b->clear_idx = log_b->next_idx;
+		raw_spin_unlock_irq(&log_b->lock);
+		break;
+	default:
+		return -ENOTTY;
+	}
+	return 0;
+}
+
+static long devkmsg_ioctl(struct file *file, unsigned int cmd,
+			  unsigned long arg)
+{
+	long ret = -ENXIO;
+	int minor = iminor(file->f_inode);
+	struct log_buffer *log_b;
+
+	if (minor == log_buf.minor)
+		return kmsg_ioctl(&log_buf, cmd, arg);
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(log_b, &log_buf.list, list) {
+		if (log_b->minor == minor) {
+			ret = kmsg_ioctl(log_b, cmd, arg);
+			break;
+		}
+	}
+	rcu_read_unlock();
+	return ret;
+}
+
 static int devkmsg_release(struct inode *inode, struct file *file)
 {
 	struct devkmsg_user *user = file->private_data;
@@ -409,6 +460,8 @@ const struct file_operations kmsg_fops = {
 	.write_iter = devkmsg_write,
 	.llseek = devkmsg_llseek,
 	.poll = devkmsg_poll,
+	.unlocked_ioctl = devkmsg_ioctl,
+	.compat_ioctl = devkmsg_ioctl,
 	.release = devkmsg_release,
 };
 
-- 
1.9.1

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

[toc] | [prev] | [next] | [standalone]


#1256671 — [RFC v5 5/8] kmsg: add function for adding and deleting additional buffers

FromPaul Osmialowski <p.osmialowsk@samsung.com>
Date2015-10-27 11:40 +0100
Subject[RFC v5 5/8] kmsg: add function for adding and deleting additional buffers
Message-ID<qo9PA-455-19@gated-at.bofh.it>
In reply to#1256658
From: Marcin Niesluchowski <m.niesluchow@samsung.com>

Additional kmsg buffers should be created and deleted dynamically.

Adding two functions
* kmsg_sys_buffer_add() creates additional kmsg buffer returning minor
* kmsg_sys_buffer_del() deletes one based on provided minor

Signed-off-by: Marcin Niesluchowski <m.niesluchow@samsung.com>
Signed-off-by: Paul Osmialowski <p.osmialowsk@samsung.com>
---
 include/linux/printk.h |   9 +++++
 kernel/printk/kmsg.c   | 107 +++++++++++++++++++++++++++++++++++++++++++++++--
 kernel/printk/printk.c |  12 ++++++
 kernel/printk/printk.h |   4 ++
 4 files changed, 129 insertions(+), 3 deletions(-)

diff --git a/include/linux/printk.h b/include/linux/printk.h
index 0c4f9de..513fa6f 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -431,6 +431,8 @@ extern const struct file_operations kmsg_fops;
 extern struct device *init_kmsg(int minor, umode_t mode);
 extern int kmsg_memory_open(struct inode *inode, struct file *filp);
 extern int kmsg_mode(int minor, umode_t *mode);
+extern int kmsg_sys_buffer_add(size_t size, umode_t mode);
+extern void kmsg_sys_buffer_del(int minor);
 
 #else
 
@@ -449,6 +451,13 @@ static inline int kmsg_mode(int minor, umode_t *mode)
 	return -ENXIO;
 }
 
+static inline int kmsg_sys_buffer_add(size_t size, umode_t mode)
+{
+	return -ENXIO;
+}
+
+static inline void kmsg_sys_buffer_del(int minor) {}
+
 #endif
 
 enum {
diff --git a/kernel/printk/kmsg.c b/kernel/printk/kmsg.c
index 726250f..9222fdc 100644
--- a/kernel/printk/kmsg.c
+++ b/kernel/printk/kmsg.c
@@ -19,6 +19,7 @@
 #include <linux/device.h>
 #include <linux/major.h>
 #include <linux/kdev_t.h>
+#include <linux/kref.h>
 
 #include <asm/uaccess.h>
 
@@ -141,8 +142,20 @@ static ssize_t kmsg_read(struct log_buffer *log_b, struct file *file,
 		}
 
 		raw_spin_unlock_irq(&log_b->lock);
-		ret = wait_event_interruptible(log_b->wait,
-					       user->seq != log_b->next_seq);
+		if (log_b == &log_buf) {
+			ret = wait_event_interruptible(log_b->wait,
+						user->seq != log_b->next_seq);
+		} else {
+			rcu_read_unlock();
+			kref_get(&log_b->refcount);
+			ret = wait_event_interruptible(log_b->wait,
+						user->seq != log_b->next_seq);
+			if (log_b->minor == -1)
+				ret = -ENXIO;
+			if (kref_put(&log_b->refcount, log_buf_release))
+				ret = -ENXIO;
+			rcu_read_lock();
+		}
 		if (ret)
 			goto out;
 		raw_spin_lock_irq(&log_b->lock);
@@ -311,8 +324,14 @@ static unsigned int devkmsg_poll(struct file *file, poll_table *wait)
 	rcu_read_lock();
 	list_for_each_entry_rcu(log_b, &log_buf.list, list) {
 		if (log_b->minor == minor) {
+			kref_get(&log_b->refcount);
+			rcu_read_unlock();
+
 			ret = kmsg_poll(log_b, file, wait);
-			break;
+
+			if (kref_put(&log_b->refcount, log_buf_release))
+				return POLLERR|POLLNVAL;
+			return ret;
 		}
 	}
 	rcu_read_unlock();
@@ -428,6 +447,88 @@ int kmsg_mode(int minor, umode_t *mode)
 	return ret;
 }
 
+static DEFINE_SPINLOCK(kmsg_sys_list_lock);
+
+int kmsg_sys_buffer_add(size_t size, umode_t mode)
+{
+	unsigned long flags;
+	int minor = log_buf.minor;
+	struct log_buffer *log_b;
+	struct log_buffer *log_b_new;
+
+	if (size < LOG_LINE_MAX + PREFIX_MAX)
+		return -EINVAL;
+
+	log_b_new = kzalloc(sizeof(struct log_buffer), GFP_KERNEL);
+	if (!log_b_new)
+		return -ENOMEM;
+
+	log_b_new->buf = kmalloc(size, GFP_KERNEL);
+	if (!log_b_new->buf) {
+		kfree(log_b_new);
+		return -ENOMEM;
+	}
+
+	log_b_new->len = size;
+	log_b_new->lock = __RAW_SPIN_LOCK_UNLOCKED(log_b_new->lock);
+	init_waitqueue_head(&log_b_new->wait);
+	kref_init(&log_b_new->refcount);
+	log_b_new->mode = mode;
+
+	kref_get(&log_b_new->refcount);
+
+	spin_lock_irqsave(&kmsg_sys_list_lock, flags);
+
+	list_for_each_entry(log_b, &log_buf.list, list) {
+		if (log_b->minor - minor > 1)
+			break;
+
+		minor = log_b->minor;
+	}
+
+	if (!(minor & MINORMASK)) {
+		kref_put(&log_b->refcount, log_buf_release);
+		spin_unlock_irqrestore(&kmsg_sys_list_lock, flags);
+		return -ERANGE;
+	}
+
+	minor += 1;
+	log_b_new->minor = minor;
+
+	list_add_tail_rcu(&log_b_new->list, &log_b->list);
+
+	spin_unlock_irqrestore(&kmsg_sys_list_lock, flags);
+
+	return minor;
+}
+
+void kmsg_sys_buffer_del(int minor)
+{
+	unsigned long flags;
+	struct log_buffer *log_b;
+
+	spin_lock_irqsave(&kmsg_sys_list_lock, flags);
+
+	list_for_each_entry(log_b, &log_buf.list, list) {
+		if (log_b->minor == minor)
+			break;
+	}
+
+	if (log_b == &log_buf) {
+		spin_unlock_irqrestore(&kmsg_sys_list_lock, flags);
+		return;
+	}
+
+	list_del_rcu(&log_b->list);
+
+	spin_unlock_irqrestore(&kmsg_sys_list_lock, flags);
+
+	log_b->minor = -1;
+	wake_up_interruptible(&log_b->wait);
+
+	kref_put(&log_b->refcount, log_buf_release);
+}
+
 static DEFINE_SPINLOCK(dump_list_lock);
 static LIST_HEAD(dump_list);
 
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index fed50da..405fe79 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -41,6 +41,8 @@
 #include <linux/irq_work.h>
 #include <linux/utsname.h>
 #include <linux/ctype.h>
+#include <linux/kref.h>
+#include <linux/slab.h>
 
 #include <asm/uaccess.h>
 
@@ -173,6 +175,7 @@ struct log_buffer log_buf = {
 	.len		= __LOG_BUF_K_LEN,
 	.lock		= __RAW_SPIN_LOCK_UNLOCKED(log_buf.lock),
 	.wait		= __WAIT_QUEUE_HEAD_INITIALIZER(log_buf.wait),
+	.refcount	= { .refcount = { .counter = 0 } },
 	.first_seq	= 0,
 	.first_idx	= 0,
 	.next_seq	= 0,
@@ -218,6 +221,15 @@ u32 log_buf_len_get(void)
 	return log_buf.len;
 }
 
+void log_buf_release(struct kref *ref)
+{
+	struct log_buffer *log_b = container_of(ref, struct log_buffer,
+						refcount);
+
+	kfree(log_b->buf);
+	kfree(log_b);
+}
+
 /*
  * Check whether there is enough free space for the given message.
  *
diff --git a/kernel/printk/printk.h b/kernel/printk/printk.h
index f9e3220..85c733f 100644
--- a/kernel/printk/printk.h
+++ b/kernel/printk/printk.h
@@ -5,6 +5,7 @@
 #include <linux/spinlock_types.h>
 #include <linux/types.h>
 #include <linux/wait.h>
+#include <linux/kref.h>
 
 #ifdef CONFIG_PRINTK
 
@@ -108,6 +109,7 @@ struct log_buffer {
 	char *buf;		/* cyclic log buffer */
 	u32 len;		/* buffer length */
 	wait_queue_head_t wait;	/* wait queue for kmsg buffer */
+	struct kref refcount;	/* refcount for kmsg_sys buffers */
 #endif
 /*
  * The lock protects kmsg buffer, indices, counters. This can be taken within
@@ -136,6 +138,8 @@ struct log_buffer {
 
 extern struct log_buffer log_buf;
 
+void log_buf_release(struct kref *ref);
+
 ssize_t msg_print_ext_header(char *buf, size_t size,
 				    struct printk_log *msg, u64 seq,
 				    enum log_flags prev_flags);
-- 
1.9.1

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

[toc] | [prev] | [next] | [standalone]


#1256672 — [RFC v5 4/8] kmsg: add additional buffers support to memory class

FromPaul Osmialowski <p.osmialowsk@samsung.com>
Date2015-10-27 11:40 +0100
Subject[RFC v5 4/8] kmsg: add additional buffers support to memory class
Message-ID<qo9PA-455-23@gated-at.bofh.it>
In reply to#1256658
From: Marcin Niesluchowski <m.niesluchow@samsung.com>

Memory class does not support additional kmsg buffers.

Add additional kmsg buffers support to:
* devnode() callback of "mem" class
* file operations of major "mem" character device

Signed-off-by: Marcin Niesluchowski <m.niesluchow@samsung.com>
Signed-off-by: Paul Osmialowski <p.osmialowsk@samsung.com>
---
 drivers/char/mem.c     | 27 ++++++++++++++++++++-------
 include/linux/printk.h | 32 ++++++++++++++++++++++++++++++++
 kernel/printk/kmsg.c   | 42 ++++++++++++++++++++++++++++++++++++++++++
 kernel/printk/printk.c |  1 +
 kernel/printk/printk.h |  1 +
 5 files changed, 96 insertions(+), 7 deletions(-)

diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 6b1721f..7d46234 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -799,9 +799,6 @@ static const struct memdev {
 	 [7] = { "full", 0666, &full_fops, 0 },
 	 [8] = { "random", 0666, &random_fops, 0 },
 	 [9] = { "urandom", 0666, &urandom_fops, 0 },
-#ifdef CONFIG_PRINTK
-	[11] = { "kmsg", 0644, &kmsg_fops, 0 },
-#endif
 };
 
 static int memory_open(struct inode *inode, struct file *filp)
@@ -811,7 +808,7 @@ static int memory_open(struct inode *inode, struct file *filp)
 
 	minor = iminor(inode);
 	if (minor >= ARRAY_SIZE(devlist))
-		return -ENXIO;
+		return kmsg_memory_open(inode, filp);
 
 	dev = &devlist[minor];
 	if (!dev->fops)
@@ -833,16 +830,28 @@ static const struct file_operations memory_fops = {
 
 static char *mem_devnode(struct device *dev, umode_t *mode)
 {
-	if (mode && devlist[MINOR(dev->devt)].mode)
-		*mode = devlist[MINOR(dev->devt)].mode;
+	int minor = MINOR(dev->devt);
+
+	if (!mode)
+		goto out;
+
+	if (minor >= ARRAY_SIZE(devlist)) {
+		kmsg_mode(minor, mode);
+		goto out;
+	}
+
+	if (devlist[minor].mode)
+		*mode = devlist[minor].mode;
+out:
 	return NULL;
 }
 
-static struct class *mem_class;
+struct class *mem_class;
 
 static int __init chr_dev_init(void)
 {
 	int minor;
+	struct device *kmsg;
 
 	if (register_chrdev(MEM_MAJOR, "mem", &memory_fops))
 		printk("unable to get major %d for memory devs\n", MEM_MAJOR);
@@ -866,6 +875,10 @@ static int __init chr_dev_init(void)
 			      NULL, devlist[minor].name);
 	}
 
+	kmsg = init_kmsg(KMSG_MINOR, 0644);
+	if (IS_ERR(kmsg))
+		return PTR_ERR(kmsg);
+
 	return tty_init();
 }
 
diff --git a/include/linux/printk.h b/include/linux/printk.h
index 9729565..0c4f9de 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -417,8 +417,40 @@ do {									\
 	no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
 #endif
 
+#define KMSG_MINOR	11
+
+struct file;
+struct inode;
+
+#ifdef CONFIG_PRINTK
+
+extern struct class *mem_class;
+
 extern const struct file_operations kmsg_fops;
 
+extern struct device *init_kmsg(int minor, umode_t mode);
+extern int kmsg_memory_open(struct inode *inode, struct file *filp);
+extern int kmsg_mode(int minor, umode_t *mode);
+
+#else
+
+static inline struct device *init_kmsg(int minor, umode_t mode)
+{
+	return NULL;
+}
+
+static inline int kmsg_memory_open(struct inode *inode, struct file *filp)
+{
+	return -ENXIO;
+}
+
+static inline int kmsg_mode(int minor, umode_t *mode)
+{
+	return -ENXIO;
+}
+
+#endif
+
 enum {
 	DUMP_PREFIX_NONE,
 	DUMP_PREFIX_ADDRESS,
diff --git a/kernel/printk/kmsg.c b/kernel/printk/kmsg.c
index 42e784bd..726250f 100644
--- a/kernel/printk/kmsg.c
+++ b/kernel/printk/kmsg.c
@@ -16,6 +16,9 @@
 #include <linux/syslog.h>
 #include <linux/uio.h>
 #include <linux/wait.h>
+#include <linux/device.h>
+#include <linux/major.h>
+#include <linux/kdev_t.h>
 
 #include <asm/uaccess.h>
 
@@ -386,6 +389,45 @@ const struct file_operations kmsg_fops = {
 	.release = devkmsg_release,
 };
 
+/* Should be used for device registration */
+struct device *init_kmsg(int minor, umode_t mode)
+{
+	log_buf.minor = minor;
+	log_buf.mode = mode;
+	return device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor),
+			NULL, "kmsg");
+}
+
+int kmsg_memory_open(struct inode *inode, struct file *filp)
+{
+	filp->f_op = &kmsg_fops;
+
+	return kmsg_fops.open(inode, filp);
+}
+
+int kmsg_mode(int minor, umode_t *mode)
+{
+	int ret = -ENXIO;
+	struct log_buffer *log_b;
+
+	if (minor == log_buf.minor) {
+		*mode = log_buf.mode;
+		return 0;
+	}
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(log_b, &log_buf.list, list) {
+		if (log_b->minor == minor) {
+			*mode = log_b->mode;
+			ret = 0;
+			break;
+		}
+	}
+	rcu_read_unlock();
+
+	return ret;
+}
+
 static DEFINE_SPINLOCK(dump_list_lock);
 static LIST_HEAD(dump_list);
 
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index cb348c1..fed50da 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -179,6 +179,7 @@ struct log_buffer log_buf = {
 	.next_idx	= 0,
 	.clear_seq	= 0,
 	.clear_idx	= 0,
+	.mode		= 0,
 	.minor		= 0,
 };
 
diff --git a/kernel/printk/printk.h b/kernel/printk/printk.h
index 8894a51..f9e3220 100644
--- a/kernel/printk/printk.h
+++ b/kernel/printk/printk.h
@@ -127,6 +127,7 @@ struct log_buffer {
 	u64 clear_seq;
 	u32 clear_idx;
 
+	int mode;		/* mode of device */
 	int minor;		/* minor representing buffer device */
 #endif
 };
-- 
1.9.1

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

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web