Beispiel #1
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.
        
        //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;
}
Beispiel #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)
{

	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;
}
Beispiel #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
	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;
}
Beispiel #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.	
		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;
}
Beispiel #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

    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;
}
Beispiel #6
0
/*
 * 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;
}
Beispiel #7
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;
}
Beispiel #8
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
    
    
    
	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;
}
Beispiel #9
0
void dead_lock(struct file* filp, osprd_info_t* d) {
    if (file2osprd(filp) == d) {
        d->deadlock++;
        return;
    } else return;
}
Beispiel #10
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

	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;
}
Beispiel #11
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?)

        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;
}
Beispiel #12
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
  
  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;
}
Beispiel #13
0
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++;
}
Beispiel #14
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 = 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;
}
Beispiel #15
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
    
	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;
}
Beispiel #16
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;
}
Beispiel #17
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
    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;
}
Beispiel #18
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
		
	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 */
}
Beispiel #19
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; 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;
}
Beispiel #20
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

        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;
}
Beispiel #21
0
void cause_deadlock(struct file *filp, osprd_info_t *d){
	if (file2osprd(filp) == d)
           d->dead++;
}
Beispiel #22
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

	// 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;
}
Beispiel #23
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

	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;
}
Beispiel #24
0
void cause_deadlock(struct file *filp, osprd_info_t *d){
	if (file2osprd(filp) == d){
		//eprintk("deadsad: %d\n", d->dead);           
		d->dead++;
	}
}
Beispiel #25
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;
}
Beispiel #26
0
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;

}
Beispiel #27
0
static int _osprd_release(struct inode *inode, struct file *filp)
{
	if (file2osprd(filp))
		osprd_close_last(inode, filp);
	return (*blkdev_release)(inode, filp);
}
Beispiel #28
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)
{
  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;
}