/* 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); }