lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20150531151143.15103.46172.stgit@localhost.localdomain>
Date:	Mon, 01 Jun 2015 00:11:43 +0900
From:	Masami Hiramatsu <masami.hiramatsu.pt@...achi.com>
To:	Arnaldo Carvalho de Melo <acme@...nel.org>
Cc:	Peter Zijlstra <a.p.zijlstra@...llo.nl>,
	Adrian Hunter <adrian.hunter@...el.com>,
	linux-kernel@...r.kernel.org, Ingo Molnar <mingo@...hat.com>,
	Paul Mackerras <paulus@...ba.org>,
	Jiri Olsa <jolsa@...nel.org>,
	Namhyung Kim <namhyung@...nel.org>,
	Borislav Petkov <bp@...e.de>,
	Hemant Kumar <hemant@...ux.vnet.ibm.com>
Subject: [RFC PATCH perf/core 10/13] perf probe: Add --cache option to cache
 the probe definitions

Add --cache option to cache the probe definitions. This
just saves the result of the dwarf analysis to probe cache.

Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@...achi.com>
---
 tools/perf/builtin-probe.c    |    1 
 tools/perf/util/build-id.c    |   13 +-
 tools/perf/util/build-id.h    |    2 
 tools/perf/util/probe-event.c |  136 +++++++++++++++----
 tools/perf/util/probe-event.h |    5 +
 tools/perf/util/probe-file.c  |  298 +++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/probe-file.h  |   20 +++
 7 files changed, 443 insertions(+), 32 deletions(-)

diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 1272559..97f4d07 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -397,6 +397,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
 		     opt_set_filter),
 	OPT_CALLBACK('x', "exec", NULL, "executable|path",
 			"target executable name or path", opt_set_target),
+	OPT_BOOLEAN(0, "cache", &probe_conf.cache, "Manipulate probe cache"),
 	OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle,
 		    "Enable symbol demangling"),
 	OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index 02a5e0d..169639e 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -313,9 +313,8 @@ void disable_buildid_cache(void)
 	no_buildid_cache = true;
 }
 
-static char *build_id_cache__dirname_from_path(const char *name,
-					       bool is_kallsyms, bool is_vdso,
-					       const char *sbuild_id)
+char *build_id_cache__dirname_from_path(const char *sbuild_id, const char *name,
+					bool is_kallsyms, bool is_vdso)
 {
 	char *realname = (char *)name, *filename;
 	bool slash = is_kallsyms || is_vdso;
@@ -344,8 +343,8 @@ int build_id_cache__list_build_ids(const char *pathname,
 	char *dir_name;
 	int ret = 0;
 
-	dir_name = build_id_cache__dirname_from_path(pathname, false, false,
-						     NULL);
+	dir_name = build_id_cache__dirname_from_path(NULL, pathname,
+						     false, false);
 	if (!dir_name)
 		return -ENOMEM;
 
@@ -372,8 +371,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 			goto out_free;
 	}
 
-	dir_name = build_id_cache__dirname_from_path(name, is_kallsyms,
-						     is_vdso, sbuild_id);
+	dir_name = build_id_cache__dirname_from_path(sbuild_id, name,
+						     is_kallsyms, is_vdso);
 	if (!dir_name)
 		goto out_free;
 
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h
index 27a14a8..a1f428d 100644
--- a/tools/perf/util/build-id.h
+++ b/tools/perf/util/build-id.h
@@ -27,6 +27,8 @@ bool perf_session__read_build_ids(struct perf_session *session, bool with_hits);
 int perf_session__write_buildid_table(struct perf_session *session, int fd);
 int perf_session__cache_build_ids(struct perf_session *session);
 
+char *build_id_cache__dirname_from_path(const char *sbuild_id, const char *name,
+					bool is_kallsyms, bool is_vdso);
 int build_id_cache__list_build_ids(const char *pathname,
 				   struct strlist **result);
 bool build_id_cache__cached(const char *sbuild_id);
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 707be38..ea40d1da 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -68,7 +68,6 @@ int e_snprintf(char *str, size_t size, const char *format, ...)
 	return ret;
 }
 
-static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
 static struct machine *host_machine;
 
 /* Initialize symbol maps and path of vmlinux/modules */
@@ -1548,7 +1547,7 @@ char *synthesize_perf_probe_arg(struct perf_probe_arg *pa)
 }
 
 /* Compose only probe point (not argument) */
-static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
+char *synthesize_perf_probe_point(struct perf_probe_point *pp)
 {
 	struct strbuf buf;
 	char *tmp;
@@ -1581,30 +1580,36 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
 	return tmp;
 }
 
