Merge branches 'x86-cleanups-for-linus' and 'x86-cpufeature-for-linus' of git://git...
[firefly-linux-kernel-4.4.55.git] / net / can / af_can.c
index 0ce2ad0696da81287b438ea1357320dd064d43e3..821022a7214f544090eb8504bce180484bf0d79e 100644 (file)
@@ -41,6 +41,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/stddef.h>
 #include <linux/init.h>
 #include <linux/kmod.h>
 #include <linux/slab.h>
@@ -220,30 +221,46 @@ static int can_create(struct net *net, struct socket *sock, int protocol,
  *  -ENOBUFS on full driver queue (see net_xmit_errno())
  *  -ENOMEM when local loopback failed at calling skb_clone()
  *  -EPERM when trying to send on a non-CAN interface
+ *  -EMSGSIZE CAN frame size is bigger than CAN interface MTU
  *  -EINVAL when the skb->data does not contain a valid CAN frame
  */
 int can_send(struct sk_buff *skb, int loop)
 {
        struct sk_buff *newskb = NULL;
-       struct can_frame *cf = (struct can_frame *)skb->data;
-       int err;
+       struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
+       int err = -EINVAL;
+
+       if (skb->len == CAN_MTU) {
+               skb->protocol = htons(ETH_P_CAN);
+               if (unlikely(cfd->len > CAN_MAX_DLEN))
+                       goto inval_skb;
+       } else if (skb->len == CANFD_MTU) {
+               skb->protocol = htons(ETH_P_CANFD);
+               if (unlikely(cfd->len > CANFD_MAX_DLEN))
+                       goto inval_skb;
+       } else
+               goto inval_skb;
 
-       if (skb->len != sizeof(struct can_frame) || cf->can_dlc > 8) {
-               kfree_skb(skb);
-               return -EINVAL;
+       /*
+        * Make sure the CAN frame can pass the selected CAN netdevice.
+        * As structs can_frame and canfd_frame are similar, we can provide
+        * CAN FD frames to legacy CAN drivers as long as the length is <= 8
+        */
+       if (unlikely(skb->len > skb->dev->mtu && cfd->len > CAN_MAX_DLEN)) {
+               err = -EMSGSIZE;
+               goto inval_skb;
        }
 
-       if (skb->dev->type != ARPHRD_CAN) {
-               kfree_skb(skb);
-               return -EPERM;
+       if (unlikely(skb->dev->type != ARPHRD_CAN)) {
+               err = -EPERM;
+               goto inval_skb;
        }
 
-       if (!(skb->dev->flags & IFF_UP)) {
-               kfree_skb(skb);
-               return -ENETDOWN;
+       if (unlikely(!(skb->dev->flags & IFF_UP))) {
+               err = -ENETDOWN;
+               goto inval_skb;
        }
 
-       skb->protocol = htons(ETH_P_CAN);
        skb_reset_network_header(skb);
        skb_reset_transport_header(skb);
 
@@ -300,6 +317,10 @@ int can_send(struct sk_buff *skb, int loop)
        can_stats.tx_frames_delta++;
 
        return 0;
+
+inval_skb:
+       kfree_skb(skb);
+       return err;
 }
 EXPORT_SYMBOL(can_send);
 
@@ -334,8 +355,8 @@ static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
  *  relevant bits for the filter.
  *
  *  The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
- *  filter for error frames (CAN_ERR_FLAG bit set in mask). For error frames
- *  there is a special filterlist and a special rx path filter handling.
+ *  filter for error messages (CAN_ERR_FLAG bit set in mask). For error msg
+ *  frames there is a special filterlist and a special rx path filter handling.
  *
  * Return:
  *  Pointer to optimal filterlist for the given can_id/mask pair.
@@ -347,7 +368,7 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
 {
        canid_t inv = *can_id & CAN_INV_FILTER; /* save flag before masking */
 
-       /* filter for error frames in extra filterlist */
+       /* filter for error message frames in extra filterlist */
        if (*mask & CAN_ERR_FLAG) {
                /* clear CAN_ERR_FLAG in filter entry */
                *mask &= CAN_ERR_MASK;
@@ -408,7 +429,7 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
  *          <received_can_id> & mask == can_id & mask
  *
  *  The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
- *  filter for error frames (CAN_ERR_FLAG bit set in mask).
+ *  filter for error message frames (CAN_ERR_FLAG bit set in mask).
  *
  *  The provided pointer to the sk_buff is guaranteed to be valid as long as
  *  the callback function is running. The callback function must *not* free
@@ -578,7 +599,7 @@ static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
                return 0;
 
        if (can_id & CAN_ERR_FLAG) {
-               /* check for error frame entries only */
+               /* check for error message frame entries only */
                hlist_for_each_entry_rcu(r, n, &d->rx[RX_ERR], list) {
                        if (can_id & r->mask) {
                                deliver(skb, r);
@@ -632,24 +653,11 @@ static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
        return matches;
 }
 
-static int can_rcv(struct sk_buff *skb, struct net_device *dev,
-                  struct packet_type *pt, struct net_device *orig_dev)
+static void can_receive(struct sk_buff *skb, struct net_device *dev)
 {
        struct dev_rcv_lists *d;
-       struct can_frame *cf = (struct can_frame *)skb->data;
        int matches;
 
-       if (!net_eq(dev_net(dev), &init_net))
-               goto drop;
-
-       if (WARN_ONCE(dev->type != ARPHRD_CAN ||
-                     skb->len != sizeof(struct can_frame) ||
-                     cf->can_dlc > 8,
-                     "PF_CAN: dropped non conform skbuf: "
-                     "dev type %d, len %d, can_dlc %d\n",
-                     dev->type, skb->len, cf->can_dlc))
-               goto drop;
-
        /* update statistics */
        can_stats.rx_frames++;
        can_stats.rx_frames_delta++;
@@ -673,7 +681,49 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev,
                can_stats.matches++;
                can_stats.matches_delta++;
        }
+}
 
+static int can_rcv(struct sk_buff *skb, struct net_device *dev,
+                  struct packet_type *pt, struct net_device *orig_dev)
+{
+       struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
+
+       if (unlikely(!net_eq(dev_net(dev), &init_net)))
+               goto drop;
+
+       if (WARN_ONCE(dev->type != ARPHRD_CAN ||
+                     skb->len != CAN_MTU ||
+                     cfd->len > CAN_MAX_DLEN,
+                     "PF_CAN: dropped non conform CAN skbuf: "
+                     "dev type %d, len %d, datalen %d\n",
+                     dev->type, skb->len, cfd->len))
+               goto drop;
+
+       can_receive(skb, dev);
+       return NET_RX_SUCCESS;
+
+drop:
+       kfree_skb(skb);
+       return NET_RX_DROP;
+}
+
+static int canfd_rcv(struct sk_buff *skb, struct net_device *dev,
+                  struct packet_type *pt, struct net_device *orig_dev)
+{
+       struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
+
+       if (unlikely(!net_eq(dev_net(dev), &init_net)))
+               goto drop;
+
+       if (WARN_ONCE(dev->type != ARPHRD_CAN ||
+                     skb->len != CANFD_MTU ||
+                     cfd->len > CANFD_MAX_DLEN,
+                     "PF_CAN: dropped non conform CAN FD skbuf: "
+                     "dev type %d, len %d, datalen %d\n",
+                     dev->type, skb->len, cfd->len))
+               goto drop;
+
+       can_receive(skb, dev);
        return NET_RX_SUCCESS;
 
 drop:
@@ -807,10 +857,14 @@ static int can_notifier(struct notifier_block *nb, unsigned long msg,
 
 static struct packet_type can_packet __read_mostly = {
        .type = cpu_to_be16(ETH_P_CAN),
-       .dev  = NULL,
        .func = can_rcv,
 };
 
+static struct packet_type canfd_packet __read_mostly = {
+       .type = cpu_to_be16(ETH_P_CANFD),
+       .func = canfd_rcv,
+};
+
 static const struct net_proto_family can_family_ops = {
        .family = PF_CAN,
        .create = can_create,
@@ -824,6 +878,12 @@ static struct notifier_block can_netdev_notifier __read_mostly = {
 
 static __init int can_init(void)
 {
+       /* check for correct padding to be able to use the structs similarly */
+       BUILD_BUG_ON(offsetof(struct can_frame, can_dlc) !=
+                    offsetof(struct canfd_frame, len) ||
+                    offsetof(struct can_frame, data) !=
+                    offsetof(struct canfd_frame, data));
+
        printk(banner);
 
        memset(&can_rx_alldev_list, 0, sizeof(can_rx_alldev_list));
@@ -846,6 +906,7 @@ static __init int can_init(void)
        sock_register(&can_family_ops);
        register_netdevice_notifier(&can_netdev_notifier);
        dev_add_pack(&can_packet);
+       dev_add_pack(&canfd_packet);
 
        return 0;
 }
@@ -860,6 +921,7 @@ static __exit void can_exit(void)
        can_remove_proc();
 
        /* protocol unregister */
+       dev_remove_pack(&canfd_packet);
        dev_remove_pack(&can_packet);
        unregister_netdevice_notifier(&can_netdev_notifier);
        sock_unregister(PF_CAN);