mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-02-19 17:09:14 -05:00
Merge tag 'linux-can-fixes-for-6.14-20250314' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can
Marc Kleine-Budde says: ==================== pull-request: can 2025-03-14 this is a pull request of 6 patches for net/main. The first patch is by Vincent Mailhol and fixes an out of bound read in strscpy() in the ucan driver. Oliver Hartkopp contributes a patch for the af_can statistics to use atomic access in the hot path. The next 2 patches are by Biju Das, target the rcar_canfd driver and fix the page entries in the AFL list. The 2 patches by Haibo Chen for the flexcan driver fix the suspend and resume functions. linux-can-fixes-for-6.14-20250314 * tag 'linux-can-fixes-for-6.14-20250314' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can: can: flexcan: disable transceiver during system PM can: flexcan: only change CAN state when link up in system PM can: rcar_canfd: Fix page entries in the AFL list dt-bindings: can: renesas,rcar-canfd: Fix typo in pattern properties for R-Car V4M can: statistics: use atomic access in hot path can: ucan: fix out of bound read in strscpy() source ==================== Link: https://patch.msgid.link/20250314130909.2890541-1-mkl@pengutronix.de Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
@@ -170,7 +170,7 @@ allOf:
|
||||
const: renesas,r8a779h0-canfd
|
||||
then:
|
||||
patternProperties:
|
||||
"^channel[5-7]$": false
|
||||
"^channel[4-7]$": false
|
||||
else:
|
||||
if:
|
||||
not:
|
||||
|
||||
@@ -2260,14 +2260,19 @@ static int __maybe_unused flexcan_suspend(struct device *device)
|
||||
|
||||
flexcan_chip_interrupts_disable(dev);
|
||||
|
||||
err = flexcan_transceiver_disable(priv);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = pinctrl_pm_select_sleep_state(device);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
netif_stop_queue(dev);
|
||||
netif_device_detach(dev);
|
||||
|
||||
priv->can.state = CAN_STATE_SLEEPING;
|
||||
}
|
||||
priv->can.state = CAN_STATE_SLEEPING;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2278,7 +2283,6 @@ static int __maybe_unused flexcan_resume(struct device *device)
|
||||
struct flexcan_priv *priv = netdev_priv(dev);
|
||||
int err;
|
||||
|
||||
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
if (netif_running(dev)) {
|
||||
netif_device_attach(dev);
|
||||
netif_start_queue(dev);
|
||||
@@ -2292,12 +2296,20 @@ static int __maybe_unused flexcan_resume(struct device *device)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = flexcan_chip_start(dev);
|
||||
err = flexcan_transceiver_enable(priv);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = flexcan_chip_start(dev);
|
||||
if (err) {
|
||||
flexcan_transceiver_disable(priv);
|
||||
return err;
|
||||
}
|
||||
|
||||
flexcan_chip_interrupts_enable(dev);
|
||||
}
|
||||
|
||||
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -787,22 +787,14 @@ static void rcar_canfd_configure_controller(struct rcar_canfd_global *gpriv)
|
||||
}
|
||||
|
||||
static void rcar_canfd_configure_afl_rules(struct rcar_canfd_global *gpriv,
|
||||
u32 ch)
|
||||
u32 ch, u32 rule_entry)
|
||||
{
|
||||
u32 cfg;
|
||||
int offset, start, page, num_rules = RCANFD_CHANNEL_NUMRULES;
|
||||
int offset, page, num_rules = RCANFD_CHANNEL_NUMRULES;
|
||||
u32 rule_entry_index = rule_entry % 16;
|
||||
u32 ridx = ch + RCANFD_RFFIFO_IDX;
|
||||
|
||||
if (ch == 0) {
|
||||
start = 0; /* Channel 0 always starts from 0th rule */
|
||||
} else {
|
||||
/* Get number of Channel 0 rules and adjust */
|
||||
cfg = rcar_canfd_read(gpriv->base, RCANFD_GAFLCFG(ch));
|
||||
start = RCANFD_GAFLCFG_GETRNC(gpriv, 0, cfg);
|
||||
}
|
||||
|
||||
/* Enable write access to entry */
|
||||
page = RCANFD_GAFL_PAGENUM(start);
|
||||
page = RCANFD_GAFL_PAGENUM(rule_entry);
|
||||
rcar_canfd_set_bit(gpriv->base, RCANFD_GAFLECTR,
|
||||
(RCANFD_GAFLECTR_AFLPN(gpriv, page) |
|
||||
RCANFD_GAFLECTR_AFLDAE));
|
||||
@@ -818,13 +810,13 @@ static void rcar_canfd_configure_afl_rules(struct rcar_canfd_global *gpriv,
|
||||
offset = RCANFD_C_GAFL_OFFSET;
|
||||
|
||||
/* Accept all IDs */
|
||||
rcar_canfd_write(gpriv->base, RCANFD_GAFLID(offset, start), 0);
|
||||
rcar_canfd_write(gpriv->base, RCANFD_GAFLID(offset, rule_entry_index), 0);
|
||||
/* IDE or RTR is not considered for matching */
|
||||
rcar_canfd_write(gpriv->base, RCANFD_GAFLM(offset, start), 0);
|
||||
rcar_canfd_write(gpriv->base, RCANFD_GAFLM(offset, rule_entry_index), 0);
|
||||
/* Any data length accepted */
|
||||
rcar_canfd_write(gpriv->base, RCANFD_GAFLP0(offset, start), 0);
|
||||
rcar_canfd_write(gpriv->base, RCANFD_GAFLP0(offset, rule_entry_index), 0);
|
||||
/* Place the msg in corresponding Rx FIFO entry */
|
||||
rcar_canfd_set_bit(gpriv->base, RCANFD_GAFLP1(offset, start),
|
||||
rcar_canfd_set_bit(gpriv->base, RCANFD_GAFLP1(offset, rule_entry_index),
|
||||
RCANFD_GAFLP1_GAFLFDP(ridx));
|
||||
|
||||
/* Disable write access to page */
|
||||
@@ -1851,6 +1843,7 @@ static int rcar_canfd_probe(struct platform_device *pdev)
|
||||
unsigned long channels_mask = 0;
|
||||
int err, ch_irq, g_irq;
|
||||
int g_err_irq, g_recc_irq;
|
||||
u32 rule_entry = 0;
|
||||
bool fdmode = true; /* CAN FD only mode - default */
|
||||
char name[9] = "channelX";
|
||||
int i;
|
||||
@@ -2023,7 +2016,8 @@ static int rcar_canfd_probe(struct platform_device *pdev)
|
||||
rcar_canfd_configure_tx(gpriv, ch);
|
||||
|
||||
/* Configure receive rules */
|
||||
rcar_canfd_configure_afl_rules(gpriv, ch);
|
||||
rcar_canfd_configure_afl_rules(gpriv, ch, rule_entry);
|
||||
rule_entry += RCANFD_CHANNEL_NUMRULES;
|
||||
}
|
||||
|
||||
/* Configure common interrupts */
|
||||
|
||||
@@ -186,7 +186,7 @@ union ucan_ctl_payload {
|
||||
*/
|
||||
struct ucan_ctl_cmd_get_protocol_version cmd_get_protocol_version;
|
||||
|
||||
u8 raw[128];
|
||||
u8 fw_str[128];
|
||||
} __packed;
|
||||
|
||||
enum {
|
||||
@@ -424,18 +424,20 @@ static int ucan_ctrl_command_out(struct ucan_priv *up,
|
||||
UCAN_USB_CTL_PIPE_TIMEOUT);
|
||||
}
|
||||
|
||||
static int ucan_device_request_in(struct ucan_priv *up,
|
||||
u8 cmd, u16 subcmd, u16 datalen)
|
||||
static void ucan_get_fw_str(struct ucan_priv *up, char *fw_str, size_t size)
|
||||
{
|
||||
return usb_control_msg(up->udev,
|
||||
usb_rcvctrlpipe(up->udev, 0),
|
||||
cmd,
|
||||
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
subcmd,
|
||||
0,
|
||||
up->ctl_msg_buffer,
|
||||
datalen,
|
||||
UCAN_USB_CTL_PIPE_TIMEOUT);
|
||||
int ret;
|
||||
|
||||
ret = usb_control_msg(up->udev, usb_rcvctrlpipe(up->udev, 0),
|
||||
UCAN_DEVICE_GET_FW_STRING,
|
||||
USB_DIR_IN | USB_TYPE_VENDOR |
|
||||
USB_RECIP_DEVICE,
|
||||
0, 0, fw_str, size - 1,
|
||||
UCAN_USB_CTL_PIPE_TIMEOUT);
|
||||
if (ret > 0)
|
||||
fw_str[ret] = '\0';
|
||||
else
|
||||
strscpy(fw_str, "unknown", size);
|
||||
}
|
||||
|
||||
/* Parse the device information structure reported by the device and
|
||||
@@ -1314,7 +1316,6 @@ static int ucan_probe(struct usb_interface *intf,
|
||||
u8 in_ep_addr;
|
||||
u8 out_ep_addr;
|
||||
union ucan_ctl_payload *ctl_msg_buffer;
|
||||
char firmware_str[sizeof(union ucan_ctl_payload) + 1];
|
||||
|
||||
udev = interface_to_usbdev(intf);
|
||||
|
||||
@@ -1527,17 +1528,6 @@ static int ucan_probe(struct usb_interface *intf,
|
||||
*/
|
||||
ucan_parse_device_info(up, &ctl_msg_buffer->cmd_get_device_info);
|
||||
|
||||
/* just print some device information - if available */
|
||||
ret = ucan_device_request_in(up, UCAN_DEVICE_GET_FW_STRING, 0,
|
||||
sizeof(union ucan_ctl_payload));
|
||||
if (ret > 0) {
|
||||
/* copy string while ensuring zero termination */
|
||||
strscpy(firmware_str, up->ctl_msg_buffer->raw,
|
||||
sizeof(union ucan_ctl_payload) + 1);
|
||||
} else {
|
||||
strcpy(firmware_str, "unknown");
|
||||
}
|
||||
|
||||
/* device is compatible, reset it */
|
||||
ret = ucan_ctrl_command_out(up, UCAN_COMMAND_RESET, 0, 0);
|
||||
if (ret < 0)
|
||||
@@ -1555,7 +1545,10 @@ static int ucan_probe(struct usb_interface *intf,
|
||||
|
||||
/* initialisation complete, log device info */
|
||||
netdev_info(up->netdev, "registered device\n");
|
||||
netdev_info(up->netdev, "firmware string: %s\n", firmware_str);
|
||||
ucan_get_fw_str(up, up->ctl_msg_buffer->fw_str,
|
||||
sizeof(up->ctl_msg_buffer->fw_str));
|
||||
netdev_info(up->netdev, "firmware string: %s\n",
|
||||
up->ctl_msg_buffer->fw_str);
|
||||
|
||||
/* success */
|
||||
return 0;
|
||||
|
||||
@@ -287,8 +287,8 @@ int can_send(struct sk_buff *skb, int loop)
|
||||
netif_rx(newskb);
|
||||
|
||||
/* update statistics */
|
||||
pkg_stats->tx_frames++;
|
||||
pkg_stats->tx_frames_delta++;
|
||||
atomic_long_inc(&pkg_stats->tx_frames);
|
||||
atomic_long_inc(&pkg_stats->tx_frames_delta);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -647,8 +647,8 @@ static void can_receive(struct sk_buff *skb, struct net_device *dev)
|
||||
int matches;
|
||||
|
||||
/* update statistics */
|
||||
pkg_stats->rx_frames++;
|
||||
pkg_stats->rx_frames_delta++;
|
||||
atomic_long_inc(&pkg_stats->rx_frames);
|
||||
atomic_long_inc(&pkg_stats->rx_frames_delta);
|
||||
|
||||
/* create non-zero unique skb identifier together with *skb */
|
||||
while (!(can_skb_prv(skb)->skbcnt))
|
||||
@@ -669,8 +669,8 @@ static void can_receive(struct sk_buff *skb, struct net_device *dev)
|
||||
consume_skb(skb);
|
||||
|
||||
if (matches > 0) {
|
||||
pkg_stats->matches++;
|
||||
pkg_stats->matches_delta++;
|
||||
atomic_long_inc(&pkg_stats->matches);
|
||||
atomic_long_inc(&pkg_stats->matches_delta);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,9 +66,9 @@ struct receiver {
|
||||
struct can_pkg_stats {
|
||||
unsigned long jiffies_init;
|
||||
|
||||
unsigned long rx_frames;
|
||||
unsigned long tx_frames;
|
||||
unsigned long matches;
|
||||
atomic_long_t rx_frames;
|
||||
atomic_long_t tx_frames;
|
||||
atomic_long_t matches;
|
||||
|
||||
unsigned long total_rx_rate;
|
||||
unsigned long total_tx_rate;
|
||||
@@ -82,9 +82,9 @@ struct can_pkg_stats {
|
||||
unsigned long max_tx_rate;
|
||||
unsigned long max_rx_match_ratio;
|
||||
|
||||
unsigned long rx_frames_delta;
|
||||
unsigned long tx_frames_delta;
|
||||
unsigned long matches_delta;
|
||||
atomic_long_t rx_frames_delta;
|
||||
atomic_long_t tx_frames_delta;
|
||||
atomic_long_t matches_delta;
|
||||
};
|
||||
|
||||
/* persistent statistics */
|
||||
|
||||
@@ -118,6 +118,13 @@ void can_stat_update(struct timer_list *t)
|
||||
struct can_pkg_stats *pkg_stats = net->can.pkg_stats;
|
||||
unsigned long j = jiffies; /* snapshot */
|
||||
|
||||
long rx_frames = atomic_long_read(&pkg_stats->rx_frames);
|
||||
long tx_frames = atomic_long_read(&pkg_stats->tx_frames);
|
||||
long matches = atomic_long_read(&pkg_stats->matches);
|
||||
long rx_frames_delta = atomic_long_read(&pkg_stats->rx_frames_delta);
|
||||
long tx_frames_delta = atomic_long_read(&pkg_stats->tx_frames_delta);
|
||||
long matches_delta = atomic_long_read(&pkg_stats->matches_delta);
|
||||
|
||||
/* restart counting in timer context on user request */
|
||||
if (user_reset)
|
||||
can_init_stats(net);
|
||||
@@ -127,35 +134,33 @@ void can_stat_update(struct timer_list *t)
|
||||
can_init_stats(net);
|
||||
|
||||
/* prevent overflow in calc_rate() */
|
||||
if (pkg_stats->rx_frames > (ULONG_MAX / HZ))
|
||||
if (rx_frames > (LONG_MAX / HZ))
|
||||
can_init_stats(net);
|
||||
|
||||
/* prevent overflow in calc_rate() */
|
||||
if (pkg_stats->tx_frames > (ULONG_MAX / HZ))
|
||||
if (tx_frames > (LONG_MAX / HZ))
|
||||
can_init_stats(net);
|
||||
|
||||
/* matches overflow - very improbable */
|
||||
if (pkg_stats->matches > (ULONG_MAX / 100))
|
||||
if (matches > (LONG_MAX / 100))
|
||||
can_init_stats(net);
|
||||
|
||||
/* calc total values */
|
||||
if (pkg_stats->rx_frames)
|
||||
pkg_stats->total_rx_match_ratio = (pkg_stats->matches * 100) /
|
||||
pkg_stats->rx_frames;
|
||||
if (rx_frames)
|
||||
pkg_stats->total_rx_match_ratio = (matches * 100) / rx_frames;
|
||||
|
||||
pkg_stats->total_tx_rate = calc_rate(pkg_stats->jiffies_init, j,
|
||||
pkg_stats->tx_frames);
|
||||
tx_frames);
|
||||
pkg_stats->total_rx_rate = calc_rate(pkg_stats->jiffies_init, j,
|
||||
pkg_stats->rx_frames);
|
||||
rx_frames);
|
||||
|
||||
/* calc current values */
|
||||
if (pkg_stats->rx_frames_delta)
|
||||
if (rx_frames_delta)
|
||||
pkg_stats->current_rx_match_ratio =
|
||||
(pkg_stats->matches_delta * 100) /
|
||||
pkg_stats->rx_frames_delta;
|
||||
(matches_delta * 100) / rx_frames_delta;
|
||||
|
||||
pkg_stats->current_tx_rate = calc_rate(0, HZ, pkg_stats->tx_frames_delta);
|
||||
pkg_stats->current_rx_rate = calc_rate(0, HZ, pkg_stats->rx_frames_delta);
|
||||
pkg_stats->current_tx_rate = calc_rate(0, HZ, tx_frames_delta);
|
||||
pkg_stats->current_rx_rate = calc_rate(0, HZ, rx_frames_delta);
|
||||
|
||||
/* check / update maximum values */
|
||||
if (pkg_stats->max_tx_rate < pkg_stats->current_tx_rate)
|
||||
@@ -168,9 +173,9 @@ void can_stat_update(struct timer_list *t)
|
||||
pkg_stats->max_rx_match_ratio = pkg_stats->current_rx_match_ratio;
|
||||
|
||||
/* clear values for 'current rate' calculation */
|
||||
pkg_stats->tx_frames_delta = 0;
|
||||
pkg_stats->rx_frames_delta = 0;
|
||||
pkg_stats->matches_delta = 0;
|
||||
atomic_long_set(&pkg_stats->tx_frames_delta, 0);
|
||||
atomic_long_set(&pkg_stats->rx_frames_delta, 0);
|
||||
atomic_long_set(&pkg_stats->matches_delta, 0);
|
||||
|
||||
/* restart timer (one second) */
|
||||
mod_timer(&net->can.stattimer, round_jiffies(jiffies + HZ));
|
||||
@@ -214,9 +219,12 @@ static int can_stats_proc_show(struct seq_file *m, void *v)
|
||||
struct can_rcv_lists_stats *rcv_lists_stats = net->can.rcv_lists_stats;
|
||||
|
||||
seq_putc(m, '\n');
|
||||
seq_printf(m, " %8ld transmitted frames (TXF)\n", pkg_stats->tx_frames);
|
||||
seq_printf(m, " %8ld received frames (RXF)\n", pkg_stats->rx_frames);
|
||||
seq_printf(m, " %8ld matched frames (RXMF)\n", pkg_stats->matches);
|
||||
seq_printf(m, " %8ld transmitted frames (TXF)\n",
|
||||
atomic_long_read(&pkg_stats->tx_frames));
|
||||
seq_printf(m, " %8ld received frames (RXF)\n",
|
||||
atomic_long_read(&pkg_stats->rx_frames));
|
||||
seq_printf(m, " %8ld matched frames (RXMF)\n",
|
||||
atomic_long_read(&pkg_stats->matches));
|
||||
|
||||
seq_putc(m, '\n');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user