]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
preliminary support for reserved items
authorAlan T. DeKok <aland@freeradius.org>
Thu, 23 May 2024 16:25:19 +0000 (12:25 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Mon, 3 Jun 2024 12:43:43 +0000 (08:43 -0400)
in order to implement application-layer watchdog packets

src/lib/bio/retry.c
src/lib/bio/retry.h

index bacedaf5ba2c0c8f96f8e7bce8d3a89f90b29582..826b7764fda12cc1937fc50c9fd24947829b3011 100644 (file)
@@ -58,6 +58,7 @@ struct fr_bio_retry_entry_s {
        size_t          size;   
 
        bool            cancelled;              //!< was this item cancelled?
+       bool            reserved;               //!< for application-layer watchdog
 };
 
 FR_DLIST_FUNCS(fr_bio_retry_list, fr_bio_retry_entry_t, entry)
@@ -153,7 +154,7 @@ static void fr_bio_retry_release(fr_bio_retry_t *my, fr_bio_retry_entry_t *item,
         *      Remove the item before calling the application "release" function.
         */
        if (my->partial != item) {
-               (void) fr_rb_remove_by_inline_node(&my->rb, &item->node);
+               if (!item->reserved) (void) fr_rb_remove_by_inline_node(&my->rb, &item->node);
        } else {
                item->cancelled = true;
        }
@@ -197,6 +198,7 @@ static int fr_bio_retry_write_item(fr_bio_retry_t *my, fr_bio_retry_entry_t *ite
        fr_retry_state_t state;
 
        fr_assert(!my->partial);
+       fr_assert(!item->reserved);
 
        /*
         *      Are we there yet?
@@ -349,14 +351,11 @@ static ssize_t fr_bio_retry_write_partial(fr_bio_t *bio, void *packet_ctx, const
        my->info.write_blocked = false;
 
        /*
-        *      The item was cancelled.  It's still in the tree, so we remove it, and reset its fields.
-        *      We then insert it into the free list.
+        *      The item was cancelled, which means it's no longer in the timer tree.
         *
         *      If it's not cancelled, then we leave it in the tree, and run its timers s normal.
         */
        if (item->cancelled) {
-               (void) fr_rb_remove_by_inline_node(&my->rb, &item->node);
-
                item->packet_ctx = NULL;
 
                fr_bio_retry_list_insert_head(&my->free, item);
@@ -719,17 +718,14 @@ static ssize_t fr_bio_retry_read(fr_bio_t *bio, void *packet_ctx, void *buffer,
                if (!item) return 0;
 
                item->retry.replies++;
-               if (item->retry.replies < item->retry.count) return 0;
 
                /*
-                *      We have a reply, so we can't possibly be partially writing the request
+                *      We have enough replies.  Release it.
                 */
-               fr_assert(item != my->partial);
+               if ((item->retry.replies >= item->retry.count) || !fr_time_delta_ispos(my->retry_config.mrd)) {
+                       fr_bio_retry_release(my, item, FR_BIO_RETRY_DONE);
+               }
 
-               /*
-                *      We've received all of the responses, we can clean up the packet.
-                */
-               fr_bio_retry_release(my, item, FR_BIO_RETRY_DONE);
                return 0;
        }
 
@@ -749,21 +745,29 @@ static ssize_t fr_bio_retry_read(fr_bio_t *bio, void *packet_ctx, void *buffer,
         */
        my->info.last_reply = fr_time();
 
+       /*
+        *      We have a new reply.  If we've received all of the replies (i.e. one), OR we don't have a
+        *      maximum lifetime for this request, then release it immediately.
+        */
+       item->retry.replies++;
+
+       /*
+        *      We don't retry application-layer watchdog packets.  And we don't run timers for them.  The
+        *      application is responsible for managing those timers itself.
+        */
+       if (item->reserved) return rcode;
+
        /*
         *      There are no more packets to send, so this connection is idle.
         *
         *      Note that partial packets aren't tracked in the timer tree.  We can't do retransmits until the
         *      socket is writable.
-        *
-        *      @todo - don't include application watchdog packets in the idle count?
         */
        if (fr_bio_retry_outstanding((fr_bio_t *) my) == 1) my->info.last_idle = my->info.last_reply;
 
        /*
-        *      We have a new reply.  If we've received all of the replies (i.e. one), OR we don't have a
-        *      maximum lifetime for this request, then release it immediately.
+        *      We have enough replies.  Release it.
         */
-       item->retry.replies++;
        if ((item->retry.replies >= item->retry.count) || !fr_time_delta_ispos(my->retry_config.mrd)) {
                fr_bio_retry_release(my, item, FR_BIO_RETRY_DONE);
                return rcode;
@@ -975,3 +979,24 @@ size_t fr_bio_retry_outstanding(fr_bio_t *bio)
         */
        return num + !my->partial->cancelled;
 }
+
+/**  Reserve an entry for later use with fr_bio_retry_rewrite()
+ *
+ *  So that application-layer watchdogs can bypass the normal write / retry routines.
+ */
+fr_bio_retry_entry_t *fr_bio_retry_item_reserve(fr_bio_t *bio)
+{
+       fr_bio_retry_t *my = talloc_get_type_abort(bio, fr_bio_retry_t);
+       fr_bio_retry_entry_t *item;
+
+       item = fr_bio_retry_list_pop_head(&my->free);
+       if (!item) return NULL;
+
+       fr_assert(item->my == my);
+       *item = (fr_bio_retry_entry_t) {
+               .my = my,
+               .reserved = true,
+       };
+
+       return item;
+}
index c0b633173519b67f3817133bcb76538a4ff696f7..4a63a81d0c683f27ea64e8aecf80ecd1ca8eae4e 100644 (file)
@@ -151,4 +151,6 @@ fr_bio_retry_info_t const *fr_bio_retry_info(fr_bio_t *bio) CC_HINT(nonnull);
 
 size_t         fr_bio_retry_outstanding(fr_bio_t *bio) CC_HINT(nonnull);
 
+fr_bio_retry_entry_t *fr_bio_retry_item_reserve(fr_bio_t *bio) CC_HINT(nonnull);
+
 #undef _CONST