int acquire_a_local_lock(state_type *state, time_type timeout, int timeout_event_type, event_content_type *event_content, double now) { SERVER_lp_state_type *pointer = &state->type.server_state; if (pointer->configuration.cc_verbose) printf("cc%d - oggetto %d per la transizione %i da lockare\n", pointer->server_id, event_content->applicative_content.object_key_id, event_content->applicative_content.tx_id); //check lock... if (pointer->cc_metadata->locks[event_content->applicative_content.object_key_id] == -1) { //not locked pointer->cc_metadata->lock_retry_num = 0; //acquire lock pointer->cc_metadata->locks[event_content->applicative_content.object_key_id] = event_content->applicative_content.tx_id; if (pointer->configuration.cc_verbose) printf("cc%d - oggetto %d per la transizione %i lockato al tempo %f \n", pointer->server_id, event_content->applicative_content.object_key_id, event_content->applicative_content.tx_id, now); return 1; } else if (pointer->cc_metadata->locks[event_content->applicative_content.object_key_id] == event_content->applicative_content.tx_id) { // already locked by me return 1; } else { //already locked by another transaction pointer->cc_metadata->lock_retry_num++; //check deadlocks (if enabled) if (pointer->configuration.deadlock_detection_enabled && check_deadlock(event_content, pointer)) { return -1; } //add the timeout event event_content_type new_event_content; memcpy(&new_event_content, event_content, sizeof(event_content_type)); ScheduleNewEvent(pointer->server_id, now + timeout, timeout_event_type, &new_event_content, sizeof(event_content_type)); //enqueue event memcpy(&new_event_content, event_content, sizeof(event_content_type)); new_event_content.applicative_content.object_key_id = event_content->applicative_content.object_key_id; enqueue_event(pointer, &new_event_content); transaction_metadata *transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer); if (transaction == NULL) { if (pointer->configuration.cc_verbose) { printf("cc%d - la tx %i non è locale\n", pointer->server_id, event_content->applicative_content.tx_id); printf("cc%d - prepare of tx %i added in the waiting event queue %f due to a lock on object :%d tx:%i\n", pointer->server_id, event_content->applicative_content.tx_id, now, event_content->applicative_content.object_key_id, new_event_content.applicative_content.tx_id); } return 0; } else { transaction->is_blocked = 1; if (pointer->configuration.cc_verbose) printf("cc%d - tx %i is waiting at time %f due to a lock lock on object :%d tx:%i\n", pointer->server_id, event_content->applicative_content.tx_id, now, event_content->applicative_content.object_key_id, new_event_content.applicative_content.tx_id); return 0; } } }
/* * osprd_ioctl(inode, filp, cmd, arg) * Called to perform an ioctl on the named file. */ int osprd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { 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; }
int acquire_local_locks(state_type *state, data_set_entry *data_set, time_type timeout, int timeout_event_type, event_content_type *event_content, double now) { SERVER_lp_state_type *pointer = &state->type.server_state; data_set_entry *entry = data_set; if (entry == NULL) { if (pointer->configuration.cc_verbose) printf("cc%d - write set of transaction %d is empty\n", pointer->server_id, event_content->applicative_content.tx_id); return 1; } while (entry != NULL) { int need_to_lock = 0; if (pointer->configuration.concurrency_control_type == ETL_2PL || pointer->configuration.concurrency_control_type == CTL_2PL) need_to_lock = is_owner(entry->object_key_id, pointer->server_id, state->num_servers, state->num_clients, state->object_replication_degree); else if (pointer->configuration.concurrency_control_type == PRIMARY_OWNER_CTL_2PL) need_to_lock = is_primary(entry->object_key_id, pointer->server_id, state->num_servers, state->num_clients); if (need_to_lock) { if (pointer->configuration.cc_verbose) printf("cc%d - object %d for transaction %i to be locked\n", pointer->server_id, entry->object_key_id, event_content->applicative_content.tx_id); //check lock... if (pointer->cc_metadata->locks[entry->object_key_id] == -1) { //not locked pointer->cc_metadata->lock_retry_num = 0; //acquire lock pointer->cc_metadata->locks[entry->object_key_id] = event_content->applicative_content.tx_id; if (pointer->configuration.cc_verbose) printf("cc%d - pbject %d for transaction %i locked at time %f \n", pointer->server_id, entry->object_key_id, event_content->applicative_content.tx_id, now); } else if (pointer->cc_metadata->locks[entry->object_key_id] == event_content->applicative_content.tx_id) { // already locked by me // go to the next entry } else { //already locked by another transaction pointer->cc_metadata->lock_retry_num++; //check deadlock (if enabled) if (pointer->configuration.deadlock_detection_enabled && check_deadlock(event_content, pointer)) { return -1; } //add the timeout event event_content_type new_event_content; memcpy(&new_event_content, event_content, sizeof(event_content_type)); ScheduleNewEvent(pointer->server_id, now + timeout, timeout_event_type, &new_event_content, sizeof(event_content_type)); //enqueue transaction memcpy(&new_event_content, event_content, sizeof(event_content_type)); new_event_content.applicative_content.object_key_id = entry->object_key_id; enqueue_event(pointer, &new_event_content); transaction_metadata *transaction = get_transaction_metadata(event_content->applicative_content.tx_id, pointer); if (transaction == NULL) { if (pointer->configuration.cc_verbose) { printf("cc%d - transaction %i is not local\n", pointer->server_id, event_content->applicative_content.tx_id); printf("cc%d - prepare of tx %i added in the waiting event queue %f due to a lock on object %d tx:%i\n", pointer->server_id, event_content->applicative_content.tx_id, now, entry->object_key_id, new_event_content.applicative_content.tx_id); } return 0; } else { transaction->is_blocked = 1; if (pointer->configuration.cc_verbose) printf("cc%d - transaction %i is waiting at time %f due to a lock on object%d tx:%i\n", pointer->server_id, event_content->applicative_content.tx_id, now, entry->object_key_id, new_event_content.applicative_content.tx_id); return 0; } } } entry = entry->next; } return 1; }
/* * osprd_ioctl(inode, filp, cmd, arg) * Called to perform an ioctl on the named file. */ int osprd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { osprd_info_t *d = file2osprd(filp); // device info int r = 0; // return value: initially 0 // is file open for writing? int filp_writable = (filp->f_mode & FMODE_WRITE) != 0; // Set 'r' to the ioctl's return value: 0 on success, negative on error if (cmd == OSPRDIOCACQUIRE) { // EXERCISE: Lock the ramdisk. // // If *filp is open for writing (filp_writable), then attempt // to write-lock the ramdisk; otherwise attempt to read-lock // the ramdisk. // // This lock request must block using 'd->blockq' until: // 1) no other process holds a write lock; // 2) either the request is for a read lock, or no other process // holds a read lock; and // 3) lock requests should be serviced in order, so no process // that blocked earlier is still blocked waiting for the // lock. // // If a process acquires a lock, mark this fact by setting // 'filp->f_flags |= F_OSPRD_LOCKED'. You also need to // keep track of how many read and write locks are held: // change the 'osprd_info_t' structure to do this. // // Also wake up processes waiting on 'd->blockq' as needed. // // If the lock request would cause a deadlock, return -EDEADLK. // If the lock request blocks and is awoken by a signal, then // return -ERESTARTSYS. // Otherwise, if we can grant the lock request, return 0. // 'd->ticket_head' and 'd->ticket_tail' should help you // service lock requests in order. These implement a ticket // order: 'ticket_tail' is the next ticket, and 'ticket_head' // is the ticket currently being served. You should set a local // variable to 'd->ticket_head' and increment 'd->ticket_head'. // Then, block at least until 'd->ticket_tail == local_ticket'. // (Some of these operations are in a critical section and must // be protected by a spinlock; which ones?) 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; }
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; }