Commit 95a639d1 authored by Johan Hovold's avatar Johan Hovold Committed by Sasha Levin

Revert "tty_port: register tty ports with serdev bus"

[ Upstream commit d3ba126a ]

This reverts commit 8ee3fde0.

The new serdev bus hooked into the tty layer in
tty_port_register_device() by registering a serdev controller instead of
a tty device whenever a serdev client is present, and by deregistering
the controller in the tty-port destructor. This is broken in several
ways:

Firstly, it leads to a NULL-pointer dereference whenever a tty driver
later deregisters its devices as no corresponding character device will
exist.

Secondly, far from every tty driver uses tty-port refcounting (e.g.
serial core) so the serdev devices might never be deregistered or
deallocated.

Thirdly, deregistering at tty-port destruction is too late as the
underlying device and structures may be long gone by then. A port is not
released before an open tty device is closed, something which a
registered serdev client can prevent from ever happening. A driver
callback while the device is gone typically also leads to crashes.

Many tty drivers even keep their ports around until the driver is
unloaded (e.g. serial core), something which even if a late callback
never happens, leads to leaks if a device is unbound from its driver and
is later rebound.

The right solution here is to add a new tty_port_unregister_device()
helper and to never call tty_device_unregister() whenever the port has
been claimed by serdev, but since this requires modifying just about
every tty driver (and multiple subsystems) it will need to be done
incrementally.

Reverting the offending patch is the first step in fixing the broken
lifetime assumptions. A follow-up patch will add a new pair of
tty-device registration helpers, which a vetted tty driver can use to
support serdev (initially serial core). When every tty driver uses the
serdev helpers (at least for deregistration), we can add serdev
registration to tty_port_register_device() again.

Note that this also fixes another issue with serdev, which currently
allocates and registers a serdev controller for every tty device
registered using tty_port_device_register() only to immediately
deregister and deallocate it when the corresponding OF node or serdev
child node is missing. This should be addressed before enabling serdev
for hot-pluggable buses.
Signed-off-by: default avatarJohan Hovold <johan@kernel.org>
Reviewed-by: default avatarRob Herring <robh@kernel.org>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarSasha Levin <alexander.levin@verizon.com>
parent 1520f7e7
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/serdev.h>
void tty_port_init(struct tty_port *port) void tty_port_init(struct tty_port *port)
{ {
...@@ -92,15 +91,7 @@ struct device *tty_port_register_device_attr(struct tty_port *port, ...@@ -92,15 +91,7 @@ struct device *tty_port_register_device_attr(struct tty_port *port,
struct device *device, void *drvdata, struct device *device, void *drvdata,
const struct attribute_group **attr_grp) const struct attribute_group **attr_grp)
{ {
struct device *dev;
tty_port_link_device(port, driver, index); tty_port_link_device(port, driver, index);
dev = serdev_tty_port_register(port, device, driver, index);
if (PTR_ERR(dev) != -ENODEV)
/* Skip creating cdev if we registered a serdev device */
return dev;
return tty_register_device_attr(driver, index, device, drvdata, return tty_register_device_attr(driver, index, device, drvdata,
attr_grp); attr_grp);
} }
...@@ -152,9 +143,6 @@ static void tty_port_destructor(struct kref *kref) ...@@ -152,9 +143,6 @@ static void tty_port_destructor(struct kref *kref)
/* check if last port ref was dropped before tty release */ /* check if last port ref was dropped before tty release */
if (WARN_ON(port->itty)) if (WARN_ON(port->itty))
return; return;
serdev_tty_port_unregister(port);
if (port->xmit_buf) if (port->xmit_buf)
free_page((unsigned long)port->xmit_buf); free_page((unsigned long)port->xmit_buf);
tty_port_destroy(port); tty_port_destroy(port);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment