/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Copyright 2014 Couchbase, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef NETBUF_H #define NETBUF_H #ifdef __cplusplus extern "C" { #endif /** * @file * @brief Netbuf write buffers */ /** * @defgroup netbufs Netbufs * * # Introduction * * ## GOALS * * 1. provide a simple buffer allocation API * From a logic perspective it's simplest to deal with a straight * contiguous buffer per packet. * * 2. provide an efficient way of sending multiple contiguous packets. This * will reduce IOV fragmentation and reduce the number of trips to the * I/O plugin for multiple writes. Currently this is done very efficiently * with the ringbuffer - however this comes at the cost of copying all * request data to the ringbuffer itself. Our aim is to reduce the * number of copies while still maintaining a packed buffer. * * 3. Allow a pluggable method by which user-provided data can be plugged * into the span/cursor/flush architecture. * * @addtogroup netbufs * @{ */ #include "sllist.h" #include "netbuf-defs.h" #include "netbuf-mblock.h" /** * @brief Structure representing a buffer within netbufs * * @note It is recommended that you maintain the individual fields in your * own structure and then re-create them as needed. The span structure is 16 * bytes on 64 bit systems, but can be reduced to 12 if needed. Additionally, * you may already have the 'size' field stored/calculated elsewhere. */ typedef struct { /** @private Parent block */ nb_MBLOCK *parent; /** @private Offset from root at which this buffer begins */ nb_SIZE offset; /** write-once: Allocation size */ nb_SIZE size; } nb_SPAN; #define NETBUF_INVALID_OFFSET (nb_SIZE)-1 /** * Creates a span from a buffer _not_ owned by netbufs. * @param span the span to initialize * @param buf the buffer * @param len the length of the buffer */ #define CREATE_STANDALONE_SPAN(span, buf, len) \ (span)->parent = (nb_MBLOCK *) (void *)buf; \ (span)->offset = NETBUF_INVALID_OFFSET; \ (span)->size = len; /** @private */ typedef struct { sllist_node slnode; char *base; nb_SIZE len; /* Extra 4 bytes here. WHAT WE DO!!! */ } nb_SNDQELEM; /** @private */ typedef struct { /** Linked list of pending spans to send */ sllist_root pending; /** * List of PDUs to be flushed. A PDU is comprised of one or more IOVs * (or even a subsection thereof) */ sllist_root pdus; /** The last window which was part of the previous fill call */ nb_SNDQELEM *last_requested; /** * Number of bytes enqueued in the 'last request' element. This is needed * because it is possible for the last element to grow in length during * a subsequent flush. */ nb_SIZE last_offset; /** Offset from last PDU which was partially flushed */ nb_SIZE pdu_offset; /** Pool of elements to utilize */ nb_MBPOOL elempool; } nb_SENDQ; struct netbuf_st { /** Send Queue */ nb_SENDQ sendq; /** Pool for variable-size data */ nb_MBPOOL datapool; nb_SETTINGS settings; }; /** * Quick way to get the span from a buffer, when the buffer is *known* to * be standalone (i.e. CREATE_STANDALONE_SPAN() * @param span The span from which to extract the buffer * @return a pointer to the buffer */ #define SPAN_SABUFFER_NC(span) ((char *)(span)->parent) /** * Quick way to get the span from a buffer when the buffer is known *not* * to be standalone * @param span The span from which to extract the buffer * @return A pointer to a buffer */ #define SPAN_MBUFFER_NC(span) ((span)->parent->root + (span)->offset) /** * @brief Retrieves a pointer to the buffer related to this span. * @param span the span from which to extract the buffer * @return a pointer to the buffer. * * @see SPAN_SABUFFER_NC * @see SPAN_MBUFFER_NC */ #define SPAN_BUFFER(span) \ (((span)->offset == NETBUF_INVALID_OFFSET) ? SPAN_SABUFFER_NC(span) : SPAN_MBUFFER_NC(span)) /** * @brief allocate a span * * Reserve a contiguous region of memory, in-order for a given span. The * span will be reserved from the last block to be flushed to the network. * * The contents of the span are guaranteed to be contiguous (though not aligned) * and are available via the SPAN_BUFFER macro. * * @return 0 if successful, -1 on error */ int netbuf_mblock_reserve(nb_MGR *mgr, nb_SPAN *span); /** * @brief release a span * * Release a span previously allocated via reserve_span. It is assumed that the * contents of the span have either: * * 1. been successfully sent to the network * 2. have just been scheduled (and are being removed due to error handling) * 3. have been partially sent to a connection which is being closed. * * @param mgr the manager in which this span is reserved * @param span the span */ void netbuf_mblock_release(nb_MGR *mgr, nb_SPAN *span); /** * @brief Enqueue a span for serialization * * Schedules an IOV to be placed inside the send queue. The storage of the * underlying buffer must not be freed or otherwise modified until it has * been sent. * * With the current usage model, flush status is implicitly completed once * a response has arrived. * * Note that you may create the IOV from a SPAN object like so: * @code{.c} * iov->iov_len = span->size; * iov->iov_base = SPAN_BUFFER(span); * @endcode */ void netbuf_enqueue(nb_MGR *mgr, const nb_IOV *bufinfo); void netbuf_enqueue_span(nb_MGR *mgr, nb_SPAN *span); /** * Gets the number of IOV structures required to flush the entire contents of * all buffers. */ unsigned int netbuf_get_niov(nb_MGR *mgr); /** * @brief * Populates an iovec structure for flushing a set of bytes from the various * blocks. * * You may call this function mutltiple times, so long as each call to * start_flush is eventually mapped with a call to end_flush. * * @code{.c} * netbuf_start_flush(mgr, iov1, niov1); * netbuf_start_flush(mgr, iov2, niov2); * ... * netbuf_end_flush(mgr, nbytes1); * netbuf_end_flush(mgr, nbytes2); * @endcode * * Additionally, only the LAST end_flush call may be supplied an nflushed * parameter which is smaller than the size returned by start_flush. If the * entire contents of the `iovs` structure cannot be flushed immediately and * you do not wish to persist it until such a time that it may be flushed, then * netbuf_reset_flush() should be called. See that functions' documentation * for more details. * * This function may be thought of advancing a virtual cursor which is mapped * to a send queue. Each time this function is called the cursor is advanced * by the number of bytes that the library expects you to flush (i.e. the return * value of this function). Typically the cursor is never rewound and any * operation that advances the cursor places the burden on the user to * actually flush the data contained within the IOV objects. * The netbuf_end_flush() function merely has the task of releasing any memory * used for mapping of already-flushed data. * * From a different perspective, each call to netbuf_start_flush() establishes * a contract between the library and the user: The library guarantees that * this specific region (referred to within the IOVs) will not be flushed by * any other subsystem (i.e. nothing else will try to flush the same data). * The user guarantees that the data will eventually be flushed, and that the * data will be flushed in the order it was received via start_flush(). * * * @param mgr the manager object * @param iovs an array of iovec structures * @param niov the number of iovec structures allocated. * @param[out] nused how many IOVs are actually required * * @return the number of bytes which can be flushed in this IOV. If the * return value is 0 then there are no more bytes to flush. * * Note that the return value is limited by the number of IOV structures * provided and should not be taken as an indicator of how many bytes are * used overall. */ nb_SIZE netbuf_start_flush(nb_MGR *mgr, nb_IOV *iovs, int niov, int *nused); /** * @brief Indicate that a flush has completed. * * Indicate that a number of bytes have been flushed. This should be called after * the data retrieved by get_flushing_iov has been flushed to the TCP buffers. * * @param mgr the manager object * @param nflushed how much data in bytes was flushed to the network. */ void netbuf_end_flush(nb_MGR *mgr, nb_SIZE nflushed); /** * Reset the flush context for the buffer. This should be called only when the * following is true: * * (1) There is only a single open call to netbuf_start_flush * (2) The last call to end_flush did not pass all the bytes which were to * be flushed. * * In this case, the next call to start_flush() will return an IOV which begins * where the last end_flush() finished, rather than the last start_flush(). * As a consequence it means that the previous IOV populated with start_flush * is no longer valid and start_flush should be called again. */ #define netbuf_reset_flush(mgr) do { \ (mgr)->sendq.last_requested = NULL; \ (mgr)->sendq.last_offset = 0; \ } while (0); /** * Informational function to get the total size of all data in the * buffers. This traverses all blocks, so call this for debugging only. */ nb_SIZE netbuf_get_size(const nb_MGR *mgr); /** * Get the maximum size of a span which can be satisfied without using an * additional block. * * @param mgr * * @param allow_wrap * Whether to take into consideration wrapping. If this is true then the span * size will allow wrapping. If disabled, then only the packed size will be * available. Consider: *
* [ ooooooo{S:10}xxxxxxxxx{C:10}ooooo{A:5} ] ** If wrapping is allowed, then the maximum span size will be 10, from 0..10 * but the last 5 bytes at the end will be lost for the duration of the block. * If wrapping is not allowed then the maximum span size will be 5. * * @return * the maximum span size without requiring additional blocks. */ nb_SIZE netbuf_mblock_get_next_size(const nb_MGR *mgr, int allow_wrap); /** * @brief Initializes an nb_MGR structure * @param mgr the manager to initialize * @param settings */ void netbuf_init(nb_MGR *mgr, const nb_SETTINGS *settings); /** * @brief Frees up any allocated resources for a given manager * @param mgr the manager for which to release resources */ void netbuf_cleanup(nb_MGR *mgr); /** * Populates the settings structure with the default settings. This structure * may then be modified or tuned and passed to netbuf_init() */ void netbuf_default_settings(nb_SETTINGS *settings); /** * Dump the internal structure of the manager to the screen. Useful for * debugging. */ void netbuf_dump_status(nb_MGR *mgr, FILE *fp); /** * Mark a PDU as being enqueued. This should be called whenever the final IOV * for a given PDU has just been enqueued. * * The PDU itself must remain valid and is assumed to contain an 'slnode' * structure which will point to the next PDU. The PDU will later be removed * from the queue when 'end_flush' has been called including its range. * * @param mgr The manager * * @param pdu An opaque pointer * * @param lloff The offset from the pdu pointer at which the slist_node * structure may be found. */ void netbuf_pdu_enqueue(nb_MGR *mgr, void *pdu, nb_SIZE lloff); /** * This callback is invoked during 'end_flush2'. * * @param pdu The PDU pointer enqueued via netbuf_pdu_enqueue() * * @param remaining A hint passed to the callback indicating how many bytes * remain on the stream. IFF remaining is greater than the size of the * PDU the callback may change internal state within the packet to mark * it as flushed. * * XXX: * If nremaining < pdusize then it must be ignored. The value * may have been previously passed to the same callback during a * prior iteration. * * This is done by design in order to avoid forcing each PDU to maintain * a variable indicating "How much was flushed". * * @param arg A pointer passed to the start_flush call; used to correlate any * data common to the queue itself. * * @return The size of the PDU. This will be used to determine future calls * to the callback for subsequent PDUs. * * If size <= remaining then this PDU will be popped off the PDU queue * and is deemed to be no longer utilized by the send queue (and may * be released from the mblock allocator; if it's being used). * * If size > remaining then no further callbacks will be invoked on * the relevant PDUs. */ typedef nb_SIZE (*nb_getsize_fn)(void *pdu, nb_SIZE remaining, void *arg); void netbuf_end_flush2(nb_MGR *mgr, unsigned int nflushed, nb_getsize_fn callback, nb_SIZE lloff, void *arg); /** * Ensures that the given internal structures of the manager are not allocated * or otherwise in use by other systems. This is useful for testing to ensure * that we wouldn't accidentally think everything is OK. * * Because resources are released at the block level, we might have had blocks * which were partially allocated. * * This also checks the PDU and send queues as well. */ int netbuf_is_clean(nb_MGR *mgr); /** * Determines if there is any data to be flushed to the network */ int netbuf_has_flushdata(nb_MGR *mgr); /**@}*/ #ifdef __cplusplus } #endif #endif /* LCB_PACKET_H */