xv6 has spinlock.c file for creating spinlock for kernel usage. But I need to implement spinlock APIs to be used at user level. For example I will implement, sp_create() to create a spinlock at user level. Or call sp_acquire(int id) to get the lock, etc. To do that I am supposed to create system calls and place the actual implementation in kernel. xv6 has spinlock functionality but only to be used at kernel level.
I thought about creating system calls which are actually calling the corresponding functions in spinlock.c to create the lock, acquire it, release it, etc. But it does not work due to some issues with interrupt disabling.
I am copying below the code I wrote so far:
//system call for lock_take():
int l_take(int lockid) {
struct proc *curproc = myproc();
//process will take lock
..
acquire(&LL.arraylockList[lockid].spnLock);
..
return 0;
}
Problem I am hitting here is that it gives me error about panic: sched locks I think it is because acquire() code has pushcli() in it.
void
acquire(struct spinlock *lk)
{
pushcli(); // disable interrupts to avoid deadlock.
if (holding(lk)) panic("acquire");
// The xchg is atomic.
while (xchg(&lk->locked, 1) != 0)
;
// Tell the C compiler and the processor to not move loads or stores
// past this point, to ensure that the critical section's memory
// references happen after the lock is acquired.
__sync_synchronize();
// Record info about lock acquisition for debugging.
lk->cpu = mycpu();
getcallerpcs(&lk, lk->pcs);
}
Then I copied the code to a new function acquire2() and used it in my system call, where pushcli() is commented out:
acquire2(struct spinlock *lk)
{
// pushcli(); // disable interrupts to avoid deadlock.
if (holding(lk)) panic("acquire");
// The xchg is atomic.
while (xchg(&lk->locked, 1) != 0) {
;
}
// Tell the C compiler and the processor to not move loads or stores
// past this point, to ensure that the critical section's memory
// references happen after the lock is acquired.
__sync_synchronize();
// Record info about lock acquisition for debugging.
lk->cpu = mycpu();
getcallerpcs(&lk, lk->pcs);
}
However, then the error message changes to this: panic: mycpu() called with interrupts enabled
It turns out to be disabling interrupts are not allowed. So, pushcli() and popcli() should not be used. Then I need to figure out how to run mycpu() atomically. Its implementation is like this:
// Must be called with interrupts disabled to avoid the caller being rescheduled
// between reading lapicid and running through the loop.
struct cpu *
mycpu(void)
{
int apicid, i;
if (readeflags() & FL_IF) panic("mycpu called with interrupts enabled\n");
apicid = lapicid();
// APIC IDs are not guaranteed to be contiguous. Maybe we should have
// a reverse map, or reserve a register to store &cpus[i].
for (i = 0; i < ncpu; ++i) {
if (cpus[i].apicid == apicid) return &cpus[i];
}
panic("unknown apicid\n");
}
The for loop and the line above it need to execute atomically. How do I do it?