Quelle scsi.c
Sprache: C
// SPDX-License-Identifier: GPL-2.0-only
/*
* scsi . c Copyright ( C ) 1992 Drew Eckhardt
* Copyright ( C ) 1993 , 1994 , 1995 , 1999 Eric Youngdale
* Copyright ( C ) 2002 , 2003 Christoph Hellwig
*
* generic mid - level SCSI driver
* Initial versions : Drew Eckhardt
* Subsequent revisions : Eric Youngdale
*
* < drew @ colorado . edu >
*
* Bug correction thanks go to :
* Rik Faith < faith @ cs . unc . edu >
* Tommy Thorn < tthorn >
* Thomas Wuensche < tw @ fgb1 . fgb . mw . tu - muenchen . de >
*
* Modified by Eric Youngdale eric @ andante . org or ericy @ gnu . ai . mit . edu to
* add scatter - gather , multiple outstanding request , and other
* enhancements .
*
* Native multichannel , wide scsi , / proc / scsi and hot plugging
* support added by Michael Neuffer < mike @ i - connect . net >
*
* Added request_module ( " scsi_hostadapter " ) for kerneld :
* ( Put an " alias scsi_hostadapter your_hostadapter " in / etc / modprobe . conf )
* Bjorn Ekwall < bj0rn @ blox . se >
* ( changed to kmod )
*
* Major improvements to the timeout , abort , and reset processing ,
* as well as performance modifications for large queue depths by
* Leonard N . Zubkoff < lnz @ dandelion . com >
*
* Converted cli ( ) code to spinlocks , Ingo Molnar
*
* Jiffies wrap fixes ( host - > resetting ) , 3 Dec 1998 Andrea Arcangeli
*
* out_of_space hacks , D . Gilbert ( dpg ) 990608
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/completion.h>
#include <linux/unistd.h>
#include <linux/spinlock.h>
#include <linux/kmod.h>
#include <linux/interrupt.h>
#include <linux/notifier.h>
#include <linux/cpu.h>
#include <linux/mutex.h>
#include <linux/unaligned.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_dbg.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_driver.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include "scsi_priv.h"
#include "scsi_logging.h"
#define CREATE_TRACE_POINTS
#include <trace/events/scsi.h>
/*
* Definitions and constants .
*/
/*
* Note - the initial logging level can be set here to log events at boot time .
* After the system is up , you may enable logging via the / proc interface .
*/
unsigned int scsi_logging_level;
#if defined (CONFIG_SCSI_LOGGING)
EXPORT_SYMBOL(scsi_logging_level);
#endif
#ifdef CONFIG_SCSI_LOGGING
void scsi_log_send(struct scsi_cmnd *cmd)
{
unsigned int level;
/*
* If ML QUEUE log level is greater than or equal to :
*
* 1 : nothing ( match completion )
*
* 2 : log opcode + command of all commands + cmd address
*
* 3 : same as 2
*
* 4 : same as 3
*/
if (unlikely(scsi_logging_level)) {
level = SCSI_LOG_LEVEL(SCSI_LOG_MLQUEUE_SHIFT,
SCSI_LOG_MLQUEUE_BITS);
if (level > 1 ) {
scmd_printk(KERN_INFO, cmd,
"Send: scmd 0x%p\n" , cmd);
scsi_print_command(cmd);
}
}
}
void scsi_log_completion(struct scsi_cmnd *cmd, int disposition)
{
unsigned int level;
/*
* If ML COMPLETE log level is greater than or equal to :
*
* 1 : log disposition , result , opcode + command , and conditionally
* sense data for failures or non SUCCESS dispositions .
*
* 2 : same as 1 but for all command completions .
*
* 3 : same as 2
*
* 4 : same as 3 plus dump extra junk
*/
if (unlikely(scsi_logging_level)) {
level = SCSI_LOG_LEVEL(SCSI_LOG_MLCOMPLETE_SHIFT,
SCSI_LOG_MLCOMPLETE_BITS);
if (((level > 0 ) && (cmd->result || disposition != SUCCESS)) ||
(level > 1 )) {
scsi_print_result(cmd, "Done" , disposition);
scsi_print_command(cmd);
if (scsi_status_is_check_condition(cmd->result))
scsi_print_sense(cmd);
if (level > 3 )
scmd_printk(KERN_INFO, cmd,
"scsi host busy %d failed %d\n" ,
scsi_host_busy(cmd->device->host),
cmd->device->host->host_failed);
}
}
}
#endif
/**
* scsi_finish_command - cleanup and pass command back to upper layer
* @ cmd : the command
*
* Description : Pass command off to upper layer for finishing of I / O
* request , waking processes that are waiting on results ,
* etc .
*/
void scsi_finish_command(struct scsi_cmnd *cmd)
{
struct scsi_device *sdev = cmd->device;
struct scsi_target *starget = scsi_target(sdev);
struct Scsi_Host *shost = sdev->host;
struct scsi_driver *drv;
unsigned int good_bytes;
scsi_device_unbusy(sdev, cmd);
/*
* Clear the flags that say that the device / target / host is no longer
* capable of accepting new commands .
*/
if (atomic_read(&shost->host_blocked))
atomic_set(&shost->host_blocked, 0 );
if (atomic_read(&starget->target_blocked))
atomic_set(&starget->target_blocked, 0 );
if (atomic_read(&sdev->device_blocked))
atomic_set(&sdev->device_blocked, 0 );
SCSI_LOG_MLCOMPLETE(4 , sdev_printk(KERN_INFO, sdev,
"Notifying upper driver of completion "
"(result %x)\n" , cmd->result));
good_bytes = scsi_bufflen(cmd);
if (!blk_rq_is_passthrough(scsi_cmd_to_rq(cmd))) {
int old_good_bytes = good_bytes;
drv = scsi_cmd_to_driver(cmd);
if (drv->done)
good_bytes = drv->done(cmd);
/*
* USB may not give sense identifying bad sector and
* simply return a residue instead , so subtract off the
* residue if drv - > done ( ) error processing indicates no
* change to the completion length .
*/
if (good_bytes == old_good_bytes)
good_bytes -= scsi_get_resid(cmd);
}
scsi_io_completion(cmd, good_bytes);
}
/*
* 4096 is big enough for saturating fast SCSI LUNs .
*/
int scsi_device_max_queue_depth(struct scsi_device *sdev)
{
return min_t(int , sdev->host->can_queue, 4096 );
}
/**
* scsi_change_queue_depth - change a device ' s queue depth
* @ sdev : SCSI Device in question
* @ depth : number of commands allowed to be queued to the driver
*
* Sets the device queue depth and returns the new value .
*/
int scsi_change_queue_depth(struct scsi_device *sdev, int depth)
{
depth = min_t(int , depth, scsi_device_max_queue_depth(sdev));
if (depth > 0 ) {
sdev->queue_depth = depth;
wmb();
}
if (sdev->request_queue)
blk_set_queue_depth(sdev->request_queue, depth);
sbitmap_resize(&sdev->budget_map, sdev->queue_depth);
return sdev->queue_depth;
}
EXPORT_SYMBOL(scsi_change_queue_depth);
/**
* 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 .
*
* 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 .
*
* 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
*
* Lock Status : None held on entry
*
* Notes : Low level drivers may call this at any time and we will do
* " The Right Thing . " We are interrupt context safe .
*/
int scsi_track_queue_full(struct scsi_device *sdev, int depth)
{
/*
* Don ' t let QUEUE_FULLs on the same
* jiffies count , they could all be from
* same event .
*/
if ((jiffies >> 4 ) == (sdev->last_queue_full_time >> 4 ))
return 0 ;
sdev->last_queue_full_time = jiffies;
if (sdev->last_queue_full_depth != depth) {
sdev->last_queue_full_count = 1 ;
sdev->last_queue_full_depth = depth;
} else {
sdev->last_queue_full_count++;
}
if (sdev->last_queue_full_count <= 10 )
return 0 ;
return scsi_change_queue_depth(sdev, depth);
}
EXPORT_SYMBOL(scsi_track_queue_full);
/**
* scsi_vpd_inquiry - Request a device provide us with a VPD page
* @ sdev : The device to ask
* @ buffer : Where to put the result
* @ page : Which Vital Product Data to return
* @ len : The length of the buffer
*
* This is an internal helper function . You probably want to use
* scsi_get_vpd_page instead .
*
* Returns size of the vpd page on success or a negative error number .
*/
static int scsi_vpd_inquiry(struct scsi_device *sdev, unsigned char *buffer,
u8 page, unsigned len)
{
int result;
unsigned char cmd[16 ];
if (len < 4 )
return -EINVAL;
cmd[0 ] = INQUIRY;
cmd[1 ] = 1 ; /* EVPD */
cmd[2 ] = page;
cmd[3 ] = len >> 8 ;
cmd[4 ] = len & 0 xff;
cmd[5 ] = 0 ; /* Control byte */
/*
* I ' m not convinced we need to try quite this hard to get VPD , but
* all the existing users tried this hard .
*/
result = scsi_execute_cmd(sdev, cmd, REQ_OP_DRV_IN, buffer, len,
30 * HZ, 3 , NULL);
if (result)
return -EIO;
/*
* Sanity check that we got the page back that we asked for and that
* the page size is not 0 .
*/
if (buffer[1 ] != page)
return -EIO;
result = get_unaligned_be16(&buffer[2 ]);
if (!result)
return -EIO;
return result + 4 ;
}
enum scsi_vpd_parameters {
SCSI_VPD_HEADER_SIZE = 4 ,
SCSI_VPD_LIST_SIZE = 36 ,
};
static int scsi_get_vpd_size(struct scsi_device *sdev, u8 page)
{
unsigned char vpd[SCSI_VPD_LIST_SIZE] __aligned(4 );
int result;
if (sdev->no_vpd_size)
return SCSI_DEFAULT_VPD_LEN;
/*
* Fetch the supported pages VPD and validate that the requested page
* number is present .
*/
if (page != 0 ) {
result = scsi_vpd_inquiry(sdev, vpd, 0 , sizeof (vpd));
if (result < SCSI_VPD_HEADER_SIZE)
return 0 ;
if (result > sizeof (vpd)) {
dev_warn_once(&sdev->sdev_gendev,
"%s: long VPD page 0 length: %d bytes\n" ,
__func__, result);
result = sizeof (vpd);
}
result -= SCSI_VPD_HEADER_SIZE;
if (!memchr(&vpd[SCSI_VPD_HEADER_SIZE], page, result))
return 0 ;
}
/*
* Fetch the VPD page header to find out how big the page
* is . This is done to prevent problems on legacy devices
* which can not handle allocation lengths as large as
* potentially requested by the caller .
*/
result = scsi_vpd_inquiry(sdev, vpd, page, SCSI_VPD_HEADER_SIZE);
if (result < 0 )
return 0 ;
if (result < SCSI_VPD_HEADER_SIZE) {
dev_warn_once(&sdev->sdev_gendev,
"%s: short VPD page 0x%02x length: %d bytes\n" ,
__func__, page, result);
return 0 ;
}
return result;
}
/**
* scsi_get_vpd_page - Get Vital Product Data from a SCSI device
* @ sdev : The device to ask
* @ page : Which Vital Product Data to return
* @ buf : where to store the VPD
* @ buf_len : number of bytes in the VPD buffer area
*
* SCSI devices may optionally supply Vital Product Data . Each ' page '
* of VPD is defined in the appropriate SCSI document ( eg SPC , SBC ) .
* If the device supports this VPD page , this routine fills @ buf
* with the data from that page and return 0 . If the VPD page is not
* supported or its content cannot be retrieved , - EINVAL is returned .
*/
int scsi_get_vpd_page(struct scsi_device *sdev, u8 page, unsigned char *buf,
int buf_len)
{
int result, vpd_len;
if (!scsi_device_supports_vpd(sdev))
return -EINVAL;
vpd_len = scsi_get_vpd_size(sdev, page);
if (vpd_len <= 0 )
return -EINVAL;
vpd_len = min(vpd_len, buf_len);
/*
* Fetch the actual page . Since the appropriate size was reported
* by the device it is now safe to ask for something bigger .
*/
memset(buf, 0 , buf_len);
result = scsi_vpd_inquiry(sdev, buf, page, vpd_len);
if (result < 0 )
return -EINVAL;
else if (result > vpd_len)
dev_warn_once(&sdev->sdev_gendev,
"%s: VPD page 0x%02x result %d > %d bytes\n" ,
__func__, page, result, vpd_len);
return 0 ;
}
EXPORT_SYMBOL_GPL(scsi_get_vpd_page);
/**
* scsi_get_vpd_buf - Get Vital Product Data from a SCSI device
* @ sdev : The device to ask
* @ page : Which Vital Product Data to return
*
* Returns % NULL upon failure .
*/
static struct scsi_vpd *scsi_get_vpd_buf(struct scsi_device *sdev, u8 page)
{
struct scsi_vpd *vpd_buf;
int vpd_len, result;
vpd_len = scsi_get_vpd_size(sdev, page);
if (vpd_len <= 0 )
return NULL;
retry_pg:
/*
* Fetch the actual page . Since the appropriate size was reported
* by the device it is now safe to ask for something bigger .
*/
vpd_buf = kmalloc(sizeof (*vpd_buf) + vpd_len, GFP_KERNEL);
if (!vpd_buf)
return NULL;
result = scsi_vpd_inquiry(sdev, vpd_buf->data, page, vpd_len);
if (result < 0 ) {
kfree(vpd_buf);
return NULL;
}
if (result > vpd_len) {
dev_warn_once(&sdev->sdev_gendev,
"%s: VPD page 0x%02x result %d > %d bytes\n" ,
__func__, page, result, vpd_len);
vpd_len = result;
kfree(vpd_buf);
goto retry_pg;
}
vpd_buf->len = result;
return vpd_buf;
}
static void scsi_update_vpd_page(struct scsi_device *sdev, u8 page,
struct scsi_vpd __rcu **sdev_vpd_buf)
{
struct scsi_vpd *vpd_buf;
vpd_buf = scsi_get_vpd_buf(sdev, page);
if (!vpd_buf)
return ;
mutex_lock(&sdev->inquiry_mutex);
vpd_buf = rcu_replace_pointer(*sdev_vpd_buf, vpd_buf,
lockdep_is_held(&sdev->inquiry_mutex));
mutex_unlock(&sdev->inquiry_mutex);
if (vpd_buf)
kfree_rcu(vpd_buf, rcu);
}
/**
* scsi_attach_vpd - Attach Vital Product Data to a SCSI device structure
* @ sdev : The device to ask
*
* Attach the ' Device Identification ' VPD page ( 0 x83 ) and the
* ' Unit Serial Number ' VPD page ( 0 x80 ) to a SCSI device
* structure . This information can be used to identify the device
* uniquely .
*/
void scsi_attach_vpd(struct scsi_device *sdev)
{
int i;
struct scsi_vpd *vpd_buf;
if (!scsi_device_supports_vpd(sdev))
return ;
/* Ask for all the pages supported by this device */
vpd_buf = scsi_get_vpd_buf(sdev, 0 );
if (!vpd_buf)
return ;
for (i = 4 ; i < vpd_buf->len; i++) {
switch (vpd_buf->data[i]) {
case 0 x0:
scsi_update_vpd_page(sdev, 0 x0, &sdev->vpd_pg0);
break ;
case 0 x80:
scsi_update_vpd_page(sdev, 0 x80, &sdev->vpd_pg80);
break ;
case 0 x83:
scsi_update_vpd_page(sdev, 0 x83, &sdev->vpd_pg83);
break ;
case 0 x89:
scsi_update_vpd_page(sdev, 0 x89, &sdev->vpd_pg89);
break ;
case 0 xb0:
scsi_update_vpd_page(sdev, 0 xb0, &sdev->vpd_pgb0);
break ;
case 0 xb1:
scsi_update_vpd_page(sdev, 0 xb1, &sdev->vpd_pgb1);
break ;
case 0 xb2:
scsi_update_vpd_page(sdev, 0 xb2, &sdev->vpd_pgb2);
break ;
case 0 xb7:
scsi_update_vpd_page(sdev, 0 xb7, &sdev->vpd_pgb7);
break ;
default :
break ;
}
}
kfree(vpd_buf);
}
/**
* scsi_report_opcode - Find out if a given command is supported
* @ sdev : scsi device to query
* @ buffer : scratch buffer ( must be at least 20 bytes long )
* @ len : length of buffer
* @ opcode : opcode for the command to look up
* @ sa : service action for the command to look up
*
* Uses the REPORT SUPPORTED OPERATION CODES to check support for the
* command identified with @ opcode and @ sa . If the command does not
* have a service action , @ sa must be 0 . Returns - EINVAL if RSOC fails ,
* 0 if the command is not supported and 1 if the device claims to
* support the command .
*/
int scsi_report_opcode(struct scsi_device *sdev, unsigned char *buffer,
unsigned int len, unsigned char opcode,
unsigned short sa)
{
unsigned char cmd[16 ];
struct scsi_sense_hdr sshdr;
int result, request_len;
const struct scsi_exec_args exec_args = {
.sshdr = &sshdr,
};
if (sdev->no_report_opcodes || sdev->scsi_level < SCSI_SPC_3)
return -EINVAL;
/* RSOC header + size of command we are asking about */
request_len = 4 + COMMAND_SIZE(opcode);
if (request_len > len) {
dev_warn_once(&sdev->sdev_gendev,
"%s: len %u bytes, opcode 0x%02x needs %u\n" ,
__func__, len, opcode, request_len);
return -EINVAL;
}
memset(cmd, 0 , 16 );
cmd[0 ] = MAINTENANCE_IN;
cmd[1 ] = MI_REPORT_SUPPORTED_OPERATION_CODES;
if (!sa) {
cmd[2 ] = 1 ; /* One command format */
cmd[3 ] = opcode;
} else {
cmd[2 ] = 3 ; /* One command format with service action */
cmd[3 ] = opcode;
put_unaligned_be16(sa, &cmd[4 ]);
}
put_unaligned_be32(request_len, &cmd[6 ]);
memset(buffer, 0 , len);
result = scsi_execute_cmd(sdev, cmd, REQ_OP_DRV_IN, buffer,
request_len, 30 * HZ, 3 , &exec_args);
if (result < 0 )
return result;
if (result && scsi_sense_valid(&sshdr) &&
sshdr.sense_key == ILLEGAL_REQUEST &&
(sshdr.asc == 0 x20 || sshdr.asc == 0 x24) && sshdr.ascq == 0 x00)
return -EINVAL;
if ((buffer[1 ] & 3 ) == 3 ) /* Command supported */
return 1 ;
return 0 ;
}
EXPORT_SYMBOL(scsi_report_opcode);
#define SCSI_CDL_CHECK_BUF_LEN 64
static bool scsi_cdl_check_cmd(struct scsi_device *sdev, u8 opcode, u16 sa,
unsigned char *buf)
{
int ret;
u8 cdlp;
/* Check operation code */
ret = scsi_report_opcode(sdev, buf, SCSI_CDL_CHECK_BUF_LEN, opcode, sa);
if (ret <= 0 )
return false ;
if ((buf[1 ] & 0 x03) != 0 x03)
return false ;
/*
* See SPC - 6 , One_command parameter data format for
* REPORT SUPPORTED OPERATION CODES . We have the following cases
* depending on rwcdlp ( buf [ 0 ] & 0 x01 ) value :
* - rwcdlp = = 0 : then cdlp indicates support for the A mode page when
* it is equal to 1 and for the B mode page when it is
* equal to 2 .
* - rwcdlp = = 1 : then cdlp indicates support for the T2A mode page
* when it is equal to 1 and for the T2B mode page when
* it is equal to 2 .
* Overall , to detect support for command duration limits , we only need
* to check that cdlp is 1 or 2 .
*/
cdlp = (buf[1 ] & 0 x18) >> 3 ;
return cdlp == 0 x01 || cdlp == 0 x02;
}
/**
* scsi_cdl_check - Check if a SCSI device supports Command Duration Limits
* @ sdev : The device to check
*/
void scsi_cdl_check(struct scsi_device *sdev)
{
bool cdl_supported;
unsigned char *buf;
/*
* Support for CDL was defined in SPC - 5 . Ignore devices reporting an
* lower SPC version . This also avoids problems with old drives choking
* on MAINTENANCE_IN / MI_REPORT_SUPPORTED_OPERATION_CODES with a
* service action specified , as done in scsi_cdl_check_cmd ( ) .
*/
if (sdev->scsi_level < SCSI_SPC_5) {
sdev->cdl_supported = 0 ;
return ;
}
buf = kmalloc(SCSI_CDL_CHECK_BUF_LEN, GFP_KERNEL);
if (!buf) {
sdev->cdl_supported = 0 ;
return ;
}
/* Check support for READ_16, WRITE_16, READ_32 and WRITE_32 commands */
cdl_supported =
scsi_cdl_check_cmd(sdev, READ_16, 0 , buf) ||
scsi_cdl_check_cmd(sdev, WRITE_16, 0 , buf) ||
scsi_cdl_check_cmd(sdev, VARIABLE_LENGTH_CMD, READ_32, buf) ||
scsi_cdl_check_cmd(sdev, VARIABLE_LENGTH_CMD, WRITE_32, buf);
if (cdl_supported) {
/*
* We have CDL support : force the use of READ16 / WRITE16 .
* READ32 and WRITE32 will be used for devices that support
* the T10_PI_TYPE2_PROTECTION protection type .
*/
sdev->use_16_for_rw = 1 ;
sdev->use_10_for_rw = 0 ;
sdev->cdl_supported = 1 ;
/*
* If the device supports CDL , make sure that the current drive
* feature status is consistent with the user controlled
* cdl_enable state .
*/
scsi_cdl_enable(sdev, sdev->cdl_enable);
} else {
sdev->cdl_supported = 0 ;
}
kfree(buf);
}
/**
* scsi_cdl_enable - Enable or disable a SCSI device supports for Command
* Duration Limits
* @ sdev : The target device
* @ enable : the target state
*/
int scsi_cdl_enable(struct scsi_device *sdev, bool enable)
{
char buf[64 ];
int ret;
if (!sdev->cdl_supported)
return -EOPNOTSUPP;
/*
* For ATA devices , CDL needs to be enabled with a SET FEATURES command .
*/
if (sdev->is_ata) {
struct scsi_mode_data data;
struct scsi_sense_hdr sshdr;
char *buf_data;
int len;
ret = scsi_mode_sense(sdev, 0 x08, 0 x0a, 0 xf2, buf, sizeof (buf),
5 * HZ, 3 , &data, NULL);
if (ret)
return -EINVAL;
/* Enable or disable CDL using the ATA feature page */
len = min_t(size_t, sizeof (buf),
data.length - data.header_length -
data.block_descriptor_length);
buf_data = buf + data.header_length +
data.block_descriptor_length;
/*
* If we want to enable CDL and CDL is already enabled on the
* device , do nothing . This avoids needlessly resetting the CDL
* statistics on the device as that is implied by the CDL enable
* action . Similar to this , there is no need to do anything if
* we want to disable CDL and CDL is already disabled .
*/
if (enable) {
if ((buf_data[4 ] & 0 x03) == 0 x02)
goto out;
buf_data[4 ] &= ~0 x03;
buf_data[4 ] |= 0 x02;
} else {
if ((buf_data[4 ] & 0 x03) == 0 x00)
goto out;
buf_data[4 ] &= ~0 x03;
}
ret = scsi_mode_select(sdev, 1 , 0 , buf_data, len, 5 * HZ, 3 ,
&data, &sshdr);
if (ret) {
if (ret > 0 && scsi_sense_valid(&sshdr))
scsi_print_sense_hdr(sdev,
dev_name(&sdev->sdev_gendev), &sshdr);
return ret;
}
}
out:
sdev->cdl_enable = enable;
return 0 ;
}
/**
* scsi_device_get - get an additional reference to a scsi_device
* @ sdev : device to get a reference to
*
* 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 .
*
* This will fail if a device is deleted or cancelled , or when the LLD module
* is in the process of being unloaded .
*/
int scsi_device_get(struct scsi_device *sdev)
{
if (sdev->sdev_state == SDEV_DEL || sdev->sdev_state == SDEV_CANCEL)
goto fail;
if (!try_module_get(sdev->host->hostt->module))
goto fail;
if (!get_device(&sdev->sdev_gendev))
goto fail_put_module;
return 0 ;
fail_put_module:
module_put(sdev->host->hostt->module);
fail:
return -ENXIO;
}
EXPORT_SYMBOL(scsi_device_get);
/**
* scsi_device_put - release a reference to a scsi_device
* @ sdev : device to release a reference on .
*
* 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)
{
struct module *mod = sdev->host->hostt->module;
put_device(&sdev->sdev_gendev);
module_put(mod);
}
EXPORT_SYMBOL(scsi_device_put);
/* helper for shost_for_each_device, see that for documentation */
struct scsi_device *__scsi_iterate_devices(struct Scsi_Host *shost,
struct scsi_device *prev)
{
struct list_head *list = (prev ? &prev->siblings : &shost->__devices);
struct scsi_device *next = NULL;
unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags);
while (list->next != &shost->__devices) {
next = list_entry(list->next, struct scsi_device, siblings);
/* skip devices that we can't get a reference to */
if (!scsi_device_get(next))
break ;
next = NULL;
list = list->next;
}
spin_unlock_irqrestore(shost->host_lock, flags);
if (prev)
scsi_device_put(prev);
return next;
}
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
* out of the loop .
*/
void starget_for_each_device(struct scsi_target *starget, void *data,
void (*fn)(struct scsi_device *, void *))
{
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
struct scsi_device *sdev;
shost_for_each_device(sdev, shost) {
if ((sdev->channel == starget->channel) &&
(sdev->id == starget->id))
fn(sdev, data);
}
}
EXPORT_SYMBOL(starget_for_each_device);
/**
* _ _ 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
* protected by shost - > host_lock .
*
* Note : The only reason why drivers would want to use this is because
* they need to access the device list in irq context . Otherwise you
* really want to use starget_for_each_device instead .
**/
void __starget_for_each_device(struct scsi_target *starget, void *data,
void (*fn)(struct scsi_device *, void *))
{
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
struct scsi_device *sdev;
__shost_for_each_device(sdev, shost) {
if ((sdev->channel == starget->channel) &&
(sdev->id == starget->id))
fn(sdev, data);
}
}
EXPORT_SYMBOL(__starget_for_each_device);
/**
* _ _ scsi_device_lookup_by_target - find a device given the target ( UNLOCKED )
* @ starget : SCSI target pointer
* @ lun : SCSI Logical Unit Number
*
* 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 . A scsi_device in state
* SDEV_DEL is skipped .
*
* 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,
u64 lun)
{
struct scsi_device *sdev;
list_for_each_entry(sdev, &starget->devices, same_target_siblings) {
if (sdev->sdev_state == SDEV_DEL)
continue ;
if (sdev->lun ==lun)
return sdev;
}
return NULL;
}
EXPORT_SYMBOL(__scsi_device_lookup_by_target);
/**
* scsi_device_lookup_by_target - find a device given the target
* @ starget : SCSI target pointer
* @ lun : SCSI Logical Unit Number
*
* Description : Looks up the scsi_device with the specified @ lun for a given
* @ starget . 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,
u64 lun)
{
struct scsi_device *sdev;
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags);
sdev = __scsi_device_lookup_by_target(starget, lun);
if (sdev && scsi_device_get(sdev))
sdev = NULL;
spin_unlock_irqrestore(shost->host_lock, flags);
return sdev;
}
EXPORT_SYMBOL(scsi_device_lookup_by_target);
/**
* _ _ scsi_device_lookup - find a device given the host ( UNLOCKED )
* @ shost : SCSI host pointer
* @ channel : SCSI channel ( zero if only one channel )
* @ id : SCSI target number ( physical unit number )
* @ lun : SCSI Logical Unit Number
*
* 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 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,
uint channel, uint id, u64 lun)
{
struct scsi_device *sdev;
list_for_each_entry(sdev, &shost->__devices, siblings) {
if (sdev->sdev_state == SDEV_DEL)
continue ;
if (sdev->channel == channel && sdev->id == id &&
sdev->lun ==lun)
return sdev;
}
return NULL;
}
EXPORT_SYMBOL(__scsi_device_lookup);
/**
* scsi_device_lookup - find a device given the host
* @ shost : SCSI host pointer
* @ channel : SCSI channel ( zero if only one channel )
* @ id : SCSI target number ( physical unit number )
* @ lun : SCSI Logical Unit Number
*
* 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, u64 lun)
{
struct scsi_device *sdev;
unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags);
sdev = __scsi_device_lookup(shost, channel, id, lun);
if (sdev && scsi_device_get(sdev))
sdev = NULL;
spin_unlock_irqrestore(shost->host_lock, flags);
return sdev;
}
EXPORT_SYMBOL(scsi_device_lookup);
MODULE_DESCRIPTION("SCSI core" );
MODULE_LICENSE("GPL" );
module_param(scsi_logging_level, int , S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(scsi_logging_level, "a bit mask of logging levels" );
static int __init init_scsi(void )
{
int error;
error = scsi_init_procfs();
if (error)
goto cleanup_queue;
error = scsi_init_devinfo();
if (error)
goto cleanup_procfs;
error = scsi_init_hosts();
if (error)
goto cleanup_devlist;
error = scsi_init_sysctl();
if (error)
goto cleanup_hosts;
error = scsi_sysfs_register();
if (error)
goto cleanup_sysctl;
scsi_netlink_init();
printk(KERN_NOTICE "SCSI subsystem initialized\n" );
return 0 ;
cleanup_sysctl:
scsi_exit_sysctl();
cleanup_hosts:
scsi_exit_hosts();
cleanup_devlist:
scsi_exit_devinfo();
cleanup_procfs:
scsi_exit_procfs();
cleanup_queue:
scsi_exit_queue();
printk(KERN_ERR "SCSI subsystem failed to initialize, error = %d\n" ,
-error);
return error;
}
static void __exit exit_scsi(void )
{
scsi_netlink_exit();
scsi_sysfs_unregister();
scsi_exit_sysctl();
scsi_exit_hosts();
scsi_exit_devinfo();
scsi_exit_procfs();
scsi_exit_queue();
}
subsys_initcall(init_scsi);
module_exit(exit_scsi);
Messung V0.5 in Prozent C=96 H=93 G=94
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.23Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-28)
¤
*Eine klare Vorstellung vom Zielzustand
2026-10-10
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126438
#Domains=1897691