Re: [PATCH v4 03/12] kgdboc: Use a platform device to handle tty drivers showing up late

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, May 07, 2020 at 01:08:41PM -0700, Douglas Anderson wrote:
> If you build CONFIG_KGDB_SERIAL_CONSOLE into the kernel then you
> should be able to have KGDB init itself at bootup by specifying the
> "kgdboc=..." kernel command line parameter.  This has worked OK for me
> for many years, but on a new device I switched to it stopped working.
> 
> The problem is that on this new device the serial driver gets its
> probe deferred.  Now when kgdb initializes it can't find the tty
> driver and when it gives up it never tries again.
> 
> We could try to find ways to move up the initialization of the serial
> driver and such a thing might be worthwhile, but it's nice to be
> robust against serial drivers that load late.  We could move kgdb to
> init itself later but that penalizes our ability to debug early boot
> code on systems where the driver inits early.  We could roll our own
> system of detecting when new tty drivers get loaded and then use that
> to figure out when kgdb can init, but that's ugly.
> 
> Instead, let's jump on the -EPROBE_DEFER bandwagon.  We'll create a
> singleton instance of a "kgdboc" platform device.  If we can't find
> our tty device when the singleton "kgdboc" probes we'll return
> -EPROBE_DEFER which means that the system will call us back later to
> try again when the tty device might be there.
> 
> We won't fully transition all of the kgdboc to a platform device
> because early kgdb initialization (via the "ekgdboc" kernel command
> line parameter) still runs before the platform device has been
> created.  The kgdb platform device is merely used as a convenient way
> to hook into the system's normal probe deferral mechanisms.
> 
> As part of this, we'll ever-so-slightly change how the "kgdboc=..."
> kernel command line parameter works.  Previously if you booted up and
> kgdb couldn't find the tty driver then later reading
> '/sys/module/kgdboc/parameters/kgdboc' would return a blank string.
> Now kgdb will keep track of the string that came as part of the
> command line and give it back to you.  It's expected that this should
> be an OK change.
> 
> Signed-off-by: Douglas Anderson <dianders@xxxxxxxxxxxx>
> Reviewed-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
> Reviewed-by: Daniel Thompson <daniel.thompson@xxxxxxxxxx>
> ---
> 
> Changes in v4: None
> Changes in v3: None
> Changes in v2: None
> 
>  drivers/tty/serial/kgdboc.c | 126 +++++++++++++++++++++++++++++-------
>  1 file changed, 101 insertions(+), 25 deletions(-)
> 
> diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c
> index 8a1a4d1b6768..519d8cfbfbed 100644
> --- a/drivers/tty/serial/kgdboc.c
> +++ b/drivers/tty/serial/kgdboc.c
> @@ -20,6 +20,7 @@
>  #include <linux/vt_kern.h>
>  #include <linux/input.h>
>  #include <linux/module.h>
> +#include <linux/platform_device.h>
>  
>  #define MAX_CONFIG_LEN		40
>  
> @@ -27,6 +28,7 @@ static struct kgdb_io		kgdboc_io_ops;
>  
>  /* -1 = init not run yet, 0 = unconfigured, 1 = configured. */
>  static int configured		= -1;
> +DEFINE_MUTEX(config_mutex);

Sparse gently pointed out to me that this should be static. Don't
worry about it though... I will fixup.


Daniel.

