issue patch in next net/eth: fix link handling

Sascha Hauer s.hauer at pengutronix.de
Fri Sep 28 03:50:47 EDT 2012


On Fri, Sep 28, 2012 at 04:28:21AM +0200, Jean-Christophe PLAGNIOL-VILLARD wrote:
> HI,
> 
> 	The patch is next
> 	net/eth: fix link handling
> 
> 	was NEVER send to the ML
> 
> 	IIRC I was the author of the first version and this disapear
> 
> 	Uwe and I just get this discussion on the kernel ML about patch update
> 

I was basically pissed off because I got the strong feeling that I spent
more time reviewing and testing the patch than you initially spent
writing it in the first place. The second version still stored apples
in edev->phydev->link and bananas in edev->carrier, but still did a
edev->carrier = dev->link.

Sorry for not posting it on the ML, doing that now.

Sascha

8<--------------------------------------------------

>From 0dc9de2efd7ea6fb613afec822b52b4bfe5a7b2e Mon Sep 17 00:00:00 2001
From: Sascha Hauer <s.hauer at pengutronix.de>
Date: Wed, 26 Sep 2012 17:53:29 +0200
Subject: [PATCH] net/eth: fix link handling

Check link status on eth device open time and then periodically
every 5 seconds. Based on work from from Jean-Christophe, but this
version does not duplicate the link status information in two different
variables.

Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
 drivers/net/phy/phy.c |   35 ++++++++++++++++-------
 include/linux/phy.h   |    3 ++
 net/eth.c             |   75 +++++++++++++++++++++++++++++++++++++------------
 3 files changed, 85 insertions(+), 28 deletions(-)

diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index e1a24fa..88c3ff7 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -29,6 +29,30 @@
 
 static int genphy_config_init(struct phy_device *phydev);
 
+int phy_update_status(struct phy_device *dev)
+{
+	struct phy_driver *drv = to_phy_driver(dev->dev.driver);
+	struct eth_device *edev = dev->attached_dev;
+	int ret;
+	int oldspeed = dev->speed, oldduplex = dev->duplex;
+
+	ret = drv->read_status(dev);
+	if (ret)
+		return ret;
+
+	if (dev->speed == oldspeed && dev->duplex == oldduplex)
+		return 0;
+
+	if (dev->adjust_link)
+		dev->adjust_link(edev);
+
+	if (dev->link)
+		printf("%dMbps %s duplex link detected\n", dev->speed,
+			dev->duplex ? "full" : "half");
+
+	return 0;
+}
+
 struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id)
 {
 	struct phy_device *dev;
@@ -172,16 +196,7 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr,
 
 	drv->config_aneg(dev);
 
-	ret = drv->read_status(dev);
-	if (ret < 0)
-		return ret;
-
-	if (dev->link)
-		printf("%dMbps %s duplex link detected\n", dev->speed,
-			dev->duplex ? "full" : "half");
-
-	if (adjust_link)
-		adjust_link(edev);
+	dev->adjust_link = adjust_link;
 
 	return 0;
 
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 8fe6b86..76f9edb 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -164,6 +164,7 @@ struct phy_device {
 	void *priv;
 
 	struct eth_device *attached_dev;
+	void (*adjust_link)(struct eth_device *dev);
 
 	struct cdev cdev;
 };
@@ -253,6 +254,8 @@ int phy_device_connect(struct eth_device *dev, struct mii_bus *bus, int addr,
 		       void (*adjust_link) (struct eth_device *edev),
 		       u32 flags, phy_interface_t interface);
 
+int phy_update_status(struct phy_device *phydev);
+
 /* Generic PHY support and helper functions */
 int genphy_restart_aneg(struct phy_device *phydev);
 int genphy_config_aneg(struct phy_device *phydev);
diff --git a/net/eth.c b/net/eth.c
index 6cfe4f2..9f52870 100644
--- a/net/eth.c
+++ b/net/eth.c
@@ -32,6 +32,7 @@
 #include <malloc.h>
 
 static struct eth_device *eth_current;
+static uint64_t last_link_check;
 
 static LIST_HEAD(netdev_list);
 
@@ -128,24 +129,64 @@ int eth_complete(struct string_list *sl, char *instr)
 }
 #endif
 
-int eth_send(void *packet, int length)
+/*
+ * Check for link if we haven't done so for longer.
+ */
+static int eth_carrier_check(int force)
 {
 	int ret;
 
-	if (!eth_current)
-		return -ENODEV;
+	if (!IS_ENABLED(CONFIG_PHYLIB))
+		return 0;
 
-	if (!eth_current->active) {
-		ret = eth_current->open(eth_current);
+	if (!eth_current->phydev)
+		return 0;
+
+	if (force || is_timeout(last_link_check, 5 * SECOND)) {
+		ret = phy_update_status(eth_current->phydev);
 		if (ret)
 			return ret;
-
-		if (eth_current->phydev)
-			eth_current->active = eth_current->phydev->link;
-		else
-			eth_current->active = 1;
+		last_link_check = get_time_ns();
 	}
 
+	return eth_current->phydev->link ? 0 : -ENETDOWN;
+}
+
+/*
+ * Check if we have a current ethernet device and
+ * eventually open it if we have to.
+ */
+static int eth_check_open(void)
+{
+	int ret;
+
+	if (!eth_current)
+		return -ENODEV;
+
+	if (eth_current->active)
+		return 0;
+
+	ret = eth_current->open(eth_current);
+	if (ret)
+		return ret;
+
+	eth_current->active = 1;
+
+	return eth_carrier_check(1);
+}
+
+int eth_send(void *packet, int length)
+{
+	int ret;
+
+	ret = eth_check_open();
+	if (ret)
+		return ret;
+
+	ret = eth_carrier_check(0);
+	if (ret)
+		return ret;
+
 	led_trigger_network(LED_TRIGGER_NET_TX);
 
 	return eth_current->send(eth_current, packet, length);
@@ -155,15 +196,13 @@ int eth_rx(void)
 {
 	int ret;
 
-	if (!eth_current)
-		return -ENODEV;
+	ret = eth_check_open();
+	if (ret)
+		return ret;
 
-	if (!eth_current->active) {
-		ret = eth_current->open(eth_current);
-		if (ret)
-			return ret;
-		eth_current->active = 1;
-	}
+	ret = eth_carrier_check(0);
+	if (ret)
+		return ret;
 
 	return eth_current->recv(eth_current);
 }
-- 
1.7.10.4

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



More information about the barebox mailing list