cregit-Linux how code gets into the kernel

Release 4.11 drivers/scsi/storvsc_drv.c

Directory: drivers/scsi
/*
 * Copyright (c) 2009, Microsoft Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place - Suite 330, Boston, MA 02111-1307 USA.
 *
 * Authors:
 *   Haiyang Zhang <haiyangz@microsoft.com>
 *   Hank Janssen  <hjanssen@microsoft.com>
 *   K. Y. Srinivasan <kys@microsoft.com>
 */

#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/completion.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/hyperv.h>
#include <linux/blkdev.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_devinfo.h>
#include <scsi/scsi_dbg.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/scsi_transport.h>

/*
 * All wire protocol details (storage protocol between the guest and the host)
 * are consolidated here.
 *
 * Begin protocol definitions.
 */

/*
 * Version history:
 * V1 Beta: 0.1
 * V1 RC < 2008/1/31: 1.0
 * V1 RC > 2008/1/31:  2.0
 * Win7: 4.2
 * Win8: 5.1
 * Win8.1: 6.0
 * Win10: 6.2
 */


#define VMSTOR_PROTO_VERSION(MAJOR_, MINOR_)	((((MAJOR_) & 0xff) << 8) | \
                                                (((MINOR_) & 0xff)))


#define VMSTOR_PROTO_VERSION_WIN6	VMSTOR_PROTO_VERSION(2, 0)

#define VMSTOR_PROTO_VERSION_WIN7	VMSTOR_PROTO_VERSION(4, 2)

#define VMSTOR_PROTO_VERSION_WIN8	VMSTOR_PROTO_VERSION(5, 1)

#define VMSTOR_PROTO_VERSION_WIN8_1	VMSTOR_PROTO_VERSION(6, 0)

#define VMSTOR_PROTO_VERSION_WIN10	VMSTOR_PROTO_VERSION(6, 2)

/*  Packet structure describing virtual storage requests. */

enum vstor_packet_operation {
	
VSTOR_OPERATION_COMPLETE_IO		= 1,
	
VSTOR_OPERATION_REMOVE_DEVICE		= 2,
	
VSTOR_OPERATION_EXECUTE_SRB		= 3,
	
VSTOR_OPERATION_RESET_LUN		= 4,
	
VSTOR_OPERATION_RESET_ADAPTER		= 5,
	
VSTOR_OPERATION_RESET_BUS		= 6,
	
VSTOR_OPERATION_BEGIN_INITIALIZATION	= 7,
	
VSTOR_OPERATION_END_INITIALIZATION	= 8,
	
VSTOR_OPERATION_QUERY_PROTOCOL_VERSION	= 9,
	
VSTOR_OPERATION_QUERY_PROPERTIES	= 10,
	
VSTOR_OPERATION_ENUMERATE_BUS		= 11,
	
VSTOR_OPERATION_FCHBA_DATA              = 12,
	
VSTOR_OPERATION_CREATE_SUB_CHANNELS     = 13,
	
VSTOR_OPERATION_MAXIMUM                 = 13
};

/*
 * WWN packet for Fibre Channel HBA
 */


struct hv_fc_wwn_packet {
	
u8	primary_active;
	
u8	reserved1[3];
	
u8	primary_port_wwn[8];
	
u8	primary_node_wwn[8];
	
u8	secondary_port_wwn[8];
	
u8	secondary_node_wwn[8];
};



/*
 * SRB Flag Bits
 */


#define SRB_FLAGS_QUEUE_ACTION_ENABLE		0x00000002

#define SRB_FLAGS_DISABLE_DISCONNECT		0x00000004

#define SRB_FLAGS_DISABLE_SYNCH_TRANSFER	0x00000008

#define SRB_FLAGS_BYPASS_FROZEN_QUEUE		0x00000010

#define SRB_FLAGS_DISABLE_AUTOSENSE		0x00000020

#define SRB_FLAGS_DATA_IN			0x00000040

#define SRB_FLAGS_DATA_OUT			0x00000080

#define SRB_FLAGS_NO_DATA_TRANSFER		0x00000000

#define SRB_FLAGS_UNSPECIFIED_DIRECTION	(SRB_FLAGS_DATA_IN | SRB_FLAGS_DATA_OUT)

#define SRB_FLAGS_NO_QUEUE_FREEZE		0x00000100

#define SRB_FLAGS_ADAPTER_CACHE_ENABLE		0x00000200

#define SRB_FLAGS_FREE_SENSE_BUFFER		0x00000400

/*
 * This flag indicates the request is part of the workflow for processing a D3.
 */

#define SRB_FLAGS_D3_PROCESSING			0x00000800

#define SRB_FLAGS_IS_ACTIVE			0x00010000

#define SRB_FLAGS_ALLOCATED_FROM_ZONE		0x00020000

#define SRB_FLAGS_SGLIST_FROM_POOL		0x00040000

#define SRB_FLAGS_BYPASS_LOCKED_QUEUE		0x00080000

#define SRB_FLAGS_NO_KEEP_AWAKE			0x00100000

#define SRB_FLAGS_PORT_DRIVER_ALLOCSENSE	0x00200000

#define SRB_FLAGS_PORT_DRIVER_SENSEHASPORT	0x00400000

#define SRB_FLAGS_DONT_START_NEXT_PACKET	0x00800000

#define SRB_FLAGS_PORT_DRIVER_RESERVED		0x0F000000

#define SRB_FLAGS_CLASS_DRIVER_RESERVED		0xF0000000


#define SP_UNTAGGED			((unsigned char) ~0)

#define SRB_SIMPLE_TAG_REQUEST		0x20

/*
 * Platform neutral description of a scsi request -
 * this remains the same across the write regardless of 32/64 bit
 * note: it's patterned off the SCSI_PASS_THROUGH structure
 */

#define STORVSC_MAX_CMD_LEN			0x10


#define POST_WIN7_STORVSC_SENSE_BUFFER_SIZE	0x14

#define PRE_WIN8_STORVSC_SENSE_BUFFER_SIZE	0x12


#define STORVSC_SENSE_BUFFER_SIZE		0x14

#define STORVSC_MAX_BUF_LEN_WITH_PADDING	0x14

/*
 * Sense buffer size changed in win8; have a run-time
 * variable to track the size we should use.  This value will
 * likely change during protocol negotiation but it is valid
 * to start by assuming pre-Win8.
 */

static int sense_buffer_size = PRE_WIN8_STORVSC_SENSE_BUFFER_SIZE;

/*
 * The storage protocol version is determined during the
 * initial exchange with the host.  It will indicate which
 * storage functionality is available in the host.
*/

static int vmstor_proto_version;


#define STORVSC_LOGGING_NONE	0

#define STORVSC_LOGGING_ERROR	1

#define STORVSC_LOGGING_WARN	2


