/* Returns nonzero if this process's lock is released, and 0 if this process did not hold a lock. */ static int try_release_lock(osprd_info_t *d, struct file *filp, int filp_writable) { if (d->write_lock_pid == current->pid) { if (!filp_writable) eprintk("How did I acquire this write lock??"); d->write_lock_pid = -1; filp->f_flags &= ~F_OSPRD_LOCKED; } else if (int_list_contains(d->read_lock_list, current->pid)) { int_list_remove(d->read_lock_list, current->pid); if (int_list_empty(d->read_lock_list)) /* Set to unlocked if no other read locks */ filp->f_flags &= ~F_OSPRD_LOCKED; } else /* No locks held by this process */ { return 0; } return 1; }
/* * 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; }
char *test_empty(){ IntList *il = create_int_list(); mu_assert(int_list_empty(il), "La lista de enteros esta vacia"); return NULL; }