Atomicidad:
Por defecto un CPU lee los datos de forma volatil, aun que éste término cobra algunos significados
hoy les hablare el más importante en el tema "drivers" ya que, si no existieses las variables atomicas
en sistemas NUMA(o en cualquier sistema de multiprocesamiento) todo sería un desastre,
no habria sincronicidad, los datos al ejecutarse en varios procesadores podrían acceder a esa variable
al mismo tiempo lo que llevaria a un caos en el kernel.

Y aun que existen otras formas de bloqueo a nivel hardware como funciónes diferibles, barreras de memoria,
semaforos, spinlocks, etc.
Hay ocaciones que necesitamos un bloqueo rapido y simple para un determinado procedimiento.
Ej:
atomic_t ato_var;
atomic_set(&ato_var, 0x15);
__asm__("lock decl -0x4(%ebp)");
Esto garantiza la atomicidad en el kernel no permitiendo interrupciones en el bus de memoria

Tasklets schedule o cronómetros del kernel:
Los tasklets(que son datos atomicos) permiten ejecutar una función, en un determinado tiempo
pero éste no es definido por el programador, si no por el kernel y es muy útil en contextos de IRQ's
ya que la operación no se vera interrumpida hasta la finalizacion del scheduler.

Por eso se me ocurrio programar esta funcion, que ademas de bloquear los datos atomicamente, configura
automáticamente un programador de tareas del kernel(tambien atomicamente), ejecutando otra función
con la diferencia que garantiza que no habrá problemas de prograpagación en el bus de memoria.

#if defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS)
   #include <linux/modversions.h>
   #define MODVERSIONS
#endif
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h> // copy_from / to_user
#include <asm/cputime.h>
#include <asm/bitops.h>
#include <linux/interrupt.h>

static struct tasklet_struct *task;
static unsigned int cont;
static unsigned long data;

static uint32_t tl_atomic_secure_recall(struct tasklet_struct *_ktask)
{
  atomic_t retn;
  if (_ktask){
    if (!test_and_set_bit(TASKLET_STATE_SCHED, &(*_ktask).state)){ // seteo y compruebo el valor de state
      __tasklet_schedule(_ktask);
      tasklet_kill(_ktask);
    }
    asm volatile(LOCK_PREFIX "andb %1,%0"
	: CONST_MASK_ADDR(0x0, &(*_ktask).state)
	: "iq" ((u8)~CONST_MASK(0x0)) ); // limpiamos el bit de la memoria
    
    atomic_set(&retn, TASKLET_STATE_RUN);
  }
  else {
    atomic_set(&retn, ENOMEM);
  }
  
  return atomic_read(&retn);
}
static uint32_t tl_atomic_secure_init(struct tasklet_struct *_ktask,
				  void(*callback_tasklet_function))
{
  atomic_t retn;
  if (_ktask){
    tasklet_init(_ktask, callback_tasklet_function, 0x0);
    tl_atomic_secure_recall(_ktask);
    atomic_set(&retn, TASKLET_STATE_RUN); // exito retorna 1
  }
  else {
    atomic_set(&retn, ENOMEM); // fuera de memoria 12
  }
  return atomic_read(&retn);
}

static void func_a_rellamar(unsigned long data)
{
  cont++;
  printk("\nfuncion llamada desde un tasklet seguro, veces %d", cont);
}

static int __section(.init.text) __cold notrace
 INIT_KERNEL_DRIVER(void)
{
  task= kmalloc(sizeof(struct tasklet_struct), GFP_KERNEL);
  tl_atomic_secure_init(task, func_a_rellamar);
  tl_atomic_secure_recall(task);
  tl_atomic_secure_recall(task);
  
  return 0;
}

static void __section(.exit.data)
 EXIT_KERNEL_DRIVER(void)
{
  if (task) kfree(task);
}

module_init(INIT_KERNEL_DRIVER);
module_exit(EXIT_KERNEL_DRIVER);
Aca el resultado:
Imagen
Responder

Volver a “Fuentes”