nuttx/mm/iob/iob_alloc.c
Xiang Xiao c47b1e2c5b !sys/types.h: change time_t and clock_t to int64_t to align with other OSes
POSIX leaves the signedness of time_t and clock_t unspecified, but
mainstream implementations (Linux glibc/musl, the BSDs, macOS, RTEMS,
Zephyr's POSIX layer, Windows _time64) expose time_t as signed 64-bit.
NuttX has historically used uint64_t only because it was tied to the
CONFIG_SYSTEM_TIME64 knob; with that gone, switch:

  time_t   : uint64_t  -> int64_t
  clock_t  : uint64_t  -> int64_t
  CLOCK_MAX: UINT64_MAX -> INT64_MAX

This lets (time_t)-1 sentinels, negative tick deltas, and host-side
headers behave as on every other POSIX system without source churn.

Headers updated:
  - include/sys/types.h, include/limits.h, include/nuttx/clock.h
  - include/nuttx/fs/hostfs.h (nuttx_time_t alias)
  - include/nuttx/{mqueue.h,wdog.h,wqueue.h,timers/clkcnt.h}

Because clock_t is now signed 64-bit, the NuttX-internal sclock_t
alias becomes redundant: every sclock_t/SCLOCK_MAX use is folded
back to clock_t/CLOCK_MAX (notably in sched/wdog, sched/mqueue,
sched/sched, sched/clock, sched/timer, libs/libc/time, fs/vfs and
the drivers/arch consumers below).

Tick/period constants (NSEC_PER_SEC, USEC_PER_SEC, MSEC_PER_SEC,
SEC_PER_MIN, ...) in include/nuttx/clock.h are retyped from "long"
literals to INT64_C(...) so that 64-bit arithmetic no longer
depends on the host's long width.

Strip now-redundant (time_t)/(clock_t)/(unsigned long) casts and
unsigned-only branches across the tree:
  - arch RTC / oneshot / tickless lowerhalfs:
      arm: cxd56xx, efm32, imxrt, lc823450, max326xx, sam34, sama5,
           samd5e5, samv7, stm32, stm32f7, stm32h7, stm32l4, stm32wb,
           xmc4
      mips: pic32mz       sparc: bm3803       x86_64: intel64
      risc-v/xtensa: espressif (esp_i2c[_slave], esp_rtc,
           esp32c3{_i2c,_rtc,_wifi_adapter}, esp32{,s2,s3}_*),
           mpfs_perf
  - drivers: audio/tone, input/aw86225, power/pm/{activity,
           stability}_governor, rpmsg/rpmsg_ping,
           timers/{ds3231,mcp794xx,pcf85263,rx8010},
           wireless/ieee80211/bcm43xxx, wireless/spirit/spirit_spi
  - core: fs/vfs/{fs_poll,fs_timerfd}, mm/iob/iob_alloc,
          libs/libc/{netdb/lib_dnscache,time/{lib_calendar2utc,
          lib_time}}, net/icmp/icmp_pmtu, net/icmpv6/icmpv6_pmtu,
          net/ipfrag, net/tcp/{tcp.h,tcp_timer},
          net/utils/net_snoop, net/mld/mld_query (drop the now-dead
          mld_mrc2mrd helper since signed math handles it directly),
          sched/clock/{clock,clock_initialize},
          sched/sched/{sched_profil,sched_setparam,sched_setscheduler},
          sched/pthread/pthread_create,
          sched/wdog/{wd_gettime,wd_start,wdog.h},
          sched/timer/timer_gettime, sched/mqueue/*

Flip the few in-tree printf format strings that assumed an
unsigned 64-bit tv_sec:
  * drivers/rpmsg/rpmsg_ping.c                       PRIu64 -> PRId64
  * arch/xtensa/src/esp32{,s2,s3}/esp32*_oneshot_lowerhalf.c
                                          PRIu32 (already wrong) -> PRId64

Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2026-05-19 16:21:28 +08:00

441 lines
13 KiB
C

/****************************************************************************
* mm/iob/iob_alloc.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <assert.h>
#include <errno.h>
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <nuttx/sched.h>
#ifdef CONFIG_IOB_ALLOC
# include <nuttx/kmalloc.h>
#endif
#include <nuttx/nuttx.h>
#include <nuttx/mm/iob.h>
#include "iob.h"
/****************************************************************************
* Private Functions
****************************************************************************/
static clock_t iob_allocwait_gettimeout(clock_t start, unsigned int timeout)
{
clock_t tick;
tick = clock_systime_ticks() - start;
if (tick >= MSEC2TICK(timeout))
{
tick = 0;
}
else
{
tick = MSEC2TICK(timeout) - tick;
}
return tick;
}
/****************************************************************************
* Name: iob_alloc_committed
*
* Description:
* Allocate an I/O buffer by taking the buffer at the head of the committed
* list.
*
****************************************************************************/
static FAR struct iob_s *iob_alloc_committed(void)
{
FAR struct iob_s *iob = NULL;
irqstate_t flags;
/* We don't know what context we are called from so we use extreme measures
* to protect the committed list: We disable interrupts very briefly.
*/
flags = spin_lock_irqsave(&g_iob_lock);
/* Take the I/O buffer from the head of the committed list */
iob = g_iob_committed;
if (iob != NULL)
{
/* Remove the I/O buffer from the committed list */
g_iob_committed = iob->io_flink;
/* Put the I/O buffer in a known state */
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_pktlen = 0; /* Total length of the packet */
}
spin_unlock_irqrestore(&g_iob_lock, flags);
return iob;
}
static FAR struct iob_s *iob_tryalloc_internal(bool throttled)
{
FAR struct iob_s *iob;
#if CONFIG_IOB_THROTTLE > 0
int16_t count = (throttled ? g_iob_count - CONFIG_IOB_THROTTLE :
g_iob_count);
/* If there are free I/O buffers for this allocation */
if (count > 0)
#endif
{
/* Take the I/O buffer from the head of the free list */
iob = g_iob_freelist;
if (iob != NULL)
{
/* Remove the I/O buffer from the free list and decrement the
* counting semaphore(s) that tracks the number of available
* IOBs.
*/
g_iob_freelist = iob->io_flink;
g_iob_count--;
DEBUGASSERT(g_iob_count >= 0);
/* Put the I/O buffer in a known state */
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_pktlen = 0; /* Total length of the packet */
return iob;
}
}
return NULL;
}
/****************************************************************************
* Name: iob_allocwait
*
* Description:
* Allocate an I/O buffer, waiting if necessary. This function cannot be
* called from any interrupt level logic.
*
****************************************************************************/
static FAR struct iob_s *iob_allocwait(bool throttled, unsigned int timeout)
{
FAR struct iob_s *iob;
irqstate_t flags;
FAR sem_t *sem;
clock_t start;
int ret = OK;
#if CONFIG_IOB_THROTTLE > 0
/* Select the semaphore to wait. */
sem = (throttled ? &g_throttle_sem : &g_iob_sem);
#else
sem = &g_iob_sem;
#endif
/* The following must be atomic; interrupt must be disabled so that there
* is no conflict with interrupt level I/O buffer allocations. This is
* not as bad as it sounds because interrupts will be re-enabled while
* we are waiting for I/O buffers to become free.
*/
flags = spin_lock_irqsave(&g_iob_lock);
/* Try to get an I/O buffer */
iob = iob_tryalloc_internal(throttled);
if (iob == NULL)
{
#if CONFIG_IOB_THROTTLE > 0
if (throttled)
{
g_throttle_wait++;
}
else
#endif
{
g_iob_count--;
}
spin_unlock_irqrestore(&g_iob_lock, flags);
if (timeout == UINT_MAX)
{
ret = nxsem_wait_uninterruptible(sem);
}
else
{
start = clock_systime_ticks();
ret = nxsem_tickwait_uninterruptible(sem,
iob_allocwait_gettimeout(start, timeout));
}
if (ret >= 0)
{
/* When we wake up from wait successfully, an I/O buffer was
* freed and we hold a count for one IOB.
*/
iob = iob_alloc_committed();
DEBUGASSERT(iob != NULL);
}
return iob;
}
spin_unlock_irqrestore(&g_iob_lock, flags);
return iob;
}
#ifdef CONFIG_IOB_ALLOC
/****************************************************************************
* Name: iob_free_dynamic
*
* Description:
* Free the I/O buffer and payload to the heap
*
* Input Parameters:
* data -
*
****************************************************************************/
static void iob_free_dynamic(FAR void *data)
{
kmm_free(data);
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: iob_timedalloc
*
* Description:
* Allocate an I/O buffer by taking the buffer at the head of the free list.
* This wait will be terminated when the specified timeout expires.
*
* Input Parameters:
* throttled - An indication of the IOB allocation is "throttled"
* timeout - Timeout value in milliseconds.
*
****************************************************************************/
FAR struct iob_s *iob_timedalloc(bool throttled, unsigned int timeout)
{
/* Were we called from the interrupt level? */
if (up_interrupt_context() || sched_idletask() || timeout == 0)
{
/* Yes, then try to allocate an I/O buffer without waiting */
return iob_tryalloc(throttled);
}
else
{
/* Then allocate an I/O buffer, waiting as necessary */
return iob_allocwait(throttled, timeout);
}
}
/****************************************************************************
* Name: iob_alloc
*
* Description:
* Allocate an I/O buffer by taking the buffer at the head of the free list.
*
****************************************************************************/
FAR struct iob_s *iob_alloc(bool throttled)
{
return iob_timedalloc(throttled, UINT_MAX);
}
/****************************************************************************
* Name: iob_tryalloc
*
* Description:
* Try to allocate an I/O buffer by taking the buffer at the head of the
* free list without waiting for a buffer to become free.
*
****************************************************************************/
FAR struct iob_s *iob_tryalloc(bool throttled)
{
FAR struct iob_s *iob;
irqstate_t flags;
/* We don't know what context we are called from so we use extreme measures
* to protect the free list: We disable interrupts very briefly.
*/
flags = spin_lock_irqsave(&g_iob_lock);
iob = iob_tryalloc_internal(throttled);
spin_unlock_irqrestore(&g_iob_lock, flags);
return iob;
}
#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 = ALIGN_UP(sizeof(struct iob_s), IOB_ALIGNMENT) + size;
iob = kmm_memalign(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 *)ALIGN_UP((uintptr_t)(iob + 1),
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;
}
/****************************************************************************
* Name: iob_init_with_data
*
* Description:
* Initialize an I/O buffer and playload
*
* 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,
* and caller need to reserve space for alignment.
* 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.
*
* +---------+
* | IOB |
* | io_data |--+
* | buffer |<-+
* +---------+
*
****************************************************************************/
FAR struct iob_s *iob_init_with_data(FAR void *data, uint16_t size,
iob_free_cb_t free_cb)
{
FAR struct iob_s *iob = (FAR struct iob_s *)data;
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_pktlen = 0; /* Total length of the packet */
iob->io_free = free_cb; /* Customer free callback */
iob->io_data = (FAR uint8_t *)ALIGN_UP((uintptr_t)(iob + 1),
IOB_ALIGNMENT);
iob->io_bufsize = ((FAR uint8_t *)data + size) - iob->io_data;
return iob;
}
#endif