-#if 0
 char *synthesize_perf_probe_command(struct perf_probe_event *pev)
 {
-	char *buf;
-	int i, len, ret;
+	struct strbuf buf;
+	char *tmp;
+	int i;
 
-	buf = synthesize_perf_probe_point(&pev->point);
-	if (!buf)
-		return NULL;
+	strbuf_init(&buf, 64);
+	if (pev->event)
+		strbuf_addf(&buf, "%s:%s=", pev->group ?: PERFPROBE_GROUP,
+			    pev->event);
+
+	tmp = synthesize_perf_probe_point(&pev->point);
+	if (!tmp)
+		goto out;
+	strbuf_addstr(&buf, tmp);
+	free(tmp);
 
-	len = strlen(buf);
 	for (i = 0; i < pev->nargs; i++) {
-		ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s",
-				 pev->args[i].name);
-		if (ret <= 0) {
-			free(buf);
-			return NULL;
-		}
-		len += ret;
+		tmp = synthesize_perf_probe_arg(pev->args + i);
+		if (!tmp)
+			goto out;
+		strbuf_addf(&buf, " %s", tmp);
+		free(tmp);
 	}
 
-	return buf;
+	tmp = strbuf_detach(&buf, NULL);
+out:
+	strbuf_release(&buf);
+	return tmp;
 }
-#endif
 
 static int __synthesize_probe_trace_arg_ref(struct probe_trace_arg_ref *ref,
 					    struct strbuf *buf, int depth)
@@ -1685,7 +1690,6 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)
 		if (synthesize_probe_trace_arg(&tev->args[i], &buf) < 0)
 			goto error;
 	}
-
 	ret = strbuf_detach(&buf, NULL);
 error:
 	strbuf_release(&buf);
@@ -1831,6 +1835,79 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
 	memset(pev, 0, sizeof(*pev));
 }
 
