From nobody Tue Dec 2 02:41:52 2025 Received: from mail-pf1-f169.google.com (mail-pf1-f169.google.com [209.85.210.169]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 258D63A8D5D for ; Tue, 18 Nov 2025 19:07:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763492852; cv=none; b=ajqYojevzDfoKjysMsGcT4c1ziaVMfyrduf89ncjRqGIBa9zXaxsY2XegkDf3acZkJw2LO0XbddKY8xFLp9IoVHNrYZdTmd2G8/SPJAbkPfImv6S+grcUnaJjc6rQ7ya76PE4zXNGG2GJUl/8KMSPrSC5HCCgPoMXhIoQevvZUg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763492852; c=relaxed/simple; bh=HT0QxSDa7a8BHmQG/mGJtMU6TV8tU166Zjb+23/L2rA=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=hJVg69rV1Z16zQvg24zJgfDngCoQ/B4vYNYQ9n4TREGNQrpmK44KTIx0Io5LNAb1UsremVxiVO2K0gjM75qFB50av2WC/UjKgCM0j77orMP9JmQ3uXj2zxAcYOTIQdm06LxsxRCy/ZdnCnCzaWWSuexE0J6zlIKVCV1NO93ckL0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=chrisdown.name; spf=pass smtp.mailfrom=chrisdown.name; dkim=pass (1024-bit key) header.d=chrisdown.name header.i=@chrisdown.name header.b=pxATSeED; arc=none smtp.client-ip=209.85.210.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=chrisdown.name Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=chrisdown.name Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=chrisdown.name header.i=@chrisdown.name header.b="pxATSeED" Received: by mail-pf1-f169.google.com with SMTP id d2e1a72fcca58-7ba55660769so4011706b3a.1 for ; Tue, 18 Nov 2025 11:07:29 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chrisdown.name; s=google; t=1763492849; x=1764097649; darn=vger.kernel.org; h=user-agent:in-reply-to:content-disposition:mime-version:references :message-id:subject:cc:to:from:date:from:to:cc:subject:date :message-id:reply-to; bh=KSozgwJ7y+AXQ2vEHjuoQgquZuDni9Lb1m7WIFPR8GY=; b=pxATSeEDmxLdAPI4OhZ1M4eUciXoTvF7Pw20t5nL0v5212Q0TuNHsYjpQj50egy2HB KnN0ngi2S4wSCDMRJHi8cMymIShWctwKiRME5iv9qT8jIlbyTQt+dF3ttkdAHcJqF6zF jIyYpnp19hqn3l/Xgulevm547sm9eqod8F0cI= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763492849; x=1764097649; h=user-agent:in-reply-to:content-disposition:mime-version:references :message-id:subject:cc:to:from:date:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=KSozgwJ7y+AXQ2vEHjuoQgquZuDni9Lb1m7WIFPR8GY=; b=t6S2GeBo8j1gzcJyplydyg2n+tzUY+HcmbK4wLckb5jgwHIormeOkGTRwv7Xg8DNdt YDo3Jj7HP50tIgdwX/5WO9ttXBnx9pr9nm67RgD0evL/QSCQuf0OoKoeWXlB9lnoNKIQ sJVtUri59qIgz/Z+wUlEtIRJkT03LlA++35i+MyAzuX6svG1/bDVQAkAdd+ETJb5ktTQ 7imJpoWB6cvCKpzkgM/T4HrarpXILtkPt71ddVDqHPGkShOyNXbayXUWxcQjV2BfwoXL mmxQ2CoxxC6iNUd58q9J3+Qj3yYfDhQbR2OZAJDxuX/533+gE8GUPpaKDZOAv/tlQi7b DjRQ== X-Gm-Message-State: AOJu0Yyp+DqyJzma7xEXyqeiFRiRbnbdhVz99JMfnDAN7BvO18yCh6+B bE9rXGmqj8r9Au2q066aNtCz8pQzTWt4qOKsL75sqQQAZLNfEmGhwOBK7ggYmTNeFSI= X-Gm-Gg: ASbGncsmcRBaIwzBG3qHDfq9jkPUbg5lb/P8x6xxk7xoceSfVI09fFRD0eUfbsCNy3b F9MtiNAjc2kwSN3n6Twgh++7QTZmsVHz+yZEUW/EnHOCjhMkjy3H+lk8Lf8Ry8yZ/Vk7UG+vddk vqz18EqfOKBe6vGUC4aO2NJLci/oh8EMb3VRtjUaJyqJ5/suQf4/Vmm/LlNUofJpRinTySTJopi tpy3IL+nZ00PDhWZJwUOM2LBmlwTA0HbAjFthz9AvdFmXjLf7tCPVoNmqFumT9pyiE7E3zhNkZ9 JjC4SO/CiB3Kz5GeuuTysAfwB7dqtUL05G2SfQQz4OgzBUOE3ut7IvAN8/8HyqXY6iqmxc1upo9 NxM5FSlCypKuhHBrTlJEzjyhZ9QjPa4MlnxilUB+ywss1KbSMxIOsfwWEYSdtDV7FB/R7rU24HN qGahwQ0r7xQLYzq/M/XVg= X-Google-Smtp-Source: AGHT+IEDS/Wv6FeLiLOblIhEKrEIohUpdRAcbphdwF92eUa/PzhXPL532kK1k4dFjYPN3NgyXgyDDQ== X-Received: by 2002:a05:6a00:4902:b0:7bb:272d:a4a9 with SMTP id d2e1a72fcca58-7bb272daffcmr11111658b3a.1.1763492849162; Tue, 18 Nov 2025 11:07:29 -0800 (PST) Received: from localhost ([116.86.198.140]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7b924aea0f8sm17405368b3a.5.2025.11.18.11.07.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 18 Nov 2025 11:07:28 -0800 (PST) Date: Wed, 19 Nov 2025 03:07:27 +0800 From: Chris Down To: Petr Mladek Cc: linux-kernel@vger.kernel.org, Greg Kroah-Hartman , Sergey Senozhatsky , Steven Rostedt , John Ogness , Geert Uytterhoeven , Tony Lindgren , kernel-team@fb.com Subject: [PATCH v7 07/13] printk: console: Introduce sysfs interface for per-console loglevels Message-ID: <464d918b5497f6c7463b96b8241a65a575d932e0.1763492585.git.chris@chrisdown.name> References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: User-Agent: Mutt/2.2.15 (2b349c5e) (2025-10-02) Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" A sysfs interface under /sys/class/console/ is created that permits viewing and configuring per-console attributes. This is the main interface with which we expect users to interact with and configure per-console loglevels. Each console device now has its own directory (for example, /sys/class/console/ttyS0/) containing the following attributes: - effective_loglevel (ro): The effective loglevel for the console after considering all loglevel authorities (e.g., global loglevel, per-console loglevel). - effective_loglevel_source (ro): The source of the effective loglevel (e.g., local, global, ignore_loglevel). - loglevel (rw): The per-console loglevel. Writing a value between 0 (KERN_EMERG) and 8 (KERN_DEBUG + 1) sets the per-console loglevel. Writing -1 disables the per-console loglevel. In terms of technical implementation, we embed a device pointer in the console struct, and register each console using it so we can expose attributes in sysfs. We currently expose the following attributes: % ls -l /sys/class/console/ttyS0/ total 0 lrwxrwxrwx 1 root root 0 Oct 23 13:17 subsystem -> ../../../../class= /console/ -r--r--r-- 1 root root 4096 Oct 23 13:18 effective_loglevel -r--r--r-- 1 root root 4096 Oct 23 13:18 effective_loglevel_source -rw-r--r-- 1 root root 4096 Oct 23 13:18 loglevel -rw-r--r-- 1 root root 4096 Oct 23 13:17 uevent The lifecycle of this classdev looks like this on registration: register_console(con)/printk_late_init() console_register_device(con) device_initialize(con->classdev) # kref_init: refcount =3D 1 device_add(con->classdev) # get_device: refcount++ (to 2) At stable state, the refcount is two. Console unregistration looks like this: unregister_console_locked(con) struct device *dev =3D console->classdev; console->classdev =3D NULL; device_unregister(dev) device_del(dev) device_remove_class_symlinks(dev) sysfs_delete_link() kernfs_remove_by_name_ns() __kernfs_remove() kernfs_drain() kernfs_drain_open_files() # wait for close() kobject_del(&dev->kobj) # removes from sysfs, does NOT change ref= count put_device(dev) # kref_put: refcount-- (from 2 to 1) put_device(dev) # kref_put: refcount-- (from 1 to 0) kobject_release() kobject_cleanup() device_release() console_classdev_release(dev) kfree(dev) Signed-off-by: Chris Down --- Documentation/ABI/testing/sysfs-class-console | 58 +++++ .../admin-guide/per-console-loglevel.rst | 38 ++++ Documentation/core-api/printk-basics.rst | 35 +-- Documentation/networking/netconsole.rst | 13 ++ MAINTAINERS | 1 + include/linux/console.h | 4 + kernel/printk/Makefile | 2 +- kernel/printk/internal.h | 6 + kernel/printk/printk.c | 14 ++ kernel/printk/sysfs.c | 213 ++++++++++++++++++ 10 files changed, 366 insertions(+), 18 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-class-console create mode 100644 kernel/printk/sysfs.c diff --git a/Documentation/ABI/testing/sysfs-class-console b/Documentation/= ABI/testing/sysfs-class-console new file mode 100644 index 000000000000..8c0f0cf3f6c5 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-console @@ -0,0 +1,58 @@ +What: /sys/class/console/ +Date: November 2025 +Contact: Chris Down +Description: Interface for viewing and setting per-console attributes, like + the per-console loglevel. For a high-level document describing + the motivations for this interface and related non-sysfs + controls, see + Documentation/admin-guide/per-console-loglevel.rst. + +What: /sys/class/console//effective_loglevel +Date: November 2025 +Contact: Chris Down +Permissions: 0444 (world readable) +Description: Read only. The currently effective loglevel for this console. + All messages emitted with a loglevel below the effective value + will be emitted to the console. + +What: /sys/class/console//effective_loglevel_source +Date: November 2025 +Contact: Chris Down +Permissions: 0444 (world readable) +Description: Read only. The currently effective loglevel source for this + console -- for example, whether it was set globally, or whether + it was set locally for this console. + + Possible values are: + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + local The loglevel comes from the console's + per-console loglevel setting. + global The loglevel comes from the global + console_loglevel. + ignore_loglevel Both the per-console loglevel and global + loglevel are ignored as ignore_loglevel is + present on the kernel command line. + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +What: /sys/class/console//loglevel +Date: November 2025 +Contact: Chris Down +Permissions: 0644 (root read/write, user read) +Description: Read write. The current per-console loglevel, which will take + effect if not overridden by other non-sysfs controls (see + Documentation/admin-guide/per-console-loglevel.rst). + + Valid values: + 1-8: LOGLEVEL_ALERT (1) to LOGLEVEL_DEBUG + 1 (8) + -1: Use global console_loglevel (default) + 0: Explicitly rejected (KERN_EMERG not allowed) + + Error codes: + EINVAL: Non-numeric input + ERANGE: Value out of valid range (< 1 or > 8, excluding -1) + ERANGE: Value is 0 (KERN_EMERG not allowed for per-console) + ERANGE: Value below system minimum_console_loglevel + + The special value -1 disables the per-console loglevel, making + the console use the global loglevel instead. + diff --git a/Documentation/admin-guide/per-console-loglevel.rst b/Documenta= tion/admin-guide/per-console-loglevel.rst index 1f8f1eabc75c..4908d5d8ed4f 100644 --- a/Documentation/admin-guide/per-console-loglevel.rst +++ b/Documentation/admin-guide/per-console-loglevel.rst @@ -69,3 +69,41 @@ The default value for ``kernel.console_loglevel`` comes = from ``CONFIG_CONSOLE_LOGLEVEL_DEFAULT``, or ``CONFIG_CONSOLE_LOGLEVEL_QUIET`` = if ``quiet`` is passed on the kernel command line. =20 +Console attributes +~~~~~~~~~~~~~~~~~~ + +Registered consoles are exposed at ``/sys/class/console``. For example, if= you +are using ``ttyS0``, the console backing it can be viewed at +``/sys/class/console/ttyS0/``. The following files are available: + +* ``effective_loglevel`` (r): The effective loglevel after considering all + loglevel authorities. For example, it shows the value of the console-spe= cific + loglevel when a console-specific loglevel is defined, and shows the glob= al + console loglevel value when the console-specific one is not defined. + +* ``effective_loglevel_source`` (r): The loglevel authority which resulted= in + the effective loglevel being set. The following values can be present: + + * ``local``: The console-specific loglevel is in effect. + + * ``global``: The global loglevel (``kernel.console_loglevel``) is in + effect. Set a console-specific loglevel to override it. + + * ``ignore_loglevel``: ``ignore_loglevel`` was specified on the kernel + command line or at ``/sys/module/printk/parameters/ignore_loglevel``. + Disable it to use level controls. + +* ``loglevel`` (rw): The local, console-specific loglevel for this console. + This will be in effect if no other global control overrides it. Look at + ``effective_loglevel`` and ``effective_loglevel_source`` to verify that. + +Deprecated +~~~~~~~~~~ + +* ``kernel.printk`` sysctl: this takes four values, setting + ``kernel.console_loglevel``, ``kernel.default_message_loglevel``, the mi= nimum + console loglevel, and a fourth unused value. The interface is generally + considered to quite confusing, doesn't perform checks on the values give= n, + and is unaware of per-console loglevel semantics. + +Chris Down , 18-November-2025 diff --git a/Documentation/core-api/printk-basics.rst b/Documentation/core-= api/printk-basics.rst index 2dde24ca7d9f..bfad359505bb 100644 --- a/Documentation/core-api/printk-basics.rst +++ b/Documentation/core-api/printk-basics.rst @@ -54,32 +54,33 @@ string, the log level is not a separate argument). The = available log levels are: =20 The log level specifies the importance of a message. The kernel decides wh= ether to show the message immediately (printing it to the current console) depen= ding -on its log level and the current *console_loglevel* (a kernel variable). I= f the -message priority is higher (lower log level value) than the *console_logle= vel* -the message will be printed to the console. +on its log level and the current global *console_loglevel* or local per-co= nsole +loglevel (kernel variables). If the message priority is higher (lower log = level +value) than the effective loglevel the message will be printed to the cons= ole. =20 If the log level is omitted, the message is printed with ``KERN_DEFAULT`` level. =20 -You can check the current *console_loglevel* with:: +You can check the current console's loglevel -- for example if you want to +check the loglevel for serial consoles: =20 - $ cat /proc/sys/kernel/printk - 4 4 1 7 + $ cat /sys/class/console/ttyS0/effective_loglevel + 6 + $ cat /sys/class/console/ttyS0/effective_loglevel_source + local =20 -The result shows the *current*, *default*, *minimum* and *boot-time-defaul= t* log -levels. +To change the default loglevel for all consoles, simply write the desired = level +to ``/proc/sys/kernel/console_loglevel``. For example:: =20 -To change the current console_loglevel simply write the desired level to -``/proc/sys/kernel/printk``. For example, to print all messages to the con= sole:: + # echo 5 > /proc/sys/kernel/console_loglevel =20 - # echo 8 > /proc/sys/kernel/printk +This sets the console_loglevel to print KERN_WARNING (4) or more severe +messages to console. Consoles with a per-console loglevel set will ignore = it +unless ``ignore_per_console_loglevel`` is set on the kernel command line o= r at +``/sys/module/printk/parameters/ignore_per_console_loglevel``. =20 -Another way, using ``dmesg``:: - - # dmesg -n 5 - -sets the console_loglevel to print KERN_WARNING (4) or more severe message= s to -console. See ``dmesg(1)`` for more information. +For more information on per-console loglevels, see +Documentation/admin-guide/per-console-loglevel.rst. =20 As an alternative to printk() you can use the ``pr_*()`` aliases for logging. This family of macros embed the log level in the macro names. For diff --git a/Documentation/networking/netconsole.rst b/Documentation/networ= king/netconsole.rst index 59cb9982afe6..5fabcf9367d1 100644 --- a/Documentation/networking/netconsole.rst +++ b/Documentation/networking/netconsole.rst @@ -78,6 +78,19 @@ Built-in netconsole starts immediately after the TCP sta= ck is initialized and attempts to bring up the supplied dev at the supplied address. =20 +You can also set a loglevel at runtime:: + + $ ls -l /sys/class/console/netcon0/ + total 0 + lrwxrwxrwx 1 root root 0 May 18 13:28 subsystem -> ../../../../class/= console/ + -r--r--r-- 1 root root 4096 May 18 13:28 effective_loglevel + -r--r--r-- 1 root root 4096 May 18 13:28 effective_loglevel_source + -r--r--r-- 1 root root 4096 May 18 13:28 enabled + -rw-r--r-- 1 root root 4096 May 18 13:28 loglevel + -rw-r--r-- 1 root root 4096 May 18 13:28 uevent + +See Documentation/admin-guide/per-console-loglevel.rst for more informatio= n. + The remote host has several options to receive the kernel messages, for example: =20 diff --git a/MAINTAINERS b/MAINTAINERS index 8018a4db2d9f..9a509a0bc65a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20505,6 +20505,7 @@ R: John Ogness R: Sergey Senozhatsky S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux.git +F: Documentation/ABI/testing/sysfs-class-console F: Documentation/admin-guide/per-console-loglevel.rst F: Documentation/core-api/printk-basics.rst F: include/linux/printk.h diff --git a/include/linux/console.h b/include/linux/console.h index c0749a48fc3f..4b2b87079cd8 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -16,6 +16,7 @@ =20 #include #include +#include #include #include #include @@ -323,6 +324,8 @@ struct nbcon_write_context { * @dropped: Number of unreported dropped ringbuffer records * @data: Driver private data * @node: hlist node for the console list + * @classdev: sysfs class device for this console, used to expose + * per-console controls in /sys/class/console// * * @nbcon_state: State for nbcon consoles * @nbcon_seq: Sequence number of the next record for nbcon to print @@ -352,6 +355,7 @@ struct console { unsigned long dropped; void *data; struct hlist_node node; + struct device *classdev; =20 /* nbcon console specific members */ =20 diff --git a/kernel/printk/Makefile b/kernel/printk/Makefile index f8004ac3983d..19e4919a13a7 100644 --- a/kernel/printk/Makefile +++ b/kernel/printk/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y =3D printk.o -obj-$(CONFIG_PRINTK) +=3D printk_safe.o nbcon.o +obj-$(CONFIG_PRINTK) +=3D sysfs.o printk_safe.o nbcon.o obj-$(CONFIG_A11Y_BRAILLE_CONSOLE) +=3D braille.o obj-$(CONFIG_PRINTK_INDEX) +=3D index.o =20 diff --git a/kernel/printk/internal.h b/kernel/printk/internal.h index 41e37b44778f..3b3a3c982412 100644 --- a/kernel/printk/internal.h +++ b/kernel/printk/internal.h @@ -48,6 +48,9 @@ int console_effective_loglevel(int con_level); =20 #ifdef CONFIG_PRINTK =20 +void console_register_device(struct console *new); +void console_setup_class(void); + #ifdef CONFIG_PRINTK_CALLER #define PRINTK_PREFIX_MAX 48 #else @@ -219,6 +222,9 @@ static inline void nbcon_kthreads_wake(void) { } static inline bool console_is_usable(struct console *con, short flags, bool use_atomic) { return false; } =20 +static inline void console_register_device(struct console *new) { } +static inline void console_setup_class(void) { } + #endif /* CONFIG_PRINTK */ =20 extern bool have_boot_console; diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 80204cbb7bc8..62114aa61999 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -4010,6 +4010,9 @@ static void try_enable_default_console(struct console= *newcon) if (newcon->index < 0) newcon->index =3D 0; =20 + newcon->level =3D LOGLEVEL_DEFAULT; + newcon->classdev =3D NULL; + if (console_call_setup(newcon, NULL) !=3D 0) return; =20 @@ -4264,6 +4267,7 @@ void register_console(struct console *newcon) if (use_device_lock) newcon->device_unlock(newcon, flags); =20 + console_register_device(newcon); console_sysfs_notify(); =20 /* @@ -4379,6 +4383,13 @@ static int unregister_console_locked(struct console = *console) if (console->flags & CON_NBCON) nbcon_free(console); =20 + if (console->classdev) { + struct device *dev =3D console->classdev; + console->classdev =3D NULL; + device_unregister(dev); + put_device(dev); + } + console_sysfs_notify(); =20 if (console->exit) @@ -4528,6 +4539,9 @@ static int __init printk_late_init(void) console_cpu_notify, NULL); WARN_ON(ret < 0); printk_sysctl_init(); + + console_setup_class(); + return 0; } late_initcall(printk_late_init); diff --git a/kernel/printk/sysfs.c b/kernel/printk/sysfs.c new file mode 100644 index 000000000000..5252e6e04908 --- /dev/null +++ b/kernel/printk/sysfs.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include "internal.h" + +static const char * +console_effective_loglevel_source_str(const struct console *con) +{ + enum loglevel_source source; + const char *str; + int con_level; + int cookie; + + cookie =3D console_srcu_read_lock(); + con_level =3D console_srcu_read_loglevel(con); + console_srcu_read_unlock(cookie); + source =3D console_effective_loglevel_source(con_level); + + switch (source) { + case LLS_IGNORE_LOGLEVEL: + str =3D "ignore_loglevel"; + break; + case LLS_LOCAL: + str =3D "local"; + break; + case LLS_GLOBAL: + str =3D "global"; + break; + default: + str =3D "unknown"; + break; + } + + return str; +} + +static ssize_t effective_loglevel_source_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct console *con =3D dev_get_drvdata(dev); + const char *str; + + str =3D console_effective_loglevel_source_str(con); + return sysfs_emit(buf, "%s\n", str); +} + +static DEVICE_ATTR_RO(effective_loglevel_source); + +static ssize_t effective_loglevel_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct console *con =3D dev_get_drvdata(dev); + int con_level; + int cookie; + + cookie =3D console_srcu_read_lock(); + con_level =3D console_srcu_read_loglevel(con); + console_srcu_read_unlock(cookie); + return sysfs_emit(buf, "%d\n", console_effective_loglevel(con_level)); +} + +static DEVICE_ATTR_RO(effective_loglevel); + +static ssize_t loglevel_show(struct device *dev, struct device_attribute *= attr, + char *buf) +{ + struct console *con =3D dev_get_drvdata(dev); + int con_level; + int cookie; + + cookie =3D console_srcu_read_lock(); + con_level =3D console_srcu_read_loglevel(con); + console_srcu_read_unlock(cookie); + return sysfs_emit(buf, "%d\n", con_level); +} + +static ssize_t loglevel_store(struct device *dev, struct device_attribute = *attr, + const char *buf, size_t size) +{ + struct console *con =3D dev_get_drvdata(dev); + ssize_t ret; + int level; + int cookie; + + ret =3D kstrtoint(buf, 10, &level); + if (ret < 0) + return ret; + + /* -1 means "use global loglevel" */ + if (level =3D=3D -1) + goto out; + + /* + * Reject level 0 (KERN_EMERG) - per-console loglevel must be > 0. + * Emergency messages should go to all consoles, so they cannot be + * filtered per-console. + */ + if (level =3D=3D 0) + return -ERANGE; + + if (console_clamp_loglevel(level) !=3D level) + return -ERANGE; + + /* + * If the system has a minimum console loglevel set (via sysctl or + * kernel parameter), enforce it. This prevents setting per-console + * loglevels below the system minimum. + */ + if (minimum_console_loglevel > CONSOLE_LOGLEVEL_MIN && + level < minimum_console_loglevel) + return -ERANGE; + +out: + cookie =3D console_srcu_read_lock(); + WRITE_ONCE(con->level, level); + console_srcu_read_unlock(cookie); + + return size; +} + +static DEVICE_ATTR_RW(loglevel); + +static struct attribute *console_sysfs_attrs[] =3D { + &dev_attr_loglevel.attr, + &dev_attr_effective_loglevel_source.attr, + &dev_attr_effective_loglevel.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(console_sysfs); + +static const struct class console_class =3D { + .name =3D "console", + .dev_groups =3D console_sysfs_groups, +}; +static bool console_class_registered; + +static void console_classdev_release(struct device *dev) +{ + kfree(dev); +} + +void console_register_device(struct console *con) +{ + /* + * We might be called from register_console() before the class is + * registered. If that happens, we'll take care of it in + * printk_late_init. + */ + if (!console_class_registered) + return; + + if (WARN_ON(con->classdev)) + return; + + con->classdev =3D kzalloc(sizeof(struct device), GFP_KERNEL); + if (!con->classdev) + return; + + device_initialize(con->classdev); + dev_set_name(con->classdev, "%s%d", con->name, con->index); + dev_set_drvdata(con->classdev, con); + con->classdev->release =3D console_classdev_release; + con->classdev->class =3D &console_class; + + /* + * This class device exists solely to expose attributes (like loglevel) + * and does not control physical power states. Power is managed by the + * underlying hardware device. Disable PM entirely to prevent the + * creation of confusing and unused power sysfs attributes. + */ + device_set_pm_not_required(con->classdev); + + if (device_add(con->classdev)) { + put_device(con->classdev); + con->classdev =3D NULL; + } +} + +void console_setup_class(void) +{ + struct console *con; + int cookie; + int err; + + /* + * printk exists for the lifetime of the kernel, it cannot be unloaded, + * so we should never end up back in here. + */ + if (WARN_ON(console_class_registered)) + return; + + err =3D class_register(&console_class); + if (err) + return; + + /* + * Take console_list_lock() before exposing the class globally. + * This ensures register_console() (which holds the lock) cannot + * see the class until it's fully initialized with dev_groups. + */ + console_list_lock(); + console_class_registered =3D true; + cookie =3D console_srcu_read_lock(); + for_each_console_srcu(con) + console_register_device(con); + console_srcu_read_unlock(cookie); + console_list_unlock(); +} --=20 2.51.2