forked from Minki/linux
linux-can-fixes-for-5.3-20190724
-----BEGIN PGP SIGNATURE----- iQFHBAABCgAxFiEEmvEkXzgOfc881GuFWsYho5HknSAFAl04JHsTHG1rbEBwZW5n dXRyb25peC5kZQAKCRBaxiGjkeSdIB52B/9S17IKTEpoNwM5KvrR+ZMqWZTx6zng iKNef14uuAemHzThMoSAJoJBGGzcgmZeu/nd0fyGg0f51kXVkapU4TsvYa2azkt9 WcTZQG3N4uUuW3D8hEbY2+au8e8ubNR+NnWQEqeoa5yuDGg7nSjD3wYd7rcY6jF5 veSGoZFQ9+MpjuYBHqtwiAauPAUqj8pHPAawGsaT/eNvsTHa7y43Iy38VbS2XTrF Ds6mwp8unE0cuBUFD49YLs3JrYsbuQRf5q2kX3/qvIO9QtJ/GQ6Dt/Sy3iC7Eo9Y PAVLK90sNb+LlRBKhfCAKUdgZsxdJ/kv6RfVDOiCoQDMVl/Ukv9UWB9Y =1E76 -----END PGP SIGNATURE----- Merge tag 'linux-can-fixes-for-5.3-20190724' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can Marc Kleine-Budde says: ==================== pull-request: can 2019-07-24 this is a pull reqeust of 7 patches for net/master. The first patch is by Rasmus Villemoes add a missing netif_carrier_off() to register_candev() so that generic netdev trigger based LEDs are initially off. Nikita Yushchenko's patch for the rcar_canfd driver fixes a possible IRQ storm on high load. The patch by Weitao Hou for the mcp251x driver add missing error checking to the work queue allocation. Both Wen Yang's and Joakim Zhang's patch for the flexcan driver fix a problem with the stop-mode. Stephane Grosjean contributes a patch for the peak_usb driver to fix a potential double kfree_skb(). The last patch is by YueHaibing and fixes the error path in can-gw's cgw_module_init() function. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
09ea26792a
@ -1249,6 +1249,8 @@ int register_candev(struct net_device *dev)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
dev->rtnl_link_ops = &can_link_ops;
|
dev->rtnl_link_ops = &can_link_ops;
|
||||||
|
netif_carrier_off(dev);
|
||||||
|
|
||||||
return register_netdev(dev);
|
return register_netdev(dev);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(register_candev);
|
EXPORT_SYMBOL_GPL(register_candev);
|
||||||
|
@ -400,9 +400,10 @@ static void flexcan_enable_wakeup_irq(struct flexcan_priv *priv, bool enable)
|
|||||||
priv->write(reg_mcr, ®s->mcr);
|
priv->write(reg_mcr, ®s->mcr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void flexcan_enter_stop_mode(struct flexcan_priv *priv)
|
static inline int flexcan_enter_stop_mode(struct flexcan_priv *priv)
|
||||||
{
|
{
|
||||||
struct flexcan_regs __iomem *regs = priv->regs;
|
struct flexcan_regs __iomem *regs = priv->regs;
|
||||||
|
unsigned int ackval;
|
||||||
u32 reg_mcr;
|
u32 reg_mcr;
|
||||||
|
|
||||||
reg_mcr = priv->read(®s->mcr);
|
reg_mcr = priv->read(®s->mcr);
|
||||||
@ -412,20 +413,37 @@ static inline void flexcan_enter_stop_mode(struct flexcan_priv *priv)
|
|||||||
/* enable stop request */
|
/* enable stop request */
|
||||||
regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
|
regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
|
||||||
1 << priv->stm.req_bit, 1 << priv->stm.req_bit);
|
1 << priv->stm.req_bit, 1 << priv->stm.req_bit);
|
||||||
|
|
||||||
|
/* get stop acknowledgment */
|
||||||
|
if (regmap_read_poll_timeout(priv->stm.gpr, priv->stm.ack_gpr,
|
||||||
|
ackval, ackval & (1 << priv->stm.ack_bit),
|
||||||
|
0, FLEXCAN_TIMEOUT_US))
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void flexcan_exit_stop_mode(struct flexcan_priv *priv)
|
static inline int flexcan_exit_stop_mode(struct flexcan_priv *priv)
|
||||||
{
|
{
|
||||||
struct flexcan_regs __iomem *regs = priv->regs;
|
struct flexcan_regs __iomem *regs = priv->regs;
|
||||||
|
unsigned int ackval;
|
||||||
u32 reg_mcr;
|
u32 reg_mcr;
|
||||||
|
|
||||||
/* remove stop request */
|
/* remove stop request */
|
||||||
regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
|
regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
|
||||||
1 << priv->stm.req_bit, 0);
|
1 << priv->stm.req_bit, 0);
|
||||||
|
|
||||||
|
/* get stop acknowledgment */
|
||||||
|
if (regmap_read_poll_timeout(priv->stm.gpr, priv->stm.ack_gpr,
|
||||||
|
ackval, !(ackval & (1 << priv->stm.ack_bit)),
|
||||||
|
0, FLEXCAN_TIMEOUT_US))
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
|
||||||
reg_mcr = priv->read(®s->mcr);
|
reg_mcr = priv->read(®s->mcr);
|
||||||
reg_mcr &= ~FLEXCAN_MCR_SLF_WAK;
|
reg_mcr &= ~FLEXCAN_MCR_SLF_WAK;
|
||||||
priv->write(reg_mcr, ®s->mcr);
|
priv->write(reg_mcr, ®s->mcr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void flexcan_error_irq_enable(const struct flexcan_priv *priv)
|
static inline void flexcan_error_irq_enable(const struct flexcan_priv *priv)
|
||||||
@ -1437,10 +1455,10 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev)
|
|||||||
|
|
||||||
priv = netdev_priv(dev);
|
priv = netdev_priv(dev);
|
||||||
priv->stm.gpr = syscon_node_to_regmap(gpr_np);
|
priv->stm.gpr = syscon_node_to_regmap(gpr_np);
|
||||||
of_node_put(gpr_np);
|
|
||||||
if (IS_ERR(priv->stm.gpr)) {
|
if (IS_ERR(priv->stm.gpr)) {
|
||||||
dev_dbg(&pdev->dev, "could not find gpr regmap\n");
|
dev_dbg(&pdev->dev, "could not find gpr regmap\n");
|
||||||
return PTR_ERR(priv->stm.gpr);
|
ret = PTR_ERR(priv->stm.gpr);
|
||||||
|
goto out_put_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
priv->stm.req_gpr = out_val[1];
|
priv->stm.req_gpr = out_val[1];
|
||||||
@ -1455,7 +1473,9 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev)
|
|||||||
|
|
||||||
device_set_wakeup_capable(&pdev->dev, true);
|
device_set_wakeup_capable(&pdev->dev, true);
|
||||||
|
|
||||||
return 0;
|
out_put_node:
|
||||||
|
of_node_put(gpr_np);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id flexcan_of_match[] = {
|
static const struct of_device_id flexcan_of_match[] = {
|
||||||
@ -1612,7 +1632,9 @@ static int __maybe_unused flexcan_suspend(struct device *device)
|
|||||||
*/
|
*/
|
||||||
if (device_may_wakeup(device)) {
|
if (device_may_wakeup(device)) {
|
||||||
enable_irq_wake(dev->irq);
|
enable_irq_wake(dev->irq);
|
||||||
flexcan_enter_stop_mode(priv);
|
err = flexcan_enter_stop_mode(priv);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
} else {
|
} else {
|
||||||
err = flexcan_chip_disable(priv);
|
err = flexcan_chip_disable(priv);
|
||||||
if (err)
|
if (err)
|
||||||
@ -1662,10 +1684,13 @@ static int __maybe_unused flexcan_noirq_resume(struct device *device)
|
|||||||
{
|
{
|
||||||
struct net_device *dev = dev_get_drvdata(device);
|
struct net_device *dev = dev_get_drvdata(device);
|
||||||
struct flexcan_priv *priv = netdev_priv(dev);
|
struct flexcan_priv *priv = netdev_priv(dev);
|
||||||
|
int err;
|
||||||
|
|
||||||
if (netif_running(dev) && device_may_wakeup(device)) {
|
if (netif_running(dev) && device_may_wakeup(device)) {
|
||||||
flexcan_enable_wakeup_irq(priv, false);
|
flexcan_enable_wakeup_irq(priv, false);
|
||||||
flexcan_exit_stop_mode(priv);
|
err = flexcan_exit_stop_mode(priv);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1508,10 +1508,11 @@ static int rcar_canfd_rx_poll(struct napi_struct *napi, int quota)
|
|||||||
|
|
||||||
/* All packets processed */
|
/* All packets processed */
|
||||||
if (num_pkts < quota) {
|
if (num_pkts < quota) {
|
||||||
napi_complete_done(napi, num_pkts);
|
if (napi_complete_done(napi, num_pkts)) {
|
||||||
/* Enable Rx FIFO interrupts */
|
/* Enable Rx FIFO interrupts */
|
||||||
rcar_canfd_set_bit(priv->base, RCANFD_RFCC(ridx),
|
rcar_canfd_set_bit(priv->base, RCANFD_RFCC(ridx),
|
||||||
RCANFD_RFCC_RFIE);
|
RCANFD_RFCC_RFIE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return num_pkts;
|
return num_pkts;
|
||||||
}
|
}
|
||||||
|
@ -664,17 +664,6 @@ static int mcp251x_power_enable(struct regulator *reg, int enable)
|
|||||||
return regulator_disable(reg);
|
return regulator_disable(reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mcp251x_open_clean(struct net_device *net)
|
|
||||||
{
|
|
||||||
struct mcp251x_priv *priv = netdev_priv(net);
|
|
||||||
struct spi_device *spi = priv->spi;
|
|
||||||
|
|
||||||
free_irq(spi->irq, priv);
|
|
||||||
mcp251x_hw_sleep(spi);
|
|
||||||
mcp251x_power_enable(priv->transceiver, 0);
|
|
||||||
close_candev(net);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mcp251x_stop(struct net_device *net)
|
static int mcp251x_stop(struct net_device *net)
|
||||||
{
|
{
|
||||||
struct mcp251x_priv *priv = netdev_priv(net);
|
struct mcp251x_priv *priv = netdev_priv(net);
|
||||||
@ -940,37 +929,43 @@ static int mcp251x_open(struct net_device *net)
|
|||||||
flags | IRQF_ONESHOT, DEVICE_NAME, priv);
|
flags | IRQF_ONESHOT, DEVICE_NAME, priv);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq);
|
dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq);
|
||||||
mcp251x_power_enable(priv->transceiver, 0);
|
goto out_close;
|
||||||
close_candev(net);
|
|
||||||
goto open_unlock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
priv->wq = alloc_workqueue("mcp251x_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM,
|
priv->wq = alloc_workqueue("mcp251x_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM,
|
||||||
0);
|
0);
|
||||||
|
if (!priv->wq) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out_clean;
|
||||||
|
}
|
||||||
INIT_WORK(&priv->tx_work, mcp251x_tx_work_handler);
|
INIT_WORK(&priv->tx_work, mcp251x_tx_work_handler);
|
||||||
INIT_WORK(&priv->restart_work, mcp251x_restart_work_handler);
|
INIT_WORK(&priv->restart_work, mcp251x_restart_work_handler);
|
||||||
|
|
||||||
ret = mcp251x_hw_reset(spi);
|
ret = mcp251x_hw_reset(spi);
|
||||||
if (ret) {
|
if (ret)
|
||||||
mcp251x_open_clean(net);
|
goto out_free_wq;
|
||||||
goto open_unlock;
|
|
||||||
}
|
|
||||||
ret = mcp251x_setup(net, spi);
|
ret = mcp251x_setup(net, spi);
|
||||||
if (ret) {
|
if (ret)
|
||||||
mcp251x_open_clean(net);
|
goto out_free_wq;
|
||||||
goto open_unlock;
|
|
||||||
}
|
|
||||||
ret = mcp251x_set_normal_mode(spi);
|
ret = mcp251x_set_normal_mode(spi);
|
||||||
if (ret) {
|
if (ret)
|
||||||
mcp251x_open_clean(net);
|
goto out_free_wq;
|
||||||
goto open_unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
can_led_event(net, CAN_LED_EVENT_OPEN);
|
can_led_event(net, CAN_LED_EVENT_OPEN);
|
||||||
|
|
||||||
netif_wake_queue(net);
|
netif_wake_queue(net);
|
||||||
|
mutex_unlock(&priv->mcp_lock);
|
||||||
|
|
||||||
open_unlock:
|
return 0;
|
||||||
|
|
||||||
|
out_free_wq:
|
||||||
|
destroy_workqueue(priv->wq);
|
||||||
|
out_clean:
|
||||||
|
free_irq(spi->irq, priv);
|
||||||
|
mcp251x_hw_sleep(spi);
|
||||||
|
out_close:
|
||||||
|
mcp251x_power_enable(priv->transceiver, 0);
|
||||||
|
close_candev(net);
|
||||||
mutex_unlock(&priv->mcp_lock);
|
mutex_unlock(&priv->mcp_lock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -568,16 +568,16 @@ static int peak_usb_ndo_stop(struct net_device *netdev)
|
|||||||
dev->state &= ~PCAN_USB_STATE_STARTED;
|
dev->state &= ~PCAN_USB_STATE_STARTED;
|
||||||
netif_stop_queue(netdev);
|
netif_stop_queue(netdev);
|
||||||
|
|
||||||
|
close_candev(netdev);
|
||||||
|
|
||||||
|
dev->can.state = CAN_STATE_STOPPED;
|
||||||
|
|
||||||
/* unlink all pending urbs and free used memory */
|
/* unlink all pending urbs and free used memory */
|
||||||
peak_usb_unlink_all_urbs(dev);
|
peak_usb_unlink_all_urbs(dev);
|
||||||
|
|
||||||
if (dev->adapter->dev_stop)
|
if (dev->adapter->dev_stop)
|
||||||
dev->adapter->dev_stop(dev);
|
dev->adapter->dev_stop(dev);
|
||||||
|
|
||||||
close_candev(netdev);
|
|
||||||
|
|
||||||
dev->can.state = CAN_STATE_STOPPED;
|
|
||||||
|
|
||||||
/* can set bus off now */
|
/* can set bus off now */
|
||||||
if (dev->adapter->dev_set_bus) {
|
if (dev->adapter->dev_set_bus) {
|
||||||
int err = dev->adapter->dev_set_bus(dev, 0);
|
int err = dev->adapter->dev_set_bus(dev, 0);
|
||||||
|
46
net/can/gw.c
46
net/can/gw.c
@ -1046,32 +1046,50 @@ static __init int cgw_module_init(void)
|
|||||||
pr_info("can: netlink gateway (rev " CAN_GW_VERSION ") max_hops=%d\n",
|
pr_info("can: netlink gateway (rev " CAN_GW_VERSION ") max_hops=%d\n",
|
||||||
max_hops);
|
max_hops);
|
||||||
|
|
||||||
register_pernet_subsys(&cangw_pernet_ops);
|
ret = register_pernet_subsys(&cangw_pernet_ops);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = -ENOMEM;
|
||||||
cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job),
|
cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job),
|
||||||
0, 0, NULL);
|
0, 0, NULL);
|
||||||
|
|
||||||
if (!cgw_cache)
|
if (!cgw_cache)
|
||||||
return -ENOMEM;
|
goto out_cache_create;
|
||||||
|
|
||||||
/* set notifier */
|
/* set notifier */
|
||||||
notifier.notifier_call = cgw_notifier;
|
notifier.notifier_call = cgw_notifier;
|
||||||
register_netdevice_notifier(¬ifier);
|
ret = register_netdevice_notifier(¬ifier);
|
||||||
|
if (ret)
|
||||||
|
goto out_register_notifier;
|
||||||
|
|
||||||
ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_GETROUTE,
|
ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_GETROUTE,
|
||||||
NULL, cgw_dump_jobs, 0);
|
NULL, cgw_dump_jobs, 0);
|
||||||
if (ret) {
|
if (ret)
|
||||||
unregister_netdevice_notifier(¬ifier);
|
goto out_rtnl_register1;
|
||||||
kmem_cache_destroy(cgw_cache);
|
|
||||||
return -ENOBUFS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Only the first call to rtnl_register_module can fail */
|
ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_NEWROUTE,
|
||||||
rtnl_register_module(THIS_MODULE, PF_CAN, RTM_NEWROUTE,
|
cgw_create_job, NULL, 0);
|
||||||
cgw_create_job, NULL, 0);
|
if (ret)
|
||||||
rtnl_register_module(THIS_MODULE, PF_CAN, RTM_DELROUTE,
|
goto out_rtnl_register2;
|
||||||
cgw_remove_job, NULL, 0);
|
ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_DELROUTE,
|
||||||
|
cgw_remove_job, NULL, 0);
|
||||||
|
if (ret)
|
||||||
|
goto out_rtnl_register3;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
out_rtnl_register3:
|
||||||
|
rtnl_unregister(PF_CAN, RTM_NEWROUTE);
|
||||||
|
out_rtnl_register2:
|
||||||
|
rtnl_unregister(PF_CAN, RTM_GETROUTE);
|
||||||
|
out_rtnl_register1:
|
||||||
|
unregister_netdevice_notifier(¬ifier);
|
||||||
|
out_register_notifier:
|
||||||
|
kmem_cache_destroy(cgw_cache);
|
||||||
|
out_cache_create:
|
||||||
|
unregister_pernet_subsys(&cangw_pernet_ops);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static __exit void cgw_module_exit(void)
|
static __exit void cgw_module_exit(void)
|
||||||
|
Loading…
Reference in New Issue
Block a user