X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fscsi%2Fscsi.c;h=f6980bd9d8f9a9a8b75a57de89cd9c8e26cc214c;hb=1c353f7d616a4ef04b5e73fe7a2184baa039f06f;hp=0fb1709ce5e371649c0c90126a3a8558a1fd13a4;hpb=4fd3670eb1d3c33e8952cf1e79edbb2d517dcfb5;p=linux-2.6-omap-h63xx.git diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 0fb1709ce5e..f6980bd9d8f 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -122,6 +122,11 @@ static const char *const scsi_device_types[] = { "Automation/Drive ", }; +/** + * scsi_device_type - Return 17 char string indicating device type. + * @type: type number to look up + */ + const char * scsi_device_type(unsigned type) { if (type == 0x1e) @@ -136,32 +141,89 @@ const char * scsi_device_type(unsigned type) EXPORT_SYMBOL(scsi_device_type); struct scsi_host_cmd_pool { - struct kmem_cache *slab; - unsigned int users; - char *name; - unsigned int slab_flags; - gfp_t gfp_mask; + struct kmem_cache *cmd_slab; + struct kmem_cache *sense_slab; + unsigned int users; + char *cmd_name; + char *sense_name; + unsigned int slab_flags; + gfp_t gfp_mask; }; static struct scsi_host_cmd_pool scsi_cmd_pool = { - .name = "scsi_cmd_cache", + .cmd_name = "scsi_cmd_cache", + .sense_name = "scsi_sense_cache", .slab_flags = SLAB_HWCACHE_ALIGN, }; static struct scsi_host_cmd_pool scsi_cmd_dma_pool = { - .name = "scsi_cmd_cache(DMA)", + .cmd_name = "scsi_cmd_cache(DMA)", + .sense_name = "scsi_sense_cache(DMA)", .slab_flags = SLAB_HWCACHE_ALIGN|SLAB_CACHE_DMA, .gfp_mask = __GFP_DMA, }; static DEFINE_MUTEX(host_cmd_pool_mutex); +/** + * scsi_pool_alloc_command - internal function to get a fully allocated command + * @pool: slab pool to allocate the command from + * @gfp_mask: mask for the allocation + * + * Returns a fully allocated command (with the allied sense buffer) or + * NULL on failure + */ +static struct scsi_cmnd * +scsi_pool_alloc_command(struct scsi_host_cmd_pool *pool, gfp_t gfp_mask) +{ + struct scsi_cmnd *cmd; + + cmd = kmem_cache_alloc(pool->cmd_slab, gfp_mask | pool->gfp_mask); + if (!cmd) + return NULL; + + memset(cmd, 0, sizeof(*cmd)); + + cmd->sense_buffer = kmem_cache_alloc(pool->sense_slab, + gfp_mask | pool->gfp_mask); + if (!cmd->sense_buffer) { + kmem_cache_free(pool->cmd_slab, cmd); + return NULL; + } + + return cmd; +} + +/** + * scsi_pool_free_command - internal function to release a command + * @pool: slab pool to allocate the command from + * @cmd: command to release + * + * the command must previously have been allocated by + * scsi_pool_alloc_command. + */ +static void +scsi_pool_free_command(struct scsi_host_cmd_pool *pool, + struct scsi_cmnd *cmd) +{ + kmem_cache_free(pool->sense_slab, cmd->sense_buffer); + kmem_cache_free(pool->cmd_slab, cmd); +} + +/** + * __scsi_get_command - Allocate a struct scsi_cmnd + * @shost: host to transmit command + * @gfp_mask: allocation mask + * + * Description: allocate a struct scsi_cmd from host's slab, recycling from the + * host's free_list if necessary. + */ struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, gfp_t gfp_mask) { struct scsi_cmnd *cmd; + unsigned char *buf; - cmd = kmem_cache_alloc(shost->cmd_pool->slab, - gfp_mask | shost->cmd_pool->gfp_mask); + cmd = scsi_pool_alloc_command(shost->cmd_pool, gfp_mask); if (unlikely(!cmd)) { unsigned long flags; @@ -173,19 +235,22 @@ struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, gfp_t gfp_mask) list_del_init(&cmd->list); } spin_unlock_irqrestore(&shost->free_list_lock, flags); + + if (cmd) { + buf = cmd->sense_buffer; + memset(cmd, 0, sizeof(*cmd)); + cmd->sense_buffer = buf; + } } return cmd; } EXPORT_SYMBOL_GPL(__scsi_get_command); -/* - * Function: scsi_get_command() - * - * Purpose: Allocate and setup a scsi command block - * - * Arguments: dev - parent scsi device - * gfp_mask- allocator flags +/** + * scsi_get_command - Allocate and setup a scsi command block + * @dev: parent scsi device + * @gfp_mask: allocator flags * * Returns: The allocated scsi command structure. */ @@ -202,7 +267,6 @@ struct scsi_cmnd *scsi_get_command(struct scsi_device *dev, gfp_t gfp_mask) if (likely(cmd != NULL)) { unsigned long flags; - memset(cmd, 0, sizeof(*cmd)); cmd->device = dev; init_timer(&cmd->eh_timeout); INIT_LIST_HEAD(&cmd->list); @@ -217,6 +281,12 @@ struct scsi_cmnd *scsi_get_command(struct scsi_device *dev, gfp_t gfp_mask) } EXPORT_SYMBOL(scsi_get_command); +/** + * __scsi_put_command - Free a struct scsi_cmnd + * @shost: dev->host + * @cmd: Command to free + * @dev: parent scsi device + */ void __scsi_put_command(struct Scsi_Host *shost, struct scsi_cmnd *cmd, struct device *dev) { @@ -231,18 +301,15 @@ void __scsi_put_command(struct Scsi_Host *shost, struct scsi_cmnd *cmd, spin_unlock_irqrestore(&shost->free_list_lock, flags); if (likely(cmd != NULL)) - kmem_cache_free(shost->cmd_pool->slab, cmd); + scsi_pool_free_command(shost->cmd_pool, cmd); put_device(dev); } EXPORT_SYMBOL(__scsi_put_command); -/* - * Function: scsi_put_command() - * - * Purpose: Free a scsi command block - * - * Arguments: cmd - command block to free +/** + * scsi_put_command - Free a scsi command block + * @cmd: command block to free * * Returns: Nothing. * @@ -263,67 +330,154 @@ void scsi_put_command(struct scsi_cmnd *cmd) } EXPORT_SYMBOL(scsi_put_command); -/* - * Function: scsi_setup_command_freelist() - * - * Purpose: Setup the command freelist for a scsi host. - * - * Arguments: shost - host to allocate the freelist for. - * - * Returns: Nothing. - */ -int scsi_setup_command_freelist(struct Scsi_Host *shost) +static struct scsi_host_cmd_pool *scsi_get_host_cmd_pool(gfp_t gfp_mask) { - struct scsi_host_cmd_pool *pool; - struct scsi_cmnd *cmd; - - spin_lock_init(&shost->free_list_lock); - INIT_LIST_HEAD(&shost->free_list); - + struct scsi_host_cmd_pool *retval = NULL, *pool; /* * Select a command slab for this host and create it if not - * yet existant. + * yet existent. */ mutex_lock(&host_cmd_pool_mutex); - pool = (shost->unchecked_isa_dma ? &scsi_cmd_dma_pool : &scsi_cmd_pool); + pool = (gfp_mask & __GFP_DMA) ? &scsi_cmd_dma_pool : + &scsi_cmd_pool; if (!pool->users) { - pool->slab = kmem_cache_create(pool->name, - sizeof(struct scsi_cmnd), 0, - pool->slab_flags, NULL); - if (!pool->slab) + pool->cmd_slab = kmem_cache_create(pool->cmd_name, + sizeof(struct scsi_cmnd), 0, + pool->slab_flags, NULL); + if (!pool->cmd_slab) + goto fail; + + pool->sense_slab = kmem_cache_create(pool->sense_name, + SCSI_SENSE_BUFFERSIZE, 0, + pool->slab_flags, NULL); + if (!pool->sense_slab) { + kmem_cache_destroy(pool->cmd_slab); goto fail; + } } pool->users++; - shost->cmd_pool = pool; + retval = pool; + fail: mutex_unlock(&host_cmd_pool_mutex); + return retval; +} +static void scsi_put_host_cmd_pool(gfp_t gfp_mask) +{ + struct scsi_host_cmd_pool *pool; + + mutex_lock(&host_cmd_pool_mutex); + pool = (gfp_mask & __GFP_DMA) ? &scsi_cmd_dma_pool : + &scsi_cmd_pool; /* - * Get one backup command for this host. + * This may happen if a driver has a mismatched get and put + * of the command pool; the driver should be implicated in + * the stack trace */ - cmd = kmem_cache_alloc(shost->cmd_pool->slab, - GFP_KERNEL | shost->cmd_pool->gfp_mask); - if (!cmd) - goto fail2; - list_add(&cmd->list, &shost->free_list); - return 0; + BUG_ON(pool->users == 0); - fail2: - if (!--pool->users) - kmem_cache_destroy(pool->slab); - return -ENOMEM; - fail: + if (!--pool->users) { + kmem_cache_destroy(pool->cmd_slab); + kmem_cache_destroy(pool->sense_slab); + } mutex_unlock(&host_cmd_pool_mutex); - return -ENOMEM; +} +/** + * scsi_allocate_command - get a fully allocated SCSI command + * @gfp_mask: allocation mask + * + * This function is for use outside of the normal host based pools. + * It allocates the relevant command and takes an additional reference + * on the pool it used. This function *must* be paired with + * scsi_free_command which also has the identical mask, otherwise the + * free pool counts will eventually go wrong and you'll trigger a bug. + * + * This function should *only* be used by drivers that need a static + * command allocation at start of day for internal functions. + */ +struct scsi_cmnd *scsi_allocate_command(gfp_t gfp_mask) +{ + struct scsi_host_cmd_pool *pool = scsi_get_host_cmd_pool(gfp_mask); + + if (!pool) + return NULL; + + return scsi_pool_alloc_command(pool, gfp_mask); } +EXPORT_SYMBOL(scsi_allocate_command); -/* - * Function: scsi_destroy_command_freelist() +/** + * scsi_free_command - free a command allocated by scsi_allocate_command + * @gfp_mask: mask used in the original allocation + * @cmd: command to free * - * Purpose: Release the command freelist for a scsi host. + * Note: using the original allocation mask is vital because that's + * what determines which command pool we use to free the command. Any + * mismatch will cause the system to BUG eventually. + */ +void scsi_free_command(gfp_t gfp_mask, struct scsi_cmnd *cmd) +{ + struct scsi_host_cmd_pool *pool = scsi_get_host_cmd_pool(gfp_mask); + + /* + * this could trigger if the mask to scsi_allocate_command + * doesn't match this mask. Otherwise we're guaranteed that this + * succeeds because scsi_allocate_command must have taken a reference + * on the pool + */ + BUG_ON(!pool); + + scsi_pool_free_command(pool, cmd); + /* + * scsi_put_host_cmd_pool is called twice; once to release the + * reference we took above, and once to release the reference + * originally taken by scsi_allocate_command + */ + scsi_put_host_cmd_pool(gfp_mask); + scsi_put_host_cmd_pool(gfp_mask); +} +EXPORT_SYMBOL(scsi_free_command); + +/** + * scsi_setup_command_freelist - Setup the command freelist for a scsi host. + * @shost: host to allocate the freelist for. * - * Arguments: shost - host that's freelist is going to be destroyed + * Description: The command freelist protects against system-wide out of memory + * deadlock by preallocating one SCSI command structure for each host, so the + * system can always write to a swap file on a device associated with that host. + * + * Returns: Nothing. + */ +int scsi_setup_command_freelist(struct Scsi_Host *shost) +{ + struct scsi_cmnd *cmd; + const gfp_t gfp_mask = shost->unchecked_isa_dma ? GFP_DMA : GFP_KERNEL; + + spin_lock_init(&shost->free_list_lock); + INIT_LIST_HEAD(&shost->free_list); + + shost->cmd_pool = scsi_get_host_cmd_pool(gfp_mask); + + if (!shost->cmd_pool) + return -ENOMEM; + + /* + * Get one backup command for this host. + */ + cmd = scsi_pool_alloc_command(shost->cmd_pool, gfp_mask); + if (!cmd) { + scsi_put_host_cmd_pool(gfp_mask); + return -ENOMEM; + } + list_add(&cmd->list, &shost->free_list); + return 0; +} + +/** + * scsi_destroy_command_freelist - Release the command freelist for a scsi host. + * @shost: host whose freelist is going to be destroyed */ void scsi_destroy_command_freelist(struct Scsi_Host *shost) { @@ -332,13 +486,10 @@ void scsi_destroy_command_freelist(struct Scsi_Host *shost) cmd = list_entry(shost->free_list.next, struct scsi_cmnd, list); list_del_init(&cmd->list); - kmem_cache_free(shost->cmd_pool->slab, cmd); + scsi_pool_free_command(shost->cmd_pool, cmd); } - - mutex_lock(&host_cmd_pool_mutex); - if (!--shost->cmd_pool->users) - kmem_cache_destroy(shost->cmd_pool->slab); - mutex_unlock(&host_cmd_pool_mutex); + shost->cmd_pool = NULL; + scsi_put_host_cmd_pool(shost->unchecked_isa_dma ? GFP_DMA : GFP_KERNEL); } #ifdef CONFIG_SCSI_LOGGING @@ -441,8 +592,12 @@ void scsi_log_completion(struct scsi_cmnd *cmd, int disposition) } #endif -/* - * Assign a serial number to the request for error recovery +/** + * scsi_cmd_get_serial - Assign a serial number to a command + * @host: the scsi host + * @cmd: command to assign serial number to + * + * Description: a serial number identifies a request for error recovery * and debugging purposes. Protected by the Host_Lock of host. */ static inline void scsi_cmd_get_serial(struct Scsi_Host *host, struct scsi_cmnd *cmd) @@ -452,14 +607,12 @@ static inline void scsi_cmd_get_serial(struct Scsi_Host *host, struct scsi_cmnd cmd->serial_number = host->cmd_serial_number++; } -/* - * Function: scsi_dispatch_command - * - * Purpose: Dispatch a command to the low-level driver. - * - * Arguments: cmd - command block we are dispatching. +/** + * scsi_dispatch_command - Dispatch a command to the low-level driver. + * @cmd: command block we are dispatching. * - * Notes: + * Return: nonzero return request was rejected and device's queue needs to be + * plugged. */ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) { @@ -585,7 +738,7 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) /** * scsi_req_abort_cmd -- Request command recovery for the specified command - * cmd: pointer to the SCSI command of interest + * @cmd: pointer to the SCSI command of interest * * This function requests that SCSI Core start recovery for the * command by deleting the timer and adding the command to the eh @@ -606,9 +759,9 @@ EXPORT_SYMBOL(scsi_req_abort_cmd); * @cmd: The SCSI Command for which a low-level device driver (LLDD) gives * ownership back to SCSI Core -- i.e. the LLDD has finished with it. * - * This function is the mid-level's (SCSI Core) interrupt routine, which - * regains ownership of the SCSI command (de facto) from a LLDD, and enqueues - * the command to the done queue for further processing. + * Description: This function is the mid-level's (SCSI Core) interrupt routine, + * which regains ownership of the SCSI command (de facto) from a LLDD, and + * enqueues the command to the done queue for further processing. * * This is the producer of the done queue who enqueues at the tail. * @@ -617,7 +770,7 @@ EXPORT_SYMBOL(scsi_req_abort_cmd); static void scsi_done(struct scsi_cmnd *cmd) { /* - * We don't have to worry about this one timing out any more. + * We don't have to worry about this one timing out anymore. * If we are unable to remove the timer, then the command * has already timed out. In which case, we have no choice but to * let the timeout function run, as we have no idea where in fact @@ -660,10 +813,11 @@ static struct scsi_driver *scsi_cmd_to_driver(struct scsi_cmnd *cmd) return *(struct scsi_driver **)cmd->request->rq_disk->private_data; } -/* - * Function: scsi_finish_command +/** + * scsi_finish_command - cleanup and pass command back to upper layer + * @cmd: the command * - * Purpose: Pass command off to upper layer for finishing of I/O + * Description: Pass command off to upper layer for finishing of I/O * request, waking processes that are waiting on results, * etc. */ @@ -698,7 +852,7 @@ void scsi_finish_command(struct scsi_cmnd *cmd) "Notifying upper driver of completion " "(result %x)\n", cmd->result)); - good_bytes = cmd->request_bufflen; + good_bytes = scsi_bufflen(cmd) + cmd->request->extra_len; if (cmd->request->cmd_type != REQ_TYPE_BLOCK_PC) { drv = scsi_cmd_to_driver(cmd); if (drv->done) @@ -708,18 +862,14 @@ void scsi_finish_command(struct scsi_cmnd *cmd) } EXPORT_SYMBOL(scsi_finish_command); -/* - * Function: scsi_adjust_queue_depth() - * - * Purpose: Allow low level drivers to tell us to change the queue depth - * on a specific SCSI device - * - * Arguments: sdev - SCSI Device in question - * tagged - Do we use tagged queueing (non-0) or do we treat - * this device as an untagged device (0) - * tags - Number of tags allowed if tagged queueing enabled, - * or number of commands the low level driver can - * queue up in non-tagged mode (as per cmd_per_lun). +/** + * scsi_adjust_queue_depth - Let low level drivers change a device's queue depth + * @sdev: SCSI Device in question + * @tagged: Do we use tagged queueing (non-0) or do we treat + * this device as an untagged device (0) + * @tags: Number of tags allowed if tagged queueing enabled, + * or number of commands the low level driver can + * queue up in non-tagged mode (as per cmd_per_lun). * * Returns: Nothing * @@ -742,8 +892,8 @@ void scsi_adjust_queue_depth(struct scsi_device *sdev, int tagged, int tags) spin_lock_irqsave(sdev->request_queue->queue_lock, flags); - /* Check to see if the queue is managed by the block layer - * if it is, and we fail to adjust the depth, exit */ + /* Check to see if the queue is managed by the block layer. + * If it is, and we fail to adjust the depth, exit. */ if (blk_queue_tagged(sdev->request_queue) && blk_queue_resize_tags(sdev->request_queue, tags) != 0) goto out; @@ -772,20 +922,17 @@ void scsi_adjust_queue_depth(struct scsi_device *sdev, int tagged, int tags) } EXPORT_SYMBOL(scsi_adjust_queue_depth); -/* - * Function: scsi_track_queue_full() +/** + * scsi_track_queue_full - track QUEUE_FULL events to adjust queue depth + * @sdev: SCSI Device in question + * @depth: Current number of outstanding SCSI commands on this device, + * not counting the one returned as QUEUE_FULL. * - * Purpose: This function will track successive QUEUE_FULL events on a + * Description: This function will track successive QUEUE_FULL events on a * specific SCSI device to determine if and when there is a * need to adjust the queue depth on the device. * - * Arguments: sdev - SCSI Device in question - * depth - Current number of outstanding SCSI commands on - * this device, not counting the one returned as - * QUEUE_FULL. - * - * Returns: 0 - No change needed - * >0 - Adjust queue depth to this new depth + * Returns: 0 - No change needed, >0 - Adjust queue depth to this new depth, * -1 - Drop back to untagged operation using host->cmd_per_lun * as the untagged command depth * @@ -824,10 +971,10 @@ int scsi_track_queue_full(struct scsi_device *sdev, int depth) EXPORT_SYMBOL(scsi_track_queue_full); /** - * scsi_device_get - get an addition reference to a scsi_device + * scsi_device_get - get an additional reference to a scsi_device * @sdev: device to get a reference to * - * Gets a reference to the scsi_device and increments the use count + * Description: Gets a reference to the scsi_device and increments the use count * of the underlying LLDD module. You must hold host_lock of the * parent Scsi_Host or already have a reference when calling this. */ @@ -849,8 +996,8 @@ EXPORT_SYMBOL(scsi_device_get); * scsi_device_put - release a reference to a scsi_device * @sdev: device to release a reference on. * - * Release a reference to the scsi_device and decrements the use count - * of the underlying LLDD module. The device is freed once the last + * Description: Release a reference to the scsi_device and decrements the use + * count of the underlying LLDD module. The device is freed once the last * user vanishes. */ void scsi_device_put(struct scsi_device *sdev) @@ -867,7 +1014,7 @@ void scsi_device_put(struct scsi_device *sdev) } EXPORT_SYMBOL(scsi_device_put); -/* helper for shost_for_each_device, thus not documented */ +/* helper for shost_for_each_device, see that for documentation */ struct scsi_device *__scsi_iterate_devices(struct Scsi_Host *shost, struct scsi_device *prev) { @@ -895,6 +1042,8 @@ EXPORT_SYMBOL(__scsi_iterate_devices); /** * starget_for_each_device - helper to walk all devices of a target * @starget: target whose devices we want to iterate over. + * @data: Opaque passed to each function call. + * @fn: Function to call on each device * * This traverses over each device of @starget. The devices have * a reference that must be released by scsi_host_put when breaking @@ -915,9 +1064,10 @@ void starget_for_each_device(struct scsi_target *starget, void *data, EXPORT_SYMBOL(starget_for_each_device); /** - * __starget_for_each_device - helper to walk all devices of a target - * (UNLOCKED) + * __starget_for_each_device - helper to walk all devices of a target (UNLOCKED) * @starget: target whose devices we want to iterate over. + * @data: parameter for callback @fn() + * @fn: callback function that is invoked for each device * * This traverses over each device of @starget. It does _not_ * take a reference on the scsi_device, so the whole loop must be @@ -946,13 +1096,13 @@ EXPORT_SYMBOL(__starget_for_each_device); * @starget: SCSI target pointer * @lun: SCSI Logical Unit Number * - * Looks up the scsi_device with the specified @lun for a give - * @starget. The returned scsi_device does not have an additional + * Description: Looks up the scsi_device with the specified @lun for a given + * @starget. The returned scsi_device does not have an additional * reference. You must hold the host's host_lock over this call and * any access to the returned scsi_device. * - * Note: The only reason why drivers would want to use this is because - * they're need to access the device list in irq context. Otherwise you + * Note: The only reason why drivers should use this is because + * they need to access the device list in irq context. Otherwise you * really want to use scsi_device_lookup_by_target instead. **/ struct scsi_device *__scsi_device_lookup_by_target(struct scsi_target *starget, @@ -974,9 +1124,9 @@ EXPORT_SYMBOL(__scsi_device_lookup_by_target); * @starget: SCSI target pointer * @lun: SCSI Logical Unit Number * - * Looks up the scsi_device with the specified @channel, @id, @lun for a - * give host. The returned scsi_device has an additional reference that - * needs to be release with scsi_host_put once you're done with it. + * Description: Looks up the scsi_device with the specified @channel, @id, @lun + * for a given host. The returned scsi_device has an additional reference that + * needs to be released with scsi_device_put once you're done with it. **/ struct scsi_device *scsi_device_lookup_by_target(struct scsi_target *starget, uint lun) @@ -996,19 +1146,19 @@ struct scsi_device *scsi_device_lookup_by_target(struct scsi_target *starget, EXPORT_SYMBOL(scsi_device_lookup_by_target); /** - * scsi_device_lookup - find a device given the host (UNLOCKED) + * __scsi_device_lookup - find a device given the host (UNLOCKED) * @shost: SCSI host pointer * @channel: SCSI channel (zero if only one channel) - * @pun: SCSI target number (physical unit number) + * @id: SCSI target number (physical unit number) * @lun: SCSI Logical Unit Number * - * Looks up the scsi_device with the specified @channel, @id, @lun for a - * give host. The returned scsi_device does not have an additional reference. - * You must hold the host's host_lock over this call and any access to the - * returned scsi_device. + * Description: Looks up the scsi_device with the specified @channel, @id, @lun + * for a given host. The returned scsi_device does not have an additional + * reference. You must hold the host's host_lock over this call and any access + * to the returned scsi_device. * * Note: The only reason why drivers would want to use this is because - * they're need to access the device list in irq context. Otherwise you + * they need to access the device list in irq context. Otherwise you * really want to use scsi_device_lookup instead. **/ struct scsi_device *__scsi_device_lookup(struct Scsi_Host *shost, @@ -1033,9 +1183,9 @@ EXPORT_SYMBOL(__scsi_device_lookup); * @id: SCSI target number (physical unit number) * @lun: SCSI Logical Unit Number * - * Looks up the scsi_device with the specified @channel, @id, @lun for a - * give host. The returned scsi_device has an additional reference that - * needs to be release with scsi_host_put once you're done with it. + * Description: Looks up the scsi_device with the specified @channel, @id, @lun + * for a given host. The returned scsi_device has an additional reference that + * needs to be released with scsi_device_put once you're done with it. **/ struct scsi_device *scsi_device_lookup(struct Scsi_Host *shost, uint channel, uint id, uint lun)