[RFC] ehea: kdump support using new shutdown hook

5 messages, 4 authors, 2007-12-13 · open the first message on its own page

[RFC] ehea: kdump support using new shutdown hook

From: Thomas Klein <hidden>
Date: 2007-12-12 16:56:49

This patch adds kdump support using the new PPC crash shutdown hook to the
ehea driver. The driver now keeps a list of firmware handles which have to
be freed in case of a crash. The crash handler does the minimum required: it
frees the firmware resource handles plus broadcast/multicast registrations.

Please comment.

Shutdown hook patches:
  http://ozlabs.org/pipermail/linuxppc-dev/2007-December/048058.html
  http://ozlabs.org/pipermail/linuxppc-dev/2007-December/048059.html


Signed-off-by: Thomas Klein <redacted>

---
diff -Nurp -X dontdiff linux-2.6.24-rc5/drivers/net/ehea/ehea.h patched_kernel/drivers/net/ehea/ehea.h
--- linux-2.6.24-rc5/drivers/net/ehea/ehea.h	2007-12-11 04:48:43.000000000 +0100
+++ patched_kernel/drivers/net/ehea/ehea.h	2007-12-12 17:30:53.000000000 +0100
@@ -40,7 +40,7 @@
 #include <asm/io.h>
 
 #define DRV_NAME	"ehea"
-#define DRV_VERSION	"EHEA_0083"
+#define DRV_VERSION	"EHEA_0084"
 
 /* eHEA capability flags */
 #define DLPAR_PORT_ADD_REM 1
@@ -386,6 +386,7 @@ struct ehea_port_res {
 
 
 #define EHEA_MAX_PORTS 16
+#define EHEA_MAX_RES_HANDLES (100 * EHEA_MAX_PORTS + 10)
 struct ehea_adapter {
 	u64 handle;
 	struct of_device *ofdev;
@@ -397,6 +398,7 @@ struct ehea_adapter {
 	u64 max_mc_mac;            /* max number of multicast mac addresses */
 	int active_ports;
 	struct list_head list;
+	u64 res_handles[EHEA_MAX_RES_HANDLES];
 };
 
 
diff -Nurp -X dontdiff linux-2.6.24-rc5/drivers/net/ehea/ehea_main.c patched_kernel/drivers/net/ehea/ehea_main.c
--- linux-2.6.24-rc5/drivers/net/ehea/ehea_main.c	2007-12-11 04:48:43.000000000 +0100
+++ patched_kernel/drivers/net/ehea/ehea_main.c	2007-12-12 17:30:53.000000000 +0100
@@ -35,6 +35,7 @@
 #include <linux/if_ether.h>
 #include <linux/notifier.h>
 #include <linux/reboot.h>
+#include <asm-powerpc/kexec.h>
 
 #include <net/ip.h>
 
@@ -2256,6 +2257,33 @@ static int ehea_clean_all_portres(struct
 	return ret;
 }
 
+static void ehea_update_adapter_handles(struct ehea_adapter *adapter)
+{
+	int i, k;
+	int j = 0;
+
+	memset(adapter->res_handles, sizeof(adapter->res_handles), 0);
+
+	for (k = 0; k < EHEA_MAX_PORTS; k++) {
+		struct ehea_port *port = adapter->port[k];
+
+		if (!port || (port->state != EHEA_PORT_UP))
+			continue;
+
+		for(i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) {
+			struct ehea_port_res *pr = &port->port_res[i];
+
+			adapter->res_handles[j++] = pr->qp->fw_handle;
+			adapter->res_handles[j++] = pr->send_cq->fw_handle;
+			adapter->res_handles[j++] = pr->recv_cq->fw_handle;
+			adapter->res_handles[j++] = pr->eq->fw_handle;
+			adapter->res_handles[j++] = pr->send_mr.handle;
+			adapter->res_handles[j++] = pr->recv_mr.handle;
+		}
+		adapter->res_handles[j++] = port->qp_eq->fw_handle;
+	}
+}
+
 static void ehea_remove_adapter_mr(struct ehea_adapter *adapter)
 {
 	if (adapter->active_ports)
@@ -2318,6 +2346,7 @@ static int ehea_up(struct net_device *de
 
 	ret = 0;
 	port->state = EHEA_PORT_UP;
+	ehea_update_adapter_handles(port->adapter);
 	goto out;
 
 out_free_irqs:
@@ -2387,6 +2416,8 @@ static int ehea_down(struct net_device *
 		ehea_info("Failed freeing resources for %s. ret=%i",
 			  dev->name, ret);
 
+	ehea_update_adapter_handles(port->adapter);
+
 	return ret;
 }
 
@@ -3302,6 +3333,71 @@ static int __devexit ehea_remove(struct 
 	return 0;
 }
 
+void ehea_crash_deregister(void)
+{
+	struct ehea_adapter *adapter;
+	int i;
+	u64 hret;
+	u8 reg_type;
+
+	list_for_each_entry(adapter, &adapter_list, list) {
+		for (i = 0; i < EHEA_MAX_PORTS; i++) {
+			struct ehea_port *port = adapter->port[i];
+			if (port->state == EHEA_PORT_UP) {
+				struct ehea_mc_list *mc_entry = port->mc_list;
+				struct list_head *pos;
+				struct list_head *temp;
+
+				/* Undo multicast registrations */
+				list_for_each_safe(pos, temp,
+						   &(port->mc_list->list)) {
+					mc_entry = list_entry(pos,
+							    struct ehea_mc_list,
+							    list);
+					ehea_multicast_reg_helper(port,
+							      mc_entry->macaddr,
+							      H_DEREG_BCMC);
+				}
+
+				/* Undo broad registration */
+				reg_type = EHEA_BCMC_BROADCAST |
+					   EHEA_BCMC_UNTAGGED;
+				ehea_h_reg_dereg_bcmc(port->adapter->handle,
+						      port->logical_port_id,
+						      reg_type, port->mac_addr,
+						      0, H_DEREG_BCMC);
+
+				reg_type = EHEA_BCMC_BROADCAST |
+					   EHEA_BCMC_VLANID_ALL;
+				ehea_h_reg_dereg_bcmc(port->adapter->handle,
+						      port->logical_port_id,
+						      reg_type, port->mac_addr,
+						      0, H_DEREG_BCMC);
+			}
+		}
+		for (i = 0; i < EHEA_MAX_RES_HANDLES; i++) {
+			u64 handle = adapter->res_handles[i];
+			if (handle) {
+				hret = ehea_h_free_resource(adapter->handle,
+							    handle,
+							    FORCE_FREE);
+			}
+		}
+
+		if (adapter->neq) {
+			hret = ehea_h_free_resource(adapter->handle,
+						    adapter->neq->fw_handle,
+						    FORCE_FREE);
+		}
+
+		if (adapter->mr.handle) {
+			hret = ehea_h_free_resource(adapter->handle,
+						    adapter->mr.handle,
+						    FORCE_FREE);
+		}
+	}
+}
+
 static int ehea_reboot_notifier(struct notifier_block *nb,
 				unsigned long action, void *unused)
 {
@@ -3373,6 +3469,9 @@ int __init ehea_module_init(void)
 		goto out;
 
 	register_reboot_notifier(&ehea_reboot_nb);
+	ret = crash_shutdown_register(&ehea_crash_deregister);
+	if (ret)
+		ehea_info("failed registering crash handler");
 
 	ret = ibmebus_register_driver(&ehea_driver);
 	if (ret) {
@@ -3386,6 +3485,7 @@ int __init ehea_module_init(void)
 		ehea_error("failed to register capabilities attribute, ret=%d",
 			   ret);
 		unregister_reboot_notifier(&ehea_reboot_nb);
+		crash_shutdown_unregister(&ehea_crash_deregister);
 		ibmebus_unregister_driver(&ehea_driver);
 		goto out;
 	}
@@ -3396,10 +3496,15 @@ out:
 
 static void __exit ehea_module_exit(void)
 {
+	int ret;
+
 	flush_scheduled_work();
 	driver_remove_file(&ehea_driver.driver, &driver_attr_capabilities);
 	ibmebus_unregister_driver(&ehea_driver);
 	unregister_reboot_notifier(&ehea_reboot_nb);
+	ret = crash_shutdown_unregister(&ehea_crash_deregister);
+	if (ret)
+		ehea_info("failed unregistering crash handler");
 	ehea_destroy_busmap();
 }

Re: [RFC] ehea: kdump support using new shutdown hook

From: Dave Jones <hidden>
Date: 2007-12-12 17:05:36

On Wed, Dec 12, 2007 at 05:53:43PM +0100, Thomas Klein wrote:

 > +static void ehea_update_adapter_handles(struct ehea_adapter *adapter)
 > +{
 > +	int i, k;
 > +	int j = 0;
 > +
 > +	memset(adapter->res_handles, sizeof(adapter->res_handles), 0);

arguments wrong way around.
 
	Dave
 
-- 
http://www.codemonkey.org.uk

Re: [RFC] ehea: kdump support using new shutdown hook

From: Michael Ellerman <hidden>
Date: 2007-12-13 06:30:37

On Wed, 2007-12-12 at 12:04 -0500, Dave Jones wrote:
On Wed, Dec 12, 2007 at 05:53:43PM +0100, Thomas Klein wrote:

 > +static void ehea_update_adapter_handles(struct ehea_adapter *adapter)
 > +{
 > +	int i, k;
 > +	int j = 0;
 > +
 > +	memset(adapter->res_handles, sizeof(adapter->res_handles), 0);

arguments wrong way around.
Remind me why bzero is deprecated again? :)

cheers

-- 
Michael Ellerman
OzLabs, IBM Australia Development Lab

wwweb: http://michael.ellerman.id.au
phone: +61 2 6212 1183 (tie line 70 21183)

We do not inherit the earth from our ancestors,
we borrow it from our children. - S.M.A.R.T Person

Re: [RFC] ehea: kdump support using new shutdown hook

From: Michael Ellerman <hidden>
Date: 2007-12-13 06:50:52

On Wed, 2007-12-12 at 17:53 +0100, Thomas Klein wrote:
This patch adds kdump support using the new PPC crash shutdown hook to the
ehea driver. The driver now keeps a list of firmware handles which have to
be freed in case of a crash. The crash handler does the minimum required: it
frees the firmware resource handles plus broadcast/multicast registrations.

Please comment.
Hi Thomas,

Here's a few ..
quoted hunk
---
diff -Nurp -X dontdiff linux-2.6.24-rc5/drivers/net/ehea/ehea.h patched_kernel/drivers/net/ehea/ehea.h
--- linux-2.6.24-rc5/drivers/net/ehea/ehea.h	2007-12-11 04:48:43.000000000 +0100
+++ patched_kernel/drivers/net/ehea/ehea.h	2007-12-12 17:30:53.000000000 +0100
@@ -386,6 +386,7 @@ struct ehea_port_res {
 

 #define EHEA_MAX_PORTS 16
+#define EHEA_MAX_RES_HANDLES (100 * EHEA_MAX_PORTS + 10)
 struct ehea_adapter {
 	u64 handle;
 	struct of_device *ofdev;
@@ -397,6 +398,7 @@ struct ehea_adapter {
 	u64 max_mc_mac;            /* max number of multicast mac addresses */
 	int active_ports;
 	struct list_head list;
+	u64 res_handles[EHEA_MAX_RES_HANDLES];
 };
I don't like this ..
quoted hunk
diff -Nurp -X dontdiff linux-2.6.24-rc5/drivers/net/ehea/ehea_main.c patched_kernel/drivers/net/ehea/ehea_main.c
--- linux-2.6.24-rc5/drivers/net/ehea/ehea_main.c	2007-12-11 04:48:43.000000000 +0100
+++ patched_kernel/drivers/net/ehea/ehea_main.c	2007-12-12 17:30:53.000000000 +0100
@@ -35,6 +35,7 @@
 #include <linux/if_ether.h>
 #include <linux/notifier.h>
 #include <linux/reboot.h>
+#include <asm-powerpc/kexec.h>
Just <asm/kexec.h>
quoted hunk
@@ -3302,6 +3333,71 @@ static int __devexit ehea_remove(struct 
 	return 0;
 }
 
+void ehea_crash_deregister(void)
+{
+	struct ehea_adapter *adapter;
+	int i;
+	u64 hret;
+	u8 reg_type;
+
+	list_for_each_entry(adapter, &adapter_list, list) {
+		for (i = 0; i < EHEA_MAX_PORTS; i++) {
+			struct ehea_port *port = adapter->port[i];
+			if (port->state == EHEA_PORT_UP) {
+				struct ehea_mc_list *mc_entry = port->mc_list;
+				struct list_head *pos;
+				struct list_head *temp;
+
+				/* Undo multicast registrations */
+				list_for_each_safe(pos, temp,
+						   &(port->mc_list->list)) {
+					mc_entry = list_entry(pos,
+							    struct ehea_mc_list,
+							    list);
+					ehea_multicast_reg_helper(port,
+							      mc_entry->macaddr,
+							      H_DEREG_BCMC);
+				}
+
+				/* Undo broad registration */
+				reg_type = EHEA_BCMC_BROADCAST |
+					   EHEA_BCMC_UNTAGGED;
+				ehea_h_reg_dereg_bcmc(port->adapter->handle,
+						      port->logical_port_id,
+						      reg_type, port->mac_addr,
+						      0, H_DEREG_BCMC);
+
+				reg_type = EHEA_BCMC_BROADCAST |
+					   EHEA_BCMC_VLANID_ALL;
+				ehea_h_reg_dereg_bcmc(port->adapter->handle,
+						      port->logical_port_id,
+						      reg_type, port->mac_addr,
+						      0, H_DEREG_BCMC);
+			}
+		}
+		for (i = 0; i < EHEA_MAX_RES_HANDLES; i++) {
+			u64 handle = adapter->res_handles[i];
+			if (handle) {
+				hret = ehea_h_free_resource(adapter->handle,
+							    handle,
+							    FORCE_FREE);
+			}
+		}
+
+		if (adapter->neq) {
+			hret = ehea_h_free_resource(adapter->handle,
+						    adapter->neq->fw_handle,
+						    FORCE_FREE);
+		}
+
+		if (adapter->mr.handle) {
+			hret = ehea_h_free_resource(adapter->handle,
+						    adapter->mr.handle,
+						    FORCE_FREE);
+		}
+	}
+}
This is sort of on the right track, I like that the ehea_h_.. routines
are basically just hypercalls, but there's a few things wrong. You're
walking a linked list, which is asking for trouble, if a single pointer
is corrupted you'll walk off into lala land. Then you're pulling fields
out of structs via pointers, again hoping that they're all valid. Then
you walk another linked list ..  And then you're pulling bits out of the
adapter again.

Ideally it would look like this:

static u64 things_to_free[];

void ehea_crash_deregister(void)
{
	for (i = 0; i < num_things_to_free; i++)
		h_call_free_a_thing(things_to_free[i]);
}
quoted hunk
 static int ehea_reboot_notifier(struct notifier_block *nb,
 				unsigned long action, void *unused)
 {
@@ -3373,6 +3469,9 @@ int __init ehea_module_init(void)
 		goto out;
 
 	register_reboot_notifier(&ehea_reboot_nb);
+	ret = crash_shutdown_register(&ehea_crash_deregister);
+	if (ret)
+		ehea_info("failed registering crash handler");
Your naming is a little confusing here, you're registering the
deregister function. I think I get it, you're unregistering the fw
handles, but perhaps ehea_crash_shutdown() would be clearer.
quoted hunk
@@ -3396,10 +3496,15 @@ out:
 
 static void __exit ehea_module_exit(void)
 {
+	int ret;
+
 	flush_scheduled_work();
 	driver_remove_file(&ehea_driver.driver, &driver_attr_capabilities);
 	ibmebus_unregister_driver(&ehea_driver);
 	unregister_reboot_notifier(&ehea_reboot_nb);
+	ret = crash_shutdown_unregister(&ehea_crash_deregister);
+	if (ret)
+		ehea_info("failed unregistering crash handler");
You don't need ret if that's all you're going to do with it.

cheers

-- 
Michael Ellerman
OzLabs, IBM Australia Development Lab

wwweb: http://michael.ellerman.id.au
phone: +61 2 6212 1183 (tie line 70 21183)

We do not inherit the earth from our ancestors,
we borrow it from our children. - S.M.A.R.T Person

Re: [RFC] ehea: kdump support using new shutdown hook

From: Subrata Modak <hidden>
Date: 2007-12-13 11:37:10

Do you think we can improve upon our own LTP-KDUMP test cases housed
here:
http://ltp.cvs.sourceforge.net/ltp/ltp/testcases/kdump/,

because of your changes below:

--Subrata

On Wed, 2007-12-12 at 17:53 +0100, Thomas Klein wrote:
quoted hunk
This patch adds kdump support using the new PPC crash shutdown hook to the
ehea driver. The driver now keeps a list of firmware handles which have to
be freed in case of a crash. The crash handler does the minimum required: it
frees the firmware resource handles plus broadcast/multicast registrations.

Please comment.

Shutdown hook patches:
  http://ozlabs.org/pipermail/linuxppc-dev/2007-December/048058.html
  http://ozlabs.org/pipermail/linuxppc-dev/2007-December/048059.html


Signed-off-by: Thomas Klein <redacted>

---
diff -Nurp -X dontdiff linux-2.6.24-rc5/drivers/net/ehea/ehea.h patched_kernel/drivers/net/ehea/ehea.h
--- linux-2.6.24-rc5/drivers/net/ehea/ehea.h	2007-12-11 04:48:43.000000000 +0100
+++ patched_kernel/drivers/net/ehea/ehea.h	2007-12-12 17:30:53.000000000 +0100
@@ -40,7 +40,7 @@
 #include <asm/io.h>

 #define DRV_NAME	"ehea"
-#define DRV_VERSION	"EHEA_0083"
+#define DRV_VERSION	"EHEA_0084"

 /* eHEA capability flags */
 #define DLPAR_PORT_ADD_REM 1
@@ -386,6 +386,7 @@ struct ehea_port_res {


 #define EHEA_MAX_PORTS 16
+#define EHEA_MAX_RES_HANDLES (100 * EHEA_MAX_PORTS + 10)
 struct ehea_adapter {
 	u64 handle;
 	struct of_device *ofdev;
@@ -397,6 +398,7 @@ struct ehea_adapter {
 	u64 max_mc_mac;            /* max number of multicast mac addresses */
 	int active_ports;
 	struct list_head list;
+	u64 res_handles[EHEA_MAX_RES_HANDLES];
 };

diff -Nurp -X dontdiff linux-2.6.24-rc5/drivers/net/ehea/ehea_main.c patched_kernel/drivers/net/ehea/ehea_main.c
--- linux-2.6.24-rc5/drivers/net/ehea/ehea_main.c	2007-12-11 04:48:43.000000000 +0100
+++ patched_kernel/drivers/net/ehea/ehea_main.c	2007-12-12 17:30:53.000000000 +0100
@@ -35,6 +35,7 @@
 #include <linux/if_ether.h>
 #include <linux/notifier.h>
 #include <linux/reboot.h>
+#include <asm-powerpc/kexec.h>

 #include <net/ip.h>
@@ -2256,6 +2257,33 @@ static int ehea_clean_all_portres(struct
 	return ret;
 }

+static void ehea_update_adapter_handles(struct ehea_adapter *adapter)
+{
+	int i, k;
+	int j = 0;
+
+	memset(adapter->res_handles, sizeof(adapter->res_handles), 0);
+
+	for (k = 0; k < EHEA_MAX_PORTS; k++) {
+		struct ehea_port *port = adapter->port[k];
+
+		if (!port || (port->state != EHEA_PORT_UP))
+			continue;
+
+		for(i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) {
+			struct ehea_port_res *pr = &port->port_res[i];
+
+			adapter->res_handles[j++] = pr->qp->fw_handle;
+			adapter->res_handles[j++] = pr->send_cq->fw_handle;
+			adapter->res_handles[j++] = pr->recv_cq->fw_handle;
+			adapter->res_handles[j++] = pr->eq->fw_handle;
+			adapter->res_handles[j++] = pr->send_mr.handle;
+			adapter->res_handles[j++] = pr->recv_mr.handle;
+		}
+		adapter->res_handles[j++] = port->qp_eq->fw_handle;
+	}
+}
+
 static void ehea_remove_adapter_mr(struct ehea_adapter *adapter)
 {
 	if (adapter->active_ports)
@@ -2318,6 +2346,7 @@ static int ehea_up(struct net_device *de

 	ret = 0;
 	port->state = EHEA_PORT_UP;
+	ehea_update_adapter_handles(port->adapter);
 	goto out;

 out_free_irqs:
@@ -2387,6 +2416,8 @@ static int ehea_down(struct net_device *
 		ehea_info("Failed freeing resources for %s. ret=%i",
 			  dev->name, ret);

+	ehea_update_adapter_handles(port->adapter);
+
 	return ret;
 }
@@ -3302,6 +3333,71 @@ static int __devexit ehea_remove(struct 
 	return 0;
 }

+void ehea_crash_deregister(void)
+{
+	struct ehea_adapter *adapter;
+	int i;
+	u64 hret;
+	u8 reg_type;
+
+	list_for_each_entry(adapter, &adapter_list, list) {
+		for (i = 0; i < EHEA_MAX_PORTS; i++) {
+			struct ehea_port *port = adapter->port[i];
+			if (port->state == EHEA_PORT_UP) {
+				struct ehea_mc_list *mc_entry = port->mc_list;
+				struct list_head *pos;
+				struct list_head *temp;
+
+				/* Undo multicast registrations */
+				list_for_each_safe(pos, temp,
+						   &(port->mc_list->list)) {
+					mc_entry = list_entry(pos,
+							    struct ehea_mc_list,
+							    list);
+					ehea_multicast_reg_helper(port,
+							      mc_entry->macaddr,
+							      H_DEREG_BCMC);
+				}
+
+				/* Undo broad registration */
+				reg_type = EHEA_BCMC_BROADCAST |
+					   EHEA_BCMC_UNTAGGED;
+				ehea_h_reg_dereg_bcmc(port->adapter->handle,
+						      port->logical_port_id,
+						      reg_type, port->mac_addr,
+						      0, H_DEREG_BCMC);
+
+				reg_type = EHEA_BCMC_BROADCAST |
+					   EHEA_BCMC_VLANID_ALL;
+				ehea_h_reg_dereg_bcmc(port->adapter->handle,
+						      port->logical_port_id,
+						      reg_type, port->mac_addr,
+						      0, H_DEREG_BCMC);
+			}
+		}
+		for (i = 0; i < EHEA_MAX_RES_HANDLES; i++) {
+			u64 handle = adapter->res_handles[i];
+			if (handle) {
+				hret = ehea_h_free_resource(adapter->handle,
+							    handle,
+							    FORCE_FREE);
+			}
+		}
+
+		if (adapter->neq) {
+			hret = ehea_h_free_resource(adapter->handle,
+						    adapter->neq->fw_handle,
+						    FORCE_FREE);
+		}
+
+		if (adapter->mr.handle) {
+			hret = ehea_h_free_resource(adapter->handle,
+						    adapter->mr.handle,
+						    FORCE_FREE);
+		}
+	}
+}
+
 static int ehea_reboot_notifier(struct notifier_block *nb,
 				unsigned long action, void *unused)
 {
@@ -3373,6 +3469,9 @@ int __init ehea_module_init(void)
 		goto out;

 	register_reboot_notifier(&ehea_reboot_nb);
+	ret = crash_shutdown_register(&ehea_crash_deregister);
+	if (ret)
+		ehea_info("failed registering crash handler");

 	ret = ibmebus_register_driver(&ehea_driver);
 	if (ret) {
@@ -3386,6 +3485,7 @@ int __init ehea_module_init(void)
 		ehea_error("failed to register capabilities attribute, ret=%d",
 			   ret);
 		unregister_reboot_notifier(&ehea_reboot_nb);
+		crash_shutdown_unregister(&ehea_crash_deregister);
 		ibmebus_unregister_driver(&ehea_driver);
 		goto out;
 	}
@@ -3396,10 +3496,15 @@ out:

 static void __exit ehea_module_exit(void)
 {
+	int ret;
+
 	flush_scheduled_work();
 	driver_remove_file(&ehea_driver.driver, &driver_attr_capabilities);
 	ibmebus_unregister_driver(&ehea_driver);
 	unregister_reboot_notifier(&ehea_reboot_nb);
+	ret = crash_shutdown_unregister(&ehea_crash_deregister);
+	if (ret)
+		ehea_info("failed unregistering crash handler");
 	ehea_destroy_busmap();
 }
--
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/
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help