WAN: HDLC: Call notifiers before and after changing device type
authorAndrew Lunn <andrew@lunn.ch>
Thu, 3 Dec 2015 20:12:31 +0000 (21:12 +0100)
committerDavid S. Miller <davem@davemloft.net>
Sat, 5 Dec 2015 22:41:42 +0000 (17:41 -0500)
An HDLC device can change type when the protocol driver is changed.
Calling the notifier change allows potential users of the interface
know about this planned change, and even block it. After the change
has occurred, send a second notification to users can evaluate the new
device type etc.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/wan/hdlc.c
drivers/net/wan/hdlc_cisco.c
drivers/net/wan/hdlc_fr.c
drivers/net/wan/hdlc_ppp.c
drivers/net/wan/hdlc_raw.c
drivers/net/wan/hdlc_raw_eth.c
drivers/net/wan/hdlc_x25.c
include/linux/hdlc.h

index 2a6595b4ae15896bf55e212f8946f50b45251147..9bd4aa8083ce787cc93ed5cfe416175b8f2b7d24 100644 (file)
@@ -276,7 +276,11 @@ void unregister_hdlc_device(struct net_device *dev)
 int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto,
                         size_t size)
 {
-       detach_hdlc_protocol(dev);
+       int err;
+
+       err = detach_hdlc_protocol(dev);
+       if (err)
+               return err;
 
        if (!try_module_get(proto->module))
                return -ENOSYS;
@@ -289,15 +293,24 @@ int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto,
                }
        }
        dev_to_hdlc(dev)->proto = proto;
+
        return 0;
 }
 
 
-void detach_hdlc_protocol(struct net_device *dev)
+int detach_hdlc_protocol(struct net_device *dev)
 {
        hdlc_device *hdlc = dev_to_hdlc(dev);
+       int err;
 
        if (hdlc->proto) {
+               err = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev);
+               err = notifier_to_errno(err);
+               if (err) {
+                       netdev_err(dev, "Refused to change device type\n");
+                       return err;
+               }
+
                if (hdlc->proto->detach)
                        hdlc->proto->detach(dev);
                module_put(hdlc->proto->module);
@@ -306,6 +319,8 @@ void detach_hdlc_protocol(struct net_device *dev)
        kfree(hdlc->state);
        hdlc->state = NULL;
        hdlc_setup_dev(dev);
+
+       return 0;
 }
 
 
index 3f20808b5ff8260441eb441cdf4c3f7a8a9ee165..a408abc25512aca0d94f16bbe0cefa0cdd758aaf 100644 (file)
@@ -378,6 +378,7 @@ static int cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
                spin_lock_init(&state(hdlc)->lock);
                dev->header_ops = &cisco_header_ops;
                dev->type = ARPHRD_CISCO;
+               call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
                netif_dormant_on(dev);
                return 0;
        }
index 89541cc90e877b476bf6d1fc973d4393bc01acc7..b6e0cfb095d35c716fbdc22c99a4907ca75b33c1 100644 (file)
@@ -1240,6 +1240,7 @@ static int fr_ioctl(struct net_device *dev, struct ifreq *ifr)
                }
                memcpy(&state(hdlc)->settings, &new_settings, size);
                dev->type = ARPHRD_FRAD;
+               call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
                return 0;
 
        case IF_PROTO_FR_ADD_PVC:
index 0d7645581f912d04b79e4374fa46c2ed451b7617..47fdb87d3567ce8ecb1f6dfc04d7f97884c57d8a 100644 (file)
@@ -687,6 +687,7 @@ static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
                dev->hard_header_len = sizeof(struct hdlc_header);
                dev->header_ops = &ppp_header_ops;
                dev->type = ARPHRD_PPP;
+               call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
                netif_dormant_on(dev);
                return 0;
        }
index 5dc153e8a29d14d9d0a68579dbe85718ea6d7955..4feb45001aacf459f235c5393679f4e937db6ab5 100644 (file)
@@ -84,6 +84,7 @@ static int raw_ioctl(struct net_device *dev, struct ifreq *ifr)
                        return result;
                memcpy(hdlc->state, &new_settings, size);
                dev->type = ARPHRD_RAWHDLC;
+               call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
                netif_dormant_off(dev);
                return 0;
        }
index 3ab72b3082dee59092d9cb9e6235b64afc21859f..2f11836078ab5abf7f415642c3fe5db2e275b571 100644 (file)
@@ -102,6 +102,7 @@ static int raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
                ether_setup(dev);
                dev->tx_queue_len = old_qlen;
                eth_hw_addr_random(dev);
+               call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
                netif_dormant_off(dev);
                return 0;
        }
index a49aec5efd2072507e0e35d9fa40fb2760df796f..e867638067a65cff846fd13dfa4644c248847a97 100644 (file)
@@ -213,6 +213,7 @@ static int x25_ioctl(struct net_device *dev, struct ifreq *ifr)
                if ((result = attach_hdlc_protocol(dev, &proto, 0)))
                        return result;
                dev->type = ARPHRD_X25;
+               call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
                netif_dormant_off(dev);
                return 0;
        }
index 1acb1445e05fe5f03ce33f8cdc6a03c35ae59763..e31bcd4c7859e5d01405868090246f4f94a984a2 100644 (file)
@@ -101,7 +101,7 @@ netdev_tx_t hdlc_start_xmit(struct sk_buff *skb, struct net_device *dev);
 int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto,
                         size_t size);
 /* May be used by hardware driver to gain control over HDLC device */
-void detach_hdlc_protocol(struct net_device *dev);
+int detach_hdlc_protocol(struct net_device *dev);
 
 static __inline__ __be16 hdlc_type_trans(struct sk_buff *skb,
                                         struct net_device *dev)