// This function is called when a /dev/osprdX file is opened. // You aren't likely to need to change this. static int osprd_open(struct inode *inode, struct file *filp) { // Always set the O_SYNC flag. That way, we will get writes immediately // instead of waiting for them to get through write-back caches. osprd_info_t *d = file2osprd(filp); filp->f_flags |= O_SYNC; for_each_open_file(current, count_ramdisks_hook, d); return 0; }
/* * osprd_ioctl(inode, filp, cmd, arg) * Called to perform an ioctl on the named file. */ int osprd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { osprd_info_t *d = file2osprd(filp); // device info int r = 0; // return value: initially 0 unsigned myTicket; // is file open for writing? int filp_writable = (filp->f_mode & FMODE_WRITE) != 0; // This line avoids compiler warnings; you may remove it. (void) filp_writable, (void) d; // Set 'r' to the ioctl's return value: 0 on success, negative on error if (cmd == OSPRDIOCACQUIRE) { // EXERCISE: Lock the ramdisk. // // If *filp is open for writing (filp_writable), then attempt // to write-lock the ramdisk; otherwise attempt to read-lock // the ramdisk. // // This lock request must block using 'd->blockq' until: // 1) no other process holds a write lock; // 2) either the request is for a read lock, or no other process // holds a read lock; and // 3) lock requests should be serviced in order, so no process // that blocked earlier is still blocked waiting for the // lock. // // If a process acquires a lock, mark this fact by setting // 'filp-void checkLocks(struct file *filp, osprd_info_t *d)>f_flags |= F_OSPRD_LOCKED'. You also need to // keep track of how many read and write locks are held: // change the 'osprd_info_t' structure to do this. // // Also wake up processes waiting on 'd->blockq' as needed. // // If the lock request would cause a deadlock, return -EDEADLK. // If the lock request blocks and is awoken by a signal, then // return -ERESTARTSYS. // Otherwise, if we can grant the lock request, return 0. // 'd->ticket_head' and 'd->ticket_tail' should help you // service lock requests in order. These implement a ticket // order: 'ticket_tail' is the next ticket, and 'ticket_head' // is the ticket currently being served. You should set a local // variable to 'd->ticket_head' and increment 'd->ticket_head'. // Then, block at least until 'd->ticket_tail == local_ticket'. // (Some of these operations are in a critical section and must // be protected by a spinlock; which ones?) /* TUAN: Ticket is used to service lock requests in order. Each process maintains a unique local_ticket, starting at ticket_head. To obtain a unique local ticket, each process atomically set its local_ticket equal to ticket_head and then incremenet the ticket_head. Whichever process grasps the lock, it will get the next value of ticket_head and again atomically increments ticket_head. Eventually, we have a list of processes with ticket 0, 1, 2, 3. ticket_tail starts at 0. A process cannot obtain the lock on the RAM disk if its local ticket does not match ticket_tail. Since process 0 has local_ticket equal to ticket_tail which is 0, only process 0 can obtain the lock. Before process 0 releases the lock, it increments ticket_tail by 1 and release the lock. Since now process 1 has its local_ticket equal to ticket_tail which is now 1, it can grasb the lock... */ //requested a WRITE lock if (filp_writable) { //get a ticket osp_spin_lock(&(d->mutex)); myTicket = d->ticket_head; d->ticket_head++; //Check for deadlock - if I have previous read lock and will have to wait if (pidInList(d->readLockingPids, current->pid)) { osp_spin_unlock(&(d->mutex)); return -EDEADLK; } /* TUAN: It is considered deadlock to request the same write lock that you already hold in your current RAM disk. */ if (pidInList(d->writeLockingPids, current->pid)) { osp_spin_unlock(&(d->mutex)); return -EDEADLK; } osp_spin_unlock(&(d->mutex)); /* TUAN: wait_event_interruptible. The first argument is the wait queue. The second argument is the condition to wake up. The process wakes up when the condition is true or a signal is received. The function returns 0 if the condition is true. Return -ERESTARTSYS if a signal is received. */ //block until all conditions are met if (wait_event_interruptible(d->blockq, d->ticket_tail==myTicket && d->writeLockingPids == NULL && d->readLockingPids == NULL)) { //I encountered a signal, return error condition if (d->ticket_tail == myTicket) { grantTicketToNextAliveProcessInOrder(d); //Tuan define this } else { //mark my ticket as not usable before process exits addToTicketList(&(d->exitedTickets), myTicket); //TUAN: this is important because when other process grants the ticket //It makes sure it not grant the ticket to processes that already exited. //It do that by incrementing ticket_tail and make sure ticket_tail not //match the already exited ticket. } return -ERESTARTSYS; //TUAN: means your system call is restartable. The process is considered exited/died. } //if I arrive here, I have the ticket to proceed, and no one else holds a read or write lock osp_spin_lock(&(d->mutex)); //claim the lock officially filp->f_flags |= F_OSPRD_LOCKED; //TUAN: we keep track of the ID processes that are holding the write lock //Later on, to detect deadlock for the current process, we look up this list //to see if we already have the read or write lock there. addToList(&(d->writeLockingPids), current->pid); //TUAN: find next usable ticket number so that the next in-order alive process can use grantTicketToNextAliveProcessInOrder(d); osp_spin_unlock(&(d->mutex)); wake_up_all(&(d->blockq)); //TUAN: wait up all processes in the wait queue d->blockq and evaluate the condition //in wait_event_interruptable for those processes that go to sleep when invoking this function. return 0; } //requested a READ lock else { //get a ticket osp_spin_lock(&(d->mutex)); myTicket = d->ticket_head; d->ticket_head++; //Check for deadlock - if I have previous write lock and will have to wait if (pidInList(d->writeLockingPids, current->pid)) { osp_spin_unlock(&(d->mutex)); return -EDEADLK; } /* TUAN: It is considered deadlock to request the same read lock that you already hold in your current RAM disk. */ if (pidInList(d->readLockingPids, current->pid)) { osp_spin_unlock(&(d->mutex)); return -EDEADLK; } osp_spin_unlock(&(d->mutex)); //block until all conditions are met if (wait_event_interruptible(d->blockq, d->ticket_tail==myTicket && d->writeLockingPids == NULL)) { //I encountered a signal, return error condition if (d->ticket_tail == myTicket) { grantTicketToNextAliveProcessInOrder(d); } else { //add my ticket to non-usable ticket numbers addToTicketList(&(d->exitedTickets), myTicket); } return -ERESTARTSYS; } //if I arrive here, I have the ticket to proceed, and no one else holds a read or write lock osp_spin_lock(&(d->mutex)); //claim the lock officially filp->f_flags |= F_OSPRD_LOCKED; addToList(&(d->readLockingPids), current->pid); //TUAN: find next usable ticket number so that the next in-order alive process can use grantTicketToNextAliveProcessInOrder(d); osp_spin_unlock(&(d->mutex)); wake_up_all(&(d->blockq)); //TUAN: wake up all the processes that are waiting for the ticket //by setting the processes in the run queue to runnable state. return 0; } } else if (cmd == OSPRDIOCTRYACQUIRE) { // EXERCISE: ATTEMPT to lock the ramdisk. // // This is just like OSPRDIOCACQUIRE, except it should never // block. If OSPRDIOCACQUIRE would block or return deadlock, // OSPRDIOCTRYACQUIRE should return -EBUSY. // Otherwise, if we can grant the lock request, return 0. // Your code here (instead of the next two lines). // WRITE lock if (filp_writable) { // Get a ticket osp_spin_lock(&(d->mutex)); myTicket = d->ticket_head; d->ticket_head++; // Check for deadlock if (pidInList(d->readLockingPids, current->pid)) { grantTicketToNextAliveProcessInOrder(d); osp_spin_unlock(&(d->mutex)); return -EBUSY; } for_each_open_file(current, checkLocks, d); if (d->holdOtherLocks) { d->holdOtherLocks = 0; grantTicketToNextAliveProcessInOrder(d); osp_spin_unlock(&(d->mutex)); return -EBUSY; } if (pidInList(d->writeLockingPids, current->pid)) { grantTicketToNextAliveProcessInOrder(d); osp_spin_unlock(&(d->mutex)); return -EINVAL; } osp_spin_unlock(&(d->mutex)); if (d->writeLockingPids != NULL || d->readLockingPids != NULL) { grantTicketToNextAliveProcessInOrder(d); return -EBUSY; } osp_spin_lock(&(d->mutex)); filp->f_flags |= F_OSPRD_LOCKED; addToList(&(d->writeLockingPids), current->pid); grantTicketToNextAliveProcessInOrder(d); osp_spin_unlock(&(d->mutex)); wake_up_all(&(d->blockq)); return 0; } else { osp_spin_lock(&(d->mutex)); myTicket = d->ticket_head; d->ticket_head++; if (pidInList(d->writeLockingPids, current->pid)) { grantTicketToNextAliveProcessInOrder(d); osp_spin_unlock(&(d->mutex)); return -EBUSY; } for_each_open_file(current, checkLocks, d); if (d->holdOtherLocks) { d->holdOtherLocks = 0; grantTicketToNextAliveProcessInOrder(d); osp_spin_unlock(&(d->mutex)); return -EBUSY; } if (pidInList(d->readLockingPids, current->pid)) { grantTicketToNextAliveProcessInOrder(d); osp_spin_unlock(&(d->mutex)); return -EINVAL; } osp_spin_unlock(&(d->mutex)); if (d->writeLockingPids != NULL) { grantTicketToNextAliveProcessInOrder(d); return -EBUSY; } osp_spin_lock(&(d->mutex)); filp->f_flags |= F_OSPRD_LOCKED; addToList(&(d->readLockingPids), current->pid); grantTicketToNextAliveProcessInOrder(d); osp_spin_unlock(&(d->mutex)); wake_up_all(&(d->blockq)); return 0; } } else if (cmd == OSPRDIOCRELEASE) { // EXERCISE: Unlock the ramdisk. // // If the file hasn't locked the ramdisk, return -EINVAL. // Otherwise, clear the lock from filp->f_flags, wake up // the wait queue, perform any additional accounting steps // you need, and return 0. // Your code here (instead of the next line). osp_spin_lock(&(d->mutex)); // TUAN: If the file hasn't locked the ramdisk, return -EINVAL if (!pidInList(d->writeLockingPids, current->pid) && !(pidInList(d->readLockingPids, current->pid))) { osp_spin_unlock(&(d->mutex)); return -EINVAL; } if (pidInList(d->writeLockingPids, current->pid)) { removeFromList(&(d->writeLockingPids), current->pid); } if (pidInList(d->readLockingPids, current->pid)) { removeFromList(&(d->readLockingPids), current->pid); } // TUAN: Clear the lock from filp->f_flags if no processes (not just current process) hold any lock. if (d->readLockingPids == NULL && d->writeLockingPids == NULL) { filp->f_flags &= !F_OSPRD_LOCKED; } osp_spin_unlock(&(d->mutex)); wake_up_all(&(d->blockq)); return 0; } else r = -ENOTTY; /* unknown command */ return r; }
/* * osprd_ioctl(inode, filp, cmd, arg) * Called to perform an ioctl on the named file. */ int osprd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { osprd_info_t *d = file2osprd(filp); // device info int r = 0; // return value: initially 0 // is file open for writing? int filp_writable = (filp->f_mode & FMODE_WRITE) != 0; // Set 'r' to the ioctl's return value: 0 on success, negative on error if (cmd == OSPRDIOCACQUIRE) { // EXERCISE: Lock the ramdisk. // // If *filp is open for writing (filp_writable), then attempt // to write-lock the ramdisk; otherwise attempt to read-lock // the ramdisk. // // This lock request must block using 'd->blockq' until: // 1) no other process holds a write lock; // 2) either the request is for a read lock, or no other process // holds a read lock; and // 3) lock requests should be serviced in order, so no process // that blocked earlier is still blocked waiting for the // lock. // // If a process acquires a lock, mark this fact by setting // 'filp->f_flags |= F_OSPRD_LOCKED'. You also need to // keep track of how many read and write locks are held: // change the 'osprd_info_t' structure to do this. // // Also wake up processes waiting on 'd->blockq' as needed. // // If the lock request would cause a deadlock, return -EDEADLK. // If the lock request blocks and is awoken by a signal, then // return -ERESTARTSYS. // Otherwise, if we can grant the lock request, return 0. // 'd->ticket_head' and 'd->ticket_tail' should help you // service lock requests in order. These implement a ticket // order: 'ticket_tail' is the next ticket, and 'ticket_head' // is the ticket currently being served. You should set a local // variable to 'd->ticket_head' and increment 'd->ticket_head'. // Then, block at least until 'd->ticket_tail == local_ticket'. // (Some of these operations are in a critical section and must // be protected by a spinlock; which ones?) int ticket; osp_spin_lock(&(d->mutex)); ticket = d->ticket_head++; if (d->ticket_tail != ticket) { osp_spin_unlock(&(d->mutex)); if (wait_event_interruptible(d->blockq, d->ticket_tail == ticket) == -ERESTARTSYS) { d->ticket_tail++; wake_up_all(&(d->blockq)); return -ERESTARTSYS; } } else { osp_spin_unlock(&(d->mutex)); } if (d->write_lock == 1) { for_each_open_file (current, add_dependencies, d); if (check_deadlock()) { release_dependencies(d); d->ticket_tail++; wake_up_all(&(d->blockq)); return -EDEADLK; } if (wait_event_interruptible(d->blockq, d->write_lock == 0) == -ERESTARTSYS) { release_dependencies(d); d->ticket_tail++; wake_up_all(&(d->blockq)); return -ERESTARTSYS; } release_dependencies(d); } if (filp_writable) { if (d->read_lock > 0) { for_each_open_file (current, add_dependencies, d); if (check_deadlock()) { release_dependencies(d); d->ticket_tail++; wake_up_all(&(d->blockq)); return -EDEADLK; } if (wait_event_interruptible(d->blockq, d->read_lock == 0) == -ERESTARTSYS) { release_dependencies(d); d->ticket_tail++; wake_up_all(&(d->blockq)); return -ERESTARTSYS; } release_dependencies(d); } d->write_lock = 1; } else { osp_spin_lock(&(d->mutex)); d->read_lock++; osp_spin_unlock(&(d->mutex)); } filp->f_flags |= F_OSPRD_LOCKED; osp_spin_lock(&(d->mutex)); d->ticket_tail++; osp_spin_unlock(&(d->mutex)); wake_up_all(&(d->blockq)); r = 0; } else if (cmd == OSPRDIOCTRYACQUIRE) { // EXERCISE: ATTEMPT to lock the ramdisk. // // This is just like OSPRDIOCACQUIRE, except it should never // block. If OSPRDIOCACQUIRE would block or return deadlock, // OSPRDIOCTRYACQUIRE should return -EBUSY. // Otherwise, if we can grant the lock request, return 0. osp_spin_lock(&(d->mutex)); if (d->ticket_tail != d->ticket_head) { osp_spin_unlock(&(d->mutex)); return -EBUSY; } d->ticket_head++; osp_spin_unlock(&(d->mutex)); if (d->write_lock == 1) { d->ticket_tail++; wake_up_all(&(d->blockq)); return -EBUSY; } if (filp_writable) { if (d->read_lock > 0) { d->ticket_tail++; wake_up_all(&(d->blockq)); return -EBUSY; } d->write_lock = 1; } else { osp_spin_lock(&(d->mutex)); d->read_lock++; osp_spin_unlock(&(d->mutex)); } filp->f_flags |= F_OSPRD_LOCKED; d->ticket_tail++; wake_up_all(&(d->blockq)); r = 0; } else if (cmd == OSPRDIOCRELEASE) { // EXERCISE: Unlock the ramdisk. // // If the file hasn't locked the ramdisk, return -EINVAL. // Otherwise, clear the lock from filp->f_flags, wake up // the wait queue, perform any additional accounting steps // you need, and return 0. if (filp->f_flags & F_OSPRD_LOCKED) { if (filp_writable) { if (d->write_lock == 1) d->write_lock = 0; else return -EINVAL; } else { osp_spin_lock(&(d->mutex)); if (d->read_lock > 0) d->read_lock--; else { osp_spin_unlock(&(d->mutex)); return -EINVAL; } osp_spin_unlock(&(d->mutex)); } filp->f_flags &= ~F_OSPRD_LOCKED; wake_up_all(&(d->blockq)); r = 0; } else { r = -EINVAL; } } else r = -ENOTTY; /* unknown command */ return r; }
/* * osprd_ioctl(inode, filp, cmd, arg) * Called to perform an ioctl on the named file. */ int osprd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { osprd_info_t *d = file2osprd(filp); // device info int r = 0; // return value: initially 0 unsigned local_ticket; // is file open for writing? int filp_writable = (filp->f_mode & FMODE_WRITE) != 0; // This line avoids compiler warnings; you may remove it. //(void) filp_writable, (void) d; // Set 'r' to the ioctl's return value: 0 on success, negative on error if (cmd == OSPRDIOCACQUIRE) { // EXERCISE: Lock the ramdisk.g // // If *filp is open for writing (filp_writable), then attempt // to write-lock the ramdisk; otherwise attempt to read-lock // the ramdisk. // // This lock request must block using 'd->blockq' until: // 1) no other process holds a write lock; // 2) either the request is for a read lock, or no other process // holds a read lock; and // 3) lock requests should be serviced in order, so no process // that blocked earlier is still blocked waiting for the // lock. // // If a process acquires a lock, mark this fact by setting // 'filp->f_flags |= F_OSPRD_LOCKED'. You also need to // keep track of how many read and write locks are held: // change the 'osprd_info_t' structure to do this. // // Also wake up processes waiting on 'd->blockq' as needed. // // If the lock request would cause a deadlock, return -EDEADLK. // If the lock request blocks and is awoken by a signal, then // return -ERESTARTSYS. // Otherwise, if we can grant the lock request, return 0. // 'd->ticket_head' and 'd->ticket_tail' should help you // service lock requests in order. These implement a ticket // order: 'ticket_tail' is the next ticket, and 'ticket_head' // is the ticket currently being served. You should set a local // variable to 'd->ticket_head' and increment 'd->ticket_head'. // Then, block at least until 'd->ticket_tail == local_ticket'. // (Some of these operations are in a critical section and must // be protected by a spinlock; which ones?) // Your code here (instead of the next two lines). //eprintk("Attempting to acquire\n"); // Block osp_spin_lock(&(d->mutex)); local_ticket = d->ticket_head; d->ticket_head++; osp_spin_unlock(&(d->mutex)); // wait_event_interruptible returns a nonzero value if // interrupted by a signal, so return -ERESTARTSYS if it does. for_each_open_file(current, cause_deadlock, d); if (d->dead > 1 && (filp->f_flags & F_OSPRD_LOCKED)) return -EDEADLK; if (wait_event_interruptible(d->blockq, d->n_writel == 0 && (!filp_writable || d->n_readl == 0) && d->ticket_tail == local_ticket)) { //eprintk ("INTERRUPTED! Head: %u Tail: %u Local: %u\n", d->ticket_head, d->ticket_tail, local_ticket); // If this process wasn't the one being served, don't consider the ticket to increment. if (d->ticket_tail == local_ticket) d->ticket_tail++; else d->desync++; return -ERESTARTSYS; } osp_spin_lock(&(d->mutex)); d->dead = 0; if (d->mutex.lock>0) r = 0; filp->f_flags |= F_OSPRD_LOCKED; if (filp_writable) { d->n_writel++; d->ticket_tail++; } else { d->n_readl++; } osp_spin_unlock(&(d->mutex)); //wake_up_all(&d->blockq); if (!filp_writable) d->ticket_tail++; r = 0; } else if (cmd == OSPRDIOCTRYACQUIRE) { // EXERCISE: ATTEMPT to lock the ramdisk. // // This is just like OSPRDIOCACQUIRE, except it should never // block. If OSPRDIOCACQUIRE would block or return deadlock, // OSPRDIOCTRYACQUIRE should return -EBUSY. // Otherwise, if we can grant the lock request, return 0. // Your code here (instead of the next two lines). //eprintk("Attempting to try acquire\n"); local_ticket = d->ticket_head; // Check for an existing lock. if (filp->f_flags & F_OSPRD_LOCKED || d->n_writel != 0 || (filp_writable && d->n_readl != 0) || d->ticket_tail != local_ticket) { r = -EBUSY;} //eprintk("Stopped\n");} // If *filp is open for writing (filp_writable), then attempt // to write-lock the ramdisk; otherwise attempt to read-lock // the ramdisk. else { osp_spin_lock(&(d->mutex)); d->ticket_head++; filp->f_flags |= F_OSPRD_LOCKED; if (filp_writable) { d->n_writel++; } else { d->n_readl++; } if(d->ticket_tail < d->ticket_head) d->ticket_tail++; osp_spin_unlock(&(d->mutex)); r = 0; wake_up_all(&d->blockq); } // Also wake up processes waiting on 'd->blockq' as needed. // // If the lock request would cause a deadlock, return -EDEADLK. // Otherwise, if we can grant the lock request, return 0. //r = -ENOTTY; } else if (cmd == OSPRDIOCRELEASE) { // EXERCISE: Unlock the ramdisk. // // If the file hasn't locked the ramdisk, return -EINVAL. if (!(filp->f_flags & F_OSPRD_LOCKED)) {r = -EINVAL; } // Otherwise, clear the lock from filp->f_flags, wake up // the wait queue, perform any additional accounting steps // you need, and return 0. else { // Clear lock flag. osp_spin_lock(&(d->mutex)); filp->f_flags &= ~F_OSPRD_LOCKED; d->n_writel = 0; d->n_readl = 0; osp_spin_unlock(&(d->mutex)); // Wake queue. wake_up_all(&d->blockq); // Return. r = 0; } } else r = -ENOTTY; /* unknown command */ return r; }
/* * osprd_ioctl(inode, filp, cmd, arg) * Called to perform an ioctl on the named file. */ int osprd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { osprd_info_t *d = file2osprd(filp); // device info int r = 0; // return value: initially 0 // is file open for writing? int filp_writable = (filp->f_mode & FMODE_WRITE) != 0; // This line avoids compiler warnings; you may remove it. (void) filp_writable, (void) d; // Set 'r' to the ioctl's return value: 0 on success, negative on error unsigned local_ticket; if (cmd == OSPRDIOCACQUIRE) { // EXERCISE: Lock the ramdisk. // // If *filp is open for writing (filp_writable), then attempt // to write-lock the ramdisk; otherwise attempt to read-lock // the ramdisk. // // This lock request must block using 'd->blockq' until: // 1) no other process holds a write lock; // 2) either the request is for a read lock, or no other process // holds a read lock; and // 3) lock requests should be serviced in order, so no process // that blocked earlier is still blocked waiting for the // lock. // // If a process acquires a lock, mark this fact by setting // 'filp->f_flags |= F_OSPRD_LOCKED'. You also need to // keep track of how many read and write locks are held: // change the 'osprd_info_t' structure to do this. // // Also wake up processes waiting on 'd->blockq' as needed. // // If the lock request would cause a deadlock, return -EDEADLK. // If the lock request blocks and is awoken by a signal, then // return -ERESTARTSYS. // Otherwise, if we can grant the lock request, return 0. // 'd->ticket_head' and 'd->ticket_tail' should help you // service lock requests in order. These implement a ticket // order: 'ticket_tail' is the next ticket, and 'ticket_head' // is the ticket currently being served. You should set a local // variable to 'd->ticket_head' and increment 'd->ticket_head'. // Then, block at least until 'd->ticket_tail == local_ticket'. // (Some of these operations are in a critical section and must // be protected by a spinlock; which ones?) // Your code here (instead of the next two lines). osp_spin_lock(&(d->mutex)); // lock while updating device local_ticket = d->ticket_head; d->ticket_head = d->ticket_head + 1; osp_spin_unlock(&(d->mutex)); for_each_open_file(current, dead_lock(filp, d), d); // lock requests block and is awoken by signal if (wait_event_interruptible( (d->blockq, d->writeLocks == 0) && (local_ticket == d->ticketTail) && (!filp_writable || d->readLocks == 0) ) ) { // sleep until any of the conditions are true if (local_ticket != d->ticket_tail) d->numInterrupt++; else d->ticket_tail++; return -ERESTARTSYS; } // deadlock exists bool deadlock_exists = d->deadlock > 1; bool locked = filp->f_flags & F_OSPRD_LOCKED; if (deadlock_exists && locked) return -EDEADLK; // lock device osp_spin_lock(&(d->mutex)); d->deadlock = 0; filp->f_flags = filp->f_flags | F_OSPRD_LOCKED; if (d->mutex.lock >= 1) r = 0; if (!filp_writable) d->readLocks++; else { d->ticket_tail++; d->writeLocks++; } osp_spin_unlock(&(d->mutex)); // finish using device r = 0; // if (!filp_writable) d->ticket_tail++; } else if (cmd == OSPRDIOCTRYACQUIRE) { // EXERCISE: ATTEMPT to lock the ramdisk. // // This is just like OSPRDIOCACQUIRE, except it should never // block. If OSPRDIOCACQUIRE would block or return deadlock, // OSPRDIOCTRYACQUIRE should return -EBUSY. // Otherwise, if we can grant the lock request, return 0. // Your code here (instead of the next two lines). local_ticket = d->ticket_head; bool lockCheck1 = local_ticket != d->ticket_tail; bool lockCheck2 = (filp_writable && d->readLocks != 0); bool lockCheck3 = d->writeLocks != 0; if (filp->f_flags & F_OSPRD_LOCKED || lockCheck1 || lockCheck2 || lockCheck3) r = -EBUSY; // device is busy, is not open for writing else { osp_spin_lock(&(d->mutex)); // lock device to try reading device filp->f_flags = filp->f_flags | F_OSPRD_LOCKED; d->ticket_head++; if (!filp_writable) d->readLocks++; // if not readable, then try to write to device else d->writeLocks++; if (d->ticket_head > d->ticket_tail) d->ticket_tail++; // get next ticket osp_spin_unlock(&(d->mutex)); wake_up_all(&d->blockq); // wake up all blocked tasks r = 0; } } else if (cmd == OSPRDIOCRELEASE) { // EXERCISE: Unlock the ramdisk. // // If the file hasn't locked the ramdisk, return -EINVAL. // Otherwise, clear the lock from filp->f_flags, wake up // the wait queue, perform any additional accounting steps // you need, and return 0. // Your code here (instead of the next line). if (!(filp->f_flags & F_OSPRD_LOCKED)) r = -EINVAL; // file hasn't locked ramdisk else { osp_spin_lock(&(d->mutex)); d->readLocks = 0; d->writeLocks = 0; r = 0; filp->f_flags = filp->f_flags & ~F_OSPRD_LOCKED; osp_spin_unlock(&(d->mutex)); wake_up_all(&d->blockq); // wake up all blocked tasks } } else r = -ENOTTY; /* unknown command */ return r; }
/* * osprd_ioctl(inode, filp, cmd, arg) * Called to perform an ioctl on the named file. */ int osprd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { osprd_info_t *d = file2osprd(filp); // device info int r = 0; // return value: initially 0 unsigned ticket; // is file open for writing? int filp_writable = (filp->f_mode & FMODE_WRITE) != 0; // This line avoids compiler warnings; you may remove it. (void) filp_writable, (void) d; int readlock = in_pid_array(d->pid_read_lock, current->pid); int writelock = in_pid_array(d->pid_write_lock, current->pid); // Set 'r' to the ioctl's return value: 0 on success, negative on error //printk(KERN_INFO "CMD %d\n", cmd); if (cmd == OSPRDIOCACQUIRE) { osp_spin_lock(&(d->mutex)); ticket = d->ticket_head; d->ticket_head++; if ((writelock && !filp_writable) || (readlock && filp_writable)) { unlockWakeIncrement(d); return -EDEADLK; } for_each_open_file(current, locks, d); if (d->any_other_lock_count) { d->any_other_lock_count = 0; unlockWakeIncrement(d); return -EDEADLK; } osp_spin_unlock(&(d->mutex)); //printk(KERN_INFO "%s\n", "CHECKING wait_event_interruptible"); if (wait_event_interruptible(d->blockq, d->ticket_tail==ticket && d->pid_write_lock->used == 0 && (d->pid_read_lock->used == 0 || !filp_writable))) { //printk(KERN_INFO "%s\n", "INSIDE wait_event_interruptible"); if (d->ticket_tail != ticket) add_to_ticket_array(&(d->exitedTickets), ticket); else ticket_to_next_process(d); return -ERESTARTSYS; } //printk(KERN_INFO "%s\n", "OBTAINING LOCK"); osp_spin_lock(&(d->mutex)); filp->f_flags |= F_OSPRD_LOCKED; if (filp_writable) add_to_pid_array(&(d->pid_write_lock), current->pid); else add_to_pid_array(&(d->pid_read_lock), current->pid); unlockWakeIncrement(d); return 0; } else if (cmd == OSPRDIOCTRYACQUIRE) { osp_spin_lock(&(d->mutex)); ticket = d->ticket_head; d->ticket_head++; if ((writelock && !filp_writable) || (readlock && filp_writable)) { unlockWakeIncrement(d); return -EBUSY; } for_each_open_file(current, locks, d); if (d->any_other_lock_count) { d->any_other_lock_count = 0; unlockWakeIncrement(d); return -EBUSY; } osp_spin_unlock(&(d->mutex)); if (d->pid_write_lock->used != 0 || (d->pid_read_lock->used != 0 && filp_writable)) { ticket_to_next_process(d); wake_up_all(&(d->blockq)); return -EBUSY; } osp_spin_lock(&(d->mutex)); filp->f_flags |= F_OSPRD_LOCKED; if (filp_writable) add_to_pid_array(&(d->pid_write_lock), current->pid); else add_to_pid_array(&(d->pid_read_lock), current->pid); unlockWakeIncrement(d); return 0; } else if (cmd == OSPRDIOCRELEASE) { // If the file hasn't locked the ramdisk, return -EINVAL. // Otherwise, clear the lock from filp->f_flags, wake up // the wait queue, perform any additional accounting steps // you need, and return 0. osp_spin_lock(&(d->mutex)); if (!readlock && !writelock) { osp_spin_unlock(&(d->mutex)); return -EINVAL; } if (readlock) { remove_from_pid_array(&d->pid_read_lock, current->pid); } if (writelock) { remove_from_pid_array(&d->pid_write_lock, current->pid); } if (d->pid_read_lock->used == 0 && d->pid_write_lock->used == 0) { filp->f_flags &= !F_OSPRD_LOCKED; } osp_spin_unlock(&(d->mutex)); wake_up_all(&(d->blockq)); } else r = -ENOTTY; return r; }