static int logging_level = STORVSC_LOGGING_ERROR;
module_param(logging_level, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(logging_level,
	"Logging level, 0 - None, 1 - Error (default), 2 - Warning.");


static inline bool do_logging(int level) { return logging_level >= level; }

Contributors

PersonTokensPropCommitsCommitProp
Long Li15100.00%1100.00%
Total15100.00%1100.00%

#define storvsc_log(dev, level, fmt, ...) \ do { \ if (do_logging(level)) \ dev_warn(&(dev)->device, fmt, ##__VA_ARGS__); \ } while (0) struct vmscsi_win8_extension { /* * The following were added in Windows 8 */ u16 reserve; u8 queue_tag; u8 queue_action; u32 srb_flags; u32 time_out_value; u32 queue_sort_ey; } __packed; struct vmscsi_request { u16 length; u8 srb_status; u8 scsi_status; u8 port_number; u8 path_id; u8 target_id; u8 lun; u8 cdb_length; u8 sense_info_length; u8 data_in; u8 reserved; u32 data_transfer_length; union { u8 cdb[STORVSC_MAX_CMD_LEN]; u8 sense_data[STORVSC_SENSE_BUFFER_SIZE]; u8 reserved_array[STORVSC_MAX_BUF_LEN_WITH_PADDING]; }; /* * The following was added in win8. */ struct vmscsi_win8_extension win8_extension; } __attribute((packed)); /* * The size of the vmscsi_request has changed in win8. The * additional size is because of new elements added to the * structure. These elements are valid only when we are talking * to a win8 host. * Track the correction to size we need to apply. This value * will likely change during protocol negotiation but it is * valid to start by assuming pre-Win8. */ static int vmscsi_size_delta = sizeof(struct vmscsi_win8_extension); /* * The list of storage protocols in order of preference. */ struct vmstor_protocol { int protocol_version; int sense_buffer_size; int vmscsi_size_delta; }; static const struct vmstor_protocol vmstor_protocols[] = { { VMSTOR_PROTO_VERSION_WIN10, POST_WIN7_STORVSC_SENSE_BUFFER_SIZE, 0 }, { VMSTOR_PROTO_VERSION_WIN8_1, POST_WIN7_STORVSC_SENSE_BUFFER_SIZE, 0 }, { VMSTOR_PROTO_VERSION_WIN8, POST_WIN7_STORVSC_SENSE_BUFFER_SIZE, 0 }, { VMSTOR_PROTO_VERSION_WIN7, PRE_WIN8_STORVSC_SENSE_BUFFER_SIZE, sizeof(struct vmscsi_win8_extension), }, { VMSTOR_PROTO_VERSION_WIN6, PRE_WIN8_STORVSC_SENSE_BUFFER_SIZE, sizeof(struct vmscsi_win8_extension), } }; /* * This structure is sent during the initialization phase to get the different * properties of the channel. */ #define STORAGE_CHANNEL_SUPPORTS_MULTI_CHANNEL 0x1 struct vmstorage_channel_properties { u32 reserved; u16 max_channel_cnt; u16 reserved1; u32 flags; u32 max_transfer_bytes; u64 reserved2; } __packed; /* This structure is sent during the storage protocol negotiations. */ struct vmstorage_protocol_version { /* Major (MSW) and minor (LSW) version numbers. */ u16 major_minor; /* * Revision number is auto-incremented whenever this file is changed * (See FILL_VMSTOR_REVISION macro above). Mismatch does not * definitely indicate incompatibility--but it does indicate mismatched * builds. * This is only used on the windows side. Just set it to 0. */ u16 revision; } __packed; /* Channel Property Flags */ #define STORAGE_CHANNEL_REMOVABLE_FLAG 0x1 #define STORAGE_CHANNEL_EMULATED_IDE_FLAG 0x2 struct vstor_packet { /* Requested operation type */ enum vstor_packet_operation operation; /* Flags - see below for values */ u32 flags; /* Status of the request returned from the server side. */ u32 status; /* Data payload area */ union { /* * Structure used to forward SCSI commands from the * client to the server. */ struct vmscsi_request vm_srb; /* Structure used to query channel properties. */ struct vmstorage_channel_properties storage_channel_properties; /* Used during version negotiations. */ struct vmstorage_protocol_version version; /* Fibre channel address packet */ struct hv_fc_wwn_packet wwn_packet; /* Number of sub-channels to create */ u16 sub_channel_count; /* This will be the maximum of the union members */ u8 buffer[0x34]; }; } __packed; /* * Packet Flags: * * This flag indicates that the server should send back a completion for this * packet. */ #define REQUEST_COMPLETION_FLAG 0x1 /* Matches Windows-end */ enum storvsc_request_type { WRITE_TYPE = 0, READ_TYPE, UNKNOWN_TYPE, }; /* * SRB status codes and masks; a subset of the codes used here. */ #define SRB_STATUS_AUTOSENSE_VALID 0x80 #define SRB_STATUS_QUEUE_FROZEN 0x40 #define SRB_STATUS_INVALID_LUN 0x20 #define SRB_STATUS_SUCCESS 0x01 #define SRB_STATUS_ABORTED 0x02 #define SRB_STATUS_ERROR 0x04 #define SRB_STATUS_DATA_OVERRUN 0x12 #define SRB_STATUS(status) \ (status & ~(SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_QUEUE_FROZEN)) /* * This is the end of Protocol specific defines. */ static int storvsc_ringbuffer_size = (256 * PAGE_SIZE); static u32 max_outstanding_req_per_channel; static int storvsc_vcpus_per_sub_channel = 4; module_param(storvsc_ringbuffer_size, int, S_IRUGO); MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); module_param(storvsc_vcpus_per_sub_channel, int, S_IRUGO); MODULE_PARM_DESC(storvsc_vcpus_per_sub_channel, "Ratio of VCPUs to subchannels"); /* * Timeout in seconds for all devices managed by this driver. */ static int storvsc_timeout = 180; #if IS_ENABLED(CONFIG_SCSI_FC_ATTRS) static struct scsi_transport_template *fc_transport_template; #endif static void storvsc_on_channel_callback(void *context); #define STORVSC_MAX_LUNS_PER_TARGET 255 #define STORVSC_MAX_TARGETS 2 #define STORVSC_MAX_CHANNELS 8 #define STORVSC_FC_MAX_LUNS_PER_TARGET 255 #define STORVSC_FC_MAX_TARGETS 128 #define STORVSC_FC_MAX_CHANNELS 8 #define STORVSC_IDE_MAX_LUNS_PER_TARGET 64 #define STORVSC_IDE_MAX_TARGETS 1 #define STORVSC_IDE_MAX_CHANNELS 1 struct storvsc_cmd_request { struct scsi_cmnd *cmd; struct hv_device *device; /* Synchronize the request/response if needed */ struct completion wait_event; struct vmbus_channel_packet_multipage_buffer mpb; struct vmbus_packet_mpb_array *payload; u32 payload_sz; struct vstor_packet vstor_packet; }; /* A storvsc device is a device object that contains a vmbus channel */ struct storvsc_device { struct hv_device *device; bool destroy; bool drain_notify; bool open_sub_channel; atomic_t num_outstanding_req; struct Scsi_Host *host; wait_queue_head_t waiting_to_drain; /* * Each unique Port/Path/Target represents 1 channel ie scsi * controller. In reality, the pathid, targetid is always 0 * and the port is set by us */ unsigned int port_number; unsigned char path_id; unsigned char target_id; /* * Max I/O, the device can support. */ u32 max_transfer_bytes; /* * Number of sub-channels we will open. */ u16 num_sc; struct vmbus_channel **stor_chns; /* * Mask of CPUs bound to subchannels. */ struct cpumask alloced_cpus; /* Used for vsc/vsp channel reset process */ struct storvsc_cmd_request init_request; struct storvsc_cmd_request reset_request; /* * Currently active port and node names for FC devices. */ u64 node_name; u64 port_name; }; struct hv_host_device { struct hv_device *dev; unsigned int port; unsigned char path; unsigned char target; }; struct storvsc_scan_work { struct work_struct work; struct Scsi_Host *host; u8 lun; u8 tgt_id; };
static void storvsc_device_scan(struct work_struct *work) { struct storvsc_scan_work *wrk; struct scsi_device *sdev; wrk = container_of(work, struct storvsc_scan_work, work); sdev = scsi_device_lookup(wrk->host, 0, wrk->tgt_id, wrk->lun); if (!sdev) goto done; scsi_rescan_device(&sdev->sdev_gendev); scsi_device_put(sdev); done: kfree(wrk); }

Contributors

PersonTokensPropCommitsCommitProp
K. Y. Srinivasan80100.00%2100.00%
Total80100.00%2100.00%


static void storvsc_host_scan(struct work_struct *work) { struct storvsc_scan_work *wrk; struct Scsi_Host *host; struct scsi_device *sdev; wrk = container_of(work, struct storvsc_scan_work, work); host = wrk->host; /* * Before scanning the host, first check to see if any of the * currrently known devices have been hot removed. We issue a * "unit ready" command against all currently known devices. * This I/O will result in an error for devices that have been * removed. As part of handling the I/O error, we remove the device. * * When a LUN is added or removed, the host sends us a signal to * scan the host. Thus we are forced to discover the LUNs that * may have been removed this way. */ mutex_lock(&host->scan_mutex); shost_for_each_device(sdev, host) scsi_test_unit_ready(sdev, 1, 1, NULL); mutex_unlock(&host->scan_mutex); /* * Now scan the host to discover LUNs that may have been added. */ scsi_scan_host(host); kfree(wrk); }

Contributors

PersonTokensPropCommitsCommitProp
K. Y. Srinivasan8797.75%375.00%
Vitaly Kuznetsov22.25%125.00%
Total89100.00%4100.00%


static void storvsc_remove_lun(struct work_struct *work) { struct storvsc_scan_work *wrk; struct scsi_device *sdev; wrk = container_of(work, struct storvsc_scan_work, work); if (!scsi_host_get(wrk->host)) goto done; sdev = scsi_device_lookup(wrk->host, 0, wrk->tgt_id, wrk->lun); if (sdev) { scsi_remove_device(sdev); scsi_device_put(sdev); } scsi_host_put(wrk->host); done: kfree(wrk); }

Contributors

PersonTokensPropCommitsCommitProp
K. Y. Srinivasan95100.00%2100.00%
Total95100.00%2100.00%

/* * We can get incoming messages from the host that are not in response to * messages that we have sent out. An example of this would be messages * received by the guest to notify dynamic addition/removal of LUNs. To * deal with potential race conditions where the driver may be in the * midst of being unloaded when we might receive an unsolicited message * from the host, we have implemented a mechanism to gurantee sequential * consistency: * * 1) Once the device is marked as being destroyed, we will fail all * outgoing messages. * 2) We permit incoming messages when the device is being destroyed, * only to properly account for messages already sent out. */
static inline struct storvsc_device *get_out_stor_device( struct hv_device *device) { struct storvsc_device *stor_device; stor_device = hv_get_drvdata(device); if (stor_device && stor_device->destroy) stor_device = NULL; return stor_device; }

Contributors

PersonTokensPropCommitsCommitProp
K. Y. Srinivasan41100.00%2100.00%
Total41100.00%2100.00%


static inline void storvsc_wait_to_drain(struct storvsc_device *dev) { dev->drain_notify = true; wait_event(dev->waiting_to_drain, atomic_read(&dev->num_outstanding_req) == 0); dev->drain_notify = false; }

Contributors

PersonTokensPropCommitsCommitProp
K. Y. Srinivasan2356.10%480.00%
Vitaly Kuznetsov1843.90%120.00%
Total41100.00%5100.00%


static inline struct storvsc_device *get_in_stor_device( struct hv_device *device) { struct storvsc_device *stor_device; stor_device = hv_get_drvdata(device); if (!stor_device) goto get_in_err; /* * If the device is being destroyed; allow incoming * traffic only to cleanup outstanding requests. */ if (stor_device->destroy && (atomic_read(&stor_device->num_outstanding_req) == 0)) stor_device = NULL; get_in_err: return stor_device; }

Contributors

PersonTokensPropCommitsCommitProp
Vitaly Kuznetsov4877.42%116.67%
K. Y. Srinivasan1422.58%583.33%
Total62100.00%6100.00%


static void handle_sc_creation(struct vmbus_channel *new_sc) { struct hv_device *device = new_sc->primary_channel->device_obj; struct storvsc_device *stor_device; struct vmstorage_channel_properties props; stor_device = get_out_stor_device(device); if (!stor_device) return; if (stor_device->open_sub_channel == false) return; memset(&props, 0, sizeof(struct vmstorage_channel_properties)); vmbus_open(new_sc, storvsc_ringbuffer_size, storvsc_ringbuffer_size, (void *)&props, sizeof(struct vmstorage_channel_properties), storvsc_on_channel_callback, new_sc); if (new_sc->state == CHANNEL_OPENED_STATE) { stor_device->stor_chns[new_sc->target_cpu] = new_sc; cpumask_set_cpu(new_sc->target_cpu, &stor_device->alloced_cpus); } }

Contributors

PersonTokensPropCommitsCommitProp
K. Y. Srinivasan126100.00%2100.00%
Total126100.00%2100.00%


static void handle_multichannel_storage(struct hv_device *device, int max_chns) { struct storvsc_device *stor_device; int num_cpus = num_online_cpus(); int num_sc; struct storvsc_cmd_request *request; struct vstor_packet *vstor_packet; int ret, t; num_sc = ((max_chns > num_cpus) ? num_cpus : max_chns); stor_device = get_out_stor_device(device); if (!stor_device) return; stor_device->num_sc = num_sc; request = &stor_device->init_request; vstor_packet = &request->vstor_packet; stor_device->open_sub_channel = true; /* * Establish a handler for dealing with subchannels. */ vmbus_set_sc_create_callback(device->channel, handle_sc_creation); /* * Check to see if sub-channels have already been created. This * can happen when this driver is re-loaded after unloading. */ if (vmbus_are_subchannels_present(device->channel)) return; stor_device->open_sub_channel = false; /* * Request the host to create sub-channels. */ memset(request, 0, sizeof(struct storvsc_cmd_request)); init_completion(&request->wait_event); vstor_packet->operation = VSTOR_OPERATION_CREATE_SUB_CHANNELS; vstor_packet->flags = REQUEST_COMPLETION_FLAG; vstor_packet->sub_channel_count = num_sc; ret = vmbus_sendpacket(device->channel, vstor_packet, (sizeof(struct vstor_packet) - vmscsi_size_delta), (unsigned long)request, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); if (ret != 0) return; t = wait_for_completion_timeout(&request->wait_event, 10*HZ); if (t == 0) return; if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || vstor_packet->status != 0) return; /* * Now that we created the sub-channels, invoke the check; this * may trigger the callback. */ stor_device->open_sub_channel = true; vmbus_are_subchannels_present(device->channel); }

