Subsecciones

- 6.12.1 rwsem.c
- 6.12.2 rwsem-spinlock.c

# 6.12 `./lib/*`

## 6.12.1 rwsem.c

Este fichero contiene las funciones para la gestión de los bloqueos de los semáforos de lectura y/o escritura -contention handling functions for R/W semaphores-.

Una de las estructuras pasadas por parámetro es `sem` de tipo `rw_semaphore`. Esta estructura se define en Linux dentro del fighero ./include/asm-i386, así

    struct rw_semaphore {
            signed long             count;
    #define RWSEM_UNLOCKED_VALUE            0x00000000
    #define RWSEM_ACTIVE_BIAS               0x00000001
    #define RWSEM_ACTIVE_MASK               0x0000ffff
    #define RWSEM_WAITING_BIAS              (-0x00010000)
    #define RWSEM_ACTIVE_READ_BIAS          RWSEM_ACTIVE_BIAS
    #define RWSEM_ACTIVE_WRITE_BIAS         (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
            spinlock_t              wait_lock;
            struct list_head        wait_list;
    #if RWSEM_DEBUG /* to be removed shortly */
            int                     debug;
    #endif
    };

Otra de las estructuras pasadas por parámetro es `rwsem_waiter`, donde se incluyen los campos necesarios para identificar al semáforo ya sea de lectura o escritura, el proceso que lo invoca y unos flags para opciones de control. Véase su implementación:

    struct rwsem_waiter {
            struct list_head        list;
            struct task_struct      *task;
            unsigned int            flags;
    #define RWSEM_WAITING_FOR_READ  0x00000001
    #define RWSEM_WAITING_FOR_WRITE 0x00000002
    };

Las modificaciones que realiza openMosix sobre el fichero rwsem.c se centran en un pequeño cambio a la funcion `rw_semaphore *rwsem_down_failed_common()`. Esta función espera para que se conceda el bloqueo de un semaforo6.5. Se añade una línia que invoca a la función
`adjust_task_mosix_context()`. Esta función está definida en ./hpc/service.c e implementada como sigue:

    void
    adjust_task_mosix_context(struct task_struct **tp)
    {
            struct task_struct *t = *tp;

            if(t->mosix.dflags & DINSCHED)
                    *tp = MOSIX_CONTEXT(t);
    }

Esta implementación consigue modificar el `task_struct` que identifica al proceso en curso para que sea compatible con el entorno de operación de openMosix. MOSIX_CONTEXT se define en ./include/linux/wait.h de forma que aplica al `task_struct` pasado como parámetro la máscara KERNEL_ADDRESS_BIT de la manera que sigue:

    #define KERNEL_ADDRESS_BIT      0x80000000
    #define MOSIX_CONTEXT(p)        ((struct task_struct *)\
                                    (((unsigned long) (p)) & ~KERNEL_ADDRESS_BIT))

Véase la función:

`static inline struct rw_semaphore *rwsem_down_failed_common()`

    static inline struct rw_semaphore *rwsem_down_failed_common(struct rw_semaphore *sem,
                                                                     struct rwsem_waiter *waiter,
                                                                     signed long adjustment)
    {
            struct task_struct *tsk = current;  /* se apunta 'tsk' al proceso en curso */
            signed long count;

            set_task_state(tsk,TASK_UNINTERRUPTIBLE);
                            /* se marca al proceso como ininterrumpible */
            spin_lock(&sem->wait_lock);   /* se pone la peticion en la lista de espera   */
            waiter->task = tsk;
    #ifdef CONFIG_MOSIX
            adjust_task_mosix_context(&waiter->task);   /* se aplica la modificaion del contexto */
    #endif /* CONFIG_MOSIX */

            list_add_tail(&waiter->list,&sem->wait_list);

            /* note that we're now waiting on the lock, but no longer actively read-locking */
            count = rwsem_atomic_update(adjustment,sem);

            /* if there are no longer active locks, wake the front queued process(es) up
             * - it might even be this process, since the waker takes a more active part
             */
            if (!(count & RWSEM_ACTIVE_MASK))
                    sem = __rwsem_do_wake(sem);

            spin_unlock(&sem->wait_lock);

            for (;;) { /*bucle a la espera de la concesion de bloqueo */
                    if (!waiter->flags)
                            break;
                    schedule();
                    set_task_state(tsk, TASK_UNINTERRUPTIBLE);
            }

            tsk->state = TASK_RUNNING;

            return sem;
    }

## 6.12.2 rwsem-spinlock.c

Este fichero sigue implementando handlers como los del fichero anterior, pero para bloqueos implementados con las funciones spinlock.

Las modificaciones se realizan en la función encargada de controlar el bloqueo exclusivo de escritura sobre el recurso que solicite el proceso. La implementación es muy parecida a la comentada anteriormente. Véase:

`void __down_write()`

    void __down_write(struct rw_semaphore *sem)
    {
            struct rwsem_waiter waiter;
            struct task_struct *tsk;

            rwsemtrace(sem,"Entering __down_write");

            spin_lock(&sem->wait_lock);

            if (sem->activity==0 && list_empty(&sem->wait_list)) {
                    /* bloqueo concedido */
                    sem->activity = -1;
                    spin_unlock(&sem->wait_lock);
                    goto out;
            }

            tsk = current;
            set_task_state(tsk,TASK_UNINTERRUPTIBLE);

            waiter.task = tsk;
    #ifdef CONFIG_MOSIX
            adjust_task_mosix_context(&waiter.task);
    #endif /* CONFIG_MOSIX */
            waiter.flags = RWSEM_WAITING_FOR_WRITE;

            list_add_tail(&waiter.list,&sem->wait_list);

            spin_unlock(&sem->wait_lock);

            for (;;) {
                    if (!waiter.flags)
                            break;
                    schedule();
                    set_task_state(tsk, TASK_UNINTERRUPTIBLE);
            }

            tsk->state = TASK_RUNNING;

     out:
            rwsemtrace(sem,"Leaving __down_write");
    }