47 #include <freeradius-devel/bio/bio_priv.h>
48 #include <freeradius-devel/bio/null.h>
49 #include <freeradius-devel/bio/buf.h>
50 #include <freeradius-devel/util/rb.h>
51 #include <freeradius-devel/util/dlist.h>
53 #define _BIO_RETRY_PRIVATE
54 #include <freeradius-devel/bio/retry.h>
132 #define fr_bio_retry_timer_clear(_x) do { \
133 talloc_const_free((_x)->ev); \
134 (_x)->next_retry_item = NULL; \
158 if (first ==
my->next_retry_item)
return 0;
165 my->next_retry_item = first;
193 if (first ==
my->partial)
goto cancel_timer;
198 if (first ==
my->next_retry_item)
return 0;
205 my->next_retry_item = first;
214 bool timer_reset =
false;
219 if (
my->partial !=
item) {
220 if (!
item->reserved) {
225 item->cancelled =
true;
238 if (
my->partial ==
item)
return;
243 if (
my->next_retry_item ==
item) {
252 fr_assert(fr_bio_retry_list_num_elements(&
my->free) == 0);
258 my->all_used =
false;
260 if (
my->cb.write_resume) (void)
my->cb.write_resume(&
my->bio);
269 item->packet_ctx = NULL;
272 fr_bio_retry_list_insert_head(&
my->free,
item);
282 if (
my->info.write_blocked) {
287 my->info.write_blocked =
true;
327 my->info.last_sent = now;
345 if (rcode ==
fr_bio_error(IO_WOULD_BLOCK))
return rcode;
354 if ((
size_t) rcode < item->size) {
398 if (rcode <= 0)
return rcode;
423 if (!
my->info.write_blocked)
return 1;
426 if (rcode <= 0)
return rcode;
428 my->info.write_blocked =
false;
465 if (rcode <= 0)
return rcode;
467 my->buffer.read += rcode;
485 if (
item->cancelled) {
486 item->packet_ctx = NULL;
488 fr_bio_retry_list_insert_head(&
my->free,
item);
492 if (rcode <= 0)
return rcode;
512 if (!
my->buffer.start ||
565 if (
my->partial)
return 0;
587 if ((
size_t) rcode == size)
return rcode;
592 if (rcode == 0)
return 0;
601 if (rcode ==
fr_bio_error(IO_WOULD_BLOCK))
return rcode;
649 my->next_retry_item = NULL;
684 item =
my->next_retry_item;
685 my->next_retry_item = NULL;
737 return next->write(
next, packet_ctx, NULL, size);
743 if (fr_bio_retry_list_num_elements(&
my->free) == 0) {
764 my->info.write_blocked =
true;
771 if (rcode < 0)
return rcode;
779 fr_assert(fr_bio_retry_list_num_elements(&
my->free) > 0);
790 if (rcode <= 0)
return rcode;
795 item = fr_bio_retry_list_pop_head(&
my->free);
802 .packet_ctx = packet_ctx,
830 if ((
size_t) rcode < size) {
868 if (rcode <= 0)
return rcode;
877 if (!
my->response(bio, &
item, packet_ctx,
buffer, size)) {
880 item->retry.replies++;
912 item->retry.replies++;
918 if (
item->reserved)
return rcode;
1002 if (!
item)
return 0;
1007 if (!
item->retry.replies)
return 0;
1029 if (
item->retry.config)
return -1;
1046 if (!
item->retry.config)
return NULL;
1048 return &
item->retry;
1104 if (!max_saved)
return NULL;
1105 if (max_saved > 65536)
return NULL;
1108 if (!
my)
return NULL;
1115 if (!items)
return NULL;
1120 fr_bio_retry_list_init(&
my->free);
1121 for (i = 0; i < max_saved; i++) {
1123 fr_bio_retry_list_insert_tail(&
my->free, &items[i]);
1133 my->rewrite = rewrite;
1135 my->response = response;
1136 my->release = release;
1139 my->info.el = cfg->
el;
1172 if (!
my->partial)
return num;
1177 return num + !
my->partial->cancelled;
1189 item = fr_bio_retry_list_pop_head(&
my->free);
1190 if (!
item)
return NULL;
static int const char char buffer[256]
static fr_bio_t * fr_bio_next(fr_bio_t *bio)
fr_bio_retry_entry_t * fr_bio_retry_item_reserve(fr_bio_t *bio)
Reserve an entry for later use with fr_bio_retry_rewrite()
static ssize_t fr_bio_retry_read(fr_bio_t *bio, void *packet_ctx, void *buffer, size_t size)
static int8_t _expiry_cmp(void const *one, void const *two)
const fr_retry_t * fr_bio_retry_entry_info(UNUSED fr_bio_t *bio, fr_bio_retry_entry_t *item)
Allow the callbacks / application to know when things are being retried.
fr_bio_retry_release_t release
callback to release a request / response pair
fr_bio_t * fr_bio_retry_alloc(TALLOC_CTX *ctx, size_t max_saved, fr_bio_retry_sent_t sent, fr_bio_retry_response_t response, fr_bio_retry_rewrite_t rewrite, fr_bio_retry_release_t release, fr_bio_retry_config_t const *cfg, fr_bio_t *next)
Allocate a fr_bio_retry_t.
fr_rb_tree_t expiry_tree
when packets expire, so that we expire packets when the socket is blocked.
int fr_bio_retry_entry_init(UNUSED fr_bio_t *bio, fr_bio_retry_entry_t *item, fr_retry_config_t const *cfg)
Set a per-packet retry config.
static ssize_t fr_bio_retry_save_write(fr_bio_retry_t *my, fr_bio_retry_entry_t *item, ssize_t rcode)
Save a partial packet when the write becomes blocked.
fr_bio_buf_t buffer
to store partial packets
static void fr_bio_retry_release(fr_bio_retry_t *my, fr_bio_retry_entry_t *item, fr_bio_retry_release_reason_t reason)
Release an entry back to the free list.
size_t fr_bio_retry_outstanding(fr_bio_t *bio)
#define fr_bio_retry_timer_clear(_x)
static int fr_bio_retry_destructor(fr_bio_retry_t *my)
Cancel all outstanding packets.
int fr_bio_retry_entry_cancel(fr_bio_t *bio, fr_bio_retry_entry_t *item)
Cancel one item.
struct fr_bio_retry_list_s fr_bio_retry_list_t
ssize_t fr_bio_retry_rewrite(fr_bio_t *bio, fr_bio_retry_entry_t *item, const void *buffer, size_t size)
Resend a packet.
static int fr_bio_retry_timer_reset(fr_bio_retry_t *my)
Reset the timer after changing the rb tree.
static void fr_bio_retry_shutdown(fr_bio_t *bio)
Orderly shutdown.
static int8_t _next_retry_cmp(void const *one, void const *two)
static int fr_bio_retry_write_item(fr_bio_retry_t *my, fr_bio_retry_entry_t *item, fr_time_t now)
Write one item.
static void fr_bio_retry_timer(UNUSED fr_event_list_t *el, fr_time_t now, void *uctx)
Run a timer event.
fr_retry_config_t retry_config
static int fr_bio_retry_write_delayed(fr_bio_retry_t *my, fr_time_t now)
fr_bio_retry_entry_t * partial
for partial writes
fr_rb_tree_t next_retry_tree
when packets are retried next
static int fr_bio_retry_expiry_timer_reset(fr_bio_retry_t *my)
Reset the expiry timer after expiring one element.
fr_bio_retry_sent_t sent
callback for when we successfully sent a packet
fr_bio_retry_entry_t * next_retry_item
for timers
fr_bio_retry_response_t response
callback to see if we got a valid response
static ssize_t fr_bio_retry_write(fr_bio_t *bio, void *packet_ctx, void const *buffer, size_t size)
Write a request, and see if we have a reply.
static ssize_t fr_bio_retry_write_fatal(fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED void const *buffer, UNUSED size_t size)
A previous timer write had a fatal error, so we forbid further writes.
bool all_used
blocked due to no free entries
static int fr_bio_retry_write_blocked(fr_bio_t *bio)
Writes are blocked.
static ssize_t fr_bio_retry_write_partial(fr_bio_t *bio, void *packet_ctx, const void *buffer, size_t size)
There's a partial packet written.
fr_bio_retry_rewrite_t rewrite
optional callback which can change a packet on retry
static void fr_bio_retry_expiry_timer(UNUSED fr_event_list_t *el, fr_time_t now, void *uctx)
Run an expiry timer event.
fr_event_timer_t const * ev
we only need one timer event: next time we do something
fr_bio_retry_info_t const * fr_bio_retry_info(fr_bio_t *bio)
static int fr_bio_retry_write_resume(fr_bio_t *bio)
Resume writes.
void * rewrite_ctx
context specifically for rewriting this packet
fr_retry_config_t retry_config
base retry config
void(* fr_bio_retry_release_t)(fr_bio_t *bio, fr_bio_retry_entry_t *retry_ctx, fr_bio_retry_release_reason_t reason)
Callback on release the packet (timeout or have all replies)
fr_retry_t retry
retry timers and counters
fr_bio_retry_release_reason_t
@ FR_BIO_RETRY_WRITE_ERROR
struct fr_bio_retry_entry_s fr_bio_retry_entry_t
uint8_t const * buffer
cached copy of the packet to send
size_t size
size of the cached packet
bool reserved
for application-layer watchdog
void(* fr_bio_retry_sent_t)(fr_bio_t *bio, void *packet_ctx, const void *buffer, size_t size, fr_bio_retry_entry_t *retry_ctx)
Callback for when a packet is sent.
ssize_t(* fr_bio_retry_rewrite_t)(fr_bio_t *bio, fr_bio_retry_entry_t *retry_ctx, const void *buffer, size_t size)
bool(* fr_bio_retry_response_t)(fr_bio_t *bio, fr_bio_retry_entry_t **item_p, void *packet_ctx, const void *buffer, size_t size)
Callback on read to see if a packet is a response.
fr_event_list_t * el
event list
void * packet_ctx
packet_ctx from the write() call
fr_rb_node_t expiry_node
for expiries
bool cancelled
was this item cancelled?
fr_bio_retry_t * my
so we can get to it from the event timer callback
fr_bio_retry_rewrite_t rewrite
per-packet rewrite callback
void * uctx
user-writable context
static void fr_bio_chain(fr_bio_t *first, fr_bio_t *second)
Chain one bio after another.
int fr_bio_buf_alloc(TALLOC_CTX *ctx, fr_bio_buf_t *bio_buf, size_t size)
ssize_t fr_bio_buf_write(fr_bio_buf_t *bio_buf, const void *buffer, size_t size)
static size_t fr_bio_buf_used(fr_bio_buf_t const *bio_buf)
static void fr_bio_buf_reset(fr_bio_buf_t *bio_buf)
static size_t fr_bio_buf_size(fr_bio_buf_t const *bio_buf)
fr_dcursor_eval_t void const * uctx
#define FR_DLIST_TYPES(_name)
Define type specific wrapper structs for dlists.
#define FR_DLIST_ENTRY(_name)
Expands to the type name used for the entry wrapper structure.
#define FR_DLIST_FUNCS(_name, _element_type, _element_entry)
Define type specific wrapper functions for dlists.
#define fr_event_timer_at(...)
int fr_bio_write_blocked(fr_bio_t *bio)
Internal BIO function to tell all BIOs that it's blocked.
Stores all information relating to an event list.
typedef FR_DLIST_HEAD(map_list) map_list_t
Given these are used in so many places, it's more friendly to have a proper type.
ssize_t fr_bio_null_write(UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED void const *buffer, UNUSED size_t size)
Always return 0 on write.
void * fr_rb_first(fr_rb_tree_t *tree)
void fr_rb_iter_delete_inorder(fr_rb_iter_inorder_t *iter)
Remove the current node from the tree.
void * fr_rb_remove_by_inline_node(fr_rb_tree_t *tree, fr_rb_node_t *node)
Remove an entry from the tree, using the node structure, without freeing the data.
void * fr_rb_iter_init_inorder(fr_rb_iter_inorder_t *iter, fr_rb_tree_t *tree)
Initialise an in-order iterator.
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
#define fr_rb_inline_init(_tree, _type, _field, _data_cmp, _data_free)
Initialises a red black tree.
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Iterator structure for in-order traversal of an rbtree.
The main red black tree structure.
#define fr_time()
Allow us to arbitrarily manipulate time.
static fr_time_delta_t fr_time_delta_from_msec(int64_t msec)
static fr_time_t fr_time_add_time_delta(fr_time_t a, fr_time_delta_t b)
static int64_t fr_time_delta_unwrap(fr_time_delta_t time)
#define fr_time_lteq(_a, _b)
#define fr_time_delta_ispos(_a)
#define fr_time_add(_a, _b)
Add a time/time delta together.
#define fr_time_gt(_a, _b)
#define fr_time_lt(_a, _b)
static int8_t fr_time_cmp(fr_time_t a, fr_time_t b)
Compare two fr_time_t values.
static fr_event_list_t * el
fr_retry_state_t fr_retry_next(fr_retry_t *r, fr_time_t now)
Initialize a retransmission counter.
void fr_retry_init(fr_retry_t *r, fr_time_t now, fr_retry_config_t const *config)
Initialize a retransmission counter.
fr_time_delta_t irt
Initial transmission time.
fr_time_t end
when we will end the retransmissions
fr_time_t next
when the next timer should be set