qio: add a few block helpers
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 5 Jun 2019 20:27:04 +0000 (16:27 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 5 Jun 2019 22:06:24 +0000 (18:06 -0400)
These will be used in new versions of qio functions, such as pullupblock
and padblock.  block_transfer_extras() ended up not being used, but I
might in the future.  It's also illustrative of the subtle differences
between transferring extras and block_replace_extras().

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/include/ns.h
kern/src/ns/allocb.c
kern/src/ns/qio.c

index fe94aac..ddab4ae 100644 (file)
@@ -436,8 +436,8 @@ struct block {
        void (*free) (struct block *);
        uint16_t flag;
        uint16_t mss;                   /* TCP MSS for TSO */
-       uint16_t network_offset;        /* offset from start */
-       uint16_t transport_offset;      /* offset from start */
+       uint16_t network_offset;        /* offset from rp */
+       uint16_t transport_offset;      /* offset from rp */
        uint16_t tx_csum_offset;        /* offset from tx_offset to store csum */
        /* might want something to track the next free extra_data slot */
        size_t extra_len;
@@ -773,6 +773,10 @@ int block_append_extra(struct block *b, uintptr_t base, uint32_t off,
                        uint32_t len, int mem_flags);
 void block_copy_metadata(struct block *new_b, struct block *old_b);
 void block_reset_metadata(struct block *b);
+void block_add_to_offsets(struct block *b, int delta);
+void block_transfer_extras(struct block *new, struct block *old);
+void block_replace_extras(struct block *new, struct block *old);
+struct block *block_realloc(struct block *b, size_t header_space);
 size_t block_copy_to_body(struct block *to, void *from, size_t copy_amt);
 int anyhigher(void);
 int anyready(void);
index b079157..47e4200 100644 (file)
@@ -196,6 +196,85 @@ void block_reset_metadata(struct block *b)
        b->transport_offset = 0;
 }
 
+/* Adds delta (which may be negative) to the block metadata offsets that are
+ * relative to b->rp. */
+void block_add_to_offsets(struct block *b, int delta)
+{
+       /* Note we do not add to tx_csum_offset.  That is relative to
+        * transport_offset */
+       b->network_offset += delta;
+       b->transport_offset += delta;
+}
+
+/* Transfers extra data from old to new.  This is not a copy nor a
+ * qclone/refcount increase on the extra data blobs.  The old block loses the
+ * data.  This changes BLEN for both, but not BHLEN.  'new' may have preexisting
+ * ebds. */
+void block_transfer_extras(struct block *new, struct block *old)
+{
+       struct extra_bdata *ebd;
+
+       for (int i = 0; i < old->nr_extra_bufs; i++) {
+               ebd = &old->extra_data[i];
+               if (!ebd->base || !ebd->len)
+                       continue;
+               block_append_extra(new, ebd->base, ebd->off, ebd->len,
+                                  MEM_WAIT);
+       }
+
+       old->extra_len = 0;
+       old->nr_extra_bufs = 0;
+       kfree(old->extra_data);
+       old->extra_data = NULL;
+}
+
+/* Like block_transfer_extras(), but new may not have preexisting ebds. */
+void block_replace_extras(struct block *new, struct block *old)
+{
+       assert(!new->extra_data);
+       new->extra_len = old->extra_len;
+       new->nr_extra_bufs = old->nr_extra_bufs;
+       new->extra_data = old->extra_data;
+       old->extra_len = 0;
+       old->nr_extra_bufs = 0;
+       old->extra_data = NULL;
+}
+
+/* Given a block, return a block with identical content but as if you allocated
+ * it freshly with 'size', meaning with size bytes in the header/main body, some
+ * of which contain the block's main body data in the new block.  Note all
+ * blocks have an extra Hdrspc bytes to the left that is not counted.
+ *
+ * One thing to consider is a block that has 'moved to the right' in its main
+ * body.  i.e. it used to have data, such as TCP/IP headers, but we've since
+ * incremented b->rp.  We're near the end of the buffer and lim - wp is small.
+ * This will give us a new block with the existing contents at the new 'default'
+ * rp.  The old data to the left of rp will be gone.
+ *
+ * b may be in a blist.  We'll deal with its next pointer.  If b is in the
+ * middle of a blist or a qio bfirst or blast, then the caller needs to deal
+ * with pointers to it. */
+struct block *block_realloc(struct block *b, size_t size)
+{
+       struct block *new;
+       size_t amt;
+
+       /* This means there is enough space for the old block data and the rest
+        * of 'size'. */
+       if (b->lim - b->wp + BHLEN(b) >= size)
+               return b;
+       size = MAX(size, BHLEN(b));
+       new = block_alloc(size, MEM_WAIT);
+       amt = block_copy_to_body(new, b->rp, BHLEN(b));
+       assert(amt == BHLEN(b));
+       new->next = b->next;
+       b->next = NULL;
+       block_copy_metadata(new, b);
+       block_replace_extras(new, b);
+       freeb(b);
+       return new;
+}
+
 size_t block_copy_to_body(struct block *to, void *from, size_t copy_amt)
 {
        copy_amt = MIN(to->lim - to->wp, copy_amt);
index f6d12aa..16caad3 100644 (file)
@@ -274,6 +274,22 @@ struct block *linearizeblock(struct block *b)
        return newb;
 }
 
+/* Helper for bookkeeping: we removed amt bytes from block b's ebd, which may
+ * have emptied it. */
+static void block_consume_ebd_bytes(struct block *b, struct extra_bdata *ebd,
+                                   size_t amt)
+{
+       ebd->len -= amt;
+       ebd->off += amt;
+       b->extra_len -= amt;
+       if (!ebd->len) {
+               /* we don't actually have to decref here.  it's also
+                * done in freeb().  this is the earliest we can free. */
+               kfree((void*)ebd->base);
+               ebd->base = ebd->off = 0;
+       }
+}
+
 /* Make sure the first block has at least n bytes in its main body.  Pulls up
  * data from the *list* of blocks.  Returns 0 if there is not enough data in the
  * block list. */
@@ -1264,16 +1280,7 @@ static size_t read_from_block(struct block *b, uint8_t *to, size_t amt)
                memcpy(to, (void*)(ebd->base + ebd->off), copy_amt);
                /* we're actually consuming the entries, just like how we
                 * advance rp up above, and might only consume part of one. */
-               ebd->len -= copy_amt;
-               ebd->off += copy_amt;
-               b->extra_len -= copy_amt;
-               if (!ebd->len) {
-                       /* we don't actually have to decref here.  it's also
-                        * done in freeb().  this is the earliest we can free.
-                        */
-                       kfree((void*)ebd->base);
-                       ebd->base = ebd->off = 0;
-               }
+               block_consume_ebd_bytes(b, ebd, copy_amt);
                to += copy_amt;
                amt -= copy_amt;
                retval += copy_amt;