/* Copyright (C) 2008 Emmanuel Varoquaux
This file is part of XOS.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#include "vfat_lock_struct.h"
#include
#include
#include
#include
#include
#include
/* Du fait de la petite taille du pool de verrous, on utilise un algorithme
* simplifie inspire d'une table de hachage a adressage ouvert : la cle de
* hachage donne simplement la "meilleure" place possible pour un id, et
* constitue le point de depart de la recherche.
*
* Cette fonction est bloquante si l'id n'est pas dans la table et que la table
* est pleine. Elle retourne NULL si l'attente a ete interrompue. */
static struct vfat_lock_struct *get(struct vfat_lock_pool_struct *lock_pool, unsigned long id)
{
int start;
struct vfat_lock_struct *nearest_free;
struct vfat_lock_struct *lck;
/* recherche d'un verrou pour l'identifiant id, et, en parallele, le verrou
libre le plus proche de la cle de hachage */
start = id % lock_pool->lock_table_size;
nearest_free = NULL;
lck = &lock_pool->lock_table[start];
do {
if (lck->id == id)
return lck;
if (!lck->count && !nearest_free)
nearest_free = lck;
if (++lck == &lock_pool->lock_table[lock_pool->lock_table_size])
lck = &lock_pool->lock_table[0];
}
while (lck != &lock_pool->lock_table[start]);
/* un verrou pour l'identifiant id n'a pas ete trouve, recherche d'un verrou
libre */
if (!(lck = nearest_free))
do
if (!condition_wait_interruptible(&lock_pool->free_condition))
return NULL;
while (!(lck = lock_pool->free));
lck->id = id;
return lck;
}
/* Identique a get() mais declenche une erreur si le verrou n'existe pas. */
static struct vfat_lock_struct *get_const(const struct vfat_lock_pool_struct *lock_pool, unsigned long id)
{
int start;
struct vfat_lock_struct *lck;
/* recherche d'un verrou pour l'identifiant id */
start = id % lock_pool->lock_table_size;
lck = &lock_pool->lock_table[start];
do {
if (lck->id == id)
return lck;
if (++lck == &lock_pool->lock_table[lock_pool->lock_table_size])
lck = &lock_pool->lock_table[0];
}
while (lck != &lock_pool->lock_table[start]);
panic("Lock not found!");
}
static void del(struct vfat_lock_pool_struct *lock_pool, struct vfat_lock_struct *lock)
{
lock_pool->free = lock;
condition_signal(&lock_pool->free_condition);
}
int vfat_lock_pool_init(struct vfat_lock_pool_struct *lock_pool, unsigned int size)
{
unsigned int i;
if (!(lock_pool->lock_table = kmalloc(size * sizeof (struct vfat_lock_struct))))
return -ENOMEM;
lock_pool->lock_table_size = size;
for (i = 0; i < lock_pool->lock_table_size; i++)
lock_pool->lock_table[i].count = 0;
condition_init(&lock_pool->free_condition);
return 0;
}
void vfat_lock_pool_destroy(struct vfat_lock_pool_struct *lock_pool)
{
kfree(lock_pool->lock_table, lock_pool->lock_table_size * sizeof (struct vfat_lock_struct));
}
int vfat_lock(struct vfat_lock_pool_struct *lock_pool, unsigned long id, int request)
{
struct vfat_lock_struct *lck;
if (!(lck = get(lock_pool, id)))
return 0;
lck->count++;
switch (request) {
case lr_read:
if (!rw_lock_lock_read(&lck->rw_lock))
goto interrupted;
break;
case lr_write:
if (!rw_lock_lock_write(&lck->rw_lock))
goto interrupted;
break;
}
lck->request = request;
return 1;
interrupted:
if (!--lck->count)
del(lock_pool, lck);
return 0;
}
void vfat_unlock(struct vfat_lock_pool_struct *lock_pool, unsigned long id)
{
struct vfat_lock_struct *lck;
lck = get_const(lock_pool, id);
switch (lck->request) {
case lr_read:
rw_lock_unlock_read(&lck->rw_lock);
break;
case lr_write:
rw_lock_unlock_write(&lck->rw_lock);
break;
}
if (!--lck->count)
del(lock_pool, lck);
}