Как да разтоварите модул на ядрото, който е създал kproc/kthread във FreeBSD

Искам да разтоваря модул, който има нишки. Позовах се на кодовете в dev/random и кодът ми е като този:

$ cat tmp.c 

#include <sys/param.h>
#include <sys/module.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/kthread.h>

/* 
 * $ ps auxH | grep kproc 
 */ 

static int kproc_control = 1; 

#define output_id(p, td, fmt, args...)                                  \ 
        printf("%s[%d]:%s[%d]:[%s] %s\n", p->p_comm, p->p_pid,          \ 
            td->td_name, td->td_tid, __func__, msg) 

static void thread_routine(void *arg) 
{ 
        char *msg = arg; 
        struct thread *td = curthread; 
        struct proc *p = td->td_proc; 

        output_id(p, td, msg); 
        pause("-", hz * 100); 
        output_id(p, td, msg); 

        kthread_exit(); 
} 

static void proc_routine(void *arg) 
{ 
        char *msg = arg; 
        struct thread *td = curthread; 
        struct proc *p = td->td_proc; 
        struct thread *ntd; 
        int error; 

        output_id(p, td, msg); 

        error = kthread_add(thread_routine, "I'm kthread", p, &ntd, 
            0, 0, "kthread"); 
        if (error) 
                printf("error: %d\n", error); 

        while (kproc_control >= 0) { 
                pause("-", hz / 10); 
        } 

        wakeup(&kproc_control); 
        kproc_exit(0); 
} 

static int foobar_init(void) 
{ 
        int error; 
        struct proc *p; 

        error = kproc_create(proc_routine, "I'm kproc", &p, 0, 0, "kproc"); 
        uprintf("error: %d\n", error); 

        return error; 
} 

static void foobar_fini(void) 
{ 
        kproc_control = -1; 
        tsleep(&kproc_control, 0, "term", 0); 
        //pause("delay", 2 * hz); 
} 

static int 
foobar_modevent(module_t mod __unused, int event, void *arg __unused) 
{ 
        int error = 0; 

        switch (event) { 
        case MOD_LOAD: 
                error = foobar_init(); 
                break; 
        case MOD_UNLOAD: 
                foobar_fini(); 
                break; 
        default: 
                error = EOPNOTSUPP; 
                break; 
        } 

        return (error); 
} 

static moduledata_t foobar_mod = { 
        "foobar", 
        foobar_modevent, 
        NULL 
}; 

DECLARE_MODULE(foobar, foobar_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 

Когато го разтоваря от kldunload, ядрото ми се срива и системата се рестартира. Какъв е правилният начин за решаване на този проблем? Всякакви коментари ще бъдат оценени! ;-)

PS. Мога ли да спя на &p->p_stype? Виждам следните кодове в exit1():

    /*
     * Note that we are exiting and do another wakeup of anyone in
     * PIOCWAIT in case they aren't listening for S_EXIT stops or
     * decided to wait again after we told them we are exiting.
     */
    p->p_flag |= P_WEXIT;
    wakeup(&p->p_stype);

PS. Актуализирани кодове:

#include <sys/param.h>
#include <sys/module.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/kthread.h>
#include <sys/lock.h>
#include <sys/mutex.h>

/*
 * $ ps auxH | grep kproc
 */

static int kproc_control = 1;
static struct proc *foobar_proc;
static struct mtx mtx;

#define output_id(p, td, fmt, args...)                                  \
        printf("%s[%d]:%s[%d]:[%s] %s\n", p->p_comm, p->p_pid,          \
            td->td_name, td->td_tid, __func__, msg)

static void thread_routine(void *arg)
{
        char *msg = arg;
        struct thread *td = curthread;
        struct proc *p = td->td_proc;

        output_id(p, td, msg);
        pause("-", hz * 100);
        output_id(p, td, msg);

        kthread_exit();
}

