#include "vfat_lock_struct.h"
#include <panic.h>
#include <rw_lock.h>
#include <condition.h>
#include <kmalloc.h>
#include <errno.h>
#include <stddef.h>
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;
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]);
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;
}
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;
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);
}