aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-09-29 15:10:40 +0100
committereunhhu <[email protected]>2026-09-29 15:10:40 +0100
commit44e9a395aaab92039ecd7c5fe546344be4a93e10 (patch)
treed4c51333a224de2014c45001e26638d062e0391a
parenta48c38d2a80efd146ba0f81cb46bac00c22297b6 (diff)
downloadpure-44e9a395aaab92039ecd7c5fe546344be4a93e10.tar.gz
pure-44e9a395aaab92039ecd7c5fe546344be4a93e10.zip
Look for a link's receiver at once when the system says it cannot get there
A send that fails with no route, nobody at the port, or the network gone (ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ENETDOWN, EADDRNOTAVAIL) now has the link look its receiver's name up again before its next packet, not three unanswered probes later: a receiver moved away from an address that is gone costs a sender one probe's wait, not three. Such a failure no longer turns GSO off either (it said nothing of GSO). In the check that moves a receiver, the sender sent 24 records again, not 243.
-rw-r--r--link/link.c19
1 files changed, 16 insertions, 3 deletions
diff --git a/link/link.c b/link/link.c
index 7027236..a020c2d 100644
--- a/link/link.c
+++ b/link/link.c
@@ -133,6 +133,7 @@ struct PureLink {
size_t segment; /* the datagram's size the bursts are cut at */
bool gso, no_gso;
unsigned looked; /* the probes unanswered when it last looked for the receiver */
+ bool nowhere; /* a send failed as the system saw no way there (no route, nobody at the port) */
};
PureLink *pure_link_open(const char *url, const char *source, uint32_t shape, const PureSealKey *own,
@@ -217,6 +218,12 @@ static void take_answers(PureLink *l, uint64_t now) {
static bool transient(void) { return errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS || errno == EINTR; }
+/* The system says there is no way to the receiver where it was. */
+static bool nowhere(void) {
+ return errno == ECONNREFUSED || errno == EHOSTUNREACH || errno == ENETUNREACH || errno == EADDRNOTAVAIL ||
+ errno == ENETDOWN;
+}
+
/* The burst in one call, cut into datagrams of the segment's size by the
* system (UDP GSO, asked for this call alone: a packet sent by itself is
* never cut, however large). */
@@ -239,10 +246,14 @@ static void flush(PureLink *l) {
unsigned n = l->burst_count, sent = 0;
bool whole = n > 1 && l->gso;
if (whole && send_burst(l) >= 0) sent = n;
+ else if (whole && nowhere()) l->nowhere = true, sent = n; /* lost: gone as sent */
else if (whole && !transient()) l->gso = false, whole = false; /* no GSO here: one by one from now on */
for (size_t at = 0; !whole && sent < n; ++sent) {
size_t size = sent + 1 < n ? l->segment : l->burst_size - at;
- if (send(l->fd, l->burst + at, size, 0) < 0 && transient()) break;
+ if (send(l->fd, l->burst + at, size, 0) < 0) {
+ if (transient()) break; /* refused: sent again */
+ l->nowhere = l->nowhere || nowhere(); /* lost, as sent */
+ }
at += size;
}
for (unsigned k = sent; k < n; ++k) pure_link_sender_refused(l->sender, l->burst_pn[k]);
@@ -314,7 +325,8 @@ static bool look(PureLink *l, char *error, size_t size) {
}
/* A link whose probes go unanswered three times in a row looks for its
- * receiver again, and at each probe after (a second apart at most). */
+ * receiver again, and at each probe after (a second apart at most); at
+ * once when the system says there is no way there. */
enum { LOOK_PROBES = 3 };
static void *run_link(void *context) {
@@ -339,7 +351,8 @@ static void *run_link(void *context) {
if (waits[0].revents) take_answers(l, now);
pure_link_sender_tick(l->sender, now);
unsigned probes = pure_link_sender_probes(l->sender);
- if (probes >= LOOK_PROBES && probes != l->looked) { /* before the probe goes, so its answer comes to the new socket */
+ if ((probes >= LOOK_PROBES && probes != l->looked) || l->nowhere) { /* before the next goes, so its answer comes to the new socket */
+ l->nowhere = false;
pthread_mutex_unlock(&l->lock); /* a name looked up may take its time: the owner queues meanwhile */
look(l, NULL, 0);
pthread_mutex_lock(&l->lock);