static void proc_routine(void *arg)
{
        char *msg = arg;
        struct thread *td = curthread;
        struct proc *p = td->td_proc;
        struct thread *ntd;
        int error;

        output_id(p, td, msg);

        error = kthread_add(thread_routine, "I'm kthread", p, &ntd,
            0, 0, "kthread");
        if (error)
                printf("error: %d\n", error);

        mtx_lock(&mtx);
        while (kproc_control >= 0) {
                mtx_unlock(&mtx);
                pause("-", hz / 10);
                mtx_lock(&mtx);
        }
        mtx_unlock(&mtx);
        kproc_exit(0);
}
static int foobar_init(void)
{
        int error;

        mtx_init(&mtx, "foobar_mtx", NULL, MTX_DEF);
        error = kproc_create(proc_routine, "I'm kproc", &foobar_proc, 0, 0, "kproc");
        uprintf("error: %d\n", error);

        return error;
}

static void foobar_fini(void)
{
        mtx_lock(&mtx);
        kproc_control = -1;
        //mtx_sleep(foobar_proc, &mtx, 0, "waiting", 0);
        mtx_sleep(&foobar_proc->p_stype, &mtx, 0, "waiting", 0);
}

static int
foobar_modevent(module_t mod __unused, int event, void *arg __unused)
{
        int error = 0;

        switch (event) {
        case MOD_LOAD:
                error = foobar_init();
                break;
        case MOD_UNLOAD:
                foobar_fini();
                break;
        default:
                error = EOPNOTSUPP;
                break;
        }

        return (error);
}

static moduledata_t foobar_mod = {
        "foobar",
        foobar_modevent,
        NULL
};

DECLARE_MODULE(foobar, foobar_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);

person user1907234    schedule 16.12.2012    source източник


Отговори (1)


По принцип изчаквате нишката или манипулатора на proc, за да се уверите, че е излязъл.

Трябва да използвате някакъв вид заключване, за да синхронизирате достъпа до споделени променливи като вашия флаг kproc_control. След това можете също да използвате mtx_sleep за атомно освобождаване на заключването и изчакване на манипулатора на proc, за да избегнете условия на състезание при справяне със събитията за прекратяване.

Моделът, който използвам изглежда така:

void proc(whatver)
{
    mtx_lock(&sc->m_lock);

    while (!sc->time_to_die) {
        mtx_unlock(&sc->m_lock);
        /* Do whatever */
        mtx_lock(&sc->m_lock);
    }

    mtx_unlock(&sc->m_lock);

    kproc_exit(0);
}

void detach(whatever)
{
    mtx_lock(&sc->m_lock);
    sc->time_to_die = 1;
    mtx_sleep(sc->m_proc, &sc->m_lock, 0, "waiting", 0);
    /* proc cleaned up, safe to continue */
}
person janm    schedule 16.12.2012
comment
Благодаря ви много за вашите ценни коментари. Но мисля, че е по-добре да спя на &sc-›m_proc-›p_stype. Тъй като 'wakeup(sc-›m_proc)' се изпълнява в kproc_exit() преди всички други нишки да са излезли, докато 'wakeup(&sc-›m_proc-›p_stype)' се изпълнява в exit1(), когато всички други нишки са излезли. Опитах се да заспя на sc-›m_proc (всъщност mtx_sleep(foobar_proc, &mtx, 0, waiting, 0);), но ядрото отново се срива. Може би се дължи на 'pause(-, hz * 100)' в thread_routine(). Ако обаче използвам mtx_sleep(&foobar_proc-›p_stype, &mtx, 0, waiting, 0), ядрото пак се срива. ;-( - person user1907234; 16.12.2012
comment
Необходимо ли е да добавите 'mtx_unlock(&sc-›m_lock);' преди 'kproc_exit(0);' в 'void proc(whatver) {}'? - person user1907234; 16.12.2012
comment
Трябва да изчакате всичко, което сте започнали. Във вашия случай мисля, че сте извикали kthread_add() и kproc_create(). Трябва да изчакате и двете да излязат. Не съм разглеждал подробно другия ви код, но зависиш от таймери, вместо да имаш правилен механизъм за синхронизиране. - person janm; 16.12.2012