From b934555fd19c74149a92469876c3adbc2d46f1a0 Mon Sep 17 00:00:00 2001 From: zhanghongyu Date: Mon, 1 Apr 2024 14:09:10 +0800 Subject: [PATCH] mm/iob: Support alloc IOB via malloc Support the network interface card driver to receive zero copies of packets and send and receive giant frame packets, allowing drivers to initialize the DMA buffer to the iob structure, and we can apply for IOB with large memory Signed-off-by: zhanghongyu --- include/nuttx/mm/iob.h | 67 +++++++++++++++++++++-- mm/iob/Kconfig | 6 +++ mm/iob/iob.h | 2 + mm/iob/iob_alloc.c | 108 +++++++++++++++++++++++++++++++++++++ mm/iob/iob_free.c | 12 +++++ mm/iob/iob_initialize.c | 18 +++++-- mm/iob/iob_update_pktlen.c | 19 +++++-- 7 files changed, 219 insertions(+), 13 deletions(-) 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 */