Changeset 0d56712 in mainline


Ignore:
Timestamp:
2012-07-07T00:27:01Z (12 years ago)
Author:
Adam Hraska <adam.hraska+hos@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
ff90f5f
Parents:
8a64e81e
Message:

workq: Added magic cookie integrity checks. Fixed test workqueue1.

Location:
kernel
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • kernel/generic/include/synch/workqueue.h

    r8a64e81e r0d56712  
    99struct work_item;
    1010struct work_queue;
     11typedef struct work_queue work_queue_t;
    1112
    1213typedef void (*work_func_t)(struct work_item *);
     
    1516        link_t queue_link;
    1617        work_func_t func;
     18       
     19#ifdef CONFIG_DEBUG
     20        /* Magic number for integrity checks. */
     21        uint32_t cookie;
     22#endif
    1723} work_t;
    1824
  • kernel/generic/src/synch/workqueue.c

    r8a64e81e r0d56712  
    99#include <macros.h>
    1010
     11#define WORKQ_MAGIC      0xf00c1333U
     12#define WORK_ITEM_MAGIC  0xfeec1777U
     13
    1114
    1215struct work_queue {
     
    4649       
    4750        link_t nb_link;
     51       
     52#ifdef CONFIG_DEBUG
     53        /* Magic cookie for integrity checks. Immutable. Accessed without lock. */
     54        uint32_t cookie;
     55#endif
    4856};
    4957
     
    7987
    8088
    81 
    8289/* Fwd decl. */
    8390static void workq_preinit(struct work_queue *workq, const char *name);
     
    8794static int _workq_enqueue(struct work_queue *workq, work_t *work_item,
    8895        work_func_t func, bool can_block);
     96static void init_work_item(work_t *work_item, work_func_t func);
    8997static signal_op_t signal_worker_logic(struct work_queue *workq, bool can_block);
    9098static void worker_thread(void *arg);
     
    93101static void cv_wait(struct work_queue *workq);
    94102static void nonblock_init(void);
     103static bool workq_corrupted(struct work_queue *workq);
     104static bool work_item_corrupted(work_t *work_item);
     105
    95106
    96107/** Creates worker thread for the system-wide worker queue. */
     
    138149        if (workq) {
    139150                if (workq_init(workq, name)) {
     151                        ASSERT(!workq_corrupted(workq));
    140152                        return workq;
    141153                }
     
    150162void workq_destroy(struct work_queue *workq)
    151163{
    152         ASSERT(workq);
     164        ASSERT(!workq_corrupted(workq));
    153165       
    154166        irq_spinlock_lock(&workq->lock, true);
     
    163175        }
    164176       
     177#ifdef CONFIG_DEBUG
     178        workq->cookie = 0;
     179#endif
     180       
    165181        free(workq);
    166182}
     
    169185static void workq_preinit(struct work_queue *workq, const char *name)
    170186{
     187#ifdef CONFIG_DEBUG
     188        workq->cookie = WORKQ_MAGIC;
     189#endif
     190       
    171191        irq_spinlock_initialize(&workq->lock, name);
    172192        condvar_initialize(&workq->activate_worker);
     
    202222static bool add_worker(struct work_queue *workq)
    203223{
     224        ASSERT(!workq_corrupted(workq));
     225
    204226        thread_t *thread = thread_create(worker_thread, workq, TASK,
    205227                THREAD_FLAG_NONE, workq->name);
     
    270292void workq_stop(struct work_queue *workq)
    271293{
     294        ASSERT(!workq_corrupted(workq));
     295       
    272296        interrupt_workers(workq);
    273297        wait_for_workers(workq);
     
    285309        /* Respect lock ordering - do not hold workq->lock during broadcast. */
    286310        irq_spinlock_unlock(&workq->lock, true);
    287        
    288311       
    289312        condvar_broadcast(&workq->activate_worker);
     
    408431        work_func_t func, bool can_block)
    409432{
     433        ASSERT(!workq_corrupted(workq));
     434       
    410435        bool success = true;
    411436        signal_op_t signal_op = NULL;
    412        
    413         link_initialize(&work_item->queue_link);
    414         work_item->func = func;
    415437       
    416438        irq_spinlock_lock(&workq->lock, true);
     
    419441                success = false;
    420442        } else {
     443                init_work_item(work_item, func);
    421444                list_append(&work_item->queue_link, &workq->queue);
    422445                ++workq->item_cnt;
     
    442465}
    443466
     467/** Prepare an item to be added to the work item queue. */
     468static void init_work_item(work_t *work_item, work_func_t func)
     469{
     470#ifdef CONFIG_DEBUG
     471        work_item->cookie = WORK_ITEM_MAGIC;
     472#endif
     473       
     474        link_initialize(&work_item->queue_link);
     475        work_item->func = func;
     476}
     477
    444478/** Returns the number of workers running work func() that are not blocked. */
    445479static size_t active_workers_now(struct work_queue *workq)
     
    497531static void signal_worker_op(struct work_queue *workq)
    498532{
     533        ASSERT(!workq_corrupted(workq));
     534
    499535        condvar_signal(&workq->activate_worker);
    500536       
     
    515551static signal_op_t signal_worker_logic(struct work_queue *workq, bool can_block)
    516552{
     553        ASSERT(!workq_corrupted(workq));
    517554        ASSERT(irq_spinlock_locked(&workq->lock));
    518555       
     
    610647                /* Copy the func field so func() can safely free work_item. */
    611648                work_func_t func = work_item->func;
    612                
     649
    613650                func(work_item);
    614651        }
     
    618655static bool dequeue_work(struct work_queue *workq, work_t **pwork_item)
    619656{
     657        ASSERT(!workq_corrupted(workq));
     658       
    620659        irq_spinlock_lock(&workq->lock, true);
    621660       
     
    646685                link_t *work_link = list_first(&workq->queue);
    647686                *pwork_item = list_get_instance(work_link, work_t, queue_link);
     687               
     688#ifdef CONFIG_DEBUG
     689                ASSERT(!work_item_corrupted(*pwork_item));
     690                (*pwork_item)->cookie = 0;
     691#endif
    648692                list_remove(work_link);
    649693                --workq->item_cnt;
     
    694738                &workq->lock, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE);
    695739
     740        ASSERT(!workq_corrupted(workq));
    696741        ASSERT(irq_spinlock_locked(&workq->lock));
    697742       
     
    711756                /* Must be blocked in user work func() and not be waiting for work. */
    712757                ASSERT(!thread->workq_idling);
     758                ASSERT(thread->state == Sleeping);
    713759                ASSERT(THREAD != thread);
    714                 ASSERT(thread->state == Sleeping);
     760                ASSERT(!workq_corrupted(thread->workq));
    715761               
    716762                /* Protected by thread->lock */
     
    732778        if (THREAD->workq && THREAD->state == Sleeping && !THREAD->workq_idling) {
    733779                ASSERT(!THREAD->workq_blocked);
     780                ASSERT(!workq_corrupted(THREAD->workq));
     781               
    734782                THREAD->workq_blocked = true;
    735783               
     
    765813                (0 < workq->activate_pending) ? "increasing" : "stable";
    766814       
    767        
    768815        irq_spinlock_unlock(&workq->lock, true);
    769816       
    770817        printf(
    771                 "Configured with: max_worker_cnt=%zu, min_worker_cnt=%zu,\n"
    772                 "  max_concurrent_workers=%zu, max_items_per_worker=%zu\n"
     818                "Configuration: max_worker_cnt=%zu, min_worker_cnt=%zu,\n"
     819                " max_concurrent_workers=%zu, max_items_per_worker=%zu\n"
    773820                "Workers: %zu\n"
    774821                "Active:  %zu (workers currently processing work)\n"
     
    813860                *pworkq = list_get_instance(list_first(&info->work_queues),
    814861                        struct work_queue, nb_link);
     862
     863                ASSERT(!workq_corrupted(*pworkq));
    815864               
    816865                list_remove(&(*pworkq)->nb_link);
     
    835884static void nonblock_init(void)
    836885{
    837         irq_spinlock_initialize(&nonblock_adder.lock, "workq:nb.lock");
     886        irq_spinlock_initialize(&nonblock_adder.lock, "kworkq-nb.lock");
    838887        condvar_initialize(&nonblock_adder.req_cv);
    839888        list_initialize(&nonblock_adder.work_queues);
    840889       
    841890        nonblock_adder.thread = thread_create(thr_nonblock_add_worker,
    842                 &nonblock_adder, TASK, THREAD_FLAG_NONE, "workq:nb");
     891                &nonblock_adder, TASK, THREAD_FLAG_NONE, "kworkq-nb");
    843892       
    844893        if (nonblock_adder.thread) {
     
    849898                 * sleep, but at least boot the system.
    850899                 */
    851                 printf("Failed to create workq:nb. Sleeping work may stall the workq.\n");
    852         }
    853 }
    854 
    855 
     900                printf("Failed to create kworkq-nb. Sleeping work may stall the workq.\n");
     901        }
     902}
     903
     904/** Returns true if the workq is definitely corrupted; false if not sure.
     905 *
     906 * Can be used outside of any locks.
     907 */
     908static bool workq_corrupted(struct work_queue *workq)
     909{
     910#ifdef CONFIG_DEBUG
     911        /*
     912         * Needed to make the most current cookie value set by workq_preinit()
     913         * visible even if we access the workq right after it is created but
     914         * on a different cpu. Otherwise, workq_corrupted() would not work
     915         * outside a lock.
     916         */
     917        memory_barrier();
     918        return NULL == workq || workq->cookie != WORKQ_MAGIC;
     919#else
     920        return false;
     921#endif
     922}
     923
     924/** Returns true if the work_item is definitely corrupted; false if not sure.
     925 *
     926 * Must be used with the work queue protecting spinlock locked.
     927 */
     928static bool work_item_corrupted(work_t *work_item)
     929{
     930#ifdef CONFIG_DEBUG
     931        return NULL == work_item || work_item->cookie != WORK_ITEM_MAGIC;
     932#else
     933        return false;
     934#endif
     935}
  • kernel/test/synch/workq-test-core.h

    r8a64e81e r0d56712  
    8282}
    8383
     84static void free_work(test_work_t *work)
     85{
     86        memsetb(work, sizeof(test_work_t), 0xfa);
     87        free(work);
     88}
     89
    8490static void reproduce(work_t *work_item)
    8591{
     
    112118                        if (child) {
    113119                                if (!core_workq_enqueue(&child->work_item, reproduce))
    114                                         free(child);
     120                                        free_work(child);
    115121                        }
    116122                }
    117123               
    118124                if (!core_workq_enqueue(work_item, reproduce)) {
    119                         if (!work->master) {
    120                                 free(work);
    121                         }
     125                        if (work->master)
     126                                TPRINTF("\nErr: Master work item exiting prematurely!\n");
     127
     128                        free_work(work);
    122129                }
    123130        } else {
     
    125132               
    126133                if (work->master && new_wave(work)) {
    127                                 core_workq_enqueue(work_item, reproduce);
     134                        if (!core_workq_enqueue(work_item, reproduce)) {
     135                                TPRINTF("\nErr: Master work could not start a new wave!\n");
     136                                free_work(work);
     137                        }
    128138                } else {
    129139                        if (work->master)
    130                                 TPRINTF("\nMaster work item done!\n");
     140                                TPRINTF("\nMaster work item done.\n");
    131141                               
    132                         memsetb(work, sizeof(test_work_t), 0xfa);
    133                         free(work);
     142                        free_work(work);
    134143                }
    135144        }
     
    180189       
    181190        for (int i = 0; i < WAVES; ++i) {
    182                 if (atomic_get(&call_cnt[i]) != exp_call_cnt) {
     191                if (atomic_get(&call_cnt[i]) == exp_call_cnt) {
     192                        TPRINTF("Ok: %u calls in wave %d, as expected.\n",
     193                                atomic_get(&call_cnt[i]), i);
     194                } else {
    183195                        success = false;
    184196                        TPRINTF("Error: %u calls in wave %d, but %zu expected.\n",
    185197                                atomic_get(&call_cnt[i]), i, exp_call_cnt);
    186                 }
     198                } 
    187199        }
    188200       
  • kernel/test/synch/workqueue1.c

    r8a64e81e r0d56712  
    3636
    3737
    38 int done = 0;
     38static work_t work;
     39static int done = 0;
    3940
    4041static void func(work_t *work_item)
     
    4748const char *test_workqueue1(void)
    4849{
    49         work_t work;
    50 
     50        done = 0;
    5151        workq_global_enqueue(&work, func);
    5252       
     
    5656        }
    5757
    58         printf("done\n");
     58        printf("\ndone\n");
    5959
    6060        return NULL;
Note: See TracChangeset for help on using the changeset viewer.