+#define strdup_or_goto(str, label)	\
+({ char *__p = NULL; if (str && !(__p = strdup(str))) goto label; __p; })
+
+static int perf_probe_point__copy(struct perf_probe_point *dst,
+				  struct perf_probe_point *src)
+{
+	dst->file = strdup_or_goto(src->file, out_err);
+	dst->function = strdup_or_goto(src->function, out_err);
+	dst->lazy_line = strdup_or_goto(src->lazy_line, out_err);
+	dst->line = src->line;
+	dst->retprobe = src->retprobe;
+	dst->offset = src->offset;
+	return 0;
+
+out_err:
+	clear_perf_probe_point(dst);
+	return -ENOMEM;
+}
+
+static int perf_probe_arg__copy(struct perf_probe_arg *dst,
+				struct perf_probe_arg *src)
+{
+	struct perf_probe_arg_field *field, **ppfield;
+
+	dst->name = strdup_or_goto(src->name, out_err);
+	dst->var = strdup_or_goto(src->var, out_err);
+	dst->type = strdup_or_goto(src->type, out_err);
+
+	field = src->field;
+	ppfield = &(dst->field);
+	while (field) {
+		*ppfield = zalloc(sizeof(*field));
+		if (!*ppfield)
+			goto out_err;
+		(*ppfield)->name = strdup_or_goto(field->name, out_err);
+		(*ppfield)->index = field->index;
+		(*ppfield)->ref = field->ref;
+		field = field->next;
+		ppfield = &((*ppfield)->next);
+	}
+	return 0;
+out_err:
+	return -ENOMEM;
+}
+
+int perf_probe_event__copy(struct perf_probe_event *dst,
+			   struct perf_probe_event *src)
+{
+	int i;
+
+	dst->event = strdup_or_goto(src->event, out_err);
+	dst->group = strdup_or_goto(src->group, out_err);
+	dst->target = strdup_or_goto(src->target, out_err);
+	dst->uprobes = src->uprobes;
+
+	if (perf_probe_point__copy(&dst->point, &src->point) < 0)
+		goto out_err;
+
+	dst->args = zalloc(sizeof(struct perf_probe_arg) * src->nargs);
+	if (!dst->args)
+		goto out_err;
+	dst->nargs = src->nargs;
+
+	for (i = 0; i < src->nargs; i++)
+		if (perf_probe_arg__copy(&dst->args[i], &src->args[i]) < 0)
+			goto out_err;
+	return 0;
+
+out_err:
+	clear_perf_probe_event(dst);
+	return -ENOMEM;
+}
+
 void clear_probe_trace_event(struct probe_trace_event *tev)
 {
 	struct probe_trace_arg_ref *ref, *next;
@@ -2191,14 +2268,16 @@ static int probe_trace_event__set_name(struct probe_trace_event *tev,
 }
 
 static int __add_probe_trace_events(struct perf_probe_event *pev,
-				     struct probe_trace_event *tevs,
-				     int ntevs, bool allow_suffix)
+				    struct probe_trace_event *tevs,
+				    int ntevs, bool allow_suffix)
 {
 	int i, fd, ret;
 	struct probe_trace_event *tev = NULL;
+	struct probe_cache *cache = NULL;
 	struct strlist *namelist;
+	int flag = PF_FL_RW | (pev->uprobes ? PF_FL_UPROBE : 0);
 
-	fd = probe_file__open(PF_FL_RW | (pev->uprobes ? PF_FL_UPROBE : 0));
+	fd = probe_file__open(flag);
 	if (fd < 0)
 		return fd;
 
@@ -2244,6 +2323,16 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 
 	/* Note that it is possible to skip all events because of blacklist */
 	if (ret >= 0 && tev->event) {
+		if (probe_conf.cache) {
+			cache = probe_cache__new(pev->target);
+			if (!cache)
+				pr_info("Warning: Failed to add cache\n");
+			else {
+				probe_cache__add_entry(cache, pev, tevs, ntevs);
+				probe_cache__commit(cache);
+				probe_cache__delete(cache);
+			}
+		}
 		/* Show how to use the event. */
 		pr_info("\nYou can now use it in all perf tools, such as:\n\n");
 		pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group,
@@ -2273,9 +2362,6 @@ static int find_probe_functions(struct map *map, char *name)
 	return found;
 }
 
-#define strdup_or_goto(str, label)	\
-	({ char *__p = strdup(str); if (!__p) goto label; __p; })
-
 void __weak arch__fix_tev_from_maps(struct perf_probe_event *pev __maybe_unused,
 				struct probe_trace_event *tev __maybe_unused,
 				struct map *map __maybe_unused) { }
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 4cff6be..9aceba3 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -12,6 +12,7 @@ struct probe_conf {
 	bool	show_location_range;
 	bool	force_add;
 	bool	no_inlines;
+	bool	cache;
 	int	max_probes;
 };
 extern struct probe_conf probe_conf;
@@ -116,6 +117,10 @@ extern int parse_probe_trace_command(const char *cmd,
 extern char *synthesize_perf_probe_command(struct perf_probe_event *pev);
 extern char *synthesize_probe_trace_command(struct probe_trace_event *tev);
 extern char *synthesize_perf_probe_arg(struct perf_probe_arg *pa);
+char *synthesize_perf_probe_point(struct perf_probe_point *pp);
+
+int perf_probe_event__copy(struct perf_probe_event *dst,
+			   struct perf_probe_event *src);
 
 /* Check the perf_probe_event needs debuginfo */
 extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev);
diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c
index 2a7cc8c..572e8c2 100644
--- a/tools/perf/util/probe-file.c
+++ b/tools/perf/util/probe-file.c
@@ -14,6 +14,7 @@
  * GNU General Public License for more details.
  *
  */
+#include <sys/uio.h>
 #include "util.h"
 #include "event.h"
 #include "strlist.h"
@@ -300,3 +301,300 @@ int probe_file__del_events(int fd, struct strfilter *filter)
 	return ret;
 }
 
