mirror of
https://github.com/apache/nuttx.git
synced 2026-08-01 20:28:58 +00:00
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>
441 lines
13 KiB
C
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
|