#include <linux/mutex.h>
 #include <linux/if_vlan.h>
 
+#if IS_ENABLED(CONFIG_IPV6)
+#include <linux/icmpv6.h>
+#endif
+
+#include <net/icmp.h>
+#include <net/route.h>
+
 #include <asm/vio.h>
 #include <asm/ldc.h>
 
        if (unlikely(!skb))
                goto out_dropped;
 
-       if (skb->len > port->rmtu)
+       if (skb->len > port->rmtu) {
+               unsigned long localmtu = port->rmtu - ETH_HLEN;
+
+               if (vio_version_after_eq(&port->vio, 1, 3))
+                       localmtu -= VLAN_HLEN;
+
+               if (skb->protocol == htons(ETH_P_IP)) {
+                       struct flowi4 fl4;
+                       struct rtable *rt = NULL;
+
+                       memset(&fl4, 0, sizeof(fl4));
+                       fl4.flowi4_oif = dev->ifindex;
+                       fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos);
+                       fl4.daddr = ip_hdr(skb)->daddr;
+                       fl4.saddr = ip_hdr(skb)->saddr;
+
+                       rt = ip_route_output_key(dev_net(dev), &fl4);
+                       if (!IS_ERR(rt)) {
+                               skb_dst_set(skb, &rt->dst);
+                               icmp_send(skb, ICMP_DEST_UNREACH,
+                                         ICMP_FRAG_NEEDED,
+                                         htonl(localmtu));
+                       }
+               }
+#if IS_ENABLED(CONFIG_IPV6)
+               else if (skb->protocol == htons(ETH_P_IPV6))
+                       icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, localmtu);
+#endif
                goto out_dropped;
+       }
 
        spin_lock_irqsave(&port->vio.lock, flags);