View Single Post
Old 07-01-2005, 14:09   #9
ilsensine
Senior Member
 
L'Avatar di ilsensine
 
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
Tecniche di locking: semafori

Esistono diverse tecniche di locking a disposizione degli sviluppatori; le principali sono gli spinlock e i semafori. Questi ultimi possono essere usati come mutex.
L'esempio seguente aggiunge al driver precedente il locking per-file utilizzando i semafori:
Codice:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>

static char *buf;
static int bsize = PAGE_SIZE;

struct kmisc_data {
	struct semaphore ksem;
};

static int kmisc_open(struct inode *ino, struct file *filp)
{
	struct kmisc_data *kdata = kmalloc(sizeof(*kdata), GFP_KERNEL);
	if (!kdata)
		return -ENOMEM;
	init_MUTEX(&kdata->ksem);
	filp->private_data = kdata;
	return 0;
}

static int kmisc_release(struct inode *ino, struct file *filp)
{
	kfree(filp->private_data);
	return 0;
}

ssize_t kmisc_read(struct file *filp, char __user *dst, size_t count, loff_t *off)
{
	ssize_t ret = 0;
	struct kmisc_data *kdata = filp->private_data;
	if (filp->f_flags & O_NONBLOCK) {
		if (down_trylock(&kdata->ksem))
			return -EAGAIN;
	} else {
		if (down_interruptible(&kdata->ksem))
			return -EINTR;
	}
	printk(KERN_INFO "%s: reading up to %d bytes at offset %lld\n", __func__,
		count, *off);
	if ((*off+count)>bsize)
		count = bsize-*off;
	if (count) {
		if (copy_to_user(dst, buf+*off, count))
			ret = -EFAULT;
		else {
			ret = count;
			*off += count;
		}
	}
	up(&kdata->ksem);
	return ret;
}

ssize_t kmisc_write(struct file *filp, const char __user *src, size_t count, loff_t *off)
{
	ssize_t ret = 0;
	struct kmisc_data *kdata = filp->private_data;
	if (filp->f_flags & O_NONBLOCK) {
		if (down_trylock(&kdata->ksem))
			return -EAGAIN;
	} else {
		if (down_interruptible(&kdata->ksem))
			return -EINTR;
	}
	if ((*off+count)>bsize) {
		printk(KERN_WARNING "%s: discarding %lld bytes on %d\n", __func__,
			(*off+count)-bsize, count);
		count = bsize-*off;
	}
	printk(KERN_INFO "%s: writing %d bytes at offset %lld\n", __func__,
		count, *off);
	if (count) {
		if (copy_from_user(buf+*off, src, count))
			ret = -EFAULT;
		else {
			ret = count;
			*off += count;
		}
	}
	up(&kdata->ksem);
	return ret;
}

static struct file_operations kops = {
	.owner		= THIS_MODULE,
	.open		= kmisc_open,
	.release	= kmisc_release,
	.read		= kmisc_read,
	.write		= kmisc_write,
};

static struct miscdevice kmisc = {
	.minor 	= MISC_DYNAMIC_MINOR,
	.name 	= "kmisc",
	.fops	= &kops
};

static int __init kmisc_init(void)
{
	int ret;
	buf = kzalloc(bsize, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;
	ret = misc_register(&kmisc);
	if (!ret)
		printk(KERN_INFO "kmisc registered on minor %d\n", kmisc.minor);
	else
		kfree(buf);
	return ret;
}

static void __exit kmisc_exit(void)
{
	misc_deregister(&kmisc);
	kfree(buf);
}


module_init(kmisc_init);
module_exit(kmisc_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ilsensine");
MODULE_DESCRIPTION("miscdevice module");
Da notare che il locking non viene fatto tramite down(), ma tramite alcune varianti. Innanzitutto se il file è stato aperto come non bloccante (O_NONBLOCK), dobbiamo onorare questa richiesta: dobbiamo quindi _tentare_ il lock con down_trylock e ritornare -EAGAIN se il nostro codice non è in grado di acquisire il lock senza attendere. Se il file è stato aperto normalmente, è invece preferibile la forma down_interruptible: questa down ritorna anche nel caso che il processo riceva un segnale, consentendo quindi un rapido ritorno in userspace per la gestione dello stesso. L'errore da ritornare in questo caso è il classico -EINTR.
La down() pura invece non ritorna mai, a meno di non aver acquisito il semaforo. Neanche un SIGKILL riuscirà ad uccidere un processo fermo dentro una down().
Notate che tutte le down, tranne la versione trylock, possono bloccare se il semaforo non è acquisibile: quindi non possono essere utilizzate in regioni del codice che non possono essere schedulate (notoriamente irq service routine, dentro uno spinlock, con gli irq disabilitati, ecc). E' legale, per contro, schedulare con un semaforo acquisito (alcuni driver, che non gradiscono multiple open, bloccano un semaforo dentro la open e lo rilasciano nella release).

Notare infine che il semaforo può essere usato anche come...semaforo, invece che come mutex; in questo caso per inizializzarlo usare sema_init (init_MUTEX non fa altro che inizializzare il semaforo a 1).
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al
andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12

Ultima modifica di ilsensine : 23-02-2006 alle 15:48.
ilsensine è offline