From: Lin Ming on
On Tue, 2010-06-29 at 16:55 +0800, Ingo Molnar wrote:
> * Lin Ming <ming.m.lin(a)intel.com> wrote:
>
> > On Fri, 2010-06-25 at 01:33 +0800, Ingo Molnar wrote:
> > > * Johannes Berg <johannes(a)sipsolutions.net> wrote:
> > >
> > > > On Thu, 2010-06-24 at 11:36 +0200, Ingo Molnar wrote:
> > > >
> > > > > That's probably best achieved via a TRACE_EVENT() variant, by passing in the
> > > > > sysfs location.
> > > > >
> > > > > It might even make sense to make this a part of TRACE_EVENT() itself and make
> > > > > 'NULL' the current default, non-sysfs-enumerated behavior. That way we can
> > > > > gradually (and non-intrusively) find all the right sysfs places for events.
> > > >
> > > > No, this doesn't work. A lot of events are multi-instance. Say you have an
> > > > event for each USB device. This event would have to show up in many places
> > > > in sysfs, and each trace_foo() invocation needs to get the struct device
> > > > pointer, not just the TRACE_EVENT() definition. Additionally, to
> > > > create/destroy the sysfs pieces we need something like init_trace_foo(dev)
> > > > and destroy_trace_foo(dev) be called when the sysfs points for the device
> > > > should be created/destroyed.
> > >
> > > Yes - but even this could be expressed via TRACE_EVENT(): by giving it a
> > > device-specific function pointer and then instantiating individual events from
> > > a single, central place in sysfs.
> > >
> > > That is the place where we already know where it ends up in sysfs, and where
> > > the event-specific function can match up whether that particular node belongs
> > > to it and whether an additional event directory should be created for that
> > > particular sysfs node.
> > >
> > > > The TRACE_EVENT() just defines the template, but such multi-instance events
> > > > really should be standardised in terms of their struct device (or maybe
> > > > kobject).
> > > >
> > > > I think that needs some TRACE_DEVICE_EVENT macro that creates the required
> > > > inlines etc, and including the init/destroy that are called when the event
> > > > should show up in sysfs.
> > > >
> > > > There's no way you can have the event show up in sysfs at the right spot
> > > > with _just_ a TRACE_EVENT macro, since at define time in the header file you
> > > > don't even have a valid struct device pointer.
> > >
> > > That would be another possible way to do it - to explicitly create the events
> > > directory. It looks a bit simpler as we wouldnt have to touch TRACE_EVENT()
> > > and because it directly expresses the 'this node has an events directory'
> > > property at the place where we create the device node.
> >
> > Let me take i915 tracepoints as an example.
> > Do you mean something like below?
> >
> > diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
> > index 423dc90..9e7e4a0 100644
> > --- a/drivers/gpu/drm/i915/i915_drv.c
> > +++ b/drivers/gpu/drm/i915/i915_drv.c
> > @@ -28,6 +28,7 @@
> > */
> >
> > #include <linux/device.h>
> > +#include <linux/perf_event.h>
> > #include "drmP.h"
> > #include "drm.h"
> > #include "i915_drm.h"
> > @@ -413,7 +414,17 @@ int i965_reset(struct drm_device *dev, u8 flags)
> > static int __devinit
> > i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
> > {
> > - return drm_get_dev(pdev, ent, &driver);
> > + struct kobject *kobj;
> > + int ret;
> > +
> > + ret = drm_get_dev(pdev, ent, &driver);
> > +
> > + if (!ret) {
> > + kobj = &pdev->dev.kobj;
> > + perf_sys_register_tp(kobj, "i915");
> > + }
> > +
> > + return ret;
>
> Yeah, something like that - assuming that this means that we'll add the events
> directory to the device directory, to all the
> /sys/bus/pci/drivers/i915/*/events/ driver directories right? (i havent
> checked the DRM code)

I haven't run the code, but I think yes.

>
> Small detail, it could be written a bit more compactly, like:

Thanks for the tip.

>
> > + int ret;
> > +
> > + ret = drm_get_dev(pdev, ent, &driver);
> > + if (!ret)
> > + perf_sys_register_tp(&pdev->dev.kobj, "i915");
> > +
> > + return ret;
>
> Also, we can (optionally) consider 'generic', subsystem level events to also
> show up under:
>
> /sys/bus/pci/drivers/i915/events/
>
> This would give a model to non-device-specific events to be listed one level
> higher in the sysfs hierarchy.
>
> This too would be done in the driver, not by generic code. It's generally the
> driver which knows how the events should be categorized.

This is a bit difficult. I'd like not to touch TRACE_EVENT().
How does the driver know if an event is 'generic' if TRACE_EVENT is not
touched?

>
> I'd imagine something similar for wireless drivers as well - most currently
> defined events would show up on a per device basis there.
>
> Can you see practical problems with this scheme?

Not now. I may find some problems when write more detail code.

Lin Ming

>
> Ingo

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

* Lin Ming <ming.m.lin(a)intel.com> wrote:

> > Also, we can (optionally) consider 'generic', subsystem level events to
> > also show up under:
> >
> > /sys/bus/pci/drivers/i915/events/
> >
> > This would give a model to non-device-specific events to be listed one
> > level higher in the sysfs hierarchy.
> >
> > This too would be done in the driver, not by generic code. It's generally
> > the driver which knows how the events should be categorized.
>
> This is a bit difficult. I'd like not to touch TRACE_EVENT(). [...]

We can certainly start with the simpler variant - it's also the more common
case.

> [...] How does the driver know if an event is 'generic' if TRACE_EVENT is
> not touched?

Well, it's per driver code which creates the 'events' directory anyway, so
that code decides where to link things. It can link it to the per driver kobj
- or to the per subsys kobj.

> > I'd imagine something similar for wireless drivers as well - most
> > currently defined events would show up on a per device basis there.
> >
> > Can you see practical problems with this scheme?
>
> Not now. I may find some problems when write more detail code.

Ok. Feel free to post RFC patches (even if they are not fully complete yet),
so that we can see how things are progressing.

I suspect the best approach would be to try to figure out the right sysfs
placement for one or two existing driver tracepoints, so that we can see it
all in practice. (Obviously any changes to drivers will have to go via the
relevant driver maintainer tree(s).)

Thanks,

Ingo
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Lin Ming on
On Tue, 2010-06-29 at 18:26 +0800, Ingo Molnar wrote:
> * Lin Ming <ming.m.lin(a)intel.com> wrote:
>
> > > Also, we can (optionally) consider 'generic', subsystem level events to
> > > also show up under:
> > >
> > > /sys/bus/pci/drivers/i915/events/
> > >
> > > This would give a model to non-device-specific events to be listed one
> > > level higher in the sysfs hierarchy.
> > >
> > > This too would be done in the driver, not by generic code. It's generally
> > > the driver which knows how the events should be categorized.
> >
> > This is a bit difficult. I'd like not to touch TRACE_EVENT(). [...]
>
> We can certainly start with the simpler variant - it's also the more common
> case.
>
> > [...] How does the driver know if an event is 'generic' if TRACE_EVENT is
> > not touched?
>
> Well, it's per driver code which creates the 'events' directory anyway, so
> that code decides where to link things. It can link it to the per driver kobj
> - or to the per subsys kobj.
>
> > > I'd imagine something similar for wireless drivers as well - most
> > > currently defined events would show up on a per device basis there.
> > >
> > > Can you see practical problems with this scheme?
> >
> > Not now. I may find some problems when write more detail code.
>
> Ok. Feel free to post RFC patches (even if they are not fully complete yet),
> so that we can see how things are progressing.
>
> I suspect the best approach would be to try to figure out the right sysfs
> placement for one or two existing driver tracepoints, so that we can see it
> all in practice. (Obviously any changes to drivers will have to go via the
> relevant driver maintainer tree(s).)

Well, take i915 tracepoints as an example, the sys structures as below

/sys/class/drm/card0/events/
|-- i915_gem_object_bind
| |-- enable
| |-- filter
| |-- format
| `-- id
|-- i915_gem_object_change_domain
| |-- enable
| |-- filter
| |-- format
| `-- id
|-- i915_gem_object_clflush
| |-- enable
| |-- filter
| |-- format
| `-- id
|-- i915_gem_object_create
| |-- enable
| |-- filter
| |-- format
| `-- id
|-- i915_gem_object_destroy
| |-- enable
| |-- filter
| |-- format
| `-- id
|-- i915_gem_object_get_fence
| |-- enable
| |-- filter
| |-- format
| `-- id
|-- i915_gem_object_unbind
| |-- enable
| |-- filter
| |-- format
| `-- id
|-- i915_gem_request_complete
| |-- enable
| |-- filter
| |-- format
| `-- id
|-- i915_gem_request_flush
| |-- enable
| |-- filter
| |-- format
| `-- id
|-- i915_gem_request_retire
| |-- enable
| |-- filter
| |-- format
| `-- id
|-- i915_gem_request_submit
| |-- enable
| |-- filter
| |-- format
| `-- id
|-- i915_gem_request_wait_begin
| |-- enable
| |-- filter
| |-- format
| `-- id
|-- i915_gem_request_wait_end
| |-- enable
| |-- filter
| |-- format
| `-- id
|-- i915_ring_wait_begin
| |-- enable
| |-- filter
| |-- format
| `-- id
`-- i915_ring_wait_end
|-- enable
|-- filter
|-- format
`-- id

And below is the very draft patch to export i915 tracepoints in sysfs.
Is it the right direction?

---
drivers/gpu/drm/i915/i915_drv.c | 15 +++-
include/linux/perf_event.h | 2 +
kernel/perf_event.c | 168 +++++++++++++++++++++++++++++++++++++++
4 files changed, 186 insertions(+), 1 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 423dc90..eb7fa9e 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -28,6 +28,7 @@
*/

#include <linux/device.h>
+#include <linux/perf_event.h>
#include "drmP.h"
#include "drm.h"
#include "i915_drm.h"
@@ -413,7 +414,19 @@ int i965_reset(struct drm_device *dev, u8 flags)
static int __devinit
i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
- return drm_get_dev(pdev, ent, &driver);
+ struct kobject *kobj;
+ struct drm_device *drm_dev;
+ int ret;
+
+ ret = drm_get_dev(pdev, ent, &driver);
+
+ if (!ret) {
+ drm_dev = pci_get_drvdata(pdev);
+ kobj = &drm_dev->primary->kdev.kobj;
+ perf_sys_register_tp(kobj, "i915");
+ }
+
+ return ret;
}