Contributors

PersonTokensPropCommitsCommitProp
K. Y. Srinivasan251100.00%2100.00%
Total251100.00%2100.00%


static void cache_wwn(struct storvsc_device *stor_device, struct vstor_packet *vstor_packet) { /* * Cache the currently active port and node ww names. */ if (vstor_packet->wwn_packet.primary_active) { stor_device->node_name = wwn_to_u64(vstor_packet->wwn_packet.primary_node_wwn); stor_device->port_name = wwn_to_u64(vstor_packet->wwn_packet.primary_port_wwn); } else { stor_device->node_name = wwn_to_u64(vstor_packet->wwn_packet.secondary_node_wwn); stor_device->port_name = wwn_to_u64(vstor_packet->wwn_packet.secondary_port_wwn); } }

Contributors

PersonTokensPropCommitsCommitProp
K. Y. Srinivasan82100.00%1100.00%
Total82100.00%1100.00%


static int storvsc_execute_vstor_op(struct hv_device *device, struct storvsc_cmd_request *request, bool status_check) { struct vstor_packet *vstor_packet; int ret, t; vstor_packet = &request->vstor_packet; init_completion(&request->wait_event); vstor_packet->flags = REQUEST_COMPLETION_FLAG; ret = vmbus_sendpacket(device->channel, vstor_packet, (sizeof(struct vstor_packet) - vmscsi_size_delta), (unsigned long)request, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); if (ret != 0) return ret; t = wait_for_completion_timeout(&request->wait_event, 5*HZ); if (t == 0) return -ETIMEDOUT; if (!status_check) return ret; if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || vstor_packet->status != 0) return -EINVAL; return ret; }

