Esempio n. 1
0
// This function is called when a /dev/osprdX file is opened.
// You aren't likely to need to change this.
static int osprd_open(struct inode *inode, struct file *filp)
{
	// Always set the O_SYNC flag. That way, we will get writes immediately
	// instead of waiting for them to get through write-back caches.
	osprd_info_t *d = file2osprd(filp);
	filp->f_flags |= O_SYNC;
	for_each_open_file(current, count_ramdisks_hook, d);
	return 0;
}
Esempio n. 2
0
/*
 * osprd_ioctl(inode, filp, cmd, arg)
 *   Called to perform an ioctl on the named file.
 */
int osprd_ioctl(struct inode *inode, struct file *filp,
                unsigned int cmd, unsigned long arg)
{
    osprd_info_t *d = file2osprd(filp);	// device info
    int r = 0;			// return value: initially 0
    unsigned myTicket;

    // is file open for writing?
    int filp_writable = (filp->f_mode & FMODE_WRITE) != 0;

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

    // Set 'r' to the ioctl's return value: 0 on success, negative on error
    if (cmd == OSPRDIOCACQUIRE) {

        // EXERCISE: Lock the ramdisk.
        //
        // If *filp is open for writing (filp_writable), then attempt
        // to write-lock the ramdisk; otherwise attempt to read-lock
        // the ramdisk.
        //
        // This lock request must block using 'd->blockq' until:
        // 1) no other process holds a write lock;
        // 2) either the request is for a read lock, or no other process
        //    holds a read lock; and
        // 3) lock requests should be serviced in order, so no process
        //    that blocked earlier is still blocked waiting for the
        //    lock.
        //
        // If a process acquires a lock, mark this fact by setting
        // 'filp-void checkLocks(struct file *filp, osprd_info_t *d)>f_flags |= F_OSPRD_LOCKED'.  You also need to
        // keep track of how many read and write locks are held:
        // change the 'osprd_info_t' structure to do this.
        //
        // Also wake up processes waiting on 'd->blockq' as needed.
        //
        // If the lock request would cause a deadlock, return -EDEADLK.
        // If the lock request blocks and is awoken by a signal, then
        // return -ERESTARTSYS.
        // Otherwise, if we can grant the lock request, return 0.

        // 'd->ticket_head' and 'd->ticket_tail' should help you
        // service lock requests in order.  These implement a ticket
        // order: 'ticket_tail' is the next ticket, and 'ticket_head'
        // is the ticket currently being served.  You should set a local
        // variable to 'd->ticket_head' and increment 'd->ticket_head'.
        // Then, block at least until 'd->ticket_tail == local_ticket'.
        // (Some of these operations are in a critical section and must
        // be protected by a spinlock; which ones?)


        /*
        TUAN: Ticket is used to service lock requests in order. Each process maintains a unique
        local_ticket, starting at ticket_head. To obtain a unique local ticket, each process
        atomically set its local_ticket equal to ticket_head and then incremenet the ticket_head.
        Whichever process grasps the lock, it will get the next value of ticket_head and again
        atomically increments ticket_head. Eventually, we have a list of processes with ticket
        0, 1, 2, 3.

        ticket_tail starts at 0. A process cannot obtain the lock on the RAM disk if its local ticket
        does not match ticket_tail. Since process 0 has local_ticket equal to ticket_tail which is 0,
        only process 0 can obtain the lock. Before process 0 releases the lock, it increments ticket_tail
        by 1 and release the lock. Since now process 1 has its local_ticket equal to ticket_tail which is
        now 1, it can grasb  the lock...
        */


        //requested a WRITE lock
        if (filp_writable) {

            //get a ticket
            osp_spin_lock(&(d->mutex));
            myTicket = d->ticket_head;
            d->ticket_head++;

            //Check for deadlock - if I have previous read lock and will have to wait
            if (pidInList(d->readLockingPids, current->pid)) {
                osp_spin_unlock(&(d->mutex));
                return -EDEADLK;
            }

            /*
            TUAN: It is considered deadlock to request the same write lock that you already hold
            in your current RAM disk.
            */
            if (pidInList(d->writeLockingPids, current->pid)) {
                osp_spin_unlock(&(d->mutex));
                return -EDEADLK;
            }

            osp_spin_unlock(&(d->mutex));

            /*
            TUAN: wait_event_interruptible. The first argument is the wait queue. The second argument is the condition to wake up.
            The process wakes up when the condition is true or a signal is received.
            The function returns 0 if the condition is true. Return -ERESTARTSYS if a signal is received.
            */

            //block until all conditions are met
            if (wait_event_interruptible(d->blockq, d->ticket_tail==myTicket && d->writeLockingPids == NULL && d->readLockingPids == NULL)) {

                //I encountered a signal, return error condition
                if (d->ticket_tail == myTicket) {
                    grantTicketToNextAliveProcessInOrder(d); //Tuan define this
                }
                else  {  //mark my ticket as not usable before process exits
                    addToTicketList(&(d->exitedTickets), myTicket);
                    //TUAN: this is important because when other process grants the ticket
                    //It makes sure it not grant the ticket to processes that already exited.
                    //It do that by incrementing ticket_tail and make sure ticket_tail not
                    //match the already exited ticket.
                }
                return -ERESTARTSYS; //TUAN: means your system call is restartable. The process is considered exited/died.
            }

            //if I arrive here, I have the ticket to proceed, and no one else holds a read or write lock
            osp_spin_lock(&(d->mutex));

            //claim the lock officially
            filp->f_flags |= F_OSPRD_LOCKED;

            //TUAN: we keep track of the ID processes that are holding the write lock
            //Later on, to detect deadlock for the current process, we look up this list
            //to see if we already have the read or write lock there.
            addToList(&(d->writeLockingPids), current->pid);

            //TUAN: find next usable ticket number so that the next in-order alive process can use
            grantTicketToNextAliveProcessInOrder(d);

            osp_spin_unlock(&(d->mutex));
            wake_up_all(&(d->blockq)); //TUAN: wait up all processes in the wait queue d->blockq and evaluate the condition
            //in wait_event_interruptable for those processes that go to sleep when invoking this function.
            return 0;
        }

        //requested a READ lock
        else {
            //get a ticket
            osp_spin_lock(&(d->mutex));
            myTicket = d->ticket_head;
            d->ticket_head++;

            //Check for deadlock - if I have previous write lock and will have to wait
            if (pidInList(d->writeLockingPids, current->pid)) {
                osp_spin_unlock(&(d->mutex));
                return -EDEADLK;
            }

            /*
            TUAN: It is considered deadlock to request the same read lock that you already hold
            in your current RAM disk.
            */
            if (pidInList(d->readLockingPids, current->pid)) {
                osp_spin_unlock(&(d->mutex));
                return -EDEADLK;
            }

            osp_spin_unlock(&(d->mutex));

            //block until all conditions are met
            if (wait_event_interruptible(d->blockq, d->ticket_tail==myTicket && d->writeLockingPids == NULL)) {
                //I encountered a signal, return error condition
                if (d->ticket_tail == myTicket) {
                    grantTicketToNextAliveProcessInOrder(d);
                }
                else { //add my ticket to non-usable ticket numbers
                    addToTicketList(&(d->exitedTickets), myTicket);
                }

                return -ERESTARTSYS;
            }

            //if I arrive here, I have the ticket to proceed, and no one else holds a read or write lock
            osp_spin_lock(&(d->mutex));

            //claim the lock officially
            filp->f_flags |= F_OSPRD_LOCKED;
            addToList(&(d->readLockingPids), current->pid);

            //TUAN: find next usable ticket number so that the next in-order alive process can use
            grantTicketToNextAliveProcessInOrder(d);

            osp_spin_unlock(&(d->mutex));
            wake_up_all(&(d->blockq)); //TUAN: wake up all the processes that are waiting for the ticket
            //by setting the processes in the run queue to runnable state.
            return 0;
        }

    } else if (cmd == OSPRDIOCTRYACQUIRE) {
        // EXERCISE: ATTEMPT to lock the ramdisk.
        //
        // This is just like OSPRDIOCACQUIRE, except it should never
        // block.  If OSPRDIOCACQUIRE would block or return deadlock,
        // OSPRDIOCTRYACQUIRE should return -EBUSY.
        // Otherwise, if we can grant the lock request, return 0.
        // Your code here (instead of the next two lines).

        // WRITE lock
        if (filp_writable)
        {
            // Get a ticket
            osp_spin_lock(&(d->mutex));
            myTicket = d->ticket_head;
            d->ticket_head++;

            // Check for deadlock
            if (pidInList(d->readLockingPids, current->pid))
            {
                grantTicketToNextAliveProcessInOrder(d);
                osp_spin_unlock(&(d->mutex));
                return -EBUSY;
            }

            for_each_open_file(current, checkLocks, d);

            if (d->holdOtherLocks)
            {
                d->holdOtherLocks = 0;
                grantTicketToNextAliveProcessInOrder(d);
                osp_spin_unlock(&(d->mutex));
                return -EBUSY;
            }

            if (pidInList(d->writeLockingPids, current->pid))
            {
                grantTicketToNextAliveProcessInOrder(d);
                osp_spin_unlock(&(d->mutex));
                return -EINVAL;
            }

            osp_spin_unlock(&(d->mutex));

            if (d->writeLockingPids != NULL || d->readLockingPids != NULL)
            {
                grantTicketToNextAliveProcessInOrder(d);
                return -EBUSY;
            }

            osp_spin_lock(&(d->mutex));
            filp->f_flags |= F_OSPRD_LOCKED;
            addToList(&(d->writeLockingPids), current->pid);
            grantTicketToNextAliveProcessInOrder(d);

            osp_spin_unlock(&(d->mutex));
            wake_up_all(&(d->blockq));
            return 0;
        }
        else
        {
            osp_spin_lock(&(d->mutex));
            myTicket = d->ticket_head;
            d->ticket_head++;

            if (pidInList(d->writeLockingPids, current->pid))
            {
                grantTicketToNextAliveProcessInOrder(d);
                osp_spin_unlock(&(d->mutex));
                return -EBUSY;
            }

            for_each_open_file(current, checkLocks, d);

            if (d->holdOtherLocks)
            {
                d->holdOtherLocks = 0;
                grantTicketToNextAliveProcessInOrder(d);
                osp_spin_unlock(&(d->mutex));
                return -EBUSY;
            }

            if (pidInList(d->readLockingPids, current->pid))
            {
                grantTicketToNextAliveProcessInOrder(d);
                osp_spin_unlock(&(d->mutex));
                return -EINVAL;
            }

            osp_spin_unlock(&(d->mutex));

            if (d->writeLockingPids != NULL)
            {
                grantTicketToNextAliveProcessInOrder(d);
                return -EBUSY;
            }

            osp_spin_lock(&(d->mutex));
            filp->f_flags |= F_OSPRD_LOCKED;
            addToList(&(d->readLockingPids), current->pid);
            grantTicketToNextAliveProcessInOrder(d);
            osp_spin_unlock(&(d->mutex));
            wake_up_all(&(d->blockq));
            return 0;
        }




    } else if (cmd == OSPRDIOCRELEASE) {

        // EXERCISE: Unlock the ramdisk.
        //
        // If the file hasn't locked the ramdisk, return -EINVAL.
        // Otherwise, clear the lock from filp->f_flags, wake up
        // the wait queue, perform any additional accounting steps
        // you need, and return 0.

        // Your code here (instead of the next line).

        osp_spin_lock(&(d->mutex));

        // TUAN: If the file hasn't locked the ramdisk, return -EINVAL
        if (!pidInList(d->writeLockingPids, current->pid) && !(pidInList(d->readLockingPids, current->pid))) {
            osp_spin_unlock(&(d->mutex));
            return -EINVAL;
        }

        if (pidInList(d->writeLockingPids, current->pid)) {
            removeFromList(&(d->writeLockingPids), current->pid);
        }

        if (pidInList(d->readLockingPids, current->pid)) {
            removeFromList(&(d->readLockingPids), current->pid);
        }

        // TUAN: Clear the lock from filp->f_flags if no processes (not just current process) hold any lock.
        if (d->readLockingPids == NULL && d->writeLockingPids == NULL) {
            filp->f_flags &= !F_OSPRD_LOCKED;
        }

        osp_spin_unlock(&(d->mutex));
        wake_up_all(&(d->blockq));

        return 0;

    } else
        r = -ENOTTY; /* unknown command */
    return r;
}
Esempio n. 3
0
/*
 * osprd_ioctl(inode, filp, cmd, arg)
 *   Called to perform an ioctl on the named file.
 */