static void
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 716f99b..2a6d834 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -1019,6 +1019,8 @@ extern int perf_swevent_get_recursion_context(void);
extern void perf_swevent_put_recursion_context(int rctx);
extern void perf_event_enable(struct perf_event *event);
extern void perf_event_disable(struct perf_event *event);
+
+extern void perf_sys_register_tp(struct kobject *kobj, char *tp_system);
#else
static inline void
perf_event_task_sched_in(struct task_struct *task) { }
diff --git a/kernel/perf_event.c b/kernel/perf_event.c
index 403d180..068ee48 100644
--- a/kernel/perf_event.c
+++ b/kernel/perf_event.c
@@ -5877,3 +5877,171 @@ static int __init perf_event_sysfs_init(void)
&perfclass_attr_group);
}
device_initcall(perf_event_sysfs_init);
+
+#define for_each_event(event, start, end) \
+ for (event = start; \
+ (unsigned long)event < (unsigned long)end; \
+ event++)
+
+extern struct ftrace_event_call __start_ftrace_events[];
+extern struct ftrace_event_call __stop_ftrace_events[];
+extern void print_event_filter(struct ftrace_event_call *call,
+ struct trace_seq *s);
+
+struct tp_kobject {
+ struct kobject *kobj;
+ struct ftrace_event_call *call;
+ struct tp_kobject *next;
+};
+
+static struct tp_kobject *tp_kobject_list;
+
+static struct ftrace_event_call *perf_sys_find_tp_call(struct kobject *kobj)
+{
+ struct tp_kobject *tp_kobj;
+
+ tp_kobj = tp_kobject_list;
+
+ while (tp_kobj) {
+ if (kobj == tp_kobj->kobj)
+ return tp_kobj->call;
+
+ tp_kobj = tp_kobj->next;
+ }
+
+ return NULL;
+}
+
+#define TP_ATTR_RO(_name) \
+ static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
+
+#define TP_ATTR(_name) \
+ static struct kobj_attribute _name##_attr = \
+ __ATTR(_name, 0644, _name##_show, _name##_store)
+
+static ssize_t enable_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct ftrace_event_call *call;
+
+ call = perf_sys_find_tp_call(kobj);
+ return sprintf(buf, "%d\n", call->flags & TRACE_EVENT_FL_ENABLED);
+}
+
+static ssize_t enable_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t len)
+{
+ /* Not implemented yet */
+
+ return 0;
+}
+TP_ATTR(enable);
+
+static ssize_t filter_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct ftrace_event_call *call;
+ struct trace_seq *s;
+
+ call = perf_sys_find_tp_call(kobj);
+
+ s = kmalloc(sizeof(*s), GFP_KERNEL);
+ if (!s)
+ return -ENOMEM;
+
+ trace_seq_init(s);
+
+ print_event_filter(call, s);
+
+ memcpy(buf, s->buffer, s->len);
+
+ kfree(s);
+
+ return s->len;
+}
+
+static ssize_t filter_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t len)
+{
+ /* Not implemented yet */
+
+ return 0;
+}
+TP_ATTR(filter);
+
+static ssize_t format_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ /* Not implemented yet */
+
+ return 0;
+}
+TP_ATTR_RO(format);
+
+static ssize_t id_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct ftrace_event_call *call;
+
+ call = perf_sys_find_tp_call(kobj);
+
+ return sprintf(buf, "%d\n", call->event.type);
+}
+TP_ATTR_RO(id);
+
+static struct attribute *tp_attrs[] = {
+ &enable_attr.attr,
+ &filter_attr.attr,
+ &format_attr.attr,
+ &id_attr.attr,
+ NULL,
+};
+
+static struct attribute_group tp_attr_group = {
+ .attrs = tp_attrs,
+};
+
+static int perf_sys_add_tp(struct kobject *parent, struct ftrace_event_call *call)
+{
+ struct tp_kobject *tp_kobj;
+ struct kobject *event_kobj;
+ int err;
+
+ event_kobj = kobject_create_and_add(call->name, parent);
+ if (!event_kobj)
+ return -ENOMEM;
+ err = sysfs_create_group(event_kobj, &tp_attr_group);
+ if (err) {
+ kobject_put(event_kobj);
+ return -ENOMEM;
+ }
+
+ tp_kobj = kmalloc(sizeof(*tp_kobj), GFP_KERNEL);
+ if (!tp_kobj) {
+ kobject_put(event_kobj);
+ return -ENOMEM;
+ }
+
+ tp_kobj->kobj = event_kobj;
+ tp_kobj->call = call;
+ tp_kobj->next = tp_kobject_list;
+ tp_kobject_list = tp_kobj;
+
+ return 0;
+}
+
+void perf_sys_register_tp(struct kobject *kobj, char *tp_system)
+{
+ struct ftrace_event_call *call;
+ struct kobject *events_kobj;
+
+ events_kobj = kobject_create_and_add("events", kobj);
+ if (!events_kobj)
+ return;
+
+ for_each_event(call, __start_ftrace_events, __stop_ftrace_events) {
+ if (call->class->system && !strcmp(call->class->system, tp_system)) {
+ perf_sys_add_tp(events_kobj, call);
+ }
+ }
+}


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

* Lin Ming <ming.m.lin(a)intel.com> wrote:

> On Tue, 2010-06-29 at 18:26 +0800, Ingo Molnar wrote:
> > * Lin Ming <ming.m.lin(a)intel.com> wrote:
> >
> > > > Also, we can (optionally) consider 'generic', subsystem level events to
> > > > also show up under:
> > > >
> > > > /sys/bus/pci/drivers/i915/events/
> > > >
> > > > This would give a model to non-device-specific events to be listed one
> > > > level higher in the sysfs hierarchy.
> > > >
> > > > This too would be done in the driver, not by generic code. It's generally
> > > > the driver which knows how the events should be categorized.
> > >
> > > This is a bit difficult. I'd like not to touch TRACE_EVENT(). [...]
> >
> > We can certainly start with the simpler variant - it's also the more common
> > case.
> >
> > > [...] How does the driver know if an event is 'generic' if TRACE_EVENT is
> > > not touched?
> >
> > Well, it's per driver code which creates the 'events' directory anyway, so
> > that code decides where to link things. It can link it to the per driver kobj
> > - or to the per subsys kobj.
> >
> > > > I'd imagine something similar for wireless drivers as well - most
> > > > currently defined events would show up on a per device basis there.
> > > >
> > > > Can you see practical problems with this scheme?
> > >
> > > Not now. I may find some problems when write more detail code.
> >
> > Ok. Feel free to post RFC patches (even if they are not fully complete yet),
> > so that we can see how things are progressing.
> >
> > I suspect the best approach would be to try to figure out the right sysfs
> > placement for one or two existing driver tracepoints, so that we can see it
> > all in practice. (Obviously any changes to drivers will have to go via the
> > relevant driver maintainer tree(s).)
>
> Well, take i915 tracepoints as an example, the sys structures as below
>
> /sys/class/drm/card0/events/
> |-- i915_gem_object_bind
> | |-- enable
> | |-- filter
> | |-- format
> | `-- id
> |-- i915_gem_object_change_domain
> | |-- enable
> | |-- filter
> | |-- format
> | `-- id
> |-- i915_gem_object_clflush
> | |-- enable
> | |-- filter
> | |-- format
> | `-- id
> |-- i915_gem_object_create
> | |-- enable
> | |-- filter
> | |-- format
> | `-- id
> |-- i915_gem_object_destroy
> | |-- enable
> | |-- filter
> | |-- format
> | `-- id
> |-- i915_gem_object_get_fence
> | |-- enable
> | |-- filter
> | |-- format
> | `-- id
> |-- i915_gem_object_unbind
> | |-- enable
> | |-- filter
> | |-- format
> | `-- id
> |-- i915_gem_request_complete
> | |-- enable
> | |-- filter
> | |-- format
> | `-- id
> |-- i915_gem_request_flush
> | |-- enable
> | |-- filter
> | |-- format
> | `-- id
> |-- i915_gem_request_retire
> | |-- enable
> | |-- filter
> | |-- format
> | `-- id
> |-- i915_gem_request_submit
> | |-- enable
> | |-- filter
> | |-- format
> | `-- id
> |-- i915_gem_request_wait_begin
> | |-- enable
> | |-- filter
> | |-- format
> | `-- id
> |-- i915_gem_request_wait_end
> | |-- enable
> | |-- filter
> | |-- format
> | `-- id
> |-- i915_ring_wait_begin
> | |-- enable
> | |-- filter
> | |-- format
> | `-- id
> `-- i915_ring_wait_end
> |-- enable
> |-- filter
> |-- format
> `-- id
>
> And below is the very draft patch to export i915 tracepoints in sysfs.
> Is it the right direction?

Yeah, i think so.

The per driver impact is small and to the point:

> drivers/gpu/drm/i915/i915_drv.c | 15 +++-

> i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
> {
> - return drm_get_dev(pdev, ent, &driver);
> + struct kobject *kobj;
> + struct drm_device *drm_dev;
> + int ret;
> +
> + ret = drm_get_dev(pdev, ent, &driver);
> +
> + if (!ret) {
> + drm_dev = pci_get_drvdata(pdev);
> + kobj = &drm_dev->primary->kdev.kobj;
> + perf_sys_register_tp(kobj, "i915");
> + }
> +
> + return ret;

(It could be even shorter - the same compactness comment as i made last time
still holds for this function.)

Thanks,

Ingo
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Corey Ashford on
On 07/02/2010 01:06 AM, Lin Ming wrote:
> On Tue, 2010-06-29 at 18:26 +0800, Ingo Molnar wrote:
>> * Lin Ming<ming.m.lin(a)intel.com> wrote:
>>
>>>> Also, we can (optionally) consider 'generic', subsystem level events to
>>>> also show up under:
>>>>
>>>> /sys/bus/pci/drivers/i915/events/
>>>>
>>>> This would give a model to non-device-specific events to be listed one
>>>> level higher in the sysfs hierarchy.
>>>>
>>>> This too would be done in the driver, not by generic code. It's generally
>>>> the driver which knows how the events should be categorized.
>>>
>>> This is a bit difficult. I'd like not to touch TRACE_EVENT(). [...]
>>
>> We can certainly start with the simpler variant - it's also the more common
>> case.
>>
>>> [...] How does the driver know if an event is 'generic' if TRACE_EVENT is
>>> not touched?
>>
>> Well, it's per driver code which creates the 'events' directory anyway, so
>> that code decides where to link things. It can link it to the per driver kobj
>> - or to the per subsys kobj.
>>
>>>> I'd imagine something similar for wireless drivers as well - most
>>>> currently defined events would show up on a per device basis there.
>>>>
>>>> Can you see practical problems with this scheme?
>>>
>>> Not now. I may find some problems when write more detail code.
>>
>> Ok. Feel free to post RFC patches (even if they are not fully complete yet),
>> so that we can see how things are progressing.
>>
>> I suspect the best approach would be to try to figure out the right sysfs
>> placement for one or two existing driver tracepoints, so that we can see it
>> all in practice. (Obviously any changes to drivers will have to go via the
>> relevant driver maintainer tree(s).)
>
> Well, take i915 tracepoints as an example, the sys structures as below
>
> /sys/class/drm/card0/events/
> |-- i915_gem_object_bind
> | |-- enable
> | |-- filter
> | |-- format
> | `-- id
....

Hi Lin,

Sorry for my late reply on this thread. I had missed these posts
earlier because I had an email filter that was set to look for messages
with "perf" in the subject, and so I missed this entire thread.

With your example here, let's say I want to open this event with the
perf_events ABI... how would I go about doing that? Have you figured
out whether the caller would read the id and pass that into the
interface, or perhaps pass in the fd of the id file (or perhaps the fd
of the specific event directory).

Also, I see the filter and format fields here. Would the caller write
to these fields to set them up? What's the format of the data that's
written to them? Would it be totally device dependent? It seems like
there should be a way for a user space tool to discover what can be
programmed into the filter and format fields.

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