+static void probe_cache_entry__delete(struct probe_cache_entry *node)
+{
+	if (!list_empty(&node->list))
+		list_del(&node->list);
+	if (node->tevlist)
+		strlist__delete(node->tevlist);
+	clear_perf_probe_event(&node->pev);
+	free(node->spev);
+	free(node);
+}
+
+static struct probe_cache_entry *
+probe_cache_entry__new(struct perf_probe_event *pev)
+{
+	struct probe_cache_entry *ret = zalloc(sizeof(*ret));
+
+	if (ret) {
+		INIT_LIST_HEAD(&ret->list);
+		ret->tevlist = strlist__new(true, NULL);
+		if (!ret->tevlist)
+			zfree(&ret);
+		if (ret && pev) {
+			ret->spev = synthesize_perf_probe_command(pev);
+			if (!ret->spev ||
+			    perf_probe_event__copy(&ret->pev, pev) < 0) {
+				probe_cache_entry__delete(ret);
+				return NULL;
+			}
+		}
+	}
+
+	return ret;
+}
+
+/* For the kernel probe caches, pass target = NULL */
+static int probe_cache__open(struct probe_cache *pcache, const char *target)
+{
+	char cpath[PATH_MAX];
+	char sbuildid[SBUILD_ID_SIZE];
+	char *dir_name;
+	bool is_kallsyms = !target;
+	int ret, fd;
+
+	if (target)
+		ret = filename__sprintf_build_id(target, sbuildid);
+	else {
+		target = "[kernel.kallsyms]";
+		ret = sysfs__sprintf_build_id("/", sbuildid);
+	}
+	if (ret < 0) {
+		pr_warning("Failed to get build-id from %s.\n", target ?: "kernel");
+		return ret;
+	}
+
+	/* If we have no buildid cache, make it */
+	if (!build_id_cache__cached(sbuildid)) {
+		ret = build_id_cache__add_s(sbuildid, target,
+					    is_kallsyms, NULL);
+		if (ret < 0) {
+			pr_warning("Failed to add cache: %s\n", target);
+			return ret;
+		}
+	}
+
+	dir_name = build_id_cache__dirname_from_path(sbuildid, target,
+						     is_kallsyms, false);
+	if (!dir_name)
+		return -ENOMEM;
+
+	snprintf(cpath, PATH_MAX, "%s/probes", dir_name);
+	fd = open(cpath, O_CREAT | O_RDWR | O_APPEND, 0644);
+	if (fd < 0)
+		pr_warning("Failed to open cache(%d): %s\n", fd, cpath);
+	free(dir_name);
+	pcache->fd = fd;
+
+	return fd;
+}
+
+static int probe_cache__load(struct probe_cache *pcache)
+{
+	struct probe_cache_entry *entry = NULL;
+	char buf[MAX_CMDLEN], *p;
+	int ret = 0;
+	FILE *fp;
+
+	fp = fdopen(dup(pcache->fd), "r");
+	while (!feof(fp)) {
+		if (!fgets(buf, MAX_CMDLEN, fp))
+			break;
+		p = strchr(buf, '\n');
+		if (p)
+			*p = '\0';
+		if (buf[0] == '#') {	/* #perf_probe_event */
+			entry = probe_cache_entry__new(NULL);
+			entry->spev = strdup(buf + 1);
+			ret = parse_perf_probe_command(buf + 1, &entry->pev);
+			if (!entry->spev || ret < 0) {
+				probe_cache_entry__delete(entry);
+				goto out;
+			}
+			list_add_tail(&entry->list, &pcache->list);
+		} else {	/* trace_probe_event */
+			if (!entry) {
+				ret = -EINVAL;
+				goto out;
+			}
+			strlist__add(entry->tevlist, buf);
+		}
+	}
+out:
+	fclose(fp);
+	return ret;
+}
+
+static struct probe_cache *probe_cache__alloc(void)
+{
+	struct probe_cache *ret = zalloc(sizeof(*ret));
+
+	if (ret) {
+		INIT_LIST_HEAD(&ret->list);
+		ret->fd = -EINVAL;
+	}
+	return ret;
+}
+
+void probe_cache__delete(struct probe_cache *pcache)
+{
+	struct probe_cache_entry *entry;
+
+	if (!pcache)
+		return;
+
+	while (!list_empty(&pcache->list)) {
+		entry = list_first_entry(&pcache->list, typeof(*entry), list);
+		probe_cache_entry__delete(entry);
+	}
+	if (pcache->fd > 0)
+		close(pcache->fd);
+	free(pcache);
+}
+
+struct probe_cache *probe_cache__new(const char *target)
+{
+	struct probe_cache *pcache = probe_cache__alloc();
+	int ret;
+
+	if (!pcache)
+		return NULL;
+
+	ret = probe_cache__open(pcache, target);
+	if (ret < 0) {
+		pr_debug("Cache open error: %d\n", ret);
+		goto out_err;
+	}
+
+	ret = probe_cache__load(pcache);
+	if (ret < 0) {
+		pr_debug("Cache read error: %d\n", ret);
+		goto out_err;
+	}
+
+	return pcache;
+
+out_err:
+	probe_cache__delete(pcache);
+	return NULL;
+}
+
+static bool streql(const char *a, const char *b)
+{
+	if (a == b)
+		return true;
+
+	if (!a || !b)
+		return false;
+
+	return !strcmp(a, b);
+}
+
+static struct probe_cache_entry *
+probe_cache__find(struct probe_cache *pcache, struct perf_probe_event *pev)
+{
+	struct probe_cache_entry *entry = NULL;
+	char *cmd = NULL;
+
+	cmd = synthesize_perf_probe_command(pev);
+	if (!cmd)
+		return NULL;
+
+	list_for_each_entry(entry, &pcache->list, list) {
+		/* Hit if same event name or same command-string */
+		if ((pev->event &&
+		     (streql(entry->pev.group, pev->group) &&
+		      streql(entry->pev.event, pev->event))) ||
+		    (!strcmp(entry->spev, cmd)))
+			goto found;
+	}
+	entry = NULL;
+
+found:
+	free(cmd);
+	return entry;
+}
+
+int probe_cache__add_entry(struct probe_cache *pcache,
+			   struct perf_probe_event *pev,
+			   struct probe_trace_event *tevs, int ntevs)
+{
+	struct probe_cache_entry *entry = NULL;
+	char *command;
+	int i, ret = 0;
+
+	if (!pcache || !pev || !tevs || ntevs <= 0) {
+		ret = -EINVAL;
+		goto out_err;
+	}
+
+	/* Remove old cache entry */
+	entry = probe_cache__find(pcache, pev);
+	if (entry)
+		probe_cache_entry__delete(entry);
+
+	ret = -ENOMEM;
+	entry = probe_cache_entry__new(pev);
+	if (!entry)
+		goto out_err;
+
+	for (i = 0; i < ntevs; i++) {
+		if (!tevs[i].point.symbol)
+			continue;
+
+		command = synthesize_probe_trace_command(&tevs[i]);
+		if (!command)
+			goto out_err;
+		strlist__add(entry->tevlist, command);
+		free(command);
+	}
+	list_add_tail(&entry->list, &pcache->list);
+	pr_debug("Added probe cache: %d\n", ntevs);
+	return 0;
+
+out_err:
+	pr_debug("Failed to add probe caches\n");
+	if (entry)
+		probe_cache_entry__delete(entry);
+	return ret;
+}
+
+static int probe_cache_entry__write(struct probe_cache_entry *entry, int fd)
+{
+	struct str_node *snode;
+	struct iovec iov[3];
+	int ret;
+
+	pr_debug("Writing cache: #%s\n", entry->spev);
+	iov[0].iov_base = (void *)"#"; iov[0].iov_len = 1;
+	iov[1].iov_base = entry->spev; iov[1].iov_len = strlen(entry->spev);
+	iov[2].iov_base = (void *)"\n"; iov[2].iov_len = 1;
+	ret = writev(fd, iov, 3);
+	if (ret < 0)
+		return ret;
+
+	strlist__for_each(snode, entry->tevlist) {
+		iov[0].iov_base = (void *)snode->s;
+		iov[0].iov_len = strlen(snode->s);
+		iov[1].iov_base = (void *)"\n"; iov[1].iov_len = 1;
+		ret = writev(fd, iov, 2);
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+
+int probe_cache__commit(struct probe_cache *pcache)
+{
+	struct probe_cache_entry *entry;
+	int ret = 0;
+
+	/* TBD: if we do not update existing entries, skip it */
+	ret = lseek(pcache->fd, 0, SEEK_SET);
+	if (ret < 0)
+		goto out;
+
+	ret = ftruncate(pcache->fd, 0);
+	if (ret < 0)
+		goto out;
+
+	list_for_each_entry(entry, &pcache->list, list) {
+		ret = probe_cache_entry__write(entry, pcache->fd);
+		pr_debug("Cache committed: %d\n", ret);
+		if (ret < 0)
+			break;
+	}
+out:
+	return ret;
+}
diff --git a/tools/perf/util/probe-file.h b/tools/perf/util/probe-file.h
index ada94a2..7b473b39 100644
--- a/tools/perf/util/probe-file.h
+++ b/tools/perf/util/probe-file.h
@@ -15,4 +15,24 @@ struct strlist *probe_file__get_rawlist(int fd);
 int probe_file__add_event(int fd, struct probe_trace_event *tev);
 int probe_file__del_events(int fd, struct strfilter *filter);
 
+/* Cache of probe definitions */
+struct probe_cache_entry {
+	struct list_head	list;
+	struct perf_probe_event pev;
+	char			*spev;
+	struct strlist		*tevlist;
+};
+
+struct probe_cache {
+	int	fd;
+	struct list_head list;
+};
+
+struct probe_cache *probe_cache__new(const char *target);
+int probe_cache__add_entry(struct probe_cache *pcache,
+			   struct perf_probe_event *pev,
+			   struct probe_trace_event *tevs, int ntevs);
+int probe_cache__commit(struct probe_cache *pcache);
+void probe_cache__delete(struct probe_cache *pcache);
+
 #endif

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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