int osprd_ioctl(struct inode *inode, struct file *filp,
		unsigned int cmd, unsigned long arg)
{
	osprd_info_t *d = file2osprd(filp);	// device info
	int r = 0;			// return value: initially 0

	// 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;
}
Esempio n. 4
0
/*
 * osprd_ioctl(inode, filp, cmd, arg)
 *   Called to perform an ioctl on the named file.
 */
int osprd_ioctl(struct inode *inode, struct file *filp,
		unsigned int cmd, unsigned long arg)
{
	osprd_info_t *d = file2osprd(filp);	// device info
	int r = 0;			// return value: initially 0
	unsigned local_ticket;
	// is file open for writing?
	int filp_writable = (filp->f_mode & FMODE_WRITE) != 0;

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

	// Set 'r' to the ioctl's return value: 0 on success, negative on error

	if (cmd == OSPRDIOCACQUIRE) {

		// EXERCISE: Lock the ramdisk.g
		//
		// If *filp is open for writing (filp_writable), then attempt
		// to write-lock the ramdisk; otherwise attempt to read-lock
		// the ramdisk.
		//
                // This lock request must block using 'd->blockq' until:
		// 1) no other process holds a write lock;
		// 2) either the request is for a read lock, or no other process
		//    holds a read lock; and
		// 3) lock requests should be serviced in order, so no process
		//    that blocked earlier is still blocked waiting for the
		//    lock.
		//
		// If a process acquires a lock, mark this fact by setting
		// 'filp->f_flags |= F_OSPRD_LOCKED'.  You also need to
		// keep track of how many read and write locks are held:
		// change the 'osprd_info_t' structure to do this.
		//
		// Also wake up processes waiting on 'd->blockq' as needed.
		//
		// If the lock request would cause a deadlock, return -EDEADLK.
		// If the lock request blocks and is awoken by a signal, then
		// return -ERESTARTSYS.
		// Otherwise, if we can grant the lock request, return 0.

		// 'd->ticket_head' and 'd->ticket_tail' should help you
		// service lock requests in order.  These implement a ticket
		// order: 'ticket_tail' is the next ticket, and 'ticket_head'
		// is the ticket currently being served.  You should set a local
		// variable to 'd->ticket_head' and increment 'd->ticket_head'.
		// Then, block at least until 'd->ticket_tail == local_ticket'.
		// (Some of these operations are in a critical section and must
		// be protected by a spinlock; which ones?)

		// Your code here (instead of the next two lines).
		//eprintk("Attempting to acquire\n");
		
		// Block
		osp_spin_lock(&(d->mutex));
		local_ticket = d->ticket_head;
		d->ticket_head++;
		osp_spin_unlock(&(d->mutex));
		
		// wait_event_interruptible returns a nonzero value if
		// interrupted by a signal, so return -ERESTARTSYS if it does.	
		for_each_open_file(current, cause_deadlock, d);
		        
		if (d->dead > 1 && (filp->f_flags & F_OSPRD_LOCKED))
			return -EDEADLK;

		if (wait_event_interruptible(d->blockq, d->n_writel == 0
			&& (!filp_writable || d->n_readl == 0)
			&& d->ticket_tail == local_ticket))
			{
				//eprintk ("INTERRUPTED! Head: %u Tail: %u Local: %u\n", d->ticket_head, d->ticket_tail, local_ticket);
				// If this process wasn't the one being served, don't consider the ticket to increment.
				if (d->ticket_tail == local_ticket)
					d->ticket_tail++;
				else d->desync++;
				return -ERESTARTSYS;
			}
		osp_spin_lock(&(d->mutex));
		
		d->dead = 0;
		
		if (d->mutex.lock>0)
			r = 0;
		filp->f_flags |= F_OSPRD_LOCKED;
		if (filp_writable)
			{ d->n_writel++; d->ticket_tail++; }
		else
			{ d->n_readl++; }
		
		osp_spin_unlock(&(d->mutex));
		//wake_up_all(&d->blockq);	
		
		if (!filp_writable)
			d->ticket_tail++;

		r = 0;

	} else if (cmd == OSPRDIOCTRYACQUIRE) {

		// EXERCISE: ATTEMPT to lock the ramdisk.
		//
		// This is just like OSPRDIOCACQUIRE, except it should never
		// block.  If OSPRDIOCACQUIRE would block or return deadlock,
		// OSPRDIOCTRYACQUIRE should return -EBUSY.
		// Otherwise, if we can grant the lock request, return 0.

		// Your code here (instead of the next two lines).
		//eprintk("Attempting to try acquire\n");
		local_ticket = d->ticket_head;
		// Check for an existing lock.
		if (filp->f_flags & F_OSPRD_LOCKED || d->n_writel != 0
			|| (filp_writable && d->n_readl != 0)
			|| d->ticket_tail != local_ticket)
		{ r = -EBUSY;} //eprintk("Stopped\n");}
		
		// If *filp is open for writing (filp_writable), then attempt
		// to write-lock the ramdisk; otherwise attempt to read-lock
		// the ramdisk.
		else 
		{
			osp_spin_lock(&(d->mutex));
			d->ticket_head++;
			filp->f_flags |= F_OSPRD_LOCKED;
			if (filp_writable)
			{ d->n_writel++; }
			else
			{ d->n_readl++; }
			if(d->ticket_tail < d->ticket_head)
				d->ticket_tail++;
			osp_spin_unlock(&(d->mutex));
			r = 0;
			wake_up_all(&d->blockq);
		 }

		// Also wake up processes waiting on 'd->blockq' as needed.
		//
		// If the lock request would cause a deadlock, return -EDEADLK.
		// Otherwise, if we can grant the lock request, return 0.

		//r = -ENOTTY;

	} else if (cmd == OSPRDIOCRELEASE) {

		// EXERCISE: Unlock the ramdisk.
		//
		// If the file hasn't locked the ramdisk, return -EINVAL.
		if (!(filp->f_flags & F_OSPRD_LOCKED))
			{r = -EINVAL; }
		
		// Otherwise, clear the lock from filp->f_flags, wake up
		// the wait queue, perform any additional accounting steps
		// you need, and return 0.
		else 
		{
			// Clear lock flag.
			osp_spin_lock(&(d->mutex));
			filp->f_flags &= ~F_OSPRD_LOCKED;
			
			d->n_writel = 0;
			d->n_readl = 0;
			
			osp_spin_unlock(&(d->mutex));
			// Wake queue.
			
			wake_up_all(&d->blockq);		
			
			// Return.
			r = 0;

		}
		

	} else
		r = -ENOTTY; /* unknown command */
	return r;
}
Esempio n. 5
0
/*
 * osprd_ioctl(inode, filp, cmd, arg)
 *   Called to perform an ioctl on the named file.
 */
int osprd_ioctl(struct inode *inode, struct file *filp,
		unsigned int cmd, unsigned long arg)
{
	osprd_info_t *d = file2osprd(filp);	// device info
	int r = 0;			// return value: initially 0

	// 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;
}
Esempio n. 6
0
/*
 * osprd_ioctl(inode, filp, cmd, arg)
 *   Called to perform an ioctl on the named file.
 */
int osprd_ioctl(struct inode *inode, struct file *filp,
		unsigned int cmd, unsigned long arg)
{
	osprd_info_t *d = file2osprd(filp);	// device info
	int r = 0;			// return value: initially 0
	unsigned 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;
}