diff --git a/include/nuttx/mm/iob.h b/include/nuttx/mm/iob.h index a530bd996f..a69be9803e 100644 --- a/include/nuttx/mm/iob.h +++ b/include/nuttx/mm/iob.h @@ -88,12 +88,18 @@ # define IOB_QEMPTY(q) ((q)->qh_head == NULL) #endif -#define IOB_BUFSIZE(p) CONFIG_IOB_BUFSIZE +#ifdef CONFIG_IOB_ALLOC +# define IOB_BUFSIZE(p) ((p)->io_bufsize) +#else +# define IOB_BUFSIZE(p) CONFIG_IOB_BUFSIZE +#endif /**************************************************************************** * Public Types ****************************************************************************/ +typedef CODE void (*iob_free_cb_t)(FAR void *data); + /* Represents one I/O buffer. A packet is contained by one or more I/O * buffers in a chain. The io_pktlen is only valid for the I/O buffer at * the head of the chain. @@ -107,16 +113,24 @@ struct iob_s /* Payload */ -#if CONFIG_IOB_BUFSIZE < 256 +#if CONFIG_IOB_BUFSIZE < 256 && !defined(CONFIG_IOB_ALLOC) uint8_t io_len; /* Length of the data in the entry */ uint8_t io_offset; /* Data begins at this offset */ #else uint16_t io_len; /* Length of the data in the entry */ uint16_t io_offset; /* Data begins at this offset */ +# ifdef CONFIG_IOB_ALLOC + uint16_t io_bufsize; /* Total length of the data buffer */ +# endif #endif unsigned int io_pktlen; /* Total length of the packet */ - uint8_t io_data[CONFIG_IOB_BUFSIZE]; +#ifdef CONFIG_IOB_ALLOC + iob_free_cb_t io_free; /* Custom free callback */ + FAR uint8_t *io_data; +#else + uint8_t io_data[CONFIG_IOB_BUFSIZE]; +#endif }; #if CONFIG_IOB_NCHAINS > 0 @@ -205,6 +219,53 @@ FAR struct iob_s *iob_alloc(bool throttled); FAR struct iob_s *iob_tryalloc(bool throttled); +#ifdef CONFIG_IOB_ALLOC +/**************************************************************************** + * Name: iob_alloc_dynamic + * + * Description: + * Allocate an I/O buffer and playload from heap + * + * Input Parameters: + * size - The size of the io_data that is allocated. + * + * +---------+ + * | IOB | + * | io_data |--+ + * | buffer |<-+ + * +---------+ + * + ****************************************************************************/ + +FAR struct iob_s *iob_alloc_dynamic(uint16_t size); + +/**************************************************************************** + * Name: iob_alloc_with_data + * + * Description: + * Allocate an I/O buffer from heap and attach the external payload + * + * Input Parameters: + * data - Make io_data point to a specific address, the caller is + * responsible for the memory management. The caller should + * ensure that the memory is not freed before the iob is freed. + * + * +---------+ +-->+--------+ + * | IOB | | | data | + * | io_data |--+ +--------+ + * +---------+ + * + * size - The size of the data parameter + * free_cb - Notify the caller when the iob is freed. The caller can + * perform additional operations on the data before it is freed. + * The free_cb is called when the iob is freed. + * + ****************************************************************************/ + +FAR struct iob_s *iob_alloc_with_data(FAR void *data, uint16_t size, + iob_free_cb_t free_cb); +#endif + /**************************************************************************** * Name: iob_navail * diff --git a/mm/iob/Kconfig b/mm/iob/Kconfig index e00deb759c..ce30937b5d 100644 --- a/mm/iob/Kconfig +++ b/mm/iob/Kconfig @@ -103,6 +103,12 @@ config IOB_NOTIFIER_DIV a notification will be sent only when there are a multiple of 4 IOBs available. +config IOB_ALLOC + bool "Dynamic I/O buffer allocation" + default n + ---help--- + This option will enable dynamic I/O buffer allocation + config IOB_DEBUG bool "Force I/O buffer debug" default n diff --git a/mm/iob/iob.h b/mm/iob/iob.h index 14947915de..77ff58ec54 100644 --- a/mm/iob/iob.h +++ b/mm/iob/iob.h @@ -39,6 +39,8 @@ * Pre-processor Definitions ****************************************************************************/ +#define ROUNDUP(x, y) (((x) + (y) - 1) / (y) * (y)) + #if defined(CONFIG_DEBUG_FEATURES) && defined(CONFIG_IOB_DEBUG) # define ioberr _err # define iobwarn _warn diff --git a/mm/iob/iob_alloc.c b/mm/iob/iob_alloc.c index 5edb369e34..c182b943a9 100644 --- a/mm/iob/iob_alloc.c +++ b/mm/iob/iob_alloc.c @@ -30,6 +30,9 @@ #include #include #include +#ifdef CONFIG_IOB_ALLOC +# include +#endif #include #include "iob.h" @@ -197,6 +200,23 @@ static FAR struct iob_s *iob_allocwait(bool throttled, unsigned int timeout) return iob; } +#ifdef CONFIG_IOB_ALLOC +/**************************************************************************** + * Name: iob_free_dynamic + * + * Description: + * Dummy free callback function, do nothing. + * + * Input Parameters: + * data - + * + ****************************************************************************/ + +static void iob_free_dynamic(FAR void *data) +{ +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -329,3 +349,91 @@ FAR struct iob_s *iob_tryalloc(bool throttled) spin_unlock_irqrestore(&g_iob_lock, flags); return NULL; } + +#ifdef CONFIG_IOB_ALLOC + +/**************************************************************************** + * Name: iob_alloc_dynamic + * + * Description: + * Allocate an I/O buffer and playload from heap + * + * Input Parameters: + * size - The size of the io_data that is allocated. + * + * +---------+ + * | IOB | + * | io_data |--+ + * | buffer |<-+ + * +---------+ + * + ****************************************************************************/ + +FAR struct iob_s *iob_alloc_dynamic(uint16_t size) +{ + FAR struct iob_s *iob; + size_t alignsize; + + alignsize = ROUNDUP(sizeof(struct iob_s), CONFIG_IOB_ALIGNMENT) + size; + + iob = kmm_memalign(CONFIG_IOB_ALIGNMENT, alignsize); + if (iob) + { + iob->io_flink = NULL; /* Not in a chain */ + iob->io_len = 0; /* Length of the data in the entry */ + iob->io_offset = 0; /* Offset to the beginning of data */ + iob->io_bufsize = size; /* Total length of the iob buffer */ + iob->io_pktlen = 0; /* Total length of the packet */ + iob->io_free = iob_free_dynamic; /* Customer free callback */ + iob->io_data = (FAR uint8_t *)ROUNDUP((uintptr_t)(iob + 1), + CONFIG_IOB_ALIGNMENT); + } + + return iob; +} + +/**************************************************************************** + * Name: iob_alloc_with_data + * + * Description: + * Allocate an I/O buffer from heap and attach the external payload + * + * Input Parameters: + * data - Make io_data point to a specific address, the caller is + * responsible for the memory management. The caller should + * ensure that the memory is not freed before the iob is freed. + * + * +---------+ +-->+--------+ + * | IOB | | | data | + * | io_data |--+ +--------+ + * +---------+ + * + * size - The size of the data parameter + * free_cb - Notify the caller when the iob is freed. The caller can + * perform additional operations on the data before it is freed. + * The free_cb is called when the iob is freed. + * + ****************************************************************************/ + +FAR struct iob_s *iob_alloc_with_data(FAR void *data, uint16_t size, + iob_free_cb_t free_cb) +{ + FAR struct iob_s *iob; + + DEBUGASSERT(free_cb != NULL); + + iob = kmm_malloc(sizeof(struct iob_s)); + if (iob) + { + iob->io_flink = NULL; /* Not in a chain */ + iob->io_len = 0; /* Length of the data in the entry */ + iob->io_offset = 0; /* Offset to the beginning of data */ + iob->io_bufsize = size; /* Total length of the iob buffer */ + iob->io_pktlen = 0; /* Total length of the packet */ + iob->io_free = free_cb; /* Customer free callback */ + iob->io_data = data; + } + + return iob; +} +#endif diff --git a/mm/iob/iob_free.c b/mm/iob/iob_free.c index f2c04be917..b8a26cce49 100644 --- a/mm/iob/iob_free.c +++ b/mm/iob/iob_free.c @@ -30,6 +30,9 @@ #include #include +#ifdef CONFIG_IOB_ALLOC +# include +#endif #include #include "iob.h" @@ -112,6 +115,15 @@ FAR struct iob_s *iob_free(FAR struct iob_s *iob) next, next->io_pktlen, next->io_len); } +#ifdef CONFIG_IOB_ALLOC + if (iob->io_free != NULL) + { + iob->io_free(iob->io_data); + kmm_free(iob); + return next; + } +#endif + /* Free the I/O buffer by adding it to the head of the free or the * committed list. We don't know what context we are called from so * we use extreme measures to protect the free list: We disable diff --git a/mm/iob/iob_initialize.c b/mm/iob/iob_initialize.c index d232f1e7bf..8852d13475 100644 --- a/mm/iob/iob_initialize.c +++ b/mm/iob/iob_initialize.c @@ -34,11 +34,15 @@ * Pre-processor Definitions ****************************************************************************/ -#define ROUNDUP(x, y) (((x) + (y) - 1) / (y) * (y)) - /* Fix the I/O Buffer size with specified alignment size */ -#define IOB_ALIGN_SIZE ROUNDUP(sizeof(struct iob_s), CONFIG_IOB_ALIGNMENT) +#ifdef CONFIG_IOB_ALLOC +# define IOB_ALIGN_SIZE ROUNDUP(sizeof(struct iob_s) + CONFIG_IOB_BUFSIZE, \ + CONFIG_IOB_ALIGNMENT) +#else +# define IOB_ALIGN_SIZE ROUNDUP(sizeof(struct iob_s), CONFIG_IOB_ALIGNMENT) +#endif + #define IOB_BUFFER_SIZE (IOB_ALIGN_SIZE * CONFIG_IOB_NBUFFERS + \ CONFIG_IOB_ALIGNMENT - 1) @@ -138,8 +142,12 @@ void iob_initialize(void) /* Add the pre-allocate I/O buffer to the head of the free list */ - iob->io_flink = g_iob_freelist; - g_iob_freelist = iob; + iob->io_flink = g_iob_freelist; +#ifdef CONFIG_IOB_ALLOC + iob->io_bufsize = CONFIG_IOB_BUFSIZE; + iob->io_data = (FAR uint8_t *)(iob + 1); +#endif + g_iob_freelist = iob; } #if CONFIG_IOB_NCHAINS > 0 diff --git a/mm/iob/iob_update_pktlen.c b/mm/iob/iob_update_pktlen.c index a5ad150135..0b9ebc8d40 100644 --- a/mm/iob/iob_update_pktlen.c +++ b/mm/iob/iob_update_pktlen.c @@ -49,9 +49,9 @@ int iob_update_pktlen(FAR struct iob_s *iob, unsigned int pktlen, { FAR struct iob_s *penultimate; FAR struct iob_s *next; - uint16_t offset = 0; + int remain = pktlen; int ninqueue = 0; - int nrequire; + int nrequire = 0; uint16_t len; /* The data offset must be less than CONFIG_IOB_BUFSIZE */ @@ -67,19 +67,28 @@ int iob_update_pktlen(FAR struct iob_s *iob, unsigned int pktlen, while (next != NULL) { ninqueue++; - offset += next->io_offset; penultimate = next; + if (remain > 0) + { + nrequire++; + remain -= IOB_BUFSIZE(next) - next->io_offset; + } + next = next->io_flink; } - /* Trim inqueue entries if needed */ + if (remain > 0) + { + nrequire += (remain + CONFIG_IOB_BUFSIZE - 1) / CONFIG_IOB_BUFSIZE; + } - nrequire = (pktlen + offset + IOB_BUFSIZE(iob) - 1) / IOB_BUFSIZE(iob); if (nrequire == 0) { nrequire = 1; } + /* Trim inqueue entries if needed */ + if (nrequire < ninqueue) { /* Loop until complete the trim */