mm/iob: Improve the IOB notifier data structures. Replace the fixed array with a singly linked list. For small value of CONFIG_IOB_NWAITERS, either solution is fine. But the fixed array does not scale well when CONFIG_IOB_NWAITERS is large.

This commit is contained in:
Gregory Nutt 2018-09-08 17:46:57 -06:00
parent 5bd0f596fc
commit 0eb1beee63
3 changed files with 173 additions and 48 deletions

View File

@ -154,6 +154,18 @@ FAR struct iob_qentry_s *iob_tryalloc_qentry(void);
FAR struct iob_qentry_s *iob_free_qentry(FAR struct iob_qentry_s *iobq); FAR struct iob_qentry_s *iob_free_qentry(FAR struct iob_qentry_s *iobq);
/****************************************************************************
* Name: iob_notify_initialize
*
* Description:
* Set up the notification structure for normal operation.
*
****************************************************************************/
#ifdef CONFIG_IOB_NOTIFIER
void iob_notify_initialize(void);
#endif
/**************************************************************************** /****************************************************************************
* Name: iob_notify_signal * Name: iob_notify_signal
* *

View File

@ -154,6 +154,13 @@ void iob_initialize(void)
nxsem_init(&g_qentry_sem, 0, CONFIG_IOB_NCHAINS); nxsem_init(&g_qentry_sem, 0, CONFIG_IOB_NCHAINS);
#endif #endif
#ifdef CONFIG_IOB_NOTIFIER
/* Initialize the IOB notification data structures */
iob_notify_initialize();
#endif
initialized = true; initialized = true;
} }
} }

View File

