]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: mux-h2: add rxbuf head/tail/count management for h2s
authorWilly Tarreau <w@1wt.eu>
Tue, 1 Oct 2024 16:16:51 +0000 (18:16 +0200)
committerWilly Tarreau <w@1wt.eu>
Sat, 12 Oct 2024 14:29:16 +0000 (16:29 +0200)
Now the h2s get their rx_head, rx_tail and rx_count associated with the
shared rxbufs. A few functions are provided to manipulate all this,
essentially allocate/release a buffer for the stream, return a buffer
pointer to the head/tail, counting allocated buffers for the stream
and reporting if a stream may still allocate.

For now this code is not used.

src/mux_h2.c

index 1952e2ec0e649dbd4bbdf103941877e9b06daa1f..34653a233d3d6656cb6add94a6d3f9a88d89845a 100644 (file)
@@ -117,6 +117,9 @@ struct h2s {
        uint64_t curr_rx_ofs;  /* stream offset at which next FC-data will arrive (includes padding) */
        uint64_t last_adv_ofs; /* stream offset corresponding to last emitted WU */
        uint64_t next_max_ofs; /* max stream offset that next WU must permit (curr_rx_ofs+rx_win) */
+       uint rx_head, rx_tail; /* head and tail of rx buffer in the conn's shared rx buf */
+       uint rx_count;         /* total number of allocated rxbufs */
+       /* 4 bytes hole here */
        struct buffer rxbuf; /* receive buffer, always valid (buf_empty or real buffer) */
        struct wait_event *subs;  /* recv wait_event the stream connector associated is waiting on (via h2_subscribe) */
        struct list list; /* To be used when adding in h2c->send_list or h2c->fctl_lsit */
@@ -692,6 +695,94 @@ static inline int h2c_max_concurrent_streams(const struct h2c *h2c)
        return ret;
 }
 
+/* Returns a pointer to the oldest rxbuf of the stream, or NULL if there is
+ * none. Note that this doesn't indicate that the buffer is allocated nor
+ * contains any data.
+ */
+static inline struct buffer *h2s_rxbuf_head(const struct h2s *h2s)
+{
+       if (!h2s->rx_head)
+               return NULL;
+       return &h2s->h2c->shared_rx_bufs[h2s->rx_head].buf;
+}
+
+/* Returns a pointer to the newest rxbuf of the stream, or NULL if there is
+ * none. Note that this doesn't indicate that the buffer is allocated nor
+ * contains any data.
+ */
+static inline struct buffer *h2s_rxbuf_tail(const struct h2s *h2s)
+{
+       if (!h2s->rx_tail)
+               return NULL;
+       return &h2s->h2c->shared_rx_bufs[h2s->rx_tail].buf;
+}
+
+/* Returns the number of allocated rxbuf slots for the stream */
+static inline uint h2s_rxbuf_cnt(const struct h2s *h2s)
+{
+       return h2s->rx_count;
+}
+
+/* Tries to get an rxbuf slot from the connection for the stream, returns its
+ * non-zero number on success, or 0 on failure. On success, it will update the
+ * stream's tail and count, and possibly head (if there was no buffer before).
+ * The buffer cell is not initialized, it's up to the caller to do it.
+ */
+static inline uint h2s_get_rxbuf(struct h2s *h2s)
+{
+       uint slot;
+
+       /* FIXME: for now each stream is granted exactly one buffer */
+       if (h2s->rx_count)
+               return 0;
+
+       slot = bl_get(h2s->h2c->shared_rx_bufs, h2s->rx_tail);
+       if (!slot)
+               return 0;
+
+       h2s->rx_count++;
+       h2s->rx_tail = slot;
+       if (!h2s->rx_head)
+               h2s->rx_head = slot;
+       return slot;
+}
+
+/* Gives back the oldest rxbuf to the connection. It's the caller's
+ * responsibility to make sure that the possible buffer there was released
+ * prior to calling this function (it may change in the future). It's safe
+ * to call this if no rxbufs are allocated. The index of the next remaining
+ * rxbuf slot is returned, or 0 when none remain.
+ */
+static inline uint h2s_put_rxbuf(struct h2s *h2s)
+{
+       if (h2s->rx_head) {
+               BUG_ON_HOT(({ const struct buffer *buf = h2s_rxbuf_head(h2s); !!(buf && b_size(buf)); }),
+                          "Attempted to release a used buffer slot");
+               h2s->rx_head = bl_put(h2s->h2c->shared_rx_bufs, h2s->rx_head);
+               if (!h2s->rx_head)
+                       h2s->rx_tail = 0;
+               h2s->rx_count--;
+       }
+       return h2s->rx_head;
+}
+
+/* Checks if the the HTX rxbuf still has available room. Returns 1 if OK, 0 if
+ * it's full. The buffer is cast to HTX for the operation, but no buffer is
+ * allocated if there is none (in which case 0 is returned).
+ */
+static inline int h2s_may_append_to_rxbuf(const struct h2s *h2s)
+{
+       struct buffer *rxbuf;
+       struct htx *htx;
+
+       rxbuf = h2s_rxbuf_tail(h2s);
+       if (!rxbuf || b_is_null(rxbuf))
+               return 0;
+
+       htx = htxbuf(rxbuf);
+       return !!htx_free_data_space(htx);
+}
+
 /* update h2c timeout if needed */
 static void h2c_update_timeout(struct h2c *h2c)
 {
@@ -1709,6 +1800,9 @@ static struct h2s *h2s_new(struct h2c *h2c, int id)
        h2s->status    = 0;
        h2s->body_len  = 0;
        h2s->rxbuf     = BUF_NULL;
+       h2s->rx_tail   = 0;
+       h2s->rx_head   = 0;
+       h2s->rx_count  = 0;
        memset(h2s->upgrade_protocol, 0, sizeof(h2s->upgrade_protocol));
 
        h2s->by_id.key = h2s->id = id;