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);
/****************************************************************************
* 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
*

View File

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

View File

@ -63,34 +63,114 @@
struct iob_notify_s
{
bool waiter; /* True if the waiter information is valid */
uint8_t signo; /* The signal number to use when notifying the PID */
pid_t pid; /* The PID to be notified when an IOB is available */
FAR struct iob_notify_s *flink; /* Supports a singly linked list */
uint8_t signo; /* The signal number for notification */
pid_t pid; /* The PID to be notified */
};
/****************************************************************************
* Private Data
****************************************************************************/
/* This is a an array of threads waiting for an IOB. When an IOB becomes
* 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
* the highest priority thread will get the IOB. Lower priority threads
* will need to call iob_notify once again.
/* This is a statically allocated pool of notification structures */
static struct iob_notify_s g_iobnotify_pool[CONFIG_IOB_NWAITERS];
/* 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
* 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
****************************************************************************/
/****************************************************************************
* 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
*
@ -124,7 +204,6 @@ static sem_t g_notify_sem = SEM_INITIALIZER(1);
int iob_notify_setup(int pid, int signo)
{
int ret;
int i;
/* 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();
}
/* 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)
{
return ret;
@ -148,24 +227,29 @@ int iob_notify_setup(int pid, int signo)
ret = iob_navail();
if (ret <= 0)
{
/* Find a free entry in the g_iob_notify[] array */
/* Allocate a new notification */
ret = -ENOSPC;
for (i = 0; i < CONFIG_IOB_NWAITERS; i++)
FAR struct iob_notify_s *notify = iob_notify_alloc();
if (notify == NULL)
{
if (!g_iob_notify[i].waiter)
{
g_iob_notify[i].waiter = true;
g_iob_notify[i].pid = pid;
g_iob_notify[i].signo = signo;
ret = -ENOSPC;
}
else
{
/* Initialize the notification structure */
ret = OK;
break;
}
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;
}
}
(void)nxsem_post(&g_notify_sem);
(void)nxsem_post(&g_iobnotify_sem);
return ret;
}
@ -190,8 +274,9 @@ int iob_notify_setup(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 i;
/* 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();
}
/* 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)
{
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.
*/
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;
break;
}
}
(void)nxsem_post(&g_notify_sem);
(void)nxsem_post(&g_iobnotify_sem);
return ret;
}
@ -249,12 +352,12 @@ int iob_notify_teardown(int pid, int signo)
void iob_notify_signal(void)
{
FAR struct iob_notify_s *notify;
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)
{
DEBUGASSERT(ret == -EINTR || ret == -ECANCELED);
@ -266,23 +369,26 @@ void iob_notify_signal(void)
sched_lock();
/* Find the entry matching this PID in the g_iob_notify[] array. We
* assume that there is only one.
*/
/* Process the notification at the head of the pending list until the
* pending list is empty */
for (i = 0; i < CONFIG_IOB_NWAITERS; i++)
while ((notify = g_iobnotify_pending) != NULL)
{
if (g_iob_notify[i].waiter)
{
/* Signal the waiter and free the entry */
/* Remove the notification from the pending list */
(void)nxsig_kill(g_iob_notify[i].pid, g_iob_notify[i].signo);
g_iob_notify[i].waiter = false;
}
g_iobnotify_pending = notify->flink;
/* 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();
(void)nxsem_post(&g_notify_sem);
(void)nxsem_post(&g_iobnotify_sem);
}
#endif /* CONFIG_IOB_NOTIFIER */