5.10.266-rc1 review
Subsystems (2)
Greg Kroah-Hartman
maintainerPATCH 116/235 mmc: vub300: fix use-after-free on disconnect
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
The vub300 driver maintains an explicit reference count for the controller and its driver data and the last reference can in theory be dropped after the driver has been unbound.
This specifically means that the controller allocation must not be device managed as that can lead to use-after-free.
Note that the lifetime is currently also incorrectly tied the parent USB device rather than interface, which can lead to memory leaks if the driver is unbound without its device being physically disconnected (e.g. on probe deferral).
Fix both issues by reverting to non-managed allocation of the controller.
--- drivers/mmc/host/vub300.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/mmc/host/vub300.c +6 -6
@@ -2281,7 +2281,7 @@ static int vub300_probe(struct usb_interdev_err (&vub300->udev->dev,"Could not find two sets of bulk-in/out endpoint pairs\n" ); retval = -EINVAL ; - goto error5; + goto err_free_host; } retval = usb_control_msg(vub300->udev, usb_rcvctrlpipe(vub300->udev, 0),
@@ -2290,14 +2290,14 @@ static int vub300_probe(struct usb_inter0x0000 ,0x0000 , &vub300->hc_info, sizeof(vub300->hc_info), 1000);if (retval <0 ) - goto error5; + goto err_free_host; retval =usb_control_msg (vub300->udev,usb_sndctrlpipe (vub300->udev,0 ),SET_ROM_WAIT_STATES ,USB_DIR_OUT |USB_TYPE_VENDOR |USB_RECIP_DEVICE , firmware_rom_wait_states,0x0000 ,NULL ,0 ,1000 );if (retval <0 ) - goto error5; + goto err_free_host; dev_info(&vub300->udev->dev, "operating_mode = %s %s %d MHz %s %d byte USB packets\n", (mmc->caps & MMC_CAP_SDIO_IRQ) ? "IRQs" : "POLL",
@@ -2312,7 +2312,7 @@ static int vub300_probe(struct usb_inter0x0000 ,0x0000 , &vub300->system_port_status, sizeof(vub300->system_port_status), 1000);if (retval <0 ) { - goto error5; + goto err_free_host; }else if (sizeof (vub300->system_port_status) == retval) { vub300->card_present = (0x0001 & vub300->system_port_status.port_flags) ?1 :0 ;
@@ -2320,7 +2320,7 @@ static int vub300_probe(struct usb_inter (0x0010 & vub300->system_port_status.port_flags) ?1 :0 ; }else { retval = -EINVAL ; - goto error5; + goto err_free_host; }usb_set_intfdata (interface, vub300);INIT_DELAYED_WORK (&vub300->pollwork, vub300_pollwork_thread);
@@ -2350,7 +2350,7 @@ static int vub300_probe(struct usb_interreturn 0 ; error6:del_timer_sync (&vub300->inactivity_timer); -error5: +err_free_host:mmc_free_host (mmc);/* * and hence also frees vub300
Greg Kroah-Hartman
maintainerPATCH 117/235 mmc: vub300: rename probe error labels
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
Error labels should be named after what they do.
Rename the probe error labels.
--- drivers/mmc/host/vub300.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-)
drivers/mmc/host/vub300.c +11 -9
@@ -2116,19 +2116,19 @@ static int vub300_probe(struct usb_inter command_out_urb =usb_alloc_urb (0 ,GFP_KERNEL );if (!command_out_urb) { retval = -ENOMEM ; - goto error0; + goto err_put_udev; } command_res_urb =usb_alloc_urb (0 ,GFP_KERNEL );if (!command_res_urb) { retval = -ENOMEM ; - goto error1; + goto err_free_out_urb; }/* this also allocates memory for our VUB300 mmc host device */ mmc =mmc_alloc_host (sizeof (struct vub300_mmc_host ), &udev->dev);if (!mmc) { retval = -ENOMEM ;dev_err (&udev->dev,"not enough memory for the mmc_host\n" ); - goto error4; + goto err_free_res_urb; }/* MMC core transfer sizes tunable parameters */ mmc->caps =0 ;
@@ -2345,23 +2345,25 @@ static int vub300_probe(struct usb_inter interface_to_InterfaceNumber(interface)); retval =mmc_add_host (mmc);if (retval) - goto error6; + goto err_delete_timer;return 0 ; -error6: -del_timer_sync (&vub300->inactivity_timer); + +err_delete_timer: +timer_delete_sync (&vub300->inactivity_timer); err_free_host:mmc_free_host (mmc);/* * and hence also frees vub300 * which is contained at the end of struct mmc */ -error4: +err_free_res_urb:usb_free_urb (command_res_urb); -error1: +err_free_out_urb:usb_free_urb (command_out_urb); -error0: +err_put_udev:usb_put_dev (udev); +return retval; }
Greg Kroah-Hartman
maintainerPATCH 118/235 mmc: vub300: fix use-after-free on probe failure
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
The vub300 driver lifetime-manages its controller state using vub300->kref, with vub300_delete() freeing the mmc host when the last reference is dropped. The probe error path after the inactivity timer has been armed still bypasses that lifetime rule, however, and falls through to mmc_free_host() directly if mmc_add_host() fails.
The race window is between arming the inactivity timer and reaching the probe error unwind after mmc_add_host() fails:
probe thread timer/workqueue
------------ ---------------
kref_init(&vub300->kref) ref = 1
kref_get(&vub300->kref) ref = 2, timer ref
add_timer(inactivity_timer) fires after one second
|
| race window
|<---------------------------------------------------->
| mmc_add_host(mmc)
inactivity timer fires vub300_queue_dead_work()
kref_get() ref = 3
queue_work(deadwork)
mmc_add_host() fails
timer_delete_sync()
mmc_free_host(mmc)
frees vub300
deadwork runs
use-after-free
The inactivity timeout is one second, so this would require mmc_add_host() to both fail and take more than one second to do so. This is unlikely to happen in practice, but the error path is still wrong.
timer_delete_sync() only waits for the timer callback itself. It does not flush deadwork that the callback may already have queued. As a result, queued deadwork can still hold a kref while the probe error path directly frees the backing mmc host, including the vub300 storage.
Fix this by using the same lifetime mechanism as disconnect. Clear vub300->interface so that the timer callback and any queued deadwork return early and drop their references, then drop the initial probe reference and return without falling through to err_free_host.
--- drivers/mmc/host/vub300.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
drivers/mmc/host/vub300.c +7 -3
@@ -2345,12 +2345,16 @@ static int vub300_probe(struct usb_inter interface_to_InterfaceNumber(interface)); retval =mmc_add_host (mmc);if (retval) - goto err_delete_timer; + goto err_stop_io;return 0 ; -err_delete_timer: -timer_delete_sync (&vub300->inactivity_timer); +err_stop_io: + vub300->interface =NULL ; +kref_put (&vub300->kref, vub300_delete); + +return retval; + err_free_host:mmc_free_host (mmc);/*
Greg Kroah-Hartman
maintainerPATCH 119/235 firmware_loader: introduce __free() cleanup hanler
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Torokhov <[email protected]>
[ Upstream commit 8dde8fa0cc3edce73c050b9882d06c1a575f6402 ]
Define cleanup handler using facilities from linux/cleanup.h to simplify error handling in code using firmware loader. This will allow writing code like this:
int driver_update_firmware(...)
{
const struct firmware *fw_entry __free(firmware) = NULL;
int error;
...
error = request_firmware(&fw_entry, fw_name, dev);
if (error) {
dev_err(dev, "failed to request firmware %s: %d",
fw_name, error);
return error;
}
error = check_firmware_valid(fw_entry);
if (error)
return error;
guard(mutex)(&instance->lock);
error = use_firmware(instance, fw);
if (error)
return error;
return 0;
}Stable-dep-of: d48795b5cd68 ("Input: ims-pcu - fix firmware leak in async update")
--- include/linux/firmware.h | 3 +++ 1 file changed, 3 insertions(+)
include/linux/firmware.h +3 -0
@@ -4,6 +4,7 @@#include <linux/types.h> #include <linux/compiler.h> +#include <linux/cleanup.h> #include <linux/gfp.h> #define FW_ACTION_NOHOTPLUG 0
@@ -118,4 +119,6 @@ static inline int request_partial_firmwaint firmware_request_cache (struct device *device,const char *name); +DEFINE_FREE(firmware, struct firmware *, release_firmware(_T)) +#endif
Greg Kroah-Hartman
maintainerPATCH 120/235 Input: ims-pcu - fix firmware leak in async update
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Torokhov <[email protected]>
[ Upstream commit d48795b5cd6828d36b707e8d62fc9e5c90e004ab ]
The firmware object was not being released if validation failed. Use __free(firmware) to ensure the firmware is always released.
--- drivers/input/misc/ims-pcu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/input/misc/ims-pcu.c +2 -3
@@ -964,9 +964,10 @@ out:return retval; } -static void ims_pcu_process_async_firmware (const struct firmware *fw, +static void ims_pcu_process_async_firmware (const struct firmware *_fw,void *context) { + const struct firmware *fw __free(firmware) = _fw;struct ims_pcu *pcu = context;int error;
@@ -987,8 +988,6 @@ static void ims_pcu_process_async_firmwaims_pcu_handle_firmware_update (pcu, fw);mutex_unlock (&pcu->cmd_mutex); -release_firmware (fw); - out:complete (&pcu->async_firmware_done); }
Greg Kroah-Hartman
maintainerPATCH 121/235 net: Add helper function to parse netlink msg of ip_tunnel_encap
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Liu Jian <[email protected]>
[ Upstream commit 537dd2d9fb9f4aa7939fb4fcf552ebe4f497bd7e ]
Add ip_tunnel_netlink_encap_parms to parse netlink msg of ip_tunnel_encap. Reduces duplicate code, no actual functional changes.
Stable-dep-of: 8211a2632466 ("net: ipip: require CAP_NET_ADMIN in the device netns for changelink")
--- include/net/ip_tunnels.h | 3 +++ net/ipv4/ip_tunnel_core.c | 35 +++++++++++++++++++++++++++++++++++ net/ipv4/ipip.c | 38 ++------------------------------------ net/ipv6/ip6_tunnel.c | 37 ++----------------------------------- net/ipv6/sit.c | 38 ++------------------------------------ 5 files changed, 44 insertions(+), 107 deletions(-)
include/net/ip_tunnels.h +3 -0
@@ -297,6 +297,9 @@ int ip_tunnel_newlink(struct net_device struct ip_tunnel_parm *p, __u32 fwmark);void ip_tunnel_setup (struct net_device *dev,unsigned int net_id); +bool ip_tunnel_netlink_encap_parms (struct nlattr *data[], +struct ip_tunnel_encap *encap); +extern const struct header_ops ip_tunnel_header_ops;__be16 ip_tunnel_parse_protocol (const struct sk_buff *skb);
net/ipv4/ip_tunnel_core.c +35 -0
@@ -1099,3 +1099,38 @@ EXPORT_SYMBOL(ip_tunnel_parse_protocol);const struct header_ops ip_tunnel_header_ops = { .parse_protocol = ip_tunnel_parse_protocol };EXPORT_SYMBOL (ip_tunnel_header_ops); + +/* This function returns true when ENCAP attributes are present in the nl msg */ +bool ip_tunnel_netlink_encap_parms (struct nlattr *data[], +struct ip_tunnel_encap *encap) +{ +bool ret = false; + +memset (encap,0 ,sizeof (*encap)); + +if (!data) +return ret; + +if (data[IFLA_IPTUN_ENCAP_TYPE ]) { + ret = true; + encap->type =nla_get_u16 (data[IFLA_IPTUN_ENCAP_TYPE ]); + } + +if (data[IFLA_IPTUN_ENCAP_FLAGS ]) { + ret = true; + encap->flags =nla_get_u16 (data[IFLA_IPTUN_ENCAP_FLAGS ]); + } + +if (data[IFLA_IPTUN_ENCAP_SPORT ]) { + ret = true; + encap->sport =nla_get_be16 (data[IFLA_IPTUN_ENCAP_SPORT ]); + } + +if (data[IFLA_IPTUN_ENCAP_DPORT ]) { + ret = true; + encap->dport =nla_get_be16 (data[IFLA_IPTUN_ENCAP_DPORT ]); + } + +return ret; +} +EXPORT_SYMBOL_GPL (ip_tunnel_netlink_encap_parms);
net/ipv4/ipip.c +2 -36
@@ -446,40 +446,6 @@ static void ipip_netlink_parms(struct nl *fwmark =nla_get_u32 (data[IFLA_IPTUN_FWMARK ]); } -/* This function returns true when ENCAP attributes are present in the nl msg */ -static bool ipip_netlink_encap_parms (struct nlattr *data[], -struct ip_tunnel_encap *ipencap) -{ -bool ret = false; - -memset (ipencap,0 ,sizeof (*ipencap)); - -if (!data) -return ret; - -if (data[IFLA_IPTUN_ENCAP_TYPE ]) { - ret = true; - ipencap->type =nla_get_u16 (data[IFLA_IPTUN_ENCAP_TYPE ]); - } - -if (data[IFLA_IPTUN_ENCAP_FLAGS ]) { - ret = true; - ipencap->flags =nla_get_u16 (data[IFLA_IPTUN_ENCAP_FLAGS ]); - } - -if (data[IFLA_IPTUN_ENCAP_SPORT ]) { - ret = true; - ipencap->sport =nla_get_be16 (data[IFLA_IPTUN_ENCAP_SPORT ]); - } - -if (data[IFLA_IPTUN_ENCAP_DPORT ]) { - ret = true; - ipencap->dport =nla_get_be16 (data[IFLA_IPTUN_ENCAP_DPORT ]); - } - -return ret; -} -static int ipip_newlink (struct net *src_net,struct net_device *dev,struct nlattr *tb[],struct nlattr *data[],struct netlink_ext_ack *extack)
@@ -489,7 +455,7 @@ static int ipip_newlink(struct net *src_struct ip_tunnel_encap ipencap;__u32 fwmark =0 ; -if (ipip_netlink_encap_parms (data, &ipencap)) { +if (ip_tunnel_netlink_encap_parms (data, &ipencap)) {int err =ip_tunnel_encap_setup (t, &ipencap);if (err <0 )
@@ -510,7 +476,7 @@ static int ipip_changelink(struct net_debool collect_md;__u32 fwmark = t->fwmark; -if (ipip_netlink_encap_parms (data, &ipencap)) { +if (ip_tunnel_netlink_encap_parms (data, &ipencap)) {int err =ip_tunnel_encap_setup (t, &ipencap);if (err <0 )
net/ipv6/ip6_tunnel.c +2 -35
@@ -2037,39 +2037,6 @@ static void ip6_tnl_netlink_parms(struct parms->fwmark =nla_get_u32 (data[IFLA_IPTUN_FWMARK ]); } -static bool ip6_tnl_netlink_encap_parms (struct nlattr *data[], -struct ip_tunnel_encap *ipencap) -{ -bool ret = false; - -memset (ipencap,0 ,sizeof (*ipencap)); - -if (!data) -return ret; - -if (data[IFLA_IPTUN_ENCAP_TYPE ]) { - ret = true; - ipencap->type =nla_get_u16 (data[IFLA_IPTUN_ENCAP_TYPE ]); - } - -if (data[IFLA_IPTUN_ENCAP_FLAGS ]) { - ret = true; - ipencap->flags =nla_get_u16 (data[IFLA_IPTUN_ENCAP_FLAGS ]); - } - -if (data[IFLA_IPTUN_ENCAP_SPORT ]) { - ret = true; - ipencap->sport =nla_get_be16 (data[IFLA_IPTUN_ENCAP_SPORT ]); - } - -if (data[IFLA_IPTUN_ENCAP_DPORT ]) { - ret = true; - ipencap->dport =nla_get_be16 (data[IFLA_IPTUN_ENCAP_DPORT ]); - } - -return ret; -} -static int ip6_tnl_newlink (struct net *src_net,struct net_device *dev,struct nlattr *tb[],struct nlattr *data[],struct netlink_ext_ack *extack)
@@ -2082,7 +2049,7 @@ static int ip6_tnl_newlink(struct net *s nt =netdev_priv (dev); -if (ip6_tnl_netlink_encap_parms (data, &ipencap)) { +if (ip_tunnel_netlink_encap_parms (data, &ipencap)) { err =ip6_tnl_encap_setup (nt, &ipencap);if (err <0 )return err;
@@ -2119,7 +2086,7 @@ static int ip6_tnl_changelink(struct netif (dev == ip6n->fb_tnl_dev)return -EINVAL ; -if (ip6_tnl_netlink_encap_parms (data, &ipencap)) { +if (ip_tunnel_netlink_encap_parms (data, &ipencap)) {int err =ip6_tnl_encap_setup (t, &ipencap);if (err <0 )
net/ipv6/sit.c +2 -36
@@ -1536,40 +1536,6 @@ static void ipip6_netlink_parms(struct n *fwmark =nla_get_u32 (data[IFLA_IPTUN_FWMARK ]); } -/* This function returns true when ENCAP attributes are present in the nl msg */ -static bool ipip6_netlink_encap_parms (struct nlattr *data[], -struct ip_tunnel_encap *ipencap) -{ -bool ret = false; - -memset (ipencap,0 ,sizeof (*ipencap)); - -if (!data) -return ret; - -if (data[IFLA_IPTUN_ENCAP_TYPE ]) { - ret = true; - ipencap->type =nla_get_u16 (data[IFLA_IPTUN_ENCAP_TYPE ]); - } - -if (data[IFLA_IPTUN_ENCAP_FLAGS ]) { - ret = true; - ipencap->flags =nla_get_u16 (data[IFLA_IPTUN_ENCAP_FLAGS ]); - } - -if (data[IFLA_IPTUN_ENCAP_SPORT ]) { - ret = true; - ipencap->sport =nla_get_be16 (data[IFLA_IPTUN_ENCAP_SPORT ]); - } - -if (data[IFLA_IPTUN_ENCAP_DPORT ]) { - ret = true; - ipencap->dport =nla_get_be16 (data[IFLA_IPTUN_ENCAP_DPORT ]); - } - -return ret; -} -#ifdef CONFIG_IPV6_SIT_6RD /* This function returns true when 6RD attributes are present in the nl msg */ static bool ipip6_netlink_6rd_parms(struct nlattr *data[],
@@ -1621,7 +1587,7 @@ static int ipip6_newlink(struct net *src nt =netdev_priv (dev); -if (ipip6_netlink_encap_parms (data, &ipencap)) { +if (ip_tunnel_netlink_encap_parms (data, &ipencap)) { err =ip_tunnel_encap_setup (nt, &ipencap);if (err <0 )return err;
@@ -1676,7 +1642,7 @@ static int ipip6_changelink(struct net_dif (dev == sitn->fb_tunnel_dev)return -EINVAL ; -if (ipip6_netlink_encap_parms (data, &ipencap)) { +if (ip_tunnel_netlink_encap_parms (data, &ipencap)) { err =ip_tunnel_encap_setup (t, &ipencap);if (err <0 )return err;
Greg Kroah-Hartman
maintainerPATCH 122/235 net: ipip: require CAP_NET_ADMIN in the device netns for changelink
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maoyi Xie <[email protected]>
[ Upstream commit 8211a26324667980a463c069469a818e71207e02 ]
ipip_changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net.
Gate ipip_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed.
--- net/ipv4/ipip.c | 3 +++ 1 file changed, 3 insertions(+)
net/ipv4/ipip.c +3 -0
@@ -476,6 +476,9 @@ static int ipip_changelink(struct net_debool collect_md;__u32 fwmark = t->fwmark; +if (!rtnl_dev_link_net_capable (dev, t->net)) +return -EPERM ; +if (ip_tunnel_netlink_encap_parms (data, &ipencap)) {int err =ip_tunnel_encap_setup (t, &ipencap);
Greg Kroah-Hartman
maintainerPATCH 123/235 net: ip6_tunnel: require CAP_NET_ADMIN in the device netns for changelink
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maoyi Xie <[email protected]>
[ Upstream commit 2496fa0b7d180b3ad356b514e7ff93bb14e6140a ]
ip6_tnl_changelink() operates on at most two netns, dev_net(dev) and the tunnel link netns t->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in t->net can rewrite a tunnel that lives in t->net.
Gate ip6_tnl_changelink() on rtnl_dev_link_net_capable() at its top, before any attribute is parsed.
--- net/ipv6/ip6_tunnel.c | 3 +++ 1 file changed, 3 insertions(+)
net/ipv6/ip6_tunnel.c +3 -0
@@ -2083,6 +2083,9 @@ static int ip6_tnl_changelink(struct netstruct ip6_tnl_net *ip6n =net_generic (net, ip6_tnl_net_id);struct ip_tunnel_encap ipencap; +if (!rtnl_dev_link_net_capable (dev, net)) +return -EPERM ; +if (dev == ip6n->fb_tnl_dev)return -EINVAL ;
Greg Kroah-Hartman
maintainerPATCH 124/235 net/sched: taprio: avoid calling child->ops->dequeue(child) twice
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vladimir Oltean <[email protected]>
[ Upstream commit 4c22942734f0814d3c928c25a80f48df0a6ce45e ]
Simplify taprio_dequeue_from_txq() by noticing that we can goto one call earlier than the previous skb_found label. This is possible because we've unified the treatment of the child->ops->dequeue(child) return call, we always try other TXQs now, instead of abandoning the root dequeue completely if we failed in the peek() case.
Stable-dep-of: e056e1dfcddc ("net/sched: sch_taprio: Replace direct dequeue call with peek and qdisc_dequeue_peeked")
--- net/sched/sch_taprio.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-)
net/sched/sch_taprio.c +3 -7
@@ -559,12 +559,8 @@ static struct sk_buff *taprio_dequeue_frif (unlikely (!child))return NULL ; -if (TXTIME_ASSIST_IS_ENABLED (q->flags)) { - skb = child->ops->dequeue (child); -if (!skb) -return NULL ; - goto skb_found; - } +if (TXTIME_ASSIST_IS_ENABLED (q->flags)) + goto skip_peek_checks; skb = child->ops->peek (child);if (!skb)
@@ -591,11 +587,11 @@ static struct sk_buff *taprio_dequeue_fr atomic_sub_return(len, &entry->budget) < 0)return NULL ; +skip_peek_checks: skb = child->ops->dequeue (child);if (unlikely (!skb))return NULL ; -skb_found:qdisc_bstats_update (sch, skb);qdisc_qstats_backlog_dec (sch, skb); sch->q.qlen--;
Greg Kroah-Hartman
maintainerPATCH 125/235 net/sched: sch_taprio: Replace direct dequeue call with peek and qdisc_dequeue_peeked
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
[ Upstream commit e056e1dfcddca877dd46d704e8ec9860cfc9ec44 ]
When taprio's software path peeks a non-work-conserving child qdisc, the child stashes the peeked skb in its gso_skb; taprio_dequeue_from_txq() then takes the packet with a direct child ->dequeue() call, which ignores that stash, orphans the peeked skb and desyncs the child's qlen/backlog. With a qfq child this re-enters the child on an emptied list and dereferences NULL, panicking the kernel from softirq on ordinary egress.
Take the packet through qdisc_dequeue_peeked(), as sch_red and sch_sfb now do. The helper returns the child's stashed skb first and is a no-op when there is none, so a work-conserving child is unaffected and the gated path now consumes the skb whose length was charged to the budget.
--- net/sched/sch_taprio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/sched/sch_taprio.c +1 -1
@@ -588,7 +588,7 @@ static struct sk_buff *taprio_dequeue_frreturn NULL ; skip_peek_checks: - skb = child->ops->dequeue (child); + skb =qdisc_dequeue_peeked (child);if (unlikely (!skb))return NULL ;
Greg Kroah-Hartman
maintainerPATCH 126/235 gpio: tegra: do not call pinctrl for GPIO direction
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
[ Upstream commit d3e91a95b2b0fc6336dbf3ec90d831a1654d2720 ]
tegra_gpio_direction_input() and tegra_gpio_direction_output() already program the GPIO controller direction registers directly. The additional pinctrl_gpio_direction_input/output() calls do not add a Tegra pinctrl operation, because the Tegra pinmux ops provide GPIO request/free handling but no gpio_set_direction hook.
The extra call still enters the pinctrl core and takes pctldev->mutex. Shared GPIO users can call the direction path while holding their per-line spinlock, so this otherwise redundant pinctrl direction call can sleep in an atomic context.
This was found by our static analysis tool and then confirmed by manual review of tegra_gpio_probe(), the Tegra GPIO direction callbacks and the Tegra pinctrl ops. The reviewed path has a default non-sleeping struct gpio_chip while the direction callback still enters the pinctrl mutex path.
A directed runtime validation kept the same non-sleeping chip registration and drove:
gpio_shared_proxy_direction_output()
gpiod_direction_output_raw_commit()
tegra_gpio_direction_output()
pinctrl_gpio_direction_output()
Lockdep reported a sleep-in-atomic warning with the shared GPIO spinlock held and pinctrl_get_device_gpio_range() plus tegra_gpio_direction_output() on the stack.
Do not mark the whole chip as can_sleep to paper over this: can_sleep describes whether get()/set() may sleep, and Tegra value access is MMIO. Remove the redundant pinctrl direction calls and keep pinctrl involvement in the existing request/free path.
--- drivers/gpio/gpio-tegra.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-)
drivers/gpio/gpio-tegra.c +2 -16
@@ -168,18 +168,11 @@ static int tegra_gpio_direction_input(st unsigned int offset) {struct tegra_gpio_info *tgi =gpiochip_get_data (chip); -int ret;tegra_gpio_mask_write (tgi,GPIO_MSK_OE (tgi, offset), offset,0 );tegra_gpio_enable (tgi, offset); - ret =pinctrl_gpio_direction_input (chip->base + offset); -if (ret <0 ) -dev_err (tgi->dev, -"Failed to set pinctrl input direction of GPIO %d: %d" , - chip->base + offset, ret); - -return ret; +return 0 ; } static int tegra_gpio_direction_output(struct gpio_chip *chip,
@@ -187,19 +180,12 @@ static int tegra_gpio_direction_output(s int value) {struct tegra_gpio_info *tgi =gpiochip_get_data (chip); -int ret;tegra_gpio_set (chip, offset, value);tegra_gpio_mask_write (tgi,GPIO_MSK_OE (tgi, offset), offset,1 );tegra_gpio_enable (tgi, offset); - ret =pinctrl_gpio_direction_output (chip->base + offset); -if (ret <0 ) -dev_err (tgi->dev, -"Failed to set pinctrl output direction of GPIO %d: %d" , - chip->base + offset, ret); - -return ret; +return 0 ; } static int tegra_gpio_get_direction(struct gpio_chip *chip,
Greg Kroah-Hartman
maintainerPATCH 127/235 bootconfig: do not put quotes on cmdline items unless necessary
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rasmus Villemoes <[email protected]>
[ Upstream commit 212f863fa8811c780abacc1d0404c573fdc0a2de ]
When trying to migrate to using bootconfig to embed the kernel's and PID1's command line with the kernel image itself, and so allowing changing that without modifying the bootloader, I noticed that /proc/cmdline changed from e.g.
console=ttymxc0,115200n8 cma=128M quiet -- --log-level=notice
to
console="ttymxc0,115200n8" cma="128M" quiet -- --log-level="notice"
The kernel parameters are parsed just fine, and the quotes are indeed stripped from the actual argv[] given to PID1. However, the quoting doesn't really serve any purpose and looks excessive, and might confuse some (naive) userspace tool trying to parse /proc/cmdline. So do not quote the value unless it contains whitespace.
Stable-dep-of: dec4d8118c17 ("bootconfig: fix NULL-pointer arithmetic in xbc_snprint_cmdline()")
--- init/main.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-)
init/main.c +9 -3
@@ -323,7 +323,7 @@ static int __init xbc_snprint_cmdline(ch {struct xbc_node *knode, *vnode;char *end = buf + size; -const char *val; +const char *val, *q;int ret;xbc_node_for_each_key_value (root, knode, val) {
@@ -341,8 +341,14 @@ static int __init xbc_snprint_cmdline(chcontinue ; }xbc_array_for_each_value (vnode, val) { - ret =snprintf (buf,rest (buf, end),"%s=\"%s\" " , - xbc_namebuf, val); +/* +* For prettier and more readable /proc/cmdline, only +* quote the value when necessary, i.e. when it contains +* whitespace. +*/ + q =strpbrk (val," \t\r\n" ) ?"\"" :"" ; + ret =snprintf (buf,rest (buf, end),"%s=%s%s%s " , + xbc_namebuf, q, val, q);if (ret <0 )return ret; buf += ret;
Greg Kroah-Hartman
maintainerPATCH 128/235 bootconfig: move xbc_snprint_cmdline() to lib/bootconfig.c
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Breno Leitao <[email protected]>
[ Upstream commit 5a643e4623238e14b03d75ca0d4eda0645720cee ]
Move xbc_snprint_cmdline() from init/main.c to lib/bootconfig.c so the function (and its xbc_namebuf scratch buffer) becomes part of the shared parser library. tools/bootconfig already compiles lib/bootconfig.c directly, which lets a follow-up patch reuse the same renderer in the userspace tool to convert a bootconfig file into a flat cmdline string at build time.
No functional change.
Stable-dep-of: dec4d8118c17 ("bootconfig: fix NULL-pointer arithmetic in xbc_snprint_cmdline()")
--- include/linux/bootconfig.h | 3 ++ init/main.c | 45 ------------------------------------ lib/bootconfig.c | 56 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 45 deletions(-)
include/linux/bootconfig.h +3 -0
@@ -202,6 +202,9 @@ static inline struct xbc_node * __init xint __init xbc_node_compose_key_after (struct xbc_node *root,struct xbc_node *node,char *buf,size_t size); +/* Render key/value pairs under @root as a flat cmdline string */ +int __init xbc_snprint_cmdline (char *buf,size_t size,struct xbc_node *root); +/** * xbc_node_compose_key() - Compose full key string of the XBC node * @node: An XBC node.
init/main.c +0 -45
51 lines not on this page · raw message
lib/bootconfig.c +56 -0
62 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 129/235 bootconfig: fix NULL-pointer arithmetic in xbc_snprint_cmdline()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Breno Leitao <[email protected]>
[ Upstream commit dec4d8118c179b3d12bca7e609054c6011c4f2ce ]
xbc_snprint_cmdline() is meant to be called twice: first with buf=NULL, size=0 to probe the rendered length, then with a real buffer to fill it (the standard snprintf() two-pass pattern). The probe call makes the function compute "buf + size" (NULL + 0) and, on every iteration, advance "buf += ret" from that NULL base and pass the result back into snprintf().
Pointer arithmetic on a NULL pointer is undefined behavior. It is harmless in the in-kernel callers today, but the follow-up patches run this same code in the userspace tools/bootconfig parser at kernel build time, where host UBSan / FORTIFY_SOURCE abort the build.
Track a running written length (size_t) instead of mutating @buf, and only form "buf + len" when @buf is non-NULL. snprintf(NULL, 0, ...) is itself well defined and returns the would-be length, so the two-pass "probe then fill" usage returns identical byte counts.
--- lib/bootconfig.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-)
lib/bootconfig.c +16 -7
@@ -348,10 +348,18 @@ static char xbc_namebuf[XBC_KEYLEN_MAX]int __init xbc_snprint_cmdline (char *buf,size_t size,struct xbc_node *root) {struct xbc_node *knode, *vnode; -char *end = buf + size;const char *val, *q; +size_t len =0 ;int ret; +/* +* Track the running written length rather than advancing @buf, so we +* never form "buf + size" or "buf += ret" while @buf is NULL (the +* size-probe call passes buf=NULL, size=0). NULL pointer arithmetic +* is undefined behavior and trips host UBSan / FORTIFY_SOURCE when +* this renderer runs at kernel build time. snprintf(NULL, 0, ...) +* itself is well defined and returns the would-be length. +*/ xbc_node_for_each_key_value (root, knode, val) { ret =xbc_node_compose_key_after (root, knode, xbc_namebuf,XBC_KEYLEN_MAX );
@@ -360,10 +368,11 @@ int __init xbc_snprint_cmdline(char *buf vnode =xbc_node_get_child (knode);if (!vnode) { - ret =snprintf (buf,rest (buf, end),"%s " , xbc_namebuf); + ret =snprintf (buf ? buf + len :NULL ,rest (len, size), +"%s " , xbc_namebuf);if (ret <0 )return ret; - buf += ret; + len += ret;continue ; }xbc_array_for_each_value (vnode, val) {
@@ -373,15 +382,15 @@ int __init xbc_snprint_cmdline(char *buf* whitespace. */ q =strpbrk (val," \t\r\n" ) ?"\"" :"" ; - ret =snprintf (buf,rest (buf, end),"%s=%s%s%s " , - xbc_namebuf, q, val, q); + ret =snprintf (buf ? buf + len :NULL ,rest (len, size), +"%s=%s%s%s " , xbc_namebuf, q, val, q);if (ret <0 )return ret; - buf += ret; + len += ret; } } -return buf - (end - size); +return len; }#undef rest
Greg Kroah-Hartman
maintainerPATCH 130/235 espintcp: use sk_msg_free_partial to fix partial send
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sabrina Dubroca <[email protected]>
[ Upstream commit 007800408002d871f5699bdb944f985896730b8f ]
sk_msg_free_partial() ensures consistency of the skmsg at every iteration, without having to manually handle uncharges and offsets. This simplifies the code, and fixes some bugs in skmsg accounting when we don't send the full contents.
--- net/xfrm/espintcp.c | 32 ++++++-------------------------- 1 file changed, 6 insertions(+), 26 deletions(-)
net/xfrm/espintcp.c +6 -26
@@ -208,41 +208,21 @@ static int espintcp_sendskmsg_locked(str {struct sk_msg *skmsg = &emsg->skmsg;struct scatterlist *sg; -int done =0 ;int ret; flags |=MSG_SENDPAGE_NOTLAST ; - sg = &skmsg->sg.data[skmsg->sg.start];do { -size_t size = sg->length - emsg->offset; -int offset = sg->offset + emsg->offset; -struct page *p; - - emsg->offset =0 ; - + sg = &skmsg->sg.data[skmsg->sg.start];if (sg_is_last (sg)) flags &= ~MSG_SENDPAGE_NOTLAST ; - p =sg_page (sg); -retry: - ret =do_tcp_sendpages (sk, p, offset, size, flags); -if (ret <0 ) { - emsg->offset = offset - sg->offset; - skmsg->sg.start += done; + ret =do_tcp_sendpages (sk,sg_page (sg), sg->offset, + sg->length, flags); +if (ret <0 )return ret; - } - -if (ret != size) { - offset += ret; - size -= ret; - goto retry; - } - done++; -put_page (p); -sk_mem_uncharge (sk, sg->length); - sg =sg_next (sg); - }while (sg); +sk_msg_free_partial (sk, skmsg, ret); + }while (skmsg->sg.size);memset (emsg,0 ,sizeof (*emsg));
Greg Kroah-Hartman
maintainerPATCH 131/235 net: ipa: fix SMEM state handle leaks in SMP2P init
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haoxiang Li <[email protected]>
[ Upstream commit 96ca1e658ae459276292bd6d971ab5d8c7e0379a ]
ipa_smp2p_init() acquires two Qualcomm SMEM state handles with qcom_smem_state_get(). However, neither the init error paths nor ipa_smp2p_exit() release them.
Release both handles with qcom_smem_state_put() in the init error paths and in ipa_smp2p_exit().
--- drivers/net/ipa/ipa_smp2p.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-)
drivers/net/ipa/ipa_smp2p.c +22 -8
@@ -222,19 +222,27 @@ int ipa_smp2p_init(struct ipa *ipa, bool &valid_bit);if (IS_ERR (valid_state))return PTR_ERR (valid_state); -if (valid_bit >=32 )/* BITS_PER_U32 */ -return -EINVAL ; +if (valid_bit >=32 ) {/* BITS_PER_U32 */ + ret = -EINVAL ; + goto err_valid_state_put; + } enabled_state =qcom_smem_state_get (dev,"ipa-clock-enabled" , &enabled_bit); -if (IS_ERR (enabled_state)) -return PTR_ERR (enabled_state); -if (enabled_bit >=32 )/* BITS_PER_U32 */ -return -EINVAL ; +if (IS_ERR (enabled_state)) { + ret =PTR_ERR (enabled_state); + goto err_valid_state_put; + } +if (enabled_bit >=32 ) {/* BITS_PER_U32 */ + ret = -EINVAL ; + goto err_enabled_state_put; + } smp2p =kzalloc (sizeof (*smp2p),GFP_KERNEL ); -if (!smp2p) -return -ENOMEM ; +if (!smp2p) { + ret = -ENOMEM ; + goto err_enabled_state_put; + } smp2p->ipa = ipa;
@@ -279,6 +287,10 @@ err_null_smp2p: ipa->smp2p =NULL ;mutex_destroy (&smp2p->mutex);kfree (smp2p); +err_enabled_state_put: +qcom_smem_state_put (enabled_state); +err_valid_state_put: +qcom_smem_state_put (valid_state);return ret; }
@@ -295,6 +307,8 @@ void ipa_smp2p_exit(struct ipa *ipa)ipa_smp2p_clock_release (ipa); ipa->smp2p =NULL ;mutex_destroy (&smp2p->mutex); +qcom_smem_state_put (smp2p->enabled_state); +qcom_smem_state_put (smp2p->valid_state);kfree (smp2p); }
Greg Kroah-Hartman
maintainerPATCH 132/235 octeontx2-pf: fix SQB pointer leak on init failure
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dawei Feng <[email protected]>
[ Upstream commit 62e7df6d042aeebd5efb581074e28865c04477be ]
otx2_init_hw_resources() initializes SQ aura and pool resources before several later setup steps. On failure, err_free_sq_ptrs only frees SQB pages, leaving the per-SQ sqb_ptrs arrays behind.
Use otx2_free_sq_res() for the SQ unwind path and let it free sqb_ptrs even when sq->sqe has not been allocated yet.
The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1.1.
An x86_64 allyesconfig build showed no new warnings. As we do not have an OcteonTX2 PF device and the corresponding AF mailbox setup to test with, no runtime testing was able to be performed.
--- drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +1 -2
@@ -1357,14 +1357,13 @@ static int otx2_init_hw_resources(structreturn err; err_free_nix_queues: -otx2_free_sq_res (pf);otx2_free_cq_res (pf);otx2_ctx_disable (mbox,NIX_AQ_CTYPE_RQ , false); err_free_txsch:if (otx2_txschq_stop (pf))dev_err (pf->dev,"%s failed to stop TX schedulers\n" , __func__); err_free_sq_ptrs: -otx2_sq_free_sqbs (pf); +otx2_free_sq_res (pf); err_free_rq_ptrs:otx2_free_aura_ptr (pf,AURA_NIX_RQ );otx2_ctx_disable (mbox,NPA_AQ_CTYPE_POOL , true);
Greg Kroah-Hartman
maintainerPATCH 133/235 fs/resctrl: Fix double-add of pseudo-locked regions RMID to free list
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Reinette Chatre <[email protected]>
[ Upstream commit b9f089723aee892efc77c349ae47a6b452b293c4 ]
A pseudo-locked group's RMID is freed when it is created. On unmount rmdir_all_sub() unconditionally frees all RMID of all groups, resulting in a double-free of the pseudo-locked group's RMID. The consequence of this is that the original free results in the pseudo-locked group's RMID being added to the rmid_free_lru linked list and the second free then attempts to add the same RMID entry to the rmid_free_lru again.
Do not double-free a pseudo-locked group's RMID.
--- arch/x86/kernel/cpu/resctrl/rdtgroup.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
arch/x86/kernel/cpu/resctrl/rdtgroup.c +7 -5
@@ -2376,10 +2376,6 @@ static void rmdir_all_sub(void)if (rdtgrp == &rdtgroup_default)continue ; -if (rdtgrp->mode ==RDT_MODE_PSEUDO_LOCKSETUP || - rdtgrp->mode ==RDT_MODE_PSEUDO_LOCKED ) -rdtgroup_pseudo_lock_remove (rdtgrp); -/* * Give any CPUs back to the default group. We cannot copy * cpu_online_mask because a CPU might have executed the
@@ -2388,7 +2384,13 @@ static void rmdir_all_sub(void)cpumask_or (&rdtgroup_default.cpu_mask, &rdtgroup_default.cpu_mask, &rdtgrp->cpu_mask); -free_rmid (rdtgrp->mon.rmid); +if (rdtgrp->mode ==RDT_MODE_PSEUDO_LOCKSETUP || + rdtgrp->mode ==RDT_MODE_PSEUDO_LOCKED ) { +rdtgroup_pseudo_lock_remove (rdtgrp); + }else { +/* Pseudo-locked group's RMID is freed during setup. */ +free_rmid (rdtgrp->mon.rmid); + }kernfs_remove (rdtgrp->kn);list_del (&rdtgrp->rdtgroup_list);
Greg Kroah-Hartman
maintainerPATCH 134/235 usb: gadget: bdc: fix checkpatch.pl spacing error
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chunfeng Yun <[email protected]>
[ Upstream commit 14a46f82d74e9bd8ce03cdbfcdb9f8408c1fc205 ]
fix checkpatch.pl error:
ERROR:SPACING: space prohibited before that ','
Stable-dep-of: 0583f2fbf8f8 ("usb: gadget: udc: bdc: free IRQ and drain func_wake_notify before teardown")
--- drivers/usb/gadget/udc/bdc/bdc_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/usb/gadget/udc/bdc/bdc_udc.c +1 -1
@@ -531,7 +531,7 @@ int bdc_udc_init(struct bdc *bdc) bdc->gadget.name =BRCM_BDC_NAME ; ret =devm_request_irq (bdc->dev, bdc->irq, bdc_udc_interrupt, -IRQF_SHARED ,BRCM_BDC_NAME , bdc); +IRQF_SHARED ,BRCM_BDC_NAME , bdc);if (ret) { dev_err(bdc->dev, "failed to request irq #%d %d\n",
Greg Kroah-Hartman
maintainerPATCH 135/235 usb: gadget: udc: bdc: free IRQ and drain func_wake_notify before teardown
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
[ Upstream commit 0583f2fbf8f86ae3a0ce054f96783dd83e65d9bb ]
The Broadcom BDC UDC driver registers its IRQ handler with devm_request_irq() in bdc_udc_init(), so the IRQ is released by devm only after bdc_remove() returns. devm releases resources in reverse LIFO order, but bdc_remove() runs bdc_udc_exit() and bdc_hw_exit() -> bdc_mem_free() manually before returning: bdc_udc_exit() tears down individual endpoint objects via bdc_free_ep(), while bdc_hw_exit() -> bdc_mem_free() frees and NULLs the DMA-coherent status-report ring (bdc->srr.sr_bds) and kfree()s bdc->bdc_ep_array. Both happen while the IRQ handler (bdc_udc_interrupt, requested with IRQF_SHARED) remains deliverable in the window up to the post-remove devm free_irq().
On receipt of a shared interrupt in that window, bdc_udc_interrupt() dereferences bdc->srr.sr_bds[bdc->srr.dqp_index] (NULL or freed DMA) and dispatches sr_handler callbacks that index into bdc_ep_array, causing a NULL-deref or use-after-free.
The same window affects the delayed_work bdc->func_wake_notify, which is armed from the IRQ handler via bdc_sr_uspc() -> handle_link_state_change() -> schedule_delayed_work() and may self-rearm from its own callback bdc_func_wake_timer(). No cancel exists anywhere in the driver, so a queued work item that fires after bdc_remove() returns and the bdc structure is devm-freed dereferences freed memory.
Replace devm_request_irq() with request_irq() and add an explicit free_irq(bdc->irq, bdc) in bdc_remove(). Clear BDC_GIE before free_irq() to stop the device from asserting interrupts, then free_irq() drains any in-flight handler, then cancel_delayed_work_sync() drains the func_wake_notify delayed work. This ordering ensures the IRQ handler and delayed work cannot interfere with the subsequent endpoint and DMA teardown in bdc_udc_exit() and bdc_hw_exit(). Wire the matching free_irq() into the bdc_udc_init() error path so the IRQ is released on probe failure, and route the bdc_init_ep() failure through err0 instead of returning directly.
This issue was found by an in-house static analysis tool.
--- drivers/usb/gadget/udc/bdc/bdc_core.c | 20 ++++++++++++++++++++ drivers/usb/gadget/udc/bdc/bdc_udc.c | 7 ++++--- 2 files changed, 24 insertions(+), 3 deletions(-)
drivers/usb/gadget/udc/bdc/bdc_core.c +20 -0
@@ -586,9 +586,29 @@ disable_clk:static int bdc_remove (struct platform_device *pdev) {struct bdc *bdc; +unsigned long flags; +u32 temp; bdc =platform_get_drvdata (pdev);dev_dbg (bdc->dev,"%s ()\n" , __func__); +/* +* Disable the device interrupt source before freeing the IRQ: +* clear BDC_GIE so the controller stops asserting interrupts, +* then free_irq drains any in-flight handler. +*/ +spin_lock_irqsave (&bdc->lock, flags); + temp =bdc_readl (bdc->regs,BDC_BDCSC ); + temp &= ~BDC_GIE ; +bdc_writel (bdc->regs,BDC_BDCSC , temp); +spin_unlock_irqrestore (&bdc->lock, flags); +free_irq (bdc->irq, bdc); +/* +* Drain func_wake_notify after free_irq: the IRQ handler arms this +* delayed_work via bdc_sr_uspc -> handle_link_state_change -> +* schedule_delayed_work (self-rearmed in bdc_func_wake_timer), so +* the IRQ must be released first to prevent re-arm after cancel. +*/ +cancel_delayed_work_sync (&bdc->func_wake_notify);bdc_udc_exit (bdc);bdc_hw_exit (bdc);bdc_phy_exit (bdc);
drivers/usb/gadget/udc/bdc/bdc_udc.c +4 -3
@@ -530,8 +530,8 @@ int bdc_udc_init(struct bdc *bdc) bdc->gadget.name =BRCM_BDC_NAME ; - ret =devm_request_irq (bdc->dev, bdc->irq, bdc_udc_interrupt, -IRQF_SHARED ,BRCM_BDC_NAME , bdc); + ret =request_irq (bdc->irq, bdc_udc_interrupt,IRQF_SHARED , +BRCM_BDC_NAME , bdc);if (ret) { dev_err(bdc->dev, "failed to request irq #%d %d\n",
@@ -542,7 +542,7 @@ int bdc_udc_init(struct bdc *bdc) ret =bdc_init_ep (bdc);if (ret) {dev_err (bdc->dev,"bdc init ep fail: %d\n" , ret); -return ret; + goto err0; } ret =usb_add_gadget_udc (bdc->dev, &bdc->gadget);
@@ -571,6 +571,7 @@ int bdc_udc_init(struct bdc *bdc) err1:usb_del_gadget_udc (&bdc->gadget); err0: +free_irq (bdc->irq, bdc);bdc_free_ep (bdc);return ret;
Greg Kroah-Hartman
maintainerPATCH 136/235 USB: serial: keyspan_pda: refactor write-room handling
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
[ Upstream commit 79fe6826a5ebee2724d432a736ec04d8dca143ba ]
Add helper to retrieve the available device transfer-buffer space.
Stable-dep-of: 42a97c0480f9 ("USB: serial: keyspan_pda: fix data loss on receive throttling")
--- drivers/usb/serial/keyspan_pda.c | 113 +++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 63 deletions(-)
drivers/usb/serial/keyspan_pda.c +50 -63
@@ -98,6 +98,42 @@ static const struct usb_device_id id_tab };#endif +static int keyspan_pda_get_write_room (struct keyspan_pda_private *priv) +{ +struct usb_serial_port *port = priv->port; +struct usb_serial *serial = priv->serial; +u8 *room; +int rc; + + room =kmalloc (1 ,GFP_KERNEL ); +if (!room) +return -ENOMEM ; + + rc =usb_control_msg (serial->dev, +usb_rcvctrlpipe (serial->dev,0 ), +6 ,/* write_room */ +USB_TYPE_VENDOR |USB_RECIP_INTERFACE + |USB_DIR_IN , +0 ,/* value: 0 means "remaining room" */ +0 ,/* index */ + room, +1 , +2000 ); +if (rc !=1 ) { +if (rc >=0 ) + rc = -EIO ; +dev_dbg (&port->dev,"roomquery failed: %d\n" , rc); + goto out_free; + } + +dev_dbg (&port->dev,"roomquery says %d\n" , *room); + rc = *room; +out_free: +kfree (room); + +return rc; +} +static void keyspan_pda_request_unthrottle (struct work_struct *work) { struct keyspan_pda_private *priv =
@@ -436,7 +472,6 @@ static int keyspan_pda_tiocmset(struct tstatic int keyspan_pda_write (struct tty_struct *tty,struct usb_serial_port *port,const unsigned char *buf,int count) { -struct usb_serial *serial = port->serial;int request_unthrottle =0 ;int rc =0 ;struct keyspan_pda_private *priv;
@@ -479,38 +514,11 @@ static int keyspan_pda_write(struct tty_device how much room it really has. This is done only on scheduler time, since usb_control_msg() sleeps. */ if (count > priv->tx_room && !in_interrupt ()) { -u8 *room; - - room =kmalloc (1 ,GFP_KERNEL ); -if (!room) { - rc = -ENOMEM ; + rc =keyspan_pda_get_write_room (priv); +if (rc <0 ) goto exit; - } - rc =usb_control_msg (serial->dev, -usb_rcvctrlpipe (serial->dev,0 ), -6 ,/* write_room */ -USB_TYPE_VENDOR |USB_RECIP_INTERFACE - |USB_DIR_IN , -0 ,/* value: 0 means "remaining room" */ -0 ,/* index */ - room, -1 , -2000 ); -if (rc >0 ) { -dev_dbg (&port->dev,"roomquery says %d\n" , *room); - priv->tx_room = *room; - } -kfree (room); -if (rc <0 ) { -dev_dbg (&port->dev,"roomquery failed\n" ); - goto exit; - } -if (rc ==0 ) { -dev_dbg (&port->dev,"roomquery returned 0 bytes\n" ); - rc = -EIO ;/* device didn't return any data */ - goto exit; - } + priv->tx_room = rc; }if (count >= priv->tx_room) {
@@ -612,48 +620,27 @@ static void keyspan_pda_dtr_rts(struct ustatic int keyspan_pda_open (struct tty_struct *tty,struct usb_serial_port *port) { -struct usb_serial *serial = port->serial; -u8 *room; -int rc =0 ; -struct keyspan_pda_private *priv; +struct keyspan_pda_private *priv =usb_get_serial_port_data (port); +int rc;/* find out how much room is in the Tx ring */ - room =kmalloc (1 ,GFP_KERNEL ); -if (!room) -return -ENOMEM ; + rc =keyspan_pda_get_write_room (priv); +if (rc <0 ) +return rc; - rc =usb_control_msg (serial->dev,usb_rcvctrlpipe (serial->dev,0 ), -6 ,/* write_room */ -USB_TYPE_VENDOR |USB_RECIP_INTERFACE - |USB_DIR_IN , -0 ,/* value */ -0 ,/* index */ - room, -1 , -2000 ); -if (rc <0 ) { -dev_dbg (&port->dev,"%s - roomquery failed\n" , __func__); - goto error; - } -if (rc ==0 ) { -dev_dbg (&port->dev,"%s - roomquery returned 0 bytes\n" , __func__); - rc = -EIO ; - goto error; - } - priv =usb_get_serial_port_data (port); - priv->tx_room = *room; - priv->tx_throttled = *room ?0 :1 ; + priv->tx_room = rc; + priv->tx_throttled = rc ?0 :1 ;/*Start reading from the device*/ rc =usb_submit_urb (port->interrupt_in_urb,GFP_KERNEL );if (rc) {dev_dbg (&port->dev,"%s - usb_submit_urb(read int) failed\n" , __func__); - goto error; +return rc; } -error: -kfree (room); -return rc; + +return 0 ; } +static void keyspan_pda_close (struct usb_serial_port *port) {struct keyspan_pda_private *priv =usb_get_serial_port_data (port);
Greg Kroah-Hartman
maintainerPATCH 137/235 USB: serial: keyspan_pda: fix write implementation
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
[ Upstream commit 7184933b52a6b3e64171819b77f0bab018696cb2 ]
Fix stalled writes by checking the available buffer space after requesting an unthrottle notification in case the device buffer is already empty so that no notification is ever sent (e.g. when doing single character writes).
This also means we can drop the room query from write() which was conditioned on in_interrupt() and prevented writing using this driver from atomic contexts (e.g. PPP).
Stable-dep-of: 42a97c0480f9 ("USB: serial: keyspan_pda: fix data loss on receive throttling")
--- drivers/usb/serial/keyspan_pda.c | 111 ++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 59 deletions(-)
drivers/usb/serial/keyspan_pda.c +52 -59
@@ -44,7 +44,6 @@struct keyspan_pda_private {int tx_room; -int tx_throttled;struct work_struct unthrottle_work;struct usb_serial *serial;struct usb_serial_port *port;
@@ -138,9 +137,13 @@ static void keyspan_pda_request_unthrott {struct keyspan_pda_private *priv = container_of(work, struct keyspan_pda_private, unthrottle_work); +struct usb_serial_port *port = priv->port;struct usb_serial *serial = priv->serial; +unsigned long flags;int result; +dev_dbg (&port->dev,"%s\n" , __func__); +/* ask the device to tell us when the tx buffer becomes sufficiently empty */ result = usb_control_msg(serial->dev,
@@ -156,8 +159,19 @@ static void keyspan_pda_request_unthrottif (result <0 )dev_dbg (&serial->dev->dev,"%s - error %d from usb_control_msg\n" , __func__, result); -} +/* +* Need to check available space after requesting notification in case +* buffer is already empty so that no notification is sent. +*/ + result =keyspan_pda_get_write_room (priv); +if (result >KEYSPAN_TX_THRESHOLD ) { +spin_lock_irqsave (&port->lock, flags); + priv->tx_room =max (priv->tx_room, result); +spin_unlock_irqrestore (&port->lock, flags); +usb_serial_port_softint (port); + } +}static void keyspan_pda_rx_interrupt (struct urb *urb) {
@@ -212,7 +226,6 @@ static void keyspan_pda_rx_interrupt(strbreak ;case 2 :/* tx unthrottle interrupt */ spin_lock_irqsave (&port->lock, flags); - priv->tx_throttled =0 ; priv->tx_room =max (priv->tx_room,KEYSPAN_TX_THRESHOLD );spin_unlock_irqrestore (&port->lock, flags);/* queue up a wakeup at scheduler time */
@@ -472,35 +485,42 @@ static int keyspan_pda_tiocmset(struct tstatic int keyspan_pda_write (struct tty_struct *tty,struct usb_serial_port *port,const unsigned char *buf,int count) { -int request_unthrottle =0 ; -int rc =0 ;struct keyspan_pda_private *priv;unsigned long flags; +int room; +int rc; priv =usb_get_serial_port_data (port); -/* guess how much room is left in the device's ring buffer, and if we -want to send more than that, check first, updating our notion of -what is left. If our write will result in no room left, ask the -device to give us an interrupt when the room available rises above -a threshold, and hold off all writers (eventually, those using -select() or poll() too) until we receive that unthrottle interrupt. -Block if we can't write anything at all, otherwise write as much as -we can. */ +/* +* Guess how much room is left in the device's ring buffer. If our +* write will result in no room left, ask the device to give us an +* interrupt when the room available rises above a threshold but also +* query how much room is currently available (in case our guess was +* too conservative and the buffer is already empty when the +* unthrottle work is scheduled). +*/ if (count ==0 ) {dev_dbg (&port->dev,"write request of 0 bytes\n" );return 0 ; } +if (count > port->bulk_out_size) + count = port->bulk_out_size; +/* we might block because of: the TX urb is in-flight (wait until it completes) the device is full (wait until it says there is room) */ spin_lock_irqsave (&port->lock, flags); -if (!test_bit (0 , &port->write_urbs_free) || priv->tx_throttled) { + room = priv->tx_room; +if (!test_bit (0 , &port->write_urbs_free) || room ==0 ) {spin_unlock_irqrestore (&port->lock, flags);return 0 ; }clear_bit (0 , &port->write_urbs_free); +if (count > room) + count = room; + priv->tx_room -= count;spin_unlock_irqrestore (&port->lock, flags);/* At this point the URB is in our control, nobody else can submit it
@@ -508,58 +528,30 @@ static int keyspan_pda_write(struct tty_finished). Also, the tx process is not throttled. So we are ready to write. */ - count = (count > port->bulk_out_size) ? port->bulk_out_size : count; +dev_dbg (&port->dev,"%s - count = %d, txroom = %d\n" , __func__, count, room); -/* Check if we might overrun the Tx buffer. If so, ask the -device how much room it really has. This is done only on -scheduler time, since usb_control_msg() sleeps. */ -if (count > priv->tx_room && !in_interrupt ()) { - rc =keyspan_pda_get_write_room (priv); -if (rc <0 ) - goto exit; +memcpy (port->write_urb->transfer_buffer, buf, count); + port->write_urb->transfer_buffer_length = count; - priv->tx_room = rc; - } + rc =usb_submit_urb (port->write_urb,GFP_ATOMIC ); +if (rc) { +dev_dbg (&port->dev,"usb_submit_urb(write bulk) failed\n" ); -if (count >= priv->tx_room) { -/* we're about to completely fill the Tx buffer, so -we'll be throttled afterwards. */ - count = priv->tx_room; - request_unthrottle =1 ; - } +spin_lock_irqsave (&port->lock, flags); + priv->tx_room =max (priv->tx_room, room + count); +spin_unlock_irqrestore (&port->lock, flags); -if (count) { -/* now transfer data */ -memcpy (port->write_urb->transfer_buffer, buf, count); -/* send the data out the bulk port */ - port->write_urb->transfer_buffer_length = count; - - priv->tx_room -= count; - - rc =usb_submit_urb (port->write_urb,GFP_ATOMIC ); -if (rc) { -dev_dbg (&port->dev,"usb_submit_urb(write bulk) failed\n" ); - goto exit; - } - }else { -/* There wasn't any room left, so we are throttled until -the buffer empties a bit */ - request_unthrottle =1 ; +set_bit (0 , &port->write_urbs_free); + +return rc; } -if (request_unthrottle) { - priv->tx_throttled =1 ;/* block writers */ +if (count == room)schedule_work (&priv->unthrottle_work); - } - rc = count; -exit: -if (rc <=0 ) -set_bit (0 , &port->write_urbs_free); -return rc; +return count; } -static void keyspan_pda_write_bulk_callback (struct urb *urb) {struct usb_serial_port *port = urb->context;
@@ -579,7 +571,7 @@ static int keyspan_pda_write_room(structint room =0 ;spin_lock_irqsave (&port->lock, flags); -if (test_bit (0 , &port->write_urbs_free) && !priv->tx_throttled) +if (test_bit (0 , &port->write_urbs_free)) room = priv->tx_room;spin_unlock_irqrestore (&port->lock, flags);
@@ -599,7 +591,7 @@ static int keyspan_pda_chars_in_buffer(sn_tty.c:normal_poll() ) that we're not writeable. */ spin_lock_irqsave (&port->lock, flags); -if (!test_bit (0 , &port->write_urbs_free) || priv->tx_throttled) +if (!test_bit (0 , &port->write_urbs_free) || priv->tx_room ==0 ) ret =256 ;spin_unlock_irqrestore (&port->lock, flags);return ret;
@@ -628,8 +620,9 @@ static int keyspan_pda_open(struct tty_sif (rc <0 )return rc; +spin_lock_irq (&port->lock); priv->tx_room = rc; - priv->tx_throttled = rc ?0 :1 ; +spin_unlock_irq (&port->lock);/*Start reading from the device*/ rc =usb_submit_urb (port->interrupt_in_urb,GFP_KERNEL );
Greg Kroah-Hartman
maintainerPATCH 138/235 USB: serial: keyspan_pda: add write-fifo support
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
[ Upstream commit 034e38e8f68767fb5438ae3e608ee82919674177 ]
Use the port write fifo and generic chars_and_buffer and write_room implementations when writing. This not only allows for more efficient transfers, but more importantly fixes the remaining issues related to the conservative write_room() implementation which could prevent the line discipline from making forward progress (e.g. waiting for n > 1 bytes of space to become available).
Note that this also allows using the driver for the system console without dropping data when the write URB is busy (including when adding carriage return on line feed).
Stable-dep-of: 42a97c0480f9 ("USB: serial: keyspan_pda: fix data loss on receive throttling")
--- drivers/usb/serial/keyspan_pda.c | 134 ++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 69 deletions(-)
drivers/usb/serial/keyspan_pda.c +65 -69
@@ -5,6 +5,7 @@* Copyright (C) 1999 - 2001 Greg Kroah-Hartman <[email protected]> * Copyright (C) 1999, 2000 Brian Warner <[email protected]> * Copyright (C) 2000 Al Borchers <[email protected]> +* Copyright (C) 2020 Johan Hovold <[email protected]> * * See Documentation/usb/usb-serial.rst for more information on using this * driver
@@ -37,7 +38,7 @@#undef XIRCOM#endif -#define DRIVER_AUTHOR "Brian Warner <[email protected]>" +#define DRIVER_AUTHOR "Brian Warner <[email protected]>, Johan Hovold <[email protected]>"#define DRIVER_DESC "USB Keyspan PDA Converter driver"#define KEYSPAN_TX_THRESHOLD 16
@@ -49,6 +50,7 @@ struct keyspan_pda_private {struct usb_serial_port *port; }; +static int keyspan_pda_write_start (struct usb_serial_port *port);#define KEYSPAN_VENDOR_ID 0x06cd#define KEYSPAN_PDA_FAKE_ID 0x0103
@@ -228,6 +230,9 @@ static void keyspan_pda_rx_interrupt(strspin_lock_irqsave (&port->lock, flags); priv->tx_room =max (priv->tx_room,KEYSPAN_TX_THRESHOLD );spin_unlock_irqrestore (&port->lock, flags); + +keyspan_pda_write_start (port); +/* queue up a wakeup at scheduler time */ usb_serial_port_softint (port);break ;
@@ -247,31 +252,30 @@ exit: __func__, retval); } -static void keyspan_pda_rx_throttle (struct tty_struct *tty) { -/* stop receiving characters. We just turn off the URB request, and -let chars pile up in the device. If we're doing hardware -flowcontrol, the device will signal the other end when its buffer -fills up. If we're doing XON/XOFF, this would be a good time to -send an XOFF, although it might make sense to foist that off -upon the device too. */ struct usb_serial_port *port = tty->driver_data; +/* +* Stop receiving characters. We just turn off the URB request, and +* let chars pile up in the device. If we're doing hardware +* flowcontrol, the device will signal the other end when its buffer +* fills up. If we're doing XON/XOFF, this would be a good time to +* send an XOFF, although it might make sense to foist that off upon +* the device too. +*/ usb_kill_urb (port->interrupt_in_urb); } -static void keyspan_pda_rx_unthrottle (struct tty_struct *tty) {struct usb_serial_port *port = tty->driver_data; -/* just restart the receive interrupt URB */ +/* just restart the receive interrupt URB */ if (usb_submit_urb (port->interrupt_in_urb,GFP_KERNEL ))dev_dbg (&port->dev,"usb_submit_urb(read urb) failed\n" ); } -static speed_t keyspan_pda_setbaud (struct usb_serial *serial,speed_t baud) {int rc;
@@ -482,15 +486,15 @@ static int keyspan_pda_tiocmset(struct treturn rc; } -static int keyspan_pda_write (struct tty_struct *tty, -struct usb_serial_port *port,const unsigned char *buf,int count) +static int keyspan_pda_write_start (struct usb_serial_port *port) { -struct keyspan_pda_private *priv; +struct keyspan_pda_private *priv =usb_get_serial_port_data (port);unsigned long flags; +struct urb *urb; +int count;int room;int rc; - priv =usb_get_serial_port_data (port);/* * Guess how much room is left in the device's ring buffer. If our * write will result in no room left, ask the device to give us an
@@ -499,105 +503,92 @@ static int keyspan_pda_write(struct tty_* too conservative and the buffer is already empty when the * unthrottle work is scheduled). */ -if (count ==0 ) { -dev_dbg (&port->dev,"write request of 0 bytes\n" ); -return 0 ; - } - -if (count > port->bulk_out_size) - count = port->bulk_out_size;/* we might block because of: the TX urb is in-flight (wait until it completes) the device is full (wait until it says there is room) */ spin_lock_irqsave (&port->lock, flags); + room = priv->tx_room; -if (!test_bit (0 , &port->write_urbs_free) || room ==0 ) { + count =kfifo_len (&port->write_fifo); + +if (!test_bit (0 , &port->write_urbs_free) || count ==0 || room ==0 ) {spin_unlock_irqrestore (&port->lock, flags);return 0 ; } -clear_bit (0 , &port->write_urbs_free); +__clear_bit (0 , &port->write_urbs_free); +if (count > room) count = room; +if (count > port->bulk_out_size) + count = port->bulk_out_size; + + urb = port->write_urb; + count =kfifo_out (&port->write_fifo, urb->transfer_buffer, count); + urb->transfer_buffer_length = count; + + port->tx_bytes += count; priv->tx_room -= count; -spin_unlock_irqrestore (&port->lock, flags); -/* At this point the URB is in our control, nobody else can submit it -again (the only sudden transition was the one from EINPROGRESS to -finished). Also, the tx process is not throttled. So we are -ready to write. */ +spin_unlock_irqrestore (&port->lock, flags);dev_dbg (&port->dev,"%s - count = %d, txroom = %d\n" , __func__, count, room); -memcpy (port->write_urb->transfer_buffer, buf, count); - port->write_urb->transfer_buffer_length = count; - - rc =usb_submit_urb (port->write_urb,GFP_ATOMIC ); + rc =usb_submit_urb (urb,GFP_ATOMIC );if (rc) {dev_dbg (&port->dev,"usb_submit_urb(write bulk) failed\n" );spin_lock_irqsave (&port->lock, flags); + port->tx_bytes -= count; priv->tx_room =max (priv->tx_room, room + count); +__set_bit (0 , &port->write_urbs_free);spin_unlock_irqrestore (&port->lock, flags); -set_bit (0 , &port->write_urbs_free); -return rc; }if (count == room)schedule_work (&priv->unthrottle_work); -return count; +return 0 ; }static void keyspan_pda_write_bulk_callback (struct urb *urb) {struct usb_serial_port *port = urb->context; +unsigned long flags; -set_bit (0 , &port->write_urbs_free); +spin_lock_irqsave (&port->lock, flags); + port->tx_bytes -= urb->transfer_buffer_length; +__set_bit (0 , &port->write_urbs_free); +spin_unlock_irqrestore (&port->lock, flags); + +keyspan_pda_write_start (port);/* queue up a wakeup at scheduler time */ usb_serial_port_softint (port); } - -static int keyspan_pda_write_room (struct tty_struct *tty) +static int keyspan_pda_write (struct tty_struct *tty,struct usb_serial_port *port, +const unsigned char *buf,int count) { -struct usb_serial_port *port = tty->driver_data; -struct keyspan_pda_private *priv =usb_get_serial_port_data (port); -unsigned long flags; -int room =0 ; - -spin_lock_irqsave (&port->lock, flags); -if (test_bit (0 , &port->write_urbs_free)) - room = priv->tx_room; -spin_unlock_irqrestore (&port->lock, flags); +int rc; -return room; -} +dev_dbg (&port->dev,"%s - count = %d\n" , __func__, count); -static int keyspan_pda_chars_in_buffer (struct tty_struct *tty) -{ -struct usb_serial_port *port = tty->driver_data; -struct keyspan_pda_private *priv; -unsigned long flags; -int ret =0 ; +if (!count) +return 0 ; - priv =usb_get_serial_port_data (port); + count =kfifo_in_locked (&port->write_fifo, buf, count, &port->lock); -/* when throttled, return at least WAKEUP_CHARS to tell select() (via -n_tty.c:normal_poll() ) that we're not writeable. */ + rc =keyspan_pda_write_start (port); +if (rc) +return rc; -spin_lock_irqsave (&port->lock, flags); -if (!test_bit (0 , &port->write_urbs_free) || priv->tx_room ==0 ) - ret =256 ; -spin_unlock_irqrestore (&port->lock, flags); -return ret; +return count; } -static void keyspan_pda_dtr_rts (struct usb_serial_port *port,int on) {struct usb_serial *serial = port->serial;
@@ -638,12 +629,19 @@ static void keyspan_pda_close(struct usb {struct keyspan_pda_private *priv =usb_get_serial_port_data (port); -usb_kill_urb (port->write_urb); +/* +* Stop the interrupt URB first as its completion handler may submit +* the write URB. +*/ usb_kill_urb (port->interrupt_in_urb); +usb_kill_urb (port->write_urb);cancel_work_sync (&priv->unthrottle_work); -} +spin_lock_irq (&port->lock); +kfifo_reset (&port->write_fifo); +spin_unlock_irq (&port->lock); +}/* download the firmware to a "fake" device (pre-renumeration) */ static int keyspan_pda_fake_startup (struct usb_serial *serial)
@@ -757,10 +755,8 @@ static struct usb_serial_driver keyspan_ .open = keyspan_pda_open, .close = keyspan_pda_close, .write = keyspan_pda_write, - .write_room = keyspan_pda_write_room, .write_bulk_callback = keyspan_pda_write_bulk_callback, .read_int_callback = keyspan_pda_rx_interrupt, - .chars_in_buffer = keyspan_pda_chars_in_buffer, .throttle = keyspan_pda_rx_throttle, .unthrottle = keyspan_pda_rx_unthrottle, .set_termios = keyspan_pda_set_termios,
Greg Kroah-Hartman
maintainerPATCH 139/235 USB: serial: keyspan_pda: clean up comments and whitespace
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
[ Upstream commit 491d6927f0de587c1d322d8b29a1187b7e06a221 ]
Clean up comment style, remove some stale or redundant comments and drop superfluous white space.
Stable-dep-of: 42a97c0480f9 ("USB: serial: keyspan_pda: fix data loss on receive throttling")
--- drivers/usb/serial/keyspan_pda.c | 97 ++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 50 deletions(-)
drivers/usb/serial/keyspan_pda.c +47 -50
@@ -11,7 +11,6 @@* driver */ -#include <linux/kernel.h> #include <linux/errno.h> #include <linux/slab.h>
@@ -146,8 +145,10 @@ static void keyspan_pda_request_unthrottdev_dbg (&port->dev,"%s\n" , __func__); -/* ask the device to tell us when the tx buffer becomes -sufficiently empty */ +/* +* Ask the device to tell us when the tx buffer becomes +* sufficiently empty. +*/ result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 7, /* request_unthrottle */
@@ -233,7 +234,6 @@ static void keyspan_pda_rx_interrupt(strkeyspan_pda_write_start (port); -/* queue up a wakeup at scheduler time */ usb_serial_port_softint (port);break ;default :
@@ -317,8 +317,6 @@ static speed_t keyspan_pda_setbaud(struc baud =9600 ; } -/* rather than figure out how to sleep while waiting for this -to complete, I just use the "legacy" API. */ rc =usb_control_msg (serial->dev,usb_sndctrlpipe (serial->dev,0 ),0 ,/* set baud */ USB_TYPE_VENDOR
@@ -331,10 +329,10 @@ static speed_t keyspan_pda_setbaud(struc 2000); /* timeout */if (rc <0 )return 0 ; +return baud; } -static void keyspan_pda_break_ctl (struct tty_struct *tty,int break_state) {struct usb_serial_port *port = tty->driver_data;
@@ -346,6 +344,7 @@ static void keyspan_pda_break_ctl(struct value =1 ;/* start break */ else value =0 ;/* clear break */ + result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 4, /* set break */ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
@@ -353,39 +352,35 @@ static void keyspan_pda_break_ctl(structif (result <0 )dev_dbg (&port->dev,"%s - error %d from usb_control_msg\n" , __func__, result); -/* there is something funky about this.. the TCSBRK that 'cu' performs -ought to translate into a break_ctl(-1),break_ctl(0) pair HZ/4 -seconds apart, but it feels like the break sent isn't as long as it -is on /dev/ttyS0 */ } -static void keyspan_pda_set_termios (struct tty_struct *tty,struct usb_serial_port *port,struct ktermios *old_termios) {struct usb_serial *serial = port->serial;speed_t speed; -/* cflag specifies lots of stuff: number of stop bits, parity, number -of data bits, baud. What can the device actually handle?: -CSTOPB (1 stop bit or 2) -PARENB (parity) -CSIZE (5bit .. 8bit) -There is minimal hw support for parity (a PSW bit seems to hold the -parity of whatever is in the accumulator). The UART either deals -with 10 bits (start, 8 data, stop) or 11 bits (start, 8 data, -1 special, stop). So, with firmware changes, we could do: -8N1: 10 bit -8N2: 11 bit, extra bit always (mark?) -8[EOMS]1: 11 bit, extra bit is parity -7[EOMS]1: 10 bit, b0/b7 is parity -7[EOMS]2: 11 bit, b0/b7 is parity, extra bit always (mark?) - -HW flow control is dictated by the tty->termios.c_cflags & CRTSCTS -bit. - -For now, just do baud. */ - +/* +* cflag specifies lots of stuff: number of stop bits, parity, number +* of data bits, baud. What can the device actually handle?: +* CSTOPB (1 stop bit or 2) +* PARENB (parity) +* CSIZE (5bit .. 8bit) +* There is minimal hw support for parity (a PSW bit seems to hold the +* parity of whatever is in the accumulator). The UART either deals +* with 10 bits (start, 8 data, stop) or 11 bits (start, 8 data, +* 1 special, stop). So, with firmware changes, we could do: +* 8N1: 10 bit +* 8N2: 11 bit, extra bit always (mark?) +* 8[EOMS]1: 11 bit, extra bit is parity +* 7[EOMS]1: 10 bit, b0/b7 is parity +* 7[EOMS]2: 11 bit, b0/b7 is parity, extra bit always (mark?) +* +* HW flow control is dictated by the tty->termios.c_cflags & CRTSCTS +* bit. +* +* For now, just do baud. +*/ speed =tty_get_baud_rate (tty); speed =keyspan_pda_setbaud (serial, speed);
@@ -394,17 +389,19 @@ static void keyspan_pda_set_termios(stru/* It hasn't changed so.. */ speed =tty_termios_baud_rate (old_termios); } -/* Only speed can change so copy the old h/w parameters -then encode the new speed */ +/* +* Only speed can change so copy the old h/w parameters then encode +* the new speed. +*/ tty_termios_copy_hw (&tty->termios, old_termios);tty_encode_baud_rate (tty, speed, speed); } - -/* modem control pins: DTR and RTS are outputs and can be controlled. -DCD, RI, DSR, CTS are inputs and can be read. All outputs can also be -read. The byte passed is: DTR(b7) DCD RI DSR CTS RTS(b2) unused unused */ - +/* +* Modem control pins: DTR and RTS are outputs and can be controlled. +* DCD, RI, DSR, CTS are inputs and can be read. All outputs can also be +* read. The byte passed is: DTR(b7) DCD RI DSR CTS RTS(b2) unused unused. +*/ static int keyspan_pda_get_modem_info (struct usb_serial *serial,unsigned char *value) {
@@ -428,7 +425,6 @@ static int keyspan_pda_get_modem_info(streturn rc; } -static int keyspan_pda_set_modem_info (struct usb_serial *serial,unsigned char value) {
@@ -504,10 +500,11 @@ static int keyspan_pda_write_start(struc* unthrottle work is scheduled). */ -/* we might block because of: -the TX urb is in-flight (wait until it completes) -the device is full (wait until it says there is room) -*/ +/* +* We might block because of: +* the TX urb is in-flight (wait until it completes) +* the device is full (wait until it says there is room) +*/ spin_lock_irqsave (&port->lock, flags); room = priv->tx_room;
@@ -566,7 +563,6 @@ static void keyspan_pda_write_bulk_callbkeyspan_pda_write_start (port); -/* queue up a wakeup at scheduler time */ usb_serial_port_softint (port); }
@@ -615,7 +611,6 @@ static int keyspan_pda_open(struct tty_s priv->tx_room = rc;spin_unlock_irq (&port->lock); -/*Start reading from the device*/ rc =usb_submit_urb (port->interrupt_in_urb,GFP_KERNEL );if (rc) {dev_dbg (&port->dev,"%s - usb_submit_urb(read int) failed\n" , __func__);
@@ -673,10 +668,12 @@ static int keyspan_pda_fake_startup(strureturn -ENOENT ; } -/* after downloading firmware Renumeration will occur in a -moment and the new device will bind to the real driver */ +/* +* After downloading firmware renumeration will occur in a moment and +* the new device will bind to the real driver. +*/ -/* we want this device to fail to have a driver assigned to it. */ +/* We want this device to fail to have a driver assigned to it. */ return 1 ; }
@@ -755,7 +752,7 @@ static struct usb_serial_driver keyspan_ .open = keyspan_pda_open, .close = keyspan_pda_close, .write = keyspan_pda_write, - .write_bulk_callback = keyspan_pda_write_bulk_callback, + .write_bulk_callback = keyspan_pda_write_bulk_callback, .read_int_callback = keyspan_pda_rx_interrupt, .throttle = keyspan_pda_rx_throttle, .unthrottle = keyspan_pda_rx_unthrottle,
Greg Kroah-Hartman
maintainerPATCH 140/235 USB: serial: keyspan_pda: fix data loss on receive throttling
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
[ Upstream commit 42a97c0480f96a2977e6d51ce512adc780f1ef5d ]
Killing the interrupt-in urb when the line disciple requests throttling may lead to data loss if an ongoing transfer is cancelled.
Instead set a flag to prevent the completion handler from resubmitting the urb until the port is unthrottled.
--- drivers/usb/serial/keyspan_pda.c | 44 +++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 9 deletions(-)
drivers/usb/serial/keyspan_pda.c +35 -9
@@ -47,6 +47,8 @@ struct keyspan_pda_private {struct work_struct unthrottle_work;struct usb_serial *serial;struct usb_serial_port *port; +bool throttled; +bool throttle_req; };static int keyspan_pda_write_start (struct usb_serial_port *port);
@@ -184,6 +186,7 @@ static void keyspan_pda_rx_interrupt(strint retval;int status = urb->status;struct keyspan_pda_private *priv; +bool throttled = false;unsigned long flags; priv =usb_get_serial_port_data (port);
@@ -245,16 +248,24 @@ static void keyspan_pda_rx_interrupt(str } exit: - retval =usb_submit_urb (urb,GFP_ATOMIC ); -if (retval) -dev_err (&port->dev, -"%s - usb_submit_urb failed with result %d\n" , - __func__, retval); +spin_lock_irqsave (&port->lock, flags); +if (priv->throttle_req) { + priv->throttled = true; + throttled = true; + } +spin_unlock_irqrestore (&port->lock, flags); + +if (!throttled) { + retval =usb_submit_urb (urb,GFP_ATOMIC ); +if (retval) +dev_err (&port->dev,"failed to resubmit in urb: %d\n" , retval); + } }static void keyspan_pda_rx_throttle (struct tty_struct *tty) {struct usb_serial_port *port = tty->driver_data; +struct keyspan_pda_private *priv =usb_get_serial_port_data (port);/* * Stop receiving characters. We just turn off the URB request, and
@@ -264,16 +275,29 @@ static void keyspan_pda_rx_throttle(stru* send an XOFF, although it might make sense to foist that off upon * the device too. */ -usb_kill_urb (port->interrupt_in_urb); +spin_lock_irq (&port->lock); + priv->throttle_req = true; +spin_unlock_irq (&port->lock); }static void keyspan_pda_rx_unthrottle (struct tty_struct *tty) {struct usb_serial_port *port = tty->driver_data; +struct keyspan_pda_private *priv =usb_get_serial_port_data (port); +bool throttled; +int ret; + +spin_lock_irq (&port->lock); + throttled = priv->throttled; + priv->throttled = false; + priv->throttle_req = false; +spin_unlock_irq (&port->lock); -/* just restart the receive interrupt URB */ -if (usb_submit_urb (port->interrupt_in_urb,GFP_KERNEL )) -dev_dbg (&port->dev,"usb_submit_urb(read urb) failed\n" ); +if (throttled) { + ret =usb_submit_urb (port->interrupt_in_urb,GFP_KERNEL ); +if (ret) +dev_err (&port->dev,"failed to submit in urb: %d\n" , ret); + } }static speed_t keyspan_pda_setbaud (struct usb_serial *serial,speed_t baud)
@@ -609,6 +633,8 @@ static int keyspan_pda_open(struct tty_sspin_lock_irq (&port->lock); priv->tx_room = rc; + priv->throttled = false; + priv->throttle_req = false;spin_unlock_irq (&port->lock); rc =usb_submit_urb (port->interrupt_in_urb,GFP_KERNEL );
Greg Kroah-Hartman
maintainerPATCH 141/235 KVM: x86: Drop @vcpu parameter from kvm_x86_ops.hwapic_isr_update()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
[ Upstream commit d39850f57d2102c6b46feb21237bc23bc42de4f7 ]
Drop the unused @vcpu parameter from hwapic_isr_update(). AMD/AVIC is unlikely to implement the helper, and VMX/APICv doesn't need the vCPU as it operates on the current VMCS. The result is somewhat odd, but allows for a decent amount of (future) cleanup in the APIC code.
No functional change intended.
Message-Id: <[email protected]>
Stable-dep-of: e800decd9c0a ("KVM: x86: Only reset TSC Deadline Timer in apic_timer_expired on KVM_RUN")
--- arch/x86/include/asm/kvm_host.h | 2 +- arch/x86/kvm/lapic.c | 10 ++++------ arch/x86/kvm/svm/avic.c | 2 +- arch/x86/kvm/svm/svm.h | 2 +- arch/x86/kvm/vmx/vmx.c | 2 +- 5 files changed, 8 insertions(+), 10 deletions(-)
arch/x86/include/asm/kvm_host.h +1 -1
@@ -1179,7 +1179,7 @@ struct kvm_x86_ops {void (*pre_update_apicv_exec_ctrl)(struct kvm *kvm,bool activate);void (*refresh_apicv_exec_ctrl)(struct kvm_vcpu *vcpu);void (*hwapic_irr_update)(struct kvm_vcpu *vcpu,int max_irr); -void (*hwapic_isr_update)(struct kvm_vcpu *vcpu,int isr); +void (*hwapic_isr_update)(int isr);bool (*guest_apic_has_interrupt)(struct kvm_vcpu *vcpu);void (*load_eoi_exitmap)(struct kvm_vcpu *vcpu,u64 *eoi_exit_bitmap);void (*set_virtual_apic_mode)(struct kvm_vcpu *vcpu);
arch/x86/kvm/lapic.c +4 -6
@@ -520,7 +520,7 @@ static inline void apic_set_isr(int vec,* just set SVI. */ if (unlikely (vcpu->arch.apicv_active)) - kvm_x86_ops.hwapic_isr_update (vcpu, vec); + kvm_x86_ops.hwapic_isr_update (vec);else { ++apic->isr_count;BUG_ON (apic->isr_count >MAX_APIC_VECTOR );
@@ -568,8 +568,7 @@ static inline void apic_clear_isr(int ve* and must be left alone. */ if (unlikely (vcpu->arch.apicv_active)) - kvm_x86_ops.hwapic_isr_update (vcpu, -apic_find_highest_isr (apic)); + kvm_x86_ops.hwapic_isr_update (apic_find_highest_isr (apic));else { --apic->isr_count;BUG_ON (apic->isr_count <0 );
@@ -2383,7 +2382,7 @@ void kvm_lapic_reset(struct kvm_vcpu *vcif (vcpu->arch.apicv_active) { kvm_x86_ops.apicv_post_state_restore (vcpu); kvm_x86_ops.hwapic_irr_update (vcpu,-1 ); - kvm_x86_ops.hwapic_isr_update (vcpu,-1 ); + kvm_x86_ops.hwapic_isr_update (-1 ); } vcpu->arch.apic_arb_prio =0 ;
@@ -2651,8 +2650,7 @@ int kvm_apic_set_state(struct kvm_vcpu * kvm_x86_ops.apicv_post_state_restore (vcpu); kvm_x86_ops.hwapic_irr_update (vcpu,apic_find_highest_irr (apic)); - kvm_x86_ops.hwapic_isr_update (vcpu, -apic_find_highest_isr (apic)); + kvm_x86_ops.hwapic_isr_update (apic_find_highest_isr (apic)); }kvm_make_request (KVM_REQ_EVENT , vcpu);if (ioapic_in_kernel (vcpu->kvm))
arch/x86/kvm/svm/avic.c +1 -1
@@ -603,7 +603,7 @@ void svm_hwapic_irr_update(struct kvm_vc { } -void svm_hwapic_isr_update (struct kvm_vcpu *vcpu,int max_isr) +void svm_hwapic_isr_update (int max_isr) { }
arch/x86/kvm/svm/svm.h +1 -1
@@ -457,7 +457,7 @@ bool svm_check_apicv_inhibit_reasons(ulovoid svm_pre_update_apicv_exec_ctrl (struct kvm *kvm,bool activate);void svm_load_eoi_exitmap (struct kvm_vcpu *vcpu,u64 *eoi_exit_bitmap);void svm_hwapic_irr_update (struct kvm_vcpu *vcpu,int max_irr); -void svm_hwapic_isr_update (struct kvm_vcpu *vcpu,int max_isr); +void svm_hwapic_isr_update (int max_isr);int svm_deliver_avic_intr (struct kvm_vcpu *vcpu,int vec);bool svm_dy_apicv_has_pending_interrupt (struct kvm_vcpu *vcpu); int svm_update_pi_irte(struct kvm *kvm, unsigned int host_irq,
arch/x86/kvm/vmx/vmx.c +1 -1
@@ -6403,7 +6403,7 @@ static void vmx_set_apic_access_page_addput_page (page); } -static void vmx_hwapic_isr_update (struct kvm_vcpu *vcpu,int max_isr) +static void vmx_hwapic_isr_update (int max_isr) {u16 status;u8 old;
Greg Kroah-Hartman
maintainerPATCH 142/235 KVM: x86: Check for in-kernel xAPIC when querying APICv for directed yield
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
[ Upstream commit ae801e1303e939ad5ebd9f390bdcc57275ada33b ]
Use kvm_vcpu_apicv_active() to check if APICv is active when seeing if a vCPU is a candidate for directed yield due to a pending ACPIv interrupt. This will allow moving apicv_active into kvm_lapic without introducing a potential NULL pointer deref (kvm_vcpu_apicv_active() effectively adds a pre-check on the vCPU having an in-kernel APIC).
No functional change intended.
Message-Id: <[email protected]>
Stable-dep-of: e800decd9c0a ("KVM: x86: Only reset TSC Deadline Timer in apic_timer_expired on KVM_RUN")
--- arch/x86/kvm/x86.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
arch/x86/kvm/x86.c +2 -1
@@ -11122,7 +11122,8 @@ bool kvm_arch_dy_runnable(struct kvm_vcp kvm_test_request(KVM_REQ_EVENT, vcpu))return true; -if (vcpu->arch.apicv_active && kvm_x86_ops.dy_apicv_has_pending_interrupt (vcpu)) +if (kvm_vcpu_apicv_active (vcpu) && + kvm_x86_ops.dy_apicv_has_pending_interrupt (vcpu))return true;return false;
Greg Kroah-Hartman
maintainerPATCH 143/235 KVM: x86: Move "apicv_active" into "struct kvm_lapic"
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
[ Upstream commit ce0a58f4756c14d7646cfdf279dbaada9d7712a0 ]
Move the per-vCPU apicv_active flag into KVM's local APIC instance. APICv is fully dependent on an in-kernel local APIC, but that's not at all clear when reading the current code due to the flag being stored in the generic kvm_vcpu_arch struct.
No functional change intended.
Message-Id: <[email protected]>
Stable-dep-of: e800decd9c0a ("KVM: x86: Only reset TSC Deadline Timer in apic_timer_expired on KVM_RUN")
--- arch/x86/include/asm/kvm_host.h | 1 - arch/x86/kvm/lapic.c | 31 ++++++++++--------------------- arch/x86/kvm/lapic.h | 3 ++- arch/x86/kvm/svm/avic.c | 2 +- arch/x86/kvm/vmx/vmx.c | 7 ++++--- arch/x86/kvm/x86.c | 12 ++++++------ 6 files changed, 23 insertions(+), 33 deletions(-)
arch/x86/include/asm/kvm_host.h +0 -1
@@ -544,7 +544,6 @@ struct kvm_vcpu_arch {u64 efer;u64 apic_base;struct kvm_lapic *apic;/* kernel irqchip context */ -bool apicv_active;bool load_eoi_exitmap_pending;DECLARE_BITMAP (ioapic_handled_vectors,256 );unsigned long apic_attention;
arch/x86/kvm/lapic.c +10 -21
@@ -482,14 +482,10 @@ static inline int apic_find_highest_irr(static inline void apic_clear_irr (int vec,struct kvm_lapic *apic) { -struct kvm_vcpu *vcpu; - - vcpu = apic->vcpu; - -if (unlikely (vcpu->arch.apicv_active)) { +if (unlikely (apic->apicv_active)) {/* need to update RVI */ kvm_lapic_clear_vector (vec, apic->regs +APIC_IRR ); - kvm_x86_ops.hwapic_irr_update (vcpu, + kvm_x86_ops.hwapic_irr_update (apic->vcpu,apic_find_highest_irr (apic)); }else { apic->irr_pending = false;
@@ -507,19 +503,15 @@ EXPORT_SYMBOL_GPL(kvm_apic_clear_irr);static inline void apic_set_isr (int vec,struct kvm_lapic *apic) { -struct kvm_vcpu *vcpu; -if (__apic_test_and_set_vector (vec, apic->regs +APIC_ISR ))return ; - vcpu = apic->vcpu; -/* * With APIC virtualization enabled, all caching is disabled * because the processor can modify ISR under the hood. Instead * just set SVI. */ -if (unlikely (vcpu->arch.apicv_active)) +if (unlikely (apic->apicv_active)) kvm_x86_ops.hwapic_isr_update (vec);else { ++apic->isr_count;
@@ -554,12 +546,9 @@ static inline int apic_find_highest_isr(static inline void apic_clear_isr (int vec,struct kvm_lapic *apic) { -struct kvm_vcpu *vcpu;if (!__apic_test_and_clear_vector (vec, apic->regs +APIC_ISR ))return ; - vcpu = apic->vcpu; -/* * We do get here for APIC virtualization enabled if the guest * uses the Hyper-V APIC enlightenment. In this case we may need
@@ -567,7 +556,7 @@ static inline void apic_clear_isr(int ve* on the other hand isr_count and highest_isr_cache are unused * and must be left alone. */ -if (unlikely (vcpu->arch.apicv_active)) +if (unlikely (apic->apicv_active)) kvm_x86_ops.hwapic_isr_update (apic_find_highest_isr (apic));else { --apic->isr_count;
@@ -706,7 +695,7 @@ static void pv_eoi_clr_pending(struct kvstatic int apic_has_interrupt_for_ppr (struct kvm_lapic *apic,u32 ppr) {int highest_irr; -if (apic->vcpu->arch.apicv_active) +if (apic->apicv_active) highest_irr = kvm_x86_ops.sync_pir_to_irr (apic->vcpu);else highest_irr =apic_find_highest_irr (apic);
@@ -1538,7 +1527,7 @@ static bool lapic_timer_int_injected(strint vec = reg &APIC_VECTOR_MASK ;void *bitmap = apic->regs +APIC_ISR ; -if (vcpu->arch.apicv_active) +if (apic->apicv_active) bitmap = apic->regs +APIC_IRR ;if (apic_test_vector (vec, bitmap))
@@ -1647,7 +1636,7 @@ static void apic_timer_expired(struct kvif (apic_lvtt_tscdeadline (apic) || ktimer->hv_timer_in_use) ktimer->expired_tscdeadline = ktimer->tscdeadline; -if (!from_timer_fn && vcpu->arch.apicv_active) { +if (!from_timer_fn && apic->apicv_active) {WARN_ON (kvm_get_running_vcpu () != vcpu);kvm_apic_inject_pending_timer_irqs (apic);return ;
@@ -2317,7 +2306,7 @@ void kvm_apic_update_apicv(struct kvm_vc {struct kvm_lapic *apic = vcpu->arch.apic; -if (vcpu->arch.apicv_active) { +if (apic->apicv_active) {/* irr_pending is always true when apicv is activated. */ apic->irr_pending = true; apic->isr_count =1 ;
@@ -2379,7 +2368,7 @@ void kvm_lapic_reset(struct kvm_vcpu *vc vcpu->arch.apic_base | MSR_IA32_APICBASE_BSP); vcpu->arch.pv_eoi.msr_val =0 ;apic_update_ppr (apic); -if (vcpu->arch.apicv_active) { +if (apic->apicv_active) { kvm_x86_ops.apicv_post_state_restore (vcpu); kvm_x86_ops.hwapic_irr_update (vcpu,-1 ); kvm_x86_ops.hwapic_isr_update (-1 );
@@ -2646,7 +2635,7 @@ int kvm_apic_set_state(struct kvm_vcpu *__start_apic_timer (apic,APIC_TMCCT );kvm_apic_update_apicv (vcpu); apic->highest_isr_cache =-1 ; -if (vcpu->arch.apicv_active) { +if (apic->apicv_active) { kvm_x86_ops.apicv_post_state_restore (vcpu); kvm_x86_ops.hwapic_irr_update (vcpu,apic_find_highest_irr (apic));
arch/x86/kvm/lapic.h +2 -1
@@ -47,6 +47,7 @@ struct kvm_lapic {struct kvm_timer lapic_timer;u32 divide_count;struct kvm_vcpu *vcpu; +bool apicv_active;bool sw_enabled;bool irr_pending;bool lvt0_in_nmi_mode;
@@ -216,7 +217,7 @@ static inline int apic_x2apic_mode(strucstatic inline bool kvm_vcpu_apicv_active (struct kvm_vcpu *vcpu) { -return vcpu->arch.apic && vcpu->arch.apicv_active; +return vcpu->arch.apic && vcpu->arch.apic->apicv_active; }static inline bool kvm_apic_has_events (struct kvm_vcpu *vcpu)
arch/x86/kvm/svm/avic.c +1 -1
@@ -673,7 +673,7 @@ void svm_load_eoi_exitmap(struct kvm_vcpint svm_deliver_avic_intr (struct kvm_vcpu *vcpu,int vec) { -if (!vcpu->arch.apicv_active) +if (!kvm_vcpu_apicv_active (vcpu))return -1 ;kvm_lapic_set_irr (vec, vcpu->arch.apic);
arch/x86/kvm/vmx/vmx.c +4 -3
@@ -4116,7 +4116,8 @@ static int vmx_deliver_posted_interrupt(if (!r)return 0 ; -if (!vcpu->arch.apicv_active) +/* Note, this is called iff the local APIC is in-kernel. */ +if (!vcpu->arch.apic->apicv_active)return -1 ;if (pi_test_and_set_pir (vector, &vmx->pi_desc))
@@ -6457,7 +6458,7 @@ static int vmx_sync_pir_to_irr(struct kvint max_irr;bool max_irr_updated; -WARN_ON (!vcpu->arch.apicv_active); +WARN_ON (!kvm_vcpu_apicv_active (vcpu));if (pi_test_on (&vmx->pi_desc)) {pi_clear_on (&vmx->pi_desc);/*
@@ -6983,7 +6984,7 @@ reenter_guest:* but it would incur the cost of a retpoline for now. * Revisit once static calls are available. */ -if (vcpu->arch.apicv_active) +if (kvm_vcpu_apicv_active (vcpu))vmx_sync_pir_to_irr (vcpu); goto reenter_guest; }
arch/x86/kvm/x86.c +6 -6
@@ -4182,7 +4182,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *static int kvm_vcpu_ioctl_get_lapic (struct kvm_vcpu *vcpu,struct kvm_lapic_state *s) { -if (vcpu->arch.apicv_active) +if (kvm_vcpu_apicv_active (vcpu)) kvm_x86_ops.sync_pir_to_irr (vcpu);return kvm_apic_get_state (vcpu, s);
@@ -8504,7 +8504,7 @@ static void update_cr8_intercept(structif (!lapic_in_kernel (vcpu))return ; -if (vcpu->arch.apicv_active) +if (vcpu->arch.apic->apicv_active)return ;if (!vcpu->arch.apic->vapic_addr)
@@ -8967,7 +8967,7 @@ void kvm_vcpu_update_apicv(struct kvm_vcif (!lapic_in_kernel (vcpu))return ; - vcpu->arch.apicv_active =kvm_apicv_activated (vcpu->kvm); + vcpu->arch.apic->apicv_active =kvm_apicv_activated (vcpu->kvm);kvm_apic_update_apicv (vcpu); kvm_x86_ops.refresh_apicv_exec_ctrl (vcpu); }
@@ -9031,7 +9031,7 @@ static void vcpu_scan_ioapic(struct kvm_if (irqchip_split (vcpu->kvm))kvm_scan_ioapic_routes (vcpu, vcpu->arch.ioapic_handled_vectors);else { -if (vcpu->arch.apicv_active) +if (kvm_vcpu_apicv_active (vcpu)) kvm_x86_ops.sync_pir_to_irr (vcpu);if (ioapic_in_kernel (vcpu->kvm))kvm_ioapic_scan_entry (vcpu, vcpu->arch.ioapic_handled_vectors);
@@ -9275,7 +9275,7 @@ static int vcpu_enter_guest(struct kvm_v* This handles the case where a posted interrupt was * notified with kvm_vcpu_kick. */ -if (kvm_lapic_enabled (vcpu) && vcpu->arch.apicv_active) +if (kvm_lapic_enabled (vcpu) &&kvm_vcpu_apicv_active (vcpu)) kvm_x86_ops.sync_pir_to_irr (vcpu);if (kvm_vcpu_exit_request (vcpu)) {
@@ -10254,7 +10254,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpuif (r <0 ) goto fail_mmu_destroy;if (kvm_apicv_activated (vcpu->kvm)) - vcpu->arch.apicv_active = true; + vcpu->arch.apic->apicv_active = true; }else static_key_slow_inc (&kvm_no_apic_vcpu);
Greg Kroah-Hartman
maintainerPATCH 144/235 KVM: Introduce vcpu->wants_to_run
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Matlack <[email protected]>
[ Upstream commit a6816314af5749cd88944bfdceb270c627cdf348 ]
Introduce vcpu->wants_to_run to indicate when a vCPU is in its core run loop, i.e. when the vCPU is running the KVM_RUN ioctl and immediate_exit was not set.
Replace all references to vcpu->run->immediate_exit with !vcpu->wants_to_run to avoid TOCTOU races with userspace. For example, a malicious userspace could invoked KVM_RUN with immediate_exit=true and then after KVM reads it to set wants_to_run=false, flip it to false. This would result in the vCPU running in KVM_RUN with wants_to_run=false. This wouldn't cause any real bugs today but is a dangerous landmine.
Stable-dep-of: e800decd9c0a ("KVM: x86: Only reset TSC Deadline Timer in apic_timer_expired on KVM_RUN")
--- arch/arm64/kvm/arm.c | 2 +- arch/mips/kvm/mips.c | 2 +- arch/powerpc/kvm/powerpc.c | 2 +- arch/s390/kvm/kvm-s390.c | 2 +- arch/x86/kvm/x86.c | 4 ++-- include/linux/kvm_host.h | 1 + virt/kvm/kvm_main.c | 3 +++ 7 files changed, 10 insertions(+), 6 deletions(-)
arch/arm64/kvm/arm.c +1 -1
@@ -686,7 +686,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vreturn ret; } -if (run->immediate_exit) +if (!vcpu->wants_to_run)return -EINTR ;vcpu_load (vcpu);
arch/mips/kvm/mips.c +1 -1
@@ -464,7 +464,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_v vcpu->mmio_needed =0 ; } -if (vcpu->run->immediate_exit) +if (!vcpu->wants_to_run) goto out;lose_fpu (1 );
arch/powerpc/kvm/powerpc.c +1 -1
@@ -1832,7 +1832,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vkvm_sigset_activate (vcpu); -if (run->immediate_exit) +if (!vcpu->wants_to_run) r = -EINTR ;else r =kvmppc_vcpu_run (vcpu);
arch/s390/kvm/kvm-s390.c +1 -1
@@ -4373,7 +4373,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vstruct kvm_run *kvm_run = vcpu->run;int rc; -if (kvm_run->immediate_exit) +if (!vcpu->wants_to_run)return -EINTR ; if (kvm_run->kvm_valid_regs & ~KVM_SYNC_S390_VALID_FIELDS ||
arch/x86/kvm/x86.c +2 -2
@@ -9648,7 +9648,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vkvm_load_guest_fpu (vcpu);if (unlikely (vcpu->arch.mp_state ==KVM_MP_STATE_UNINITIALIZED )) { -if (kvm_run->immediate_exit) { +if (!vcpu->wants_to_run) { r = -EINTR ; goto out; }
@@ -9692,7 +9692,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_v }else WARN_ON (vcpu->arch.pio.count || vcpu->mmio_needed); -if (kvm_run->immediate_exit) +if (!vcpu->wants_to_run) r = -EINTR ;else r =vcpu_run (vcpu);
include/linux/kvm_host.h +1 -0
@@ -318,6 +318,7 @@ struct kvm_vcpu { bool dy_eligible; } spin_loop;#endif +bool wants_to_run;bool preempted;bool ready;struct kvm_vcpu_arch arch;
virt/kvm/kvm_main.c +3 -0
@@ -3366,7 +3366,10 @@ static long kvm_vcpu_ioctl(struct file *synchronize_rcu ();put_pid (oldpid); } + vcpu->wants_to_run = !READ_ONCE (vcpu->run->immediate_exit); r =kvm_arch_vcpu_ioctl_run (vcpu); + vcpu->wants_to_run = false; +trace_kvm_userspace_exit (vcpu->run->exit_reason, r);break ; }
Greg Kroah-Hartman
maintainerPATCH 145/235 KVM: x86: Only reset TSC Deadline Timer in apic_timer_expired on KVM_RUN
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Venkatesh Srinivas <[email protected]>
[ Upstream commit e800decd9c0ac4349bcd8f8f9b29fd21fe93165e ]
On Intel platforms with a VMX preemption timer and APICv, if a VMM calls KVM_GET_LAPIC before KVM_GET_MSRS to save the vCPU state, it is possible to lose a pending timer interrupt.
If the thread running these ioctls is migrated to another core after calling KVM_GET_LAPIC but before KVM_GET_MSRS and the guest is using their LAPIC timer in TSC-deadline mode, not only does the save LAPIC state not carry the pending interrupt, the TSCDEADLINE MSR will be zeroed.
After migration across CPUs, KVM_GET_MSRS calls vcpu_load, posting the interrupt and clearing the MSR:vcpu_load() ->
kvm_arch_vcpu_load() ->
kvm_lapic_restart_hv_timer() ->
start_hv_timer() ->
apic_timer_expired() ->
kvm_apic_inject_pending_timer_irqs()
. post interrupt into the LAPIC state
. clear IA32_TSCDEADLINEThe saved LAPIC state will be missing the pending interrupt and the saved MSR will be zero. Oops.
Fix by only posting an interrupt when we're attempting to enter the guest (vcpu->wants_to_run == true), not for vcpu_load from other paths.
Message-ID: <[email protected]>
--- arch/x86/kvm/lapic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/kvm/lapic.c +1 -1
@@ -1636,7 +1636,7 @@ static void apic_timer_expired(struct kvif (apic_lvtt_tscdeadline (apic) || ktimer->hv_timer_in_use) ktimer->expired_tscdeadline = ktimer->tscdeadline; -if (!from_timer_fn && apic->apicv_active) { +if (!from_timer_fn && apic->apicv_active && vcpu->wants_to_run) {WARN_ON (kvm_get_running_vcpu () != vcpu);kvm_apic_inject_pending_timer_irqs (apic);return ;
Greg Kroah-Hartman
maintainerPATCH 146/235 drm/dp/mst: fix buffer overflows in sideband chunk accumulation
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashutosh Desai <[email protected]>
[ Upstream commit 55bd5e685bda455b9b50c835f8c8442d52a344a3 ]
drm_dp_sideband_append_payload() has three related bugs when processing device-provided sideband reply data:
1. Zero-length curchunk_len underflow: msg_len is a 6-bit field taken directly from the DP sideband header. If a device sends msg_len=0, curchunk_len is set to zero. The condition (curchunk_idx >= curchunk_len) is immediately true, and curchunk_len-1 wraps to 255 (u8 underflow). drm_dp_msg_data_crc4() reads 255 bytes from chunk[48], then memcpy() writes 255 bytes into msg[], both far out of bounds.
2. chunk[48] overflow: curchunk_len can reach 63 (6-bit field). chunk[] is only 48 bytes. Multi-iteration payload assembly appends 16-byte blocks until curchunk_idx reaches curchunk_len, writing up to 15 bytes past the end of chunk[] into msg[].
3. msg[256] overflow: each chunk contributes (curchunk_len-1) bytes to msg[]. No check ensures curlen + (curchunk_len-1) stays within msg[256], so the memcpy can spill into adjacent struct fields.
All three are reachable from any DP MST device that can forge sideband reply messages on a physical connection.
--- drivers/gpu/drm/drm_dp_mst_topology.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/gpu/drm/drm_dp_mst_topology.c +9 -0
@@ -778,6 +778,12 @@ static bool drm_dp_sideband_append_paylo {u8 crc4; +/* curchunk_len must be >= 1 (min 1 CRC byte) and fit in chunk[] */ +if (!msg->curchunk_len || + msg->curchunk_len >ARRAY_SIZE (msg->chunk) || + msg->curchunk_idx + replybuflen >ARRAY_SIZE (msg->chunk)) +return false; +memcpy (&msg->chunk[msg->curchunk_idx], replybuf, replybuflen); msg->curchunk_idx += replybuflen;
@@ -788,6 +794,9 @@ static bool drm_dp_sideband_append_payloprint_hex_dump (KERN_DEBUG ,"wrong crc" ,DUMP_PREFIX_NONE ,16 ,1 , msg->chunk, msg->curchunk_len, false); +/* Guard against accumulated msg[] overflow */ +if (msg->curlen + msg->curchunk_len -1 >ARRAY_SIZE (msg->msg)) +return false;/* copy chunk into bigger msg */ memcpy (&msg->msg[msg->curlen], msg->chunk, msg->curchunk_len -1 ); msg->curlen += msg->curchunk_len -1 ;
Greg Kroah-Hartman
maintainerPATCH 147/235 usb: gadget: f_tcm: synchronize delayed set_alt with teardown
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cen Zhang <[email protected]>
[ Upstream commit 79e2d75725c85607f8a9d87ae9cace62a19f767d ]
The f_tcm set_alt() path defers endpoint setup to a work item and completes the delayed status response from process context. The delayed work uses f_tcm private state and may complete the setup request after disconnect or function teardown has already moved on.
Cancel and drain the delayed set_alt work when the function is unbound or freed. For disable paths, which are reached under the composite device lock, use a small state machine and a non-sleeping cancellation path instead of cancel_work_sync(). If the work is already running, mark it cancelled and let the worker own the cleanup; otherwise tcm_disable() can cancel the queued work and clean up immediately.
Also serialize the final delayed-status completion with the cancellation check while holding the composite device lock. This prevents a disconnect from clearing delayed_status while the worker is about to complete the control request.
Validation reproduced this kernel report:
BUG: KASAN: slab-use-after-free in tcm_delayed_set_alt+0x6c/0xef0 Call Trace: <TASK> dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ? tcm_delayed_set_alt+0x6c/0xef0 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x188/0x320 ? tcm_delayed_set_alt+0x6c/0xef0 kasan_report+0xe0/0x110 ? tcm_delayed_set_alt+0x6c/0xef0 tcm_delayed_set_alt+0x6c/0xef0 ? __pfx_tcm_delayed_set_alt+0x10/0x10 ? process_one_work+0x4cb/0xb90 ? rcu_is_watching+0x20/0x50 ? tcm_delayed_set_alt+0x9/0xef0 process_one_work+0x4d7/0xb90 ? __pfx_process_one_work+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __list_add_valid_or_report+0x37/0xf0 ? __pfx_tcm_delayed_set_alt+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 worker_thread+0x2d8/0x570 ? __pfx_worker_thread+0x10/0x10 kthread+0x1ad/0x1f0 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x3c9/0x540 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x2e9/0x730 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 </TASK> Allocated by task 544: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0x8f/0xa0 tcm_alloc+0x68/0x180 usb_get_function+0x36/0x60 config_usb_cfg_link+0x125/0x1b0 configfs_symlink+0x322/0x890 vfs_symlink+0xc2/0x270 filename_symlinkat+0x295/0x2f0 __x64_sys_symlinkat+0x62/0x90 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 661: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x43/0x70 kfree+0x2f9/0x530 config_usb_cfg_unlink+0x173/0x1e0 configfs_unlink+0x1fa/0x340 vfs_unlink+0x15c/0x510 filename_unlinkat+0x2ba/0x450 __x64_sys_unlinkat+0x63/0x90 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f
--- drivers/usb/gadget/function/f_tcm.c | 192 ++++++++++++++++++++++++++++++------ drivers/usb/gadget/function/tcm.h | 13 ++ 2 files changed, 177 insertions(+), 28 deletions(-)
drivers/usb/gadget/function/f_tcm.c +164 -28
@@ -2065,31 +2065,158 @@ ep_fail:return -ENOTSUPP ; } -struct guas_setup_wq { -struct work_struct work; -struct f_uas *fu; -unsigned int alt; -}; +static void tcm_cleanup_old_alt (struct f_uas *fu) +{ +if (fu->flags &USBG_IS_UAS ) +uasp_cleanup_old_alt (fu); +else if (fu->flags &USBG_IS_BOT ) +bot_cleanup_old_alt (fu); + fu->flags =0 ; +} + +static void tcm_delayed_set_alt_done (struct f_uas *fu) +{ +unsigned long flags; + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); + fu->delayed_set_alt_state =USBG_DELAYED_SET_ALT_IDLE ; + fu->delayed_set_alt_cancel = false; +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); +} + +static bool tcm_delayed_set_alt_cancelled (struct f_uas *fu) +{ +bool cancelled; +unsigned long flags; + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); + cancelled = fu->delayed_set_alt_cancel; +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); + +return cancelled; +} + +static bool tcm_complete_delayed_status (struct f_uas *fu) +{ +struct usb_composite_dev *cdev = fu->function.config->cdev; +struct usb_request *req = cdev->req; +unsigned long cdev_flags; +bool cancelled; +int ret; + +spin_lock_irqsave (&cdev->lock, cdev_flags); +spin_lock (&fu->delayed_set_alt_lock); + cancelled = fu->delayed_set_alt_cancel; +if (!cancelled) { + fu->delayed_set_alt_state =USBG_DELAYED_SET_ALT_IDLE ; + fu->delayed_set_alt_cancel = false; + } +spin_unlock (&fu->delayed_set_alt_lock); + +if (cancelled) { +spin_unlock_irqrestore (&cdev->lock, cdev_flags); +return false; + } + +if (cdev->delayed_status ==0 ) { +WARN (cdev,"%s: Unexpected call\n" , __func__); + }else if (--cdev->delayed_status ==0 ) { + req->length =0 ; + req->context = cdev; + ret =usb_ep_queue (cdev->gadget->ep0, req,GFP_ATOMIC ); +if (ret ==0 ) { + cdev->setup_pending = true; + }else { + req->status =0 ; + req->complete (cdev->gadget->ep0, req); + } + } + +spin_unlock_irqrestore (&cdev->lock, cdev_flags); + +return true; +} + +static bool tcm_cancel_delayed_set_alt (struct f_uas *fu) +{ +bool cleanup = false; +bool cancel = false; +unsigned long flags; + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); +switch (fu->delayed_set_alt_state) { +case USBG_DELAYED_SET_ALT_IDLE : + cleanup = true; +break ; +case USBG_DELAYED_SET_ALT_QUEUED : +case USBG_DELAYED_SET_ALT_RUNNING : + fu->delayed_set_alt_cancel = true; + cancel = true; +break ; + } +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); + +if (cancel &&cancel_work (&fu->delayed_set_alt)) { +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); +if (fu->delayed_set_alt_state ==USBG_DELAYED_SET_ALT_QUEUED ) { + fu->delayed_set_alt_state =USBG_DELAYED_SET_ALT_IDLE ; + fu->delayed_set_alt_cancel = false; + cleanup = true; + } +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); + } + +return cleanup; +} + +static void tcm_cancel_delayed_set_alt_sync (struct f_uas *fu) +{ +unsigned long flags; + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); +if (fu->delayed_set_alt_state !=USBG_DELAYED_SET_ALT_IDLE ) + fu->delayed_set_alt_cancel = true; +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); + +cancel_work_sync (&fu->delayed_set_alt); + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); + fu->delayed_set_alt_state =USBG_DELAYED_SET_ALT_IDLE ; + fu->delayed_set_alt_cancel = false; +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); +}static void tcm_delayed_set_alt (struct work_struct *wq) { - struct guas_setup_wq *work = container_of(wq, struct guas_setup_wq, - work); -struct f_uas *fu = work->fu; -int alt = work->alt; + struct f_uas *fu = container_of(wq, struct f_uas, delayed_set_alt); +unsigned long flags; +unsigned int alt; + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); +if (fu->delayed_set_alt_state !=USBG_DELAYED_SET_ALT_QUEUED ) { +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); +return ; + } + fu->delayed_set_alt_state =USBG_DELAYED_SET_ALT_RUNNING ; + alt = fu->delayed_alt; +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); -kfree (work); +tcm_cleanup_old_alt (fu); -if (fu->flags &USBG_IS_BOT ) -bot_cleanup_old_alt (fu); -if (fu->flags &USBG_IS_UAS ) -uasp_cleanup_old_alt (fu); +if (tcm_delayed_set_alt_cancelled (fu)) + goto out_done;if (alt ==USB_G_ALT_INT_BBB )bot_set_alt (fu);else if (alt ==USB_G_ALT_INT_UAS )uasp_set_alt (fu); -usb_composite_setup_continue (fu->function.config->cdev); + +if (tcm_complete_delayed_status (fu)) +return ; + +tcm_cleanup_old_alt (fu); +out_done: +tcm_delayed_set_alt_done (fu); }static int tcm_get_alt (struct usb_function *f,unsigned intf)
@@ -2115,15 +2242,20 @@ static int tcm_set_alt(struct usb_functireturn -EOPNOTSUPP ;if ((alt ==USB_G_ALT_INT_BBB ) || (alt ==USB_G_ALT_INT_UAS )) { -struct guas_setup_wq *work; +unsigned long flags; + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); +if (fu->delayed_set_alt_state !=USBG_DELAYED_SET_ALT_IDLE ) { +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, + flags); +return -EBUSY ; + } + fu->delayed_alt = alt; + fu->delayed_set_alt_cancel = false; + fu->delayed_set_alt_state =USBG_DELAYED_SET_ALT_QUEUED ; +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); - work =kmalloc (sizeof (*work),GFP_ATOMIC ); -if (!work) -return -ENOMEM ; -INIT_WORK (&work->work, tcm_delayed_set_alt); - work->fu = fu; - work->alt = alt; -schedule_work (&work->work); +schedule_work (&fu->delayed_set_alt);return USB_GADGET_DELAYED_STATUS ; }return -EOPNOTSUPP ;
@@ -2133,11 +2265,8 @@ static void tcm_disable(struct usb_funct {struct f_uas *fu =to_f_uas (f); -if (fu->flags &USBG_IS_UAS ) -uasp_cleanup_old_alt (fu); -else if (fu->flags &USBG_IS_BOT ) -bot_cleanup_old_alt (fu); - fu->flags =0 ; +if (tcm_cancel_delayed_set_alt (fu)) +tcm_cleanup_old_alt (fu); } static int tcm_setup(struct usb_function *f,
@@ -2285,11 +2414,16 @@ static void tcm_free(struct usb_function {struct f_uas *tcm =to_f_uas (f); +tcm_cancel_delayed_set_alt_sync (tcm);kfree (tcm); }static void tcm_unbind (struct usb_configuration *c,struct usb_function *f) { +struct f_uas *fu =to_f_uas (f); + +tcm_cancel_delayed_set_alt_sync (fu); +tcm_cleanup_old_alt (fu);usb_free_all_descriptors (f); }
@@ -2322,6 +2456,8 @@ static struct usb_function *tcm_alloc(st fu->function.disable = tcm_disable; fu->function.free_func = tcm_free; fu->tpg = tpg_instances[i].tpg; +INIT_WORK (&fu->delayed_set_alt, tcm_delayed_set_alt); +spin_lock_init (&fu->delayed_set_alt_lock);mutex_unlock (&tpg_instances_lock);return &fu->function;
drivers/usb/gadget/function/tcm.h +13 -0
@@ -3,6 +3,7 @@#define __TARGET_USB_GADGET_H__#include <linux/kref.h> +#include <linux/spinlock.h> /* #include <linux/usb/uas.h> */ #include <linux/usb/composite.h> #include <linux/usb/uas.h>
@@ -26,6 +27,12 @@ enum {#define USB_G_DEFAULT_SESSION_TAGS 128 +enum { +USBG_DELAYED_SET_ALT_IDLE =0 , +USBG_DELAYED_SET_ALT_QUEUED , +USBG_DELAYED_SET_ALT_RUNNING , +}; +struct tcm_usbg_nexus {struct se_session *tvn_se_sess; };
@@ -117,6 +124,12 @@ struct f_uas {#define USBG_IS_BOT (1 << 3)#define USBG_BOT_CMD_PEND (1 << 4) +struct work_struct delayed_set_alt; +spinlock_t delayed_set_alt_lock;/* protects delayed_set_alt_* */ +unsigned int delayed_alt; +unsigned int delayed_set_alt_state; +bool delayed_set_alt_cancel; +struct usbg_cdb cmd;struct usb_ep *ep_in;struct usb_ep *ep_out;
Greg Kroah-Hartman
maintainerPATCH 148/235 drm/dp/mst: fix OOB reads on 2-byte fields in sideband reply parsers
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashutosh Desai <[email protected]>
[ Upstream commit 6b89ba3dba2f583626fb693e47e951ffb8bf591f ]
Three sideband reply parsers read 16-bit fields as:
val = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
and check bounds only after the fact. When idx == raw->curlen, raw->msg[idx+1] reads one byte past the received message data into the following struct fields (curchunk_len, curchunk_idx, curlen).
Affected functions:
- drm_dp_sideband_parse_enum_path_resources_ack() full_payload_bw_number and avail_payload_bw_number fields
- drm_dp_sideband_parse_allocate_payload_ack() allocated_pbn field
- drm_dp_sideband_parse_query_payload_ack() allocated_pbn field
Fix by using a single combined check (idx + 2 > curlen) before each 2-byte read. Since the check is strictly tighter than idx > curlen, no separate step is needed.
--- drivers/gpu/drm/drm_dp_mst_topology.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-)
drivers/gpu/drm/drm_dp_mst_topology.c +4 -13
@@ -920,16 +920,13 @@ static bool drm_dp_sideband_parse_enum_p repmsg->u.path_resources.port_number = (raw->msg[idx] >>4 ) &0xf ; repmsg->u.path_resources.fec_capable = raw->msg[idx] &0x1 ; idx++; -if (idx > raw->curlen) +if (idx +2 > raw->curlen) goto fail_len; repmsg->u.path_resources.full_payload_bw_number = (raw->msg[idx] <<8 ) | (raw->msg[idx+1 ]); idx +=2 ; -if (idx > raw->curlen) +if (idx +2 > raw->curlen) goto fail_len; repmsg->u.path_resources.avail_payload_bw_number = (raw->msg[idx] <<8 ) | (raw->msg[idx+1 ]); - idx +=2 ; -if (idx > raw->curlen) - goto fail_len;return true; fail_len:DRM_DEBUG_KMS ("enum resource parse length fail %d %d\n" , idx, raw->curlen);
@@ -947,12 +944,9 @@ static bool drm_dp_sideband_parse_alloca goto fail_len; repmsg->u.allocate_payload.vcpi = raw->msg[idx]; idx++; -if (idx > raw->curlen) +if (idx +2 > raw->curlen) goto fail_len; repmsg->u.allocate_payload.allocated_pbn = (raw->msg[idx] <<8 ) | (raw->msg[idx+1 ]); - idx +=2 ; -if (idx > raw->curlen) - goto fail_len;return true; fail_len:DRM_DEBUG_KMS ("allocate payload parse length fail %d %d\n" , idx, raw->curlen);
@@ -966,12 +960,9 @@ static bool drm_dp_sideband_parse_query_ repmsg->u.query_payload.port_number = (raw->msg[idx] >>4 ) &0xf ; idx++; -if (idx > raw->curlen) +if (idx +2 > raw->curlen) goto fail_len; repmsg->u.query_payload.allocated_pbn = (raw->msg[idx] <<8 ) | (raw->msg[idx +1 ]); - idx +=2 ; -if (idx > raw->curlen) - goto fail_len;return true; fail_len:DRM_DEBUG_KMS ("query payload parse length fail %d %d\n" , idx, raw->curlen);
Greg Kroah-Hartman
maintainerPATCH 149/235 drm/dp/mst: fix OOB reads in remote DPCD/I2C sideband reply parsers
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashutosh Desai <[email protected]>
[ Upstream commit 1a8f537f5a1eeac941f262fe73078d6b08ba83c0 ]
drm_dp_sideband_parse_remote_dpcd_read() reads num_bytes from the raw message and then unconditionally does:
memcpy(bytes, &raw->msg[idx], num_bytes);
without checking that idx + num_bytes <= raw->curlen. raw->msg[] is 256 bytes; if a malicious or misbehaving MST hub sets num_bytes larger than the remaining payload, the memcpy reads past the received data into whatever follows in raw->msg[].
drm_dp_sideband_parse_remote_i2c_read_ack() has the same flaw (noted with a /* TODO check */ comment since the code was introduced).
Fix both functions by using a single combined check
(idx + num_bytes > curlen) before each memcpy. Since num_bytes is u8, it is always >= 0, so this strictly subsumes the simpler idx > curlen form and no separate step is needed.
--- drivers/gpu/drm/drm_dp_mst_topology.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/gpu/drm/drm_dp_mst_topology.c +4 -2
@@ -868,7 +868,7 @@ static bool drm_dp_sideband_parse_remote goto fail_len; repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx]; idx++; -if (idx > raw->curlen) +if (idx + repmsg->u.remote_dpcd_read_ack.num_bytes > raw->curlen) goto fail_len;memcpy (repmsg->u.remote_dpcd_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_dpcd_read_ack.num_bytes);
@@ -904,7 +904,9 @@ static bool drm_dp_sideband_parse_remote goto fail_len; repmsg->u.remote_i2c_read_ack.num_bytes = raw->msg[idx]; idx++; -/* TODO check */ +if (idx + repmsg->u.remote_i2c_read_ack.num_bytes > raw->curlen) + goto fail_len; +memcpy (repmsg->u.remote_i2c_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_i2c_read_ack.num_bytes);return true; fail_len:
Greg Kroah-Hartman
maintainerPATCH 150/235 dma-buf/drivers: make reserving a shared slot mandatory v4
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian König <[email protected]>
[ Upstream commit c8d4c18bfbc4ab467188dbe45cc8155759f49d9e ]
Audit all the users of dma_resv_add_excl_fence() and make sure they reserve a shared slot also when only trying to add an exclusive fence.
This is the next step towards handling the exclusive fence like a shared one.
v2: fix missed case in amdgpu
v3: and two more radeon, rename function
v4: add one more case to TTM, fix i915 after rebase
Stable-dep-of: a48bbcc7ac73 ("drm/virtio: use uninterruptible resv lock for plane updates")
--- drivers/dma-buf/dma-resv.c | 8 ++++---- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c | 4 ++-- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 2 +- drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c | 2 +- drivers/gpu/drm/i915/i915_vma.c | 2 +- drivers/gpu/drm/lima/lima_gem.c | 2 +- drivers/gpu/drm/msm/msm_gem_submit.c | 2 +- drivers/gpu/drm/nouveau/nouveau_fence.c | 2 +- drivers/gpu/drm/qxl/qxl_release.c | 2 +- drivers/gpu/drm/radeon/radeon_vm.c | 2 +- drivers/gpu/drm/ttm/ttm_bo.c | 4 ++-- drivers/gpu/drm/ttm/ttm_execbuf_util.c | 4 ++-- drivers/gpu/drm/vc4/vc4_gem.c | 2 +- drivers/gpu/drm/vgem/vgem_fence.c | 2 +- include/linux/dma-resv.h | 2 +- 15 files changed, 21 insertions(+), 21 deletions(-)
drivers/dma-buf/dma-resv.c +4 -4
@@ -173,7 +173,7 @@ void dma_resv_fini(struct dma_resv *obj)EXPORT_SYMBOL (dma_resv_fini);/** -* dma_resv_reserve_shared - Reserve space to add shared fences to +* dma_resv_reserve_fences - Reserve space to add shared fences to * a dma_resv. * @obj: reservation object * @num_fences: number of fences we want to add
@@ -184,7 +184,7 @@ EXPORT_SYMBOL(dma_resv_fini);* RETURNS * Zero for success, or -errno */ -int dma_resv_reserve_shared (struct dma_resv *obj,unsigned int num_fences) +int dma_resv_reserve_fences (struct dma_resv *obj,unsigned int num_fences) {struct dma_resv_list *old, *new;unsigned int i, j, k, max;
@@ -250,7 +250,7 @@ int dma_resv_reserve_shared(struct dma_rreturn 0 ; } -EXPORT_SYMBOL (dma_resv_reserve_shared); +EXPORT_SYMBOL (dma_resv_reserve_fences);/** * dma_resv_add_shared_fence - Add a fence to a shared slot
@@ -258,7 +258,7 @@ EXPORT_SYMBOL(dma_resv_reserve_shared);* @fence: the shared fence to add * * Add a fence to a shared slot, obj->lock must be held, and -* dma_resv_reserve_shared() has been called. +* dma_resv_reserve_fences() has been called. */ void dma_resv_add_shared_fence (struct dma_resv *obj,struct dma_fence *fence) {
drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +2 -2
@@ -950,7 +950,7 @@ static int init_kfd_vm(struct amdgpu_vm AMDGPU_FENCE_OWNER_KFD, false);if (ret) goto wait_pd_fail; - ret =dma_resv_reserve_shared (vm->root.base.bo->tbo.base.resv,1 ); + ret =dma_resv_reserve_fences (vm->root.base.bo->tbo.base.resv,1 );if (ret) goto reserve_shared_fail; amdgpu_bo_fence(vm->root.base.bo,
@@ -2205,7 +2205,7 @@ int amdgpu_amdkfd_add_gws_to_process(voi* Add process eviction fence to bo so they can * evict each other. */ - ret =dma_resv_reserve_shared (gws_bo->tbo.base.resv,1 ); + ret =dma_resv_reserve_fences (gws_bo->tbo.base.resv,1 );if (ret) goto reserve_shared_fail;amdgpu_bo_fence (gws_bo, &process_info->eviction_fence->base, true);
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +1 -1
@@ -2904,7 +2904,7 @@ int amdgpu_vm_init(struct amdgpu_deviceif (r) goto error_free_root; - r =dma_resv_reserve_shared (root->tbo.base.resv,1 ); + r =dma_resv_reserve_fences (root->tbo.base.resv,1 );if (r) goto error_unreserve;
drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c +1 -1
@@ -180,7 +180,7 @@ static int submit_fence_sync(struct etnastruct dma_resv *robj = bo->obj->base.resv;if (!(bo->flags &ETNA_SUBMIT_BO_WRITE )) { - ret =dma_resv_reserve_shared (robj,1 ); + ret =dma_resv_reserve_fences (robj,1 );if (ret)return ret; }
drivers/gpu/drm/i915/i915_vma.c +1 -1
@@ -1241,7 +1241,7 @@ int i915_vma_move_to_active(struct i915_ obj->write_domain =I915_GEM_DOMAIN_RENDER ; obj->read_domains =0 ; }else { - err =dma_resv_reserve_shared (vma->resv,1 ); + err =dma_resv_reserve_fences (vma->resv,1 );if (unlikely (err))return err;
drivers/gpu/drm/lima/lima_gem.c +1 -1
@@ -263,7 +263,7 @@ static int lima_gem_sync_bo(struct lima_int err =0 ;if (!write) { - err =dma_resv_reserve_shared (lima_bo_resv (bo),1 ); + err =dma_resv_reserve_fences (lima_bo_resv (bo),1 );if (err)return err; }
drivers/gpu/drm/msm/msm_gem_submit.c +1 -1
@@ -230,7 +230,7 @@ static int submit_fence_sync(struct msm_* strange place to call it. OTOH this is a * convenient can-fail point to hook it in. */ - ret =dma_resv_reserve_shared (msm_obj->base.resv, + ret =dma_resv_reserve_fences (msm_obj->base.resv,1 );if (ret)return ret;
drivers/gpu/drm/nouveau/nouveau_fence.c +1 -1
@@ -349,7 +349,7 @@ nouveau_fence_sync(struct nouveau_bo *nvint ret =0 , i;if (!exclusive) { - ret =dma_resv_reserve_shared (resv,1 ); + ret =dma_resv_reserve_fences (resv,1 );if (ret)return ret;
drivers/gpu/drm/qxl/qxl_release.c +1 -1
@@ -239,7 +239,7 @@ static int qxl_release_validate_bo(strucreturn ret; } - ret =dma_resv_reserve_shared (bo->tbo.base.resv,1 ); + ret =dma_resv_reserve_fences (bo->tbo.base.resv,1 );if (ret)return ret;
drivers/gpu/drm/radeon/radeon_vm.c +1 -1
@@ -831,7 +831,7 @@ static int radeon_vm_update_ptes(structint r;radeon_sync_resv (rdev, &ib->sync, pt->tbo.base.resv, true); - r =dma_resv_reserve_shared (pt->tbo.base.resv,1 ); + r =dma_resv_reserve_fences (pt->tbo.base.resv,1 );if (r)return r;
drivers/gpu/drm/ttm/ttm_bo.c +2 -2
@@ -830,7 +830,7 @@ static int ttm_bo_add_move_fence(structdma_resv_add_shared_fence (bo->base.resv, fence); - ret =dma_resv_reserve_shared (bo->base.resv,1 ); + ret =dma_resv_reserve_fences (bo->base.resv,1 );if (unlikely (ret)) {dma_fence_put (fence);return ret;
@@ -950,7 +950,7 @@ int ttm_bo_mem_space(struct ttm_buffer_obool type_found = false;int i, ret; - ret =dma_resv_reserve_shared (bo->base.resv,1 ); + ret =dma_resv_reserve_fences (bo->base.resv,1 );if (unlikely (ret))return ret;
drivers/gpu/drm/ttm/ttm_execbuf_util.c +2 -2
@@ -106,7 +106,7 @@ int ttm_eu_reserve_buffers(struct ww_acqif (!entry->num_shared)continue ; - ret =dma_resv_reserve_shared (bo->base.resv, + ret =dma_resv_reserve_fences (bo->base.resv, entry->num_shared);if (!ret)continue ;
@@ -123,7 +123,7 @@ int ttm_eu_reserve_buffers(struct ww_acq }if (!ret && entry->num_shared) - ret =dma_resv_reserve_shared (bo->base.resv, + ret =dma_resv_reserve_fences (bo->base.resv, entry->num_shared);if (unlikely (ret !=0 )) {
drivers/gpu/drm/vc4/vc4_gem.c +1 -1
@@ -644,7 +644,7 @@ retry:for (i =0 ; i < exec->bo_count; i++) { bo = &exec->bo[i]->base; - ret =dma_resv_reserve_shared (bo->resv,1 ); + ret =dma_resv_reserve_fences (bo->resv,1 );if (ret) {vc4_unlock_bo_reservations (dev, exec, acquire_ctx);return ret;
drivers/gpu/drm/vgem/vgem_fence.c +1 -1
@@ -162,7 +162,7 @@ int vgem_fence_attach_ioctl(struct drm_ddma_resv_lock (resv,NULL );if (arg->flags &VGEM_FENCE_WRITE )dma_resv_add_excl_fence (resv, fence); -else if ((ret =dma_resv_reserve_shared (resv,1 )) ==0 ) +else if ((ret =dma_resv_reserve_fences (resv,1 )) ==0 )dma_resv_add_shared_fence (resv, fence);dma_resv_unlock (resv);
include/linux/dma-resv.h +1 -1
@@ -273,7 +273,7 @@ dma_resv_get_excl_rcu(struct dma_resv *ovoid dma_resv_init (struct dma_resv *obj);void dma_resv_fini (struct dma_resv *obj); -int dma_resv_reserve_shared (struct dma_resv *obj,unsigned int num_fences); +int dma_resv_reserve_fences (struct dma_resv *obj,unsigned int num_fences);void dma_resv_add_shared_fence (struct dma_resv *obj,struct dma_fence *fence);void dma_resv_add_excl_fence (struct dma_resv *obj,struct dma_fence *fence);
Greg Kroah-Hartman
maintainerPATCH 151/235 drm/virtio: use uninterruptible resv lock for plane updates
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Deepanshu Kartikey <[email protected]>
[ Upstream commit a48bbcc7ac739e93562d6148c6fa504c2e9f22f8 ]
virtio_gpu_cursor_plane_update() and virtio_gpu_resource_flush() lock the framebuffer BO's dma_resv via virtio_gpu_array_lock_resv() and ignore its return value. The function can fail with -EINTR from dma_resv_lock_interruptible() (signal during lock wait) or with -ENOMEM from dma_resv_reserve_fences() (fence slot allocation), leaving the resv lock not held. The queue path then walks the object array and calls dma_resv_add_fence(), which requires the lock held; with lockdep enabled this trips dma_resv_assert_held():
WARNING: drivers/dma-buf/dma-resv.c:296 at dma_resv_add_fence+0x71e/0x840 Call Trace: virtio_gpu_array_add_fence virtio_gpu_queue_ctrl_sgs virtio_gpu_queue_fenced_ctrl_buffer virtio_gpu_cursor_plane_update drm_atomic_helper_commit_planes drm_atomic_helper_commit_tail commit_tail drm_atomic_helper_commit drm_atomic_commit drm_atomic_helper_update_plane __setplane_atomic drm_mode_cursor_universal drm_mode_cursor_common drm_mode_cursor_ioctl drm_ioctl __x64_sys_ioctl
Beyond the WARN, mutating the dma_resv fence list without the lock races with concurrent readers/writers and can corrupt the list.
Both call sites run inside the .atomic_update plane callback, which DRM atomic helpers do not allow to fail (by the time it runs, the commit has been signed off to userspace and there is no clean rollback path). Moving the lock acquisition to .prepare_fb was rejected because the broader lock scope deadlocks against other BO locking paths in the same atomic commit.
Introduce virtio_gpu_lock_one_resv_uninterruptible() that uses dma_resv_lock() instead of dma_resv_lock_interruptible(). This eliminates the -EINTR failure mode -- the realistic syzbot trigger -- without extending the lock hold across the commit. The helper locks a single BO and rejects nents > 1 with -EINVAL; both fix sites lock exactly one BO.
Use it from virtio_gpu_cursor_plane_update() and virtio_gpu_resource_flush(); check the return value to handle the remaining -ENOMEM case from dma_resv_reserve_fences() by freeing the objs and skipping the plane update for that frame. The framebuffer BOs touched here are not shared with other contexts and lock contention is expected to be brief, so the loss of signal-interruptibility is acceptable.
Other callers of virtio_gpu_array_lock_resv() (the ioctl paths) continue to use the interruptible variant.
The bug was reported by syzbot, triggered via fault injection (fail_nth) on the DRM_IOCTL_MODE_CURSOR path, which forces the -ENOMEM branch in dma_resv_reserve_fences().
--- drivers/gpu/drm/virtio/virtgpu_drv.h | 1 + drivers/gpu/drm/virtio/virtgpu_gem.c | 17 +++++++++++++++++ drivers/gpu/drm/virtio/virtgpu_plane.c | 5 ++++- 3 files changed, 22 insertions(+), 1 deletion(-)
drivers/gpu/drm/virtio/virtgpu_drv.h +1 -0
@@ -259,6 +259,7 @@ virtio_gpu_array_from_handles(struct drmvoid virtio_gpu_array_add_obj (struct virtio_gpu_object_array *objs,struct drm_gem_object *obj);int virtio_gpu_array_lock_resv (struct virtio_gpu_object_array *objs); +int virtio_gpu_lock_one_resv_uninterruptible (struct virtio_gpu_object_array *objs);void virtio_gpu_array_unlock_resv (struct virtio_gpu_object_array *objs);void virtio_gpu_array_add_fence (struct virtio_gpu_object_array *objs,struct dma_fence *fence);
drivers/gpu/drm/virtio/virtgpu_gem.c +17 -0
@@ -217,6 +217,23 @@ int virtio_gpu_array_lock_resv(struct vireturn ret; } +int virtio_gpu_lock_one_resv_uninterruptible (struct virtio_gpu_object_array *objs) +{ +int ret; + +if (objs->nents !=1 ) +return -EINVAL ; + +dma_resv_lock (objs->objs[0 ]->resv,NULL ); + + ret =dma_resv_reserve_fences (objs->objs[0 ]->resv,1 ); +if (ret) { +virtio_gpu_array_unlock_resv (objs); +return ret; + } +return 0 ; +} +void virtio_gpu_array_unlock_resv (struct virtio_gpu_object_array *objs) {if (objs->nents ==1 ) {
drivers/gpu/drm/virtio/virtgpu_plane.c +4 -1
@@ -260,7 +260,10 @@ static void virtio_gpu_cursor_plane_updaif (!objs)return ;virtio_gpu_array_add_obj (objs, vgfb->base.obj[0 ]); -virtio_gpu_array_lock_resv (objs); +if (virtio_gpu_lock_one_resv_uninterruptible (objs)) { +virtio_gpu_array_put_free (objs); +return ; + } virtio_gpu_cmd_transfer_to_host_2d (vgdev, 0, plane->state->crtc_w,
Greg Kroah-Hartman
maintainerPATCH 152/235 drm/displayid: fix Tiled Display Topology ID size
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jani Nikula <[email protected]>
[ Upstream commit 90c0486a82e27393f9eaf3bb350f51a0bd38cb6b ]
The Tiled Display Topology ID of a DisplayID Tiled Display Topology Data Block consists of three fields:
- Tiled Display Manufacturer/Vendor ID Field (3 bytes)
- Tiled Display Product ID Code Field (2 bytes)
- Tiled Display Serial Number Field (4 bytes)
i.e. a total of 9 bytes, not 8.
The DisplayID Tiled Display Topology ID is used as the tile group identifier.
Update both struct displayid_tiled_block topology_id member and struct drm_tile_group group_data member to full 9 bytes.
The group data was missing the last byte of the serial number. I don't know whether there are known bug reports that might be linked to this, but it's plausible the last byte could be the differentiating part for the tile groups, and fewer tile groups might have been created than intended.
--- drivers/gpu/drm/drm_connector.c | 12 ++++++------ include/drm/drm_connector.h | 6 +++--- include/drm/drm_displayid.h | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-)
drivers/gpu/drm/drm_connector.c +6 -6
@@ -2586,7 +2586,7 @@ EXPORT_SYMBOL(drm_mode_put_tile_group);/** * drm_mode_get_tile_group - get a reference to an existing tile group * @dev: DRM device -* @topology: 8-bytes unique per monitor. +* @topology_id: 9-byte unique ID per monitor. * * Use the unique bytes to get a reference to an existing tile group. *
@@ -2594,14 +2594,14 @@ EXPORT_SYMBOL(drm_mode_put_tile_group);* tile group or NULL if not found. */ struct drm_tile_group *drm_mode_get_tile_group (struct drm_device *dev, -const char topology[8 ]) +const char topology_id[9 ]) {struct drm_tile_group *tg;int id;mutex_lock (&dev->mode_config.idr_mutex);idr_for_each_entry (&dev->mode_config.tile_idr, tg, id) { -if (!memcmp (tg->group_data, topology,8 )) { +if (!memcmp (tg->group_data, topology_id,sizeof (tg->group_data))) {if (!kref_get_unless_zero (&tg->refcount)) tg =NULL ;mutex_unlock (&dev->mode_config.idr_mutex);
@@ -2616,7 +2616,7 @@ EXPORT_SYMBOL(drm_mode_get_tile_group);/** * drm_mode_create_tile_group - create a tile group from a displayid description * @dev: DRM device -* @topology: 8-bytes unique per monitor. +* @topology_id: 9-byte unique ID per monitor. * * Create a tile group for the unique monitor, and get a unique * identifier for the tile group.
@@ -2625,7 +2625,7 @@ EXPORT_SYMBOL(drm_mode_get_tile_group);* new tile group or NULL. */ struct drm_tile_group *drm_mode_create_tile_group (struct drm_device *dev, -const char topology[8 ]) +const char topology_id[9 ]) {struct drm_tile_group *tg;int ret;
@@ -2635,7 +2635,7 @@ struct drm_tile_group *drm_mode_create_treturn NULL ;kref_init (&tg->refcount); -memcpy (tg->group_data, topology,8 ); +memcpy (tg->group_data, topology_id,sizeof (tg->group_data)); tg->dev = dev;mutex_lock (&dev->mode_config.idr_mutex);
include/drm/drm_connector.h +3 -3
@@ -1689,13 +1689,13 @@ struct drm_tile_group {struct kref refcount;struct drm_device *dev;int id; -u8 group_data[8 ]; +u8 group_data[9 ]; };struct drm_tile_group *drm_mode_create_tile_group (struct drm_device *dev, -const char topology[8 ]); +const char topology_id[9 ]);struct drm_tile_group *drm_mode_get_tile_group (struct drm_device *dev, -const char topology[8 ]); +const char topology_id[9 ]);void drm_mode_put_tile_group (struct drm_device *dev,struct drm_tile_group *tg);
include/drm/drm_displayid.h +1 -1
@@ -71,7 +71,7 @@ struct displayid_tiled_block {u8 topo[3 ];u8 tile_size[4 ];u8 tile_pixel_bezel[5 ]; -u8 topology_id[8 ]; +u8 topology_id[9 ]; } __packed;struct displayid_detailed_timings_1 {
Greg Kroah-Hartman
maintainerPATCH 153/235 drm/tegra: fbdev: Remove offset into framebuffer memory
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Zimmermann <[email protected]>
[ Upstream commit a18b6e30ecd69096beda4a0c96d2570900c3879a ]
The screen_buffer field in struct fb_info contains the kernel address of the first byte of framebuffer memory. Do not add the display offset. This offset only describes scrolling during scanout.
--- drivers/gpu/drm/tegra/fb.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-)
drivers/gpu/drm/tegra/fb.c +2 -6
@@ -211,7 +211,6 @@ static int tegra_fbdev_probe(struct drm_struct drm_mode_fb_cmd2 cmd = {0 };unsigned int bytes_per_pixel;struct drm_framebuffer *fb; -unsigned long offset;struct fb_info *info;struct tegra_bo *bo;size_t size;
@@ -257,9 +256,6 @@ static int tegra_fbdev_probe(struct drm_drm_fb_helper_fill_info (info, helper, sizes); - offset = info->var.xoffset * bytes_per_pixel + - info->var.yoffset * fb->pitches[0 ]; -if (bo->pages) { bo->vaddr =vmap (bo->pages, bo->num_pages,VM_MAP ,pgprot_writecombine (PAGE_KERNEL ));
@@ -271,9 +267,9 @@ static int tegra_fbdev_probe(struct drm_ } drm->mode_config.fb_base = (resource_size_t )bo->iova; - info->screen_base = (void __iomem *)bo->vaddr + offset; + info->screen_base = (void __iomem *)bo->vaddr; info->screen_size = size; - info->fix.smem_start = (unsigned long )(bo->iova + offset); + info->fix.smem_start = (unsigned long )(bo->iova); info->fix.smem_len = size;return 0 ;
Greg Kroah-Hartman
maintainerPATCH 154/235 drm/virtio: Return proper error codes instead of -1
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Osipenko <[email protected]>
[ Upstream commit 4c703f5d6f776eaa6a98611c9b5dfe800fbeb0c8 ]
Don't return -1 in error cases, return proper error code. The returned error codes propagate to error messages and to userspace and it's always good to have a meaningful error number for debugging purposes.
Stable-dep-of: 4e1a53892ba7 ("drm/virtio: bound EDID block reads to the response buffer")
--- drivers/gpu/drm/virtio/virtgpu_vq.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/gpu/drm/virtio/virtgpu_vq.c +3 -3
@@ -329,7 +329,7 @@ static int virtio_gpu_queue_ctrl_sgs(strif (fence && vbuf->objs)virtio_gpu_array_unlock_resv (vbuf->objs);free_vbuf (vgdev, vbuf); -return -1 ; +return -ENODEV ; }if (vgdev->has_indirect)
@@ -393,7 +393,7 @@ static int virtio_gpu_queue_fenced_ctrl_if (!sgt) {if (fence && vbuf->objs)virtio_gpu_array_unlock_resv (vbuf->objs); -return -1 ; +return -ENOMEM ; } elemcnt += sg_ents;
@@ -725,7 +725,7 @@ static int virtio_get_edid_block(void *dsize_t start = block *EDID_LENGTH ;if (start + len >le32_to_cpu (resp->size)) -return -1 ; +return -EINVAL ;memcpy (buf, resp->edid + start, len);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 155/235 drm/virtio: bound EDID block reads to the response buffer
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
[ Upstream commit 4e1a53892ba7f8a3e1da6bfc53c83ae7c812dccd ]
virtio_get_edid_block() validates the read offset only against the device-supplied resp->size field, never against the fixed-size resp->edid array. The EDID block index is driven by the device-supplied extension count, so a malicious virtio-gpu backend can advertise a large size together with a high block count and read far past the array into adjacent kernel memory, which is then surfaced in the parsed EDID (an out-of-bounds read / info leak).
Also reject any read whose end exceeds the size of the edid array. Conforming EDID responses stay within the array and are unaffected.
--- drivers/gpu/drm/virtio/virtgpu_vq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/gpu/drm/virtio/virtgpu_vq.c +2 -1
@@ -724,7 +724,8 @@ static int virtio_get_edid_block(void *dstruct virtio_gpu_resp_edid *resp = data;size_t start = block *EDID_LENGTH ; -if (start + len >le32_to_cpu (resp->size)) +if (start + len >le32_to_cpu (resp->size) || + start + len >sizeof (resp->edid))return -EINVAL ;memcpy (buf, resp->edid + start, len);return 0 ;
Greg Kroah-Hartman
maintainerPATCH 156/235 media: aspeed: fix missing of_reserved_mem_device_release() on probe failure
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Carlier <[email protected]>
[ Upstream commit 253c8ef7d57da0c74db251f385324faaa5ae2257 ]
aspeed_video_init() calls of_reserved_mem_device_init() to associate reserved memory regions with the device. When aspeed_video_setup_video() subsequently fails in aspeed_video_probe(), the error path frees the JPEG buffer and unprepares the clocks but does not release the reserved memory association, leaking the rmem_assigned_device entry on the global list.
The normal remove path already calls of_reserved_mem_device_release() correctly; only the probe error path was missing it.
Add the missing of_reserved_mem_device_release() call to the aspeed_video_setup_video() failure cleanup.
--- drivers/media/platform/aspeed-video.c | 1 + 1 file changed, 1 insertion(+)
drivers/media/platform/aspeed-video.c +1 -0
@@ -1724,6 +1724,7 @@ static int aspeed_video_probe(struct pla rc =aspeed_video_setup_video (video);if (rc) {aspeed_video_free_buf (video, &video->jpeg); +of_reserved_mem_device_release (&pdev->dev);clk_unprepare (video->vclk);clk_unprepare (video->eclk);return rc;
Greg Kroah-Hartman
maintainerPATCH 157/235 drm/i915/hdcp: require monotonically increasing seq_num_v
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jani Nikula <[email protected]>
[ Upstream commit db9e64c983dcb07ff256bd455f258c44aa530ff8 ]
The HDCP 2.2 specification requires the seq_num_v to be monotonically increasing, and repeated seq_num_v needs to be treated as an integrity failure. Make it so.
For the first message, seq_num_v must be zero, and is already checked. We can only check for less-than-or-equal for the subsequent messages, where hdcp2_encrypted is true.
Discovered using AI-assisted static analysis confirmed by Intel Product Security.
--- drivers/gpu/drm/i915/display/intel_hdcp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/gpu/drm/i915/display/intel_hdcp.c +4 -3
@@ -1534,9 +1534,10 @@ int hdcp2_authenticate_repeater_topologyreturn -EINVAL ; } -if (seq_num_v < hdcp->seq_num_v) { -/* Roll over of the seq_num_v from repeater. Reauthenticate. */ -drm_dbg_kms (&dev_priv->drm,"Seq_num_v roll over.\n" ); +if (hdcp->hdcp2_encrypted && seq_num_v <= hdcp->seq_num_v) { +/* Reauthenticate on Seq_num_v repeat or rollover */ +drm_dbg_kms (&dev_priv->drm,"Seq_num_v %s\n" , + seq_num_v == hdcp->seq_num_v ?"repeat" :"rollover" );return -EINVAL ; }
Greg Kroah-Hartman
maintainerPATCH 158/235 media: marvell-cam: fix missing pci_disable_device() on remove
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
[ Upstream commit 033ff0420e4c9c240ae5523fff39770298efa964 ]
During manual code audit, we found that cafe_pci_probe() enables the PCI device with pci_enable_device(), and its probe error path properly calls pci_disable_device() on failure.
However, cafe_pci_remove() tears down the controller and frees the driver data without disabling the PCI device, leaving the remove path inconsistent with probe cleanup.
Add the missing pci_disable_device() call to cafe_pci_remove().
--- drivers/media/platform/marvell-ccic/cafe-driver.c | 1 + 1 file changed, 1 insertion(+)
drivers/media/platform/marvell-ccic/cafe-driver.c +1 -0
@@ -600,6 +600,7 @@ static void cafe_pci_remove(struct pci_dreturn ; }cafe_shutdown (cam); +pci_disable_device (pdev);kfree (cam); }
Greg Kroah-Hartman
maintainerPATCH 159/235 media: i2c: imx219: Drop IMX219_VTS_* macros
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Laurent Pinchart <[email protected]>
[ Upstream commit 5ebbdd7aab3321e60a8be23aac1fee4f16644021 ]
The IMX219_VTS_* macros define default VTS values for the modes supported by the driver. They are used in a single place, and hinder readability compared to using the value directly as a decimal number. Drop them.
Stable-dep-of: 2c4f1ba73543 ("media: imx219: Fix maximum frame length in lines")
--- drivers/media/i2c/imx219.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-)
drivers/media/i2c/imx219.c +7 -17
@@ -49,20 +49,10 @@/* V_TIMING internal */ #define IMX219_REG_VTS 0x0160 -#define IMX219_VTS_15FPS 0x0dc6 -#define IMX219_VTS_30FPS_1080P 0x06e3 -#define IMX219_VTS_30FPS_BINNED 0x06e3 -#define IMX219_VTS_30FPS_640x480 0x06e3 -#define IMX219_VTS_MAX 0xffff +#define IMX219_FLL_MAX 0xffff#define IMX219_VBLANK_MIN 4 -/*Frame Length Line*/ -#define IMX219_FLL_MIN 0x08a6 -#define IMX219_FLL_MAX 0xffff -#define IMX219_FLL_STEP 1 -#define IMX219_FLL_DEFAULT 0x0c98 -/* HBLANK control - read only */ #define IMX219_PPL_DEFAULT 3448
@@ -382,7 +372,7 @@ static const struct imx219_mode supporte .width = 3280, .height =2464 }, - .vts_def = IMX219_VTS_15FPS, + .vts_def = 3526, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), .regs = mode_3280x2464_regs,
@@ -399,7 +389,7 @@ static const struct imx219_mode supporte .width = 1920, .height =1080 }, - .vts_def = IMX219_VTS_30FPS_1080P, + .vts_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs), .regs = mode_1920_1080_regs,
@@ -416,7 +406,7 @@ static const struct imx219_mode supporte .width = 3280, .height =2464 }, - .vts_def = IMX219_VTS_30FPS_BINNED, + .vts_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs), .regs = mode_1640_1232_regs,
@@ -433,7 +423,7 @@ static const struct imx219_mode supporte .width = 1280, .height =960 }, - .vts_def = IMX219_VTS_30FPS_640x480, + .vts_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_640_480_regs), .regs = mode_640_480_regs,
@@ -829,7 +819,7 @@ static int imx219_set_pad_format(struct imx219->mode = mode;/* Update limits and set FPS to default */ __v4l2_ctrl_modify_range (imx219->vblank,IMX219_VBLANK_MIN , -IMX219_VTS_MAX - mode->height,1 , +IMX219_FLL_MAX - mode->height,1 , mode->vts_def - mode->height);__v4l2_ctrl_s_ctrl (imx219->vblank, mode->vts_def - mode->height);
@@ -1246,7 +1236,7 @@ static int imx219_init_controls(struct i/* Initial vblank/hblank/exposure parameters based on current mode */ imx219->vblank =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops,V4L2_CID_VBLANK ,IMX219_VBLANK_MIN , -IMX219_VTS_MAX - height,1 , +IMX219_FLL_MAX - height,1 , imx219->mode->vts_def - height); hblank =IMX219_PPL_DEFAULT - imx219->mode->width; imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
Greg Kroah-Hartman
maintainerPATCH 160/235 media: i2c: imx219: Correct the minimum vblanking value
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Plowman <[email protected]>
[ Upstream commit e3b82d49bf676f3c873e642038765eac32ab6d39 ]
The datasheet for this sensor documents the minimum vblanking as being 32 lines. It does fix some problems with occasional black lines at the bottom of images (tested on Raspberry Pi).
Stable-dep-of: 2c4f1ba73543 ("media: imx219: Fix maximum frame length in lines")
--- drivers/media/i2c/imx219.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/i2c/imx219.c +1 -1
@@ -51,7 +51,7 @@#define IMX219_REG_VTS 0x0160#define IMX219_FLL_MAX 0xffff -#define IMX219_VBLANK_MIN 4 +#define IMX219_VBLANK_MIN 32/* HBLANK control - read only */ #define IMX219_PPL_DEFAULT 3448
Greg Kroah-Hartman
maintainerPATCH 161/235 media: i2c: imx219: Rename VTS to FRM_LENGTH
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jai Luthra <[email protected]>
[ Upstream commit 04f78503f99ae7e9887c7fe5e4bc54a7cfb10fe0 ]
The IMX219 datasheet refers to the vertical length + blanking as FRM_LENGTH instead of VTS.
Stable-dep-of: 2c4f1ba73543 ("media: imx219: Fix maximum frame length in lines")
--- drivers/media/i2c/imx219.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-)
drivers/media/i2c/imx219.c +16 -17
@@ -47,12 +47,6 @@#define IMX219_DEFAULT_LINK_FREQ 456000000 -/* V_TIMING internal */ -#define IMX219_REG_VTS 0x0160 -#define IMX219_FLL_MAX 0xffff - -#define IMX219_VBLANK_MIN 32 -/* HBLANK control - read only */ #define IMX219_PPL_DEFAULT 3448
@@ -77,6 +71,11 @@#define IMX219_DGTL_GAIN_DEFAULT 0x0100#define IMX219_DGTL_GAIN_STEP 1 +/* V_TIMING internal */ +#define IMX219_REG_FRM_LENGTH_A 0x0160 +#define IMX219_FLL_MAX 0xffff +#define IMX219_VBLANK_MIN 32 +#define IMX219_REG_ORIENTATION 0x0172/* Binning Mode */
@@ -135,7 +134,7 @@ struct imx219_mode {struct v4l2_rect crop;/* V-timing */ -unsigned int vts_def; +unsigned int fll_def;/* Default register values */ struct imx219_reg_list reg_list;
@@ -372,7 +371,7 @@ static const struct imx219_mode supporte .width = 3280, .height =2464 }, - .vts_def = 3526, + .fll_def = 3526, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), .regs = mode_3280x2464_regs,
@@ -389,7 +388,7 @@ static const struct imx219_mode supporte .width = 1920, .height =1080 }, - .vts_def = 1763, + .fll_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs), .regs = mode_1920_1080_regs,
@@ -406,7 +405,7 @@ static const struct imx219_mode supporte .width = 3280, .height =2464 }, - .vts_def = 1763, + .fll_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs), .regs = mode_1640_1232_regs,
@@ -423,7 +422,7 @@ static const struct imx219_mode supporte .width = 1280, .height =960 }, - .vts_def = 1763, + .fll_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_640_480_regs), .regs = mode_640_480_regs,
@@ -660,7 +659,7 @@ static int imx219_set_ctrl(struct v4l2_c imx219->vflip->val << 1);break ;case V4L2_CID_VBLANK : - ret =imx219_write_reg (imx219,IMX219_REG_VTS , + ret =imx219_write_reg (imx219,IMX219_REG_FRM_LENGTH_A ,IMX219_REG_VALUE_16BIT , imx219->mode->height + ctrl->val);break ;
@@ -820,11 +819,11 @@ static int imx219_set_pad_format(struct/* Update limits and set FPS to default */ __v4l2_ctrl_modify_range (imx219->vblank,IMX219_VBLANK_MIN ,IMX219_FLL_MAX - mode->height,1 , - mode->vts_def - mode->height); + mode->fll_def - mode->height);__v4l2_ctrl_s_ctrl (imx219->vblank, - mode->vts_def - mode->height); + mode->fll_def - mode->height);/* Update max exposure while meeting expected vblanking */ - exposure_max = mode->vts_def -4 ; + exposure_max = mode->fll_def -4 ; exposure_def = (exposure_max <IMX219_EXPOSURE_DEFAULT ) ? exposure_max :IMX219_EXPOSURE_DEFAULT ; __v4l2_ctrl_modify_range(imx219->exposure,
@@ -1237,14 +1236,14 @@ static int imx219_init_controls(struct i imx219->vblank =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops,V4L2_CID_VBLANK ,IMX219_VBLANK_MIN ,IMX219_FLL_MAX - height,1 , - imx219->mode->vts_def - height); + imx219->mode->fll_def - height); hblank =IMX219_PPL_DEFAULT - imx219->mode->width; imx219->hblank =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops,V4L2_CID_HBLANK , hblank, hblank,1 , hblank);if (imx219->hblank) imx219->hblank->flags |=V4L2_CTRL_FLAG_READ_ONLY ; - exposure_max = imx219->mode->vts_def -4 ; + exposure_max = imx219->mode->fll_def -4 ; exposure_def = (exposure_max <IMX219_EXPOSURE_DEFAULT ) ? exposure_max :IMX219_EXPOSURE_DEFAULT ; imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
Greg Kroah-Hartman
maintainerPATCH 162/235 media: imx219: Fix maximum frame length in lines
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sakari Ailus <[email protected]>
[ Upstream commit 2c4f1ba7354312ad2d6e34e70a518a51a9344715 ]
The driver used the maximum frame length in lines value of 0xffff, but the maximum appears to be 0xfffe instead. Fix it.
--- drivers/media/i2c/imx219.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/i2c/imx219.c +1 -1
@@ -73,7 +73,7 @@/* V_TIMING internal */ #define IMX219_REG_FRM_LENGTH_A 0x0160 -#define IMX219_FLL_MAX 0xffff +#define IMX219_FLL_MAX 0xfffe#define IMX219_VBLANK_MIN 32#define IMX219_REG_ORIENTATION 0x0172
Greg Kroah-Hartman
maintainerPATCH 163/235 wifi: ath6kl: fix use-after-free in aggr_reset_state()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Daniel Hodges <[email protected]>
[ Upstream commit ba7debb4dd6427386862220e8335a53a4bfc235d ]
The aggr_reset_state() function uses timer_delete() (non-synchronous) for the aggregation timer before proceeding to delete TID state and before the structure is freed by callers like aggr_module_destroy().
If the timer callback (aggr_timeout) is executing when aggr_reset_state() is called, the callback will continue to access aggr_conn fields like rx_tid[] and stat[] which may be freed immediately after by kfree(aggr_info->aggr_conn) in aggr_module_destroy().
Additionally, the timer callback can re-arm itself via mod_timer() while aggr_reset_state() is running, creating a more complex race condition.
Use timer_delete_sync() instead to ensure any running timer callback has completed before returning.
--- drivers/net/wireless/ath/ath6kl/txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/ath/ath6kl/txrx.c +1 -1
@@ -1829,7 +1829,7 @@ void aggr_reset_state(struct aggr_info_creturn ;if (aggr_conn->timer_scheduled) { -del_timer (&aggr_conn->timer); +timer_delete_sync (&aggr_conn->timer); aggr_conn->timer_scheduled = false; }
Greg Kroah-Hartman
maintainerPATCH 164/235 media: v4l: async: Set owner for async sub-devices
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sakari Ailus <[email protected]>
[ Upstream commit 8a718752f5c339137c5b05e54f116cd26d5a4143 ]
Set the owner field of the async sub-devices by making v4l2_async_register_subdev() a macro and obtaining THIS_MODULE that way.
Stable-dep-of: 06cb687a5132 ("media: v4l2-fwnode: Fix subdev owner overwritten in v4l2_async_register_subdev_sensor()")
--- drivers/media/v4l2-core/v4l2-async.c | 8 ++++++-- include/media/v4l2-async.h | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-)
drivers/media/v4l2-core/v4l2-async.c +6 -2
@@ -747,7 +747,7 @@ v4l2_async_notifier_add_devname_subdev(s }EXPORT_SYMBOL_GPL (v4l2_async_notifier_add_devname_subdev); -int v4l2_async_register_subdev (struct v4l2_subdev *sd) +int __v4l2_async_register_subdev (struct v4l2_subdev *sd,struct module *module) {struct v4l2_async_notifier *subdev_notifier;struct v4l2_async_notifier *notifier;
@@ -761,6 +761,8 @@ int v4l2_async_register_subdev(struct v4if (!sd->fwnode && sd->dev) sd->fwnode =dev_fwnode (sd->dev); + sd->owner = module; +mutex_lock (&list_lock);INIT_LIST_HEAD (&sd->async_list);
@@ -811,9 +813,11 @@ err_unbind:mutex_unlock (&list_lock); + sd->owner =NULL ; +return ret; } -EXPORT_SYMBOL (v4l2_async_register_subdev); +EXPORT_SYMBOL (__v4l2_async_register_subdev);void v4l2_async_unregister_subdev (struct v4l2_subdev *sd) {
include/media/v4l2-async.h +3 -1
@@ -298,7 +298,9 @@ void v4l2_async_notifier_cleanup(struct* * @sd: pointer to &struct v4l2_subdev */ -int v4l2_async_register_subdev (struct v4l2_subdev *sd); +#define v4l2_async_register_subdev (sd) \ + __v4l2_async_register_subdev(sd, THIS_MODULE) +int __v4l2_async_register_subdev (struct v4l2_subdev *sd,struct module *module);/** * v4l2_async_register_subdev_sensor_common - registers a sensor sub-device to
Greg Kroah-Hartman
maintainerPATCH 165/235 media: v4l2-fwnode: Fix subdev owner overwritten in v4l2_async_register_subdev_sensor()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mirela Rabulea <[email protected]>
[ Upstream commit 06cb687a5132fcffe624c0070576ab852ac6b568 ]
The v4l2 helper v4l2_async_register_subdev_sensor() calls v4l2_async_register_subdev(), which is a macro that expands to __v4l2_async_register_subdev(sd,THIS_MODULE). Since the macro is expanded inside v4l2-fwnode.c, THIS_MODULE resolves to the v4l2-fwnode module rather than the sensor driver module that originally set sd->owner. When v4l2-fwnode is built-in, THIS_MODULE evaluates to NULL, which then overwrites the sensor driver's owner with NULL.
This causes the problem that the sensor module's reference count is never incremented during async registration, so the module can be removed while the subdevice is still in use by a notifier (e.g., a CSI-2 receiver bridge driver).
Fix this by renaming v4l2_async_register_subdev_sensor() to __v4l2_async_register_subdev_sensor() with an added explicit module argument and introducing a wrapper macro:
#define v4l2_async_register_subdev_sensor(sd) \
__v4l2_async_register_subdev_sensor(sd, THIS_MODULE)This ensures the sensor driver module is properly referenced even when the sensor driver does not init the owner field before calling v4l2_async_register_subdev_sensor() and prevents premature module removal.
--- drivers/media/v4l2-core/v4l2-fwnode.c | 7 ++++--- include/media/v4l2-async.h | 5 ++++- 2 files changed, 8 insertions(+), 4 deletions(-)
drivers/media/v4l2-core/v4l2-fwnode.c +4 -3
@@ -1337,7 +1337,8 @@ int v4l2_async_notifier_parse_fwnode_sen }EXPORT_SYMBOL_GPL (v4l2_async_notifier_parse_fwnode_sensor_common); -int v4l2_async_register_subdev_sensor_common (struct v4l2_subdev *sd) +int __v4l2_async_register_subdev_sensor_common (struct v4l2_subdev *sd, +struct module *module) {struct v4l2_async_notifier *notifier;int ret;
@@ -1360,7 +1361,7 @@ int v4l2_async_register_subdev_sensor_coif (ret <0 ) goto out_cleanup; - ret =v4l2_async_register_subdev (sd); + ret =__v4l2_async_register_subdev (sd, module);if (ret <0 ) goto out_unregister;
@@ -1377,7 +1378,7 @@ out_cleanup:return ret; } -EXPORT_SYMBOL_GPL (v4l2_async_register_subdev_sensor_common); +EXPORT_SYMBOL_GPL (__v4l2_async_register_subdev_sensor_common);MODULE_LICENSE ("GPL" );MODULE_AUTHOR ("Sakari Ailus <[email protected]>" );
include/media/v4l2-async.h +4 -1
@@ -321,8 +321,11 @@ int __v4l2_async_register_subdev(struct* An error is returned if the module is no longer loaded on any attempts * to register it. */ +#define v4l2_async_register_subdev_sensor_common (sd) \ + __v4l2_async_register_subdev_sensor_common(sd, THIS_MODULE)int __must_check -v4l2_async_register_subdev_sensor_common (struct v4l2_subdev *sd); +__v4l2_async_register_subdev_sensor_common (struct v4l2_subdev *sd, +struct module *module);/** * v4l2_async_unregister_subdev - unregisters a sub-device to the asynchronous
Greg Kroah-Hartman
maintainerPATCH 166/235 wifi: brcmfmac: drain bus_reset work on device removal
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
[ Upstream commit 43b25879f004c98defa2776bedc6ca4763c51945 ]
brcmf_fw_crashed() and the debugfs "reset" entry both schedule drvr->bus_reset, whose callback recovers drvr through container_of() and dereferences it. The removal path frees drvr (brcmf_free -> wiphy_free) without draining the work, so a bus_reset callback pending or running during removal can outlive drvr.
Cancellation cannot live in brcmf_detach() or brcmf_free(): the work callback reaches teardown through the bus .reset op (PCIe brcmf_pcie_reset -> brcmf_detach; SDIO brcmf_sdio_bus_reset -> brcmf_sdiod_remove -> brcmf_free), so cancelling there would wait for the running work and deadlock.
Add a per-bus mutex (bus_reset_lock) and route all arming through brcmf_bus_schedule_reset(), which under the lock skips when the bus is marked removing. Each bus remove entry calls brcmf_bus_cancel_reset_work(), which under the same lock sets removing and cancels the work. Holding the mutex across cancel_work_sync() makes the set-removing + drain step atomic. Every producer reaches the arming path from process context -- the PCIe firmware-halt notification runs in the threaded IRQ handler (brcmf_pcie_isr_thread) and the SDIO hostmail path runs from the data workqueue -- so the mutex is taken only in sleepable contexts. Where applicable the remove entry first stops the firmware-crash producer: on PCIe mask the mailbox and synchronize_irq; on SDIO unregister the bus interrupt and cancel the data worker, which also reports firmware halts through brcmf_fw_crashed(). The mutex is initialized at bus allocation. The SDIO suspend power-off path frees drvr through the same brcmf_sdiod_remove() and takes the same lock; resume re-allows the work only on a successful re-probe.
Also guard brcmf_fw_crashed() against a NULL bus_if/drvr: it can fire before brcmf_attach() wires up drvr, and it dereferences drvr (bphy_err/brcmf_dev_coredump) before reaching the arming gate.
The bus_reset work is shared across buses, so the drain is applied to every remove path: PCIe (the .reset op introduced by the Fixes commit), SDIO (arms the same work through brcmf_fw_crashed()), and USB (via the debugfs "reset" entry). cancel_work_sync() drains a running or pending bus_reset work item before removal frees drvr, and patch 1/2 makes the scratch-buffer release safe when reset teardown has already released those DMA buffers.
This patch fixes the lifetime of the bus_reset work item itself. It does not attempt to address the separate, pre-existing lifetime of the asynchronous firmware completion started by the PCIe reset path. That callback needs its own lifetime/ownership protocol and is being tracked separately.
This issue was found by an in-house static analysis tool.
--- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 13 ++++ drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h | 6 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 45 +++++++++++++- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 6 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 6 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h | 1 drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 3 7 files changed, 77 insertions(+), 3 deletions(-)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +13 -0
@@ -1042,6 +1042,7 @@ static int brcmf_ops_sdio_probe(struct s bus_if =kzalloc (sizeof (struct brcmf_bus ),GFP_KERNEL );if (!bus_if)return -ENOMEM ; +mutex_init (&bus_if->bus_reset_lock); sdiodev =kzalloc (sizeof (struct brcmf_sdio_dev ),GFP_KERNEL );if (!sdiodev) {kfree (bus_if);
@@ -1101,6 +1102,14 @@ static void brcmf_ops_sdio_remove(structif (func->num !=1 )return ; +/* Drain bus_reset before the shared brcmf_sdiod_remove() +* teardown, which the SDIO reset callback also reaches. The +* data worker can arm bus_reset via brcmf_fw_crashed(); cancel +* it first. +*/ +brcmf_sdio_cancel_datawork (sdiodev->bus); +brcmf_bus_cancel_reset_work (bus_if); +/* only proceed with rest of cleanup if func 1 */ brcmf_sdiod_remove (sdiodev);
@@ -1162,6 +1171,8 @@ static int brcmf_ops_sdio_suspend(struct }else {/* power will be cut so remove device, probe again in resume */ brcmf_sdiod_intr_unregister (sdiodev); +brcmf_sdio_cancel_datawork (sdiodev->bus); +brcmf_bus_cancel_reset_work (bus_if); ret =brcmf_sdiod_remove (sdiodev);if (ret)brcmf_err ("Failed to remove device on suspend\n" );
@@ -1187,6 +1198,8 @@ static int brcmf_ops_sdio_resume(struct ret =brcmf_sdiod_probe (sdiodev);if (ret)brcmf_err ("Failed to probe device on resume\n" ); +else +brcmf_bus_allow_reset_work (bus_if); }else {if (sdiodev->wowl_enabled && sdiodev->settings->bus.sdio.oob_irq_supported)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +6 -0
@@ -6,6 +6,7 @@#ifndef BRCMFMAC_BUS_H #define BRCMFMAC_BUS_H +#include <linux/mutex.h> #include "debug.h" /* IDs of the 6 default common rings of msgbuf protocol */
@@ -149,11 +150,16 @@ struct brcmf_bus {u32 chiprev;bool always_use_fws_queue;bool wowl_supported; +bool removing;/* device removal in progress; quiesce async work */ +struct mutex bus_reset_lock;const struct brcmf_bus_ops *ops;struct brcmf_bus_msgbuf *msgbuf; }; +void brcmf_bus_cancel_reset_work (struct brcmf_bus *bus_if); +void brcmf_bus_allow_reset_work (struct brcmf_bus *bus_if); +/* * callback wrappers */
drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +42 -3
68 lines not on this page · raw message
drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +6 -0
@@ -1892,6 +1892,7 @@ brcmf_pcie_probe(struct pci_dev *pdev, c ret = -ENOMEM ; goto fail; } +mutex_init (&bus->bus_reset_lock); bus->msgbuf =kzalloc (sizeof (*bus->msgbuf),GFP_KERNEL );if (!bus->msgbuf) { ret = -ENOMEM ;
@@ -1963,6 +1964,11 @@ brcmf_pcie_remove(struct pci_dev *pdev)if (devinfo->ci)brcmf_pcie_intr_disable (devinfo); +if (devinfo->irq_allocated) +synchronize_irq (pdev->irq); + +brcmf_bus_cancel_reset_work (bus); +brcmf_detach (&pdev->dev);brcmf_free (&pdev->dev);
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +6 -0
@@ -4530,6 +4530,12 @@ fail:return NULL ; } +void brcmf_sdio_cancel_datawork (struct brcmf_sdio *bus) +{ +if (bus) +cancel_work_sync (&bus->datawork); +} +/* Detach and free everything */ void brcmf_sdio_remove (struct brcmf_sdio *bus) {
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h +1 -0
@@ -373,6 +373,7 @@ int brcmf_sdiod_remove(struct brcmf_sdiostruct brcmf_sdio *brcmf_sdio_probe (struct brcmf_sdio_dev *sdiodev);void brcmf_sdio_remove (struct brcmf_sdio *bus);void brcmf_sdio_isr (struct brcmf_sdio *bus,bool in_isr); +void brcmf_sdio_cancel_datawork (struct brcmf_sdio *bus);void brcmf_sdio_wd_timer (struct brcmf_sdio *bus,bool active);void brcmf_sdio_wowl_config (struct device *dev,bool enabled);
drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +3 -0
@@ -1273,6 +1273,7 @@ static int brcmf_usb_probe_cb(struct brc ret = -ENOMEM ; goto fail; } +mutex_init (&bus->bus_reset_lock); bus->dev = dev; bus_pub->bus = bus;
@@ -1338,6 +1339,8 @@ brcmf_usb_disconnect_cb(struct brcmf_usbreturn ;brcmf_dbg (USB ,"Enter, bus_pub %p\n" , devinfo); +brcmf_bus_cancel_reset_work (devinfo->bus_pub.bus); +brcmf_detach (devinfo->dev);brcmf_free (devinfo->dev);kfree (devinfo->bus_pub.bus);
Greg Kroah-Hartman
maintainerPATCH 167/235 ALSA: seq: close a re-opened queue timer in the destructor
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Norbert Szetei <[email protected]>
[ Upstream commit 2c4dc0ed50b05cd847a4b34b8cebf0775f19aeb9 ]
queue_delete() closes the queue timer, then frees it. snd_seq_timer_close() clears q->timer->timeri. snd_use_lock_sync() then drains borrowers, and snd_seq_timer_delete() frees q->timer.
A borrower can re-open the timer inside that window. A SET_QUEUE_CLIENT that took a queueptr() use_lock reference before the queue was unlinked runs snd_seq_timer_open() after the close. Open refuses re-open only while timeri is set, and the close just cleared it, so it re-opens timeri.
snd_seq_timer_delete() does not close that instance. Its snd_seq_timer_stop() is a no-op, because running was cleared first. So it frees q->timer with the instance still live. The queue is freed next.
The instance stays on the global timer with callback_data pointing at the freed queue. A non-owner START on the unlocked queue arms it. The next tick derefs the freed queue in snd_seq_timer_interrupt().
Reachable by an unprivileged user with access to /dev/snd/seq. No CAP and no queue ownership required.
Close any lingering instance in the destructor. There, ->timeri can no longer change: the queue is unlinked and all use_lock borrowers have drained, so no snd_seq_queue_use() can re-open it. Close it before clearing q->timer. snd_timer_close() waits for any in-flight snd_seq_timer_interrupt() to finish, and that callback still reads q->timer (via snd_seq_check_queue()), so q->timer must stay valid until it drains.
--- sound/core/seq/seq_timer.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
sound/core/seq/seq_timer.c +12 -1
@@ -58,12 +58,23 @@ struct snd_seq_timer *snd_seq_timer_new(void snd_seq_timer_delete (struct snd_seq_timer **tmr) {struct snd_seq_timer *t = *tmr; - *tmr =NULL ; +struct snd_timer_instance *ti;if (t ==NULL ) {pr_debug ("ALSA: seq: snd_seq_timer_delete() called with NULL timer\n" );return ; } + +spin_lock_irq (&t->lock); + ti = t->timeri; + t->timeri =NULL ; +spin_unlock_irq (&t->lock); +if (ti) { +snd_timer_close (ti); +snd_timer_instance_free (ti); + } + + *tmr =NULL ; t->running =0 ;/* reset time */
Greg Kroah-Hartman
maintainerPATCH 168/235 serial: 8250_mid: Remove unneeded test for ->setup() presence
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andy Shevchenko <[email protected]>
[ Upstream commit 324facd1ccb353a213ea2c2785604f2507f79297 ]
All supported platforms by this driver require ->setup() and ->exit(). Remove unneeded test for ->setup() presence.
Stable-dep-of: 7fb13fd7e9a5 ("serial: 8250_mid: Fix NULL function pointer dereference on DNV/ICX-D/SNR platforms")
--- drivers/tty/serial/8250/8250_mid.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
drivers/tty/serial/8250/8250_mid.c +3 -5
@@ -319,11 +319,9 @@ static int mid8250_probe(struct pci_devif (!uart.port.membase)return -ENOMEM ; -if (mid->board->setup) { - ret = mid->board->setup (mid, &uart.port); -if (ret) -return ret; - } + ret = mid->board->setup (mid, &uart.port); +if (ret) +return ret; ret =mid8250_dma_setup (mid, &uart);if (ret)
Greg Kroah-Hartman
maintainerPATCH 169/235 serial: 8250_mid: Fix NULL function pointer dereference on DNV/ICX-D/SNR platforms
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiangshan Yi <[email protected]>
[ Upstream commit 7fb13fd7e9a59a37cd911efff83abe19e3ee029d ]
Commit b1b4efea05a5 ("serial: 8250_mid: Disable DMA for selected platforms") replaced the dnv_board setup and exit callbacks with PTR_IF(false, ...), which evaluates to NULL. However, the three call sites in mid8250_probe() and mid8250_remove() unconditionally dereference these function pointers without NULL checks, causing a NULL pointer dereference (kernel oops) on any Denverton (DNV), Ice Lake Xeon D (ICX-D/CDF), or Snowridge (SNR) platform.
Fix this by adding the missing NULL checks before calling the setup and exit callbacks.
--- drivers/tty/serial/8250/8250_mid.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-)
drivers/tty/serial/8250/8250_mid.c +9 -5
@@ -319,9 +319,11 @@ static int mid8250_probe(struct pci_devif (!uart.port.membase)return -ENOMEM ; - ret = mid->board->setup (mid, &uart.port); -if (ret) -return ret; +if (mid->board->setup) { + ret = mid->board->setup (mid, &uart.port); +if (ret) +return ret; + } ret =mid8250_dma_setup (mid, &uart);if (ret)
@@ -337,7 +339,8 @@ static int mid8250_probe(struct pci_devreturn 0 ; err: - mid->board->exit (mid); +if (mid->board->exit) + mid->board->exit (mid);return ret; }
@@ -347,7 +350,8 @@ static void mid8250_remove(struct pci_deserial8250_unregister_port (mid->line); - mid->board->exit (mid); +if (mid->board->exit) + mid->board->exit (mid); }static const struct mid8250_board pnw_board = {
Greg Kroah-Hartman
maintainerPATCH 170/235 mptcp: only set DATA_FIN when a mapping is present
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
[ Upstream commit b2ff91b752b0d85e8815e7f44fd85205c4268094 ]
mptcp_get_options() clears only the status group of struct mptcp_options_received; data_seq, subflow_seq and data_len are filled in by mptcp_parse_option() exclusively inside the DSS mapping block, which runs only when the DSS M (mapping present) bit is set.
A peer can send a DSS option with the DATA_FIN flag set but the mapping bit clear. The parser then records mp_opt->data_fin while leaving data_len and data_seq uninitialized. For a zero-length segment mptcp_incoming_options() evaluates
if (mp_opt.data_fin && mp_opt.data_len == 1 && mptcp_update_rcv_data_fin(msk, mp_opt.data_seq, mp_opt.dsn64))
which reads the uninitialized data_len and data_seq; KMSAN reports an uninit-value in mptcp_incoming_options(). The stale data_seq can also be fed into the receive-side DATA_FIN sequence tracking.
Record the DATA_FIN flag only when the DSS option carries a mapping, so data_fin is never set without data_seq and data_len also being present. data_fin is part of the status group that mptcp_get_options() clears up front, so on the no-map path it stays zero and the zero-length DATA_FIN branch is simply skipped. A DATA_FIN is always transmitted together with a mapping (mptcp_write_data_fin() sets use_map along with data_seq and data_len), so legitimate DATA_FIN handling is unaffected.
Move the pr_debug() that logs the parsed DSS flags below the mapping block, so it reports the final data_fin value instead of the stale one it would otherwise print before the assignment.
--- net/mptcp/options.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
net/mptcp/options.c +7 -6
@@ -142,17 +142,11 @@ static void mptcp_parse_option(const str*/ mp_opt->mpc_map =0 ; flags = (*ptr++) &MPTCP_DSS_FLAG_MASK ; - mp_opt->data_fin = (flags &MPTCP_DSS_DATA_FIN ) !=0 ; mp_opt->dsn64 = (flags &MPTCP_DSS_DSN64 ) !=0 ; mp_opt->use_map = (flags &MPTCP_DSS_HAS_MAP ) !=0 ; mp_opt->ack64 = (flags &MPTCP_DSS_ACK64 ) !=0 ; mp_opt->use_ack = (flags &MPTCP_DSS_HAS_ACK ); -pr_debug ("data_fin=%d dsn64=%d use_map=%d ack64=%d use_ack=%d\n" , - mp_opt->data_fin, mp_opt->dsn64, - mp_opt->use_map, mp_opt->ack64, - mp_opt->use_ack); - expected_opsize =TCPOLEN_MPTCP_DSS_BASE ;if (mp_opt->use_ack) {
@@ -163,12 +157,18 @@ static void mptcp_parse_option(const str }if (mp_opt->use_map) { + mp_opt->data_fin = (flags &MPTCP_DSS_DATA_FIN ) !=0 ;if (mp_opt->dsn64) expected_opsize +=TCPOLEN_MPTCP_DSS_MAP64 ;else expected_opsize +=TCPOLEN_MPTCP_DSS_MAP32 ; } +pr_debug ("data_fin=%d dsn64=%d use_map=%d ack64=%d use_ack=%d\n" , + mp_opt->data_fin, mp_opt->dsn64, + mp_opt->use_map, mp_opt->ack64, + mp_opt->use_ack); +/* RFC 6824, Section 3.3: * If a checksum is present, but its use had * not been negotiated in the MP_CAPABLE handshake,
@@ -306,6 +306,7 @@ void mptcp_get_options(const struct sk_b mp_opt->port =0 ; mp_opt->rm_addr =0 ; mp_opt->dss =0 ; + mp_opt->data_fin =0 ; length = (th->doff *4 ) -sizeof (struct tcphdr ); ptr = (const unsigned char *)(th +1 );
Greg Kroah-Hartman
maintainerPATCH 171/235 sc16is7xx: Properly resume TX after stop
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tomasz Moń <[email protected]>
[ Upstream commit cc4c1d05eb10c3ad4c6315f1897bc56b1e7429aa ]
sc16is7xx_stop_tx() clears THRI bit and thus disables THRI interrupt. This makes it possible for transmission to cease indefinitely when more than 64 characters are being sent.
The sc16is7xx_handle_tx() call executed by sc16is7xx_tx_proc() can send up to FIFO length (64) characters. If more characters are written to the output buffer, then the THRI interrupt is needed.
Solve the issue by enabling THRI interrupt in sc16is7xx_tx_proc().
Stable-dep-of: af071d9e07e5 ("serial: sc16is7xx: implement gpio get_direction() callback")
--- drivers/tty/serial/sc16is7xx.c | 47 +++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-)
drivers/tty/serial/sc16is7xx.c +41 -6
@@ -307,7 +307,8 @@ struct sc16is7xx_devtype {struct sc16is7xx_one_config {unsigned int flags; -u8 ier_clear; +u8 ier_mask; +u8 ier_val; };struct sc16is7xx_one {
@@ -341,6 +342,9 @@ static struct uart_driver sc16is7xx_uart .nr = SC16IS7XX_MAX_DEVS, }; +static void sc16is7xx_ier_set (struct uart_port *port,u8 bit); +static void sc16is7xx_stop_tx (struct uart_port *port); +#define to_sc16is7xx_port (p,e) ((container_of((p), struct sc16is7xx_port, e)))#define to_sc16is7xx_one (p,e) ((container_of((p), struct sc16is7xx_one, e)))
@@ -650,6 +654,7 @@ static void sc16is7xx_handle_tx(struct ustruct sc16is7xx_port *s =dev_get_drvdata (port->dev);struct circ_buf *xmit = &port->state->xmit;unsigned int txlen, to_send, i; +unsigned long flags;if (unlikely (port->x_char)) {sc16is7xx_port_write (port,SC16IS7XX_THR_REG , port->x_char);
@@ -658,8 +663,12 @@ static void sc16is7xx_handle_tx(struct ureturn ; } -if (uart_circ_empty (xmit) ||uart_tx_stopped (port)) +if (uart_circ_empty (xmit) ||uart_tx_stopped (port)) { +spin_lock_irqsave (&port->lock, flags); +sc16is7xx_stop_tx (port); +spin_unlock_irqrestore (&port->lock, flags);return ; + }/* Get length of data pending in circular buffer */ to_send =uart_circ_chars_pending (xmit);
@@ -686,8 +695,13 @@ static void sc16is7xx_handle_tx(struct usc16is7xx_fifo_write (port, to_send); } +spin_lock_irqsave (&port->lock, flags);if (uart_circ_chars_pending (xmit) <WAKEUP_CHARS )uart_write_wakeup (port); + +if (uart_circ_empty (xmit)) +sc16is7xx_stop_tx (port); +spin_unlock_irqrestore (&port->lock, flags); }static bool sc16is7xx_port_irq (struct sc16is7xx_port *s,int portno)
@@ -762,6 +776,7 @@ static void sc16is7xx_tx_proc(struct kth {struct uart_port *port = &(to_sc16is7xx_one (ws, tx_work)->port);struct sc16is7xx_port *s =dev_get_drvdata (port->dev); +unsigned long flags;if ((port->rs485.flags &SER_RS485_ENABLED ) && (port->rs485.delay_rts_before_send >0 ))
@@ -770,6 +785,10 @@ static void sc16is7xx_tx_proc(struct kthmutex_lock (&s->efr_lock);sc16is7xx_handle_tx (port);mutex_unlock (&s->efr_lock); + +spin_lock_irqsave (&port->lock, flags); +sc16is7xx_ier_set (port,SC16IS7XX_IER_THRI_BIT ); +spin_unlock_irqrestore (&port->lock, flags); }static void sc16is7xx_reconf_rs485 (struct uart_port *port)
@@ -819,7 +838,7 @@ static void sc16is7xx_reg_proc(struct kt }if (config.flags &SC16IS7XX_RECONF_IER )sc16is7xx_port_update (&one->port,SC16IS7XX_IER_REG , - config.ier_clear,0 ); + config.ier_mask, config.ier_val);if (config.flags &SC16IS7XX_RECONF_RS485 )sc16is7xx_reconf_rs485 (&one->port);
@@ -830,8 +849,24 @@ static void sc16is7xx_ier_clear(struct ustruct sc16is7xx_port *s =dev_get_drvdata (port->dev);struct sc16is7xx_one *one =to_sc16is7xx_one (port, port); +lockdep_assert_held_once (&port->lock); + + one->config.flags |=SC16IS7XX_RECONF_IER ; + one->config.ier_mask |= bit; + one->config.ier_val &= ~bit; +kthread_queue_work (&s->kworker, &one->reg_work); +} + +static void sc16is7xx_ier_set (struct uart_port *port,u8 bit) +{ +struct sc16is7xx_port *s =dev_get_drvdata (port->dev); +struct sc16is7xx_one *one =to_sc16is7xx_one (port, port); + +lockdep_assert_held_once (&port->lock); + one->config.flags |=SC16IS7XX_RECONF_IER ; - one->config.ier_clear |= bit; + one->config.ier_mask |= bit; + one->config.ier_val |= bit;kthread_queue_work (&s->kworker, &one->reg_work); }
@@ -1059,8 +1094,8 @@ static int sc16is7xx_startup(struct uartSC16IS7XX_EFCR_TXDISABLE_BIT , 0); -/* Enable RX, TX interrupts */ - val =SC16IS7XX_IER_RDI_BIT |SC16IS7XX_IER_THRI_BIT ; +/* Enable RX interrupt */ + val =SC16IS7XX_IER_RDI_BIT ;sc16is7xx_port_write (port,SC16IS7XX_IER_REG , val);return 0 ;
Greg Kroah-Hartman
maintainerPATCH 172/235 serial: sc16is7xx: Fill in rs485_supported
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilpo Järvinen <[email protected]>
[ Upstream commit 267913ecf73745ca3e8fc8282671b0b4f24df5fe ]
Add information on supported serial_rs485 features.
This driver does not support delay_rts_after_send but the pre-existing behavior is to return -EINVAL if delay_rts_after_send is non-zero. In contrast, other drivers that do not support delay_rts_after_send either zero delay_rts_after_send or do not care (leave the inaccurate value). As changing this would cause userspace visible impact, the change is not attempted here. But perhaps it should be still tried (maybe nobody finds that kind of API oddity significant)?
Stable-dep-of: af071d9e07e5 ("serial: sc16is7xx: implement gpio get_direction() callback")
--- drivers/tty/serial/sc16is7xx.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/tty/serial/sc16is7xx.c +7 -0
@@ -1239,6 +1239,12 @@ static int sc16is7xx_gpio_direction_outp }#endif +static const struct serial_rs485 sc16is7xx_rs485_supported = { + .flags =SER_RS485_ENABLED |SER_RS485_RTS_AFTER_SEND , + .delay_rts_before_send =1 , + .delay_rts_after_send =1 ,/* Not supported but keep returning -EINVAL */ +}; +static int sc16is7xx_probe (struct device *dev,const struct sc16is7xx_devtype *devtype,struct regmap *regmap,int irq)
@@ -1326,6 +1332,7 @@ static int sc16is7xx_probe(struct device s->p[i].port.iotype =UPIO_PORT ; s->p[i].port.uartclk = freq; s->p[i].port.rs485_config = sc16is7xx_config_rs485; + s->p[i].port.rs485_supported = &sc16is7xx_rs485_supported; s->p[i].port.ops = &sc16is7xx_ops; s->p[i].port.line =sc16is7xx_alloc_line ();if (s->p[i].port.line >=SC16IS7XX_MAX_DEVS ) {
Greg Kroah-Hartman
maintainerPATCH 173/235 serial: sc16is7xx: remove obsolete out_thread label
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hugo Villeneuve <[email protected]>
[ Upstream commit dabc54a45711fe77674a6c0348231e00e66bd567 ]
Commit c8f71b49ee4d ("serial: sc16is7xx: setup GPIO controller later in probe") moved GPIO setup code later in probe function. Doing so also required to move ports cleanup code (out_ports label) after the GPIO cleanup code.
After these moves, the out_thread label becomes misplaced and makes part of the cleanup code illogical.
This patch remove the now obsolete out_thread label and make GPIO setup code jump to out_ports label if it fails.
Stable-dep-of: af071d9e07e5 ("serial: sc16is7xx: implement gpio get_direction() callback")
--- drivers/tty/serial/sc16is7xx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/tty/serial/sc16is7xx.c +1 -3
@@ -1397,7 +1397,7 @@ static int sc16is7xx_probe(struct device s->gpio.can_sleep =1 ; ret =gpiochip_add_data (&s->gpio, s);if (ret) - goto out_thread; + goto out_ports; }#endif
@@ -1423,8 +1423,6 @@ static int sc16is7xx_probe(struct device#ifdef CONFIG_GPIOLIB if (devtype->nr_gpio)gpiochip_remove (&s->gpio); - -out_thread:#endif out_ports:
Greg Kroah-Hartman
maintainerPATCH 174/235 serial: sc16is7xx: fix regression with GPIO configuration
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hugo Villeneuve <[email protected]>
[ Upstream commit 0499942928341d572a42199580433c2b0725211e ]
Commit 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") and commit 21144bab4f11 ("sc16is7xx: Handle modem status lines") changed the function of the GPIOs pins to act as modem control lines without any possibility of selecting GPIO function.
As a consequence, applications that depends on GPIO lines configured by default as GPIO pins no longer work as expected.
Also, the change to select modem control lines function was done only for channel A of dual UART variants (752/762). This was not documented in the log message.
Allow to specify GPIO or modem control line function in the device tree, and for each of the ports (A or B).
Do so by using the new device-tree property named "nxp,modem-control-line-ports" (property added in separate patch).
When registering GPIO chip controller, mask-out GPIO pins declared as modem control lines according to this new DT property.
Stable-dep-of: af071d9e07e5 ("serial: sc16is7xx: implement gpio get_direction() callback")
--- drivers/tty/serial/sc16is7xx.c | 124 ++++++++++++++++++++++++++++++++++------- 1 file changed, 105 insertions(+), 19 deletions(-)
drivers/tty/serial/sc16is7xx.c +105 -19
@@ -237,7 +237,8 @@/* IOControl register bits (Only 750/760) */ #define SC16IS7XX_IOCONTROL_LATCH_BIT (1 << 0)/* Enable input latching */ -#define SC16IS7XX_IOCONTROL_MODEM_BIT (1 << 1)/* Enable GPIO[7:4] as modem pins */ +#define SC16IS7XX_IOCONTROL_MODEM_A_BIT (1 << 1)/* Enable GPIO[7:4] as modem A pins */ +#define SC16IS7XX_IOCONTROL_MODEM_B_BIT (1 << 2)/* Enable GPIO[3:0] as modem B pins */ #define SC16IS7XX_IOCONTROL_SRESET_BIT (1 << 3)/* Software Reset */ /* EFCR register bits */
@@ -294,6 +295,7 @@/* Misc definitions */ #define SC16IS7XX_FIFO_SIZE (64)#define SC16IS7XX_REG_SHIFT 2 +#define SC16IS7XX_GPIOS_PER_BANK 4struct sc16is7xx_devtype {char name[10 ];
@@ -326,7 +328,9 @@ struct sc16is7xx_port {struct clk *clk;#ifdef CONFIG_GPIOLIB struct gpio_chip gpio; +unsigned long gpio_valid_mask;#endif +u8 mctrl_mask;unsigned char buf[SC16IS7XX_FIFO_SIZE ];struct kthread_worker kworker;struct task_struct *kworker_task;
@@ -1237,8 +1241,98 @@ static int sc16is7xx_gpio_direction_outpreturn 0 ; } + +static int sc16is7xx_gpio_init_valid_mask (struct gpio_chip *chip, +unsigned long *valid_mask, +unsigned int ngpios) +{ +struct sc16is7xx_port *s =gpiochip_get_data (chip); + + *valid_mask = s->gpio_valid_mask; + +return 0 ; +} + +static int sc16is7xx_setup_gpio_chip (struct sc16is7xx_port *s) +{ +struct device *dev = s->p[0 ].port.dev; + +if (!s->devtype->nr_gpio) +return 0 ; + +switch (s->mctrl_mask) { +case 0 : + s->gpio_valid_mask =GENMASK (7 ,0 ); +break ; +case SC16IS7XX_IOCONTROL_MODEM_A_BIT : + s->gpio_valid_mask =GENMASK (3 ,0 ); +break ; +case SC16IS7XX_IOCONTROL_MODEM_B_BIT : + s->gpio_valid_mask =GENMASK (7 ,4 ); +break ; +default : +break ; + } + +if (s->gpio_valid_mask ==0 ) +return 0 ; + + s->gpio.owner =THIS_MODULE ; + s->gpio.parent = dev; + s->gpio.label =dev_name (dev); + s->gpio.init_valid_mask = sc16is7xx_gpio_init_valid_mask; + s->gpio.direction_input = sc16is7xx_gpio_direction_input; + s->gpio.get = sc16is7xx_gpio_get; + s->gpio.direction_output = sc16is7xx_gpio_direction_output; + s->gpio.set = sc16is7xx_gpio_set; + s->gpio.base =-1 ; + s->gpio.ngpio = s->devtype->nr_gpio; + s->gpio.can_sleep =1 ; + +return gpiochip_add_data (&s->gpio, s); +}#endif +/* +* Configure ports designated to operate as modem control lines. +*/ +static int sc16is7xx_setup_mctrl_ports (struct sc16is7xx_port *s) +{ +int i; +int ret; +int count; +u32 mctrl_port[2 ]; +struct device *dev = s->p[0 ].port.dev; + + count =device_property_count_u32 (dev,"nxp,modem-control-line-ports" ); +if (count <0 || count >ARRAY_SIZE (mctrl_port)) +return 0 ; + + ret =device_property_read_u32_array (dev,"nxp,modem-control-line-ports" , + mctrl_port, count); +if (ret) +return ret; + + s->mctrl_mask =0 ; + +for (i =0 ; i < count; i++) { +/* Use GPIO lines as modem control lines */ +if (mctrl_port[i] ==0 ) + s->mctrl_mask |=SC16IS7XX_IOCONTROL_MODEM_A_BIT ; +else if (mctrl_port[i] ==1 ) + s->mctrl_mask |=SC16IS7XX_IOCONTROL_MODEM_B_BIT ; + } + +if (s->mctrl_mask) +regmap_update_bits ( + s->regmap, +SC16IS7XX_IOCONTROL_REG <<SC16IS7XX_REG_SHIFT , +SC16IS7XX_IOCONTROL_MODEM_A_BIT | +SC16IS7XX_IOCONTROL_MODEM_B_BIT , s->mctrl_mask); + +return 0 ; +} +static const struct serial_rs485 sc16is7xx_rs485_supported = { .flags =SER_RS485_ENABLED |SER_RS485_RTS_AFTER_SEND , .delay_rts_before_send =1 ,
@@ -1346,6 +1440,7 @@ static int sc16is7xx_probe(struct devicesc16is7xx_port_write (&s->p[i].port,SC16IS7XX_EFCR_REG ,SC16IS7XX_EFCR_RXDISABLE_BIT |SC16IS7XX_EFCR_TXDISABLE_BIT ); +/* Initialize kthread work structs */ kthread_init_work (&s->p[i].tx_work, sc16is7xx_tx_proc);kthread_init_work (&s->p[i].reg_work, sc16is7xx_reg_proc);
@@ -1382,23 +1477,14 @@ static int sc16is7xx_probe(struct device s->p[u].irda_mode = true; } + ret =sc16is7xx_setup_mctrl_ports (s); +if (ret) + goto out_ports; +#ifdef CONFIG_GPIOLIB -if (devtype->nr_gpio) { -/* Setup GPIO cotroller */ - s->gpio.owner =THIS_MODULE ; - s->gpio.parent = dev; - s->gpio.label =dev_name (dev); - s->gpio.direction_input = sc16is7xx_gpio_direction_input; - s->gpio.get = sc16is7xx_gpio_get; - s->gpio.direction_output = sc16is7xx_gpio_direction_output; - s->gpio.set = sc16is7xx_gpio_set; - s->gpio.base =-1 ; - s->gpio.ngpio = devtype->nr_gpio; - s->gpio.can_sleep =1 ; - ret =gpiochip_add_data (&s->gpio, s); -if (ret) - goto out_ports; - } + ret =sc16is7xx_setup_gpio_chip (s); +if (ret) + goto out_ports;#endif /*
@@ -1421,7 +1507,7 @@ static int sc16is7xx_probe(struct devicereturn 0 ;#ifdef CONFIG_GPIOLIB -if (devtype->nr_gpio) +if (s->gpio_valid_mask)gpiochip_remove (&s->gpio);#endif
@@ -1446,7 +1532,7 @@ static int sc16is7xx_remove(struct devicint i;#ifdef CONFIG_GPIOLIB -if (s->devtype->nr_gpio) +if (s->gpio_valid_mask)gpiochip_remove (&s->gpio);#endif
Greg Kroah-Hartman
maintainerPATCH 175/235 serial: sc16is7xx: implement gpio get_direction() callback
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hugo Villeneuve <[email protected]>
[ Upstream commit af071d9e07e57cfff239e8d09d2f3b05ebc9c667 ]
It's strongly recommended for GPIO drivers to always implement the .get_direction() callback - even when the direction is tracked in software. The GPIO core emits a warning when the callback is missing and a user reads the direction of a line, e.g. via /sys/kernel/debug/gpio.
--- drivers/tty/serial/sc16is7xx.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/tty/serial/sc16is7xx.c +12 -0
@@ -1204,6 +1204,17 @@ static void sc16is7xx_gpio_set(struct gp val ? BIT(offset) : 0); } +static int sc16is7xx_gpio_get_direction (struct gpio_chip *chip,unsigned int offset) +{ +struct sc16is7xx_port *s =gpiochip_get_data (chip); +struct uart_port *port = &s->p[0 ].port; +unsigned int val; + + val =sc16is7xx_port_read (port,SC16IS7XX_IODIR_REG ); + +return val &BIT (offset) ?GPIO_LINE_DIRECTION_OUT :GPIO_LINE_DIRECTION_IN ; +} +static int sc16is7xx_gpio_direction_input (struct gpio_chip *chip,unsigned offset) {
@@ -1281,6 +1292,7 @@ static int sc16is7xx_setup_gpio_chip(str s->gpio.parent = dev; s->gpio.label =dev_name (dev); s->gpio.init_valid_mask = sc16is7xx_gpio_init_valid_mask; + s->gpio.get_direction = sc16is7xx_gpio_get_direction; s->gpio.direction_input = sc16is7xx_gpio_direction_input; s->gpio.get = sc16is7xx_gpio_get; s->gpio.direction_output = sc16is7xx_gpio_direction_output;
Greg Kroah-Hartman
maintainerPATCH 176/235 mptcp: fix subflow accounting on close
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Paolo Abeni <[email protected]>
[ Upstream commit 95d686517884a403412b000361cee2b08b2ed1e6 ]
If the PM closes a fully established MPJ subflow or the subflow creation errors out in it's early stage the subflows counter is not bumped accordingly.
This change adds the missing accounting, additionally taking care of updating accordingly the 'accept_subflow' flag.
Stable-dep-of: f3ca0ee2cc30 ("mptcp: decrement subflows counter on failed passive join")
--- net/mptcp/protocol.h | 14 ++++++++++++++ net/mptcp/subflow.c | 12 +++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-)
net/mptcp/protocol.h +14 -0
@@ -498,6 +498,20 @@ void mptcp_pm_nl_rm_subflow_received(strint mptcp_pm_nl_get_local_id (struct mptcp_sock *msk,struct sock_common *skc);bool mptcp_pm_nl_is_backup (struct mptcp_sock *msk,struct mptcp_addr_info *skc); +/* called under PM lock */ +static inline void __mptcp_pm_close_subflow (struct mptcp_sock *msk) +{ +if (--msk->pm.subflows < msk->pm.subflows_max) +WRITE_ONCE (msk->pm.accept_subflow, true); +} + +static inline void mptcp_pm_close_subflow (struct mptcp_sock *msk) +{ +spin_lock_bh (&msk->pm.lock); +__mptcp_pm_close_subflow (msk); +spin_unlock_bh (&msk->pm.lock); +} +static inline struct mptcp_ext *mptcp_get_ext (struct sk_buff *skb) {return (struct mptcp_ext *)skb_ext_find (skb,SKB_EXT_MPTCP );
net/mptcp/subflow.c +9 -3
@@ -1122,18 +1122,18 @@ int __mptcp_subflow_connect(struct sockstruct sockaddr_storage addr;int remote_id = remote->id;int local_id = loc->id; +int err = -ENOTCONN ;struct socket *sf;struct sock *ssk;u32 remote_token;int addrlen; -int err;if (!mptcp_is_fully_established (sk)) -return -ENOTCONN ; + goto err_out; err =mptcp_subflow_create_socket (sk, &sf);if (err) -return err; + goto err_out; ssk = sf->sk; subflow =mptcp_subflow_ctx (ssk);
@@ -1187,6 +1187,12 @@ int __mptcp_subflow_connect(struct sock failed:sock_release (sf); + +err_out: +/* we account subflows before the creation, and this failures will not +* be caught by sk_state_change() +*/ +mptcp_pm_close_subflow (msk);return err; }
Greg Kroah-Hartman
maintainerPATCH 177/235 mptcp: decrement subflows counter on failed passive join
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chenguang Zhao <[email protected]>
[ Upstream commit f3ca0ee2cc308e33896536789cbc5f3a12ca7b30 ]
mptcp_pm_allow_new_subflow() increments extra_subflows before __mptcp_finish_join() on the passive MP_JOIN path.
In case of race conditions, the subflow is dropped without calling mptcp_close_ssk(), so the counter is not rolled back.
Call mptcp_pm_close_subflow() when the join completion fails to decrement the subflows counter.
--- net/mptcp/protocol.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
net/mptcp/protocol.c +3 -1
@@ -2663,8 +2663,10 @@ bool mptcp_finish_join(struct sock *sk)if (ret && !WARN_ON_ONCE (!list_empty (&subflow->node)))list_add_tail (&subflow->node, &msk->join_list);spin_unlock_bh (&msk->join_list_lock); -if (!ret) +if (!ret) { +mptcp_pm_close_subflow (msk);return false; + }/* attach to msk socket only after we are sure he will deal with us * at close time
Greg Kroah-Hartman
maintainerPATCH 178/235 sctp: avoid auth_enable sysctl UAF during netns teardown
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
[ Upstream commit f8d5e7846025f4ab15a461235f8ebae9094a361a ]
proc_sctp_do_auth() updates the SCTP control socket after changing net.sctp.auth_enable. The handler gets the per-net SCTP state from ctl->data, so an already opened sysctl file can still target a network namespace while that namespace is being torn down.
SCTP previously registered its per-net sysctls from sctp_defaults_init(), while the control socket is created later from sctp_ctrlsock_init(). This exposed a window during initialization where auth_enable was writable before net->sctp.ctl_sock existed, and a teardown window where auth_enable stayed writable after inet_ctl_sock_destroy() had released the control socket.
Move the per-net SCTP sysctl registration into sctp_ctrlsock_init() after sctp_ctl_sock_init() succeeds, and unregister the sysctl table before destroying the control socket in sctp_ctrlsock_exit(). If sysctl registration fails after the control socket was created, destroy the control socket in the same init path.
Make sctp_sysctl_net_unregister() tolerate a missing header and clear the saved pointer so init-error and exit paths can safely share the unregister helper.
--- net/sctp/protocol.c | 20 ++++++++++++-------- net/sctp/sysctl.c | 9 +++++++-- 2 files changed, 19 insertions(+), 10 deletions(-)
net/sctp/protocol.c +12 -8
@@ -1286,10 +1286,6 @@ static int __net_init sctp_defaults_init/* Initialize maximum autoclose timeout. */ net->sctp.max_autoclose =INT_MAX /HZ ; - status =sctp_sysctl_net_register (net); -if (status) - goto err_sysctl_register; -/* Allocate and initialise sctp mibs. */ status =init_sctp_mibs (net);if (status)
@@ -1323,8 +1319,6 @@ err_init_proc:cleanup_sctp_mibs (net);#endif err_init_mibs: -sctp_sysctl_net_unregister (net); -err_sysctl_register:return status; }
@@ -1339,7 +1333,6 @@ static void __net_exit sctp_defaults_exi net->sctp.proc_net_sctp =NULL ;#endif cleanup_sctp_mibs (net); -sctp_sysctl_net_unregister (net); }static struct pernet_operations sctp_defaults_ops = {
@@ -1353,16 +1346,27 @@ static int __net_init sctp_ctrlsock_init/* Initialize the control inode/socket for handling OOTB packets. */ status =sctp_ctl_sock_init (net); -if (status) +if (status) {pr_err ("Failed to initialize the SCTP control sock\n" ); +return status; + } + + status =sctp_sysctl_net_register (net); +if (status) { +inet_ctl_sock_destroy (net->sctp.ctl_sock); + net->sctp.ctl_sock =NULL ; + }return status; } static void __net_exit sctp_ctrlsock_exit(struct net *net) { +sctp_sysctl_net_unregister (net); +/* Free the control endpoint. */ inet_ctl_sock_destroy (net->sctp.ctl_sock); + net->sctp.ctl_sock =NULL ; }static struct pernet_operations sctp_ctrlsock_ops = {
net/sctp/sysctl.c +7 -2
@@ -515,11 +515,16 @@ int sctp_sysctl_net_register(struct netvoid sctp_sysctl_net_unregister (struct net *net) { +struct ctl_table_header *header = net->sctp.sysctl_header;struct ctl_table *table; - table = net->sctp.sysctl_header->ctl_table_arg; -unregister_net_sysctl_table (net->sctp.sysctl_header); +if (!header) +return ; + + table = header->ctl_table_arg; +unregister_net_sysctl_table (header);kfree (table); + net->sctp.sysctl_header =NULL ; }static struct ctl_table_header *sctp_sysctl_header;
Greg Kroah-Hartman
maintainerPATCH 179/235 ceph: avoid fs reclaim while using current->journal_info
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Max Kellermann <[email protected]>
[ Upstream commit 5b602344a49e039e792ce5a8923bcc61412ee134 ]
handle_reply() stores a ceph_mds_request pointer in current->journal_info while filling the inode and dentry cache from an MDS reply.
An allocation in this section can enter direct reclaim and prune dentries from another filesystem. If this dirties an ext4 inode, ext4 starts a JBD2 transaction. JBD2 interprets the Ceph request in current->journal_info as a journal handle and dereferences the request's r_tid as h_transaction, causing a kernel crash, e.g.:
Unable to handle kernel paging request at virtual address 00000000077b4818 [...] Internal error: Oops: 0000000096000004 [#1] SMP Modules linked in: CPU: 6 UID: 0 PID: 2699135 Comm: kworker/6:3 Tainted: G W 6.18.38-i3 #1113 NONE [...] Workqueue: ceph-msgr ceph_con_workfn pstate: 80400009 (Nzcv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : jbd2__journal_start+0x2c/0x208 lr : __ext4_journal_start_sb+0x100/0x178 [...] Call trace: jbd2__journal_start+0x2c/0x208 (P) __ext4_journal_start_sb+0x100/0x178 ext4_dirty_inode+0x3c/0x90 __mark_inode_dirty+0x58/0x400 iput.part.0+0x2b0/0x370 iput+0x18/0x30 dentry_unlink_inode+0xc0/0x158 __dentry_kill+0x80/0x250 shrink_dentry_list+0x90/0x130 prune_dcache_sb+0x60/0x98 super_cache_scan+0xe8/0x190 do_shrink_slab+0x174/0x388 shrink_slab+0xd8/0x4c0 shrink_node+0x31c/0x908 do_try_to_free_pages+0xd0/0x508 try_to_free_pages+0x11c/0x238 __alloc_frozen_pages_noprof+0x4d0/0xdd0 __folio_alloc_noprof+0x18/0x70 __filemap_get_folio+0x248/0x440 ceph_readdir_prepopulate+0x570/0x9e8 mds_dispatch+0x1424/0x1ba0 ceph_con_process_message+0x74/0xa0 ceph_con_v1_try_read+0x3a0/0x1510 ceph_con_workfn+0x260/0x460
Enter a scoped NOFS allocation context and leave it after clearing journal_info. This prevents filesystem reclaim from recursing into another filesystem while the field contains Ceph-private data.
--- fs/ceph/mds_client.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
fs/ceph/mds_client.c +11 -0
@@ -6,6 +6,7 @@#include <linux/slab.h> #include <linux/gfp.h> #include <linux/sched.h> +#include <linux/sched/mm.h> #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/ratelimit.h>
@@ -3126,6 +3127,7 @@ static void handle_reply(struct ceph_mdsstruct ceph_mds_reply_head *head = msg->front.iov_base;struct ceph_mds_reply_info_parsed *rinfo;/* parsed reply info */ struct ceph_snap_realm *realm; +unsigned int nofs_flags;u64 tid;int err, result;int mds = session->s_mds;
@@ -3258,6 +3260,14 @@ static void handle_reply(struct ceph_mds/* insert trace into our cache */ mutex_lock (&req->r_fill_mutex); + +/* disable fs reclaim while we are using current->journal_info +* for our own purposes, or else shrinkers of other +* filesystems might dereference this pointer as a different +* type +*/ + nofs_flags =memalloc_nofs_save (); + current->journal_info = req; err =ceph_fill_trace (mdsc->fsc->sb, req);if (err ==0 ) {
@@ -3266,6 +3276,7 @@ static void handle_reply(struct ceph_mdsceph_readdir_prepopulate (req, req->r_session); } current->journal_info =NULL ; +memalloc_nofs_restore (nofs_flags);mutex_unlock (&req->r_fill_mutex);up_read (&mdsc->snap_rwsem);
Greg Kroah-Hartman
maintainerPATCH 180/235 libceph: Amend checking to fix `make W=1` build breakage
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andy Shevchenko <[email protected]>
[ Upstream commit 04d8712b079327409b09dee628378f9583e2e035 ]
In a few cases the code compares 32-bit value to a SIZE_MAX derived constant which is much higher than that value on 64-bit platforms, Clang, in particular, is not happy about this
net/ceph/osdmap.c:1441:10: error: result of comparison of constant 4611686018427387891 with expression of type 'u32' (aka 'unsigned int') is always false [-Werror,-Wtautological-constant-out-of-range-compare]
1441 | if (len > (SIZE_MAX - sizeof(*pg)) / sizeof(u32))
| ~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
net/ceph/osdmap.c:1624:10: error: result of comparison of constant 2305843009213693945 with expression of type 'u32' (aka 'unsigned int') is always false [-Werror,-Wtautological-constant-out-of-range-compare]
1624 | if (len > (SIZE_MAX - sizeof(*pg)) / (2 * sizeof(u32)))
| ~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Fix this by casting to size_t. Note, that possible replacement of SIZE_MAX by U32_MAX may lead to the behaviour changes on the corner cases.
Stable-dep-of: 9f00f9cf2be2 ("libceph: bound pg_{temp,upmap,upmap_items} length to CEPH_PG_MAX_SIZE")
--- net/ceph/osdmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/ceph/osdmap.c +2 -2
@@ -1424,7 +1424,7 @@ static struct ceph_pg_mapping *__decode_ceph_decode_32_safe (p, end, len, e_inval);if (len ==0 && incremental)return NULL ;/* new_pg_temp: [] to remove */ -if (len > (SIZE_MAX -sizeof (*pg)) /sizeof (u32)) +if ((size_t )len > (SIZE_MAX -sizeof (*pg)) /sizeof (u32))return ERR_PTR (-EINVAL );ceph_decode_need (p, end, len *sizeof (u32), e_inval);
@@ -1607,7 +1607,7 @@ static struct ceph_pg_mapping *__decode_u32 len, i;ceph_decode_32_safe (p, end, len, e_inval); -if (len > (SIZE_MAX -sizeof (*pg)) / (2 *sizeof (u32))) +if ((size_t )len > (SIZE_MAX -sizeof (*pg)) / (2 *sizeof (u32)))return ERR_PTR (-EINVAL );ceph_decode_need (p, end,2 * len *sizeof (u32), e_inval);
Greg Kroah-Hartman
maintainerPATCH 181/235 libceph: bound pg_{temp,upmap,upmap_items} length to CEPH_PG_MAX_SIZE
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei <[email protected]>
[ Upstream commit 9f00f9cf2be293efe899db67dc5272e3a9c62717 ]
__decode_pg_temp() decodes an user-controlled length but only rejects values large enough to overflow the allocation; it does not bound it to CEPH_PG_MAX_SIZE. The helper backs both pg_temp and pg_upmap decoding, and apply_upmap()/get_temp_osds() later copy the decoded list into the fixed-size on-stack array struct ceph_osds.osds[CEPH_PG_MAX_SIZE]. A monitor that sends an OSDMap with a pg_temp/pg_upmap entry longer than 32 thus causes a stack out-of-bounds write.
An OSD set for a single PG can never exceed CEPH_PG_MAX_SIZE, so reject longer entries at decode time. The bound is well below the old overflow threshold, so it also covers the allocation-size overflow the previous check guarded against.
BUG: KASAN: stack-out-of-bounds in ceph_pg_to_up_acting_osds Write of size 4 ... by task exploit kasan_report (mm/kasan/report.c:595) ceph_pg_to_up_acting_osds (net/ceph/osdmap.c:2617 net/ceph/osdmap.c:2833) calc_target (net/ceph/osd_client.c:1638) __submit_request (net/ceph/osd_client.c:2394) ceph_osdc_start_request (net/ceph/osd_client.c:2490) ceph_osdc_call (net/ceph/osd_client.c:5164) rbd_dev_image_probe (drivers/block/rbd.c:6899) do_rbd_add (drivers/block/rbd.c:7138) ... kernel BUG at net/ceph/osdmap.c:2670!
[ idryomov: do the same in __decode_pg_upmap_items() ]
--- net/ceph/osdmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/ceph/osdmap.c +2 -2
@@ -1424,7 +1424,7 @@ static struct ceph_pg_mapping *__decode_ceph_decode_32_safe (p, end, len, e_inval);if (len ==0 && incremental)return NULL ;/* new_pg_temp: [] to remove */ -if ((size_t )len > (SIZE_MAX -sizeof (*pg)) /sizeof (u32)) +if (len >CEPH_PG_MAX_SIZE )return ERR_PTR (-EINVAL );ceph_decode_need (p, end, len *sizeof (u32), e_inval);
@@ -1607,7 +1607,7 @@ static struct ceph_pg_mapping *__decode_u32 len, i;ceph_decode_32_safe (p, end, len, e_inval); -if ((size_t )len > (SIZE_MAX -sizeof (*pg)) / (2 *sizeof (u32))) +if (len >CEPH_PG_MAX_SIZE )return ERR_PTR (-EINVAL );ceph_decode_need (p, end,2 * len *sizeof (u32), e_inval);
Greg Kroah-Hartman
maintainerPATCH 182/235 libceph: fix two unsafe bare decodes in decode_lockers()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pavitra Jha <[email protected]>
[ Upstream commit a109a556115271ca7896dcda7b4b7e45e156c227 ]
decode_lockers() in cls_lock_client.c contains two bare decode operations that allow a malicious or compromised OSD to trigger slab-out-of-bounds reads:
1. ceph_decode_32(p) at the num_lockers field has no preceding bounds check. ceph_start_decoding() accepts struct_len=0 as valid -- the internal ceph_decode_need(p, end, 0, bad) always passes -- so when an OSD sends struct_len=0, ceph_start_decoding() returns success with p == end. The immediately following bare ceph_decode_32(p) then reads 4 bytes past the validated buffer boundary. The garbage value is passed directly to kzalloc_objs() as the locker count.
The sibling function decode_watchers() in osd_client.c already uses ceph_decode_32_safe() after its own ceph_start_decoding() call. decode_lockers() was the only site using the bare variant.
2. ceph_decode_8(p) after the decode_locker() loop has no preceding bounds check. If an OSD crafts num_lockers such that the loop advances p exactly to end, the subsequent bare ceph_decode_8(p) reads one byte past the validated buffer boundary. The result is passed directly into *type, which is used as a lock type discriminator by callers, giving an OSD-controlled one-byte OOB read with direct influence over the lock type field.
Fix both by replacing bare operations with their safe variants:
ceph_decode_32(p) -> ceph_decode_32_safe(p, end, *num_lockers,
err_inval)
ceph_decode_8(p) -> ceph_decode_8_safe(p, end, *type,
err_free_lockers)
The goto targets differ intentionally:
err_inval: is a new label returning -EINVAL directly. It is used for the pre-allocation failure path where *lockers is not yet allocated and must not be passed to ceph_free_lockers().
err_free_lockers: is the existing label. It is used for the post-allocation failure path where *lockers is allocated and must be freed.
ret is set to -EINVAL before ceph_decode_8_safe() so that err_free_lockers returns the correct error code on bounds violation. Without this, err_free_lockers would return a stale ret value (0 from the successful decode_locker() loop), silently swallowing the error.
-EINVAL is correct for both failure paths. The data received from the OSD is structurally malformed. -ENOMEM would misrepresent the failure class to callers and to stable@ backporters triaging error paths.
Attacker model: a malicious or compromised OSD in a multi-tenant Ceph deployment can trigger this against any kernel client that issues the lock.get_info class method (e.g. during RBD exclusive lock acquisition).
[ idryomov: trim changelog, formatting ]
--- net/ceph/cls_lock_client.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
net/ceph/cls_lock_client.c +6 -2
@@ -298,7 +298,7 @@ static int decode_lockers(void **p, voidif (ret)return ret; - *num_lockers =ceph_decode_32 (p); +ceph_decode_32_safe (p, end, *num_lockers, err_inval); *lockers =kcalloc (*num_lockers,sizeof (**lockers),GFP_NOIO );if (!*lockers)return -ENOMEM ;
@@ -309,7 +309,8 @@ static int decode_lockers(void **p, void goto err_free_lockers; } - *type =ceph_decode_8 (p); + ret = -EINVAL ; +ceph_decode_8_safe (p, end, *type, err_free_lockers); s =ceph_extract_encoded_string (p, end,NULL ,GFP_NOIO );if (IS_ERR (s)) { ret =PTR_ERR (s);
@@ -319,6 +320,9 @@ static int decode_lockers(void **p, void *tag = s;return 0 ; +err_inval: +return -EINVAL ; + err_free_lockers:ceph_free_lockers (*lockers, *num_lockers);return ret;
Greg Kroah-Hartman
maintainerPATCH 183/235 libceph: add doutc and *_client debug macros support
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiubo Li <[email protected]>
[ Upstream commit 5c5f0d2b5f92c47baf82b9b211e27edd7d195158 ]
This will help print the fsid and client's global_id in debug logs, and also print the function names.
[ idryomov: %lld -> %llu, leading space for doutc(), don't include
__func__ in pr_*() variants ]
Stable-dep-of: 50958bb928ba ("ceph: fix hanging __ceph_get_caps() with stale mds_wanted")
--- include/linux/ceph/ceph_debug.h | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
include/linux/ceph/ceph_debug.h +38 -0
@@ -19,12 +19,25 @@pr_debug ("%.*s %12.12s:%-4d : " fmt, \8 - (int )sizeof (KBUILD_MODNAME )," " , \ kbasename(__FILE__), __LINE__, ##__VA_ARGS__) +# define doutc (client, fmt, ...) \ + pr_debug("%.*s %12.12s:%-4d : [%pU %llu] " fmt, \ + 8 - (int)sizeof(KBUILD_MODNAME), " ", \ + kbasename(__FILE__), __LINE__, \ + &client->fsid, client->monc.auth->global_id, \ + ##__VA_ARGS__)# else /* faux printk call just to see any compiler warnings. */ # define dout (fmt, ...) do { \ if (0) \ printk(KERN_DEBUG fmt, ##__VA_ARGS__); \ } while (0) +# define doutc (client, fmt, ...) do { \ + if (0) \ + printk(KERN_DEBUG "[%pU %llu] " fmt, \ + &client->fsid, \ + client->monc.auth->global_id, \ + ##__VA_ARGS__); \ + } while (0)# endif #else
@@ -33,7 +46,32 @@* or, just wrap pr_debug */ # define dout (fmt, ...) pr_debug(" " fmt, ##__VA_ARGS__) +# define doutc (client, fmt, ...) \ + pr_debug(" [%pU %llu] %s: " fmt, &client->fsid, \ + client->monc.auth->global_id, __func__, ##__VA_ARGS__)#endif +#define pr_notice_client (client, fmt, ...) \ + pr_notice("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#define pr_info_client (client, fmt, ...) \ + pr_info("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#define pr_warn_client (client, fmt, ...) \ + pr_warn("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#define pr_warn_once_client (client, fmt, ...) \ + pr_warn_once("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#define pr_err_client (client, fmt, ...) \ + pr_err("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#define pr_warn_ratelimited_client (client, fmt, ...) \ + pr_warn_ratelimited("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#define pr_err_ratelimited_client (client, fmt, ...) \ + pr_err_ratelimited("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#endif
Greg Kroah-Hartman
maintainerPATCH 184/235 ceph: rename _to_client() to _to_fs_client()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiubo Li <[email protected]>
[ Upstream commit 5995d90d2d19f337df6a50bcf4699ef053214dac ]
We need to covert the inode to ceph_client in the following commit, and will add one new helper for that, here we rename the old helper to _fs_client().
Stable-dep-of: 50958bb928ba ("ceph: fix hanging __ceph_get_caps() with stale mds_wanted")
--- fs/ceph/addr.c | 24 ++++++++++++------------ fs/ceph/cache.c | 2 +- fs/ceph/caps.c | 36 ++++++++++++++++++------------------ fs/ceph/dir.c | 20 ++++++++++---------- fs/ceph/export.c | 10 +++++----- fs/ceph/file.c | 24 ++++++++++++------------ fs/ceph/inode.c | 20 ++++++++++---------- fs/ceph/ioctl.c | 8 ++++---- fs/ceph/super.c | 22 +++++++++++----------- fs/ceph/super.h | 8 ++++---- fs/ceph/xattr.c | 8 ++++---- 11 files changed, 91 insertions(+), 91 deletions(-)
fs/ceph/addr.c +12 -12
@@ -178,7 +178,7 @@ static int ceph_do_readpage(struct file {struct inode *inode =file_inode (filp);struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_osd_client *osdc = &fsc->client->osdc;struct ceph_osd_request *req;struct ceph_vino vino =ceph_vino (inode);
@@ -266,7 +266,7 @@ static int ceph_readpage(struct file *fistatic void finish_read (struct ceph_osd_request *req) {struct inode *inode = req->r_inode; -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_osd_data *osd_data;int rc = req->r_result <=0 ? req->r_result :0 ;int bytes = req->r_result >=0 ? req->r_result :0 ;
@@ -275,7 +275,7 @@ static void finish_read(struct ceph_osd_dout ("finish_read %p req %p rc %d bytes %d\n" , inode, req, rc, bytes);if (rc == -EBLOCKLISTED ) -ceph_inode_to_client (inode)->blocklisted = true; +ceph_inode_to_fs_client (inode)->blocklisted = true;/* unlock all pages, zeroing any data we didn't read */ osd_data =osd_req_op_extent_osd_data (req,0 );
@@ -319,7 +319,7 @@ static int start_read(struct inode *inod struct list_head *page_list, int max) {struct ceph_osd_client *osdc = - &ceph_inode_to_client (inode)->client->osdc; + &ceph_inode_to_fs_client (inode)->client->osdc;struct ceph_inode_info *ci =ceph_inode (inode);struct page *page =lru_to_page (page_list);struct ceph_vino vino;
@@ -453,7 +453,7 @@ static int ceph_readpages(struct file *f struct list_head *page_list, unsigned nr_pages) {struct inode *inode =file_inode (file); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_file_info *fi = file->private_data;struct ceph_rw_context *rw_ctx;int rc =0 ;
@@ -591,7 +591,7 @@ static int writepage_nounlock(struct pag {struct inode *inode = page->mapping->host;struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_snap_context *snapc, *oldest;loff_t page_off =page_offset (page);int err;
@@ -733,7 +733,7 @@ static void writepages_finish(struct cepint rc = req->r_result;struct ceph_snap_context *snapc = req->r_snapc;struct address_space *mapping = inode->i_mapping; -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);bool remove_page;dout ("writepages_finish %p rc %d\n" , inode, rc);
@@ -815,7 +815,7 @@ static int ceph_writepages_start(struct {struct inode *inode = mapping->host;struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_vino vino =ceph_vino (inode);pgoff_t index, start_index, end =-1 ;struct ceph_snap_context *snapc =NULL , *last_snapc =NULL , *pgsnapc;
@@ -1252,7 +1252,7 @@ static struct ceph_snap_context * ceph_find_incompatible(struct page *page) {struct inode *inode = page->mapping->host; -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_inode_info *ci =ceph_inode (inode);if (READ_ONCE (fsc->mount_state) ==CEPH_MOUNT_SHUTDOWN ) {
@@ -1739,7 +1739,7 @@ int ceph_uninline_data(struct file *filp {struct inode *inode =file_inode (filp);struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_osd_request *req;struct page *page =NULL ;u64 len, inline_version;
@@ -1896,7 +1896,7 @@ enum {static int __ceph_pool_perm_get (struct ceph_inode_info *ci,s64 pool,struct ceph_string *pool_ns) { -struct ceph_fs_client *fsc =ceph_inode_to_client (&ci->vfs_inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (&ci->vfs_inode);struct ceph_mds_client *mdsc = fsc->mdsc;struct ceph_osd_request *rd_req =NULL , *wr_req =NULL ;struct rb_node **p, *parent;
@@ -2085,7 +2085,7 @@ int ceph_pool_perm_check(struct inode *ireturn 0 ; } -if (ceph_test_mount_opt (ceph_inode_to_client (inode), +if (ceph_test_mount_opt (ceph_inode_to_fs_client (inode),NOPOOLPERM ))return 0 ;
fs/ceph/cache.c +1 -1
@@ -138,7 +138,7 @@ static const struct fscache_cookie_def cvoid ceph_fscache_register_inode_cookie (struct inode *inode) {struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_aux_inode aux;/* No caching for filesystem */
fs/ceph/caps.c +18 -18
@@ -633,7 +633,7 @@ void ceph_add_cap(struct inode *inode, unsigned seq, unsigned mseq, u64 realmino, int flags, struct ceph_cap **new_cap) { - struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; + struct ceph_mds_client *mdsc = ceph_inode_to_fs_client(inode)->mdsc;struct ceph_inode_info *ci =ceph_inode (inode);struct ceph_cap *cap;int mds = session->s_mds;
@@ -940,7 +940,7 @@ int __ceph_caps_issued_mask(struct ceph_int __ceph_caps_issued_mask_metric (struct ceph_inode_info *ci,int mask,int touch) { -struct ceph_fs_client *fsc =ceph_sb_to_client (ci->vfs_inode.i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (ci->vfs_inode.i_sb);int r; r =__ceph_caps_issued_mask (ci, mask, touch);
@@ -1014,7 +1014,7 @@ int __ceph_caps_file_wanted(struct ceph_const int WR_SHIFT =ffs (CEPH_FILE_MODE_WR );const int LAZY_SHIFT =ffs (CEPH_FILE_MODE_LAZY );struct ceph_mount_options *opt = -ceph_inode_to_client (&ci->vfs_inode)->mount_options; +ceph_inode_to_fs_client (&ci->vfs_inode)->mount_options;unsigned long used_cutoff = jiffies - opt->caps_wanted_delay_max *HZ ;unsigned long idle_cutoff = jiffies - opt->caps_wanted_delay_min *HZ ;
@@ -1126,7 +1126,7 @@ static void drop_inode_snap_realm(structif (realm->ino == ci->i_vino.ino) realm->inode =NULL ;spin_unlock (&realm->inodes_with_caps_lock); -ceph_put_snap_realm (ceph_sb_to_client (ci->vfs_inode.i_sb)->mdsc, +ceph_put_snap_realm (ceph_sb_to_fs_client (ci->vfs_inode.i_sb)->mdsc, realm); }
@@ -1151,7 +1151,7 @@ void __ceph_remove_cap(struct ceph_cap *dout ("__ceph_remove_cap %p from %p\n" , cap, &ci->vfs_inode); - mdsc =ceph_inode_to_client (&ci->vfs_inode)->mdsc; + mdsc =ceph_inode_to_fs_client (&ci->vfs_inode)->mdsc;/* remove from inode's cap rbtree, and clear auth cap */ rb_erase (&cap->ci_node, &ci->i_caps);
@@ -1635,7 +1635,7 @@ void ceph_flush_snaps(struct ceph_inode_ struct ceph_mds_session **psession) {struct inode *inode = &ci->vfs_inode; -struct ceph_mds_client *mdsc =ceph_inode_to_client (inode)->mdsc; +struct ceph_mds_client *mdsc =ceph_inode_to_fs_client (inode)->mdsc;struct ceph_mds_session *session =NULL ;bool need_put = false;int mds;
@@ -1707,7 +1707,7 @@ int __ceph_mark_dirty_caps(struct ceph_i struct ceph_cap_flush **pcf) {struct ceph_mds_client *mdsc = -ceph_sb_to_client (ci->vfs_inode.i_sb)->mdsc; +ceph_sb_to_fs_client (ci->vfs_inode.i_sb)->mdsc;struct inode *inode = &ci->vfs_inode;int was = ci->i_dirty_caps;int dirty =0 ;
@@ -1830,7 +1830,7 @@ static u64 __mark_caps_flushing(struct i struct ceph_mds_session *session, bool wake, u64 *oldest_flush_tid) { - struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; + struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc;struct ceph_inode_info *ci =ceph_inode (inode);struct ceph_cap_flush *cf =NULL ;int flushing;
@@ -2211,7 +2211,7 @@ ack:*/ static int try_flush_caps (struct inode *inode,u64 *ptid) { -struct ceph_mds_client *mdsc =ceph_sb_to_client (inode->i_sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc;struct ceph_inode_info *ci =ceph_inode (inode);struct ceph_mds_session *session =NULL ;int flushing =0 ;
@@ -2301,7 +2301,7 @@ static int caps_are_flushed(struct inode*/ static int unsafe_request_wait (struct inode *inode) { -struct ceph_mds_client *mdsc =ceph_sb_to_client (inode->i_sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc;struct ceph_inode_info *ci =ceph_inode (inode);struct ceph_mds_request *req1 =NULL , *req2 =NULL ;int ret, err =0 ;
@@ -2480,7 +2480,7 @@ int ceph_write_inode(struct inode *inode caps_are_flushed(inode, flush_tid)); }else {struct ceph_mds_client *mdsc = -ceph_sb_to_client (inode->i_sb)->mdsc; +ceph_sb_to_fs_client (inode->i_sb)->mdsc;spin_lock (&ci->i_ceph_lock);if (__ceph_caps_dirty (ci))
@@ -2729,7 +2729,7 @@ static int try_get_cap_refs(struct inode loff_t endoff, int flags, int *got) { struct ceph_inode_info *ci = ceph_inode(inode); -struct ceph_mds_client *mdsc =ceph_inode_to_client (inode)->mdsc; +struct ceph_mds_client *mdsc =ceph_inode_to_fs_client (inode)->mdsc;int ret =0 ;int have, implemented;bool snap_rwsem_locked = false;
@@ -2949,7 +2949,7 @@ int ceph_get_caps(struct file *filp, intstruct ceph_file_info *fi = filp->private_data;struct inode *inode =file_inode (filp);struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);int ret, _got, flags; ret =ceph_pool_perm_check (inode, need);
@@ -3651,7 +3651,7 @@ static void handle_cap_flush_ack(struct __releases(ci->i_ceph_lock) {struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_mds_client *mdsc =ceph_sb_to_client (inode->i_sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc;struct ceph_cap_flush *cf, *tmp_cf;LIST_HEAD (to_remove);unsigned seq =le32_to_cpu (m->seq);
@@ -3757,7 +3757,7 @@ void __ceph_remove_capsnap(struct inode bool *wake_ci, bool *wake_mdsc) { struct ceph_inode_info *ci = ceph_inode(inode); -struct ceph_mds_client *mdsc =ceph_sb_to_client (inode->i_sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc;bool ret;lockdep_assert_held (&ci->i_ceph_lock);
@@ -3801,7 +3801,7 @@ static void handle_cap_flushsnap_ack(str struct ceph_mds_session *session) {struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_mds_client *mdsc =ceph_sb_to_client (inode->i_sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc;u64 follows =le64_to_cpu (m->snap_follows);struct ceph_cap_snap *capsnap;bool flushed = false;
@@ -3885,7 +3885,7 @@ static void handle_cap_export(struct inostruct ceph_mds_cap_peer *ph, struct ceph_mds_session *session) { - struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; + struct ceph_mds_client *mdsc = ceph_inode_to_fs_client(inode)->mdsc;struct ceph_mds_session *tsession =NULL ;struct ceph_cap *cap, *tcap, *new_cap =NULL ;struct ceph_inode_info *ci =ceph_inode (inode);
@@ -4504,7 +4504,7 @@ int ceph_drop_caps_for_unlink(struct inoif (__ceph_caps_dirty (ci)) {struct ceph_mds_client *mdsc = -ceph_inode_to_client (inode)->mdsc; +ceph_inode_to_fs_client (inode)->mdsc;__cap_delay_requeue_front (mdsc, ci); } }
fs/ceph/dir.c +10 -10
@@ -307,7 +307,7 @@ static int ceph_readdir(struct file *filstruct ceph_dir_file_info *dfi = file->private_data;struct inode *inode =file_inode (file);struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_mds_client *mdsc = fsc->mdsc;int i;int err;
@@ -677,7 +677,7 @@ out:int ceph_handle_snapdir (struct ceph_mds_request *req,struct dentry *dentry,int err) { -struct ceph_fs_client *fsc =ceph_sb_to_client (dentry->d_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (dentry->d_sb);struct inode *parent =d_inode (dentry->d_parent);/* we hold i_mutex */ /* .snap dir? */
@@ -746,7 +746,7 @@ static bool is_root_ceph_dentry(struct istatic struct dentry *ceph_lookup (struct inode *dir,struct dentry *dentry,unsigned int flags) { -struct ceph_fs_client *fsc =ceph_sb_to_client (dir->i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (dir->i_sb);struct ceph_mds_client *mdsc =ceph_sb_to_mdsc (dir->i_sb);struct ceph_mds_request *req;int op;
@@ -1128,7 +1128,7 @@ static int get_caps_for_async_unlink(str*/ static int ceph_unlink (struct inode *dir,struct dentry *dentry) { -struct ceph_fs_client *fsc =ceph_sb_to_client (dir->i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (dir->i_sb);struct ceph_mds_client *mdsc = fsc->mdsc;struct inode *inode =d_inode (dentry);struct ceph_inode_info *ci =ceph_inode (inode);
@@ -1288,7 +1288,7 @@ void __ceph_dentry_lease_touch(struct cereturn ; } - mdsc =ceph_sb_to_client (dn->d_sb)->mdsc; + mdsc =ceph_sb_to_fs_client (dn->d_sb)->mdsc;spin_lock (&mdsc->dentry_list_lock);list_move_tail (&di->lease_list, &mdsc->dentry_leases);spin_unlock (&mdsc->dentry_list_lock);
@@ -1335,7 +1335,7 @@ void __ceph_dentry_dir_lease_touch(strucreturn ; } - mdsc =ceph_sb_to_client (dn->d_sb)->mdsc; + mdsc =ceph_sb_to_fs_client (dn->d_sb)->mdsc;spin_lock (&mdsc->dentry_list_lock);__dentry_dir_lease_touch (mdsc, di),spin_unlock (&mdsc->dentry_list_lock);
@@ -1349,7 +1349,7 @@ static void __dentry_lease_unlist(structif (list_empty (&di->lease_list))return ; - mdsc =ceph_sb_to_client (di->dentry->d_sb)->mdsc; + mdsc =ceph_sb_to_fs_client (di->dentry->d_sb)->mdsc;spin_lock (&mdsc->dentry_list_lock);list_del_init (&di->lease_list);spin_unlock (&mdsc->dentry_list_lock);
@@ -1704,7 +1704,7 @@ static int ceph_d_revalidate(struct dentdout ("d_revalidate %p '%pd' inode %p offset 0x%llx\n" , dentry, dentry, inode,ceph_dentry (dentry)->offset); - mdsc =ceph_sb_to_client (dir->i_sb)->mdsc; + mdsc =ceph_sb_to_fs_client (dir->i_sb)->mdsc;/* always trust cached snapped dentries, snapdir dentry */ if (ceph_snap (dir) !=CEPH_NOSNAP ) {
@@ -1810,7 +1810,7 @@ static int ceph_d_delete(const struct destatic void ceph_d_release (struct dentry *dentry) {struct ceph_dentry_info *di =ceph_dentry (dentry); -struct ceph_fs_client *fsc =ceph_sb_to_client (dentry->d_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (dentry->d_sb);dout ("d_release %p\n" , dentry);
@@ -1879,7 +1879,7 @@ static ssize_t ceph_read_dir(struct fileint left;const int bufsize =1024 ; -if (!ceph_test_mount_opt (ceph_sb_to_client (inode->i_sb),DIRSTAT )) +if (!ceph_test_mount_opt (ceph_sb_to_fs_client (inode->i_sb),DIRSTAT ))return -EISDIR ;if (!dfi->dir_info) {
fs/ceph/export.c +5 -5
@@ -122,7 +122,7 @@ static int ceph_encode_fh(struct inode *static struct inode *__lookup_inode (struct super_block *sb,u64 ino) { -struct ceph_mds_client *mdsc =ceph_sb_to_client (sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (sb)->mdsc;struct inode *inode;struct ceph_vino vino;int err;
@@ -198,7 +198,7 @@ static struct dentry *__snapfh_to_dentry struct ceph_nfs_snapfh *sfh, bool want_parent) { -struct ceph_mds_client *mdsc =ceph_sb_to_client (sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (sb)->mdsc;struct ceph_mds_request *req;struct inode *inode;struct ceph_vino vino;
@@ -304,7 +304,7 @@ static struct dentry *ceph_fh_to_dentry(static struct dentry *__get_parent (struct super_block *sb,struct dentry *child,u64 ino) { -struct ceph_mds_client *mdsc =ceph_sb_to_client (sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (sb)->mdsc;struct ceph_mds_request *req;struct inode *inode;int mask;
@@ -426,7 +426,7 @@ static int __get_snap_name(struct dentry {struct inode *inode =d_inode (child);struct inode *dir =d_inode (parent); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_mds_request *req =NULL ;char *last_name =NULL ;unsigned next_offset =2 ;
@@ -529,7 +529,7 @@ static int ceph_get_name(struct dentry *if (ceph_snap (inode) !=CEPH_NOSNAP )return __get_snap_name (parent, name, child); - mdsc =ceph_inode_to_client (inode)->mdsc; + mdsc =ceph_inode_to_fs_client (inode)->mdsc; req =ceph_mdsc_create_request (mdsc,CEPH_MDS_OP_LOOKUPNAME ,USE_ANY_MDS );if (IS_ERR (req))
fs/ceph/file.c +12 -12
@@ -233,7 +233,7 @@ static int ceph_init_file_info(struct inspin_lock_init (&fi->rw_contexts_lock);INIT_LIST_HEAD (&fi->rw_contexts); - fi->filp_gen =READ_ONCE (ceph_inode_to_client (inode)->filp_gen); + fi->filp_gen =READ_ONCE (ceph_inode_to_fs_client (inode)->filp_gen);return 0 ; }
@@ -338,7 +338,7 @@ out:int ceph_open (struct inode *inode,struct file *file) {struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_sb_to_client (inode->i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (inode->i_sb);struct ceph_mds_client *mdsc = fsc->mdsc;struct ceph_mds_request *req;struct ceph_file_info *fi = file->private_data;
@@ -687,7 +687,7 @@ static int ceph_finish_async_create(struint ceph_atomic_open (struct inode *dir,struct dentry *dentry,struct file *file,unsigned flags,umode_t mode) { -struct ceph_fs_client *fsc =ceph_sb_to_client (dir->i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (dir->i_sb);struct ceph_mds_client *mdsc = fsc->mdsc;struct ceph_mds_request *req;struct dentry *dn;
@@ -869,7 +869,7 @@ static ssize_t ceph_sync_read(struct kiostruct file *file = iocb->ki_filp;struct inode *inode =file_inode (file);struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_osd_client *osdc = &fsc->client->osdc;ssize_t ret;u64 off = iocb->ki_pos;
@@ -1089,7 +1089,7 @@ static void ceph_aio_complete_req(structif (aio_work) {INIT_WORK (&aio_work->work, ceph_aio_retry_work); aio_work->req = req; -queue_work (ceph_inode_to_client (inode)->inode_wq, +queue_work (ceph_inode_to_fs_client (inode)->inode_wq, &aio_work->work);return ; }
@@ -1208,7 +1208,7 @@ ceph_direct_read_write(struct kiocb *iocstruct file *file = iocb->ki_filp;struct inode *inode =file_inode (file);struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_client_metric *metric = &fsc->mdsc->metric;struct ceph_vino vino;struct ceph_osd_request *req;
@@ -1418,7 +1418,7 @@ ceph_sync_write(struct kiocb *iocb, strustruct file *file = iocb->ki_filp;struct inode *inode =file_inode (file);struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_vino vino;struct ceph_osd_request *req;struct page **pages;
@@ -1703,7 +1703,7 @@ static ssize_t ceph_write_iter(struct kistruct ceph_file_info *fi = file->private_data;struct inode *inode =file_inode (file);struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_osd_client *osdc = &fsc->client->osdc;struct ceph_cap_flush *prealloc_cf;ssize_t count, written =0 ;
@@ -1897,7 +1897,7 @@ out_unlocked:static loff_t ceph_llseek (struct file *file,loff_t offset,int whence) {struct inode *inode = file->f_mapping->host; -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);loff_t i_size;loff_t ret;
@@ -1990,7 +1990,7 @@ static int ceph_zero_partial_object(stru loff_t offset, loff_t *length) { struct ceph_inode_info *ci = ceph_inode(inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);struct ceph_osd_request *req;struct ceph_snap_context *snapc;int ret =0 ;
@@ -2324,7 +2324,7 @@ static ssize_t __ceph_copy_file_range(ststruct ceph_inode_info *src_ci =ceph_inode (src_inode);struct ceph_inode_info *dst_ci =ceph_inode (dst_inode);struct ceph_cap_flush *prealloc_cf; -struct ceph_fs_client *src_fsc =ceph_inode_to_client (src_inode); +struct ceph_fs_client *src_fsc =ceph_inode_to_fs_client (src_inode);loff_t size;ssize_t ret = -EIO , bytes;u64 src_objnum, dst_objnum, src_objoff, dst_objoff;
@@ -2332,7 +2332,7 @@ static ssize_t __ceph_copy_file_range(stint src_got =0 , dst_got =0 , err, dirty;if (src_inode->i_sb != dst_inode->i_sb) { -struct ceph_fs_client *dst_fsc =ceph_inode_to_client (dst_inode); +struct ceph_fs_client *dst_fsc =ceph_inode_to_fs_client (dst_inode);if (ceph_fsid_compare (&src_fsc->client->fsid, &dst_fsc->client->fsid)) {
fs/ceph/inode.c +10 -10
@@ -1240,7 +1240,7 @@ int ceph_fill_trace(struct super_block *struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info;struct inode *in =NULL ;struct ceph_vino tvino, dvino; -struct ceph_fs_client *fsc =ceph_sb_to_client (sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (sb);int err =0 ; dout("fill_trace %p is_dentry %d is_target %d\n", req,
@@ -1815,7 +1815,7 @@ void ceph_async_iput(struct inode *inodefor (;;) {if (atomic_add_unless (&inode->i_count,-1 ,1 ))break ; -if (queue_work (ceph_inode_to_client (inode)->inode_wq, +if (queue_work (ceph_inode_to_fs_client (inode)->inode_wq, &ceph_inode (inode)->i_work))break ;/* queue work failed, i_count must be at least 2 */
@@ -1832,7 +1832,7 @@ void ceph_queue_writeback(struct inode *set_bit (CEPH_I_WORK_WRITEBACK , &ci->i_work_mask);ihold (inode); -if (queue_work (ceph_inode_to_client (inode)->inode_wq, +if (queue_work (ceph_inode_to_fs_client (inode)->inode_wq, &ci->i_work)) {dout ("ceph_queue_writeback %p\n" , inode); }else {
@@ -1851,7 +1851,7 @@ void ceph_queue_invalidate(struct inodeset_bit (CEPH_I_WORK_INVALIDATE_PAGES , &ci->i_work_mask);ihold (inode); -if (queue_work (ceph_inode_to_client (inode)->inode_wq, +if (queue_work (ceph_inode_to_fs_client (inode)->inode_wq, &ceph_inode (inode)->i_work)) {dout ("ceph_queue_invalidate %p\n" , inode); }else {
@@ -1871,7 +1871,7 @@ void ceph_queue_vmtruncate(struct inodeset_bit (CEPH_I_WORK_VMTRUNCATE , &ci->i_work_mask);ihold (inode); -if (queue_work (ceph_inode_to_client (inode)->inode_wq, +if (queue_work (ceph_inode_to_fs_client (inode)->inode_wq, &ci->i_work)) {dout ("ceph_queue_vmtruncate %p\n" , inode); }else {
@@ -1884,7 +1884,7 @@ void ceph_queue_vmtruncate(struct inodestatic void ceph_do_invalidate_pages (struct inode *inode) {struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);u32 orig_gen;int check =0 ;
@@ -2033,7 +2033,7 @@ int __ceph_setattr(struct inode *inode,struct ceph_inode_info *ci =ceph_inode (inode);unsigned int ia_valid = attr->ia_valid;struct ceph_mds_request *req; -struct ceph_mds_client *mdsc =ceph_sb_to_client (inode->i_sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc;struct ceph_cap_flush *prealloc_cf;int issued;int release =0 , dirtied =0 ;
@@ -2248,7 +2248,7 @@ int __ceph_setattr(struct inode *inode,int ceph_setattr (struct dentry *dentry,struct iattr *attr) {struct inode *inode =d_inode (dentry); -struct ceph_fs_client *fsc =ceph_inode_to_client (inode); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (inode);int err;if (ceph_snap (inode) !=CEPH_NOSNAP )
@@ -2281,7 +2281,7 @@ int ceph_setattr(struct dentry *dentry,int __ceph_do_getattr (struct inode *inode,struct page *locked_page,int mask,bool force) { -struct ceph_fs_client *fsc =ceph_sb_to_client (inode->i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (inode->i_sb);struct ceph_mds_client *mdsc = fsc->mdsc;struct ceph_mds_request *req;int mode;
@@ -2401,7 +2401,7 @@ int ceph_getattr(const struct path *path stat->dev = ci->i_snapid_map ? ci->i_snapid_map->dev :0 ;if (S_ISDIR (inode->i_mode)) { -if (ceph_test_mount_opt (ceph_sb_to_client (inode->i_sb), +if (ceph_test_mount_opt (ceph_sb_to_fs_client (inode->i_sb),RBYTES )) stat->size = ci->i_rbytes;else
fs/ceph/ioctl.c +4 -4
@@ -64,7 +64,7 @@ static long __validate_layout(struct cepstatic long ceph_ioctl_set_layout (struct file *file,void __user *arg) {struct inode *inode =file_inode (file); -struct ceph_mds_client *mdsc =ceph_sb_to_client (inode->i_sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc;struct ceph_mds_request *req;struct ceph_ioctl_layout l;struct ceph_inode_info *ci =ceph_inode (file_inode (file));
@@ -139,7 +139,7 @@ static long ceph_ioctl_set_layout_policystruct ceph_mds_request *req;struct ceph_ioctl_layout l;int err; -struct ceph_mds_client *mdsc =ceph_sb_to_client (inode->i_sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc;/* copy and validate */ if (copy_from_user (&l, arg,sizeof (l)))
@@ -182,7 +182,7 @@ static long ceph_ioctl_get_dataloc(strucstruct inode *inode =file_inode (file);struct ceph_inode_info *ci =ceph_inode (inode);struct ceph_osd_client *osdc = - &ceph_sb_to_client (inode->i_sb)->client->osdc; + &ceph_sb_to_fs_client (inode->i_sb)->client->osdc;struct ceph_object_locator oloc;CEPH_DEFINE_OID_ONSTACK (oid);u32 xlen;
@@ -243,7 +243,7 @@ static long ceph_ioctl_lazyio(struct filstruct ceph_file_info *fi = file->private_data;struct inode *inode =file_inode (file);struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_mds_client *mdsc =ceph_inode_to_client (inode)->mdsc; +struct ceph_mds_client *mdsc =ceph_inode_to_fs_client (inode)->mdsc;if ((fi->fmode &CEPH_FILE_MODE_LAZY ) ==0 ) {spin_lock (&ci->i_ceph_lock);
fs/ceph/super.c +11 -11
@@ -41,7 +41,7 @@ static LIST_HEAD(ceph_fsc_list);*/ static void ceph_put_super (struct super_block *s) { -struct ceph_fs_client *fsc =ceph_sb_to_client (s); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (s);dout ("put_super\n" );ceph_mdsc_close_sessions (fsc->mdsc);
@@ -49,7 +49,7 @@ static void ceph_put_super(struct super_static int ceph_statfs (struct dentry *dentry,struct kstatfs *buf) { -struct ceph_fs_client *fsc =ceph_inode_to_client (d_inode (dentry)); +struct ceph_fs_client *fsc =ceph_inode_to_fs_client (d_inode (dentry));struct ceph_mon_client *monc = &fsc->client->monc;struct ceph_statfs st;int i, err;
@@ -112,7 +112,7 @@ static int ceph_statfs(struct dentry *destatic int ceph_sync_fs (struct super_block *sb,int wait) { -struct ceph_fs_client *fsc =ceph_sb_to_client (sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (sb);if (!wait) {dout ("sync_fs (non-blocking)\n" );
@@ -529,7 +529,7 @@ static int compare_mount_options(struct*/ static int ceph_show_options (struct seq_file *m,struct dentry *root) { -struct ceph_fs_client *fsc =ceph_sb_to_client (root->d_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (root->d_sb);struct ceph_mount_options *fsopt = fsc->mount_options;size_t pos;int ret;
@@ -841,7 +841,7 @@ static void destroy_caches(void)*/ static void ceph_umount_begin (struct super_block *sb) { -struct ceph_fs_client *fsc =ceph_sb_to_client (sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (sb);dout ("ceph_umount_begin - starting forced umount\n" );if (!fsc)
@@ -1001,7 +1001,7 @@ static int ceph_compare_super(struct supstruct ceph_fs_client *new = fc->s_fs_info;struct ceph_mount_options *fsopt = new->mount_options;struct ceph_options *opt = new->client->options; -struct ceph_fs_client *fsc =ceph_sb_to_client (sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (sb);dout ("ceph_compare_super %p\n" , sb);
@@ -1094,9 +1094,9 @@ static int ceph_get_tree(struct fs_conte goto out; } -if (ceph_sb_to_client (sb) != fsc) { +if (ceph_sb_to_fs_client (sb) != fsc) {destroy_fs_client (fsc); - fsc =ceph_sb_to_client (sb); + fsc =ceph_sb_to_fs_client (sb);dout ("get_sb got existing client %p\n" , fsc); }else {dout ("get_sb using new client %p\n" , fsc);
@@ -1147,7 +1147,7 @@ static int ceph_reconfigure_fc(struct fs {struct ceph_parse_opts_ctx *pctx = fc->fs_private;struct ceph_mount_options *fsopt = pctx->opts; -struct ceph_fs_client *fsc =ceph_sb_to_client (fc->root->d_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (fc->root->d_sb);if (fsopt->flags &CEPH_MOUNT_OPT_ASYNC_DIROPS )ceph_set_mount_opt (fsc,ASYNC_DIROPS );
@@ -1218,7 +1218,7 @@ nomem:static void ceph_kill_sb (struct super_block *s) { -struct ceph_fs_client *fsc =ceph_sb_to_client (s); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (s);dout ("kill_sb %p\n" , s);
@@ -1256,7 +1256,7 @@ MODULE_ALIAS_FS("ceph");int ceph_force_reconnect (struct super_block *sb) { -struct ceph_fs_client *fsc =ceph_sb_to_client (sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (sb);int err =0 ;ceph_umount_begin (sb);
fs/ceph/super.h +4 -4
@@ -440,13 +440,13 @@ ceph_inode(const struct inode *inode) }static inline struct ceph_fs_client * -ceph_inode_to_client (const struct inode *inode) +ceph_inode_to_fs_client (const struct inode *inode) {return (struct ceph_fs_client *)inode->i_sb->s_fs_info; }static inline struct ceph_fs_client * -ceph_sb_to_client (const struct super_block *sb) +ceph_sb_to_fs_client (const struct super_block *sb) {return (struct ceph_fs_client *)sb->s_fs_info; }
@@ -454,7 +454,7 @@ ceph_sb_to_client(const struct super_blostatic inline struct ceph_mds_client *ceph_sb_to_mdsc (const struct super_block *sb) { -return (struct ceph_mds_client *)ceph_sb_to_client (sb)->mdsc; +return (struct ceph_mds_client *)ceph_sb_to_fs_client (sb)->mdsc; } static inline struct ceph_vino
@@ -510,7 +510,7 @@ static inline u64 ceph_snap(struct inode*/ static inline u64 ceph_present_ino (struct super_block *sb,u64 ino) { -if (unlikely (ceph_test_mount_opt (ceph_sb_to_client (sb),INO32 ))) +if (unlikely (ceph_test_mount_opt (ceph_sb_to_fs_client (sb),INO32 )))return ceph_ino_to_ino32 (ino);return ino; }
fs/ceph/xattr.c +4 -4
@@ -56,7 +56,7 @@ static bool ceph_vxattrcb_layout_exists(static ssize_t ceph_vxattrcb_layout (struct ceph_inode_info *ci,char *val,size_t size) { -struct ceph_fs_client *fsc =ceph_sb_to_client (ci->vfs_inode.i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (ci->vfs_inode.i_sb);struct ceph_osd_client *osdc = &fsc->client->osdc;struct ceph_string *pool_ns;s64 pool = ci->i_layout.pool_id;
@@ -160,7 +160,7 @@ static ssize_t ceph_vxattrcb_layout_pool char *val, size_t size) { ssize_t ret; -struct ceph_fs_client *fsc =ceph_sb_to_client (ci->vfs_inode.i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (ci->vfs_inode.i_sb);struct ceph_osd_client *osdc = &fsc->client->osdc;s64 pool = ci->i_layout.pool_id;const char *pool_name;
@@ -954,7 +954,7 @@ out:static int ceph_sync_setxattr (struct inode *inode,const char *name,const char *value,size_t size,int flags) { -struct ceph_fs_client *fsc =ceph_sb_to_client (inode->i_sb); +struct ceph_fs_client *fsc =ceph_sb_to_fs_client (inode->i_sb);struct ceph_inode_info *ci =ceph_inode (inode);struct ceph_mds_request *req;struct ceph_mds_client *mdsc = fsc->mdsc;
@@ -1021,7 +1021,7 @@ int __ceph_setxattr(struct inode *inode, {struct ceph_vxattr *vxattr;struct ceph_inode_info *ci =ceph_inode (inode); -struct ceph_mds_client *mdsc =ceph_sb_to_client (inode->i_sb)->mdsc; +struct ceph_mds_client *mdsc =ceph_sb_to_fs_client (inode->i_sb)->mdsc;struct ceph_cap_flush *prealloc_cf =NULL ;struct ceph_buffer *old_blob =NULL ;int issued;
Greg Kroah-Hartman
maintainerPATCH 185/235 ceph: fix hanging __ceph_get_caps() with stale mds_wanted
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Max Kellermann <[email protected]>
[ Upstream commit 50958bb928bad3bdba9e5d1b7ff4bbadcf6951e6 ]
A reader can hang forever in __ceph_get_caps() when the client no longer holds FILE_RD, but local cap state still says that the capability is already wanted (via mds_wanted).
One way to trigger this is through MDS cap revocation. If another client performs a conflicting operation, the MDS can revoke FILE_RD from the reader; the next read then has to reacquire FILE_RD. If the cap update that should request FILE_RD never reaches the MDS after cap->mds_wanted was raised, the reader is left holding only non-file caps while local mds_wanted still includes the file read caps.
In that state, try_get_cap_refs() sees need <= mds_wanted and returns 0, so __ceph_get_caps() just waits on i_cap_wq. If the cap update that was supposed to request FILE_RD never reaches the MDS after cap->mds_wanted was` raised, no further request is sent and the waiter can sleep indefinitely until unrelated cap traffic happens to wake it up.
The ordering issue is that cap->mds_wanted is updated in __prep_cap() before the CEPH_MSG_CLIENT_CAPS message is actually queued for send. That makes one field serve two different meanings at
once: what this client wants, and what the client believes the MDS already knows it wants.
A proper fix would be to split those states and track whether a cap update is actually in flight or has been observed by the MDS. However, simply moving the cap->mds_wanted assignment later would not be sufficient: queueing the message in the messenger does not guarantee that the MDS processed that specific wanted set, and reconnect or message loss can still invalidate that assumption. Fixing that properly would require a larger rework of the cap state machine.
To allow simpler backports to stable kernels, this patch implements a simpler workaround:
- stop waiting forever in __ceph_get_caps(); after a bounded wait, fall back to the renew path
- make ceph_renew_caps() issue a synchronous OPEN request whenever the inode still does not actually hold the wanted caps, instead of only calling ceph_check_caps()
The extra issued-vs-wanted check in ceph_renew_caps() is necessary because the previous test only checked whether the inode still had any real caps at all. That is not enough after revocation: the client can still hold something like pLs and yet be missing FILE_RD completely. In that case, falling back to ceph_check_caps() is not sufficient, because it still trusts cap->mds_wanted and may resend nothing. By requiring (issued & wanted) == wanted before taking the asynchronous path, the code only uses ceph_check_caps() when the wanted caps are already actually issued. Otherwise, it sends the synchronous OPEN renew.
This preserves the existing asynchronous fast path when the wanted caps are already issued, avoids changing cap-state semantics, and fixes the hang by guaranteeing that a stalled waiter eventually retries through a path that does not rely on the stale mds_wanted state.
[ idryomov: move CEPH_GET_CAPS_WAIT_TIMEOUT from libceph.h to
mds_client.h, formatting ]
--- fs/ceph/caps.c | 17 +++++++++++++++-- fs/ceph/file.c | 9 +++++---- fs/ceph/mds_client.h | 2 ++ 3 files changed, 22 insertions(+), 6 deletions(-)
fs/ceph/caps.c +15 -2
@@ -2995,7 +2995,19 @@ int ceph_get_caps(struct file *filp, int ret = -ERESTARTSYS ;break ; } -wait_woken (&wait,TASK_INTERRUPTIBLE ,MAX_SCHEDULE_TIMEOUT ); + +/* +* If a cap update is lost after +* mds_wanted was raised, waiting +* forever will never make progress. +* Retry the renew path periodically +* so we can resend synchronously. +*/ +if (!wait_woken (&wait,TASK_INTERRUPTIBLE , +CEPH_GET_CAPS_WAIT_TIMEOUT )) { + ret = -ESTALE ; +break ; + } }remove_wait_queue (&ci->i_cap_wq, &wait);
@@ -3027,7 +3039,8 @@ int ceph_get_caps(struct file *filp, intcontinue ; }if (ret == -ESTALE ) { -/* session was killed, try renew caps */ +/* session was killed or a waited cap +* request needs a retry */ ret =ceph_renew_caps (inode, flags);if (ret ==0 )continue ;
fs/ceph/file.c +5 -4
@@ -277,21 +277,22 @@ static int ceph_init_file(struct inode * }/* -* try renew caps after session gets killed. +* Retry cap acquisition after a stale session or a lost cap update. */ int ceph_renew_caps (struct inode *inode,int fmode) {struct ceph_mds_client *mdsc =ceph_sb_to_mdsc (inode->i_sb);struct ceph_inode_info *ci =ceph_inode (inode);struct ceph_mds_request *req; -int err, flags, wanted; +int err, flags, wanted, issued;spin_lock (&ci->i_ceph_lock);__ceph_touch_fmode (ci, mdsc, fmode); wanted =__ceph_caps_file_wanted (ci); + issued =__ceph_caps_issued (ci,NULL );if (__ceph_is_any_real_caps (ci) && - (!(wanted &CEPH_CAP_ANY_WR ) || ci->i_auth_cap)) { -int issued =__ceph_caps_issued (ci,NULL ); + (!(wanted &CEPH_CAP_ANY_WR ) || ci->i_auth_cap) && + (issued & wanted) == wanted) {spin_unlock (&ci->i_ceph_lock);dout ("renew caps %p want %s issued %s updating mds_wanted\n" , inode,ceph_cap_string (wanted),ceph_cap_string (issued));
fs/ceph/mds_client.h +2 -0
@@ -61,6 +61,8 @@ enum ceph_feature_type {struct ceph_fs_client ;struct ceph_cap ; +#define CEPH_GET_CAPS_WAIT_TIMEOUT (5 * HZ) +/* * parsed info about a single inode. pointers are into the encoded * on-wire structures within the mds reply message payload.
Greg Kroah-Hartman
maintainerPATCH 186/235 net/sched: serialize qdisc_rtab_list against concurrent get/put
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aldo Ariel Panzardo <[email protected]>
[ Upstream commit f43ee0c0730d6191629b5ee1ceae27b1ebfdc047 ]
qdisc_get_rtab() and qdisc_put_rtab() mutate the process-global singly linked list qdisc_rtab_list and a plain non-atomic 'int refcnt' with no lock. This was only safe because every caller historically held the RTNL mutex, which serialized all rate-table lookups, inserts and frees.
That invariant no longer holds. cls_flower sets TCF_PROTO_OPS_DOIT_UNLOCKED, so tc_new_tfilter() keeps rtnl_held == false for it and sets TCA_ACT_FLAGS_NO_RTNL. That flag propagates through tcf_exts_validate_ex() -> tcf_action_init() -> tcf_action_init_1() -> tcf_police_init(), which calls qdisc_get_rtab()/qdisc_put_rtab() with the RTNL mutex NOT held. Two RTM_NEWTFILTER requests on different CPUs, each adding a flower filter with a police action carrying the same rate, then race on qdisc_rtab_list and on the non-atomic refcnt, leading to a use-after-free / double-free of the kmalloc-2k struct qdisc_rate_table. qdisc_rtab_list is a single global (not per-netns), so the corrupted object is shared system-wide.
BUG: KASAN: slab-use-after-free in qdisc_put_rtab+0x12f/0x160 qdisc_put_rtab+0x12f/0x160 tcf_police_init+0xda9/0x1590 tcf_action_init_1+0x460/0x6b0 tcf_action_init+0x439/0xa40 tcf_exts_validate_ex+0x42d/0x550 fl_change+0xddd/0x7da0 tc_new_tfilter+0xaa7/0x2420 rtnetlink_rcv_msg+0x95e/0xe90 which belongs to the cache kmalloc-2k of size 2048
Protect qdisc_rtab_list and the refcount with a dedicated spinlock. The (sleeping, GFP_KERNEL) allocation in qdisc_get_rtab() is performed before taking the lock; if a concurrent inserter added an identical table in the meantime the freshly allocated one is freed under the lock, so no duplicate is leaked. qdisc_put_rtab() now decrements the refcount and unlinks under the same lock.
--- net/sched/sch_api.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-)
net/sched/sch_api.c +19 -5
@@ -410,12 +410,13 @@ static __u8 __detect_linklayer(struct tc }static struct qdisc_rate_table *qdisc_rtab_list; +static DEFINE_SPINLOCK (qdisc_rtab_lock);struct qdisc_rate_table *qdisc_get_rtab (struct tc_ratespec *r,struct nlattr *tab,struct netlink_ext_ack *extack) { -struct qdisc_rate_table *rtab; +struct qdisc_rate_table *rtab, *new_rtab; if (tab == NULL || r->rate == 0 || r->cell_log == 0 || r->cell_log >= 32 ||
@@ -424,15 +425,20 @@ struct qdisc_rate_table *qdisc_get_rtab(return NULL ; } + new_rtab =kmalloc (sizeof (*new_rtab),GFP_KERNEL ); + +spin_lock (&qdisc_rtab_lock);for (rtab = qdisc_rtab_list; rtab; rtab = rtab->next) {if (!memcmp (&rtab->rate, r,sizeof (struct tc_ratespec )) && !memcmp (&rtab->data,nla_data (tab),1024 )) { rtab->refcnt++; +spin_unlock (&qdisc_rtab_lock); +kfree (new_rtab);return rtab; } } - rtab =kmalloc (sizeof (*rtab),GFP_KERNEL ); + rtab = new_rtab;if (rtab) { rtab->rate = *r; rtab->refcnt =1 ;
@@ -444,6 +450,7 @@ struct qdisc_rate_table *qdisc_get_rtab( }else {NL_SET_ERR_MSG (extack,"Failed to allocate new qdisc rate table" ); } +spin_unlock (&qdisc_rtab_lock);return rtab; }EXPORT_SYMBOL (qdisc_get_rtab);
@@ -452,18 +459,25 @@ void qdisc_put_rtab(struct qdisc_rate_ta {struct qdisc_rate_table *rtab, **rtabp; -if (!tab || --tab->refcnt) +if (!tab) +return ; + +spin_lock (&qdisc_rtab_lock); +if (--tab->refcnt) { +spin_unlock (&qdisc_rtab_lock);return ; + }for (rtabp = &qdisc_rtab_list; (rtab = *rtabp) !=NULL ; rtabp = &rtab->next) {if (rtab == tab) { *rtabp = rtab->next; -kfree (rtab); -return ; +break ; } } +spin_unlock (&qdisc_rtab_lock); +kfree (tab); }EXPORT_SYMBOL (qdisc_put_rtab);
Greg Kroah-Hartman
maintainerPATCH 187/235 net: gro: fix double aggregation of flush-marked skbs
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shiming Cheng <[email protected]>
[ Upstream commit e751256486d0ded20f5a9f9863467f1dce65142f ]
Commit 0ab03f353d36 ("net-gro: Fix GRO flush when receiving a GSO packet.") added a flush check to skb_gro_receive(), but skb_gro_receive_list() lacks the same validation.
As a result, packets marked with NAPI_GRO_CB(skb)->flush may still be re-aggregated.
This allows already-GRO'd packets with existing frag_list to be re-aggregated into a new GRO session, corrupting the frag_list chain structure. When skb_segment() attempts to unpack these malformed packets, it encounters invalid state and triggers a kernel panic.
Scenario (Tethering/Device forwarding):
1. Driver: Generated aggregated packet P1 via LRO with frag_list
2. Dev A: Receives aggregated fraglist packet and flush flag set
3. Dev A: Re-enters GRO, skb_gro_receive_list() is called
4. Missing flush check allows re-aggregation despite flush flag
5. Frag_list chain becomes corrupted (loops or dangling refs)
6. Dev B: TX path calls skb_segment(), crashes on corrupted frag_list
Root cause in skb_segment():
The check at line ~4891:
if (hsize <= 0 && i >= nfrags && skb_headlen(list_skb) &&
(skb_headlen(list_skb) == len || sg)) { When frag_list is corrupted by double aggregation, when list_skb is a NULL pointer from skb->next, skb_headlen(list_skb) dereference NULL/corrupted pointers occurs.
Call Trace: skb_headlen(NULL skb) skb_segment tcp_gso_segment tcp4_gso_segment inet_gso_segment skb_mac_gso_segment __skb_gso_segment skb_gso_segment validate_xmit_skb validate_xmit_skb_list sch_direct_xmit qdisc_restart __qdisc_run qdisc_run net_tx_action
Fix: Add NAPI_GRO_CB(skb)->flush validation to the early-return check in skb_gro_receive_list(), matching the defensive programming pattern of skb_gro_receive().
--- net/core/skbuff.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
net/core/skbuff.c +3 -1
@@ -3830,7 +3830,9 @@ EXPORT_SYMBOL_GPL(skb_segment_list);int skb_gro_receive_list (struct sk_buff *p,struct sk_buff *skb) { -if (unlikely (p->len + skb->len >=65536 )) +/* make sure to check flush flag and to not merge */ +if (unlikely (p->len + skb->len >=65536 || +NAPI_GRO_CB (skb)->flush))return -E2BIG ;if (NAPI_GRO_CB (p)->last == p)
Greg Kroah-Hartman
maintainerPATCH 188/235 super: fix emergency thaw deadlock on frozen block devices
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit 749d7aa0377aae32af8c0a4ad43371e7bf830ab5 ]
do_thaw_all_callback() calls bdev_thaw() while holding sb->s_umount exclusively. If the block device was frozen via bdev_freeze() dropping the last block layer freeze reference calls fs_bdev_thaw() which reacquires s_umount:
do_thaw_all_callback(sb)
super_lock_excl(sb) # holds sb->s_umount
bdev_thaw(sb->s_bdev)
mutex_lock(&bdev->bd_fsfreeze_mutex)
# bd_fsfreeze_count drops 1 -> 0
bd_holder_ops->thaw == fs_bdev_thaw
get_bdev_super(bdev)
bdev_super_lock(bdev, true)
super_lock(sb, true)
down_write(&sb->s_umount) # same task: deadlockThe emergency thaw worker deadlocks against itself holding both s_umount and bd_fsfreeze_mutex. That fscks any subsequent unmount, freeze, or thaw of that filesystem and block device.
[ 81.878470] sysrq: Show Blocked State [ 81.880140] task:kworker/0:1 state:D stack:0 pid:11 tgid:11 ppid:2 task_flags:0x4208060 flags:0x00080000 [ 81.884876] Workqueue: events do_thaw_all [ 81.886656] Call Trace: [ 81.887759] <TASK> [ 81.888763] __schedule+0x579/0x1420 [ 81.890372] schedule+0x3a/0x100 [ 81.891794] schedule_preempt_disabled+0x15/0x30 [ 81.893848] rwsem_down_write_slowpath+0x1ea/0x900 [ 81.895191] ? __pfx_do_thaw_all_callback+0x10/0x10 [ 81.896528] down_write+0xbd/0xc0 [ 81.897505] super_lock+0x91/0x180 [ 81.898457] ? __mutex_lock+0xa99/0x1140 [ 81.900748] ? __mutex_unlock_slowpath+0x1f/0x400 [ 81.902069] bdev_super_lock+0x5b/0x150 [ 81.903132] get_bdev_super+0x10/0x60 [ 81.904042] fs_bdev_thaw+0x23/0xf0 [ 81.904755] bdev_thaw+0x82/0x100 [ 81.905484] do_thaw_all_callback+0x2c/0x50 [ 81.906298] __iterate_supers+0x5d/0x130 [ 81.907067] do_thaw_all+0x20/0x40 [ 81.907739] process_one_work+0x206/0x5e0 [ 81.908545] worker_thread+0x1e2/0x3c0 [ 81.909339] ? __pfx_worker_thread+0x10/0x10 [ 81.910171] kthread+0xf4/0x130 [ 81.910799] ? __pfx_kthread+0x10/0x10 [ 81.911528] ret_from_fork+0x2e2/0x3b0 [ 81.912259] ? __pfx_kthread+0x10/0x10 [ 81.913010] ret_from_fork_asm+0x1a/0x30 [ 81.913806] </TASK>
bdev_super_lock() even documents the violated requirement with lockdep_assert_not_held(&sb->s_umount).
Acquiring bd_fsfreeze_mutex under s_umount also inverts the bd_fsfreeze_mutex vs. s_umount ordering established by bdev_{freeze,thaw}() and can thus ABBA against a concurrent block-layer freeze even when the recursive path isn't hit.
Fix this by not holding s_umount around the bdev_thaw() loop at all. Pin the superblock with an active reference instead as filesystems_freeze_callback() does. The active reference keeps the superblock from being shut down and so ->s_bdev stays valid without holding s_umount. The block-layer-held freeze is dropped by fs_bdev_thaw() with FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE exactly as a regular unfreeze would and thaw_super_locked() handles filesystem-level freezes as before.
The emergency thaw path has deadlocked like this in one form or another for a long long time but the current exclusively-held shape dates back to commit [1] where thaw_bdev() already ended in thaw_super() with s_umount held by do_thaw_all_callback().
--- fs/super.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-)
fs/super.c +15 -4
@@ -1048,13 +1048,24 @@ void emergency_remount(void)static void do_thaw_all_callback (struct super_block *sb) { +bool active = false; + +down_write (&sb->s_umount); +if (sb->s_root && sb->s_flags &SB_BORN ) + active =atomic_inc_not_zero (&sb->s_active); +up_write (&sb->s_umount); +if (!active) +return ; + +/* thaw_bdev() acquires s_umount so it must not be held here */ +emergency_thaw_bdev (sb); +down_write (&sb->s_umount); -if (sb->s_root && sb->s_flags &SB_BORN ) { -emergency_thaw_bdev (sb); +if (sb->s_root && sb->s_flags &SB_BORN )thaw_super_locked (sb); - }else { +else up_write (&sb->s_umount); - } +deactivate_super (sb); }static void do_thaw_all (struct work_struct *work)
Greg Kroah-Hartman
maintainerPATCH 189/235 ftrace: Add global mutex to serialize trace_parser access
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tengda Wu <[email protected]>
[ Upstream commit 7720b63bcef3f54c7fe288774b720a227d54a306 ]
In ftrace, the trace_parser structure is allocated and initialized when a trace file is opened, and is subsequently used across write and release handlers to parse user input.
The affected handler paths and their specific functions are:
- Open paths: ftrace_regex_open(), ftrace_graph_open()
- Write paths: ftrace_regex_write(), ftrace_graph_write()
- Release paths: ftrace_regex_release(), ftrace_graph_release()
If userspace opens a trace file descriptor and shares it across multiple threads, concurrent write calls will race on the parser's internal state, specifically the 'idx', 'cont', and 'buffer' fields, leading to corrupted input or undefined behavior.
Fix this by adding a global mutex, parser_lock, to serialize all access to trace_parser across write and release paths, preventing concurrent corruption of parser state.
--- kernel/trace/ftrace.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
kernel/trace/ftrace.c +15 -0
@@ -1043,6 +1043,12 @@ struct ftrace_ops global_ops = { };/* +* parser_lock - Protects trace_parser state against concurrent operations. +* Held across trace_get_user() and subsequent buffer parsing to prevent races. +*/ +static DEFINE_MUTEX (parser_lock); + +/* * Used by the stack undwinder to know about dynamic ftrace trampolines. */ struct ftrace_ops *ftrace_ops_trampoline (unsigned long addr)
@@ -4934,6 +4940,8 @@ ftrace_regex_write(struct file *file, co/* iter->hash is a local copy, so we don't need regex_lock */ parser = &iter->parser; + +mutex_lock (&parser_lock); read =trace_get_user (parser, ubuf, cnt, ppos); if (read >= 0 && trace_parser_loaded(parser) &&
@@ -4947,6 +4955,7 @@ ftrace_regex_write(struct file *file, co ret = read; out: +mutex_unlock (&parser_lock);return ret; }
@@ -5682,12 +5691,14 @@ int ftrace_regex_release(struct inode *i iter = file->private_data; parser = &iter->parser; +mutex_lock (&parser_lock);if (trace_parser_loaded (parser)) {int enable = !(iter->flags &FTRACE_ITER_NOTRACE );ftrace_process_regex (iter, parser->buffer, parser->idx, enable); } +mutex_unlock (&parser_lock);trace_parser_put (parser);
@@ -6005,10 +6016,12 @@ ftrace_graph_release(struct inode *inode parser = &fgd->parser; +mutex_lock (&parser_lock);if (trace_parser_loaded ((parser))) { ret =ftrace_graph_set_hash (fgd->new_hash, parser->buffer); } +mutex_unlock (&parser_lock);trace_parser_put (parser);
@@ -6127,6 +6140,7 @@ ftrace_graph_write(struct file *file, co parser = &fgd->parser; +mutex_lock (&parser_lock); read =trace_get_user (parser, ubuf, cnt, ppos); if (read >= 0 && trace_parser_loaded(parser) &&
@@ -6139,6 +6153,7 @@ ftrace_graph_write(struct file *file, coif (!ret) ret = read; +mutex_unlock (&parser_lock);return ret; }
Greg Kroah-Hartman
maintainerPATCH 190/235 skbuff: introduce skb_pull_data
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Augusto von Dentz <[email protected]>
[ Upstream commit 13244cccc2b61ec715f0ac583d3037497004d4a5 ]
Like skb_pull but returns the original data pointer before pulling the data after performing a check against sbk->len.
This allows to change code that does "struct foo *p = (void *)skb->data;" which is hard to audit and error prone, to:
p = skb_pull_data(skb, sizeof(*p));
if (!p)
return;Which is both safer and cleaner.
Stable-dep-of: 47778d2c2087 ("Bluetooth: HIDP: reject frames without a transaction header")
--- include/linux/skbuff.h | 2 ++ net/core/skbuff.c | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+)
include/linux/skbuff.h +2 -0
@@ -2317,6 +2317,8 @@ static inline void *skb_pull_inline(strureturn unlikely (len > skb->len) ?NULL :__skb_pull (skb, len); } +void *skb_pull_data (struct sk_buff *skb,size_t len); +void *__pskb_pull_tail (struct sk_buff *skb,int delta);static inline void *__pskb_pull (struct sk_buff *skb,unsigned int len)
net/core/skbuff.c +24 -0
@@ -1977,6 +1977,30 @@ void *skb_pull(struct sk_buff *skb, unsiEXPORT_SYMBOL (skb_pull);/** +* skb_pull_data - remove data from the start of a buffer returning its +* original position. +* @skb: buffer to use +* @len: amount of data to remove +* +* This function removes data from the start of a buffer, returning +* the memory to the headroom. A pointer to the original data in the buffer +* is returned after checking if there is enough data to pull. Once the +* data has been pulled future pushes will overwrite the old data. +*/ +void *skb_pull_data (struct sk_buff *skb,size_t len) +{ +void *data = skb->data; + +if (skb->len < len) +return NULL ; + +skb_pull (skb, len); + +return data; +} +EXPORT_SYMBOL (skb_pull_data); + +/** * skb_trim - remove end from a buffer * @skb: buffer to alter * @len: new length
Greg Kroah-Hartman
maintainerPATCH 191/235 Bluetooth: HIDP: reject frames without a transaction header
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sangho Lee <[email protected]>
[ Upstream commit 47778d2c2087b5d192398f6fddf692d16a5431cf ]
hidp_recv_ctrl_frame() and hidp_recv_intr_frame() read skb->data[0] before checking that the L2CAP SDU contains a transaction header. A connected HIDP peer can send an empty basic-mode SDU and make both paths use an uninitialized byte from skb tailroom.
KMSAN reports the use in hidp_session_run(), with the uninitialized value originating in __alloc_skb() through vhci_write(). The control path produces two reports and the interrupt path produces one.
The byte can also be controlled by a malformed lower-layer packet. If an HCI ACL packet contains an L2CAP PDU with a declared zero-length payload followed by an extra 0x15 byte, l2cap_recv_acldata() reduces skb->len to the declared PDU length before dispatch. The current HIDP path nevertheless consumes the extra byte as HIDP_TRANS_HID_CONTROL | HIDP_CTRL_VIRTUAL_CABLE_UNPLUG and terminates the HIDP session. With this change, the same packet is discarded and a subsequent feature report request succeeds.
Pull the transaction header with skb_pull_data() and discard frames that do not contain it.
--- net/bluetooth/hidp/core.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-)
net/bluetooth/hidp/core.c +15 -10
@@ -551,16 +551,18 @@ static int hidp_process_data(struct hidpstatic void hidp_recv_ctrl_frame (struct hidp_session *session,struct sk_buff *skb) { -unsigned char hdr, type, param; +unsigned char type, param; +u8 *hdr;int free_skb =1 ;BT_DBG ("session %p skb %p len %d" , session, skb, skb->len); - hdr = skb->data[0 ]; -skb_pull (skb,1 ); + hdr =skb_pull_data (skb,1 ); +if (!hdr) + goto free; - type = hdr &HIDP_HEADER_TRANS_MASK ; - param = hdr &HIDP_HEADER_PARAM_MASK ; + type = *hdr &HIDP_HEADER_TRANS_MASK ; + param = *hdr &HIDP_HEADER_PARAM_MASK ;switch (type) {case HIDP_TRANS_HANDSHAKE :
@@ -581,6 +583,7 @@ static void hidp_recv_ctrl_frame(structbreak ; } +free:if (free_skb)kfree_skb (skb); }
@@ -588,14 +591,15 @@ static void hidp_recv_ctrl_frame(structstatic void hidp_recv_intr_frame (struct hidp_session *session,struct sk_buff *skb) { -unsigned char hdr; +u8 *hdr;BT_DBG ("session %p skb %p len %d" , session, skb, skb->len); - hdr = skb->data[0 ]; -skb_pull (skb,1 ); + hdr =skb_pull_data (skb,1 ); +if (!hdr) + goto free; -if (hdr == (HIDP_TRANS_DATA |HIDP_DATA_RTYPE_INPUT )) { +if (*hdr == (HIDP_TRANS_DATA |HIDP_DATA_RTYPE_INPUT )) {hidp_set_timer (session);if (session->input)
@@ -607,9 +611,10 @@ static void hidp_recv_intr_frame(structBT_DBG ("report len %d" , skb->len); } }else { -BT_DBG ("Unsupported protocol header 0x%02x" , hdr); +BT_DBG ("Unsupported protocol header 0x%02x" , *hdr); } +free:kfree_skb (skb); }
Greg Kroah-Hartman
maintainerPATCH 192/235 mm/vmstat: fold stranded per-cpu node stats when a node comes online
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gregory Price <[email protected]>
[ Upstream commit ea3034b2b00fa50c8d2518d0804c9d427bbafa86 ]
A per-node vmstat counter is pgdat->vm_stat[] plus per-cpu deltas. A balanced counter can sit split as global=+N / per-cpu=-N.
The folds reconciling the split only walk online nodes, so when try_offline_node() marks a node offline the per-cpu deltas are stranded.
A subsequent online resets the per-cpu area but not pgdat->vm_stat[], orphaning the +N permanently. All NR_VM_NODE_STAT_ITEMS are affected.
The existing code zeroes the per-cpu counters and causes a permanent skew. Fold the stranded deltas instead, before the node rejoins the online set. The node is not online yet and the hotplug lock is held, so the remote access to per-cpu values is safe.
Discovered when node compaction hung for a nearly empty node, as the math to determine throttling broke. Reproduced by repeated memory hotplug/unplug cycles on a node under pressure: NR_ISOLATED_ANON ratchets up and never returns to zero.
--- mm/memory_hotplug.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-)
mm/memory_hotplug.c +11 -4
@@ -894,7 +894,7 @@ static pg_data_t __ref *hotadd_new_pgdatalloc_percpu (struct per_cpu_nodestat );arch_refresh_nodedata (nid, pgdat); }else { -int cpu; +int cpu, i;/* * Reset the nr_zones, order and highest_zoneidx before reuse. * Note that kswapd will init kswapd_highest_zoneidx properly
@@ -903,10 +903,17 @@ static pg_data_t __ref *hotadd_new_pgdat pgdat->nr_zones =0 ; pgdat->kswapd_order =0 ; pgdat->kswapd_highest_zoneidx =0 ; -for_each_online_cpu (cpu) { -struct per_cpu_nodestat *p; +/* +* Hot-unplug can leave per-cpu vmstat deltas unfolded (folders skip +* offline nodes) - reconcile this at online. Foreign access to counters +* is safe: the node is not online yet and we hold the hotplug lock. +*/ +for_each_possible_cpu (cpu) { +struct per_cpu_nodestat *p =per_cpu_ptr (pgdat->per_cpu_nodestats, cpu); - p =per_cpu_ptr (pgdat->per_cpu_nodestats, cpu); +for (i =0 ; i <NR_VM_NODE_STAT_ITEMS ; i++) +if (p->vm_node_stat_diff[i]) +node_page_state_add (p->vm_node_stat_diff[i], pgdat, i);memset (p,0 ,sizeof (*p)); } }
Greg Kroah-Hartman
maintainerPATCH 193/235 net: pktgen: fix code style (WARNING: Block comments)
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Seiderer <[email protected]>
[ Upstream commit 870b856cb478bc02fffe4d89897e62c692efb09a ]
Fix checkpatch code style warnings:
WARNING: Block comments use a trailing */ on a separate line
+ * removal by worker thread */
WARNING: Block comments use * on subsequent lines
+ __u8 tos; /* six MSB of (former) IPv4 TOS
+ are for dscp codepoint */
WARNING: Block comments use a trailing */ on a separate line
+ are for dscp codepoint */
WARNING: Block comments use * on subsequent lines
+ __u8 traffic_class; /* ditto for the (former) Traffic Class in IPv6
+ (see RFC 3260, sec. 4) */
WARNING: Block comments use a trailing */ on a separate line
+ (see RFC 3260, sec. 4) */
WARNING: Block comments use * on subsequent lines
+ /* = {
+ 0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB,
WARNING: Block comments use * on subsequent lines
+ /* Field for thread to receive "posted" events terminate,
+ stop ifs etc. */
WARNING: Block comments use a trailing */ on a separate line
+ stop ifs etc. */
WARNING: Block comments should align the * on each line
+ * we go look for it ...
+*/
WARNING: Block comments use a trailing */ on a separate line
+ * we resolve the dst issue */
WARNING: Block comments use a trailing */ on a separate line
+ * with proc_create_data() */
Stable-dep-of: 817ff6efdb7f ("net: pktgen: fix proc entry use-after-free")
--- net/core/pktgen.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-)
net/core/pktgen.c +23 -16
@@ -275,7 +275,8 @@ struct pktgen_dev {int pkt_overhead;/* overhead for MPLS, VLANs, IPSEC etc */ int nfrags;int removal_mark;/* non-zero => the device is marked for -* removal by worker thread */ +* removal by worker thread +*/ struct page *page;u64 delay;/* nano-seconds */
@@ -338,10 +339,12 @@ struct pktgen_dev {__u16 udp_dst_max;/* exclusive, dest UDP port */ /* DSCP + ECN */ -__u8 tos;/* six MSB of (former) IPv4 TOS -are for dscp codepoint */ -__u8 traffic_class;/* ditto for the (former) Traffic Class in IPv6 -(see RFC 3260, sec. 4) */ +__u8 tos;/* six MSB of (former) IPv4 TOS +* are for dscp codepoint +*/ +__u8 traffic_class;/* ditto for the (former) Traffic Class in IPv6 +* (see RFC 3260, sec. 4) +*/ /* MPLS */ unsigned int nr_labels;/* Depth of stack, 0 = no MPLS */
@@ -375,12 +378,12 @@ struct pktgen_dev {__u8 hh[14 ];/* = { -0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB, - -We fill in SRC address later -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x08, 0x00 -}; +* 0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB, +* +* We fill in SRC address later +* 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +* 0x08, 0x00 +* }; */ __u16 pad;/* pad out the hh struct to an even 16 bytes */
@@ -443,7 +446,8 @@ struct pktgen_thread {char result[512 ];/* Field for thread to receive "posted" events terminate, -stop ifs etc. */ +* stop ifs etc. +*/ u32 control;int cpu;
@@ -2235,7 +2239,7 @@ static inline int f_pick(struct pktgen_d#ifdef CONFIG_XFRM /* If there was already an IPSEC SA, we keep it as is, else * we go look for it ... -*/ +*/ #define DUMMY_MARK 0static void get_ipsec_sa (struct pktgen_dev *pkt_dev,int flow) {
@@ -2506,7 +2510,8 @@ static int pktgen_output_ipsec(struct skif (!x)return 0 ;/* XXX: we dont support tunnel mode for now until -* we resolve the dst issue */ +* we resolve the dst issue +*/ if ((x->props.mode !=XFRM_MODE_TRANSPORT ) && (pkt_dev->spi ==0 ))return 0 ;
@@ -3569,7 +3574,8 @@ static int add_dev_to_thread(struct pktg* userspace on another CPU than the kthread. The if_lock() * is used here to sync with concurrent instances of * _rem_dev_from_if_list() invoked via kthread, which is also -* updating the if_list */ +* updating the if_list +*/ if_lock (t);if (pkt_dev->pg_thread) {
@@ -3766,7 +3772,8 @@ static int pktgen_remove_device(struct p/* Remove proc before if_list entry, because add_device uses * list to determine if interface already exist, avoid race -* with proc_create_data() */ +* with proc_create_data() +*/ proc_remove (pkt_dev->entry);/* And update the thread if_list */
Greg Kroah-Hartman
maintainerPATCH 194/235 net: pktgen: fix proc entry use-after-free
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chengfeng Ye <[email protected]>
[ Upstream commit 817ff6efdb7f484ea547218e11e17d8e43daa3b4 ]
pktgen_change_name() replaces pkt_dev->entry while holding t->if_lock. pktgen_remove_device() removes the same entry before _rem_dev_from_if_list() takes that lock.
This allows the following interleaving:
CPU 0 (NETDEV_CHANGENAME) CPU 1 (kpktgend)
if_lock(t)
proc_remove(pkt_dev->entry)
proc_remove(pkt_dev->entry)
pkt_dev->entry = proc_create_data(...)
if_unlock(t)
The kthread can pass the stale proc_dir_entry to proc_remove() after the rename path has freed it. A reproducer with a widened race window reports:
BUG: KASAN: slab-use-after-free in proc_remove+0x78/0x80 Read of size 8 at addr ffff8881478fea70 by task kpktgend_0/67 Call Trace: proc_remove+0x78/0x80 pktgen_remove_device.isra.0+0x11c/0x4c0 pktgen_thread_worker+0x1214/0x6bc0 kthread+0x2c6/0x3b0 Allocated by task 95: __proc_create+0x204/0x790 proc_create_data+0x72/0xe0 pktgen_thread_write+0xd61/0x1510 Freed by task 28: kmem_cache_free+0xcb/0x3d0 proc_free_inode+0x5b/0x80 rcu_core+0x50a/0x1850 The buggy address belongs to the object at ffff8881478fea00 which belongs to the cache proc_dir_entry of size 192
Move proc_remove() into the if_lock-protected list removal helper. Keep it before list_del_rcu() to preserve the ordering required by add_device(). The rename path must then finish replacing the entry before removal, or it observes that the device is no longer on the list.
--- net/core/pktgen.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
net/core/pktgen.c +1 -3
@@ -3745,6 +3745,7 @@ static void _rem_dev_from_if_list(structstruct pktgen_dev *p;if_lock (t); +proc_remove (pkt_dev->entry);list_for_each_safe (q, n, &t->if_list) { p = list_entry(q, struct pktgen_dev, list);if (p == pkt_dev)
@@ -3774,9 +3775,6 @@ static int pktgen_remove_device(struct p* list to determine if interface already exist, avoid race * with proc_create_data() */ -proc_remove (pkt_dev->entry); - -/* And update the thread if_list */ _rem_dev_from_if_list (t, pkt_dev);#ifdef CONFIG_XFRM
Greg Kroah-Hartman
maintainerPATCH 195/235 scsi: sd: sd_zbc: Improve source code documentation
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bart Van Assche <[email protected]>
[ Upstream commit aa96bfb4caff59c93f0637092efe3a714cab0fe6 ]
Add several kernel-doc headers. Declare input arrays const. Specify the array size in function declarations.
Stable-dep-of: 93dde0bf2f39 ("scsi: scsi_debug: Fix REPORT ZONES alloc_len underflow OOB write")
--- drivers/scsi/sd.h | 5 +--- drivers/scsi/sd_zbc.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 6 deletions(-)
drivers/scsi/sd.h +2 -3
@@ -217,7 +217,7 @@ static inline int sd_is_zoned(struct scs#ifdef CONFIG_BLK_DEV_ZONED void sd_zbc_release_disk (struct scsi_disk *sdkp); -int sd_zbc_read_zones (struct scsi_disk *sdkp,unsigned char *buffer); +int sd_zbc_read_zones (struct scsi_disk *sdkp,u8 buf[SD_BUF_SIZE ]);int sd_zbc_revalidate_zones (struct scsi_disk *sdkp);blk_status_t sd_zbc_setup_zone_mgmt_cmnd (struct scsi_cmnd *cmd,unsigned char op,bool all);
@@ -233,8 +233,7 @@ blk_status_t sd_zbc_prepare_zone_append(static inline void sd_zbc_release_disk (struct scsi_disk *sdkp) {} -static inline int sd_zbc_read_zones (struct scsi_disk *sdkp, -unsigned char *buf) +static inline int sd_zbc_read_zones (struct scsi_disk *sdkp,u8 buf[SD_BUF_SIZE ]) {return 0 ; }
drivers/scsi/sd_zbc.c +52 -3
97 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 196/235 scsi: sd: sd_zbc: Use logical blocks as unit when querying zones
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Damien Le Moal <[email protected]>
[ Upstream commit 43af5da09efb8abe450ec859d3063adeb7d1eb54 ]
When querying zones, track the position in logical blocks instead of in sectors. This change slightly simplifies sd_zbc_report_zones().
Stable-dep-of: 93dde0bf2f39 ("scsi: scsi_debug: Fix REPORT ZONES alloc_len underflow OOB write")
--- drivers/scsi/sd_zbc.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
drivers/scsi/sd_zbc.c +5 -6
@@ -224,7 +224,7 @@ int sd_zbc_report_zones(struct gendisk * unsigned int nr_zones, report_zones_cb cb, void *data) { struct scsi_disk *sdkp = scsi_disk(disk); -sector_t capacity =logical_to_sectors (sdkp->device, sdkp->capacity); +sector_t lba =sectors_to_logical (sdkp->device, sector);unsigned int nr, i;unsigned char *buf;size_t offset, buflen =0 ;
@@ -235,7 +235,7 @@ int sd_zbc_report_zones(struct gendisk */* Not a zoned device */ return -EOPNOTSUPP ; -if (!capacity) +if (!sdkp->capacity)/* Device gone or invalid */ return -ENODEV ;
@@ -243,9 +243,8 @@ int sd_zbc_report_zones(struct gendisk *if (!buf)return -ENOMEM ; -while (zone_idx < nr_zones && sector < capacity) { - ret =sd_zbc_do_report_zones (sdkp, buf, buflen, -sectors_to_logical (sdkp->device, sector), true); +while (zone_idx < nr_zones && lba < sdkp->capacity) { + ret =sd_zbc_do_report_zones (sdkp, buf, buflen, lba, true);if (ret) goto out;
@@ -263,7 +262,7 @@ int sd_zbc_report_zones(struct gendisk * zone_idx++; } - sector +=sd_zbc_zone_sectors (sdkp) * i; + lba += sdkp->zone_blocks * i; } ret = zone_idx;
Greg Kroah-Hartman
maintainerPATCH 197/235 scsi: sd: sd_zbc: Return early in sd_zbc_check_zoned_characteristics()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Damien Le Moal <[email protected]>
[ Upstream commit 60caf3758103b8edc90724ba781ff119f739162a ]
Return early in sd_zbc_check_zoned_characteristics() for host-aware disks. This patch does not change any functionality but makes a later patch easier to read.
Stable-dep-of: 93dde0bf2f39 ("scsi: scsi_debug: Fix REPORT ZONES alloc_len underflow OOB write")
--- drivers/scsi/sd_zbc.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
drivers/scsi/sd_zbc.c +7 -6
@@ -591,14 +591,15 @@ static int sd_zbc_check_zoned_characteri sdkp->zones_optimal_open =get_unaligned_be32 (&buf[8 ]); sdkp->zones_optimal_nonseq =get_unaligned_be32 (&buf[12 ]); sdkp->zones_max_open =0 ; - }else { -/* Host-managed */ - sdkp->urswrz = buf[4 ] &1 ; - sdkp->zones_optimal_open =0 ; - sdkp->zones_optimal_nonseq =0 ; - sdkp->zones_max_open =get_unaligned_be32 (&buf[16 ]); +return 0 ; } +/* Host-managed */ + sdkp->urswrz = buf[4 ] &1 ; + sdkp->zones_optimal_open =0 ; + sdkp->zones_optimal_nonseq =0 ; + sdkp->zones_max_open =get_unaligned_be32 (&buf[16 ]); +/* * Check for unconstrained reads: host-managed devices with * constrained reads (drives failing read after write pointer)
Greg Kroah-Hartman
maintainerPATCH 198/235 scsi: scsi_debug: Rename zone type constants
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Damien Le Moal <[email protected]>
[ Upstream commit 35dbe2b9a7b0c92777c855c6a2cca8390f4c166b ]
Rename the scsi_debug zone type constants to prevent a conflict with the ZBC_ZONE_TYPE_GAP constant from include/scsi/scsi_proto.h.
Stable-dep-of: 93dde0bf2f39 ("scsi: scsi_debug: Fix REPORT ZONES alloc_len underflow OOB write")
--- drivers/scsi/scsi_debug.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
drivers/scsi/scsi_debug.c +9 -9
@@ -250,9 +250,9 @@ static const char *sdebug_version_date =/* Zone types (zbcr05 table 25) */ enum sdebug_z_type { -ZBC_ZONE_TYPE_CNV =0x1 , -ZBC_ZONE_TYPE_SWR =0x2 , -ZBC_ZONE_TYPE_SWP =0x3 , +ZBC_ZTYPE_CNV =0x1 , +ZBC_ZTYPE_SWR =0x2 , +ZBC_ZTYPE_SWP =0x3 , };/* enumeration names taken from table 26, zbcr05 */
@@ -2681,7 +2681,7 @@ static struct sdeb_zone_state *zbc_zone(static inline bool zbc_zone_is_conv (struct sdeb_zone_state *zsp) { -return zsp->z_type ==ZBC_ZONE_TYPE_CNV ; +return zsp->z_type ==ZBC_ZTYPE_CNV ; } static void zbc_close_zone(struct sdebug_dev_info *devip,
@@ -2780,7 +2780,7 @@ static void zbc_inc_wp(struct sdebug_devif (zbc_zone_is_conv (zsp))return ; -if (zsp->z_type ==ZBC_ZONE_TYPE_SWR ) { +if (zsp->z_type ==ZBC_ZTYPE_SWR ) { zsp->z_wp += num;if (zsp->z_wp >= zend)zbc_set_zone_full (devip, zsp);
@@ -2847,7 +2847,7 @@ static int check_zbc_access_params(strucreturn 0 ; } -if (zsp->z_type ==ZBC_ZONE_TYPE_SWR ) { +if (zsp->z_type ==ZBC_ZTYPE_SWR ) {/* Writes cannot cross sequential zone boundaries */ if (zsp_end != zsp) { mk_sense_buffer(scp, ILLEGAL_REQUEST,
@@ -4913,14 +4913,14 @@ static int sdebug_device_create_zones(st zsp->z_start = zstart;if (i < devip->nr_conv_zones) { - zsp->z_type =ZBC_ZONE_TYPE_CNV ; + zsp->z_type =ZBC_ZTYPE_CNV ; zsp->z_cond =ZBC_NOT_WRITE_POINTER ; zsp->z_wp = (sector_t)-1 ; }else {if (devip->zmodel ==BLK_ZONED_HM ) - zsp->z_type =ZBC_ZONE_TYPE_SWR ; + zsp->z_type =ZBC_ZTYPE_SWR ;else - zsp->z_type =ZBC_ZONE_TYPE_SWP ; + zsp->z_type =ZBC_ZTYPE_SWP ; zsp->z_cond =ZC1_EMPTY ; zsp->z_wp = zsp->z_start; }
Greg Kroah-Hartman
maintainerPATCH 199/235 scsi: scsi_debug: Fix REPORT ZONES alloc_len underflow OOB write
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ibrahim Hashimov <[email protected]>
resp_report_zones() sizes the reply buffer from the CDB allocation length. The v3 fix rounds alloc_len up with ALIGN() before deriving the descriptor count:
rep_max_zones = (ALIGN((u64)alloc_len, RZONES_DESC_HD) - RZONES_DESC_HD) >> ilog2(RZONES_DESC_HD); arr_len = (u64)RZONES_DESC_HD * (rep_max_zones + 1);
For alloc_len in 0xFFFFFFC1..0xFFFFFFFF, ALIGN() rounds up to 0x100000000, so arr_len is 4 GB. On 32-bit, kzalloc()'s size_t is 32-bit and truncates 0x100000000 to 0; kzalloc(0) returns ZERO_SIZE_PTR, which passes the !arr check, and desc = arr + 64 is then dereferenced in the loop -> out-of-bounds write / panic.
Clamp rep_max_zones to devip->nr_zones. The loop already stops at sdebug_capacity (after nr_zones zones), so a report can never hold more than nr_zones descriptors; the clamp does not change the report, it only bounds arr_len to (nr_zones + 1) * RZONES_DESC_HD, a real device property that can never reach 0x100000000.
[ Adjusted for 5.10: resp_report_zones() here predates commit 4a5fc1c6d752
("scsi: scsi_debug: Add gap zone support"), so it still computes
max_zones = devip->nr_zones - (zs_lba >> devip->zsize_shift);
and bounds the descriptor loop with it. Keep that existing, tighter clamp instead of introducing a second one against devip->nr_zones:
max_zones is by construction <= devip->nr_zones and is the actual number of descriptors the loop can emit, so it satisfies the upstream requirement that arr_len be bounded by a real device property while leaving the reported zone list unchanged.
Without the fix 5.10 has the same class of bug from the other end of the range: for alloc_len in 1..63 the unsigned (alloc_len - 64) underflows, kzalloc(alloc_len) returns a sub-64-byte buffer, and both the report header at arr + 0 and desc = arr + 64 are written out of bounds. Sizing the allocation from rep_max_zones rather than from alloc_len fixes that too. ]
--- drivers/scsi/scsi_debug.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/scsi/scsi_debug.c +6 -3
@@ -4320,6 +4320,7 @@ static int resp_report_zones(struct scsiu32 alloc_len, rep_opts, rep_len;bool partial;u64 lba, zs_lba; +u64 arr_len;u8 *arr =NULL , *desc;u8 *cmd = scp->cmnd;struct sdeb_zone_state *zsp;
@@ -4343,10 +4344,12 @@ static int resp_report_zones(struct scsi } max_zones = devip->nr_zones - (zs_lba >> devip->zsize_shift); - rep_max_zones =min ((alloc_len -64 ) >>ilog2 (RZONES_DESC_HD ), - max_zones); + rep_max_zones = (ALIGN ((u64 )alloc_len,RZONES_DESC_HD ) -RZONES_DESC_HD ) >> +ilog2 (RZONES_DESC_HD ); + rep_max_zones = min_t(unsigned int, rep_max_zones, max_zones); + arr_len = (u64 )RZONES_DESC_HD * (rep_max_zones +1 ); - arr =kzalloc (alloc_len,GFP_ATOMIC | __GFP_NOWARN); + arr =kzalloc (arr_len,GFP_ATOMIC | __GFP_NOWARN);if (!arr) {mk_sense_buffer (scp,ILLEGAL_REQUEST ,INSUFF_RES_ASC ,INSUFF_RES_ASCQ );
Greg Kroah-Hartman
maintainerPATCH 200/235 ice: fix VF interrupts cleanup
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dawid Osuchowski <[email protected]>
[ Upstream commit fb096882095e5a8d6b5159e43793d4a38a0c5b1f ]
When a virtual function sends an IRQ map command, the PF will set up interrupts according to that request. However, because these interrupts are never reset, the next time Virtual Function initializes, the interrupts are still enabled for a given VF, which leads to performance degradation in certain cases due to interrupts being unexpectedly enabled and thus causing interrupt floods.
--- drivers/net/ethernet/intel/ice/ice_virtchnl_pf.c | 48 +++++++++++++++++++++++ 1 file changed, 48 insertions(+)
drivers/net/ethernet/intel/ice/ice_virtchnl_pf.c +48 -0
@@ -1262,6 +1262,30 @@ static bool ice_is_vf_disabled(struct ic }/** +* ice_reset_interrupts - clear all queue interrupt configuration for a VSI +* @vsi: the VSI whose interrupt registers should be cleared +* +* Zero the QINT_RQCTL and QINT_TQCTL registers for all allocated queues +* in the VSI. This clears the entire register including MSIX_INDX, ITR_INDX, +* CAUSE_ENA and NEXTQ fields, unlike ice_vf_dis_rxq_interrupt() which only +* clears the CAUSE_ENA bit. +*/ +static void ice_reset_interrupts (struct ice_vsi *vsi) +{ +struct ice_pf *pf = vsi->back; +struct ice_hw *hw = &pf->hw; +int i; + +ice_for_each_alloc_rxq (vsi, i) +wr32 (hw,QINT_RQCTL (vsi->rxq_map[i]),0 ); + +ice_for_each_alloc_txq (vsi, i) +wr32 (hw,QINT_TQCTL (vsi->txq_map[i]),0 ); + +ice_flush (hw); +} + +/** * ice_reset_vf - Reset a particular VF * @vf: pointer to the VF structure * @is_vflr: true if VFLR was issued, false if not
@@ -1304,6 +1328,9 @@ bool ice_reset_vf(struct ice_vf *vf, booice_dis_vf_qs (vf); +/* cleanup interrupt registers */ +ice_reset_interrupts (vsi); +/* Call Disable LAN Tx queue AQ whether or not queues are * enabled. This is needed for successful completion of VFR. */
@@ -2582,6 +2609,24 @@ static void ice_vf_ena_rxq_interrupt(str }/** +* ice_vf_dis_rxq_interrupt - disable Rx queue interrupt via QINT_RQCTL +* @vsi: VSI of the VF to configure +* @q_idx: VF queue index used to determine the queue in the PF's space +*/ +static void ice_vf_dis_rxq_interrupt (struct ice_vsi *vsi,u32 q_idx) +{ +struct ice_hw *hw = &vsi->back->hw; +u32 pfq = vsi->rxq_map[q_idx]; +u32 reg; + + reg =rd32 (hw,QINT_RQCTL (pfq)); + reg &= ~QINT_RQCTL_CAUSE_ENA_M ; +wr32 (hw,QINT_RQCTL (pfq), reg); + +ice_flush (hw); +} + +/** * ice_vc_ena_qs_msg * @vf: pointer to the VF info * @msg: pointer to the msg buffer
@@ -2753,6 +2798,8 @@ static int ice_vc_dis_qs_msg(struct ice_ goto error_param; } +for_each_set_bit (vf_q_id, &q_map,ICE_MAX_RSS_QS_PER_VF ) +ice_vf_dis_rxq_interrupt (vsi, vf_q_id);bitmap_zero (vf->rxq_ena,ICE_MAX_RSS_QS_PER_VF ); }else if (q_map) {for_each_set_bit (vf_q_id, &q_map,ICE_MAX_RSS_QS_PER_VF ) {
@@ -2773,6 +2820,7 @@ static int ice_vc_dis_qs_msg(struct ice_ goto error_param; } +ice_vf_dis_rxq_interrupt (vsi, vf_q_id);/* Clear enabled queues flag */ clear_bit (vf_q_id, vf->rxq_ena); }
Greg Kroah-Hartman
maintainerPATCH 201/235 ice: fix memory leak in ice_lbtest_prepare_rings()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dawei Feng <[email protected]>
[ Upstream commit 3a9de5590da4ffd9e9c541c4c4d492aa2b54cf6e ]
ice_lbtest_prepare_rings() frees Rx rings only when ice_vsi_start_all_rx_rings() fails. If ice_vsi_setup_rx_rings() fails after allocating some descriptors, or if ice_vsi_cfg_lan() fails after the Rx rings were prepared, the function reaches the Tx cleanup path without releasing the initialized Rx resources.
Fix this by adding separate unwind paths for Rx setup failure and LAN configuration failure. The Rx setup failure path releases the partially prepared Rx rings before freeing Tx rings, while later failures first undo the LAN Tx configuration and then release the Rx rings in reverse setup order.
The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc7.
An x86_64 allyesconfig build showed no new warnings. As we do not have an Intel E800 Series adapter available to run the ethtool offline loopback selftest, no runtime testing was able to be performed.
--- drivers/net/ethernet/intel/ice/ice_ethtool.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/net/ethernet/intel/ice/ice_ethtool.c +5 -5
@@ -478,18 +478,18 @@ static int ice_lbtest_prepare_rings(stru status =ice_vsi_cfg (vsi);if (status) - goto err_setup_rx_ring; + goto err_cfg_lan; status =ice_vsi_start_all_rx_rings (vsi);if (status) - goto err_start_rx_ring; + goto err_cfg_lan;return status; -err_start_rx_ring: -ice_vsi_free_rx_rings (vsi); -err_setup_rx_ring: +err_cfg_lan:ice_vsi_stop_lan_tx_rings (vsi,ICE_NO_RESET ,0 ); +err_setup_rx_ring: +ice_vsi_free_rx_rings (vsi); err_setup_tx_ring:ice_vsi_free_tx_rings (vsi);
Greg Kroah-Hartman
maintainerPATCH 202/235 i2c: bcm-iproc: remove printout on handled timeouts
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wolfram Sang <[email protected]>
[ Upstream commit 796e2c260187e32530cf343546ba1cdf2e2f5491 ]
I2C and SMBus timeouts are not something the user needs to be informed about on controller level. The client driver may know if that really is a problem and give more detailed information to the user. The controller should just pass this information upwards. Remove the printout.
Stable-dep-of: 98f2e9e6d6f9 ("i2c: iproc: reset bus after timeout if START_BUSY is stuck")
--- drivers/i2c/busses/i2c-bcm-iproc.c | 2 -- 1 file changed, 2 deletions(-)
drivers/i2c/busses/i2c-bcm-iproc.c +0 -2
@@ -789,8 +789,6 @@ static int bcm_iproc_i2c_xfer_wait(struc }if (!time_left && !iproc_i2c->xfer_is_done) { -dev_err (iproc_i2c->device,"transaction timed out\n" ); -/* flush both TX/RX FIFOs */ val =BIT (M_FIFO_RX_FLUSH_SHIFT ) |BIT (M_FIFO_TX_FLUSH_SHIFT );iproc_i2c_wr_reg (iproc_i2c,M_FIFO_CTRL_OFFSET , val);
Greg Kroah-Hartman
maintainerPATCH 203/235 i2c: iproc: reset bus after timeout if START_BUSY is stuck
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jonas Gorski <[email protected]>
[ Upstream commit 98f2e9e6d6f91a6abb43f166b244b428ba85fa2b ]
If a transaction times out, the START_BUSY signal can stay up, and subsequent transactaction attempts will fail as the bus is still considered busy.
I can easily trigger this by attempting to read from an address with no device, e.g. when running i2cdetect. After the first read times out, all subsequent read attempts return busy.
To get to a working state again, the controller needs to be reset to clear the START_BUSY signal. So check for START_BUSY still asserted on a timeout, and do reset in case it is,
This is also done by the original non-upstream iproc-smbus driver implementation [1].
Works around situations like:
bcm-iproc-2c 1803b000.i2c: transaction timed out
bcm-iproc-2c 1803b000.i2c: bus is busy
bcm-iproc-2c 1803b000.i2c: bus is busy
bcm-iproc-2c 1803b000.i2c: bus is busy
bcm-iproc-2c 1803b000.i2c: bus is busy
bcm-iproc-2c 1803b000.i2c: bus is busy
...where the bus never recovers after a timeout.
--- drivers/i2c/busses/i2c-bcm-iproc.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
drivers/i2c/busses/i2c-bcm-iproc.c +11 -0
@@ -789,6 +789,17 @@ static int bcm_iproc_i2c_xfer_wait(struc }if (!time_left && !iproc_i2c->xfer_is_done) { +/* +* The controller may fail to clear START_BUSY after a timeout, +* reset the controller to recover in that case. +*/ +if (!!(iproc_i2c_rd_reg (iproc_i2c,M_CMD_OFFSET ) & +BIT (M_CMD_START_BUSY_SHIFT ))) { +bcm_iproc_i2c_enable_disable (iproc_i2c, false); +bcm_iproc_i2c_init (iproc_i2c); +bcm_iproc_i2c_enable_disable (iproc_i2c, true); + } +/* flush both TX/RX FIFOs */ val =BIT (M_FIFO_RX_FLUSH_SHIFT ) |BIT (M_FIFO_TX_FLUSH_SHIFT );iproc_i2c_wr_reg (iproc_i2c,M_FIFO_CTRL_OFFSET , val);
Greg Kroah-Hartman
maintainerPATCH 204/235 fsnotify: opt-in for permission events at file open time
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Amir Goldstein <[email protected]>
[ Upstream commit a94204f4d48e28a711b7ed10399f749286c433e3 ]
Legacy inotify/fanotify listeners can add watches for events on inode, parent or mount and expect to get events (e.g. FS_MODIFY) on files that were already open at the time of setting up the watches.
fanotify permission events are typically used by Anti-malware sofware, that is watching the entire mount and it is not common to have more that one Anti-malware engine installed on a system.
To reduce the overhead of the fsnotify_file_perm() hooks on every file access, relax the semantics of the legacy FAN_ACCESS_PERM event to generate events only if there were *any* permission event listeners on the filesystem at the time that the file was opened.
The new semantic is implemented by extending the FMODE_NONOTIFY bit into two FMODE_NONOTIFY_* bits, that are used to store a mode for which of the events types to report.
This is going to apply to the new fanotify pre-content events in order to reduce the cost of the new pre-content event vfs hooks.
[Thanks to Bert Karwatzki <[email protected]> for reporting a bug in this code with CONFIG_FANOTIFY_ACCESS_PERMISSIONS disabled]
Stable-dep-of: db1856ea9196 ("binfmt_misc: restore write access when removing an entry")
--- include/linux/fs.h | 6 ++++++ 1 file changed, 6 insertions(+)
include/linux/fs.h +6 -0
@@ -179,6 +179,12 @@ typedef int (dio_iodone_t)(struct kiocb#define FMODE_BUF_RASYNC ((__force fmode_t)0x40000000)/* +* fsnotify pre-content events do not exist in this kernel, so a file is never +* watched by a pre-content event listener. +*/ +#define FMODE_FSNOTIFY_HSM (mode) 0 + +/* * Attribute flags. These should be or-ed together to figure out what * has been changed! */
Greg Kroah-Hartman
maintainerPATCH 205/235 fs: dont block write during exec on pre-content watched files
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Amir Goldstein <[email protected]>
[ Upstream commit 0357ef03c94ef835bd44a0658b8edb672a9dbf51 ]
Commit 2a010c412853 ("fs: don't block i_writecount during exec") removed the legacy behavior of getting ETXTBSY on attempt to open and executable file for write while it is being executed.
This commit was reverted because an application that depends on this legacy behavior was broken by the change.
We need to allow HSM writing into executable files while executed to fill their content on-the-fly.
To that end, disable the ETXTBSY legacy behavior for files that are watched by pre-content events.
This change is not expected to cause regressions with existing systems which do not have any pre-content event listeners.
Stable-dep-of: db1856ea9196 ("binfmt_misc: restore write access when removing an entry")
--- fs/binfmt_elf.c | 4 ++-- fs/binfmt_elf_fdpic.c | 4 ++-- fs/exec.c | 6 +++--- include/linux/fs.h | 22 ++++++++++++++++++++++ 4 files changed, 29 insertions(+), 7 deletions(-)
fs/binfmt_elf.c +2 -2
@@ -1243,7 +1243,7 @@ out_free_interp: } reloc_func_desc = interp_load_addr; -allow_write_access (interpreter); +exe_file_allow_write_access (interpreter);fput (interpreter);kfree (interp_elf_ex);
@@ -1332,7 +1332,7 @@ out_free_dentry:kfree (interp_elf_ex);kfree (interp_elf_phdata); out_free_file: -allow_write_access (interpreter); +exe_file_allow_write_access (interpreter);if (interpreter)fput (interpreter); out_free_ph:
fs/binfmt_elf_fdpic.c +2 -2
@@ -398,7 +398,7 @@ static int load_elf_fdpic_binary(struct goto error; } -allow_write_access (interpreter); +exe_file_allow_write_access (interpreter);fput (interpreter); interpreter =NULL ; }
@@ -471,7 +471,7 @@ static int load_elf_fdpic_binary(struct error:if (interpreter) { -allow_write_access (interpreter); +exe_file_allow_write_access (interpreter);fput (interpreter); }kfree (interpreter_name);
fs/exec.c +3 -3
@@ -929,7 +929,7 @@ static struct file *do_open_execat(int f path_noexec(&file->f_path)) goto exit; - err =deny_write_access (file); + err =exe_file_deny_write_access (file);if (err) goto exit;
@@ -1485,7 +1485,7 @@ static void free_bprm(struct linux_binprabort_creds (bprm->cred); }if (bprm->file) { -allow_write_access (bprm->file); +exe_file_allow_write_access (bprm->file);fput (bprm->file); }if (bprm->executable)
@@ -1773,7 +1773,7 @@ static int exec_binprm(struct linux_binp bprm->file = bprm->interpreter; bprm->interpreter =NULL ; -allow_write_access (exec); +exe_file_allow_write_access (exec);if (unlikely (bprm->have_execfd)) {if (bprm->executable) {fput (exec);
include/linux/fs.h +22 -0
@@ -2987,6 +2987,28 @@ static inline void allow_write_access(stif (file)atomic_inc (&file_inode (file)->i_writecount); } + +/* +* Do not prevent write to executable file when watched by pre-content events. +* +* Note that FMODE_FSNOTIFY_HSM mode is set depending on pre-content watches at +* the time of file open and remains constant for entire lifetime of the file, +* so if pre-content watches are added post execution or removed before the end +* of the execution, it will not cause i_writecount reference leak. +*/ +static inline int exe_file_deny_write_access (struct file *exe_file) +{ +if (unlikely (FMODE_FSNOTIFY_HSM (exe_file->f_mode))) +return 0 ; +return deny_write_access (exe_file); +} +static inline void exe_file_allow_write_access (struct file *exe_file) +{ +if (unlikely (!exe_file ||FMODE_FSNOTIFY_HSM (exe_file->f_mode))) +return ; +allow_write_access (exe_file); +} +static inline bool inode_is_open_for_write (const struct inode *inode) {return atomic_read (&inode->i_writecount) >0 ;
Greg Kroah-Hartman
maintainerPATCH 206/235 binfmt_misc: restore write access when removing an entry
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
[ Upstream commit db1856ea9196cf6e015d12199a34c0b9313c7bfa ]
Registering an entry with the MISC_FMT_OPEN_FILE flag opens the interpreter via open_exec() which denies write access to it for as long as the entry exists. Removing the entry closes the interpreter file via filp_close() but never restores write access, leaving the inode's i_writecount permanently negative. Opening the interpreter for writing keeps failing with ETXTBSY long after the entry is gone until the inode is evicted from the inode cache.
Commit 90f601b497d7 ("binfmt_misc: restore write access before closing files opened by open_exec()") fixed the same imbalance in the error path of bm_register_write() but the actual removal path has been leaking the write denial since the introduction of the flag.
Restore write access in put_binfmt_handler() before closing the interpreter file.
--- fs/binfmt_misc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/binfmt_misc.c +3 -1
@@ -163,8 +163,10 @@ static Node *get_binfmt_handler(struct lstatic void put_binfmt_handler (Node *e) {if (refcount_dec_and_test (&e->users)) { -if (e->flags &MISC_FMT_OPEN_FILE ) +if (e->flags &MISC_FMT_OPEN_FILE ) { +exe_file_allow_write_access (e->interp_file);filp_close (e->interp_file,NULL ); + }kfree (e); } }
Greg Kroah-Hartman
maintainerPATCH 207/235 can: gs_usb: gs_usb_receive_bulk_callback(): resubmit URB on skb allocation failure
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marc Kleine-Budde <[email protected]>
[ Upstream commit 68c5724ecd159992f76edb7b57dc508a44c8b7da ]
If the allocation of the SKB in gs_usb_receive_bulk_callback() fails, the driver returns from the callback without resubmitting the URB in order to receive further USB in URBs.
This results in a silent performance degradation which, if it occurs repeatedly, results in starvation of USB in traffic.
Instead of returning immediately, try to resend the URB. If this also fails, this is logged as an info message.
--- drivers/net/can/usb/gs_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/can/usb/gs_usb.c +1 -1
@@ -332,7 +332,7 @@ static void gs_usb_receive_bulk_callbackif (hf->echo_id ==-1 ) {/* normal rx */ skb =alloc_can_skb (dev->netdev, &cf);if (!skb) -return ; + goto resubmit_urb; cf->can_id =le32_to_cpu (hf->can_id);
Greg Kroah-Hartman
maintainerPATCH 208/235 hwmon: (npcm750-pwm-fan): stop fan timer on device detach
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hongyan Xu <[email protected]>
[ Upstream commit f27f6976ea269219c1259a7c2f8c6dfe782540a3 ]
When a fan tach channel is present, npcm7xx_pwm_fan_probe() starts fan_timer. The timer callback polls tach state and rearms the timer, but the driver has no remove callback or devm cleanup action to stop it. On device detach, the devm-managed driver data and I/O mappings can be released while the timer is still pending or running.
Register a devm cleanup action before starting the timer and shut the timer down synchronously from that action.
This issue was found by a static analysis tool.
--- drivers/hwmon/npcm750-pwm-fan.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
drivers/hwmon/npcm750-pwm-fan.c +11 -0
@@ -360,6 +360,11 @@ static void npcm7xx_fan_polling(struct tadd_timer (&data->fan_timer); } +static void npcm7xx_fan_cleanup (void *timer) +{ +timer_delete_sync (timer); +} +static inline void npcm7xx_fan_compute (struct npcm7xx_pwm_fan_data *data,u8 fan,u8 cmp,u8 fan_id,u8 flag_int,u8 flag_mode,u8 flag_clear)
@@ -1003,6 +1008,12 @@ static int npcm7xx_pwm_fan_probe(structmsecs_to_jiffies (NPCM7XX_FAN_POLL_TIMER_200MS );timer_setup (&data->fan_timer, npcm7xx_fan_polling,0 ); + ret =devm_add_action_or_reset (dev, + npcm7xx_fan_cleanup, + &data->fan_timer); +if (ret) +return ret; +add_timer (&data->fan_timer);break ; }
Greg Kroah-Hartman
maintainerPATCH 209/235 drm/amd/pm: fix torn gpu metrics reads
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yang Wang <[email protected]>
[ Upstream commit 048f4541b71fb19645fb79d6e62e6e4da23a4035 ]
amdgpu_dpm_get_gpu_metrics() returns a pointer to the shared metrics cache after dropping adev->pm.mutex. The sysfs path then copies from that pointer. Another reader can refresh the cache in place during the copy and return a snapshot containing data from two generations.
Pass caller-provided storage through the DPM interface and copy the metrics while the mutex is held. This keeps the cache pointer private and makes each sysfs read observe one complete sample.
--- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)
drivers/gpu/drm/amd/pm/amdgpu_pm.c +6 -8
@@ -1994,20 +1994,18 @@ static ssize_t amdgpu_get_gpu_metrics(streturn ret; } +mutex_lock (&adev->pm.mutex);if (is_support_sw_smu (adev)) size =smu_sys_get_gpu_metrics (&adev->smu, &gpu_metrics);else if (adev->powerplay.pp_funcs->get_gpu_metrics) size =amdgpu_dpm_get_gpu_metrics (adev, &gpu_metrics); -if (size <=0 ) - goto out; - -if (size >=PAGE_SIZE ) - size =PAGE_SIZE -1 ; - -memcpy (buf, gpu_metrics, size); +if (size >0 ) { + size =min_t (ssize_t, size,PAGE_SIZE -1 ); +memcpy (buf, gpu_metrics, size); + } +mutex_unlock (&adev->pm.mutex); -out:pm_runtime_mark_last_busy (ddev->dev);pm_runtime_put_autosuspend (ddev->dev);
Greg Kroah-Hartman
maintainerPATCH 210/235 ALSA: usx2y: Fix potential leaks of uninitialized memory
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
[ Upstream commit 4e268db74770b454b877ab5260f1868a457d212c ]
usx2y drivers may expose the allocated pages via mmap, but it performs zero-clear only for the struct size, not aligned with the page size. This leaves out some uninitialized trailing bytes.
This patch fixes the clearance to cover all memory that are exposed to user-space.
Stable-dep-of: 2ca1eea3cd17 ("ALSA: usx2y: bound the hwdep mmap fault offset")
--- sound/usb/usx2y/usX2Yhwdep.c | 8 ++++---- sound/usb/usx2y/usbus428ctldefs.h | 2 ++ sound/usb/usx2y/usbusx2y.c | 2 +- sound/usb/usx2y/usx2yhwdeppcm.c | 13 ++++++++----- 4 files changed, 15 insertions(+), 10 deletions(-)
sound/usb/usx2y/usX2Yhwdep.c +4 -4
@@ -55,17 +55,17 @@ static int snd_us428ctls_mmap(struct sndreturn -EBUSY ;/* if userspace tries to mmap beyond end of our buffer, fail */ -if (size >PAGE_ALIGN (sizeof (struct us428ctls_sharedmem ))) { -snd_printd ("%lu > %lu\n" , size, (unsigned long )sizeof (struct us428ctls_sharedmem )); +if (size >US428_SHAREDMEM_PAGES ) { +snd_printd ("%lu > %lu\n" , size, (unsigned long )US428_SHAREDMEM_PAGES );return -EINVAL ; }if (!us428->us428ctls_sharedmem) {init_waitqueue_head (&us428->us428ctls_wait_queue_head); - us428->us428ctls_sharedmem =alloc_pages_exact (sizeof (struct us428ctls_sharedmem ),GFP_KERNEL ); + us428->us428ctls_sharedmem =alloc_pages_exact (US428_SHAREDMEM_PAGES ,GFP_KERNEL );if (!us428->us428ctls_sharedmem)return -ENOMEM ; -memset (us428->us428ctls_sharedmem,-1 ,sizeof (struct us428ctls_sharedmem )); +memset (us428->us428ctls_sharedmem,-1 ,US428_SHAREDMEM_PAGES ); us428->us428ctls_sharedmem->ctl_snapshot_last =-2 ; } area->vm_ops = &us428ctls_vm_ops;
sound/usb/usx2y/usbus428ctldefs.h +2 -0
@@ -89,3 +89,5 @@ struct us428ctls_sharedmem {struct us428_p4out p4out[N_US428_P4OUT_BUFS ];int p4out_last, p4out_sent; }; + +#define US428_SHAREDMEM_PAGES PAGE_ALIGN(sizeof(struct us428ctls_sharedmem))
sound/usb/usx2y/usbusx2y.c +1 -1
@@ -376,7 +376,7 @@ static void snd_usx2y_card_private_free(usb_free_urb (usx2y->in04_urb);if (usx2y->us428ctls_sharedmem)free_pages_exact (usx2y->us428ctls_sharedmem, -sizeof (*usx2y->us428ctls_sharedmem)); +US428_SHAREDMEM_PAGES );if (usx2y->card_index >=0 && usx2y->card_index <SNDRV_CARDS ) snd_usx2y_card_used[usx2y->card_index] =0 ; }
sound/usb/usx2y/usx2yhwdeppcm.c +8 -5
@@ -485,6 +485,9 @@ static int usx2y_usbpcm_urbs_start(strucreturn err; } +#define USX2Y_HWDEP_PCM_PAGES \ + PAGE_ALIGN(sizeof(struct snd_usx2y_hwdep_pcm_shm)) +/* * prepare callback *
@@ -501,11 +504,11 @@ static int snd_usx2y_usbpcm_prepare(strusnd_printdd ("snd_usx2y_pcm_prepare(%p)\n" , substream);if (!usx2y->hwdep_pcm_shm) { - usx2y->hwdep_pcm_shm =alloc_pages_exact (sizeof (struct snd_usx2y_hwdep_pcm_shm ), + usx2y->hwdep_pcm_shm =alloc_pages_exact (USX2Y_HWDEP_PCM_PAGES ,GFP_KERNEL );if (!usx2y->hwdep_pcm_shm)return -ENOMEM ; -memset (usx2y->hwdep_pcm_shm,0 ,sizeof (struct snd_usx2y_hwdep_pcm_shm )); +memset (usx2y->hwdep_pcm_shm,0 ,USX2Y_HWDEP_PCM_PAGES ); }mutex_lock (&usx2y->pcm_mutex);
@@ -692,8 +695,8 @@ static int snd_usx2y_hwdep_pcm_mmap(strureturn -EBUSY ;/* if userspace tries to mmap beyond end of our buffer, fail */ -if (size >PAGE_ALIGN (sizeof (struct snd_usx2y_hwdep_pcm_shm ))) { -snd_printd ("%lu > %lu\n" , size, (unsigned long )sizeof (struct snd_usx2y_hwdep_pcm_shm )); +if (size >USX2Y_HWDEP_PCM_PAGES ) { +snd_printd ("%lu > %lu\n" , size, (unsigned long )USX2Y_HWDEP_PCM_PAGES );return -EINVAL ; }
@@ -711,7 +714,7 @@ static void snd_usx2y_hwdep_pcm_private_struct usx2ydev *usx2y = hwdep->private_data;if (usx2y->hwdep_pcm_shm) -free_pages_exact (usx2y->hwdep_pcm_shm,sizeof (struct snd_usx2y_hwdep_pcm_shm )); +free_pages_exact (usx2y->hwdep_pcm_shm,USX2Y_HWDEP_PCM_PAGES ); }int usx2y_hwdep_pcm_new (struct snd_card *card)
Greg Kroah-Hartman
maintainerPATCH 211/235 ALSA: usx2y: bound the hwdep mmap fault offset
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baul Lee <[email protected]>
[ Upstream commit 2ca1eea3cd17930daffe9e429a7c89232036ec24 ]
snd_us428ctls_vm_fault() turns the faulting page offset into a kernel address with no bound of any kind:
offset = vmf->pgoff << PAGE_SHIFT; vaddr = (char *)(...)->us428ctls_sharedmem + offset; page = virt_to_page(vaddr); get_page(page); vmf->page = page; return 0;
snd_us428ctls_mmap() checks only the length of the mapping, never the offset, and us428ctls_sharedmem is a single page from alloc_pages_exact(). For a character device file_mmap_size_max() returns ULONG_MAX, so the mm layer imposes no ceiling either. Every page offset above zero resolves to a struct page outside the object, and the handler installs it into the caller's address space read-write; the vma is not marked read-only.
The caller picks the page frame with a single mmap() argument and gets read-write access to a page of kernel memory it does not own; an offset that lands in an unpopulated vmemmap region oopses instead.
A process that can open the hwdep node of an attached US-X2Y reaches this after loading the FPGA image through the same node; no capability check is involved.
On 7.2.0-rc5 (arm64), mmap() with a large offset:
Unable to handle kernel paging request at virtual address fffffdffc45d5ac8 pc : snd_us428ctls_vm_fault+0x68/0x140 [snd_usb_usx2y] Call trace: snd_us428ctls_vm_fault+0x68/0x140 [snd_usb_usx2y] __do_fault __handle_mm_fault handle_mm_fault el0_da
Reject any offset outside the shared region. The pcm hwdep handler in usx2yhwdeppcm.c computes its address the same way and needs the same bound.
Discovered by XBOW, triaged by Baul Lee <[email protected]>
--- sound/usb/usx2y/usX2Yhwdep.c | 2 ++ sound/usb/usx2y/usx2yhwdeppcm.c | 2 ++ 2 files changed, 4 insertions(+)
sound/usb/usx2y/usX2Yhwdep.c +2 -0
@@ -29,6 +29,8 @@ static vm_fault_t snd_us428ctls_vm_fault vmf->pgoff); offset = vmf->pgoff <<PAGE_SHIFT ; +if (offset >=US428_SHAREDMEM_PAGES ) +return VM_FAULT_SIGBUS ; vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->us428ctls_sharedmem + offset; page =virt_to_page (vaddr);get_page (page);
sound/usb/usx2y/usx2yhwdeppcm.c +2 -0
@@ -674,6 +674,8 @@ static vm_fault_t snd_usx2y_hwdep_pcm_vmvoid *vaddr; offset = vmf->pgoff <<PAGE_SHIFT ; +if (offset >=USX2Y_HWDEP_PCM_PAGES ) +return VM_FAULT_SIGBUS ; vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->hwdep_pcm_shm + offset; vmf->page =virt_to_page (vaddr);get_page (vmf->page);
Greg Kroah-Hartman
maintainerPATCH 212/235 sched/psi: Shut down rtpoll_timer in psi_cgroup_free()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejun Heo <[email protected]>
[ Upstream commit 5457025fa8ca3c0d2732109513de839e3e797190 ]
psi_schedule_rtpoll_work() is called locklessly from the scheduler hotpath and can race psi_trigger_destroy() taking down the last rtpoll trigger under rtpoll_trigger_lock:
psi_schedule_rtpoll_work() psi_trigger_destroy()
rcu_read_lock();
task = rcu_dereference(rtpoll_task);
rcu_assign_pointer(rtpoll_task, NULL);
timer_delete(&rtpoll_timer);
mod_timer(&rtpoll_timer, ...);
rcu_read_unlock();
synchronize_rcu();
kthread_stop(task_to_destroy);The group can then be freed with the re-armed timer still pending, and poll_timer_fn() runs on freed memory.
461daba06bdc ("psi: eliminate kthread_worker from psi trigger scheduling mechanism") deleted the timer synchronously after the synchronize_rcu(), which prevented this but raced trigger creation instead: the deletion could cancel the timer that a new trigger set armed during the grace period and, as creation also reinitialized the timer at the time, corrupt it. 8f91efd870ea ("psi: Fix race between psi_trigger_create/destroy") moved the initialization into group_init() and the deletion into the locked section, trading the creation races for the window above.
Neither placement in the destruction path works. A pending timer firing while the group is alive is harmless though. poll_timer_fn() just wakes the rtpoll waitqueue and doesn't re-arm itself. Bind the timer to the group's lifetime instead and shut it down in psi_cgroup_free(). Nothing can arm it by then. timer_shutdown_sync() because the timer is never armed again.
--- kernel/sched/psi.c | 6 ++++++ 1 file changed, 6 insertions(+)
kernel/sched/psi.c +6 -0
@@ -950,6 +950,12 @@ void psi_cgroup_free(struct cgroup *cgroreturn ;cancel_delayed_work_sync (&cgroup->psi.avgs_work); +/* +* A psi_schedule_poll_work() call racing the last trigger's +* destruction may have re-armed the timer after psi_trigger_destroy() +* deleted it. Spurious firing while the group is alive is harmless. +*/ +timer_delete_sync (&cgroup->psi.poll_timer);free_percpu (cgroup->psi.pcpu);/* All triggers must be removed by now */ WARN_ONCE (cgroup->psi.poll_states,"psi: trigger leak\n" );
Greg Kroah-Hartman
maintainerPATCH 213/235 net/sched: act_gact, act_police: range check the fallback control action
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hyunjung Ko <[email protected]>
[ Upstream commit 883b56ae58fe657d8497806c7059646e9ba6dbd0 ]
tcf_action_check_ctrlact() range checks the primary control action:
if (!opcode) ret = action > TC_ACT_VALUE_MAX ? -EINVAL : 0;
TC_ACT_VALUE_MAX is TC_ACT_TRAP, so kernel-internal verdicts above it cannot be set that way. But act_gact and act_police each carry a second, independent control action supplied by user space that never reaches that helper - TCA_GACT_PROB.paction and TCA_POLICE_RESULT. Both only reject TC_ACT_GOTO_CHAIN, so any other value is stored verbatim and returned verbatim from the action.
In particular user space can store TC_ACT_CONSUMED, which is TC_ACT_VALUE_MAX + 1 and is deliberately not part of the UAPI value range. That verdict tells every caller the action took ownership of the skb, so nobody frees it: sch_handle_ingress(), sch_handle_egress() and tcf_qevent_handle() all deliberately skip the free for it. The result is one leaked sk_buff plus its data buffer per packet traversing the filter, unbounded, for all traffic on the chain including kernel-generated packets.
Both are trivially deterministic. act_gact clamps tcfg_pval to >= 1, so with pval = 1 gact_determ() returns the fallback for every packet. act_police has no mandatory rate, so rate = 0 leaves tcfp_mtu = ~0 and tcf_police_mtu_check() always passes.
TC_ACT_CONSUMED was added by commit 720f22fed81b ("net: sched: refactor reinsert action"), after both goto-chain guards were written:
commit 9469f375ab09 ("net/sched: act_gact: disallow 'goto chain' on fallback control action") and
commit c08f5ed5d625 ("net/sched: act_police: disallow 'goto chain' on fallback control action"). Neither guard was widened when the new verdict appeared.
Factor the existing range test out of tcf_action_check_ctrlact() as tcf_action_valid() and apply it to both fallbacks. The helper cannot call tcf_action_check_ctrlact() directly because that also allocates a goto_chain, which is exactly what these two sites must not do.
Reproduced on v7.2-rc6: kmemleak reports one leaked 232-byte skbuff_head_cache object plus its 704-byte data buffer per packet. With this patch both configurations are rejected with -EINVAL and kmemleak reports none.
--- include/net/act_api.h | 20 ++++++++++++++++++++ net/sched/act_gact.c | 5 +++++ net/sched/act_police.c | 6 ++++++ 3 files changed, 31 insertions(+)
include/net/act_api.h +20 -0
@@ -240,6 +240,26 @@ int tcf_action_check_ctrlact(int action, struct netlink_ext_ack *newchain);struct tcf_chain *tcf_action_set_ctrlact (struct tc_action *a,int action,struct tcf_chain *newchain); + +/* Range check for a control action supplied by user space. +* +* This is the same test tcf_action_check_ctrlact() applies to the primary +* control action, factored out for the *fallback* control actions +* (act_gact's TCA_GACT_PROB.paction and act_police's TCA_POLICE_RESULT), +* which must not reach tcf_action_check_ctrlact() because they have no +* goto_chain to allocate. Without it, user space can store kernel-internal +* verdicts such as TC_ACT_CONSUMED, which is TC_ACT_VALUE_MAX + 1 and is +* deliberately not part of the UAPI value range. +*/ +static inline bool tcf_action_valid (int action) +{ +int opcode =TC_ACT_EXT_OPCODE (action); + +if (!opcode) +return action <=TC_ACT_VALUE_MAX ; +return opcode <=TC_ACT_EXT_OPCODE_MAX || action ==TC_ACT_UNSPEC ; +} +#endif /* CONFIG_NET_CLS_ACT */ static inline void tcf_action_stats_update(struct tc_action *a, u64 bytes,
net/sched/act_gact.c +5 -0
@@ -89,6 +89,11 @@ static int tcf_gact_init(struct net *net p_parm =nla_data (tb[TCA_GACT_PROB ]);if (p_parm->ptype >=MAX_RAND )return -EINVAL ; +if (!tcf_action_valid (p_parm->paction)) { +NL_SET_ERR_MSG (extack, +"invalid fallback control action" ); +return -EINVAL ; + }if (TC_ACT_EXT_CMP (p_parm->paction,TC_ACT_GOTO_CHAIN )) {NL_SET_ERR_MSG (extack,"goto chain not allowed on fallback" );
net/sched/act_police.c +6 -0
@@ -134,6 +134,12 @@ static int tcf_police_init(struct net *nif (tb[TCA_POLICE_RESULT ]) { tcfp_result =nla_get_u32 (tb[TCA_POLICE_RESULT ]); +if (!tcf_action_valid (tcfp_result)) { +NL_SET_ERR_MSG (extack, +"invalid fallback control action" ); + err = -EINVAL ; + goto failure; + }if (TC_ACT_EXT_CMP (tcfp_result,TC_ACT_GOTO_CHAIN )) {NL_SET_ERR_MSG (extack,"goto chain not allowed on fallback" );
Greg Kroah-Hartman
maintainerPATCH 214/235 mm/ptdump: always stabilise against page table freeing using init_mm
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: "Lorenzo Stoakes (ARM)" <[email protected]>
[ Upstream commit 27c32e5538344b13c1505a08861e04620c125d47 ]
Previous commits have established the invariant that kernel page table freeing is performed while an mmap read lock on init_mm is held, which fixes races between ptdump and kernel page table freeing over init_mm.
However, x86 and arm64 can perform a ptdump over an mm other than init_mm via ptdump_walk_pgd() and since kernel memory ranges are shared across non-kernel mm's, this means that the race still exists for these cases.
Fix this by acquiring a nested mmap write lock for init_mm in ptdump_walk_pgd().
This is safe as we take this after mmap write locking the mm, and nothing acquires the init_mm lock first before locking an arbitrary mm, so no deadlock is possible.
Also update walk_page_range_debug() to assert that init_mm is write locked, add a comment explaining why and remove some redundant code, and eliminate the unnecessary and confusing invocation of walk_kernel_page_table_range().
We can safely remove the non-NULL check for walk.mm, as the mmap lock asserts would NULL pointer deref if it was (and of course no callers do this).
The first point at which ptdump can race kernel page table freeing is commit b6bdb7517c3d ("mm/vmalloc: add interfaces to free unmapped page table"), so we target this in the Fixes tag.
--- mm/pagewalk.c | 8 ++++++++ mm/ptdump.c | 7 +++++++ 2 files changed, 15 insertions(+)
mm/pagewalk.c +8 -0
@@ -457,6 +457,14 @@ int walk_page_range_novma(struct mm_strureturn -EINVAL ;mmap_assert_write_locked (walk.mm); +/* +* x86, arm64 ptdump allow walks of efi mm's and x86 ptdump allows walks +* of arbitrary mm's. +* +* However, they both must also hold the init_mm lock to account for +* concurrent kernel page table freeing. +*/ +mmap_assert_write_locked (&init_mm);return walk_pgd_range (start, end, &walk); }
mm/ptdump.c +7 -0
@@ -143,11 +143,18 @@ void ptdump_walk_pgd(struct ptdump_stateget_online_mems ();mmap_write_lock (mm); +/* To stabilise kernel page tables we must hold the init_mm lock too. */ +if (mm != &init_mm) +mmap_write_lock_nested (&init_mm,SINGLE_DEPTH_NESTING ); +while (range->start != range->end) {walk_page_range_novma (mm, range->start, range->end, &ptdump_ops, pgd, st); range++; } + +if (mm != &init_mm) +mmap_write_unlock (&init_mm);mmap_write_unlock (mm);put_online_mems ();
Greg Kroah-Hartman
maintainerPATCH 215/235 net: atlantic: free stranded TX buffers on ring deinit
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yangyu Chen <[email protected]>
commit 452636ea5410a96e02ebaaf80b21e3620b98e0dd upstream.
aq_vec_deinit() drains the TX rings with a single aq_ring_tx_clean() call, which frees at most AQ_CFG_TX_CLEAN_BUDGET (256) descriptors and stops at hw_head, which no longer moves once aq_vec_stop() has stopped the hardware and NAPI. Completed descriptors beyond the budget and everything still posted in [hw_head, sw_tail) keep their skb or xdp_frame when the interface goes down: aq_vec_ring_free() then frees the buffer ring and the references are lost for good.
Today this is a silent memory leak on every interface down under TX/XDP_TX load. With the conversion of the RX path to page_pool posted for net-next it becomes much more visible: XDP_TX frames carry fragment references on the RX ring's page_pool, so a single stranded frame keeps the pool's inflight count above zero forever. page_pool_destroy() then never completes, the pool is leaked together with its pages, and "page_pool_release_retry() stalled pool shutdown" is warned every 60 seconds from that point on, on every ifdown, XDP detach or ring resize under XDP_TX load.
Bring back aq_ring_tx_deinit() as it was before the removal and use it for teardown again, with one extension: TX rings can hold xdp_frames nowadays, so release those too. They are returned with xdp_return_frame() since this runs in process context.
--- drivers/net/ethernet/aquantia/atlantic/aq_ring.c | 25 +++++++++++++++++++++++ drivers/net/ethernet/aquantia/atlantic/aq_ring.h | 1 drivers/net/ethernet/aquantia/atlantic/aq_vec.c | 2 - 3 files changed, 27 insertions(+), 1 deletion(-)
drivers/net/ethernet/aquantia/atlantic/aq_ring.c +25 -0
@@ -314,6 +314,31 @@ bool aq_ring_tx_clean(struct aq_ring_s *return !!budget; } +void aq_ring_tx_deinit (struct aq_ring_s *self) +{ +if (!self) +return ; + +for (; self->sw_head != self->sw_tail; + self->sw_head =aq_ring_next_dx (self, self->sw_head)) { +struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head]; +struct device *ndev =aq_nic_get_dev (self->aq_nic); + +if (buff->is_mapped) { +if (buff->is_sop) { +dma_unmap_single (ndev, buff->pa, buff->len, +DMA_TO_DEVICE ); + }else { +dma_unmap_page (ndev, buff->pa, buff->len, +DMA_TO_DEVICE ); + } + } + +if (buff->is_eop && buff->skb) +dev_kfree_skb_any (buff->skb); + } +} +static void aq_rx_checksum (struct aq_ring_s *self,struct aq_ring_buff_s *buff,struct sk_buff *skb)
drivers/net/ethernet/aquantia/atlantic/aq_ring.h +1 -0
@@ -182,6 +182,7 @@ void aq_ring_update_queue_state(struct avoid aq_ring_queue_wake (struct aq_ring_s *ring);void aq_ring_queue_stop (struct aq_ring_s *ring);bool aq_ring_tx_clean (struct aq_ring_s *self); +void aq_ring_tx_deinit (struct aq_ring_s *self); int aq_ring_rx_clean(struct aq_ring_s *self, struct napi_struct *napi, int *work_done,
drivers/net/ethernet/aquantia/atlantic/aq_vec.c +1 -1
@@ -270,7 +270,7 @@ void aq_vec_deinit(struct aq_vec_s *selffor (i =0U ; self->tx_rings > i; ++i) { ring = self->ring[i]; -aq_ring_tx_clean (&ring[AQ_VEC_TX_ID ]); +aq_ring_tx_deinit (&ring[AQ_VEC_TX_ID ]);aq_ring_rx_deinit (&ring[AQ_VEC_RX_ID ]); }
Greg Kroah-Hartman
maintainerPATCH 216/235 arm64: tegra: Add EL2 virtual timer interrupt for Tegra194
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jon Hunter <[email protected]>
[ Upstream commit a7c28483fd57dd0e1487024af70622315320774b ]
Commit d87773de9efe ("clocksource/drivers/arm_arch_timer: Default to EL2 virtual timer when running VHE") updated the ARM arch timer driver to use the virtual timer by default if the CPU is running at EL2 with VHE enabled. If the CPU is running at EL2 with VHE enabled but there is no interrupt provided for the virtual timer, then the following warning is displayed:
arch_timer: [Firmware Bug]: VHE-capable CPU without EL2 virtual timer
interrupt
This warning is observed on Tegra194 platforms. Tegra194 SoC includes NVIDIA Carmel ARM v8.2 CPUs and support an EL2 virtual timer. Fix the above warning by adding the PPI for the EL2 virtual timer interrupt for Tegra194.
--- arch/arm64/boot/dts/nvidia/tegra194.dtsi | 2 ++ 1 file changed, 2 insertions(+)
arch/arm64/boot/dts/nvidia/tegra194.dtsi +2 -0
@@ -2375,6 +2375,8 @@ timer { <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>, <GIC_PPI 10 + (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>, + <GIC_PPI 15 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>; interrupt-parent = <&gic>; always-on;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 217/235 crypto: ccm - Set rfc4309 maxauthsize from child
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Herbert Xu <[email protected]>
[ Upstream commit 438f4896f78f69ec73d5f32d2c024193f1223569 ]
Set the maxauthsize of rfc4309 using that of the child algorithm.
--- crypto/ccm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
crypto/ccm.c +1 -1
@@ -752,7 +752,7 @@ static int crypto_rfc4309_create(struct crypto_template *tmpl, inst->alg.ivsize =8 ; inst->alg.chunksize =crypto_aead_alg_chunksize (alg); - inst->alg.maxauthsize =16 ; + inst->alg.maxauthsize =crypto_aead_alg_maxauthsize (alg); inst->alg.base.cra_ctxsize =sizeof (struct crypto_rfc4309_ctx );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 218/235 netfilter: ipset: fix refcount race between list:set GC and swap
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei (Microsoft) <[email protected]>
[ Upstream commit 0c88868271653537ed443272dd8e7d13634d214b ]
__ip_set_put_byindex() resolved the index to a set pointer under RCU, then took ip_set_ref_lock in __ip_set_put() to decrement set->ref. ip_set_swap() holds that same lock while swapping both the ip_set_list slots and the two sets' ref counters, so it can interleave between the dereference and the lock acquisition, leaving the caller to decrement a set whose reference already moved to the other index and hit BUG_ON(set->ref == 0). list_set_gc() reaches this from timer softirq, which the nfnl mutex does not serialize against swap: an expiring list:set member calls list_set_del() -> ip_set_put_byindex() while IPSET_CMD_SWAP runs on the referenced sets.
Resolve the index and decrement under ip_set_ref_lock, as ip_set_swap() already does, keeping the refcount tied to the index rather than to a stale set pointer.
kernel BUG at net/netfilter/ipset/ip_set_core.c:685!
Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI RIP: 0010:ip_set_put_byindex (net/netfilter/ipset/ip_set_core.c:870) Call Trace: <IRQ> list_set_del (net/netfilter/ipset/ip_set_list_set.c:159) set_cleanup_entries (net/netfilter/ipset/ip_set_list_set.c:181) list_set_gc (net/netfilter/ipset/ip_set_list_set.c:578) call_timer_fn (kernel/time/timer.c:1748) __run_timers (kernel/time/timer.c:1799 kernel/time/timer.c:2374) run_timer_softirq (kernel/time/timer.c:2405) </IRQ> Kernel panic - not syncing: Fatal exception in interrupt
--- net/netfilter/ipset/ip_set_core.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-)
net/netfilter/ipset/ip_set_core.c +13 -6
@@ -679,11 +679,18 @@ __ip_set_get(struct ip_set *set) }static void -__ip_set_put (struct ip_set *set) +__ip_set_put_locked (struct ip_set *set) { -write_lock_bh (&ip_set_ref_lock); +lockdep_assert_held (&ip_set_ref_lock);BUG_ON (set->ref ==0 ); set->ref--; +} + +static void +__ip_set_put (struct ip_set *set) +{ +write_lock_bh (&ip_set_ref_lock); +__ip_set_put_locked (set);write_unlock_bh (&ip_set_ref_lock); }
@@ -856,11 +863,11 @@ __ip_set_put_byindex(struct ip_set_net *inst, ip_set_id_t index) {struct ip_set *set; -rcu_read_lock (); - set =rcu_dereference (inst->ip_set_list)[index]; +write_lock_bh (&ip_set_ref_lock); + set =ip_set (inst, index);if (set) -__ip_set_put (set); -rcu_read_unlock (); +__ip_set_put_locked (set); +write_unlock_bh (&ip_set_ref_lock); }void
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 219/235 netfilter: nf_tables_offload: suppress WARN_ON_ONCE for ENOMEM in abort path
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexey Velichayshiy <[email protected]>
[ Upstream commit d02f592064347e0c1e0d84f24941ad338838cc48 ]
In nft_flow_rule_offload_abort(), WARN_ON_ONCE(err) is triggered on every error during rollback, including -ENOMEM. Memory allocation failures are expected under low-memory conditions and do not indicate a kernel bug.
Trace for example:nft_flow_offload_chain() // FLOW_BLOCK_BIND
nft_flow_block_chain()
nft_chain_offload_cmd()
nft_block_offload_cmd()
->ndo_setup_tc()
nsim_setup_tc()
flow_block_cb_setup_simple()
flow_block_cb_alloc() // fails to -ENOMEMThe warning was reproduced on the 5.10 stable kernel under memory pressure via fault injection, but the underlying bug exists in mainline as well, as demonstrated by the ENOMEM trace above. The following splat was triggered during nf_tables transaction processing:
WARNING: CPU: 0 PID: 8567 at net/netfilter/nf_tables_offload.c:532 nft_flow_rule_offload_abort net/netfilter/nf_tables_offload.c:532 [inline] WARNING: CPU: 0 PID: 8567 at net/netfilter/nf_tables_offload.c:532 nft_flow_rule_offload_commit+0x971/0xcd0 net/netfilter/nf_tables_offload.c:591 Modules linked in: CPU: 0 PID: 8567 Comm: syz-executor.0 Not tainted 5.10.260-syzkaller #0 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 RIP: 0010:nft_flow_rule_offload_abort net/netfilter/nf_tables_offload.c:532 [inline] RIP: 0010:nft_flow_rule_offload_commit+0x971/0xcd0 net/netfilter/nf_tables_offload.c:591 Call Trace: nf_tables_commit+0x3bd/0x4bd0 net/netfilter/nf_tables_api.c:8604 nfnetlink_rcv_batch+0xb1e/0x1f20 net/netfilter/nfnetlink.c:509 nfnetlink_rcv_skb_batch net/netfilter/nfnetlink.c:579 [inline] nfnetlink_rcv+0x3b3/0x420 net/netfilter/nfnetlink.c:597 netlink_unicast_kernel net/netlink/af_netlink.c:1314 [inline] netlink_unicast+0x6cd/0xa00 net/netfilter/af_netlink.c:1340 netlink_sendmsg+0x906/0xe10 net/netfilter/af_netlink.c:1919 sock_sendmsg_nosec net/socket.c:651 [inline] __sock_sendmsg+0x155/0x190 net/socket.c:663 ____sys_sendmsg+0x705/0x870 net/socket.c:2379 ___sys_sendmsg+0x100/0x170 net/socket.c:2433 __sys_sendmsg+0xe9/0x1c0 net/socket.c:2462 do_syscall_64+0x33/0x40 arch/x86/entry/common.c:46 entry_SYSCALL_64_after_hwframe+0x67/0xd1
Change the condition to WARN_ON_ONCE(err && err != -ENOMEM) so that warnings are only emitted for unexpected errors. This aligns with the common kernel practice of not warning on -ENOMEM.
Found by Linux Verification Center (linuxtesting.org) with Syzkaller.
--- net/netfilter/nf_tables_offload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/netfilter/nf_tables_offload.c +1 -1
@@ -522,7 +522,7 @@ static void nft_flow_rule_offload_abort(struct net *net,break ; } -if (WARN_ON_ONCE (err)) +if (WARN_ON_ONCE (err && err != -ENOMEM ))break ; } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 220/235 netfilter: flowtable: publish GC-visible tuple last
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jérémy Jean <[email protected]>
[ Upstream commit 2014ac62df9d45bb9a004a043e85df7be09ed780 ]
nf_flow_table_iterate() only treats original-direction tuple nodes as owning entries. Publishing the original node first lets GC observe and free a flow while flow_offload_add() is still inserting the reply node. Publish the reply node first and the original node last so GC never sees a partially installed flow.
KASAN can trigger slab-use-after-free read and write reports in the flowtable/rhashtable path (rht_deferred_worker, jhash, flow_offload_del, flow_offload_lookup, etc.).
--- net/netfilter/nf_flow_table_core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
net/netfilter/nf_flow_table_core.c +4 -3
@@ -228,17 +228,18 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow) flow->timeout = nf_flowtable_time_stamp +NF_FLOW_TIMEOUT ; err =rhashtable_insert_fast (&flow_table->rhashtable, - &flow->tuplehash[0 ].node, + &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY ].node, nf_flow_offload_rhash_params);if (err <0 )return err; +/* GC only iterates original-direction entries; publish original last. */ err =rhashtable_insert_fast (&flow_table->rhashtable, - &flow->tuplehash[1 ].node, + &flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL ].node, nf_flow_offload_rhash_params);if (err <0 ) {rhashtable_remove_fast (&flow_table->rhashtable, - &flow->tuplehash[0 ].node, + &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY ].node, nf_flow_offload_rhash_params);return err; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 221/235 netfilter: ipset: fix list type element drift bug
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit 4cbd69766b35a089664cadb1f613bb85f7ef77a9 ]
If list_set_uadd() calls list_set_replace() to swap an expired entry, the element count remains the same, therefore the increment must be elided.
--- net/netfilter/ipset/ip_set_list_set.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
net/netfilter/ipset/ip_set_list_set.c +5 -2
@@ -301,9 +301,12 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext, e->set = set;INIT_LIST_HEAD (&e->list);list_set_init_extensions (set, ext, e); -if (n) +if (n) {list_set_replace (set, e, n); -else if (next) +return 0 ; + } + +if (next)list_add_tail_rcu (&e->list, &next->list);else if (prev)list_add_rcu (&e->list, &prev->list);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 222/235 net: packet: fix wrong transport_header when sending VLAN-tagged frame
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wei Fang <[email protected]>
[ Upstream commit 01fdecc0480d916c799dbee584833a4a37e94d06 ]
In packet_parse_headers(), when processing a VLAN-tagged frame, skb_set_network_header() is called to advance network_header past the VLAN tag to the inner protocol header. skb_probe_transport_header() is then called with skb->protocol still set to the outer VLAN EtherType (e.g. ETH_P_8021Q), while nhoff (derived from skb_network_offset()) already points past the VLAN tag to the inner protocol header.
In __skb_flow_dissect(), proto is initialized to ETH_P_8021Q and nhoff points past the VLAN tag. When the dissector hits case ETH_P_8021Q, it reads a struct vlan_hdr at nhoff via __skb_header_pointer(), but that offset contains the inner protocol header (e.g. an IP header). The bytes are misinterpreted as a VLAN header, yielding a garbage encapsulated EtherType that matches no known protocol. The dissector returns false, so skb_probe_transport_header() never calls skb_set_transport_header(), leaving transport_header at its uninitialized sentinel value (~0U).
Move skb_probe_transport_header() to before skb_set_network_header(). At the time skb_probe_transport_header() is called, network_header still points to the VLAN header, so nhoff correctly points to the VLAN header. The flow dissector can then parse the VLAN header, extract the inner EtherType, and advance nhoff to the inner protocol header, allowing transport_header to be set correctly.
--- net/packet/af_packet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/packet/af_packet.c +2 -2
@@ -1945,13 +1945,13 @@ static void packet_parse_headers(struct sk_buff *skb, struct socket *sock) sock->type == SOCK_RAW) skb->protocol =dev_parse_header_protocol (skb); +skb_probe_transport_header (skb); +/* Move network header to the right position for VLAN tagged packets */ if (likely (skb->dev->type ==ARPHRD_ETHER ) &ð_type_vlan (skb->protocol) &&vlan_get_protocol_and_depth (skb, skb->protocol, &depth) !=0 )skb_set_network_header (skb, depth); - -skb_probe_transport_header (skb); }/*
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 223/235 ASoC: xilinx: formatter_pcm: pass aud_drv_data to irq handlers
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rosen Penev <[email protected]>
[ Upstream commit f12afefb7b01f94d6d66d397f323a9914edbf70e ]
The irq handlers take a struct device pointer and call dev_get_drvdata() to obtain the driver data. However, the driver data is only set at the end of probe, after devm_request_irq(), so an interrupt taken in between causes the handlers to pass a NULL pointer to readl() and crash.
Pass the private data directly as the devm_request_irq() argument instead of the device pointer, matching what the handlers expect.
--- sound/soc/xilinx/xlnx_formatter_pcm.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-)
sound/soc/xilinx/xlnx_formatter_pcm.c +4 -6
@@ -281,8 +281,7 @@ static irqreturn_t xlnx_mm2s_irq_handler(int irq, void *arg) {u32 val;void __iomem *reg; -struct device *dev = arg; -struct xlnx_pcm_drv_data *adata =dev_get_drvdata (dev); +struct xlnx_pcm_drv_data *adata = arg; reg = adata->mmio +XLNX_MM2S_OFFSET +XLNX_AUD_STS ; val =readl (reg);
@@ -300,8 +299,7 @@ static irqreturn_t xlnx_s2mm_irq_handler(int irq, void *arg) {u32 val;void __iomem *reg; -struct device *dev = arg; -struct xlnx_pcm_drv_data *adata =dev_get_drvdata (dev); +struct xlnx_pcm_drv_data *adata = arg; reg = adata->mmio +XLNX_S2MM_OFFSET +XLNX_AUD_STS ; val =readl (reg);
@@ -637,7 +635,7 @@ static int xlnx_formatter_pcm_probe(struct platform_device *pdev) } ret =devm_request_irq (dev, aud_drv_data->mm2s_irq, xlnx_mm2s_irq_handler,0 , -"xlnx_formatter_pcm_mm2s_irq" , dev); +"xlnx_formatter_pcm_mm2s_irq" , aud_drv_data);if (ret) {dev_err (dev,"xlnx audio mm2s irq request failed\n" ); goto clk_err;
@@ -664,7 +662,7 @@ static int xlnx_formatter_pcm_probe(struct platform_device *pdev) ret =devm_request_irq (dev, aud_drv_data->s2mm_irq, xlnx_s2mm_irq_handler,0 ,"xlnx_formatter_pcm_s2mm_irq" , - dev); + aud_drv_data);if (ret) {dev_err (dev,"xlnx audio s2mm irq request failed\n" ); goto clk_err;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 224/235 af_packet: Dont send zero-byte data in tpacket_snd().
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 6bcd76c134c55c697148acb5c0194e9666abdf84 ]
syzbot reported a WARNING in __dev_queue_xmit() triggered via tpacket_snd():
skb_assert_len
WARNING: at include/linux/skbuff.h:2753 skb_assert_len WARNING: at __dev_queue_xmit+0x21bc/0x4970 net/core/dev.c:4781 Call Trace: <TASK> dev_queue_xmit include/linux/netdevice.h:3448 [inline] packet_xmit+0x243/0x310 net/packet/af_packet.c:276 tpacket_snd net/packet/af_packet.c:2907 [inline] packet_sendmsg+0x28d6/0x4eb0 net/packet/af_packet.c:3134
When sending 0-byte packets via TPACKET ring buffer on devices with no hard header (e.g. dev->hard_header_len == 0), tpacket_fill_skb() populates an skb with skb->len == 0 and returns 0. tpacket_snd() then forwards this empty skb to packet_xmit(), causing __dev_queue_xmit() to hit skb_assert_len(skb).
Similar checks exist in packet_snd() via commit dc633700f00f ("net/af_packet: check len when min_header_len equals to 0") and in packet_sendmsg_spkt() via commit 6a341729fb31 ("af_packet: Don't send zero-byte data in packet_sendmsg_spkt().").
Return -EINVAL in tpacket_fill_skb() when skb->len is zero to reject zero-length packets in tpacket_snd().
--- net/packet/af_packet.c | 3 +++ 1 file changed, 3 insertions(+)
net/packet/af_packet.c +3 -0
@@ -2649,6 +2649,9 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, len = ((to_write > len_max) ? len_max : to_write); } +if (unlikely (!skb->len)) +return -EINVAL ; +packet_parse_headers (skb, sock);return tp_len;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 225/235 net: ethernet: ti: am65-cpsw: move ale selection in pdata
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Grygorii Strashko <[email protected]>
[ Upstream commit 7747d4b72f7702b2f19b9f91cc783eb38a2028bf ]
In preparation of adding more multi-port K3 CPSW versions move ALE selection in am65_cpsw_pdata, so it can be selected basing on DT compatibility property.
Stable-dep-of: 36a05d282007 ("net: ethernet: ti: am65-cpsw-nuss: Fix port_id extraction from SRC TAG")
--- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 4 +++- drivers/net/ethernet/ti/am65-cpsw-nuss.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-)
drivers/net/ethernet/ti/am65-cpsw-nuss.c +3 -1
@@ -1988,10 +1988,12 @@ static const struct soc_device_attribute am65_cpsw_socinfo[] = {static const struct am65_cpsw_pdata am65x_sr1_0 = { .quirks =AM65_CPSW_QUIRK_I2027_NO_TX_CSUM , + .ale_dev_id ="am65x-cpsw2g" , };static const struct am65_cpsw_pdata j721e_pdata = { .quirks =0 , + .ale_dev_id ="am65x-cpsw2g" , };static const struct of_device_id am65_cpsw_nuss_of_mtable[] = {
@@ -2123,7 +2125,7 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev) ale_params.ale_ageout =AM65_CPSW_ALE_AGEOUT_DEFAULT ; ale_params.ale_ports = common->port_num +1 ; ale_params.ale_regs = common->cpsw_base +AM65_CPSW_NU_ALE_BASE ; - ale_params.dev_id ="am65x-cpsw2g" ; + ale_params.dev_id = common->pdata.ale_dev_id; ale_params.bus_freq = common->bus_freq; common->ale =cpsw_ale_create (&ale_params);
drivers/net/ethernet/ti/am65-cpsw-nuss.h +1 -0
@@ -77,6 +77,7 @@ struct am65_cpsw_rx_chn {struct am65_cpsw_pdata {u32 quirks; +const char *ale_dev_id; };struct am65_cpsw_common {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 226/235 net: ethernet: ti: am65-cpsw: move free desc queue mode selection in pdata
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Grygorii Strashko <[email protected]>
[ Upstream commit c6275c02a09730b365fffe3372fbe768cef8eb37 ]
In preparation of adding more multi-port K3 CPSW versions move free descriptor queue mode selection in am65_cpsw_pdata, so it can be selected basing on DT compatibility property.
Stable-dep-of: 36a05d282007 ("net: ethernet: ti: am65-cpsw-nuss: Fix port_id extraction from SRC TAG")
--- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 4 +++- drivers/net/ethernet/ti/am65-cpsw-nuss.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-)
drivers/net/ethernet/ti/am65-cpsw-nuss.c +3 -1
@@ -1578,7 +1578,6 @@ static int am65_cpsw_nuss_init_rx_chns(struct am65_cpsw_common *common) };struct k3_ring_cfg fdqring_cfg = { .elm_size =K3_RINGACC_RING_ELSIZE_8 , - .mode =K3_RINGACC_RING_MODE_MESSAGE , .flags =K3_RINGACC_RING_SHARED , };struct k3_udma_glue_rx_flow_cfg rx_flow_cfg = {
@@ -1592,6 +1591,7 @@ static int am65_cpsw_nuss_init_rx_chns(struct am65_cpsw_common *common) rx_flow_cfg.ring_rxfdq0_id = fdqring_id; rx_flow_cfg.rx_cfg.size = max_desc_num; rx_flow_cfg.rxfdq_cfg.size = max_desc_num; + rx_flow_cfg.rxfdq_cfg.mode = common->pdata.fdqring_mode; ret =k3_udma_glue_rx_flow_init (rx_chn->rx_chn, i, &rx_flow_cfg);
@@ -1989,11 +1989,13 @@ static const struct soc_device_attribute am65_cpsw_socinfo[] = {static const struct am65_cpsw_pdata am65x_sr1_0 = { .quirks =AM65_CPSW_QUIRK_I2027_NO_TX_CSUM , .ale_dev_id ="am65x-cpsw2g" , + .fdqring_mode =K3_RINGACC_RING_MODE_MESSAGE , };static const struct am65_cpsw_pdata j721e_pdata = { .quirks =0 , .ale_dev_id ="am65x-cpsw2g" , + .fdqring_mode =K3_RINGACC_RING_MODE_MESSAGE , };static const struct of_device_id am65_cpsw_nuss_of_mtable[] = {
drivers/net/ethernet/ti/am65-cpsw-nuss.h +2 -0
@@ -11,6 +11,7 @@#include <linux/netdevice.h> #include <linux/phy.h> #include <linux/platform_device.h> +#include <linux/soc/ti/k3-ringacc.h> #include "am65-cpsw-qos.h" struct am65_cpts ;
@@ -77,6 +78,7 @@ struct am65_cpsw_rx_chn {struct am65_cpsw_pdata {u32 quirks; +enum k3_ring_mode fdqring_mode;const char *ale_dev_id; };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 227/235 net: ethernet: ti: am65-cpsw: add multi port support in mac-only mode
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Grygorii Strashko <[email protected]>
[ Upstream commit 84b4aa4932490c9f88f13d8f3b3cd1f3b6116991 ]
This patch adds final multi-port support to TI AM65x CPSW driver path in preparation for adding support for multi-port devices, like Main CPSW0 on K3 J721E SoC or future CPSW3g on K3 AM64x SoC.
- the separate netdev is created for every enabled external Port;
- DMA channels are common/shared for all external Ports and the RX/TX NAPI and DMA processing assigned to first available netdev;
- external Ports are configured in mac-only mode, which is similar to TI "dual-mac" mode for legacy TI CPSW - packets are sent to the Host port only in ingress and directly to the Port on egress. No packet switching between external ports happens.
- every port supports the same features as current AM65x CPSW on external device.
Stable-dep-of: 36a05d282007 ("net: ethernet: ti: am65-cpsw-nuss: Fix port_id extraction from SRC TAG")
--- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 129 ++++++++++++++--------- drivers/net/ethernet/ti/am65-cpsw-nuss.h | 1 + 2 files changed, 82 insertions(+), 48 deletions(-)
drivers/net/ethernet/ti/am65-cpsw-nuss.c +81 -48
@@ -270,8 +270,8 @@ static int am65_cpsw_nuss_ndo_slave_kill_vid(struct net_device *ndev,return ret; } -static void am65_cpsw_slave_set_promisc_2g (struct am65_cpsw_port *port, -bool promisc) +static void am65_cpsw_slave_set_promisc (struct am65_cpsw_port *port, +bool promisc) {struct am65_cpsw_common *common = port->common;
@@ -296,7 +296,7 @@ static void am65_cpsw_nuss_ndo_slave_set_rx_mode(struct net_device *ndev)bool promisc; promisc = !!(ndev->flags &IFF_PROMISC ); -am65_cpsw_slave_set_promisc_2g (port, promisc); +am65_cpsw_slave_set_promisc (port, promisc);if (promisc)return ;
@@ -627,13 +627,13 @@ static int am65_cpsw_nuss_ndo_slave_open(struct net_device *ndev)am65_cpsw_port_set_sl_mac (port, ndev->dev_addr); -if (port->slave.mac_only) +if (port->slave.mac_only) {/* enable mac-only mode on port */ cpsw_ale_control_set (common->ale, port->port_id,ALE_PORT_MACONLY ,1 ); -if (AM65_CPSW_IS_CPSW2G (common))cpsw_ale_control_set (common->ale, port->port_id,ALE_PORT_NOLEARN ,1 ); + } port_mask =BIT (port->port_id) |ALE_PORT_HOST ; cpsw_ale_add_ucast(common->ale, ndev->dev_addr,
@@ -1367,7 +1367,7 @@ static void am65_cpsw_nuss_ndo_get_stats(struct net_device *dev, stats->tx_dropped = dev->stats.tx_dropped; } -static const struct net_device_ops am65_cpsw_nuss_netdev_ops_2g = { +static const struct net_device_ops am65_cpsw_nuss_netdev_ops = { .ndo_open = am65_cpsw_nuss_ndo_slave_open, .ndo_stop = am65_cpsw_nuss_ndo_slave_stop, .ndo_start_xmit = am65_cpsw_nuss_ndo_slave_xmit,
@@ -1389,7 +1389,6 @@ static void am65_cpsw_nuss_slave_disable_unused(struct am65_cpsw_port *port)if (!port->disabled)return ; - common->disabled_ports_mask |=BIT (port->port_id);cpsw_ale_control_set (common->ale, port->port_id,ALE_PORT_STATE ,ALE_PORT_STATE_DISABLE );
@@ -1754,8 +1753,10 @@ static int am65_cpsw_nuss_init_slave_ports(struct am65_cpsw_common *common) } port->disabled = !of_device_is_available (port_np); -if (port->disabled) +if (port->disabled) { + common->disabled_ports_mask |=BIT (port->port_id);continue ; + } port->slave.ifphy =devm_of_phy_get (dev, port_np,NULL );if (IS_ERR (port->slave.ifphy)) {
@@ -1810,6 +1811,12 @@ static int am65_cpsw_nuss_init_slave_ports(struct am65_cpsw_common *common) }of_node_put (node); +/* is there at least one ext.port */ +if (!(~common->disabled_ports_mask &GENMASK (common->port_num,1 ))) { +dev_err (dev,"No Ext. port are available\n" ); +return -ENODEV ; + } +return 0 ; of_node_put:
@@ -1825,14 +1832,18 @@ static void am65_cpsw_pcpu_stats_free(void *data)free_percpu (stats); } -static int am65_cpsw_nuss_init_ndev_2g (struct am65_cpsw_common *common) +static int +am65_cpsw_nuss_init_port_ndev (struct am65_cpsw_common *common,u32 port_idx) {struct am65_cpsw_ndev_priv *ndev_priv;struct device *dev = common->dev;struct am65_cpsw_port *port;int ret; - port =am65_common_get_port (common,1 ); + port = &common->ports[port_idx]; + +if (port->disabled) +return 0 ;/* alloc netdev */ port->ndev = devm_alloc_etherdev_mqs(common->dev,
@@ -1862,7 +1873,7 @@ static int am65_cpsw_nuss_init_ndev_2g(struct am65_cpsw_common *common) port->ndev->features = port->ndev->hw_features |NETIF_F_HW_VLAN_CTAG_FILTER ; port->ndev->vlan_features |=NETIF_F_SG ; - port->ndev->netdev_ops = &am65_cpsw_nuss_netdev_ops_2g; + port->ndev->netdev_ops = &am65_cpsw_nuss_netdev_ops; port->ndev->ethtool_ops = &am65_cpsw_ethtool_ops_slave;/* Disable TX checksum offload by default due to HW bug */
@@ -1875,29 +1886,41 @@ static int am65_cpsw_nuss_init_ndev_2g(struct am65_cpsw_common *common) ret =devm_add_action_or_reset (dev, am65_cpsw_pcpu_stats_free, ndev_priv->stats); -if (ret) { -dev_err (dev,"Failed to add percpu stat free action %d\n" , ret); -return ret; +if (ret) +dev_err (dev,"failed to add percpu stat free action %d\n" , ret); + +if (!common->dma_ndev) + common->dma_ndev = port->ndev; + +return ret; +} + +static int am65_cpsw_nuss_init_ndevs (struct am65_cpsw_common *common) +{ +int ret; +int i; + +for (i =0 ; i < common->port_num; i++) { + ret =am65_cpsw_nuss_init_port_ndev (common, i); +if (ret) +return ret; } -netif_napi_add (port->ndev, &common->napi_rx, +netif_napi_add (common->dma_ndev, &common->napi_rx, am65_cpsw_nuss_rx_poll,NAPI_POLL_WEIGHT );return ret; } -static int am65_cpsw_nuss_ndev_add_napi_2g (struct am65_cpsw_common *common) +static int am65_cpsw_nuss_ndev_add_tx_napi (struct am65_cpsw_common *common) {struct device *dev = common->dev; -struct am65_cpsw_port *port;int i, ret =0 ; - port =am65_common_get_port (common,1 ); -for (i =0 ; i < common->tx_ch_num; i++) {struct am65_cpsw_tx_chn *tx_chn = &common->tx_chns[i]; -netif_tx_napi_add (port->ndev, &tx_chn->napi_tx, +netif_tx_napi_add (common->dma_ndev, &tx_chn->napi_tx, am65_cpsw_nuss_tx_poll,NAPI_POLL_WEIGHT ); ret = devm_request_irq(dev, tx_chn->irq,
@@ -1915,16 +1938,27 @@ static int am65_cpsw_nuss_ndev_add_napi_2g(struct am65_cpsw_common *common)return ret; } -static int am65_cpsw_nuss_ndev_reg_2g (struct am65_cpsw_common *common) +static void am65_cpsw_nuss_cleanup_ndev (struct am65_cpsw_common *common) +{ +struct am65_cpsw_port *port; +int i; + +for (i =0 ; i < common->port_num; i++) { + port = &common->ports[i]; +if (port->ndev) +unregister_netdev (port->ndev); + } +} + +static int am65_cpsw_nuss_register_ndevs (struct am65_cpsw_common *common) {struct device *dev = common->dev;struct am65_cpsw_port *port; -int ret =0 ; +int ret =0 , i; - port =am65_common_get_port (common,1 ); - ret =am65_cpsw_nuss_ndev_add_napi_2g (common); + ret =am65_cpsw_nuss_ndev_add_tx_napi (common);if (ret) - goto err; +return ret; ret = devm_request_irq(dev, common->rx_chns.irq, am65_cpsw_nuss_rx_irq,
@@ -1932,17 +1966,31 @@ static int am65_cpsw_nuss_ndev_reg_2g(struct am65_cpsw_common *common)if (ret) {dev_err (dev,"failure requesting rx irq %u, %d\n" , common->rx_chns.irq, ret); - goto err; +return ret; + } + +for (i =0 ; i < common->port_num; i++) { + port = &common->ports[i]; + +if (!port->ndev) +continue ; + + ret =register_netdev (port->ndev); +if (ret) { +dev_err (dev,"error registering slave net device%i %d\n" , + i, ret); + goto err_cleanup_ndev; + } } - ret =register_netdev (port->ndev); -if (ret) -dev_err (dev,"error registering slave net device %d\n" , ret);/* can't auto unregister ndev using devm_add_action() due to * devres release sequence in DD core for DMA */ -err: +return 0 ; + +err_cleanup_ndev: +am65_cpsw_nuss_cleanup_ndev (common);return ret; }
@@ -1955,19 +2003,7 @@ int am65_cpsw_nuss_update_tx_chns(struct am65_cpsw_common *common, int num_tx)if (ret)return ret; -return am65_cpsw_nuss_ndev_add_napi_2g (common); -} - -static void am65_cpsw_nuss_cleanup_ndev (struct am65_cpsw_common *common) -{ -struct am65_cpsw_port *port; -int i; - -for (i =0 ; i < common->port_num; i++) { - port = &common->ports[i]; -if (port->ndev) -unregister_netdev (port->ndev); - } +return am65_cpsw_nuss_ndev_add_tx_napi (common); }struct am65_cpsw_soc_pdata {
@@ -2055,9 +2091,6 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev)if (common->port_num <1 || common->port_num >AM65_CPSW_MAX_PORTS )return -ENOENT ; -if (common->port_num !=1 ) -return -EOPNOTSUPP ; - common->rx_flow_id_base =-1 ;init_completion (&common->tdown_complete); common->tx_ch_num =1 ;
@@ -2147,11 +2180,11 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev)dev_set_drvdata (dev, common); - ret =am65_cpsw_nuss_init_ndev_2g (common); + ret =am65_cpsw_nuss_init_ndevs (common);if (ret) goto err_of_clear; - ret =am65_cpsw_nuss_ndev_reg_2g (common); + ret =am65_cpsw_nuss_register_ndevs (common);if (ret) goto err_of_clear;
drivers/net/ethernet/ti/am65-cpsw-nuss.h +1 -0
@@ -94,6 +94,7 @@ struct am65_cpsw_common {struct am65_cpsw_host host;struct am65_cpsw_port *ports;u32 disabled_ports_mask; +struct net_device *dma_ndev;int usage_count;/* number of opened ports */ struct cpsw_ale *ale;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 228/235 net: ethernet: ti: am65-cpsw-nuss: Fix port_id extraction from SRC TAG
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Siddharth Vadapalli <[email protected]>
[ Upstream commit 36a05d2820077bb3955acb8111e1041d39148037 ]
On the packet reception path, the ID of the MAC Port on which the packet was received, is embedded in the RX DMA Descriptor's metadata. The ID is extracted using the helper function cppi5_desc_get_tags_ids() which fills in the 16-bit Source Tag into the 'port_id' variable. However, it is only the lower 8-bits of the 16-bit Source Tag that represent the MAC Port ID, while the upper 8-bits are Hardware-Reserved and carry an arbitrary value. With the existing logic, sporadic kernel crash is observed due to the subsequent driver code accessing out-of-bound memory because of an invalid port_id.
Hence, fix the port_id extraction logic to use only the lower 8-bits of the Source Tag as the MAC Port ID.
--- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/net/ethernet/ti/am65-cpsw-nuss.c +2 -0
@@ -779,6 +779,8 @@ static int am65_cpsw_nuss_rx_packets(struct am65_cpsw_common *common,cppi5_hdesc_get_obuf (desc_rx, &buf_dma, &buf_dma_len); pkt_len =cppi5_hdesc_get_pktlen (desc_rx);cppi5_desc_get_tags_ids (&desc_rx->hdr, &port_id,NULL ); +/* Port ID is contained in the lower 8-bits of the 16-bit Source Tag */ + port_id &=0xFF ;dev_dbg (dev,"%s rx port_id:%d\n" , __func__, port_id); port =am65_common_get_port (common, port_id); ndev = port->ndev;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 229/235 net/smc: rdma write inline if qp has sufficient inline space
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangguan Wang <[email protected]>
commit 793a7df63071eb09e5b88addf2a569d7bfd3c973 upstream.
Rdma write with inline flag when sending small packages, whose length is shorter than the qp's max_inline_data, can help reducing latency.
In my test environment, which are 2 VMs running on the same physical host and whose NICs(ConnectX-4Lx) are working on SR-IOV mode, qperf shows 0.5us-0.7us improvement in latency.
Test command:
server: smc_run taskset -c 1 qperf
client: smc_run taskset -c 1 qperf <server ip> -oo \
msg_size:1:2K:*2 -t 30 -vu tcp_lat
The results shown below:
msgsize before after 1B 11.2 us 10.6 us (-0.6 us) 2B 11.2 us 10.7 us (-0.5 us) 4B 11.3 us 10.7 us (-0.6 us) 8B 11.2 us 10.6 us (-0.6 us) 16B 11.3 us 10.7 us (-0.6 us) 32B 11.3 us 10.6 us (-0.7 us) 64B 11.2 us 11.2 us (0 us) 128B 11.2 us 11.2 us (0 us) 256B 11.2 us 11.2 us (0 us) 512B 11.4 us 11.3 us (-0.1 us) 1KB 11.4 us 11.5 us (0.1 us) 2KB 11.5 us 11.5 us (0 us)
Stable-dep-of: 5d9686af2976 ("net: smc: fix splice entry lifetime imbalance in smc_rx_splice")
--- net/smc/smc_tx.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-)
net/smc/smc_tx.c +12 -5
@@ -320,12 +320,20 @@ static int smcr_tx_rdma_writes(struct smint rc;for (dstchunk =0 ; dstchunk <2 ; dstchunk++) { -struct ib_sge *sge = - wr_rdma_buf->wr_tx_rdma[dstchunk].wr.sg_list; +struct ib_rdma_wr *wr = &wr_rdma_buf->wr_tx_rdma[dstchunk]; +struct ib_sge *sge = wr->wr.sg_list; +u64 base_addr = dma_addr; + +if (dst_len < link->qp_attr.cap.max_inline_data) { + base_addr = (uintptr_t )conn->sndbuf_desc->cpu_addr; + wr->wr.send_flags |=IB_SEND_INLINE ; + }else { + wr->wr.send_flags &= ~IB_SEND_INLINE ; + } num_sges =0 ;for (srcchunk =0 ; srcchunk <2 ; srcchunk++) { - sge[srcchunk].addr = dma_addr + src_off; + sge[srcchunk].addr = base_addr + src_off; sge[srcchunk].length = src_len; num_sges++;
@@ -339,8 +347,7 @@ static int smcr_tx_rdma_writes(struct sm src_len = dst_len - src_len;/* remainder */ src_len_sum += src_len; } - rc =smc_tx_rdma_write (conn, dst_off, num_sges, - &wr_rdma_buf->wr_tx_rdma[dstchunk]); + rc =smc_tx_rdma_write (conn, dst_off, num_sges, wr);if (rc)return rc;if (dst_len_sum == len)
Greg Kroah-Hartman
maintainerPATCH 230/235 net: smc: fix splice entry lifetime imbalance in smc_rx_splice
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Daming Li <[email protected]>
commit 5d9686af2976741bbd79b150d1c9e60b81e7f12e upstream.
smc_rx_splice() passes pages to splice_to_pipe() before taking the references that cover the lifetime of each splice entry. In the VM-backed RMB path, splice_to_pipe() may drop unqueued entries through smc_rx_spd_release(), while queued entries are released later via the pipe buffer callback.
The old post-splice accounting also derives the number of queued VM pages from an offset mutated while building the descriptor, and a multi-page splice pairs one sock_hold() with multiple sock_put() calls.
Take the page and socket references for every candidate entry before splice_to_pipe(), and drop the matching private state, page reference, and socket reference from smc_rx_spd_release() for entries that never get queued. This fixes a refcount imbalance that can underflow page refcounts and trigger a use-after-free.
--- net/smc/smc_rx.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
net/smc/smc_rx.c +8 -4
@@ -144,7 +144,12 @@ static const struct pipe_buf_operationsstatic void smc_rx_spd_release (struct splice_pipe_desc *spd,unsigned int i) { +struct smc_spd_priv *priv = (struct smc_spd_priv *)spd->partial[i].private; +struct sock *sk = &priv->smc->sk; + +kfree (priv);put_page (spd->pages[i]); +sock_put (sk); } static int smc_rx_splice(struct pipe_inode_info *pipe, char *src, size_t len,
@@ -164,6 +169,8 @@ static int smc_rx_splice(struct pipe_ino partial.len = len; partial.private = (unsigned long )priv; +get_page (smc->conn.rmb_desc->pages); +sock_hold (&smc->sk); spd.nr_pages_max =1 ; spd.nr_pages =1 ; spd.pages = &smc->conn.rmb_desc->pages;
@@ -172,11 +179,8 @@ static int smc_rx_splice(struct pipe_ino spd.spd_release = smc_rx_spd_release; bytes =splice_to_pipe (pipe, &spd); -if (bytes >0 ) { -sock_hold (&smc->sk); -get_page (smc->conn.rmb_desc->pages); +if (bytes >0 )atomic_add (bytes, &smc->conn.splice_pending); - }return bytes; }
Greg Kroah-Hartman
maintainerPATCH 231/235 binfmt_misc: use exe_file_deny_write_access() for the interpreter clone
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit fa5990ca8fd917003e526036bcc50413edb9722c upstream.
For MISC_FMT_OPEN_FILE entries load_misc_binary() clones the registered interpreter file and denies write access to the clone via plain deny_write_access(). The clone is installed as bprm->interpreter and later released by the exec machinery through exe_file_allow_write_access() which skips the i_writecount increment for files with FMODE_FSNOTIFY_HSM set.
The deny and allow side can therefore come to different conclusions when pre-content watches are in play: if a pre-content watch is added to the interpreter after registration every subsequent exec through that entry takes a write denial on the clone that is never paired with a write allowance, driving the interpreter inode's i_writecount further down with each exec and leaving the interpreter unwritable even after the entry and all its users are gone.
Take the write denial via exe_file_deny_write_access() so both sides of the pairing base their decision on the same file mode, and propagate failure instead of silently ignoring it: an interpreter that is concurrently open for writing now fails the exec with ETXTBSY, exactly like an interpreter freshly opened via open_exec() would.
--- fs/binfmt_misc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
fs/binfmt_misc.c +8 -2
@@ -220,8 +220,14 @@ static int load_misc_binary(struct linuxif (fmt->flags &MISC_FMT_OPEN_FILE ) { interp_file =file_clone_open (fmt->interp_file); -if (!IS_ERR (interp_file)) -deny_write_access (interp_file); +if (!IS_ERR (interp_file)) { +int err =exe_file_deny_write_access (interp_file); + +if (err) { +fput (interp_file); + interp_file =ERR_PTR (err); + } + } }else { interp_file =open_exec (fmt->interpreter); }
Greg Kroah-Hartman
maintainerPATCH 232/235 udmabuf: Ensure to perform cache synchronisation in begin_cpu_udmabuf()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Robert Mader <[email protected]>
commit 1d0e25c1ddf2063c499264fb2ba0fa6a3e4f8a00 upstream.
The message of commit 504e2b4ab97a ("dma-buf/udmabuf: skip redundant cpu sync to fix cacheline EEXIST warning") says:
Quoted 4 lines
The CPU sync at map/unmap time is also redundant for udmabuf:
begin_cpu_udmabuf()andend_cpu_udmabuf()already perform explicit cache synchronization via dma_sync_sgtable_for_cpu/device() when CPU access is requested through the dma-buf interface.
This, however, does not apply to the first time begin_cpu_udmabuf() is called on an udmabuf, in which case the implementation previously relied on get_sg_table() to perform the cache synchronisation.
Ensure to call dma_sync_sgtable_for_cpu() in that case as well.
--- drivers/dma-buf/udmabuf.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
drivers/dma-buf/udmabuf.c +5 -4
@@ -121,21 +121,22 @@ static int begin_cpu_udmabuf(struct dma_ {struct udmabuf *ubuf = buf->priv;struct device *dev = ubuf->device->this_device; -int ret =0 ;if (!ubuf->sg) { ubuf->sg =get_sg_table (dev, buf, direction);if (IS_ERR (ubuf->sg)) { +int ret; + ret =PTR_ERR (ubuf->sg); ubuf->sg =NULL ; +return ret; }else { ubuf->sg_dir = direction; } - }else { -dma_sync_sgtable_for_cpu (dev, ubuf->sg, direction); } -return ret; +dma_sync_sgtable_for_cpu (dev, ubuf->sg, direction); +return 0 ; } static int end_cpu_udmabuf(struct dma_buf *buf,
Greg Kroah-Hartman
maintainerPATCH 233/235 i2c: smbus: Check for parent device before dereference
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andy Shevchenko <[email protected]>
commit 8302532f47bb6c3aa1ed2043d30187ca307f176a upstream.
An I²C adapter might be instantiated without parent. In such case there is no property can be retrieved. Skip SMBus alert setup when this happens.
--- drivers/i2c/i2c-core-smbus.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/i2c/i2c-core-smbus.c +6 -2
@@ -703,10 +703,14 @@ EXPORT_SYMBOL_GPL(i2c_new_smbus_alert_de#if IS_ENABLED (CONFIG_I2C_SMBUS )int i2c_setup_smbus_alert (struct i2c_adapter *adapter) { +struct device *parent = adapter->dev.parent;int irq; - irq =device_property_match_string (adapter->dev.parent,"interrupt-names" , -"smbus_alert" ); +/* Adapter instantiated without parent, skip the SMBus alert setup */ +if (!parent) +return 0 ; + + irq =device_property_match_string (parent,"interrupt-names" ,"smbus_alert" );if (irq == -EINVAL || irq == -ENODATA )return 0 ;else if (irq <0 )
Greg Kroah-Hartman
maintainerPATCH 234/235 net: ethernet: ti: am65-cpsw: fix error handling in am65_cpsw_nuss_probe()
In reply to Greg Kroah-Hartman5.10-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhang Changzhong <[email protected]>
commit 46fb6512538d201d9a5b2bd7138b6751c37fdf0b upstream.
The am65_cpsw_nuss_cleanup_ndev() function calls unregister_netdev() even if register_netdev() fails, which triggers WARN_ON(1) in unregister_netdevice_many(). To fix it, make sure that unregister_netdev() is called only on registered netdev.
Compile tested only.
--- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/ti/am65-cpsw-nuss.c +1 -1
@@ -1947,7 +1947,7 @@ static void am65_cpsw_nuss_cleanup_ndev(for (i =0 ; i < common->port_num; i++) { port = &common->ports[i]; -if (port->ndev) +if (port->ndev && port->ndev->reg_state ==NETREG_REGISTERED )unregister_netdev (port->ndev); } }