📄 scsi_target.c
字号:
case TMF_TARGET_WARM_RESET: case TMF_TARGET_COLD_RESET: { spin_lock_irqsave(&target_data.cmd_queue_lock, flags); list_for_each_entry(cmd_curr,&target_data.cmd_queue,link) { scsi_release(cmd_curr); } spin_unlock_irqrestore(&target_data.cmd_queue_lock, flags); aen_notify(msg->message, 0); break; } default: { printk("%s Bad message code %d\n", current->comm, msg->message); break; } } /* switch */ kfree(msg); msg = NULL; }# ifdef DEBUG_SCSI_THREAD printk("%s Searching command queue\n", current->comm);# endif /* There is a harmless race here. * This loop is the ONLY place a command can be removed from the queue. * So once we get lptr, it cannot be made invalid elsewhere. * However, if a new element is added to the end of the queue * (the ONLY place new elements are ever added) as lptr is being * picked up or as next is being picked up or after next has been * picked up, then we might not see this new element on the * next iteration of this loop. * That should not cause a problem, because the ONLY place a new * command can be added to the queue is in rx_cmnd(), and after * adding the new command to the queue, rx_cmnd() also does an "up" * on target_sem, which will restart the outer loop of this thread * and we will be back into this loop again in no time. * Why do we say this race is harmless? * The loop initialization generated by list_for_each_safe: * lptr = (&target_data.cmd_queue)->next * and the code: * next = lptr->next; * will always work properly, because list_add_tail() always sets * the referenced "next" field last when adding a new element to * the queue (i.e., AFTER all other pointer fields are set up * correctly). Therefore, there is a race with list_add_tail(), * but we always get a pointer to a valid structure (either the head * of the list, in which case we don't process the new element, or * the new element which is completely filled in, in which case we * do process the new element). */ list_for_each_safe(lptr, next, &target_data.cmd_queue) {# ifdef DEBUG_SCSI_THREAD printk("%s lptr %p next %p\n", current->comm, lptr, next);# endif cmd_curr = list_entry(lptr, Target_Scsi_Cmnd, link);# ifdef DEBUG_SCSI_THREAD printk("%s cmd_curr %p\n", current->comm, cmd_curr);# endif# ifdef DEBUG_SCSI_THREAD printk("%s command %p id %d status %d\n", current->comm, cmd_curr, cmd_curr->id, cmd_curr->state);# endif /* is command received */ if (cmd_curr->state == ST_NEW_CMND) {# ifdef DEBUG_SCSI_THREAD printk("%s New command %p id: %d\n", current->comm, cmd_curr, cmd_curr->id);# endif lun = cmd_curr->lun;#if defined(DISKIO) if (!down_interruptible(&target_map_sem)) {#if defined(TRUST_CDB) list_for_each_entry(this_item, &target_map_list, link) { if (this_item->target_id == cmd_curr->target_id && this_item->in_use) { this_device = this_item->the_device; break; } }#else if (cmd_curr->target_id < MAX_TARGETS) { if (lun < MAX_LUNS) { this_item = &target_map[cmd_curr->target_id][lun]; if (this_item->in_use) { this_device = this_item->the_device; } } if (!this_device) { this_item = &target_map[cmd_curr->target_id][0]; if (this_item->in_use) { printk("%s No lun %u for target id %u, " "using lun 0 instead\n", current->comm, lun, cmd_curr->target_id); this_device = this_item->the_device; lun = 0; } } }#endif up(&target_map_sem); } if (this_device) {/* Ming Zhang, mingz@ele.uri.edu */#ifdef K26 cmd_curr->req = scsi_allocate_request(this_device, GFP_ATOMIC);#else cmd_curr->req = scsi_allocate_request(this_device);#endif } else { printk("%s No device for lun %u target id %u\n", current->comm, lun, cmd_curr->target_id); goto scsi_thread_out; }#else cmd_curr->req = (Scsi_Request *)kmalloc(sizeof(Scsi_Request), GFP_KERNEL | GFP_ATOMIC); if (cmd_curr->req) { memset(cmd_curr->req, 0, sizeof(Scsi_Request)); if (lun >= MAX_LUNS) cmd_curr->req->sr_allowed = 1; }#endif if (!cmd_curr->req) { printk("%s no space for Scsi_Request\n", current->comm); goto scsi_thread_out; } memcpy(cmd_curr->req->sr_cmnd, cmd_curr->cmd, cmd_curr->len); if (handle_cmd(cmd_curr)) { printk("%s error in handle_cmd for command %d\n", current->comm, cmd_curr->id); /* is bailing out a good idea? */ goto scsi_thread_out; } } /* is a command pending */ if (cmd_curr->state == ST_PENDING) {# ifdef DEBUG_SCSI_THREAD printk("%s command %p id %d pending\n", current->comm, cmd_curr, cmd_curr->id);# endif /* call the rdy_to_xfer function */ if (hand_to_front_end(cmd_curr)) { printk("%s error in hand_to_front_end for command %d\n", current->comm, cmd_curr->id); goto scsi_thread_out; } } /* is data received */ if (cmd_curr->state == ST_TO_PROCESS) { /* * we have received the data - does this * go off to handle_cmd again ? */# ifdef DEBUG_SCSI_THREAD printk("%s command %p id %d with data received\n", current->comm, cmd_curr, cmd_curr->id);# endif if (handle_cmd(cmd_curr)) { printk("%s error in handle_cmd for command %d\n", current->comm, cmd_curr->id); /* is bailing out a good idea? */ goto scsi_thread_out; } }# ifdef GENERICIO /* is command processed */ if (cmd_curr->state == ST_PROCESSED) {# ifdef DEBUG_SCSI_THREAD printk("%s command %p id %d processed\n", current->comm, cmd_curr, cmd_curr->id);# endif /* we have some reading to do */ dev_file = cmd_curr->fd; if ((dev_file) && (dev_file->f_op) && (dev_file->f_op->read)) { old_fs = get_fs(); set_fs(get_ds()); i = dev_file->f_op->read(dev_file, (__u8 *) cmd_curr->sg, sizeof(sg_io_hdr_t), &dev_file->f_pos); set_fs(old_fs); if ((i < 0) && (i != -EAGAIN)) { printk("%s read error %d\n", current->comm, i); goto scsi_thread_out; } cmd_curr->state = ST_DONE; } }# endif /* is command done */ if (cmd_curr->state == ST_DONE) { /* * hand it off to the front end driver * to transmit *//* Ming Zhang, mingz@ele.uri.edu */#ifdef K26#ifdef DISKIO /* * for DISKIO under K26, should reassign sg_dma_address here */ for (i = 0; i < cmd_curr->req->sr_use_sg; i++) { sg_dma_address(((struct scatterlist *)cmd_curr->req->sr_buffer) + i) = (dma_addr_t)page_address(((struct scatterlist *)cmd_curr->req->sr_buffer)[i].page); ((struct scatterlist *)cmd_curr->req->sr_buffer)[i].offset = (unsigned long) sg_dma_address(((struct scatterlist *)cmd_curr->req->sr_buffer) + i) & ~PAGE_MASK; }#endif#endif# ifdef DEBUG_SCSI_THREAD printk("%s command %p id %d done\n", current->comm, cmd_curr, cmd_curr->id);# endif if (hand_to_front_end(cmd_curr)) { printk("%s error in hand_to_front_end for command %d\n", current->comm, cmd_curr->id); goto scsi_thread_out; } } /* can command be dequeued */ if (cmd_curr->state == ST_DEQUEUE) { /* * dequeue the command and free it */# ifdef DEBUG_SCSI_THREAD printk("%s command %p id %d - to dequeue req %p\n", current->comm, cmd_curr, cmd_curr->id, cmd_curr->req);# endif# ifdef GENERICIO /* free up the SCSI generic stuff */ if (cmd_curr->sg->dxferp) kfree(cmd_curr->sg->dxferp); kfree(cmd_curr->sg->sbp); kfree(cmd_curr->sg);# endif if (cmd_curr->req) { /* free up pages */ st_list = (struct scatterlist *) cmd_curr->req->sr_buffer; for (i = 0; i < cmd_curr->req->sr_use_sg; i++) {/* Ming Zhang, mingz@ele.uri.edu */#ifdef K26 kunmap(st_list[i].page); __free_page(st_list[i].page);#else free_page((long int) st_list[i].address);#endif }# ifdef DEBUG_SCSI_THREAD printk("%s command %p id %d - freed %d pages\n", current->comm, cmd_curr, cmd_curr->id, i);# endif /* free up scatterlist */ if (cmd_curr->req->sr_use_sg) kfree(st_list); /* free up Scsi_Request */# ifdef DISKIO scsi_release_request(cmd_curr->req);# else kfree(cmd_curr->req);# endif } /* dequeue and free up Target_Scsi_Cmnd */ spin_lock_irqsave(&target_data.cmd_queue_lock, flags); list_del(lptr); spin_unlock_irqrestore(&target_data.cmd_queue_lock, flags); kfree(cmd_curr);# ifdef DEBUG_SCSI_THREAD printk("%s command %p - all free\n", current->comm, cmd_curr);# endif } }# ifdef DEBUG_SCSI_THREAD printk("%s going back to sleep again\n", current->comm);# endif }scsi_thread_out: up(&target_data.thread_sem); printk("%s Exiting pid %d\n", current->comm, current->pid); return;}/* * rx_cmnd: this function is the basic function called by any front end * when it receives a SCSI command. The rx_cmnd function then fills up * a Target_Scsi_Cmnd struct, adds to the queue list, awakens the mid- * level thread and then returns the struct to the front end driver * INPUT: device, target_id, lun, SCSI CDB as an unsigned char * length of the command (or size of scsi_cdb array if * unavailable * OUTPUT: Target_Scsi_Cmnd struct or NULL if things go wrong */Target_Scsi_Cmnd *rx_cmnd(Scsi_Target_Device * device, __u64 target_id, __u64 lun, __u8 *scsi_cdb, int len, int datalen, int in_flags, Target_Scsi_Cmnd **result_command){ Target_Scsi_Cmnd *command; unsigned long flags; *result_command = NULL; if (!target_data.thread_id) { printk("rx_cmnd: No Mid-level running !!!!\n"); return NULL; } if (!device) { printk("rx_cmnd: No device given !!!!\n"); return NULL; } *result_command = command = (Target_Scsi_Cmnd *)kmalloc(sizeof(Target_Scsi_Cmnd), GFP_KERNEL | GFP_ATOMIC); if (command == NULL) { printk("rx_cmnd: No space for command\n"); /* sendsig (SIGKILL, target_data.thread_id, 0); */ return NULL; }# ifdef DEBUG_RX_CMND printk("rx_cmnd: filling up command struct %p\n", command);# endif /* fill in Target_Scsi_Cmnd */ command->req = NULL; command->state = ST_NEW_CMND; command->abort_code = CMND_OPEN; command->device = device; command->dev_id = device->id; /*ramesh@global.com added data length and flgs to the command structure */ command->datalen = datalen; command->flags = in_flags; /* cdeng change target_id later if lun doesn't match */ command->target_id = target_id; command->lun = unpack_lun((__u8 *)&lun); INIT_LIST_HEAD(&command->link); if ((len <= MAX_COMMAND_SIZE) && (len > 0)) command->len = len; else {// printk ("setting cmd len to %d instead of %d\n", MAX_COMMAND_SIZE, len); command->len = MAX_COMMAND_SIZE; } memcpy(command->cmd, scsi_cdb, command->len);# if defined (FILEIO) || defined (GENERICIO) /* fill in the file pointer */ command->fd = NULL; if (command->target_id < MAX_TARGETS && command->lun < MAX_LUNS && !down_interruptible(&target_map_sem)) { struct target_map_item *this_item; this_item = &target_map[command->target_id][command->lun]; if (this_item->in_use) { command->fd = this_item->the_file;# if defined (GENERICIO) command->blk_size = this_item->bytes_per_block;# endif } up(&target_map_sem); } if (command->fd == NULL) { printk("%s No target for command with targetid %u, lun %u\n", current->comm, command->target_id, command->lun); /* * Arne Redlich <agr1@sourceforge.net>: * kfree()'ing the command and returning NULL here will cause * the connection / session to be killed, rendering the target * unusable with initiators that don't understand/use the * REPORT_LUNS response and instead rely on probing LUNs by * INQUIRYs to a range of (possibly not available) LUNs * - which is actually the case with the Cisco (3.4.3) * and UNH (1.5.3, 1.5.4) initiators. Instead, doing nothing * here and letting handle_cmd() generate a TYPE_NO_LUN INQUIRY * response catches this quite gracefully. */ }# endif spin_lock_irqsave(&target_data.cmd_queue_lock, flags);# ifdef DEBUG_RX_CMND printk("rx_cmnd: locked cmd_queue_lock for %p\n", command);# endif command->id = ++target_data.command_id; /* check this to make sure you dont have a command with this id ????? * IGNORE FOR NOW */ if (!command->id) { /* FOR WRAP AROUNDS */ command->id = ++target_data.command_id; } list_add_tail(&command->link, &target_data.cmd_queue);# ifdef DEBUG_RX_CMND printk("rx_cmnd: unlock cmd_queue_lock for %p with id %d\n",command,command->id);# endif spin_unlock_irqrestore(&target_data.cmd_queue_lock, flags); /* wake up scsi_target_process_thread */ if (atomic_read(&target_data.target_sem.count) <= 0) { up(&target_data.target_sem); } return command;}/* * scsi_rx_data: This function is called by the lower-level driver to * tell the mid-level that data corresponding to a command has been * received. This function can be called from within an interrupt * handler (??). All this function does currently is to change the * state of a command and then wake the mid-level thread to deal with * this command. * INPUT: scsi command for which data has been received (MUST not be NULL) * OUTPUT: 0 if okay else < 0 */intscsi_rx_data(Target_Scsi_Cmnd * the_command){ the_command->state = ST_TO_PROCESS; /* wake up the mid-level scsi_target_process_thread */ if (atomic_read(&target_data.target_sem.count) <= 0) { up(&target_data.target_sem); } return 0;}/* * scsi_target_done: This is the function called by the low-level driver * to signify that it has completed the execution of a given scsi cmnd * This function needs to remove the resources that have been allocated * to the given command, dequeue the command etc. This function will be * called from within the context of an interrupt handler. So, it may * be best to leave it up to the mid-level thread to actually deal with * these functions. Right now, I am setting it up so that the status of * the command is changed and the mid-level is awakened. * INPUT: scsi command to be dequeued * OUTPUT: 0 if everything is okay * < 0 if there is trouble */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -