diff options
author | 2023-02-21 18:24:12 -0800 | |
---|---|---|
committer | 2023-02-21 18:24:12 -0800 | |
commit | 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch) | |
tree | cc5c2d0a898769fd59549594fedb3ee6f84e59a0 /arch/um/drivers/line.c | |
download | linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip |
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
Pull networking updates from Jakub Kicinski:
"Core:
- Add dedicated kmem_cache for typical/small skb->head, avoid having
to access struct page at kfree time, and improve memory use.
- Introduce sysctl to set default RPS configuration for new netdevs.
- Define Netlink protocol specification format which can be used to
describe messages used by each family and auto-generate parsers.
Add tools for generating kernel data structures and uAPI headers.
- Expose all net/core sysctls inside netns.
- Remove 4s sleep in netpoll if carrier is instantly detected on
boot.
- Add configurable limit of MDB entries per port, and port-vlan.
- Continue populating drop reasons throughout the stack.
- Retire a handful of legacy Qdiscs and classifiers.
Protocols:
- Support IPv4 big TCP (TSO frames larger than 64kB).
- Add IP_LOCAL_PORT_RANGE socket option, to control local port range
on socket by socket basis.
- Track and report in procfs number of MPTCP sockets used.
- Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path
manager.
- IPv6: don't check net.ipv6.route.max_size and rely on garbage
collection to free memory (similarly to IPv4).
- Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986).
- ICMP: add per-rate limit counters.
- Add support for user scanning requests in ieee802154.
- Remove static WEP support.
- Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate
reporting.
- WiFi 7 EHT channel puncturing support (client & AP).
BPF:
- Add a rbtree data structure following the "next-gen data structure"
precedent set by recently added linked list, that is, by using
kfunc + kptr instead of adding a new BPF map type.
- Expose XDP hints via kfuncs with initial support for RX hash and
timestamp metadata.
- Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to
better support decap on GRE tunnel devices not operating in collect
metadata.
- Improve x86 JIT's codegen for PROBE_MEM runtime error checks.
- Remove the need for trace_printk_lock for bpf_trace_printk and
bpf_trace_vprintk helpers.
- Extend libbpf's bpf_tracing.h support for tracing arguments of
kprobes/uprobes and syscall as a special case.
- Significantly reduce the search time for module symbols by
livepatch and BPF.
- Enable cpumasks to be used as kptrs, which is useful for tracing
programs tracking which tasks end up running on which CPUs in
different time intervals.
- Add support for BPF trampoline on s390x and riscv64.
- Add capability to export the XDP features supported by the NIC.
- Add __bpf_kfunc tag for marking kernel functions as kfuncs.
- Add cgroup.memory=nobpf kernel parameter option to disable BPF
memory accounting for container environments.
Netfilter:
- Remove the CLUSTERIP target. It has been marked as obsolete for
years, and we still have WARN splats wrt races of the out-of-band
/proc interface installed by this target.
- Add 'destroy' commands to nf_tables. They are identical to the
existing 'delete' commands, but do not return an error if the
referenced object (set, chain, rule...) did not exist.
Driver API:
- Improve cpumask_local_spread() locality to help NICs set the right
IRQ affinity on AMD platforms.
- Separate C22 and C45 MDIO bus transactions more clearly.
- Introduce new DCB table to control DSCP rewrite on egress.
- Support configuration of Physical Layer Collision Avoidance (PLCA)
Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of
shared medium Ethernet.
- Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing
preemption of low priority frames by high priority frames.
- Add support for controlling MACSec offload using netlink SET.
- Rework devlink instance refcounts to allow registration and
de-registration under the instance lock. Split the code into
multiple files, drop some of the unnecessarily granular locks and
factor out common parts of netlink operation handling.
- Add TX frame aggregation parameters (for USB drivers).
- Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning
messages with notifications for debug.
- Allow offloading of UDP NEW connections via act_ct.
- Add support for per action HW stats in TC.
- Support hardware miss to TC action (continue processing in SW from
a specific point in the action chain).
- Warn if old Wireless Extension user space interface is used with
modern cfg80211/mac80211 drivers. Do not support Wireless
Extensions for Wi-Fi 7 devices at all. Everyone should switch to
using nl80211 interface instead.
- Improve the CAN bit timing configuration. Use extack to return
error messages directly to user space, update the SJW handling,
including the definition of a new default value that will benefit
CAN-FD controllers, by increasing their oscillator tolerance.
New hardware / drivers:
- Ethernet:
- nVidia BlueField-3 support (control traffic driver)
- Ethernet support for imx93 SoCs
- Motorcomm yt8531 gigabit Ethernet PHY
- onsemi NCN26000 10BASE-T1S PHY (with support for PLCA)
- Microchip LAN8841 PHY (incl. cable diagnostics and PTP)
- Amlogic gxl MDIO mux
- WiFi:
- RealTek RTL8188EU (rtl8xxxu)
- Qualcomm Wi-Fi 7 devices (ath12k)
- CAN:
- Renesas R-Car V4H
Drivers:
- Bluetooth:
- Set Per Platform Antenna Gain (PPAG) for Intel controllers.
- Ethernet NICs:
- Intel (1G, igc):
- support TSN / Qbv / packet scheduling features of i226 model
- Intel (100G, ice):
- use GNSS subsystem instead of TTY
- multi-buffer XDP support
- extend support for GPIO pins to E823 devices
- nVidia/Mellanox:
- update the shared buffer configuration on PFC commands
- implement PTP adjphase function for HW offset control
- TC support for Geneve and GRE with VF tunnel offload
- more efficient crypto key management method
- multi-port eswitch support
- Netronome/Corigine:
- add DCB IEEE support
- support IPsec offloading for NFP3800
- Freescale/NXP (enetc):
- support XDP_REDIRECT for XDP non-linear buffers
- improve reconfig, avoid link flap and waiting for idle
- support MAC Merge layer
- Other NICs:
- sfc/ef100: add basic devlink support for ef100
- ionic: rx_push mode operation (writing descriptors via MMIO)
- bnxt: use the auxiliary bus abstraction for RDMA
- r8169: disable ASPM and reset bus in case of tx timeout
- cpsw: support QSGMII mode for J721e CPSW9G
- cpts: support pulse-per-second output
- ngbe: add an mdio bus driver
- usbnet: optimize usbnet_bh() by avoiding unnecessary queuing
- r8152: handle devices with FW with NCM support
- amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation
- virtio-net: support multi buffer XDP
- virtio/vsock: replace virtio_vsock_pkt with sk_buff
- tsnep: XDP support
- Ethernet high-speed switches:
- nVidia/Mellanox (mlxsw):
- add support for latency TLV (in FW control messages)
- Microchip (sparx5):
- separate explicit and implicit traffic forwarding rules, make
the implicit rules always active
- add support for egress DSCP rewrite
- IS0 VCAP support (Ingress Classification)
- IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS
etc.)
- ES2 VCAP support (Egress Access Control)
- support for Per-Stream Filtering and Policing (802.1Q,
8.6.5.1)
- Ethernet embedded switches:
- Marvell (mv88e6xxx):
- add MAB (port auth) offload support
- enable PTP receive for mv88e6390
- NXP (ocelot):
- support MAC Merge layer
- support for the the vsc7512 internal copper phys
- Microchip:
- lan9303: convert to PHYLINK
- lan966x: support TC flower filter statistics
- lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x
- lan937x: support Credit Based Shaper configuration
- ksz9477: support Energy Efficient Ethernet
- other:
- qca8k: convert to regmap read/write API, use bulk operations
- rswitch: Improve TX timestamp accuracy
- Intel WiFi (iwlwifi):
- EHT (Wi-Fi 7) rate reporting
- STEP equalizer support: transfer some STEP (connection to radio
on platforms with integrated wifi) related parameters from the
BIOS to the firmware.
- Qualcomm 802.11ax WiFi (ath11k):
- IPQ5018 support
- Fine Timing Measurement (FTM) responder role support
- channel 177 support
- MediaTek WiFi (mt76):
- per-PHY LED support
- mt7996: EHT (Wi-Fi 7) support
- Wireless Ethernet Dispatch (WED) reset support
- switch to using page pool allocator
- RealTek WiFi (rtw89):
- support new version of Bluetooth co-existance
- Mobile:
- rmnet: support TX aggregation"
* tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits)
page_pool: add a comment explaining the fragment counter usage
net: ethtool: fix __ethtool_dev_mm_supported() implementation
ethtool: pse-pd: Fix double word in comments
xsk: add linux/vmalloc.h to xsk.c
sefltests: netdevsim: wait for devlink instance after netns removal
selftest: fib_tests: Always cleanup before exit
net/mlx5e: Align IPsec ASO result memory to be as required by hardware
net/mlx5e: TC, Set CT miss to the specific ct action instance
net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG
net/mlx5: Refactor tc miss handling to a single function
net/mlx5: Kconfig: Make tc offload depend on tc skb extension
net/sched: flower: Support hardware miss to tc action
net/sched: flower: Move filter handle initialization earlier
net/sched: cls_api: Support hardware miss to tc action
net/sched: Rename user cookie and act cookie
sfc: fix builds without CONFIG_RTC_LIB
sfc: clean up some inconsistent indentings
net/mlx4_en: Introduce flexible array to silence overflow warning
net: lan966x: Fix possible deadlock inside PTP
net/ulp: Remove redundant ->clone() test in inet_clone_ulp().
...
Diffstat (limited to '')
-rw-r--r-- | arch/um/drivers/line.c | 764 |
1 files changed, 764 insertions, 0 deletions
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c new file mode 100644 index 000000000..02b0befd6 --- /dev/null +++ b/arch/um/drivers/line.c @@ -0,0 +1,764 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) + */ + +#include <linux/irqreturn.h> +#include <linux/kd.h> +#include <linux/sched/signal.h> +#include <linux/slab.h> + +#include "chan.h" +#include <irq_kern.h> +#include <irq_user.h> +#include <kern_util.h> +#include <os.h> + +#define LINE_BUFSIZE 4096 + +static irqreturn_t line_interrupt(int irq, void *data) +{ + struct chan *chan = data; + struct line *line = chan->line; + + if (line) + chan_interrupt(line, irq); + + return IRQ_HANDLED; +} + +/* + * Returns the free space inside the ring buffer of this line. + * + * Should be called while holding line->lock (this does not modify data). + */ +static unsigned int write_room(struct line *line) +{ + int n; + + if (line->buffer == NULL) + return LINE_BUFSIZE - 1; + + /* This is for the case where the buffer is wrapped! */ + n = line->head - line->tail; + + if (n <= 0) + n += LINE_BUFSIZE; /* The other case */ + return n - 1; +} + +unsigned int line_write_room(struct tty_struct *tty) +{ + struct line *line = tty->driver_data; + unsigned long flags; + unsigned int room; + + spin_lock_irqsave(&line->lock, flags); + room = write_room(line); + spin_unlock_irqrestore(&line->lock, flags); + + return room; +} + +unsigned int line_chars_in_buffer(struct tty_struct *tty) +{ + struct line *line = tty->driver_data; + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&line->lock, flags); + /* write_room subtracts 1 for the needed NULL, so we readd it.*/ + ret = LINE_BUFSIZE - (write_room(line) + 1); + spin_unlock_irqrestore(&line->lock, flags); + + return ret; +} + +/* + * This copies the content of buf into the circular buffer associated with + * this line. + * The return value is the number of characters actually copied, i.e. the ones + * for which there was space: this function is not supposed to ever flush out + * the circular buffer. + * + * Must be called while holding line->lock! + */ +static int buffer_data(struct line *line, const char *buf, int len) +{ + int end, room; + + if (line->buffer == NULL) { + line->buffer = kmalloc(LINE_BUFSIZE, GFP_ATOMIC); + if (line->buffer == NULL) { + printk(KERN_ERR "buffer_data - atomic allocation " + "failed\n"); + return 0; + } + line->head = line->buffer; + line->tail = line->buffer; + } + + room = write_room(line); + len = (len > room) ? room : len; + + end = line->buffer + LINE_BUFSIZE - line->tail; + + if (len < end) { + memcpy(line->tail, buf, len); + line->tail += len; + } + else { + /* The circular buffer is wrapping */ + memcpy(line->tail, buf, end); + buf += end; + memcpy(line->buffer, buf, len - end); + line->tail = line->buffer + len - end; + } + + return len; +} + +/* + * Flushes the ring buffer to the output channels. That is, write_chan is + * called, passing it line->head as buffer, and an appropriate count. + * + * On exit, returns 1 when the buffer is empty, + * 0 when the buffer is not empty on exit, + * and -errno when an error occurred. + * + * Must be called while holding line->lock!*/ +static int flush_buffer(struct line *line) +{ + int n, count; + + if ((line->buffer == NULL) || (line->head == line->tail)) + return 1; + + if (line->tail < line->head) { + /* line->buffer + LINE_BUFSIZE is the end of the buffer! */ + count = line->buffer + LINE_BUFSIZE - line->head; + + n = write_chan(line->chan_out, line->head, count, + line->write_irq); + if (n < 0) + return n; + if (n == count) { + /* + * We have flushed from ->head to buffer end, now we + * must flush only from the beginning to ->tail. + */ + line->head = line->buffer; + } else { + line->head += n; + return 0; + } + } + + count = line->tail - line->head; + n = write_chan(line->chan_out, line->head, count, + line->write_irq); + + if (n < 0) + return n; + + line->head += n; + return line->head == line->tail; +} + +void line_flush_buffer(struct tty_struct *tty) +{ + struct line *line = tty->driver_data; + unsigned long flags; + + spin_lock_irqsave(&line->lock, flags); + flush_buffer(line); + spin_unlock_irqrestore(&line->lock, flags); +} + +/* + * We map both ->flush_chars and ->put_char (which go in pair) onto + * ->flush_buffer and ->write. Hope it's not that bad. + */ +void line_flush_chars(struct tty_struct *tty) +{ + line_flush_buffer(tty); +} + +int line_write(struct tty_struct *tty, const unsigned char *buf, int len) +{ + struct line *line = tty->driver_data; + unsigned long flags; + int n, ret = 0; + + spin_lock_irqsave(&line->lock, flags); + if (line->head != line->tail) + ret = buffer_data(line, buf, len); + else { + n = write_chan(line->chan_out, buf, len, + line->write_irq); + if (n < 0) { + ret = n; + goto out_up; + } + + len -= n; + ret += n; + if (len > 0) + ret += buffer_data(line, buf + n, len); + } +out_up: + spin_unlock_irqrestore(&line->lock, flags); + return ret; +} + +void line_throttle(struct tty_struct *tty) +{ + struct line *line = tty->driver_data; + + deactivate_chan(line->chan_in, line->read_irq); + line->throttled = 1; +} + +void line_unthrottle(struct tty_struct *tty) +{ + struct line *line = tty->driver_data; + + line->throttled = 0; + chan_interrupt(line, line->read_irq); +} + +static irqreturn_t line_write_interrupt(int irq, void *data) +{ + struct chan *chan = data; + struct line *line = chan->line; + int err; + + /* + * Interrupts are disabled here because genirq keep irqs disabled when + * calling the action handler. + */ + + spin_lock(&line->lock); + err = flush_buffer(line); + if (err == 0) { + spin_unlock(&line->lock); + return IRQ_NONE; + } else if ((err < 0) && (err != -EAGAIN)) { + line->head = line->buffer; + line->tail = line->buffer; + } + spin_unlock(&line->lock); + + tty_port_tty_wakeup(&line->port); + + return IRQ_HANDLED; +} + +int line_setup_irq(int fd, int input, int output, struct line *line, void *data) +{ + const struct line_driver *driver = line->driver; + int err; + + if (input) { + err = um_request_irq(UM_IRQ_ALLOC, fd, IRQ_READ, + line_interrupt, 0, + driver->read_irq_name, data); + if (err < 0) + return err; + + line->read_irq = err; + } + + if (output) { + err = um_request_irq(UM_IRQ_ALLOC, fd, IRQ_WRITE, + line_write_interrupt, 0, + driver->write_irq_name, data); + if (err < 0) + return err; + + line->write_irq = err; + } + + return 0; +} + +static int line_activate(struct tty_port *port, struct tty_struct *tty) +{ + int ret; + struct line *line = tty->driver_data; + + ret = enable_chan(line); + if (ret) + return ret; + + if (!line->sigio) { + chan_enable_winch(line->chan_out, port); + line->sigio = 1; + } + + chan_window_size(line, &tty->winsize.ws_row, + &tty->winsize.ws_col); + + return 0; +} + +static void unregister_winch(struct tty_struct *tty); + +static void line_destruct(struct tty_port *port) +{ + struct tty_struct *tty = tty_port_tty_get(port); + struct line *line = tty->driver_data; + + if (line->sigio) { + unregister_winch(tty); + line->sigio = 0; + } +} + +static const struct tty_port_operations line_port_ops = { + .activate = line_activate, + .destruct = line_destruct, +}; + +int line_open(struct tty_struct *tty, struct file *filp) +{ + struct line *line = tty->driver_data; + + return tty_port_open(&line->port, tty, filp); +} + +int line_install(struct tty_driver *driver, struct tty_struct *tty, + struct line *line) +{ + int ret; + + ret = tty_standard_install(driver, tty); + if (ret) + return ret; + + tty->driver_data = line; + + return 0; +} + +void line_close(struct tty_struct *tty, struct file * filp) +{ + struct line *line = tty->driver_data; + + tty_port_close(&line->port, tty, filp); +} + +void line_hangup(struct tty_struct *tty) +{ + struct line *line = tty->driver_data; + + tty_port_hangup(&line->port); +} + +void close_lines(struct line *lines, int nlines) +{ + int i; + + for(i = 0; i < nlines; i++) + close_chan(&lines[i]); +} + +int setup_one_line(struct line *lines, int n, char *init, + const struct chan_opts *opts, char **error_out) +{ + struct line *line = &lines[n]; + struct tty_driver *driver = line->driver->driver; + int err = -EINVAL; + + if (line->port.count) { + *error_out = "Device is already open"; + goto out; + } + + if (!strcmp(init, "none")) { + if (line->valid) { + line->valid = 0; + kfree(line->init_str); + tty_unregister_device(driver, n); + parse_chan_pair(NULL, line, n, opts, error_out); + err = 0; + } + } else { + char *new = kstrdup(init, GFP_KERNEL); + if (!new) { + *error_out = "Failed to allocate memory"; + return -ENOMEM; + } + if (line->valid) { + tty_unregister_device(driver, n); + kfree(line->init_str); + } + line->init_str = new; + line->valid = 1; + err = parse_chan_pair(new, line, n, opts, error_out); + if (!err) { + struct device *d = tty_port_register_device(&line->port, + driver, n, NULL); + if (IS_ERR(d)) { + *error_out = "Failed to register device"; + err = PTR_ERR(d); + parse_chan_pair(NULL, line, n, opts, error_out); + } + } + if (err) { + line->init_str = NULL; + line->valid = 0; + kfree(new); + } + } +out: + return err; +} + +/* + * Common setup code for both startup command line and mconsole initialization. + * @lines contains the array (of size @num) to modify; + * @init is the setup string; + * @error_out is an error string in the case of failure; + */ + +int line_setup(char **conf, unsigned int num, char **def, + char *init, char *name) +{ + char *error; + + if (*init == '=') { + /* + * We said con=/ssl= instead of con#=, so we are configuring all + * consoles at once. + */ + *def = init + 1; + } else { + char *end; + unsigned n = simple_strtoul(init, &end, 0); + + if (*end != '=') { + error = "Couldn't parse device number"; + goto out; + } + if (n >= num) { + error = "Device number out of range"; + goto out; + } + conf[n] = end + 1; + } + return 0; + +out: + printk(KERN_ERR "Failed to set up %s with " + "configuration string \"%s\" : %s\n", name, init, error); + return -EINVAL; +} + +int line_config(struct line *lines, unsigned int num, char *str, + const struct chan_opts *opts, char **error_out) +{ + char *end; + int n; + + if (*str == '=') { + *error_out = "Can't configure all devices from mconsole"; + return -EINVAL; + } + + n = simple_strtoul(str, &end, 0); + if (*end++ != '=') { + *error_out = "Couldn't parse device number"; + return -EINVAL; + } + if (n >= num) { + *error_out = "Device number out of range"; + return -EINVAL; + } + + return setup_one_line(lines, n, end, opts, error_out); +} + +int line_get_config(char *name, struct line *lines, unsigned int num, char *str, + int size, char **error_out) +{ + struct line *line; + char *end; + int dev, n = 0; + + dev = simple_strtoul(name, &end, 0); + if ((*end != '\0') || (end == name)) { + *error_out = "line_get_config failed to parse device number"; + return 0; + } + + if ((dev < 0) || (dev >= num)) { + *error_out = "device number out of range"; + return 0; + } + + line = &lines[dev]; + + if (!line->valid) + CONFIG_CHUNK(str, size, n, "none", 1); + else { + struct tty_struct *tty = tty_port_tty_get(&line->port); + if (tty == NULL) { + CONFIG_CHUNK(str, size, n, line->init_str, 1); + } else { + n = chan_config_string(line, str, size, error_out); + tty_kref_put(tty); + } + } + + return n; +} + +int line_id(char **str, int *start_out, int *end_out) +{ + char *end; + int n; + + n = simple_strtoul(*str, &end, 0); + if ((*end != '\0') || (end == *str)) + return -1; + + *str = end; + *start_out = n; + *end_out = n; + return n; +} + +int line_remove(struct line *lines, unsigned int num, int n, char **error_out) +{ + if (n >= num) { + *error_out = "Device number out of range"; + return -EINVAL; + } + return setup_one_line(lines, n, "none", NULL, error_out); +} + +int register_lines(struct line_driver *line_driver, + const struct tty_operations *ops, + struct line *lines, int nlines) +{ + struct tty_driver *driver; + int err; + int i; + + driver = tty_alloc_driver(nlines, TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV); + if (IS_ERR(driver)) + return PTR_ERR(driver); + + driver->driver_name = line_driver->name; + driver->name = line_driver->device_name; + driver->major = line_driver->major; + driver->minor_start = line_driver->minor_start; + driver->type = line_driver->type; + driver->subtype = line_driver->subtype; + driver->init_termios = tty_std_termios; + + for (i = 0; i < nlines; i++) { + tty_port_init(&lines[i].port); + lines[i].port.ops = &line_port_ops; + spin_lock_init(&lines[i].lock); + lines[i].driver = line_driver; + INIT_LIST_HEAD(&lines[i].chan_list); + } + tty_set_operations(driver, ops); + + err = tty_register_driver(driver); + if (err) { + printk(KERN_ERR "register_lines : can't register %s driver\n", + line_driver->name); + tty_driver_kref_put(driver); + for (i = 0; i < nlines; i++) + tty_port_destroy(&lines[i].port); + return err; + } + + line_driver->driver = driver; + mconsole_register_dev(&line_driver->mc); + return 0; +} + +static DEFINE_SPINLOCK(winch_handler_lock); +static LIST_HEAD(winch_handlers); + +struct winch { + struct list_head list; + int fd; + int tty_fd; + int pid; + struct tty_port *port; + unsigned long stack; + struct work_struct work; +}; + +static void __free_winch(struct work_struct *work) +{ + struct winch *winch = container_of(work, struct winch, work); + um_free_irq(WINCH_IRQ, winch); + + if (winch->pid != -1) + os_kill_process(winch->pid, 1); + if (winch->stack != 0) + free_stack(winch->stack, 0); + kfree(winch); +} + +static void free_winch(struct winch *winch) +{ + int fd = winch->fd; + winch->fd = -1; + if (fd != -1) + os_close_file(fd); + __free_winch(&winch->work); +} + +static irqreturn_t winch_interrupt(int irq, void *data) +{ + struct winch *winch = data; + struct tty_struct *tty; + struct line *line; + int fd = winch->fd; + int err; + char c; + struct pid *pgrp; + + if (fd != -1) { + err = generic_read(fd, &c, NULL); + if (err < 0) { + if (err != -EAGAIN) { + winch->fd = -1; + list_del(&winch->list); + os_close_file(fd); + printk(KERN_ERR "winch_interrupt : " + "read failed, errno = %d\n", -err); + printk(KERN_ERR "fd %d is losing SIGWINCH " + "support\n", winch->tty_fd); + INIT_WORK(&winch->work, __free_winch); + schedule_work(&winch->work); + return IRQ_HANDLED; + } + goto out; + } + } + tty = tty_port_tty_get(winch->port); + if (tty != NULL) { + line = tty->driver_data; + if (line != NULL) { + chan_window_size(line, &tty->winsize.ws_row, + &tty->winsize.ws_col); + pgrp = tty_get_pgrp(tty); + if (pgrp) + kill_pgrp(pgrp, SIGWINCH, 1); + put_pid(pgrp); + } + tty_kref_put(tty); + } + out: + return IRQ_HANDLED; +} + +void register_winch_irq(int fd, int tty_fd, int pid, struct tty_port *port, + unsigned long stack) +{ + struct winch *winch; + + winch = kmalloc(sizeof(*winch), GFP_KERNEL); + if (winch == NULL) { + printk(KERN_ERR "register_winch_irq - kmalloc failed\n"); + goto cleanup; + } + + *winch = ((struct winch) { .list = LIST_HEAD_INIT(winch->list), + .fd = fd, + .tty_fd = tty_fd, + .pid = pid, + .port = port, + .stack = stack }); + + if (um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt, + IRQF_SHARED, "winch", winch) < 0) { + printk(KERN_ERR "register_winch_irq - failed to register " + "IRQ\n"); + goto out_free; + } + + spin_lock(&winch_handler_lock); + list_add(&winch->list, &winch_handlers); + spin_unlock(&winch_handler_lock); + + return; + + out_free: + kfree(winch); + cleanup: + os_kill_process(pid, 1); + os_close_file(fd); + if (stack != 0) + free_stack(stack, 0); +} + +static void unregister_winch(struct tty_struct *tty) +{ + struct list_head *ele, *next; + struct winch *winch; + struct tty_struct *wtty; + + spin_lock(&winch_handler_lock); + + list_for_each_safe(ele, next, &winch_handlers) { + winch = list_entry(ele, struct winch, list); + wtty = tty_port_tty_get(winch->port); + if (wtty == tty) { + list_del(&winch->list); + spin_unlock(&winch_handler_lock); + free_winch(winch); + break; + } + tty_kref_put(wtty); + } + spin_unlock(&winch_handler_lock); +} + +static void winch_cleanup(void) +{ + struct winch *winch; + + spin_lock(&winch_handler_lock); + while ((winch = list_first_entry_or_null(&winch_handlers, + struct winch, list))) { + list_del(&winch->list); + spin_unlock(&winch_handler_lock); + + free_winch(winch); + + spin_lock(&winch_handler_lock); + } + + spin_unlock(&winch_handler_lock); +} +__uml_exitcall(winch_cleanup); + +char *add_xterm_umid(char *base) +{ + char *umid, *title; + int len; + + umid = get_umid(); + if (*umid == '\0') + return base; + + len = strlen(base) + strlen(" ()") + strlen(umid) + 1; + title = kmalloc(len, GFP_KERNEL); + if (title == NULL) { + printk(KERN_ERR "Failed to allocate buffer for xterm title\n"); + return base; + } + + snprintf(title, len, "%s (%s)", base, umid); + return title; +} |