>  
>  static char config[MAX_CONFIG_LEN];
>  static struct kparam_string kps = {
> @@ -38,6 +40,8 @@ static int kgdboc_use_kms;  /* 1 if we use kernel mode switching */
>  static struct tty_driver	*kgdb_tty_driver;
>  static int			kgdb_tty_line;
>  
> +static struct platform_device *kgdboc_pdev;
> +
>  #ifdef CONFIG_KDB_KEYBOARD
>  static int kgdboc_reset_connect(struct input_handler *handler,
>  				struct input_dev *dev,
> @@ -133,11 +137,13 @@ static void kgdboc_unregister_kbd(void)
>  
>  static void cleanup_kgdboc(void)
>  {
> +	if (configured != 1)
> +		return;
> +
>  	if (kgdb_unregister_nmi_console())
>  		return;
>  	kgdboc_unregister_kbd();
> -	if (configured == 1)
> -		kgdb_unregister_io_module(&kgdboc_io_ops);
> +	kgdb_unregister_io_module(&kgdboc_io_ops);
>  }
>  
>  static int configure_kgdboc(void)
> @@ -198,20 +204,79 @@ static int configure_kgdboc(void)
>  	kgdb_unregister_io_module(&kgdboc_io_ops);
>  noconfig:
>  	kgdboc_unregister_kbd();
> -	config[0] = 0;
>  	configured = 0;
> -	cleanup_kgdboc();
>  
>  	return err;
>  }
>  
> +static int kgdboc_probe(struct platform_device *pdev)
> +{
> +	int ret = 0;
> +
> +	mutex_lock(&config_mutex);
> +	if (configured != 1) {
> +		ret = configure_kgdboc();
> +
> +		/* Convert "no device" to "defer" so we'll keep trying */
> +		if (ret == -ENODEV)
> +			ret = -EPROBE_DEFER;
> +	}
> +	mutex_unlock(&config_mutex);
> +
> +	return ret;
> +}
> +
> +static struct platform_driver kgdboc_platform_driver = {
> +	.probe = kgdboc_probe,
> +	.driver = {
> +		.name = "kgdboc",
> +		.suppress_bind_attrs = true,
> +	},
> +};
> +
>  static int __init init_kgdboc(void)
>  {
> -	/* Already configured? */
> -	if (configured == 1)
> +	int ret;
> +
> +	/*
> +	 * kgdboc is a little bit of an odd "platform_driver".  It can be
> +	 * up and running long before the platform_driver object is
> +	 * created and thus doesn't actually store anything in it.  There's
> +	 * only one instance of kgdb so anything is stored as global state.
> +	 * The platform_driver is only created so that we can leverage the
> +	 * kernel's mechanisms (like -EPROBE_DEFER) to call us when our
> +	 * underlying tty is ready.  Here we init our platform driver and
> +	 * then create the single kgdboc instance.
> +	 */
> +	ret = platform_driver_register(&kgdboc_platform_driver);
> +	if (ret)
> +		return ret;
> +
> +	kgdboc_pdev = platform_device_alloc("kgdboc", PLATFORM_DEVID_NONE);
> +	if (!kgdboc_pdev) {
> +		ret = -ENOMEM;
> +		goto err_did_register;
> +	}
> +
> +	ret = platform_device_add(kgdboc_pdev);
> +	if (!ret)
>  		return 0;
>  
> -	return configure_kgdboc();
> +	platform_device_put(kgdboc_pdev);
> +
> +err_did_register:
> +	platform_driver_unregister(&kgdboc_platform_driver);
> +	return ret;
> +}
> +
> +static void exit_kgdboc(void)
> +{
> +	mutex_lock(&config_mutex);
> +	cleanup_kgdboc();
> +	mutex_unlock(&config_mutex);
> +
> +	platform_device_unregister(kgdboc_pdev);
> +	platform_driver_unregister(&kgdboc_platform_driver);
>  }
>  
>  static int kgdboc_get_char(void)
> @@ -234,24 +299,20 @@ static int param_set_kgdboc_var(const char *kmessage,
>  				const struct kernel_param *kp)
>  {
>  	size_t len = strlen(kmessage);
> +	int ret = 0;
>  
>  	if (len >= MAX_CONFIG_LEN) {
>  		pr_err("config string too long\n");
>  		return -ENOSPC;
>  	}
>  
> -	/* Only copy in the string if the init function has not run yet */
> -	if (configured < 0) {
> -		strcpy(config, kmessage);
> -		return 0;
> -	}
> -
>  	if (kgdb_connected) {
>  		pr_err("Cannot reconfigure while KGDB is connected.\n");
> -
>  		return -EBUSY;
>  	}
>  
> +	mutex_lock(&config_mutex);
> +
>  	strcpy(config, kmessage);
>  	/* Chop out \n char as a result of echo */
>  	if (len && config[len - 1] == '\n')
> @@ -260,8 +321,30 @@ static int param_set_kgdboc_var(const char *kmessage,
>  	if (configured == 1)
>  		cleanup_kgdboc();
>  
> -	/* Go and configure with the new params. */
> -	return configure_kgdboc();
> +	/*
> +	 * Configure with the new params as long as init already ran.
> +	 * Note that we can get called before init if someone loads us
> +	 * with "modprobe kgdboc kgdboc=..." or if they happen to use the
> +	 * the odd syntax of "kgdboc.kgdboc=..." on the kernel command.
> +	 */
> +	if (configured >= 0)
> +		ret = configure_kgdboc();
> +
> +	/*
> +	 * If we couldn't configure then clear out the config.  Note that
> +	 * specifying an invalid config on the kernel command line vs.
> +	 * through sysfs have slightly different behaviors.  If we fail
> +	 * to configure what was specified on the kernel command line
> +	 * we'll leave it in the 'config' and return -EPROBE_DEFER from
> +	 * our probe.  When specified through sysfs userspace is
> +	 * responsible for loading the tty driver before setting up.
> +	 */
> +	if (ret)
> +		config[0] = '\0';
> +
> +	mutex_unlock(&config_mutex);
> +
> +	return ret;
>  }
>  
>  static int dbg_restore_graphics;
> @@ -320,15 +403,8 @@ __setup("kgdboc=", kgdboc_option_setup);
>  /* This is only available if kgdboc is a built in for early debugging */
>  static int __init kgdboc_early_init(char *opt)
>  {
> -	/* save the first character of the config string because the
> -	 * init routine can destroy it.
> -	 */
> -	char save_ch;
> -
>  	kgdboc_option_setup(opt);
> -	save_ch = config[0];
> -	init_kgdboc();
> -	config[0] = save_ch;
> +	configure_kgdboc();
>  	return 0;
>  }
>  
> @@ -336,7 +412,7 @@ early_param("ekgdboc", kgdboc_early_init);
>  #endif /* CONFIG_KGDB_SERIAL_CONSOLE */
>  
>  module_init(init_kgdboc);
> -module_exit(cleanup_kgdboc);
> +module_exit(exit_kgdboc);
>  module_param_call(kgdboc, param_set_kgdboc_var, param_get_string, &kps, 0644);
>  MODULE_PARM_DESC(kgdboc, "<serial_device>[,baud]");
>  MODULE_DESCRIPTION("KGDB Console TTY Driver");
> -- 
> 2.26.2.645.ge9eca65c58-goog
> 



[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux