// This function is called when a /dev/osprdX file is finally closed. // (If the file descriptor was dup2ed, this function is called only when the // last copy is closed.) static int osprd_close_last(struct inode *inode, struct file *filp) { if (filp) { osprd_info_t *d = file2osprd(filp); int filp_writable = filp->f_mode & FMODE_WRITE; // EXERCISE: If the user closes a ramdisk file that holds // a lock, release the lock. Also wake up blocked processes // as appropriate. // Your code here. //need to be tested osp_spin_lock(&d->mutex); if(filp->f_flags & F_OSPRD_LOCKED) { if(filp_writable) { d->writelock=0; d->write_lock_pid = -1; } else { d->readlock--; pid_list_t prev = d->read_lock_pids; pid_list_t curr = d->read_lock_pids; while(curr != NULL) { if(curr->pid == current->pid) { if(prev == NULL) d->read_lock_pids = curr->next; else prev->next = curr->next; break; } else { prev = curr; curr = curr->next; } } } filp->f_flags &= ~F_OSPRD_LOCKED;//set to zero wake_up_all(&d->blockq); osp_spin_unlock(&d->mutex); } else { osp_spin_unlock(&d->mutex); return 0; } // This line avoids compiler warnings; you may remove it. (void) filp_writable, (void) 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) { int r, filp_writable; // return value: initially 0 osprd_info_t *d; r=0; d = file2osprd(filp); // device info if(!d) return -1; // is file open for writing? 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) { unsigned my_ticket; //TO DO: deadlock detection: first only check self lock //check_dead_lock() to be implemented if(check_dead_lock(filp_writable, d)) return -EDEADLK; osp_spin_lock(&(d->mutex)); my_ticket = d->ticket_head; d->head_write = filp_writable; d->ticket_head++; osp_spin_unlock(&(d->mutex)); if(filp_writable) { //write-lock //d->blockq: processes waiting on device if(wait_event_interruptible(d->blockq, d->ticket_tail == my_ticket && d->write_locking_pids.size == 0 && d->read_locking_pids.size == 0)) { //orthogonal to w6hat d->ticket_tail is osp_spin_lock(&(d->mutex)); if(d->ticket_tail == my_ticket) { d->ticket_tail = return_valid_ticket(&(d->invalid_tickets), d->ticket_tail+1); wake_up_all(&(d->blockq)); } else { add_to_ticket_list(&(d->invalid_tickets), my_ticket); } osp_spin_unlock(&(d->mutex)); // wake_up_all(&(d->blockq)); return -ERESTARTSYS; } //wait_event_interruptible() returns 0 osp_spin_lock(&(d->mutex)); filp->f_flags |= F_OSPRD_LOCKED; add_to_pid_list(&(d->write_locking_pids), current->pid); d->ticket_tail = return_valid_ticket(&(d->invalid_tickets), d->ticket_tail+1); if(d->ticket_tail == d->ticket_head) d->head_write = -1; } else { //read_lock if(wait_event_interruptible(d->blockq, d->ticket_tail == my_ticket && d->write_locking_pids.size == 0)) { // eprintk("***************SIGNAL RECEIVED*****************\n"); osp_spin_lock(&(d->mutex)); if(d->ticket_tail == my_ticket) { // eprintk("***************TAIL BEFORE IS: %d*****************\n", d->ticket_tail); d->ticket_tail = return_valid_ticket(&(d->invalid_tickets), d->ticket_tail+1); // eprintk("***************TAIL AFTER IS: %d*****************\n", d->ticket_tail); wake_up_all(&(d->blockq)); } else { add_to_ticket_list(&(d->invalid_tickets), my_ticket); } osp_spin_unlock(&(d->mutex)); // wake_up_all(&(d->blockq)); return -ERESTARTSYS; } // eprintk("***************GOING TO GET READ LOCK*****************\n"); osp_spin_lock(&(d->mutex)); filp->f_flags |= F_OSPRD_LOCKED; add_to_pid_list(&(d->read_locking_pids), current->pid); d->ticket_tail = return_valid_ticket(&(d->invalid_tickets), d->ticket_tail+1); if(d->ticket_tail == d->ticket_head) d->head_write = -1; } osp_spin_unlock(&(d->mutex)); wake_up_all(&(d->blockq)); return 0; // 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). // eprintk("Attempting to acquire\n"); // r = -ENOTTY; } else if (cmd == OSPRDIOCTRYACQUIRE) { // eprintk("hehe"); if(check_dead_lock(filp_writable, d)) return -EBUSY; if(filp_writable) { osp_spin_lock(&(d->mutex)); if(!(d->ticket_tail == d->ticket_head && d->write_locking_pids.size == 0 && d->read_locking_pids.size == 0)) { osp_spin_unlock(&(d->mutex)); return -EBUSY; } filp->f_flags |= F_OSPRD_LOCKED; d->ticket_head++; add_to_pid_list(&(d->write_locking_pids), current->pid); d->ticket_tail = return_valid_ticket(&(d->invalid_tickets), d->ticket_tail+1); if(d->ticket_tail == d->ticket_head) d->head_write = -1; } else { osp_spin_lock(&(d->mutex)); if(!(d->ticket_tail == d->ticket_head && d->write_locking_pids.size == 0)) { osp_spin_unlock(&(d->mutex)); return -EBUSY; } filp->f_flags |= F_OSPRD_LOCKED; d->ticket_head++; add_to_pid_list(&(d->read_locking_pids), current->pid); d->ticket_tail = return_valid_ticket(&(d->invalid_tickets), d->ticket_tail+1); if(d->ticket_tail == d->ticket_head) d->head_write = -1; } osp_spin_unlock(&(d->mutex)); wake_up_all(&(d->blockq)); return 0; // 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"); // r = -ENOTTY; } else if (cmd == OSPRDIOCRELEASE) { // eprintk("***************TRYING TO RELEASE*****************\n"); if((filp->f_flags & F_OSPRD_LOCKED) == 0) return -EINVAL; //write list has at most 1 element //read list can have multiple elements but they don't need to block each other if(filp_writable) { // eprintk("***************RELEASING WRITE LOCK*****************\n"); osp_spin_lock(&(d->mutex)); remove_pid_list(&(d->write_locking_pids), current->pid); } else { osp_spin_lock(&(d->mutex)); remove_pid_list(&(d->read_locking_pids), current->pid); } if(d->write_locking_pids.size == 0 && d->read_locking_pids.size == 0) { filp->f_flags &= !F_OSPRD_LOCKED; } osp_spin_unlock(&(d->mutex)); wake_up_all(&(d->blockq)); return 0; // EXERCISE: Unlock the ramdisk. // (process) // 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). // r = -ENOTTY; } 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; }
/* * 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. if (wait_event_interruptible(d->blockq, 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; } if (wait_event_interruptible(d->blockq, d->ticket_tail == local_ticket && d->n_writel == 0 && (!filp_writable || d->n_readl == 0))) { //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; } //for_each_open_file(current, cause_deadlock, d); //if (d->dead > 1) // return -EDEADLK; osp_spin_lock(&(d->mutex)); //eprintk("head: %d\ntail: %d\n", d->ticket_head, d->ticket_tail); //eprintk("LOCKED\n"); if (d->mutex.lock>0) r = 0; filp->f_flags |= F_OSPRD_LOCKED; if (filp_writable) { d->n_writel++; eprintk("WRITE, ticket: %d\n", local_ticket);} else { d->n_readl++; eprintk("READ, ticket: %d\n", local_ticket);} d->ticket_tail++; osp_spin_unlock(&(d->mutex)); wake_up_all(&d->blockq); //eprintk("UNLOCKED\n"); eprintk("unlock\n"); r = 0; //osp_spin_unlock(&(d->mutex)); } 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; d->mutex.lock = 0; osp_spin_unlock(&(d->mutex)); // Wake queue. wake_up_all(&d->blockq); 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 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); if (pid_array_contains(d->pid_array, d->pid_count, current->pid)) { osp_spin_unlock(&d->mutex); wake_up_all(&d->blockq); return -EDEADLK; } unsigned local_ticket = d->ticket_head; d->ticket_head++; osp_spin_unlock(&d->mutex); if (filp_writable) { // request a write lock r = wait_event_interruptible(d->blockq, (!d->n_write_locks && !d->n_read_locks && d->ticket_tail == local_ticket)); // no read or write locks are held, we can give the write lock if (r != -ERESTARTSYS) { osp_spin_lock(&(d->mutex)); d->n_write_locks++; filp->f_flags |= F_OSPRD_LOCKED; d->pid_array[d->pid_count] = current->pid; d->pid_count++; d->ticket_tail++; osp_spin_unlock(&d->mutex); } else { osp_spin_lock(&d->mutex); d->ticket_tail++; osp_spin_unlock(&d->mutex); } } else { r = wait_event_interruptible(d->blockq, (!d->n_write_locks && d->ticket_tail == local_ticket)); if (r != -ERESTARTSYS) { // no write locks are held, so give the read lock osp_spin_lock(&d->mutex); d->n_read_locks++; filp->f_flags |= F_OSPRD_LOCKED; d->pid_array[d->pid_count] = current->pid; d->pid_count++; d->ticket_tail++; osp_spin_unlock(&d->mutex); } else { osp_spin_lock(&d->mutex); d->ticket_tail++; osp_spin_unlock(&d->mutex); } } wake_up_all(&d->blockq); } 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). osp_spin_lock(&d->mutex); if (pid_array_contains(d->pid_array, d->pid_count, current->pid)) { osp_spin_unlock(&d->mutex); wake_up_all(&d->blockq); return -EBUSY; } osp_spin_unlock(&d->mutex); if (filp_writable) { // try to acquire a write lock if (!d->n_write_locks && !d->n_read_locks) { // we can give the lock osp_spin_lock(&(d->mutex)); d->n_write_locks++; filp->f_flags |= F_OSPRD_LOCKED; d->pid_array[d->pid_count] = current->pid; d->pid_count++; osp_spin_unlock(&d->mutex); } else { r = -EBUSY; } } else { // try to acquire a read lock if (!d->n_write_locks) { osp_spin_lock(&(d->mutex)); d->n_read_locks++; filp->f_flags |= F_OSPRD_LOCKED; d->pid_array[d->pid_count] = current->pid; d->pid_count++; osp_spin_unlock(&d->mutex); } else { r = -EBUSY; } } } 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) > 0) { // the file is locking the ramdisk osp_spin_lock(&d->mutex); remove_pid_from_pid_array(d->pid_array, d->pid_count, current->pid); d->pid_count--; if (filp_writable) { d->n_write_locks--; } else { d->n_read_locks--; } if (d->n_write_locks == 0 && d->n_read_locks == 0) { filp->f_flags &= ~F_OSPRD_LOCKED; } osp_spin_unlock(&d->mutex); r = 0; wake_up_all(&d->blockq); } else { r = -EINVAL; } } else r = -ENOTTY; /* unknown command */ return r; }
/* * osprd_ioctl(inode, filp, cmd, arg)osp_spin_lock(&d->mutex); char wake = 't'; if(d->readlockPids->num > 1) { findpid(d->readlockPids,current->pid,'r'); wake = 'f'; } else if(d->readlockPids->num == 1) { findpid(d->readlockPids,current->pid,'r'); filp->f_flags &= ~F_OSPRD_LOCKED; } else //must be a writer..... { d->nwriters = 0; filp->f_flags &= ~F_OSPRD_LOCKED; } osp_spin_unlock(&d->mutex); if(wake == 't') wake_up_all(d->blockq); * 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 int my_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. // // 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). if(filp_writable) { if((d->nwriters == 1 && d->writelockPid == current->pid) || findpid(d->readlockPids,current->pid,'f')) return -EDEADLK; osp_spin_lock(&d->mutex); my_ticket = d->ticket_head; d->ticket_head++; osp_spin_unlock(&d->mutex); int stat = wait_event_interruptible(d->blockq, d->ticket_tail == my_ticket && d->nwriters == 0 && d->readlockPids->num == 0);//or filp->f_flags & F_OSPRD_LOCKED == 0 if(stat == -ERESTARTSYS) { if(my_ticket == d->ticket_tail) { //lock mutex //increment the ticket tail to the first alive //unlock mutex osp_spin_lock(&d->mutex); while(findticket(d->exitlist,d->ticket_tail,'f') && d->ticket_tail<d->ticket_head) // increment to first alive process d->ticket_tail++; osp_spin_unlock(&d->mutex); } else { osp_spin_lock(&d->mutex); pushticket(d->exitlist,my_ticket); //what if multiple processes get killed at the same time osp_spin_unlock(&d->mutex); } r = stat; } else { osp_spin_lock(&d->mutex); d->nwriters = 1; d->writelockPid = current->pid; filp->f_flags |= F_OSPRD_LOCKED; // d->ticket_tail++;//writer calls wake up all only when it releases... while(findticket(d->exitlist,d->ticket_tail,'f') && d->ticket_tail<d->ticket_head) // increment to first alive process d->ticket_tail++; osp_spin_unlock(&d->mutex); r = 0; } } else { if(d->nwriters == 1 && d->writelockPid == current->pid) return -EDEADLK; osp_spin_lock(&d->mutex); my_ticket = d->ticket_head; d->ticket_head++; osp_spin_unlock(&d->mutex); int stat = wait_event_interruptible(d->blockq, d->ticket_tail == my_ticket && d->nwriters == 0); if(stat == -ERESTARTSYS) { if(my_ticket == d->ticket_tail) { //lock mutex //increment the ticket tail to the first alive //unlock mutex osp_spin_lock(&d->mutex); while(findticket(d->exitlist,d->ticket_tail,'f') && d->ticket_tail<d->ticket_head) // increment to first alive process d->ticket_tail++; osp_spin_unlock(&d->mutex); } else { osp_spin_lock(&d->mutex); pushticket(d->exitlist,my_ticket); //what if multiple processes get killed at the same time osp_spin_unlock(&d->mutex); } r = stat; } else // got the lock { osp_spin_lock(&d->mutex); //multiple readers try to push into readlist pushpid(d->readlockPids, current->pid); d->ticket_tail++; while(findticket(d->exitlist,d->ticket_tail,'f') && d->ticket_tail<d->ticket_head) // increment to first alive process d->ticket_tail++; filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); wake_up_all(&d->blockq);// just incrementing ticket_tail doesnt work, must also wake up all to make them reevaluate condition. r = 0; } } /*eprintk("Attempting to acquire\n"); r = -ENOTTY; */ } 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. if(filp_writable) { osp_spin_lock(&d->mutex); if(d->nwriters == 0 && d->readlockPids->num == 0) { d->nwriters = 1; d->writelockPid = current->pid; filp->f_flags |= F_OSPRD_LOCKED; r = 0; } else r = -EBUSY; osp_spin_unlock(&d->mutex); } else { osp_spin_lock(&d->mutex); if(d->nwriters == 0) { pushpid(d->readlockPids, current->pid); filp->f_flags |= F_OSPRD_LOCKED; r = 0; } else r = -EBUSY; osp_spin_unlock(&d->mutex); } // Your code here (instead of the next two lines). //eprintk("Attempting to try acquire\n"); //r = -ENOTTY; } 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). //r = -ENOTTY; if(!(filp->f_flags & F_OSPRD_LOCKED)) return -EINVAL; osp_spin_lock(&d->mutex); char wake = 't'; if(d->readlockPids->num > 1) { findpid(d->readlockPids,current->pid,'r'); wake = 'f'; } else if(d->readlockPids->num == 1) { findpid(d->readlockPids,current->pid,'r'); filp->f_flags &= ~F_OSPRD_LOCKED; } else //must be a writer..... { d->nwriters = 0; filp->f_flags &= ~F_OSPRD_LOCKED; } osp_spin_unlock(&d->mutex); if(wake == 't') wake_up_all(&d->blockq); 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; // 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 // 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->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). int my_ticket = 0; ///keep track of my ticket osp_spin_lock(&d->mutex); my_ticket = d->ticket_head++; osp_spin_unlock(&d->mutex); d->pid_tracker[my_ticket%MAX_MEM_SIZE] = current->pid; #ifdef DEBUG eprintk("Handling ACQUIRE, my_ticket is %d\n",my_ticket); eprintk("Current pid is :%d\n", current->pid); #endif //check self deadlock osp_spin_lock(&d->mutex); int run_count = d->read_count + d->write_count; int index = (d->ticket_tail - 1)%MAX_MEM_SIZE; osp_spin_unlock(&d->mutex); while(run_count > 0) { if(d->pid_tracker[index] == current->pid) return -EDEADLK; index--; if(index<0) index = MAX_MEM_SIZE - 1; run_count--; } //check loop deadlock if(filp_writable) { wait_event_interruptible(d->blockq, d->write_count == 0 && d->read_count == 0 && d->ticket_tail >= my_ticket); osp_spin_lock(&d->mutex); d->write_count++; d->ticket_tail++; filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); } else { wait_event_interruptible(d->blockq, d->write_count == 0 && d->ticket_tail >= my_ticket); osp_spin_lock(&d->mutex); d->ticket_tail++; d->read_count++; filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); } r=0; /* eprintk("Attempting to acquire\n"); r = -ENOTTY; */ } 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). #ifdef DEBUG eprintk("Handling TRY ACQUIRE\n"); #endif if(filp_writable) { osp_spin_lock(&d->mutex); if(write_condition(d)) { d->write_count++; filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); } else { osp_spin_unlock(&d->mutex); return -EBUSY; } } else { osp_spin_lock(&d->mutex); if(read_condition(d)) { d->read_count++; filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); } else { osp_spin_unlock(&d->mutex); return -EBUSY; } } r = 0; /* eprintk("Attempting to try acquire\n"); r = -ENOTTY; */ } 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). //r = -ENOTTY; #ifdef DEBUG eprintk("Handling RELEASE\n"); #endif if(filp->f_flags != F_OSPRD_LOCKED) return -EINVAL; //No lock on the ramdisk //check whether current process owns a lock osp_spin_lock(&d->mutex); int showup = 0; int run_count = d->read_count + d->write_count; int index = (d->ticket_tail - 1)%MAX_MEM_SIZE; osp_spin_unlock(&d->mutex); while(run_count > 0) { if(d->pid_tracker[index] == current->pid) { showup = 1; break; } index--; if(index<0) index = MAX_MEM_SIZE - 1; run_count--; } if(showup == 0) return -EINVAL;//current process don't own the lock osp_spin_lock(&d->mutex); filp->f_flags = 0; //reset the flag? if(filp_writable) { d->write_count--; } else { d->read_count--; } wake_up_all(&d->blockq); //Wake up request that holds the next ticket osp_spin_unlock(&d->mutex); r = 0; } else r = -ENOTTY; /* unknown command */ return r; }
void dead_lock(struct file* filp, osprd_info_t* d) { if (file2osprd(filp) == d) { d->deadlock++; return; } else return; }
/* * 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 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). //buy a ticket and wait in line osp_spin_lock(&(d->mutex)); unsigned my_ticket = d->ticket_head; d->ticket_head++; osp_spin_unlock(&(d->mutex)); if (filp_writable) { //trying to obtain write lock /*wait until no one has read or write lock if condition becomes true, signal is sent to process and needs to invalidate the ticket*/ //eprintk("trying to get write lock...\nnum_writer: %d num_reader: %d\n", d->num_writer, d->num_reader); if (wait_event_interruptible(d->blockq, d->ticket_tail==my_ticket&&d->num_reader==0 && d->num_writer==0)==-ERESTARTSYS) { osp_spin_lock(&(d->mutex)); if (d->ticket_tail==my_ticket) { d->ticket_tail++; } else{ d->invalid_tickets_array[d->num_invalid_tikets++]=my_ticket; d->num_invalid_tikets++; } osp_spin_unlock(&(d->mutex)); return -ERESTARTSYS; } //get your write lock here, good luck writing! //eprintk("I got write lock!\n"); osp_spin_lock(&(d->mutex)); d->num_writer++; d->current_popular_writer = current->pid; filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&(d->mutex)); }else{ //trying to obtain read lock //eprintk("trying to get read lock...\nnum_writer: %d num_reader\n",d->num_writer, d->num_reader); if (wait_event_interruptible(d->blockq, (d->ticket_tail==my_ticket && d->num_writer==0))==-ERESTARTSYS) { osp_spin_lock(&(d->mutex)); if (d->ticket_tail==my_ticket) { d->ticket_tail++; } else{ d->invalid_tickets_array[d->num_invalid_tikets++]=my_ticket; d->num_invalid_tikets++; } osp_spin_unlock(&(d->mutex)); return -ERESTARTSYS; } //get your read lock here, good luck reading! //eprintk("I got read lock!\n"); osp_spin_lock(&(d->mutex)); d->num_reader++; filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&(d->mutex)); } //next in line please! But we need to check if next guy is still alive :-) osp_spin_lock(&(d->mutex)); d->ticket_tail++; int i=0; for (; i<d->num_invalid_tikets; i++) { if (d->invalid_tickets_array[i]==d->ticket_tail) { d->ticket_tail++; d->invalid_tickets_array[i] = d->invalid_tickets_array[d->num_invalid_tikets]; d->num_invalid_tikets--; i=0; } } osp_spin_unlock(&(d->mutex)); 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). osp_spin_lock(&(d->mutex)); if (filp_writable) { //a writer wants to publish his/her book! if (d->num_reader==0 && d->num_writer==0) { //writer get the lock, good luck writing! d->num_writer++; d->current_popular_writer = current->pid; filp->f_flags |= F_OSPRD_LOCKED; }else{ osp_spin_unlock(&(d->mutex)); return -EBUSY; } }else{ //an avid reader is waiting for new book release! if (d->num_writer==0) { //reader grabs a book and run away d->num_reader++; filp->f_flags |= F_OSPRD_LOCKED; }else{ osp_spin_unlock(&(d->mutex)); return -EBUSY; } } osp_spin_unlock(&(d->mutex)); 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). osp_spin_lock(&(d->mutex)); if (!(filp->f_flags & F_OSPRD_LOCKED)) { //no lock flag return -EINVAL; } if (filp_writable) { //fire all writers //eprintk("release write lock\n"); d->num_writer=0; filp->f_flags &= ~F_OSPRD_LOCKED; }else{ //one reader quit reading d->num_reader--; //eprintk("reduce reade locks to %d\n",d->num_reader); if (d->num_reader==0) { filp->f_flags &= ~F_OSPRD_LOCKED; } } wake_up_all(&(d->blockq)); osp_spin_unlock(&(d->mutex)); 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; // 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?) unsigned my_ticket; int ret; osp_spin_lock(&d->mutex); /* Check for deadlock: can't acquire r/w lock if already locked by this process */ if (int_list_contains(d->read_lock_list, current->pid) || d->write_lock_pid == current->pid) { osp_spin_unlock(&d->mutex); return -EDEADLK; } /* Increment ticket_head after checking for deadlock, or else ticket becomes invalid */ my_ticket = d->ticket_head++; /* If write lock request, must wait until no more read locks AND no more write locks; if read lock request, wait until no more write locks. */ if (filp_writable) { osp_spin_unlock(&d->mutex); ret = wait_event_interruptible(d->blockq, d->ticket_tail == my_ticket && int_list_empty(d->read_lock_list) && d->write_lock_pid == -1); if (!ret) { /* Set device to locked, indicate that this process holds the write lock, and advance d->ticket_tail. */ osp_spin_lock(&d->mutex); filp->f_flags |= F_OSPRD_LOCKED; d->write_lock_pid = current->pid; advance_to_next_valid_ticket(d); osp_spin_unlock(&d->mutex); } else /* Woken up by signal */ { /* If ticket_tail == this thread's ticket, advance to next valid ticket; otherwise, mark this thread's ticket as invalid */ SIGNAL_WAKE_UP: if (d->ticket_tail == my_ticket) { osp_spin_lock(&d->mutex); advance_to_next_valid_ticket(d); osp_spin_unlock(&d->mutex); } else { osp_spin_lock(&d->mutex); int_list_add(d->invalid_tickets, my_ticket); osp_spin_unlock(&d->mutex); } return -ERESTARTSYS; } } else { osp_spin_unlock(&d->mutex); ret = wait_event_interruptible(d->blockq, d->ticket_tail == my_ticket && d->write_lock_pid == -1); if (!ret) { /* Set device to locked, indicate that this process holds a read lock, and advance d->ticket_tail. */ osp_spin_lock(&d->mutex); filp->f_flags |= F_OSPRD_LOCKED; int_list_add(d->read_lock_list, current->pid); advance_to_next_valid_ticket(d); osp_spin_unlock(&d->mutex); wake_up_all(&d->blockq); } else /* Woken up by signal */ goto SIGNAL_WAKE_UP; } 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); /* Check for deadlock normally */ if (int_list_contains(d->read_lock_list, current->pid) || d->write_lock_pid == current->pid) { osp_spin_unlock(&d->mutex); return -EBUSY; } /* Check for lock acquire conditions: - ticket_tail must equal ticket_head - there must be no write lock - if write lock request, must be no read locks */ if (d->ticket_head != d->ticket_tail || d->write_lock_pid != -1) { osp_spin_unlock(&d->mutex); return -EBUSY; } if (filp_writable && !int_list_empty(d->read_lock_list)) { osp_spin_unlock(&d->mutex); return -EBUSY; } /* Acquire the read/write lock */ if (filp_writable) d->write_lock_pid = current->pid; else int_list_add(d->read_lock_list, current->pid); filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); 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. /* Same as release procedure in osprd_close_last() */ osp_spin_lock(&d->mutex); if (try_release_lock(d, filp, filp_writable)) { osp_spin_unlock(&d->mutex); wake_up_all(&d->blockq); } else { osp_spin_unlock(&d->mutex); } 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 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). unsigned local_ticket; if(d==NULL) return -1; osp_spin_lock(&d->mutex); if(current->pid == d->write_pid) { osp_spin_unlock(&d->mutex); return -EDEADLK; } local_ticket = d->ticket_head; if(filp_writable)//write locks { pid_list last; pid_list now; last = NULL; now = d->read_pids; while(now != NULL) { if(now->pid == current->pid) { osp_spin_unlock(&d->mutex); return -EDEADLK; } else { last = now; now = now->next_pid; } } osp_spin_unlock(&d->mutex); while(1) { if(wait_event_interruptible(d->blockq,1) == -ERESTARTSYS) return -ERESTARTSYS; else if(d->write_lock_size ==0 && d->read_lock_size ==0 && local_ticket == d->ticket_tail) break; else schedule(); } osp_spin_lock(&d->mutex); d->write_lock_size=1; d->write_pid = current->pid; } else//read lock { osp_spin_unlock(&d->mutex); while(1) { if(wait_event_interruptible(d->blockq,1) == -ERESTARTSYS) return -ERESTARTSYS; else if(d->write_lock_size ==0 && d->read_lock_size ==0 && local_ticket == d->ticket_tail) break; else schedule(); } osp_spin_lock(&d->mutex); d->read_lock_size++; set_read_list(NULL, d->read_pids, d); d->ticket_head++; } filp->f_flags |= F_OSPRD_LOCKED; d->ticket_tail++; osp_spin_unlock(&d->mutex); } 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). unsigned local_ticket; if(d==NULL) return -1; local_ticket = d->ticket_head; if(filp_writable) { osp_spin_lock(&d->mutex); if(d->write_lock_size !=0 || d->read_lock_size !=0 || local_ticket != d->ticket_tail) r = -EBUSY; else { filp->f_flags |= F_OSPRD_LOCKED; d->write_lock_size=1; d->write_pid = current->pid; } osp_spin_unlock(&d->mutex); } else { osp_spin_lock(&d->mutex); if(d->write_lock_size !=0 || d->read_lock_size !=0 || local_ticket != d->ticket_tail) r = -EBUSY; else { filp->f_flags |= F_OSPRD_LOCKED; d->read_lock_size++; set_read_list(NULL, d->read_pids, d); d->ticket_head++; d->ticket_tail++; } osp_spin_unlock(&d->mutex); } } 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(d==NULL) return -1; if((filp->f_flags & F_OSPRD_LOCKED)==0) r = -EINVAL; else { osp_spin_lock(&d->mutex); if(filp_writable) { d->write_lock_size=0; d->write_pid = -1; } else { d->read_lock_size--; release_read_lock(d->read_pids, NULL); } if(d->read_lock_size == 0 && d->write_lock_size ==0) wake_up_all(&d->blockq); filp->f_flags ^= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); } } else r = -ENOTTY; /* unknown command */ return r; }
void count_ramdisks_hook(struct file *filp, osprd_info_t *d) { osprd_info_t *dnew ; // This hook function will be passed to for_each_file. if ((dnew = file2osprd(filp)) != NULL) dnew->num_ramdisks_open++; }
/* * 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) { int r = 0; // is file open for writing? int filp_writable = (filp->f_mode & FMODE_WRITE) != 0; osprd_info_t *d = file2osprd(filp); // device info DEFINE_WAIT(wait); // wait queue entry in case we block wait.func = &default_wake_function; // 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 a writable file, 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 may 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. // Your code here (instead of the next two lines). if(filp_writable){ // Attempt to take write lock if(d->num_ramdisks_open){ d->num_ramdisks_open = 0; r = -EDEADLK; return r; } if(waitqueue_active(&d->blockq) || d->write_lock_count || d->read_lock_count || (filp->f_flags & F_OSPRD_LOCKED)) { /* Enque writer process and call scheduler if * i. Wait queue is not empty * ii. No. of readers > 0 * iii. No. of writers > 0 * iv. Ramdisk has been locked */ osp_spin_lock(&d->mutex); prepare_to_wait_exclusive(&d->blockq,&wait,TASK_INTERRUPTIBLE); osp_spin_unlock(&d->mutex); do{ schedule(); /* if signal has occured, return ERESTARTSYS to caller */ if(signal_pending(current)){ r = -ERESTARTSYS; return r; } }while(d->write_lock_count || d->read_lock_count || (filp->f_flags & F_OSPRD_LOCKED)); /* All condtions for locking satisfied; unblock (dequeue) */ finish_wait(&d->blockq, &wait); } /* Acquire write lock */ osp_spin_lock(&d->mutex); filp->f_flags |= F_OSPRD_LOCKED; d->write_lock_count++; osp_spin_unlock(&d->mutex); } else { // Attempt to take read lock /* Enque writer process and call scheduler if * i. Wait queue is not empty * ii. No. of writers > 0 * iii. Ramdisk has been locked */ if(waitqueue_active(&d->blockq) || d->write_lock_count || (filp->f_flags & F_OSPRD_LOCKED)) { osp_spin_lock(&d->mutex); prepare_to_wait_exclusive(&d->blockq,&wait,TASK_INTERRUPTIBLE); osp_spin_unlock(&d->mutex); do{ schedule(); /* if signal has occured, return ERESTARTSYS to caller */ if(signal_pending(current)){ r = -ERESTARTSYS; return r; } } while(d->write_lock_count || (filp->f_flags & F_OSPRD_LOCKED)); /* All condtions for locking satisfied; unblock (dequeue) */ finish_wait(&d->blockq, &wait); } /* Acquire read lock */ osp_spin_lock(&d->mutex); filp->f_flags |= F_OSPRD_LOCKED; d->read_lock_count++; #if 0 /* Wake up next reader in the queue to ensure that * - when a writer dequeues, all subsequent readers in the queue * till the first writer, are woken up. * - the writer reaches the head of the queue to be called next * * This causues TEST CASE 15 to fail. So I have commented it. */ if(waitqueue_active(&d->blockq)) wake_up(&d->blockq); #endif osp_spin_unlock(&d->mutex); } #if 0 eprintk("Attempting to acquire\n"); r = -ENOTTY; #endif } else if (cmd == OSPRDIOCTRYACQUIRE) { // EXERCISE: ATTEMPT 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). if(filp_writable){ // Attempt to take write lock /* Enque writer process and call scheduler if * i. Wait queue is not empty * ii. No. of readers > 0 * iii. No. of writers > 0 * iv. Ramdisk has been locked */ if(waitqueue_active(&d->blockq) || d->write_lock_count || d->read_lock_count || (filp->f_flags & F_OSPRD_LOCKED)) { /* Not able to acuqire write lock; return EBUSY */ r = -EBUSY; return r; } /* Acquire write lock */ osp_spin_lock(&d->mutex); filp->f_flags |= F_OSPRD_LOCKED; d->write_lock_count++; osp_spin_unlock(&d->mutex); } else { /* Enque writer process and call scheduler if * i. Wait queue is not empty * ii. No. of writers > 0 * iii. Ramdisk has been locked */ if(waitqueue_active(&d->blockq) || d->write_lock_count || (filp->f_flags & F_OSPRD_LOCKED)) { /* Not able to acuqire read lock; return EBUSY */ r = -EBUSY; return r; } /* Acquire read lock */ osp_spin_lock(&d->mutex); filp->f_flags |= F_OSPRD_LOCKED; d->read_lock_count++; osp_spin_unlock(&d->mutex); } #if 0 eprintk("Attempting to try acquire\n"); r = -ENOTTY; #endif } 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)) /* you should not be here */ r = -EINVAL; else { /* Release read or write lock as appropriate */ osp_spin_lock(&d->mutex); filp->f_flags &= (~F_OSPRD_LOCKED); if(filp_writable) d->write_lock_count = 0; else d->read_lock_count--; if(waitqueue_active(&d->blockq)) { wake_up(&d->blockq); } //d->num_ramdisks_open--; osp_spin_unlock(&d->mutex); } // r = -ENOTTY; } 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 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). //eprintk("Attempting to acquire\n"); r = -ENOTTY; osp_spin_lock(&d->mutex); if(current->pid == d->write_lock_pid) { osp_spin_unlock(&d->mutex); return -EDEADLK; } unsigned local_ticket = d->ticket_head; d->ticket_head++; if(filp_writable) { pid_list_t prev = NULL; pid_list_t curr = d->read_lock_pids; while(curr != NULL) { if(curr->pid == current->pid) { osp_spin_unlock(&d->mutex); return -EDEADLK; } else { prev = curr; curr = curr->next; } } while(d->writelock !=0 || d->readlock!=0 || local_ticket!=d->ticket_tail) { // eprintk("in write while\n"); int returnValue = wait_event_interruptible(d->blockq,1); osp_spin_unlock(&d->mutex); if(returnValue == -ERESTARTSYS) return returnValue; schedule(); osp_spin_lock(&d->mutex); } filp->f_flags |= F_OSPRD_LOCKED; d->writelock=1;//should be 1 d->write_lock_pid = current->pid; } else { while(d->writelock!=0 || d->ticket_tail != local_ticket) { // eprintk("in read while\n"); int returnValue = wait_event_interruptible(d->blockq,1); osp_spin_unlock(&d->mutex); if(returnValue == -ERESTARTSYS) return returnValue; schedule(); osp_spin_lock(&d->mutex); } filp->f_flags |= F_OSPRD_LOCKED; d->readlock++; pid_list_t prev = NULL; pid_list_t curr = d->read_lock_pids; while(curr != NULL) { prev = curr; curr = curr->next; } if(prev == NULL) { d->read_lock_pids = kmalloc(sizeof(pid_list_t), GFP_ATOMIC); d->read_lock_pids->pid = current->pid; d->read_lock_pids->next = NULL; } else { // assign to next prev->next = kmalloc(sizeof(pid_list_t), GFP_ATOMIC); prev->next->pid = current->pid; prev->next->next = NULL; } } d->ticket_tail++; osp_spin_unlock(&d->mutex); 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"); r = -ENOTTY; if(filp_writable) { osp_spin_lock(&d->mutex);//should put it here CS if(d->writelock==0 && d->readlock==0 && d->ticket_head==d->ticket_tail) { //if not deadlock if(d->write_lock_pid == current->pid) { osp_spin_unlock(&d->mutex); return -EDEADLK; } else { filp->f_flags |= F_OSPRD_LOCKED; d->writelock=1; d->write_lock_pid = current->pid; r=0; } } else { r=-EBUSY; } osp_spin_unlock(&d->mutex); } else { osp_spin_lock(&d->mutex);//should put it here CS if(d->writelock==0 && d->ticket_head==d->ticket_tail) { //if not deadlock filp->f_flags |= F_OSPRD_LOCKED; d->readlock++; // Add pid to read lock pid lists pid_list_t prev = NULL; pid_list_t curr = d->read_lock_pids; while(curr != NULL) { prev = curr; curr = curr->next; } if(prev == NULL) { d->read_lock_pids = kmalloc(sizeof(pid_list_t), GFP_ATOMIC); d->read_lock_pids->pid = current->pid; d->read_lock_pids->next = NULL; } else { // assign to next prev->next = kmalloc(sizeof(pid_list_t), GFP_ATOMIC); prev->next->pid = current->pid; prev->next->next = NULL; } r=0; } else if(d->write_lock_pid == current->pid) { osp_spin_unlock(&d->mutex); return -EDEADLK; } else { r = -EBUSY; } osp_spin_unlock(&d->mutex); } } 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). r = -ENOTTY; if(filp->f_flags & F_OSPRD_LOCKED==0) { r = -EINVAL; } else { osp_spin_lock(&d->mutex);//should put it here CS if(filp_writable) // how to know it is read or write lock { d->writelock=0; d->write_lock_pid = -1; } else { d->readlock--; // Clear this PID from the read lock list pid_list_t prev = NULL; pid_list_t curr = d->read_lock_pids; while(curr != NULL) { if(curr->pid == current->pid) { if(prev == NULL) d->read_lock_pids = curr->next; else prev->next = curr->next; kfree(curr); break; } else { prev = curr; curr = curr->next; } } } filp->f_flags &= ~F_OSPRD_LOCKED;//set to zero wake_up_all(&d->blockq); r=0; osp_spin_unlock(&d->mutex); } } 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 DEFINE_WAIT(wait); //using the low level stuff // 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->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). if (filp_writable) //means we want the write lock. { osp_spin_lock(&d->mutex); if (d->q_size > 0) //if another proc is waiting, give control to "front of line" { if (!d->write_lock && !d->read_locks) // no locks except us wake_up_all(&d->blockq); d->q_size++; //add to back of queue prepare_to_wait_exclusive(&d->blockq, &wait, TASK_INTERRUPTIBLE); // add to write queue osp_spin_unlock(&d->mutex); schedule(); //go to sleep until wake_up_all wakes us //wake up osp_spin_lock(&d->mutex); finish_wait(&d->blockq, &wait); //delete from queue d->q_size--; //check that wasn't interrupted if (signal_pending(current)) { osp_spin_unlock(&d->mutex); return -ERESTARTSYS; } } // at "front of line." Now check that no readers / writers while (d->write_lock || d->read_locks) { //if the lock is held just go back to back of line. prepare_to_wait_exclusive(&d->blockq, &wait, TASK_INTERRUPTIBLE); d->q_size++; osp_spin_unlock(&d->mutex); schedule(); //wake up osp_spin_lock(&d->mutex); finish_wait(&d->blockq, &wait); d->q_size--; if (signal_pending(current)) { osp_spin_unlock(&d->mutex); return -ERESTARTSYS; } } //when this breaks we can get the lock. d->write_lock = 1; d->write_lock_owner = current->pid; filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); } else //we want a read lock { osp_spin_lock(&d->mutex); if (d->q_size > 0) //if another proc is waiting, give control to "front of line" { if (!d->write_lock && !d->read_locks) // no locks except us wake_up_all(&d->blockq); d->q_size++; //add to back of queue prepare_to_wait_exclusive(&d->blockq, &wait, TASK_INTERRUPTIBLE); // add to write queue osp_spin_unlock(&d->mutex); schedule(); //go to sleep until wake_up_all wakes us //wake up osp_spin_lock(&d->mutex); finish_wait(&d->blockq, &wait); //delete from queue d->q_size--; //check that wasn't interrupted if (signal_pending(current)) { osp_spin_unlock(&d->mutex); return -ERESTARTSYS; } } // at "front of line." Now check that no writers (readers ok) while (d->write_lock) { //if the lock is held just go back to back of line. prepare_to_wait_exclusive(&d->blockq, &wait, TASK_INTERRUPTIBLE); d->q_size++; osp_spin_unlock(&d->mutex); schedule(); //wake up osp_spin_lock(&d->mutex); finish_wait(&d->blockq, &wait); d->q_size--; if (signal_pending(current)) { osp_spin_unlock(&d->mutex); return -ERESTARTSYS; } } //when this breaks we can get the lock. d->read_locks++; filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); } } 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). //r = -ENOTTY; if (filp_writable) { //try to get a write lock osp_spin_lock(&d->mutex); if (d->write_lock || d->read_locks) //if the file is locked, fail { osp_spin_unlock(&d->mutex); return -EBUSY; } else //no write lock, no read locks. { //get the write lock d->write_lock = 1; d->write_lock_owner = current->pid; filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); } } else //read lock { osp_spin_lock(&d->mutex); if (d->write_lock) //locked for writing { osp_spin_unlock(&d->mutex); return -EBUSY; } else { d->read_locks++; filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); } } } 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)) return -EINVAL; //the file isn't even locked yadingus //else filp->f_flags &= ~F_OSPRD_LOCKED; //unlock flag osp_spin_lock(&d->mutex); if (filp_writable) //had a write lock { d->write_lock = 0; //release the lock d->write_lock_owner = -1; wake_up_all(&d->blockq); //wake up the queue and get next } else //read lock { d->read_locks--; if (!d->read_locks) //wake up the queue if no more readers wake_up_all(&d->blockq); } osp_spin_unlock(&d->mutex); } 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 pid_t pid; node_t check; int wait, i; unsigned ticket; // is file open for writing? int filp_writable = (filp->f_mode & FMODE_WRITE) != 0; if (cmd == OSPRDIOCACQUIRE) { // The process id trying to get this lock is the unique identifier // in our list pid = current->pid; // Check if this pid has already tried to get a lock on this device check = check_in_list(d->lock_list, pid); if (check) { return -EDEADLK; } /* This whole section is to prevent deadlock. The global lock is necessary to avoid raceconditions, like processes getting inserted into the list after we've already checked it. This is a huge unfortunate bottleneck. I does prevent deadlock, however. */ osp_spin_lock(&g_mutex); for (i = 0; i < NOSPRD; i++) { if(d == &osprds[i]) { //Checks if this our own device continue; } else { node_t traversal = d->lock_list; //Traversal set to beginning of our lock linked list node_t start; // The starting position of our pid in the other list check = NULL; start = check_in_list(osprds[i].lock_list, pid); // We only care if our is also has another lock in this node if(start == NULL) continue; while(traversal != NULL) { // Only care about stuff after our pid check = check_in_list(start, traversal->pid); // We must make sure that our pid does not hold // a lock that would block anything in another queue if(check != NULL) { osp_spin_unlock(&g_mutex); return -EDEADLK; } traversal = traversal->next; } } } // It doesn't cause deadlock, so me must insert it into the list so // that others can check if this process will cause deadlock insert_node (&d->lock_list, pid, d->read_locks, d->write_locks); osp_spin_unlock(&g_mutex); // 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?) if (filp_writable) { // Get the next ticket osp_spin_lock(&d->mutex); ticket = d->ticket_tail; d->ticket_tail++; osp_spin_unlock(&d->mutex); // Wait for the ticket to become the current job wait = wait_event_interruptible( d->blockq, (ticket == d->ticket_head && d->read_locks == 0 && d->write_locks == 0) ); // Interupt handling if (wait != 0) { // Wait until we have the ticket // If we increment too early, another process could wait // forever while(ticket != d->ticket_head) { yield(); continue; } osp_spin_lock(&d->mutex); d->ticket_head++; osp_spin_unlock(&d->mutex); return wait; } // Give the lock for this job osp_spin_lock(&d->mutex); d->write_locks++; filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); } else { // Read only // Get the next ticket osp_spin_lock(&d->mutex); ticket = d->ticket_tail; d->ticket_tail++; osp_spin_unlock(&d->mutex); // Wait for the ticket to become the current job wait = wait_event_interruptible( d->blockq, // We don't need to check for other read tasks, take note (ticket == d->ticket_head && d->write_locks == 0) ); // Interrupt handling if (wait != 0) { // Wait until we have the ticket // If we increment too early, another process could wait // forever while(ticket != d->ticket_head) { yield(); continue; } osp_spin_lock(&d->mutex); d->ticket_head++; osp_spin_unlock(&d->mutex); wake_up_all(&d->blockq); return wait; } // Give the lock to the current job osp_spin_lock(&d->mutex); d->read_locks++; filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); // Since the next task might be a read task, we wake everyone wake_up_all(&d->blockq); } 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. if (filp_writable) { // Get the next ticket osp_spin_lock(&d->mutex); ticket = d->ticket_tail; d->ticket_tail++; osp_spin_unlock(&d->mutex); // Check if we can get the ticket if (ticket == d->ticket_head && d->read_locks == 0 && d->write_locks == 0) { osp_spin_lock(&d->mutex); d->write_locks++; filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); } else { // Clean up the tickets, make sure they are back to // where they should be osp_spin_lock(&d->mutex); d->ticket_head++; osp_spin_unlock(&d->mutex); return -EBUSY; } } else { // Read only // Get the next ticket osp_spin_lock(&d->mutex); ticket = d->ticket_tail; d->ticket_tail++; osp_spin_unlock(&d->mutex); // Check if we can get the ticket if (ticket == d->ticket_head && d->write_locks == 0) { osp_spin_lock(&d->mutex); d->read_locks++; filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); // Wake up everyone for the same reasons wake_up_all(&d->blockq); } else { // Clean up the tickets, make sure they are back to // where they should be osp_spin_lock(&d->mutex); d->ticket_head++; osp_spin_unlock(&d->mutex); return -EBUSY; } } // Success 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). // Check to make sure has lock if (!(filp->f_flags & F_OSPRD_LOCKED)) { return -EINVAL; } // Clear the locked flag filp->f_flags &= ~(F_OSPRD_LOCKED); // Increment the current job and increment the number of finished // writing/reading tasks if(filp_writable) { osp_spin_lock(&d->mutex); d->ticket_head++; d->write_locks--; osp_spin_unlock(&d->mutex); } else { osp_spin_lock(&d->mutex); d->ticket_head++; d->read_locks--; osp_spin_unlock(&d->mutex); } // We're finished, notify everyone wake_up_all(&d->blockq); return 0; } else return -ENOTTY; /* unknown command */ }
/* * 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; int i; // 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?) // (simple deadlock) // check if process attempting to read/write to a device it holds if (d->write_proc == current->pid) return -EDEADLK; for (i = 0; i < OSPRD_MAJOR; i++) { if (d->read_procs[i] == current->pid) return -EDEADLK; } // grab ticket osp_spin_lock(&d->mutex); ticket = d->ticket_head; d->ticket_head++; osp_spin_unlock(&d->mutex); if (filp_writable) { // wait for read/write locks r = wait_event_interruptible(d->blockq, d->ticket_tail == ticket && d->num_read_locks == 0 && d->write_locked == 0); // if interrupted by signal if (r == -ERESTARTSYS) { // reset ticket queues if (ticket == d->ticket_tail) d->ticket_tail++; else d->ticket_head--; return r; } // write lock acquired osp_spin_lock(&d->mutex); d->write_locked = 1; // add current process to write lock holder d->write_proc = current->pid; } else { // wait for read locks r = wait_event_interruptible(d->blockq, d->ticket_tail >= ticket && d->write_locked == 0); // if interrupted by signal if (r == -ERESTARTSYS) { // reset ticket queues if (ticket == d->ticket_tail) d->ticket_tail++; else d->ticket_head--; return r; } // read lock acquired osp_spin_lock(&d->mutex); d->num_read_locks++; // add current process to read lock holders for (i = 0; i < OSPRD_MAJOR; i++) { if (d->read_procs[i] == -1) { d->read_procs[i] = current->pid; break; } } } // lock acquired d->ticket_tail++; filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); } 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. if (filp_writable) { // check for read/write locks if (d->num_read_locks > 0 || d->write_locked == 1) return -EBUSY; // write lock acquired osp_spin_lock(&d->mutex); d->write_locked = 1; // add current process to write lock holder d->write_proc = current->pid; } else { // check for write lock if (d->write_locked == 1) return -EBUSY; // read lock acquired osp_spin_lock(&d->mutex); d->num_read_locks++; // add current process to read lock holders for (i = 0; i < OSPRD_MAJOR; i++) { if (d->read_procs[i] == -1) { d->read_procs[i] = current->pid; break; } } } // lock acquired d->ticket_head++; d->ticket_tail++; filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); } 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) == 0) return -EINVAL; osp_spin_lock(&d->mutex); if (filp_writable) { d->write_locked = 0; d->write_proc = -1; } else { d->num_read_locks--; for (i = 0; i < OSPRD_MAJOR; i++) { if (d->read_procs[i] == current->pid) { d->read_procs[i] = -1; break; } } } filp->f_flags ^= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); wake_up_all(&d->blockq); } 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 if (cmd == OSPRDIOCACQUIRE) { // EXERCISE: Lock the ramdisk. if(d == NULL) return -1; osp_spin_lock(&d->mutex); // If the current process already has a write lock, all locks are bad, // and we would deadlock, so don't try if(current->pid == d->write_lock_pid) { osp_spin_unlock(&d->mutex); return -EDEADLK; } // Place itself in the ticket line for processing unsigned local_ticket = d->ticket_head; d->ticket_head++; // If *filp is open for writing (filp_writable), then attempt // to write-lock the ramdisk; if(filp_writable) { // Check to make sure this process doesn't have a read lock // If this process has a read lock, we will deadlock, so don't try pid_list_t prev = NULL; pid_list_t curr = d->read_lock_pids; while(curr != NULL) { if(curr->pid == current->pid) { osp_spin_unlock(&d->mutex); return -EDEADLK; } else { prev = curr; curr = curr->next; } } // Block while conditions aren't met while( d->write_lock != 0 || d->read_locks != 0 || local_ticket != d->ticket_tail ) { int wait_result = wait_event_interruptible(d->blockq, 1); osp_spin_unlock(&d->mutex); if(wait_result == -ERESTARTSYS) return -ERESTARTSYS; schedule(); osp_spin_lock(&d->mutex); } // Activate the write lock filp->f_flags |= F_OSPRD_LOCKED; d->write_lock = 1; d->write_lock_pid = current->pid; } // Otherwise attempt to read-lock the ramdisk. else { // Block while conditions aren't met while( d->write_lock != 0 || local_ticket != d->ticket_tail ) { int wait_result = wait_event_interruptible(d->blockq, 1); osp_spin_unlock(&d->mutex); if(wait_result == -ERESTARTSYS) return -ERESTARTSYS; schedule(); osp_spin_lock(&d->mutex); } filp->f_flags |= F_OSPRD_LOCKED; d->read_locks++; // Add pid to read lock pid lists pid_list_t prev = NULL; pid_list_t curr = d->read_lock_pids; while(curr != NULL) { prev = curr; curr = curr->next; } if(prev == NULL) { d->read_lock_pids = kmalloc(sizeof(pid_list_t), GFP_ATOMIC); d->read_lock_pids->pid = current->pid; d->read_lock_pids->next = NULL; } else { // assign to next prev->next = kmalloc(sizeof(pid_list_t), GFP_ATOMIC); prev->next->pid = current->pid; prev->next->next = NULL; } } d->ticket_tail++; osp_spin_unlock(&d->mutex); // 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?) } else if (cmd == OSPRDIOCTRYACQUIRE) { if(d == NULL) return -1; // 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. // If *filp is open for writing (filp_writable), then attempt // to write-lock the ramdisk; if(filp_writable) { osp_spin_lock(&d->mutex); if( d->write_lock != 0 || d->read_locks != 0 || d->ticket_head != d->ticket_tail ) { r = -EBUSY; } else { // Check for a deadlock if(d->write_lock_pid == current->pid) { osp_spin_unlock(&d->mutex); return -EDEADLK; } else { filp->f_flags |= F_OSPRD_LOCKED; d->write_lock = 1; d->write_lock_pid = current->pid; } } osp_spin_unlock(&d->mutex); } // Otherwise attempt to read-lock the ramdisk. else { osp_spin_lock(&d->mutex); // Check for conditions, it might be busy if( d->write_lock != 0 || d->ticket_head != d->ticket_tail ) { r = -EBUSY; } // Check for deadlock; do not give read lock if process has write lock else if(d->write_lock_pid == current->pid) { osp_spin_unlock(&d->mutex); return -EDEADLK; } else { filp->f_flags |= F_OSPRD_LOCKED; d->read_locks++; // Add pid to read lock pid lists pid_list_t prev = NULL; pid_list_t curr = d->read_lock_pids; while(curr != NULL) { prev = curr; curr = curr->next; } if(prev == NULL) { d->read_lock_pids = kmalloc(sizeof(pid_list_t), GFP_ATOMIC); d->read_lock_pids->pid = current->pid; d->read_lock_pids->next = NULL; } else { // assign to next prev->next = kmalloc(sizeof(pid_list_t), GFP_ATOMIC); prev->next->pid = current->pid; prev->next->next = NULL; } } osp_spin_unlock(&d->mutex); } // Unlock the ramdisk. } else if (cmd == OSPRDIOCRELEASE) { if(d == NULL) return -1; osp_spin_lock(&d->mutex); // If the file hasn't locked the ramdisk, return -EINVAL. if((filp->f_flags & F_OSPRD_LOCKED) == 0) { r = -EINVAL; } else { if(filp_writable) { // Clear the write lock and wake up others d->write_lock = 0; d->write_lock_pid = -1; wake_up_all(&d->blockq); } else { // Clear the read lock d->read_locks--; // Clear this PID from the read lock list pid_list_t prev = NULL; pid_list_t curr = d->read_lock_pids; while(curr != NULL) { if(curr->pid == current->pid) { if(prev == NULL) d->read_lock_pids = curr->next; else prev->next = curr->next; kfree(curr); break; } else { prev = curr; curr = curr->next; } } // Wake up other processes wake_up_all(&d->blockq); } // Clear the lock from filp->f_flags filp->f_flags &= !F_OSPRD_LOCKED; } osp_spin_unlock(&d->mutex); } else r = -ENOTTY; /* unknown command */ return r; }
void cause_deadlock(struct file *filp, osprd_info_t *d){ if (file2osprd(filp) == d) d->dead++; }
/* * 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 // 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?) // Used to track the current request unsigned local_ticket; if(check_deadlock(d)) return -EDEADLK; // If we can't acquire the lock we put ourselves in the back of the queue // when the lock is released ticket_head will be incremented and we'll be // woken up while(try_acquire_file_lock(filp) != 0) { spin_lock(&d->mutex); d->ticket_tail++; local_ticket = d->ticket_tail; d->lock_waiter_l = list_add_to_front(d->lock_waiter_l, current->pid); spin_unlock(&d->mutex); wait_event_interruptible(d->blockq, d->ticket_head == local_ticket || d->num_to_requeue > 0); spin_lock(&d->mutex); d->lock_waiter_l = list_remove_element(d->lock_waiter_l, current->pid); spin_unlock(&d->mutex); // process any pending signals by re-queueing everything if(d->num_to_requeue > 0) { // Note: do NOT wake threads here, each thread should requeue only ONCE spin_lock(&d->mutex); d->num_to_requeue--; spin_unlock(&d->mutex); // If we find another pending signal, dispatch that too if(signal_pending(current)) return -ERESTARTSYS; } else if(signal_pending(current)) // See if we were woken up by a signal { // For simplicity, we requeue all waiting tasks (-1 which // is the process) being "popped" off the wait queue spin_lock(&d->mutex); d->num_to_requeue = (d->ticket_tail - d->ticket_head - 1); d->ticket_head = 0; d->ticket_tail = 0; // All threads are woken to notify them of requeuing // meanwhile, we wait until that finishes (no need to check // for more interrupts, the process will exit anyway). wake_up_all(&d->blockq); spin_unlock(&d->mutex); // Sanity check if(d->num_to_requeue > 0) { wait_event(d->blockq, d->num_to_requeue == 0); wake_up_all(&d->blockq); // Wake everyone up again to check for other pending signals } return -ERESTARTSYS; } } 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. r = try_acquire_file_lock(filp); if(r == -EDEADLK) r = -EBUSY; } 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. r = release_file_lock(filp); } 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 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). //eprintk("Attempting to acquire\n"); unsigned my_ticket; // TODO DEADLOCK DETECTION if(d->write_locking_pids->head!=NULL && current->pid == d->write_locking_pids->head->num){ return -EDEADLK; } osp_spin_lock(&(d->mutex)); my_ticket = d->ticket_head; d->ticket_head++; osp_spin_unlock(&(d->mutex)); if(filp_writable){ //write-lock //if the condition is true, the function returns 0 //otherwise, block, no value returned //if it receives a signal, it returns non-zero value //blockq stores processes that are waiting on the current device if(wait_event_interruptible(d->blockq,d->ticket_tail == my_ticket && d->write_locking_pids->size == 0 && d->read_locking_pids->size == 0)){ //don't give lock, enter the if statement only when you receive a signal //kill current process and store its ticket number into invalid ticket list //osp_spin_lock(&(d->mutex)); if(d->ticket_tail == my_ticket){ //the current process is the next one to have the lock, but we receive kill sginal //find the next valid ticket //return_valid_ticket function returns the next valid ticket d->ticket_tail = return_valid_ticket(d->invalid_tickets, d->ticket_tail+1); //wake_up_all(&(d->blockq)); } else{ add_to_ticket_list(d->invalid_tickets,my_ticket); //no need to change the ticket tail } //osp_spin_unlock(&(d->mutex)); return -ERESTARTSYS; } //wait_event_interruptible() returns 0 //the current process can get the lock on the device osp_spin_lock(&(d->mutex)); filp->f_flags |= F_OSPRD_LOCKED; add_to_ticket_list(d->write_locking_pids, current->pid); //increase ticket tail to be the next valid ticket d->ticket_tail = return_valid_ticket(d->invalid_tickets, d->ticket_tail+1); osp_spin_unlock(&(d->mutex)); wake_up_all(&(d->blockq)); return 0; } else{ //read-lock //multiple processes can have read lock on one device if(wait_event_interruptible(d->blockq,d->ticket_tail == my_ticket && d->write_locking_pids->size == 0)){ osp_spin_lock(&(d->mutex)); if(d->ticket_tail == my_ticket){ d->ticket_tail = return_valid_ticket(d->invalid_tickets, d->ticket_tail+1); } else{ add_to_ticket_list(d->invalid_tickets,my_ticket); } osp_spin_unlock(&(d->mutex)); return -ERESTARTSYS; } //wait_event_interruptible() returns 0 //the current process can get the lock on the device osp_spin_lock(&(d->mutex)); filp->f_flags |= F_OSPRD_LOCKED; add_to_ticket_list(d->read_locking_pids, current->pid); //increase ticket tail to be the next valid ticket d->ticket_tail = return_valid_ticket(d->invalid_tickets, d->ticket_tail+1); osp_spin_unlock(&(d->mutex)); wake_up_all(&(d->blockq)); 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). unsigned my_ticket; // TODO DEADLOCK DETECTION osp_spin_lock(&(d->mutex)); my_ticket = d->ticket_head; d->ticket_head++; osp_spin_unlock(&(d->mutex)); if(filp_writable){ //write-lock //if the condition is true, the function returns 0 //otherwise, block, no value returned //if it receives a signal, it returns non-zero value //blockq stores processes that are waiting on the current device if(!(d->blockq,d->ticket_tail == my_ticket && d->write_locking_pids->size == 0 && d->read_locking_pids->size == 0) ){ d->ticket_tail = return_valid_ticket(d->invalid_tickets, d->ticket_tail+1); return -EBUSY; } //wait_event_interruptible() returns 0 //the current process can get the lock on the device osp_spin_lock(&(d->mutex)); filp->f_flags |= F_OSPRD_LOCKED; add_to_ticket_list(d->write_locking_pids, current->pid); //increase ticket tail to be the next valid ticket d->ticket_tail = return_valid_ticket(d->invalid_tickets, d->ticket_tail+1); osp_spin_unlock(&(d->mutex)); } else{ //read-lock //multiple processes can have read lock on one device if(!(d->blockq,d->ticket_tail == my_ticket && d->write_locking_pids->size == 0) ){ d->ticket_tail = return_valid_ticket(d->invalid_tickets, d->ticket_tail+1); return -EBUSY; } //wait_event_interruptible() returns 0 //the current process can get the lock on the device osp_spin_lock(&(d->mutex)); filp->f_flags |= F_OSPRD_LOCKED; add_to_ticket_list(d->read_locking_pids, current->pid); //increase ticket tail to be the next valid ticket d->ticket_tail = return_valid_ticket(d->invalid_tickets, d->ticket_tail+1); osp_spin_unlock(&(d->mutex)); } wake_up_all(&d->blockq); return 0; //r = -ENOTTY; } 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) == 0) return -EINVAL; osp_spin_lock(&(d->mutex)); if (filp_writable) { remove_from_list(d->write_locking_pids,current->pid); } else { remove_from_list(d->read_locking_pids,current->pid); } filp->f_flags &= !F_OSPRD_LOCKED; osp_spin_unlock(&(d->mutex)); wake_up_all (&d->blockq); } else r = -ENOTTY; /* unknown command */ return r; }
void cause_deadlock(struct file *filp, osprd_info_t *d){ if (file2osprd(filp) == d){ //eprintk("deadsad: %d\n", d->dead); d->dead++; } }
/* * 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; }
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 cur_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 eprintk("%d\n", (int)current->pid); if (cmd == OSPRDIOCACQUIRE) { // EXERCISE: Lock the ramdisk. osp_spin_lock(&d->mutex); if (check_deadlock(d)) { //osp_spin_unlock(&d->mutex); //return -EDEADLK; r = -EDEADLK; } else { add_check_deadlock_list(current->pid, d); eprintk ("add_check_deadlock_list"); cur_ticket = d->ticket_head; d->ticket_head ++ ; } osp_spin_unlock(&d->mutex); if (r != 0) { return r; } if(filp_writable) { //osp_spin_lock(&d->mutex); int w = wait_event_interruptible(d->blockq, (d->number_write_lock==0&&d->number_read_lock==0&&cur_ticket==d->ticket_tail)); //Blocks the current task on a wait queue until a CONDITION becomes true. //A request for a write lock on a ramdisk file will block until no other files on that //ramdisk have a read or writeloc if(w == -ERESTARTSYS) { osp_spin_lock(&d->mutex); //if the process is interrupted by signal if (cur_ticket == d->ticket_tail) d->ticket_tail++; //already in the next avalable ticket else d->ticket_head--; //destory this ticket osp_spin_unlock(&d->mutex); return w; } osp_spin_lock(&d->mutex); //Acquire a mutex (lock the mutex) d->ticket_tail++; d->write_lock_holder = current->pid; d->number_write_lock = 1; filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); //Release (unlock) the mutex } else { int w = wait_event_interruptible(d->blockq, (d->number_write_lock==0&&cur_ticket==d->ticket_tail)); //Blocks the current task on a wait queue until a CONDITION becomes true. //A request for a write lock on a ramdisk file will block until no other files on that //ramdisk have a read or writeloc if(w == -ERESTARTSYS) { osp_spin_lock(&d->mutex); //if the process is interrupted by signal if (cur_ticket == d->ticket_tail) d->ticket_tail++; //already in the next avalable ticket else d->ticket_head--; osp_spin_unlock(&d->mutex); //destory this ticket return w; } osp_spin_lock(&d->mutex); //Acquire a mutex (lock the mutex) d->ticket_tail++; add_read_pid(current->pid,d); d->number_read_lock++; filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); //Release (unlock) the mutex } } 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). if (filp_writable) { //atomically acquire write lock osp_spin_lock (&d->mutex); //atomicity if ((d->number_read_lock >0) || (d->number_write_lock>0)) { osp_spin_unlock (&d->mutex); return -EBUSY; } else //d->number_read_lock ==0) && (d->number_write_lock==0) { d->write_lock_holder = current->pid; d->number_write_lock ++; d->ticket_tail++; d->ticket_head++; filp -> f_flags |= F_OSPRD_LOCKED; osp_spin_unlock (&d->mutex); } } else //opened for read { //atomically acquire read lock osp_spin_lock (&d->mutex); { if (d->number_write_lock>0) //can't get read lock { osp_spin_unlock(&d->mutex); return -EBUSY; } else { add_read_pid (current->pid,d); d->number_read_lock++; d->ticket_tail++; d->ticket_head++; filp -> f_flags |= F_OSPRD_LOCKED; osp_spin_unlock (&d->mutex); } } } //eprintk("Attempting to try acquire\n"); //r = -ENOTTY; } 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. //osp_spin_lock (&d->mutex); if ((filp->f_flags & F_OSPRD_LOCKED)==0) { //osp_spin_unlock (&d->mutex); return -EINVAL; } else { osp_spin_lock (&d->mutex); d->check_deadlock_list_head = list_remove_element(d->check_deadlock_list_head,current->pid); if (filp_writable) //release the write locker { d->write_lock_holder = -1; d->number_write_lock --; } else //release the read locker { d->number_read_lock --; d->pid_list_head = list_remove_element(d->pid_list_head,current->pid); /*if (list_free_all (pid_list_head) == -ENOTTY) return -ENOTTY;*/ if (d->pid_list_head == NULL) return -ENOTTY; } filp->f_flags &= ~F_OSPRD_LOCKED; osp_spin_unlock (&d->mutex); wake_up_all (&d->blockq); } // Your code here (instead of the next line). //r = -ENOTTY; } else r = -ENOTTY; /* unknown command */ return r; }
static int _osprd_release(struct inode *inode, struct file *filp) { if (file2osprd(filp)) osprd_close_last(inode, filp); return (*blkdev_release)(inode, filp); }
/* * 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) { printk("entered function\n"); osprd_info_t *d = file2osprd(filp); // device info if (!d) return -1; // bad filp 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 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?) // Protect critical section osp_spin_lock(&d->mutex); // Set local variable to 'd->ticket_head' and increment 'd->ticket_head' unsigned local_ticket = d->ticket_head++; // Deadlocks with itself if (current->pid == d->write_pid) { osp_spin_unlock(&d->mutex); return -EDEADLK; } // Check all read locks pid_list_t curr = d->read_pids; while (curr != NULL) { if (current->pid == curr->pid) { osp_spin_unlock(&d->mutex); return -EDEADLK; } else curr = curr->next; } osp_spin_unlock(&d->mutex); // If *filp is open for writing (filp_writable), then attempt // to write-lock the ramdisk; if (filp_writable) { printk("attempting to acquire write lock\n"); printk("head: %d | tail: %d\n", d->ticket_head, d->ticket_tail); // 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. // block at least until 'd->ticket_tail == local_ticket' int wait_return = wait_event_interruptible(d->blockq, d->num_write == 0 && d->num_read == 0 && d->ticket_tail >= local_ticket); printk("after wait return\n"); printk("head: %d | tail: %d\n", d->ticket_head, d->ticket_tail); if (wait_return == -ERESTARTSYS) { printk("ERESTARTSYS WRITE\n"); return -ERESTARTSYS; } // Protect critical section osp_spin_lock(&d->mutex); d->write_pid = current->pid; d->num_write++; d->ticket_tail++; printk("Updated write!!\n"); printk("head: %d | tail: %d\n", d->ticket_head, d->ticket_tail); osp_spin_unlock(&d->mutex); // If a process acquires a lock, mark this fact by setting // 'filp->f_flags |= F_OSPRD_LOCKED'. filp->f_flags |= F_OSPRD_LOCKED; } //otherwise attempt to read-lock the ramdisk. else { printk("attempting to acquire read lock\n"); printk("head: %d | tail: %d\n", d->ticket_head, d->ticket_tail); // 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. // block at least until 'd->ticket_tail == local_ticket' int wait_return = wait_event_interruptible(d->blockq, d->num_write == 0 && d->ticket_tail >= local_ticket); printk("after wait return\n"); printk("head: %d | tail: %d\n", d->ticket_head, d->ticket_tail); if (wait_return == -ERESTARTSYS) { printk("ERESTARTSYS READ\n"); return -ERESTARTSYS; } // Protect critical section osp_spin_lock(&d->mutex); // Add pid to end of list // This is the first entry if (d->read_pids == NULL) { d->read_pids = kmalloc(sizeof(pid_list_t), GFP_ATOMIC); d->read_pids->pid = current->pid; d->read_pids->next = NULL; } // Not first entry else { pid_list_t curr = d->read_pids; while (curr->next != NULL) curr = curr->next; curr->next = kmalloc(sizeof(pid_list_t), GFP_ATOMIC); curr->next->pid = current->pid; curr->next->next = NULL; } d->num_read++; d->ticket_tail++; printk("Updated tail and read!!\n"); printk("head: %d | tail: %d\n", d->ticket_head, d->ticket_tail); // If a process acquires a lock, mark this fact by setting // 'filp->f_flags |= F_OSPRD_LOCKED'. filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); } //eprintk("Attempting to acquire\n"); 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"); // Protect critical section osp_spin_lock(&d->mutex); // Deadlocks with itself if (current->pid == d->write_pid) { osp_spin_unlock(&d->mutex); eprintk("deadlock with write lock\n"); return -EBUSY; } // Check all read locks pid_list_t curr = d->read_pids; while (curr != NULL) { if (current->pid == curr->pid) { eprintk("deadlock with read lock\n"); osp_spin_unlock(&d->mutex); return -EBUSY; } curr = curr->next; } osp_spin_unlock(&d->mutex); // Write lock request if (filp_writable) { // Protect critical section osp_spin_lock(&d->mutex); // Lock request can be satisfied if (d->num_write == 0 && d->num_read == 0) { d->num_write++; // If a process acquires a lock, mark this fact by setting // 'filp->f_flags |= F_OSPRD_LOCKED'. filp->f_flags |= F_OSPRD_LOCKED; } // OSPRDIOCACQUIRE would block if conditions above not met else { osp_spin_unlock(&d->mutex); eprintk("busy instead of blocking write\n"); return -EBUSY; } osp_spin_unlock(&d->mutex); } // Read lock request else { // Protect critical section osp_spin_lock(&d->mutex); if (d->num_write == 0) { // Add pid to end of list // This is the first entry if (d->read_pids == NULL) { d->read_pids = kmalloc(sizeof(pid_list_t), GFP_ATOMIC); d->read_pids->pid = current->pid; d->read_pids->next = NULL; } // Not first entry else { pid_list_t curr = d->read_pids; while (curr->next != NULL) curr = curr->next; curr->next = kmalloc(sizeof(pid_list_t), GFP_ATOMIC); curr->next->pid = current->pid; curr->next->next = NULL; } d->num_read++; // If a process acquires a lock, mark this fact by setting // 'filp->f_flags |= F_OSPRD_LOCKED'. filp->f_flags |= F_OSPRD_LOCKED; osp_spin_unlock(&d->mutex); } // OSPRDIOCACQUIRE would block if conditions above not met else { printk("BUSY instead of blocking read\n"); osp_spin_unlock(&d->mutex); return -EBUSY; } } 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. printk("attempting to release\n"); // If the file hasn't locked the ramdisk, return -EINVAL. if ((filp->f_flags & F_OSPRD_LOCKED) == 0) 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. else { osp_spin_lock(&d->mutex); if (filp_writable) { d->num_write--; d->write_pid = -1; } else { d->num_read--; pid_list_t curr = d->read_pids; // If first on the list if (current->pid == curr->pid) d->read_pids = curr->next; else { while (curr->next != NULL) { // Find pid to skip over and connect if (current->pid == curr->next->pid) { curr->next = curr->next->next; break; } else curr = curr->next; } } } filp->f_flags &= !F_OSPRD_LOCKED; wake_up_all(&d->blockq); osp_spin_unlock(&d->mutex); } r = 0; } else r = -ENOTTY; /* unknown command */ return r; }