示例#1
0
文件: osprd.c 项目: klcathy/cs111
void checkLocks(struct file *filp, osprd_info_t *d)
{
    osprd_info_t *other;

    if ((other = file2osprd(filp)) != NULL)
    {
        if (pidInList(other->readLockingPids, current->pid) || pidInList(other->writeLockingPids, current->pid))
        {
            d->holdOtherLocks = 1;
            return;
        }
    }
}
示例#2
0
// 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.
		osp_spin_lock(&(d->mutex));

		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);
		}

		if (d->readLockingPids == NULL && d->writeLockingPids == NULL) {
			filp->f_flags &= !F_OSPRD_LOCKED;
		}
		
		osp_spin_unlock(&(d->mutex));
		wake_up_all(&(d->blockq));

		// This line avoids compiler warnings; you may remove it.
		(void) filp_writable, (void) d;

	}

	return 0;
}
示例#3
0
文件: osprd.c 项目: klcathy/cs111
// 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);

        if (d == NULL)
            return 1;

        osp_spin_lock(&(d->mutex));

        // 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);
        }

        // 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;
}
示例#4
0
文件: osprd.c 项目: klcathy/cs111
/*
 * 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;
}