@ -63,34 +63,114 @@
struct iob_notify_s struct iob_notify_s
{ {
bool waiter; /* True if the waiter information is valid */ FAR struct iob_notify_s *flink; /* Supports a singly linked list */
uint8_t signo; /* The signal number to use when notifying the PID */ uint8_t signo; /* The signal number for notification */
pid_t pid; /* The PID to be notified when an IOB is available */ pid_t pid; /* The PID to be notified */
}; };
/**************************************************************************** /****************************************************************************
* Private Data * Private Data
****************************************************************************/ ****************************************************************************/
/* This is a an array of threads waiting for an IOB. When an IOB becomes /* This is a statically allocated pool of notification structures */
* available, *all* of the waiters in this array will be signaled and the
* entry will be marked invalid. If there are multiple waiters then only static struct iob_notify_s g_iobnotify_pool[CONFIG_IOB_NWAITERS];
* the highest priority thread will get the IOB. Lower priority threads
* will need to call iob_notify once again. /* This is a list of free notification structures */
static FAR struct iob_notify_s *g_iobnotify_free;
/* This is a singly linked list pending notifications. When an IOB becomes
* available, *all* of the waiters in this list will be signaled and the
* entry will be freed. If there are multiple waiters then only the highest
* priority thread will get the IOB. Lower priority threads will need to
* call iob_notify_setup() once again.
*/ */
static struct iob_notify_s g_iob_notify[CONFIG_IOB_NWAITERS]; static FAR struct iob_notify_s *g_iobnotify_pending;
/* This semaphore is used as mutex to enforce mutually exclusive access to /* This semaphore is used as mutex to enforce mutually exclusive access to
* the g_io_notify[] array. * the IOB notification structures.
*/ */
static sem_t g_notify_sem = SEM_INITIALIZER(1); static sem_t g_iobnotify_sem;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: iob_notify_alloc
*
* Description:
* Allocate a notification structure by removing it from the free list.
*
****************************************************************************/
static FAR struct iob_notify_s *iob_notify_alloc(void)
{
FAR struct iob_notify_s *notify;
notify = g_iobnotify_free;
if (notify != NULL)
{
g_iobnotify_free = notify->flink;
notify->flink = NULL;
}
return notify;
}
/****************************************************************************
* Name: iob_notify_free
*
* Description:
* Free a notification structure by returning it to the head of the free
* list.
*
****************************************************************************/
static void iob_notify_free(FAR struct iob_notify_s *notify)
{
notify->flink = g_iobnotify_free;
g_iobnotify_free = notify;
}
/**************************************************************************** /****************************************************************************
* Public Functions * Public Functions
****************************************************************************/ ****************************************************************************/
/****************************************************************************
* Name: iob_notify_initialize
*
* Description:
* Set up the notification structure for normal operation.
*
****************************************************************************/
void iob_notify_initialize(void)
{
int i;
/* Add each notification structure to the free list */
for (i = 0; i < CONFIG_IOB_NWAITERS; i++)
{
FAR struct iob_notify_s *notify = &g_iobnotify_pool[i];
/* Add the pre-allocated notification to the head of the free list */
notify->flink = g_iobnotify_free;
g_iobnotify_free = notify;
}
/* Initialize the semaphore that enforces mutually exclusive access to the
* notification data structures.
*/
nxsem_init(&g_iobnotify_sem, 0, 1);
}
/**************************************************************************** /****************************************************************************
* Name: iob_notify_setup * Name: iob_notify_setup
* *
@ -124,7 +204,6 @@ static sem_t g_notify_sem = SEM_INITIALIZER(1);
int iob_notify_setup(int pid, int signo) int iob_notify_setup(int pid, int signo)
{ {
int ret; int ret;
int i;
/* If the pid is zero, then use the pid of the calling thread */ /* If the pid is zero, then use the pid of the calling thread */
@ -133,9 +212,9 @@ int iob_notify_setup(int pid, int signo)
pid = getpid(); pid = getpid();
} }
/* Get exclusive access to the notifier array */ /* Get exclusive access to the notifier data structures */
ret = nxsem_wait(&g_notify_sem); ret = nxsem_wait(&g_iobnotify_sem);
if (ret < 0) if (ret < 0)
{ {
return ret; return ret;
@ -148,24 +227,29 @@ int iob_notify_setup(int pid, int signo)
ret = iob_navail(); ret = iob_navail();
if (ret <= 0) if (ret <= 0)
{ {
/* Find a free entry in the g_iob_notify[] array */ /* Allocate a new notification */
FAR struct iob_notify_s *notify = iob_notify_alloc();
if (notify == NULL)
{
ret = -ENOSPC; ret = -ENOSPC;
for (i = 0; i < CONFIG_IOB_NWAITERS; i++) }
else
{ {
if (!g_iob_notify[i].waiter) /* Initialize the notification structure */
{
g_iob_notify[i].waiter = true;
g_iob_notify[i].pid = pid;
g_iob_notify[i].signo = signo;
notify->pid = pid;
notify->signo = signo;
/* And add it to the head of the pending list */
notify->flink = g_iobnotify_pending;
g_iobnotify_pending = notify;
ret = OK; ret = OK;
break;
}
} }
} }
(void)nxsem_post(&g_notify_sem); (void)nxsem_post(&g_iobnotify_sem);
return ret; return ret;
} }
@ -190,8 +274,9 @@ int iob_notify_setup(int pid, int signo)
int iob_notify_teardown(int pid, int signo) int iob_notify_teardown(int pid, int signo)
{ {
FAR struct iob_notify_s *notify;
FAR struct iob_notify_s *prev;
int ret; int ret;
int i;
/* If the pid is zero, then use the pid of the calling thread */ /* If the pid is zero, then use the pid of the calling thread */
@ -200,30 +285,48 @@ int iob_notify_teardown(int pid, int signo)
pid = getpid(); pid = getpid();
} }
/* Get exclusive access to the notifier array */ /* Get exclusive access to the notifier data structures */
ret = nxsem_wait(&g_notify_sem); ret = nxsem_wait(&g_iobnotify_sem);
if (ret < 0) if (ret < 0)
{ {
return ret; return ret;
} }
/* Find the entry matching this PID in the g_iob_notify[] array. We /* Find the entry matching this PID in the g_iobnotify_pending list. We
* assume that there is only one. * assume that there is only one.
*/ */
ret = -ENOENT; ret = -ENOENT;
for (i = 0; i < CONFIG_IOB_NWAITERS; i++) for (prev = NULL, notify = g_iobnotify_pending;
notify != NULL;
prev = notify, notify = notify->flink)
{ {
if (g_iob_notify[i].waiter && g_iob_notify[i].pid == pid) /* Is this the one we were looking for? */
if (notify->pid == pid)
{ {
g_iob_notify[i].waiter = false; /* Yes, remove it from the pending list */
if (prev == NULL)
{
g_iobnotify_pending = notify->flink;
}
else
{
prev->flink = notify->flink;
}
/* And add it to the free list */
iob_notify_free(notify);
ret = OK; ret = OK;
break; break;
} }
} }
(void)nxsem_post(&g_notify_sem); (void)nxsem_post(&g_iobnotify_sem);
return ret; return ret;
} }
@ -249,12 +352,12 @@ int iob_notify_teardown(int pid, int signo)
void iob_notify_signal(void) void iob_notify_signal(void)
{ {
FAR struct iob_notify_s *notify;
int ret; int ret;
int i;
/* Get exclusive access to the notifier array */ /* Get exclusive access to the notifier data structure */
ret = nxsem_wait(&g_notify_sem); ret = nxsem_wait(&g_iobnotify_sem);
while (ret < 0) while (ret < 0)
{ {
DEBUGASSERT(ret == -EINTR || ret == -ECANCELED); DEBUGASSERT(ret == -EINTR || ret == -ECANCELED);
@ -266,23 +369,26 @@ void iob_notify_signal(void)
sched_lock(); sched_lock();
/* Find the entry matching this PID in the g_iob_notify[] array. We /* Process the notification at the head of the pending list until the
* assume that there is only one. * pending list is empty */
*/
for (i = 0; i < CONFIG_IOB_NWAITERS; i++) while ((notify = g_iobnotify_pending) != NULL)
{ {
if (g_iob_notify[i].waiter) /* Remove the notification from the pending list */
{
/* Signal the waiter and free the entry */
(void)nxsig_kill(g_iob_notify[i].pid, g_iob_notify[i].signo); g_iobnotify_pending = notify->flink;
g_iob_notify[i].waiter = false;
} /* Signal the waiter */
(void)nxsig_kill(notify->pid, notify->signo);
/* Free the notification by returning it to the free list */
iob_notify_free(notify);
} }
sched_unlock(); sched_unlock();
(void)nxsem_post(&g_notify_sem); (void)nxsem_post(&g_iobnotify_sem);
} }
#endif /* CONFIG_IOB_NOTIFIER */ #endif /* CONFIG_IOB_NOTIFIER */