MINOR: listener: workaround for closing a tiny race between resume_listener() and stopping

This is an alternative fix that tries to address the same issue as
d1ebee177 ("BUG/MINOR: listener: close tiny race between
resume_listener() and stopping") while allowing resume_listener() to be
more versatile.

Indeed, because of the previous fix, resume_listener() is not able to
rebind stopped listeners, and this breaks the original behavior that is
documented in the function description:

 "If the listener was only in the assigned
  state, it's totally rebound. This can happen if a pause() has completely
  stopped it. If the resume fails, 0 is returned and an error might be
  displayed."

With relax_listener(), we now make sure to check l->state under the
listener lock so we don't call resume_listener() when the conditions are not
met.

As such, concurrently stopped listeners may not be rebound using
relax_listener().

Note: the documented race can't happen since 1b927eb3c ("MEDIUM: proto: stop
protocols under thread isolation during soft stop"), but older versions are
concerned as 1b927eb3c was not marked for backports.
Moreover, the patch also prevents the race between protocol_pause_all() and
resuming from LIMITED or FULL states.

This commit depends on:
  - "MINOR: listener: add relax_listener() function"

This should be backported with d1ebee177 up to 2.4
(d1ebee177 is marked to be backported for all stable versions but the current
patch does not apply for versions < 2.4)

(cherry picked from commit f5d98938ad0edf85ae4d10a6d6c088915c1faaed)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 3353ccc853dcfd547d5cdd7561a2af24eaad9887)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
(cherry picked from commit c2d190000824da34deafcdc82a7ef3c495752b10)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit b84a6bb601180104659179b03f1ba107c6633aee)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/src/listener.c b/src/listener.c
index 84064d6..28c3e63 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -508,10 +508,6 @@
 	if (l->state == LI_READY)
 		goto end;
 
-	/* the listener might have been stopped in parallel */
-	if (l->state < LI_PAUSED)
-		goto end;
-
 	if (l->rx.proto->resume)
 		ret = l->rx.proto->resume(l);
 
@@ -567,7 +563,7 @@
 }
 
 /* Marks a ready listener as full so that the stream code tries to re-enable
- * it upon next close() using resume_listener().
+ * it upon next close() using relax_listener().
  */
 static void listener_full(struct listener *l)
 {
@@ -605,7 +601,7 @@
 		/* This cannot fail because the listeners are by definition in
 		 * the LI_LIMITED state.
 		 */
-		resume_listener(listener, 0, 0);
+		relax_listener(listener, 0, 0);
 	}
 }
 
@@ -618,7 +614,7 @@
 		/* This cannot fail because the listeners are by definition in
 		 * the LI_LIMITED state.
 		 */
-		resume_listener(listener, 0, 0);
+		relax_listener(listener, 0, 0);
 	}
 }
 
@@ -1144,7 +1140,7 @@
 	      (!tick_isset(global_listener_queue_task->expire) ||
 	       tick_is_expired(global_listener_queue_task->expire, now_ms))))) {
 		/* at least one thread has to this when quitting */
-		resume_listener(l, 0, 0);
+		relax_listener(l, 0, 0);
 
 		/* Dequeues all of the listeners waiting for a resource */
 		dequeue_all_listeners();
@@ -1203,7 +1199,7 @@
 	_HA_ATOMIC_DEC(&l->thr_conn[tid]);
 
 	if (l->state == LI_FULL || l->state == LI_LIMITED)
-		resume_listener(l, 0, 0);
+		relax_listener(l, 0, 0);
 
 	/* Dequeues all of the listeners waiting for a resource */
 	dequeue_all_listeners();
diff --git a/src/proxy.c b/src/proxy.c
index fe0d1e2..686c6d1 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -2836,7 +2836,7 @@
 	px->maxconn = v;
 	list_for_each_entry(l, &px->conf.listeners, by_fe) {
 		if (l->state == LI_FULL)
-			resume_listener(l, 1, 0);
+			relax_listener(l, 1, 0);
 	}
 
 	if (px->maxconn > px->feconn)