Contributors

PersonTokensPropCommitsCommitProp
K. Y. Srinivasan143100.00%8100.00%
Total143100.00%8100.00%


static int storvsc_channel_init(struct hv_device *device, bool is_fc) { struct storvsc_device *stor_device; struct storvsc_cmd_request *request; struct vstor_packet *vstor_packet; int ret, i; int max_chns; bool process_sub_channels = false; stor_device = get_out_stor_device(device); if (!stor_device) return -ENODEV; request = &stor_device->init_request; vstor_packet = &request->vstor_packet; /* * Now, initiate the vsc/vsp initialization protocol on the open * channel */ memset(request, 0, sizeof(struct storvsc_cmd_request)); vstor_packet->operation = VSTOR_OPERATION_BEGIN_INITIALIZATION; ret = storvsc_execute_vstor_op(device, request, true); if (ret) return ret; /* * Query host supported protocol version. */ for (i = 0; i < ARRAY_SIZE(vmstor_protocols); i++) { /* reuse the packet for version range supported */ memset(vstor_packet, 0, sizeof(struct vstor_packet)); vstor_packet->operation = VSTOR_OPERATION_QUERY_PROTOCOL_VERSION; vstor_packet->version.major_minor = vmstor_protocols[i].protocol_version; /* * The revision number is only used in Windows; set it to 0. */ vstor_packet->version.revision = 0; ret = storvsc_execute_vstor_op(device, request, false); if (ret != 0) return ret; if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO) return -EINVAL; if (vstor_packet->status == 0) { vmstor_proto_version = vmstor_protocols[i].protocol_version; sense_buffer_size = vmstor_protocols[i].sense_buffer_size; vmscsi_size_delta = vmstor_protocols[i].vmscsi_size_delta; break; } } if (vstor_packet->status != 0) return -EINVAL; memset(vstor_packet, 0, sizeof(struct vstor_packet)); vstor_packet->operation = VSTOR_OPERATION_QUERY_PROPERTIES; ret = storvsc_execute_vstor_op(device, request, true); if (ret != 0) return ret; /* * Check to see if multi-channel support is there. * Hosts that implement protocol version of 5.1 and above * support multi-channel. */ max_chns = vstor_packet->storage_channel_properties.max_channel_cnt; /* * Allocate state to manage the sub-channels. * We allocate an array based on the numbers of possible CPUs * (Hyper-V does not support cpu online/offline). * This Array will be sparseley populated with unique * channels - primary + sub-channels. * We will however populate all the slots to evenly distribute * the load. */ stor_device->stor_chns = kzalloc(sizeof(void *) * num_possible_cpus(), GFP_KERNEL); if (stor_device->stor_chns == NULL) return -ENOMEM; stor_device->stor_chns[device->channel->target_cpu] = device->channel; cpumask_set_cpu(device->channel->target_cpu, &stor_device->alloced_cpus); if (vmstor_proto_version >= VMSTOR_PROTO_VERSION_WIN8) { if (vstor_packet->storage_channel_properties.flags & STORAGE_CHANNEL_SUPPORTS_MULTI_CHANNEL) process_sub_channels = true; } stor_device->max_transfer_bytes = vstor_packet->storage_channel_properties.max_transfer_bytes; if (!is_fc) goto done; /* * For FC devices retrieve FC HBA data. */ memset(vstor_packet, 0, sizeof(struct vstor_packet)); vstor_packet->operation = VSTOR_OPERATION_FCHBA_DATA; ret = storvsc_execute_vstor_op(device, request, true); if (ret != 0) return ret; /* * Cache the currently active port and node ww names. */ cache_wwn(stor_device, vstor_packet); done: memset(vstor_packet, 0, sizeof(struct vstor_packet)); vstor_packet->operation = VSTOR_OPERATION_END_INITIALIZATION; ret = storvsc_execute_vstor_op(device, request, true); if (ret != 0) return ret; if (process_sub_channels) handle_multichannel_storage(device, max_chns); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
K. Y. Srinivasan43185.52%1076.92%
Keith Mange7314.48%323.08%
Total504100.00%13100.00%


