mirror of
https://github.com/LineageOS/android_kernel_fxtec_sm6115.git
synced 2026-04-03 00:03:15 +00:00
version 4.19.325-cip127 * tag 'v4.19.325-cip127' of https://git.kernel.org/pub/scm/linux/kernel/git/cip/linux-cip: CIP: Bump version suffix to -cip127 after merge from cip/linux-4.19.y-st tree Update localversion-st, tree is up-to-date with 5.10.247. usb: uas: fix urb unmapping issue when the uas device is remove during ongoing data transfer scsi: pm80xx: Set phy->enable_completion only when we drm/amd/display: Check NULL before accessing wifi: ath10k: Fix connection after GTK rekeying pinctrl: single: fix bias pull up/down handling in pin_config_set ovl: fix UAF in ovl_dentry_update_reval by moving dput() in ovl_link_up fs: writeback: fix use-after-free in __mark_inode_dirty() libceph: fix potential use-after-free in have_mon_and_osd_map() drm: sti: fix device leaks at component probe USB: serial: option: add support for Rolling RW101R-GL USB: serial: ftdi_sio: add support for u-blox EVK-M101 usb: storage: sddr55: Reject out-of-bound new_pba USB: storage: Remove subclass and protocol overrides from Novatek quirk usb: storage: Fix memory leak in USB bulk transport usb: gadget: f_eem: Fix memory leak in eem_unwrap slimbus: ngd: Fix reference count leak in qcom_slim_ngd_notify_slaves dm-verity: fix unreliable memory allocation can: sun4i_can: sun4i_can_interrupt(): fix max irq loop handling can: sja1000: fix max irq loop handling atm/fore200e: Fix possible data race in fore200e_open() MIPS: mm: Prevent a TLB shutdown on initial uniquification iio:common:ssp_sensors: Fix an error handling path ssp_probe() spi: bcm63xx: fix premature CS deassertion on RX-only transactions net: sxgbe: fix potential NULL dereference in sxgbe_rx() can: kvaser_usb: leaf: Fix potential infinite loop in command parsers mtd: onenand: Pass correct pointer to IRQ handler fsdax: mark the iomap argument to dax_iomap_sector as const net: sctp: Fix some typos iommu/amd: Skip enabling command/event buffers for kdump nvme-fc: use lock accessing port_state and rport state net: netpoll: fix incorrect refcount handling causing incorrect cleanup net: ethernet: ti: netcp: Standardize knav_dma_open_channel to return NULL on error tipc: Fix use-after-free in tipc_mon_reinit_self(). sctp: prevent possible shift-out-of-bounds in sctp_transport_update_rto Bluetooth: btusb: reorder cleanup in btusb_disconnect to avoid UAF wifi: brcmfmac: fix crash while sending Action Frames in standalone AP Mode net/sched: sch_qfq: Fix null-deref in agg_dequeue Input: pegasus-notetaker - fix potential out-of-bounds access Input: remove third argument of usb_maxpacket() usb: deprecate the third argument of usb_maxpacket() ata: libata-scsi: Fix system suspend for a security locked drive fs/proc: fix uaf in proc_readdir_de() pmdomain: imx: Fix reference count leak in imx_gpc_remove pmdomain: arm: scmi: Fix genpd leak on provider registration failure net: qede: Initialize qede_ll_ops with designated initializer uio_hv_generic: Set event for all channels on the device ALSA: usb-audio: fix uac2 clock source at terminal parser kconfig/nconf: Initialize the default locale at startup kconfig/mconf: Initialize the default locale at startup vsock: Ignore signal/timeout on connect() if already established s390/ctcm: Fix double-kfree net: openvswitch: remove never-working support for setting nsh fields mlxsw: spectrum: Fix memory leak in mlxsw_sp_flower_stats() MIPS: Malta: Fix !EVA SOC-it PCI MMIO scsi: target: tcm_loop: Fix segfault in tcm_loop_tpg_address_show() scsi: sg: Do not sleep in atomic context Input: cros_ec_keyb - fix an invalid memory access be2net: pass wrb_params in case of OS2BMC HID: quirks: work around VID/PID conflict for 0x4c4a/0x4155 isdn: mISDN: hfcsusb: fix memory leak in hfcsusb_probe() spi: Try to get ACPI GPIO IRQ earlier ipv4: route: Prevent rt_bind_exception() from rebinding stale fnhe strparser: Fix signed/unsigned mismatch bug gcov: add support for GCC 15 mm/ksm: fix flag-dropping behavior in ksm_madvise ALSA: usb-audio: Fix NULL pointer dereference in snd_usb_mixer_controls_badd ASoC: cs4271: Fix regulator leak on probe failure regulator: fixed: use dev_err_probe for register Bluetooth: L2CAP: export l2cap_chan_hold for modules net_sched: remove need_resched() from qdisc_run() net/mlx5e: Fix maxrate wraparound in threshold between units net: sched: act_ife: initialize struct tc_ife to fix KMSAN kernel-infoleak Bluetooth: 6lowpan: Don't hold spin lock over sleeping functions Bluetooth: 6lowpan: fix BDADDR_LE vs ADDR_LE_DEV address type confusion Bluetooth: 6lowpan: reset link-local header on ipv6 recv path net: fec: correct rx_bytes statistic for the case SHIFT16 is set ASoC: max98090/91: fixed max98091 ALSA widget powering up/down HID: quirks: avoid Cooler Master MM712 dongle wakeup bug NFS4: Fix state renewals missing after boot extcon: adc-jack: Cleanup wakeup source only if it was enabled net: usb: qmi_wwan: initialize MAC header offset in qmimux_rx_fixup sctp: Prevent TOCTOU out-of-bounds write sctp: Hold RCU read lock while iterating over address list net: vlan: sync VLAN features with lower device ceph: add checking of wait_for_completion_killable() return value fbdev: Add bounds checking in bit_putcs to fix vmalloc-out-of-bounds ACPI: property: Return present device nodes only on fwnode interface 9p: sysfs_init: don't hardcode error to ENOMEM 9p: fix /sys/fs/9p/caches overwriting itself fs/hpfs: Fix error code for new_inode() failure in mkdir/create/mknod/symlink ACPICA: Update dsmethod.c to get rid of unused variable warning page_pool: Clamp pool size to max 16K pages Bluetooth: bcsp: receive data only if registered Bluetooth: SCO: Fix UAF on sco_conn_free net: macb: avoid dealing with endianness in macb_set_hwaddr() nfs4_setup_readdir(): insufficient locking for ->d_parent->d_inode dereferencing NFSv4.1: fix mount hang after CREATE_SESSION failure NFSv4: handle ERR_GRACE on delegation recalls remoteproc: qcom: q6v5: Avoid handling handover twice sparc/module: Add R_SPARC_UA64 relocation handling net: intel: fm10k: Fix parameter idx set but not used jfs: fix uninitialized waitqueue in transaction manager jfs: Verify inode mode when loading from disk ipv6: np->rxpmtu race annotation usb: xhci: plat: Facilitate using autosuspend for xhci plat devices usb: mon: Increase BUFF_MAX to 64 MiB to support multi-MB URBs allow finish_no_open(file, ERR_PTR(-E...)) scsi: lpfc: Define size of debugfs entry for xri rebalancing scsi: lpfc: Check return status of lpfc_reset_flush_io_context during TGT_RESET selftests/Makefile: include $(INSTALL_DEP_TARGETS) in clean target to clean net/lib dependency net/cls_cgroup: Fix task_get_classid() during qdisc run media: redrat3: use int type to store negative error codes net: sh_eth: Disable WoL if system can not suspend usb: gadget: f_hid: Fix zero length packet transfer net: call cond_resched() less often in __release_sock() ALSA: usb-audio: apply quirk for MOONDROP Quark2 net: nfc: nci: Increase NCI_DATA_TIMEOUT to 3000 ms dmaengine: mv_xor: match alloc_wc and free_wc scsi: pm8001: Use int instead of u32 to store error codes mips: lantiq: xway: sysctrl: rename stp clock mips: lantiq: danube: add missing properties to cpu node media: fix uninitialized symbol warnings extcon: adc-jack: Fix wakeup source leaks on device unbind rds: Fix endianness annotation for RDS_MPATH_HASH net: Call trace_sock_exceed_buf_limit() for memcg failure with SK_MEM_RECV. char: misc: Does not request module for miscdevice with dynamic minor usb: gadget: f_ncm: Fix MAC assignment NCM ethernet iio: adc: spear_adc: mask SPEAR_ADC_STATUS channel and avg sample before setting register media: imon: make send_packet() more robust net: ipv6: fix field-spanning memcpy warning in AH output bridge: Redirect to backup port when port is administratively down media: pci: ivtv: Don't create fake v4l2_fh drm/amdkfd: return -ENOTTY for unsupported IOCTLs selftests/net: Ensure assert() triggers in psock_tpacket.c selftests/net: Replace non-standard __WORDSIZE with sizeof(long) * 8 PCI: Disable MSI on RDC PCI to PCIe bridges drm/nouveau: replace snprintf() with scnprintf() in nvkm_snprintbf() mfd: madera: Work around false-positive -Wininitialized warning mfd: stmpe: Remove IRQ domain upon removal tools/power x86_energy_perf_policy: Prefer driver HWP limits tools/power x86_energy_perf_policy: Enhance HWP enable tools/cpupower: Fix incorrect size in cpuidle_state_disable() hwmon: (dell-smm) Add support for Dell OptiPlex 7040 uprobe: Do not emulate/sstep original instruction when ip is changed clocksource/drivers/vf-pit: Replace raw_readl/writel to readl/writel video: backlight: lp855x_bl: Set correct EPROM start for LP8556 tee: allow a driver to allocate a tee_device without a pool ACPICA: dispatcher: Use acpi_ds_clear_operands() in acpi_ds_call_control_method() irqchip/gic-v2m: Handle Multiple MSI base IRQ Alignment arc: Fix __fls() const-foldability via __builtin_clzl() cpufreq/longhaul: handle NULL policy in longhaul_exit selftests/bpf: Fix bpf_prog_detach2 usage in test_lirc_mode2 ACPI: video: force native for Lenovo 82K8 memstick: Add timeout to prevent indefinite waiting bpf: Don't use %pK through printk spi: loopback-test: Don't use %pK through printk soc: qcom: smem: Fix endian-unaware access of num_entries usb: gadget: f_fs: Fix epfile null pointer access after ep enable. serial: 8250_dw: Use devm_clk_get_optional() to get the input clock can: gs_usb: increase max interface to U8_MAX devcoredump: Fix circular locking dependency with devcd->mutex. x86/resctrl: Fix miscount of bandwidth event when reactivating previously unavailable RMID net: phy: dp83867: Disable EEE support as not implemented regmap: slimbus: fix bus_context pointer in regmap init calls usbnet: Prevents free active kevent wifi: ath10k: Fix memory leak on unsupported WMI command ASoC: qdsp6: q6asm: do not sleep while atomic fbdev: pvr2fb: Fix leftover reference to ONCHIP_NR_DMA_CHANNELS fbdev: bitblit: bound-check glyph index in bit_putcs* ACPI: video: Fix use-after-free in acpi_video_switch_brightness() fbdev: atyfb: Check if pll_ops->init_pll failed net: usb: asix_devices: Check return value of usbnet_get_endpoints btrfs: use smp_mb__after_atomic() when forcing COW in create_pending_snapshot() x86/bugs: Fix reporting of LFENCE retpoline Conflicts: drivers/slimbus/qcom-ngd-ctrl.c drivers/usb/gadget/function/f_fs.c drivers/usb/host/xhci-plat.c Change-Id: Id995e84f0ff2c819145f7ebd62bc8dcd47fbbb47
554 lines
13 KiB
C
554 lines
13 KiB
C
/*
|
|
* Stream Parser
|
|
*
|
|
* Copyright (c) 2016 Tom Herbert <tom@herbertland.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2
|
|
* as published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/bpf.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/errqueue.h>
|
|
#include <linux/file.h>
|
|
#include <linux/in.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/net.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/rculist.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/socket.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/workqueue.h>
|
|
#include <net/strparser.h>
|
|
#include <net/netns/generic.h>
|
|
#include <net/sock.h>
|
|
|
|
static struct workqueue_struct *strp_wq;
|
|
|
|
static inline struct _strp_msg *_strp_msg(struct sk_buff *skb)
|
|
{
|
|
return (struct _strp_msg *)((void *)skb->cb +
|
|
offsetof(struct sk_skb_cb, strp));
|
|
}
|
|
|
|
/* Lower lock held */
|
|
static void strp_abort_strp(struct strparser *strp, int err)
|
|
{
|
|
/* Unrecoverable error in receive */
|
|
|
|
cancel_delayed_work(&strp->msg_timer_work);
|
|
|
|
if (strp->stopped)
|
|
return;
|
|
|
|
strp->stopped = 1;
|
|
|
|
if (strp->sk) {
|
|
struct sock *sk = strp->sk;
|
|
|
|
/* Report an error on the lower socket */
|
|
sk->sk_err = -err;
|
|
sk->sk_error_report(sk);
|
|
}
|
|
}
|
|
|
|
static void strp_start_timer(struct strparser *strp, long timeo)
|
|
{
|
|
if (timeo && timeo != LONG_MAX)
|
|
mod_delayed_work(strp_wq, &strp->msg_timer_work, timeo);
|
|
}
|
|
|
|
/* Lower lock held */
|
|
static void strp_parser_err(struct strparser *strp, int err,
|
|
read_descriptor_t *desc)
|
|
{
|
|
desc->error = err;
|
|
kfree_skb(strp->skb_head);
|
|
strp->skb_head = NULL;
|
|
strp->cb.abort_parser(strp, err);
|
|
}
|
|
|
|
static inline int strp_peek_len(struct strparser *strp)
|
|
{
|
|
if (strp->sk) {
|
|
struct socket *sock = strp->sk->sk_socket;
|
|
|
|
return sock->ops->peek_len(sock);
|
|
}
|
|
|
|
/* If we don't have an associated socket there's nothing to peek.
|
|
* Return int max to avoid stopping the strparser.
|
|
*/
|
|
|
|
return INT_MAX;
|
|
}
|
|
|
|
/* Lower socket lock held */
|
|
static int __strp_recv(read_descriptor_t *desc, struct sk_buff *orig_skb,
|
|
unsigned int orig_offset, size_t orig_len,
|
|
size_t max_msg_size, long timeo)
|
|
{
|
|
struct strparser *strp = (struct strparser *)desc->arg.data;
|
|
struct _strp_msg *stm;
|
|
struct sk_buff *head, *skb;
|
|
size_t eaten = 0, cand_len;
|
|
ssize_t extra;
|
|
int err;
|
|
bool cloned_orig = false;
|
|
|
|
if (strp->paused)
|
|
return 0;
|
|
|
|
head = strp->skb_head;
|
|
if (head) {
|
|
/* Message already in progress */
|
|
if (unlikely(orig_offset)) {
|
|
/* Getting data with a non-zero offset when a message is
|
|
* in progress is not expected. If it does happen, we
|
|
* need to clone and pull since we can't deal with
|
|
* offsets in the skbs for a message expect in the head.
|
|
*/
|
|
orig_skb = skb_clone(orig_skb, GFP_ATOMIC);
|
|
if (!orig_skb) {
|
|
STRP_STATS_INCR(strp->stats.mem_fail);
|
|
desc->error = -ENOMEM;
|
|
return 0;
|
|
}
|
|
if (!pskb_pull(orig_skb, orig_offset)) {
|
|
STRP_STATS_INCR(strp->stats.mem_fail);
|
|
kfree_skb(orig_skb);
|
|
desc->error = -ENOMEM;
|
|
return 0;
|
|
}
|
|
cloned_orig = true;
|
|
orig_offset = 0;
|
|
}
|
|
|
|
if (!strp->skb_nextp) {
|
|
/* We are going to append to the frags_list of head.
|
|
* Need to unshare the frag_list.
|
|
*/
|
|
err = skb_unclone(head, GFP_ATOMIC);
|
|
if (err) {
|
|
STRP_STATS_INCR(strp->stats.mem_fail);
|
|
desc->error = err;
|
|
return 0;
|
|
}
|
|
|
|
if (unlikely(skb_shinfo(head)->frag_list)) {
|
|
/* We can't append to an sk_buff that already
|
|
* has a frag_list. We create a new head, point
|
|
* the frag_list of that to the old head, and
|
|
* then are able to use the old head->next for
|
|
* appending to the message.
|
|
*/
|
|
if (WARN_ON(head->next)) {
|
|
desc->error = -EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
skb = alloc_skb(0, GFP_ATOMIC);
|
|
if (!skb) {
|
|
STRP_STATS_INCR(strp->stats.mem_fail);
|
|
desc->error = -ENOMEM;
|
|
return 0;
|
|
}
|
|
skb->len = head->len;
|
|
skb->data_len = head->len;
|
|
skb->truesize = head->truesize;
|
|
*_strp_msg(skb) = *_strp_msg(head);
|
|
strp->skb_nextp = &head->next;
|
|
skb_shinfo(skb)->frag_list = head;
|
|
strp->skb_head = skb;
|
|
head = skb;
|
|
} else {
|
|
strp->skb_nextp =
|
|
&skb_shinfo(head)->frag_list;
|
|
}
|
|
}
|
|
}
|
|
|
|
while (eaten < orig_len) {
|
|
/* Always clone since we will consume something */
|
|
skb = skb_clone(orig_skb, GFP_ATOMIC);
|
|
if (!skb) {
|
|
STRP_STATS_INCR(strp->stats.mem_fail);
|
|
desc->error = -ENOMEM;
|
|
break;
|
|
}
|
|
|
|
cand_len = orig_len - eaten;
|
|
|
|
head = strp->skb_head;
|
|
if (!head) {
|
|
head = skb;
|
|
strp->skb_head = head;
|
|
/* Will set skb_nextp on next packet if needed */
|
|
strp->skb_nextp = NULL;
|
|
stm = _strp_msg(head);
|
|
memset(stm, 0, sizeof(*stm));
|
|
stm->strp.offset = orig_offset + eaten;
|
|
} else {
|
|
/* Unclone if we are appending to an skb that we
|
|
* already share a frag_list with.
|
|
*/
|
|
if (skb_has_frag_list(skb)) {
|
|
err = skb_unclone(skb, GFP_ATOMIC);
|
|
if (err) {
|
|
STRP_STATS_INCR(strp->stats.mem_fail);
|
|
desc->error = err;
|
|
break;
|
|
}
|
|
}
|
|
|
|
stm = _strp_msg(head);
|
|
*strp->skb_nextp = skb;
|
|
strp->skb_nextp = &skb->next;
|
|
head->data_len += skb->len;
|
|
head->len += skb->len;
|
|
head->truesize += skb->truesize;
|
|
}
|
|
|
|
if (!stm->strp.full_len) {
|
|
ssize_t len;
|
|
|
|
len = (*strp->cb.parse_msg)(strp, head);
|
|
|
|
if (!len) {
|
|
/* Need more header to determine length */
|
|
if (!stm->accum_len) {
|
|
/* Start RX timer for new message */
|
|
strp_start_timer(strp, timeo);
|
|
}
|
|
stm->accum_len += cand_len;
|
|
eaten += cand_len;
|
|
STRP_STATS_INCR(strp->stats.need_more_hdr);
|
|
WARN_ON(eaten != orig_len);
|
|
break;
|
|
} else if (len < 0) {
|
|
if (len == -ESTRPIPE && stm->accum_len) {
|
|
len = -ENODATA;
|
|
strp->unrecov_intr = 1;
|
|
} else {
|
|
strp->interrupted = 1;
|
|
}
|
|
strp_parser_err(strp, len, desc);
|
|
break;
|
|
} else if (len > max_msg_size) {
|
|
/* Message length exceeds maximum allowed */
|
|
STRP_STATS_INCR(strp->stats.msg_too_big);
|
|
strp_parser_err(strp, -EMSGSIZE, desc);
|
|
break;
|
|
} else if (len <= (ssize_t)head->len -
|
|
(ssize_t)skb->len - stm->strp.offset) {
|
|
/* Length must be into new skb (and also
|
|
* greater than zero)
|
|
*/
|
|
STRP_STATS_INCR(strp->stats.bad_hdr_len);
|
|
strp_parser_err(strp, -EPROTO, desc);
|
|
break;
|
|
}
|
|
|
|
stm->strp.full_len = len;
|
|
}
|
|
|
|
extra = (ssize_t)(stm->accum_len + cand_len) -
|
|
stm->strp.full_len;
|
|
|
|
if (extra < 0) {
|
|
/* Message not complete yet. */
|
|
if (stm->strp.full_len - stm->accum_len >
|
|
strp_peek_len(strp)) {
|
|
/* Don't have the whole message in the socket
|
|
* buffer. Set strp->need_bytes to wait for
|
|
* the rest of the message. Also, set "early
|
|
* eaten" since we've already buffered the skb
|
|
* but don't consume yet per strp_read_sock.
|
|
*/
|
|
|
|
if (!stm->accum_len) {
|
|
/* Start RX timer for new message */
|
|
strp_start_timer(strp, timeo);
|
|
}
|
|
|
|
stm->accum_len += cand_len;
|
|
eaten += cand_len;
|
|
strp->need_bytes = stm->strp.full_len -
|
|
stm->accum_len;
|
|
STRP_STATS_ADD(strp->stats.bytes, cand_len);
|
|
desc->count = 0; /* Stop reading socket */
|
|
break;
|
|
}
|
|
stm->accum_len += cand_len;
|
|
eaten += cand_len;
|
|
WARN_ON(eaten != orig_len);
|
|
break;
|
|
}
|
|
|
|
/* Positive extra indicates ore bytes than needed for the
|
|
* message
|
|
*/
|
|
|
|
WARN_ON(extra > cand_len);
|
|
|
|
eaten += (cand_len - extra);
|
|
|
|
/* Hurray, we have a new message! */
|
|
cancel_delayed_work(&strp->msg_timer_work);
|
|
strp->skb_head = NULL;
|
|
strp->need_bytes = 0;
|
|
STRP_STATS_INCR(strp->stats.msgs);
|
|
|
|
/* Give skb to upper layer */
|
|
strp->cb.rcv_msg(strp, head);
|
|
|
|
if (unlikely(strp->paused)) {
|
|
/* Upper layer paused strp */
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (cloned_orig)
|
|
kfree_skb(orig_skb);
|
|
|
|
STRP_STATS_ADD(strp->stats.bytes, eaten);
|
|
|
|
return eaten;
|
|
}
|
|
|
|
int strp_process(struct strparser *strp, struct sk_buff *orig_skb,
|
|
unsigned int orig_offset, size_t orig_len,
|
|
size_t max_msg_size, long timeo)
|
|
{
|
|
read_descriptor_t desc; /* Dummy arg to strp_recv */
|
|
|
|
desc.arg.data = strp;
|
|
|
|
return __strp_recv(&desc, orig_skb, orig_offset, orig_len,
|
|
max_msg_size, timeo);
|
|
}
|
|
EXPORT_SYMBOL_GPL(strp_process);
|
|
|
|
static int strp_recv(read_descriptor_t *desc, struct sk_buff *orig_skb,
|
|
unsigned int orig_offset, size_t orig_len)
|
|
{
|
|
struct strparser *strp = (struct strparser *)desc->arg.data;
|
|
|
|
return __strp_recv(desc, orig_skb, orig_offset, orig_len,
|
|
strp->sk->sk_rcvbuf, strp->sk->sk_rcvtimeo);
|
|
}
|
|
|
|
static int default_read_sock_done(struct strparser *strp, int err)
|
|
{
|
|
return err;
|
|
}
|
|
|
|
/* Called with lock held on lower socket */
|
|
static int strp_read_sock(struct strparser *strp)
|
|
{
|
|
struct socket *sock = strp->sk->sk_socket;
|
|
read_descriptor_t desc;
|
|
|
|
if (unlikely(!sock || !sock->ops || !sock->ops->read_sock))
|
|
return -EBUSY;
|
|
|
|
desc.arg.data = strp;
|
|
desc.error = 0;
|
|
desc.count = 1; /* give more than one skb per call */
|
|
|
|
/* sk should be locked here, so okay to do read_sock */
|
|
sock->ops->read_sock(strp->sk, &desc, strp_recv);
|
|
|
|
desc.error = strp->cb.read_sock_done(strp, desc.error);
|
|
|
|
return desc.error;
|
|
}
|
|
|
|
/* Lower sock lock held */
|
|
void strp_data_ready(struct strparser *strp)
|
|
{
|
|
if (unlikely(strp->stopped) || strp->paused)
|
|
return;
|
|
|
|
/* This check is needed to synchronize with do_strp_work.
|
|
* do_strp_work acquires a process lock (lock_sock) whereas
|
|
* the lock held here is bh_lock_sock. The two locks can be
|
|
* held by different threads at the same time, but bh_lock_sock
|
|
* allows a thread in BH context to safely check if the process
|
|
* lock is held. In this case, if the lock is held, queue work.
|
|
*/
|
|
if (sock_owned_by_user_nocheck(strp->sk)) {
|
|
queue_work(strp_wq, &strp->work);
|
|
return;
|
|
}
|
|
|
|
if (strp->need_bytes) {
|
|
if (strp_peek_len(strp) < strp->need_bytes)
|
|
return;
|
|
}
|
|
|
|
if (strp_read_sock(strp) == -ENOMEM)
|
|
queue_work(strp_wq, &strp->work);
|
|
}
|
|
EXPORT_SYMBOL_GPL(strp_data_ready);
|
|
|
|
static void do_strp_work(struct strparser *strp)
|
|
{
|
|
/* We need the read lock to synchronize with strp_data_ready. We
|
|
* need the socket lock for calling strp_read_sock.
|
|
*/
|
|
strp->cb.lock(strp);
|
|
|
|
if (unlikely(strp->stopped))
|
|
goto out;
|
|
|
|
if (strp->paused)
|
|
goto out;
|
|
|
|
if (strp_read_sock(strp) == -ENOMEM)
|
|
queue_work(strp_wq, &strp->work);
|
|
|
|
out:
|
|
strp->cb.unlock(strp);
|
|
}
|
|
|
|
static void strp_work(struct work_struct *w)
|
|
{
|
|
do_strp_work(container_of(w, struct strparser, work));
|
|
}
|
|
|
|
static void strp_msg_timeout(struct work_struct *w)
|
|
{
|
|
struct strparser *strp = container_of(w, struct strparser,
|
|
msg_timer_work.work);
|
|
|
|
/* Message assembly timed out */
|
|
STRP_STATS_INCR(strp->stats.msg_timeouts);
|
|
strp->cb.lock(strp);
|
|
strp->cb.abort_parser(strp, -ETIMEDOUT);
|
|
strp->cb.unlock(strp);
|
|
}
|
|
|
|
static void strp_sock_lock(struct strparser *strp)
|
|
{
|
|
lock_sock(strp->sk);
|
|
}
|
|
|
|
static void strp_sock_unlock(struct strparser *strp)
|
|
{
|
|
release_sock(strp->sk);
|
|
}
|
|
|
|
int strp_init(struct strparser *strp, struct sock *sk,
|
|
const struct strp_callbacks *cb)
|
|
{
|
|
|
|
if (!cb || !cb->rcv_msg || !cb->parse_msg)
|
|
return -EINVAL;
|
|
|
|
/* The sk (sock) arg determines the mode of the stream parser.
|
|
*
|
|
* If the sock is set then the strparser is in receive callback mode.
|
|
* The upper layer calls strp_data_ready to kick receive processing
|
|
* and strparser calls the read_sock function on the socket to
|
|
* get packets.
|
|
*
|
|
* If the sock is not set then the strparser is in general mode.
|
|
* The upper layer calls strp_process for each skb to be parsed.
|
|
*/
|
|
|
|
if (!sk) {
|
|
if (!cb->lock || !cb->unlock)
|
|
return -EINVAL;
|
|
}
|
|
|
|
memset(strp, 0, sizeof(*strp));
|
|
|
|
strp->sk = sk;
|
|
|
|
strp->cb.lock = cb->lock ? : strp_sock_lock;
|
|
strp->cb.unlock = cb->unlock ? : strp_sock_unlock;
|
|
strp->cb.rcv_msg = cb->rcv_msg;
|
|
strp->cb.parse_msg = cb->parse_msg;
|
|
strp->cb.read_sock_done = cb->read_sock_done ? : default_read_sock_done;
|
|
strp->cb.abort_parser = cb->abort_parser ? : strp_abort_strp;
|
|
|
|
INIT_DELAYED_WORK(&strp->msg_timer_work, strp_msg_timeout);
|
|
INIT_WORK(&strp->work, strp_work);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(strp_init);
|
|
|
|
/* Sock process lock held (lock_sock) */
|
|
void __strp_unpause(struct strparser *strp)
|
|
{
|
|
strp->paused = 0;
|
|
|
|
if (strp->need_bytes) {
|
|
if (strp_peek_len(strp) < strp->need_bytes)
|
|
return;
|
|
}
|
|
strp_read_sock(strp);
|
|
}
|
|
EXPORT_SYMBOL_GPL(__strp_unpause);
|
|
|
|
void strp_unpause(struct strparser *strp)
|
|
{
|
|
strp->paused = 0;
|
|
|
|
/* Sync setting paused with RX work */
|
|
smp_mb();
|
|
|
|
queue_work(strp_wq, &strp->work);
|
|
}
|
|
EXPORT_SYMBOL_GPL(strp_unpause);
|
|
|
|
/* strp must already be stopped so that strp_recv will no longer be called.
|
|
* Note that strp_done is not called with the lower socket held.
|
|
*/
|
|
void strp_done(struct strparser *strp)
|
|
{
|
|
WARN_ON(!strp->stopped);
|
|
|
|
cancel_delayed_work_sync(&strp->msg_timer_work);
|
|
cancel_work_sync(&strp->work);
|
|
|
|
if (strp->skb_head) {
|
|
kfree_skb(strp->skb_head);
|
|
strp->skb_head = NULL;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(strp_done);
|
|
|
|
void strp_stop(struct strparser *strp)
|
|
{
|
|
strp->stopped = 1;
|
|
}
|
|
EXPORT_SYMBOL_GPL(strp_stop);
|
|
|
|
void strp_check_rcv(struct strparser *strp)
|
|
{
|
|
queue_work(strp_wq, &strp->work);
|
|
}
|
|
EXPORT_SYMBOL_GPL(strp_check_rcv);
|
|
|
|
static int __init strp_mod_init(void)
|
|
{
|
|
strp_wq = create_singlethread_workqueue("kstrp");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit strp_mod_exit(void)
|
|
{
|
|
destroy_workqueue(strp_wq);
|
|
}
|
|
module_init(strp_mod_init);
|
|
module_exit(strp_mod_exit);
|
|
MODULE_LICENSE("GPL");
|