diff options
author | Ahmed S. Darwish <ahmed.darwish@valeo.com> | 2015-01-26 00:27:19 -0500 |
---|---|---|
committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2015-01-28 07:39:37 -0500 |
commit | 3b07a444ee48a8081d2ed3f0abeeabd038404ac1 (patch) | |
tree | ea72a3af95e8f495b736d3c684d464c708673462 /drivers/net/can/usb | |
parent | ff660f75be36e6db75d45dc742a4a468c5a9c20d (diff) |
can: kvaser_usb: Update interface state before exiting on OOM
Update all of the can interface's state and error counters before
trying any skb allocation that can actually fail with -ENOMEM.
Suggested-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: Ahmed S. Darwish <ahmed.darwish@valeo.com>
Acked-by: Andri Yngvason <andri.yngvason@marel.com>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Diffstat (limited to 'drivers/net/can/usb')
-rw-r--r-- | drivers/net/can/usb/kvaser_usb.c | 181 |
1 files changed, 105 insertions, 76 deletions
diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c index 7af379ca861b..f57ce556c678 100644 --- a/drivers/net/can/usb/kvaser_usb.c +++ b/drivers/net/can/usb/kvaser_usb.c | |||
@@ -273,6 +273,10 @@ struct kvaser_msg { | |||
273 | } u; | 273 | } u; |
274 | } __packed; | 274 | } __packed; |
275 | 275 | ||
276 | struct kvaser_usb_error_summary { | ||
277 | u8 channel, status, txerr, rxerr, error_factor; | ||
278 | }; | ||
279 | |||
276 | struct kvaser_usb_tx_urb_context { | 280 | struct kvaser_usb_tx_urb_context { |
277 | struct kvaser_usb_net_priv *priv; | 281 | struct kvaser_usb_net_priv *priv; |
278 | u32 echo_index; | 282 | u32 echo_index; |
@@ -615,6 +619,54 @@ static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv) | |||
615 | priv->tx_contexts[i].echo_index = MAX_TX_URBS; | 619 | priv->tx_contexts[i].echo_index = MAX_TX_URBS; |
616 | } | 620 | } |
617 | 621 | ||
622 | static void kvaser_usb_rx_error_update_can_state(struct kvaser_usb_net_priv *priv, | ||
623 | const struct kvaser_usb_error_summary *es) | ||
624 | { | ||
625 | struct net_device_stats *stats; | ||
626 | enum can_state new_state; | ||
627 | |||
628 | stats = &priv->netdev->stats; | ||
629 | new_state = priv->can.state; | ||
630 | |||
631 | netdev_dbg(priv->netdev, "Error status: 0x%02x\n", es->status); | ||
632 | |||
633 | if (es->status & (M16C_STATE_BUS_OFF | M16C_STATE_BUS_RESET)) { | ||
634 | priv->can.can_stats.bus_off++; | ||
635 | new_state = CAN_STATE_BUS_OFF; | ||
636 | } else if (es->status & M16C_STATE_BUS_PASSIVE) { | ||
637 | if (priv->can.state != CAN_STATE_ERROR_PASSIVE) | ||
638 | priv->can.can_stats.error_passive++; | ||
639 | new_state = CAN_STATE_ERROR_PASSIVE; | ||
640 | } else if (es->status & M16C_STATE_BUS_ERROR) { | ||
641 | if ((priv->can.state < CAN_STATE_ERROR_WARNING) && | ||
642 | ((es->txerr >= 96) || (es->rxerr >= 96))) { | ||
643 | priv->can.can_stats.error_warning++; | ||
644 | new_state = CAN_STATE_ERROR_WARNING; | ||
645 | } else if ((priv->can.state > CAN_STATE_ERROR_ACTIVE) && | ||
646 | ((es->txerr < 96) && (es->rxerr < 96))) { | ||
647 | new_state = CAN_STATE_ERROR_ACTIVE; | ||
648 | } | ||
649 | } | ||
650 | |||
651 | if (!es->status) | ||
652 | new_state = CAN_STATE_ERROR_ACTIVE; | ||
653 | |||
654 | if (priv->can.restart_ms && | ||
655 | (priv->can.state >= CAN_STATE_BUS_OFF) && | ||
656 | (new_state < CAN_STATE_BUS_OFF)) { | ||
657 | priv->can.can_stats.restarts++; | ||
658 | } | ||
659 | |||
660 | if (es->error_factor) { | ||
661 | priv->can.can_stats.bus_error++; | ||
662 | stats->rx_errors++; | ||
663 | } | ||
664 | |||
665 | priv->bec.txerr = es->txerr; | ||
666 | priv->bec.rxerr = es->rxerr; | ||
667 | priv->can.state = new_state; | ||
668 | } | ||
669 | |||
618 | static void kvaser_usb_rx_error(const struct kvaser_usb *dev, | 670 | static void kvaser_usb_rx_error(const struct kvaser_usb *dev, |
619 | const struct kvaser_msg *msg) | 671 | const struct kvaser_msg *msg) |
620 | { | 672 | { |
@@ -622,30 +674,30 @@ static void kvaser_usb_rx_error(const struct kvaser_usb *dev, | |||
622 | struct sk_buff *skb; | 674 | struct sk_buff *skb; |
623 | struct net_device_stats *stats; | 675 | struct net_device_stats *stats; |
624 | struct kvaser_usb_net_priv *priv; | 676 | struct kvaser_usb_net_priv *priv; |
625 | unsigned int new_state; | 677 | struct kvaser_usb_error_summary es = { }; |
626 | u8 channel, status, txerr, rxerr, error_factor; | 678 | enum can_state old_state; |
627 | 679 | ||
628 | switch (msg->id) { | 680 | switch (msg->id) { |
629 | case CMD_CAN_ERROR_EVENT: | 681 | case CMD_CAN_ERROR_EVENT: |
630 | channel = msg->u.error_event.channel; | 682 | es.channel = msg->u.error_event.channel; |
631 | status = msg->u.error_event.status; | 683 | es.status = msg->u.error_event.status; |
632 | txerr = msg->u.error_event.tx_errors_count; | 684 | es.txerr = msg->u.error_event.tx_errors_count; |
633 | rxerr = msg->u.error_event.rx_errors_count; | 685 | es.rxerr = msg->u.error_event.rx_errors_count; |
634 | error_factor = msg->u.error_event.error_factor; | 686 | es.error_factor = msg->u.error_event.error_factor; |
635 | break; | 687 | break; |
636 | case CMD_LOG_MESSAGE: | 688 | case CMD_LOG_MESSAGE: |
637 | channel = msg->u.log_message.channel; | 689 | es.channel = msg->u.log_message.channel; |
638 | status = msg->u.log_message.data[0]; | 690 | es.status = msg->u.log_message.data[0]; |
639 | txerr = msg->u.log_message.data[2]; | 691 | es.txerr = msg->u.log_message.data[2]; |
640 | rxerr = msg->u.log_message.data[3]; | 692 | es.rxerr = msg->u.log_message.data[3]; |
641 | error_factor = msg->u.log_message.data[1]; | 693 | es.error_factor = msg->u.log_message.data[1]; |
642 | break; | 694 | break; |
643 | case CMD_CHIP_STATE_EVENT: | 695 | case CMD_CHIP_STATE_EVENT: |
644 | channel = msg->u.chip_state_event.channel; | 696 | es.channel = msg->u.chip_state_event.channel; |
645 | status = msg->u.chip_state_event.status; | 697 | es.status = msg->u.chip_state_event.status; |
646 | txerr = msg->u.chip_state_event.tx_errors_count; | 698 | es.txerr = msg->u.chip_state_event.tx_errors_count; |
647 | rxerr = msg->u.chip_state_event.rx_errors_count; | 699 | es.rxerr = msg->u.chip_state_event.rx_errors_count; |
648 | error_factor = 0; | 700 | es.error_factor = 0; |
649 | break; | 701 | break; |
650 | default: | 702 | default: |
651 | dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n", | 703 | dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n", |
@@ -653,116 +705,93 @@ static void kvaser_usb_rx_error(const struct kvaser_usb *dev, | |||
653 | return; | 705 | return; |
654 | } | 706 | } |
655 | 707 | ||
656 | if (channel >= dev->nchannels) { | 708 | if (es.channel >= dev->nchannels) { |
657 | dev_err(dev->udev->dev.parent, | 709 | dev_err(dev->udev->dev.parent, |
658 | "Invalid channel number (%d)\n", channel); | 710 | "Invalid channel number (%d)\n", es.channel); |
659 | return; | 711 | return; |
660 | } | 712 | } |
661 | 713 | ||
662 | priv = dev->nets[channel]; | 714 | priv = dev->nets[es.channel]; |
663 | stats = &priv->netdev->stats; | 715 | stats = &priv->netdev->stats; |
664 | 716 | ||
717 | /* Update all of the can interface's state and error counters before | ||
718 | * trying any skb allocation that can actually fail with -ENOMEM. | ||
719 | */ | ||
720 | old_state = priv->can.state; | ||
721 | kvaser_usb_rx_error_update_can_state(priv, &es); | ||
722 | |||
665 | skb = alloc_can_err_skb(priv->netdev, &cf); | 723 | skb = alloc_can_err_skb(priv->netdev, &cf); |
666 | if (!skb) { | 724 | if (!skb) { |
667 | stats->rx_dropped++; | 725 | stats->rx_dropped++; |
668 | return; | 726 | return; |
669 | } | 727 | } |
670 | 728 | ||
671 | new_state = priv->can.state; | 729 | if (es.status & (M16C_STATE_BUS_OFF | M16C_STATE_BUS_RESET)) { |
672 | |||
673 | netdev_dbg(priv->netdev, "Error status: 0x%02x\n", status); | ||
674 | |||
675 | if (status & (M16C_STATE_BUS_OFF | M16C_STATE_BUS_RESET)) { | ||
676 | cf->can_id |= CAN_ERR_BUSOFF; | 730 | cf->can_id |= CAN_ERR_BUSOFF; |
677 | 731 | ||
678 | priv->can.can_stats.bus_off++; | ||
679 | if (!priv->can.restart_ms) | 732 | if (!priv->can.restart_ms) |
680 | kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP); | 733 | kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP); |
681 | |||
682 | netif_carrier_off(priv->netdev); | 734 | netif_carrier_off(priv->netdev); |
683 | 735 | } else if (es.status & M16C_STATE_BUS_PASSIVE) { | |
684 | new_state = CAN_STATE_BUS_OFF; | 736 | if (old_state != CAN_STATE_ERROR_PASSIVE) { |
685 | } else if (status & M16C_STATE_BUS_PASSIVE) { | ||
686 | if (priv->can.state != CAN_STATE_ERROR_PASSIVE) { | ||
687 | cf->can_id |= CAN_ERR_CRTL; | 737 | cf->can_id |= CAN_ERR_CRTL; |
688 | 738 | ||
689 | if (txerr || rxerr) | 739 | if (es.txerr || es.rxerr) |
690 | cf->data[1] = (txerr > rxerr) | 740 | cf->data[1] = (es.txerr > es.rxerr) |
691 | ? CAN_ERR_CRTL_TX_PASSIVE | 741 | ? CAN_ERR_CRTL_TX_PASSIVE |
692 | : CAN_ERR_CRTL_RX_PASSIVE; | 742 | : CAN_ERR_CRTL_RX_PASSIVE; |
693 | else | 743 | else |
694 | cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE | | 744 | cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE | |
695 | CAN_ERR_CRTL_RX_PASSIVE; | 745 | CAN_ERR_CRTL_RX_PASSIVE; |
696 | |||
697 | priv->can.can_stats.error_passive++; | ||
698 | } | 746 | } |
699 | 747 | } else if (es.status & M16C_STATE_BUS_ERROR) { | |
700 | new_state = CAN_STATE_ERROR_PASSIVE; | 748 | if ((old_state < CAN_STATE_ERROR_WARNING) && |
701 | } else if (status & M16C_STATE_BUS_ERROR) { | 749 | ((es.txerr >= 96) || (es.rxerr >= 96))) { |
702 | if ((priv->can.state < CAN_STATE_ERROR_WARNING) && | ||
703 | ((txerr >= 96) || (rxerr >= 96))) { | ||
704 | cf->can_id |= CAN_ERR_CRTL; | 750 | cf->can_id |= CAN_ERR_CRTL; |
705 | cf->data[1] = (txerr > rxerr) | 751 | cf->data[1] = (es.txerr > es.rxerr) |
706 | ? CAN_ERR_CRTL_TX_WARNING | 752 | ? CAN_ERR_CRTL_TX_WARNING |
707 | : CAN_ERR_CRTL_RX_WARNING; | 753 | : CAN_ERR_CRTL_RX_WARNING; |
708 | 754 | } else if ((old_state > CAN_STATE_ERROR_ACTIVE) && | |
709 | priv->can.can_stats.error_warning++; | 755 | ((es.txerr < 96) && (es.rxerr < 96))) { |
710 | new_state = CAN_STATE_ERROR_WARNING; | ||
711 | } else if ((priv->can.state > CAN_STATE_ERROR_ACTIVE) && | ||
712 | ((txerr < 96) && (rxerr < 96))) { | ||
713 | cf->can_id |= CAN_ERR_PROT; | 756 | cf->can_id |= CAN_ERR_PROT; |
714 | cf->data[2] = CAN_ERR_PROT_ACTIVE; | 757 | cf->data[2] = CAN_ERR_PROT_ACTIVE; |
715 | |||
716 | new_state = CAN_STATE_ERROR_ACTIVE; | ||
717 | } | 758 | } |
718 | } | 759 | } |
719 | 760 | ||
720 | if (!status) { | 761 | if (!es.status) { |
721 | cf->can_id |= CAN_ERR_PROT; | 762 | cf->can_id |= CAN_ERR_PROT; |
722 | cf->data[2] = CAN_ERR_PROT_ACTIVE; | 763 | cf->data[2] = CAN_ERR_PROT_ACTIVE; |
723 | |||
724 | new_state = CAN_STATE_ERROR_ACTIVE; | ||
725 | } | 764 | } |
726 | 765 | ||
727 | if (priv->can.restart_ms && | 766 | if (priv->can.restart_ms && |
728 | (priv->can.state >= CAN_STATE_BUS_OFF) && | 767 | (old_state >= CAN_STATE_BUS_OFF) && |
729 | (new_state < CAN_STATE_BUS_OFF)) { | 768 | (priv->can.state < CAN_STATE_BUS_OFF)) { |
730 | cf->can_id |= CAN_ERR_RESTARTED; | 769 | cf->can_id |= CAN_ERR_RESTARTED; |
731 | netif_carrier_on(priv->netdev); | 770 | netif_carrier_on(priv->netdev); |
732 | |||
733 | priv->can.can_stats.restarts++; | ||
734 | } | 771 | } |
735 | 772 | ||
736 | if (error_factor) { | 773 | if (es.error_factor) { |
737 | priv->can.can_stats.bus_error++; | ||
738 | stats->rx_errors++; | ||
739 | |||
740 | cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; | 774 | cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; |
741 | 775 | ||
742 | if (error_factor & M16C_EF_ACKE) | 776 | if (es.error_factor & M16C_EF_ACKE) |
743 | cf->data[3] |= (CAN_ERR_PROT_LOC_ACK); | 777 | cf->data[3] |= (CAN_ERR_PROT_LOC_ACK); |
744 | if (error_factor & M16C_EF_CRCE) | 778 | if (es.error_factor & M16C_EF_CRCE) |
745 | cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ | | 779 | cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ | |
746 | CAN_ERR_PROT_LOC_CRC_DEL); | 780 | CAN_ERR_PROT_LOC_CRC_DEL); |
747 | if (error_factor & M16C_EF_FORME) | 781 | if (es.error_factor & M16C_EF_FORME) |
748 | cf->data[2] |= CAN_ERR_PROT_FORM; | 782 | cf->data[2] |= CAN_ERR_PROT_FORM; |
749 | if (error_factor & M16C_EF_STFE) | 783 | if (es.error_factor & M16C_EF_STFE) |
750 | cf->data[2] |= CAN_ERR_PROT_STUFF; | 784 | cf->data[2] |= CAN_ERR_PROT_STUFF; |
751 | if (error_factor & M16C_EF_BITE0) | 785 | if (es.error_factor & M16C_EF_BITE0) |
752 | cf->data[2] |= CAN_ERR_PROT_BIT0; | 786 | cf->data[2] |= CAN_ERR_PROT_BIT0; |
753 | if (error_factor & M16C_EF_BITE1) | 787 | if (es.error_factor & M16C_EF_BITE1) |
754 | cf->data[2] |= CAN_ERR_PROT_BIT1; | 788 | cf->data[2] |= CAN_ERR_PROT_BIT1; |
755 | if (error_factor & M16C_EF_TRE) | 789 | if (es.error_factor & M16C_EF_TRE) |
756 | cf->data[2] |= CAN_ERR_PROT_TX; | 790 | cf->data[2] |= CAN_ERR_PROT_TX; |
757 | } | 791 | } |
758 | 792 | ||
759 | cf->data[6] = txerr; | 793 | cf->data[6] = es.txerr; |
760 | cf->data[7] = rxerr; | 794 | cf->data[7] = es.rxerr; |
761 | |||
762 | priv->bec.txerr = txerr; | ||
763 | priv->bec.rxerr = rxerr; | ||
764 | |||
765 | priv->can.state = new_state; | ||
766 | 795 | ||
767 | stats->rx_packets++; | 796 | stats->rx_packets++; |
768 | stats->rx_bytes += cf->can_dlc; | 797 | stats->rx_bytes += cf->can_dlc; |
@@ -786,6 +815,9 @@ static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv, | |||
786 | } | 815 | } |
787 | 816 | ||
788 | if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) { | 817 | if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) { |
818 | stats->rx_over_errors++; | ||
819 | stats->rx_errors++; | ||
820 | |||
789 | skb = alloc_can_err_skb(priv->netdev, &cf); | 821 | skb = alloc_can_err_skb(priv->netdev, &cf); |
790 | if (!skb) { | 822 | if (!skb) { |
791 | stats->rx_dropped++; | 823 | stats->rx_dropped++; |
@@ -795,9 +827,6 @@ static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv, | |||
795 | cf->can_id |= CAN_ERR_CRTL; | 827 | cf->can_id |= CAN_ERR_CRTL; |
796 | cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; | 828 | cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; |
797 | 829 | ||
798 | stats->rx_over_errors++; | ||
799 | stats->rx_errors++; | ||
800 | |||
801 | stats->rx_packets++; | 830 | stats->rx_packets++; |
802 | stats->rx_bytes += cf->can_dlc; | 831 | stats->rx_bytes += cf->can_dlc; |
803 | netif_rx(skb); | 832 | netif_rx(skb); |