static void storvsc_handle_error(struct vmscsi_request *vm_srb, struct scsi_cmnd *scmnd, struct Scsi_Host *host, u8 asc, u8 ascq) { struct storvsc_scan_work *wrk; void (*process_err_fn)(struct work_struct *work); bool do_work = false; switch (SRB_STATUS(vm_srb->srb_status)) { case SRB_STATUS_ERROR: /* * Let upper layer deal with error when * sense message is present. */ if (vm_srb->srb_status & SRB_STATUS_AUTOSENSE_VALID) break; /* * If there is an error; offline the device since all * error recovery strategies would have already been * deployed on the host side. However, if the command * were a pass-through command deal with it appropriately. */ switch (scmnd->cmnd[0]) { case ATA_16: case ATA_12: set_host_byte(scmnd, DID_PASSTHROUGH); break; /* * On Some Windows hosts TEST_UNIT_READY command can return * SRB_STATUS_ERROR, let the upper level code deal with it * based on the sense information. */ case TEST_UNIT_READY: break; default: set_host_byte(scmnd, DID_TARGET_FAILURE); } break; case SRB_STATUS_INVALID_LUN: do_work = true; process_err_fn = storvsc_remove_lun; break; case SRB_STATUS_ABORTED: if (vm_srb->srb_status & SRB_STATUS_AUTOSENSE_VALID && (asc == 0x2a) && (ascq == 0x9)) { do_work = true; process_err_fn = storvsc_device_scan; /* * Retry the I/O that trigerred this. */ set_host_byte(scmnd, DID_REQUEUE); } break; } if (!do_work) return; /* * We need to schedule work to process this error; schedule it. */ wrk = kmalloc(sizeof(struct storvsc_scan_work), GFP_ATOMIC); if (!wrk) { set_host_byte(scmnd, DID_TARGET_FAILURE); return; } wrk->host = host; wrk->lun = vm_srb->