port: Refresh link status on faults.

ptp4l gets the ENOBUFS error on the netlink socket when the kernel has
to drop messages due to full socket buffer. If ptp4l has a port in the
faulty state waiting for the link to go up and that event corresponds
to one of the dropped netlink messages, the port will be stuck in the
faulty state until the link goes down and up again.

To prevent the port from getting stuck, request the current link status
when dispatching the EV_FAULT_DETECTED event. Also, reopen the socket to
get rid of the buffered messages when handling the fault and again when
reinitializing the port.

Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
diff --git a/port.c b/port.c
index 623b520..a1d0f2c 100644
--- a/port.c
+++ b/port.c
@@ -1995,6 +1995,20 @@
 	return port_cmlds_renew(p, now.tv_sec);
 }
 
+static void port_rtnl_initialize(struct port *p)
+{
+	/* Reopen the socket to get rid of buffered messages */
+	if (p->fda.fd[FD_RTNL] >= 0) {
+		rtnl_close(p->fda.fd[FD_RTNL]);
+	}
+	p->fda.fd[FD_RTNL] = rtnl_open();
+	if (p->fda.fd[FD_RTNL] >= 0) {
+		rtnl_link_query(p->fda.fd[FD_RTNL], interface_name(p->iface));
+	}
+
+	clock_fda_changed(p->clock);
+}
+
 void port_disable(struct port *p)
 {
 	int i;
@@ -2108,13 +2122,8 @@
 		if (p->bmca == BMCA_NOOP) {
 			port_set_delay_tmo(p);
 		}
-		if (p->fda.fd[FD_RTNL] == -1) {
-			p->fda.fd[FD_RTNL] = rtnl_open();
-		}
-		if (p->fda.fd[FD_RTNL] >= 0) {
-			const char *ifname = interface_name(p->iface);
-			rtnl_link_query(p->fda.fd[FD_RTNL], ifname);
-		}
+
+		port_rtnl_initialize(p);
 	}
 
 	port_nrate_initialize(p);
@@ -3794,6 +3803,13 @@
 		if (port_link_status_get(p) && clear_fault_asap(&i)) {
 			pr_notice("%s: clearing fault immediately", p->log_name);
 			next = p->state_machine(next, EV_FAULT_CLEARED, 0);
+		} else if (event == EV_FAULT_DETECTED) {
+			/*
+			 * Reopen the netlink socket and refresh the link
+			 * status in case the fault was triggered by a missed
+			 * netlink message (ENOBUFS).
+			 */
+			port_rtnl_initialize(p);
 		}
 	}