RFC: Proposal for BSG Interface
PATCH 1/2 RFC: Proposal for BSG Interface
This patch contains the bsg interface based on
the bsg interface in FC.
Developed and submitted earlier by James Smart.
--- drivers/scsi/scsi_transport_iscsi.c | 459 ++++++++++++++++++++++++++++++++++- include/scsi/Kbuild | 1 + include/scsi/scsi_bsg_iscsi.h | 253 +++++++++++++++++++ include/scsi/scsi_transport_iscsi.h | 47 ++++ 4 files changed, 759 insertions(+), 1 deletions(-) create mode 100644 include/scsi/scsi_bsg_iscsi.h
drivers/scsi/scsi_transport_iscsi.c +458 -1
@@ -29,6 +29,8 @@#include <scsi/scsi_transport.h> #include <scsi/scsi_transport_iscsi.h> #include <scsi/iscsi_if.h> +#include <scsi/scsi_bsg_iscsi.h> +#include <scsi/scsi_cmnd.h> #define ISCSI_SESSION_ATTRS 22#define ISCSI_CONN_ATTRS 13
@@ -268,6 +270,447 @@ struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle) }EXPORT_SYMBOL_GPL (iscsi_lookup_endpoint); + +/* +* BSG support +*/ + +/** +* iscsi_destroy_bsgjob - routine to teardown/delete a iscsi bsg job +* @job: iscsi_bsg_job that is to be torn down +*/ +static void +iscsi_destroy_bsgjob (struct iscsi_bsg_job *job) +{ +unsigned long flags; + +spin_lock_irqsave (&job->job_lock, flags); +if (job->ref_cnt) { +spin_unlock_irqrestore (&job->job_lock, flags); +return ; + } +spin_unlock_irqrestore (&job->job_lock, flags); + +put_device (job->dev);/* release reference for the request */ + +kfree (job->request_payload.sg_list); +kfree (job->reply_payload.sg_list); +kfree (job); +} + +/** +* iscsi_bsg_jobdone - completion routine for bsg requests that the LLD has +* completed +* @job: iscsi_bsg_job that is complete +*/ +static void +iscsi_bsg_jobdone (struct iscsi_bsg_job *job) +{ +struct request *req = job->req; +struct request *rsp = req->next_rq; +int err; + + err = job->req->errors = job->reply->result; + +if (err <0 ) +/* we're only returning the result field in the reply */ + job->req->sense_len =sizeof (uint32_t ); +else + job->req->sense_len = job->reply_len; + +/* we assume all request payload was transferred, residual == 0 */ + req->resid_len =0 ; + +if (rsp) { +WARN_ON (job->reply->reply_payload_rcv_len > rsp->resid_len); + +/* set reply (bidi) residual */ + rsp->resid_len -=min (job->reply->reply_payload_rcv_len, + rsp->resid_len); + } +blk_complete_request (req); +} + +/** +* iscsi_bsg_softirq_done - softirq done routine for destroying the bsg requests +* @rq: BSG request that holds the job to be destroyed +*/ +static void +iscsi_bsg_softirq_done (struct request *rq) +{ +struct iscsi_bsg_job *job = rq->special; +unsigned long flags; + +spin_lock_irqsave (&job->job_lock, flags); + job->state_flags |=ISCSI_RQST_STATE_DONE ; + job->ref_cnt--; +spin_unlock_irqrestore (&job->job_lock, flags); + +blk_end_request_all (rq, rq->errors); +iscsi_destroy_bsgjob (job); +} + +/** +* iscsi_bsg_job_timeout - handler for when a bsg request timesout +* @req: request that timed out +*/ +static enum blk_eh_timer_return +iscsi_bsg_job_timeout (struct request *req) +{ +struct iscsi_bsg_job *job = (void *) req->special; +struct Scsi_Host *shost = job->shost; +struct iscsi_internal *i =to_iscsi_internal (shost->transportt); +unsigned long flags; +int err =0 , done =0 ; + +spin_lock_irqsave (&job->job_lock, flags); +if (job->state_flags &ISCSI_RQST_STATE_DONE ) + done =1 ; +else + job->ref_cnt++; +spin_unlock_irqrestore (&job->job_lock, flags); + +if (!done && i->iscsi_transport->bsg_timeout) { +/* call LLDD to abort the i/o as it has timed out */ + err = i->iscsi_transport->bsg_timeout (job); +if (err == -EAGAIN ) { + job->ref_cnt--; +return BLK_EH_RESET_TIMER ; + }else if (err) +printk (KERN_ERR "ERROR: iSCSI BSG request timeout - " +"LLD abort failed with status %d\n" , err); + } + +/* the blk_end_sync_io() doesn't check the error */ +if (done) +return BLK_EH_NOT_HANDLED ; +else +return BLK_EH_HANDLED ; +} + +static int +iscsi_bsg_map_buffer (struct iscsi_bsg_buffer *buf,struct request *req) +{ +size_t sz = (sizeof (struct scatterlist ) * req->nr_phys_segments); + +BUG_ON (!req->nr_phys_segments); + + buf->sg_list =kzalloc (sz,GFP_KERNEL ); +if (!buf->sg_list) +return -ENOMEM ; +sg_init_table (buf->sg_list, req->nr_phys_segments); + buf->sg_cnt =blk_rq_map_sg (req->q, req, buf->sg_list); + buf->payload_len =blk_rq_bytes (req); +return 0 ; +} + +/** +* iscsi_req_to_bsgjob - Allocate/create the iscsi_bsg_job structure for the +* bsg request +* @req: BSG request that needs a job structure +* @shost: SCSI Host corresponding to the bsg object +*/ +static int +iscsi_req_to_bsgjob (struct request *req,struct Scsi_Host *shost) +{ +struct iscsi_internal *i =to_iscsi_internal (shost->transportt); +struct request *rsp = req->next_rq; +struct iscsi_bsg_job *job; +int ret; + +BUG_ON (req->special); + + job =kzalloc (sizeof (struct iscsi_bsg_job ) + + i->iscsi_transport->dd_bsg_size,GFP_KERNEL ); +if (!job) +return -ENOMEM ; + +/* +* Note: this is a bit silly. +* The request gets formatted as a SGIO v4 ioctl request, which +* then gets reformatted as a blk request, which then gets +* reformatted as a iscsi bsg request. And on completion, we have +* to wrap return results such that SGIO v4 thinks it was a scsi +* status. I hope this was all worth it. +*/ + + req->special = job; + job->shost = shost; + job->req = req; +if (i->iscsi_transport->dd_bsg_size) + job->dd_data = (void *)&job[1 ]; +spin_lock_init (&job->job_lock); + job->request = (struct iscsi_bsg_request *)req->cmd; + job->request_len = req->cmd_len; + job->reply = req->sense; + job->reply_len =SCSI_SENSE_BUFFERSIZE ;/* Size of sense buffer +* allocated */ +if (req->bio) { + ret =iscsi_bsg_map_buffer (&job->request_payload, req); +if (ret) + goto failjob_rls_job; + } +if (rsp && rsp->bio) { + ret =iscsi_bsg_map_buffer (&job->reply_payload, rsp); +if (ret) + goto failjob_rls_rqst_payload; + } + job->job_done = iscsi_bsg_jobdone; + job->dev = &shost->shost_gendev; +get_device (job->dev);/* take a reference for the request */ + + job->ref_cnt =1 ; + +return 0 ; + + +failjob_rls_rqst_payload: +kfree (job->request_payload.sg_list); +failjob_rls_job: +kfree (job); +return -ENOMEM ; +} + + +enum iscsi_dispatch_result { +DISPATCH_BREAK ,/* on return, q is locked, break from q loop */ +DISPATCH_LOCKED ,/* on return, q is locked, continue on */ +DISPATCH_UNLOCKED ,/* on return, q is unlocked, continue on */ +}; + +/** +* iscsi_bsg_host_dispatch - process iscsi host bsg requests and +* dispatch to LLDD +* @q: iscsi host request queue +* @shost: scsi host request queue attached to +* @job: bsg job to be processed +*/ +static enum iscsi_dispatch_result +iscsi_bsg_host_dispatch (struct request_queue *q,struct Scsi_Host *shost, +struct iscsi_bsg_job *job) +{ +unsigned char *pdata; +struct iscsi_internal *i =to_iscsi_internal (shost->transportt); +int cmdlen =sizeof (uint32_t );/* start with length of msgcode */ +int ret, num; +unsigned int tot_len =0 ; +struct scatterlist *sge =NULL ; + +/* Validate the host command */ + job->request_payload.va =NULL ; +switch (job->request->msgcode) { +case ISCSI_BSG_HST_NET_CONFIG : + cmdlen +=sizeof (struct iscsi_bsg_host_net_config ); +/* +* Validate set ops have a request payload. +* Get ops are allowed to have none/partial/all reply payloads +*/ +if ((job->request->rqst_data.h_netconfig.set_op) && + (!job->request_payload.payload_len)) { + ret = -EINVAL ; + goto fail_host_msg; + } + tot_len =0 ; +for_each_sg (job->request_payload.sg_list, sge, + job->request_payload.sg_cnt, num) { + tot_len += sge->length; + } + + job->request_payload.va =kmalloc (tot_len,GFP_KERNEL ); +if (job->request_payload.va ==NULL ) { + ret = -EINVAL ; + goto fail_host_msg; + } + pdata = job->request_payload.va; + job->request_payload.size = tot_len; +for_each_sg (job->request_payload.sg_list, sge, + job->request_payload.sg_cnt, num) { +memcpy (pdata,sg_virt (sge), sge->length); + pdata += sge->length; + + } +break ; + +case ISCSI_BSG_HST_VENDOR : + cmdlen +=sizeof (struct iscsi_bsg_host_vendor ); +if ((shost->hostt->vendor_id ==0L ) || + (job->request->rqst_data.h_vendor.vendor_id != + shost->hostt->vendor_id)) { + ret = -ESRCH ; + goto fail_host_msg; + } +break ; + +default : + ret = -EBADR ; + goto fail_host_msg; + } + +/* check if we really have all the request data needed */ +if (job->request_len < cmdlen) { + ret = -ENOMSG ; + goto fail_host_msg; + } + + ret = i->iscsi_transport->bsg_request (job); +if (!ret) { +kfree (job->request_payload.va); +return DISPATCH_UNLOCKED ; + } + +fail_host_msg: +kfree (job->request_payload.va); + +/* return the errno failure code as the only status */ +BUG_ON (job->reply_len <sizeof (uint32_t )); + job->reply->reply_payload_rcv_len =0 ; + job->reply->result = ret; + job->reply_len =sizeof (uint32_t ); +iscsi_bsg_jobdone (job); +return DISPATCH_UNLOCKED ; +} + +/** +* iscsi_bsg_request_handler - generic handler for bsg requests +* @q: request queue to manage +* @shost: Scsi_Host related to the bsg object +* @dev: device structure for bsg object +* +* NOTE: only shost messages are expected +*/ +static void +iscsi_bsg_request_handler (struct request_queue *q,struct Scsi_Host *shost, +struct device *dev) +{ +struct request *req; +struct iscsi_bsg_job *job; +enum iscsi_dispatch_result ret; + +if (!get_device (dev)) +return ; + +while (!blk_queue_plugged (q)) { + + req =blk_fetch_request (q); +if (!req) +break ; + +spin_unlock_irq (q->queue_lock); + + ret =iscsi_req_to_bsgjob (req, shost); +if (ret) { + req->errors = ret; +blk_end_request (req, ret,blk_rq_bytes (req)); +spin_lock_irq (q->queue_lock); +continue ; + } + + job = req->special; + +/* check if we have the msgcode value at least */ +if (job->request_len <sizeof (uint32_t )) { +BUG_ON (job->reply_len <sizeof (uint32_t )); + job->reply->reply_payload_rcv_len =0 ; + job->reply->result = -ENOMSG ; + job->reply_len =sizeof (uint32_t ); +iscsi_bsg_jobdone (job); +spin_lock_irq (q->queue_lock); +continue ; + } + +/* the dispatch routines will unlock the queue_lock */ + ret =iscsi_bsg_host_dispatch (q, shost, job); + +/* did dispatcher hit state that can't process any more */ +if (ret ==DISPATCH_BREAK ) +break ; + +/* did dispatcher had released the lock */ +if (ret ==DISPATCH_UNLOCKED ) +spin_lock_irq (q->queue_lock); + } + +spin_unlock_irq (q->queue_lock); +put_device (dev); +spin_lock_irq (q->queue_lock); +} + +/** +* iscsi_bsg_host_handler - handler for bsg requests for a iscsi host +* @q: iscsi host request queue +*/ +static void +iscsi_bsg_host_handler (struct request_queue *q) +{ +struct Scsi_Host *shost = q->queuedata; + +iscsi_bsg_request_handler (q, shost, &shost->shost_gendev); +} + +/** +* iscsi_bsg_hostadd - Create and add the bsg hooks so we can receive requests +* @shost: shost for iscsi_host +* @cls_host: iscsi_cls_host adding the structures to +*/ +static int +iscsi_bsg_hostadd (struct Scsi_Host *shost,struct iscsi_cls_host *ihost) +{ +struct device *dev = &shost->shost_gendev; +struct iscsi_internal *i =to_iscsi_internal (shost->transportt); +struct request_queue *q; +int err; +char bsg_name[20 ]; + + ihost->rqst_q =NULL ; + +if (!i->iscsi_transport->bsg_request) +return -ENOTSUPP ; + +snprintf (bsg_name,sizeof (bsg_name), +"iscsi_host%d" , shost->host_no); + + q =__scsi_alloc_queue (shost, iscsi_bsg_host_handler); +if (!q) { +printk (KERN_ERR "iscsi_host%d: bsg interface failed to " +"initialize - no request queue\n" , + shost->host_no); +return -ENOMEM ; + } + + q->queuedata = shost; +queue_flag_set_unlocked (QUEUE_FLAG_BIDI , q); +blk_queue_softirq_done (q, iscsi_bsg_softirq_done); +blk_queue_rq_timed_out (q, iscsi_bsg_job_timeout); +blk_queue_rq_timeout (q,ISCSI_DEFAULT_BSG_TIMEOUT ); + + err =bsg_register_queue (q, dev, bsg_name,NULL ); +if (err) { +printk (KERN_ERR "iscsi_host%d: bsg interface failed to " +"initialize - register queue\n" , + shost->host_no); +blk_cleanup_queue (q); +return err; + } + + ihost->rqst_q = q; +return 0 ; +} + +/** +* iscsi_bsg_remove - Deletes the bsg hooks on iscsi objects +* @q: the request_queue that is to be torn down. +*/ +static void +iscsi_bsg_remove (struct request_queue *q) +{ +if (q) { +bsg_unregister_queue (q); +blk_cleanup_queue (q); + } +} + +/* End BSG routines */ +static int iscsi_setup_host (struct transport_container *tc,struct device *dev,struct device *cdev) {
@@ -277,13 +720,27 @@ static int iscsi_setup_host(struct transport_container *tc, struct device *dev,memset (ihost,0 ,sizeof (*ihost));atomic_set (&ihost->nr_scans,0 );mutex_init (&ihost->mutex); + +iscsi_bsg_hostadd (shost, ihost); +/* ignore any bsg add error - we just can't do sgio */ + +return 0 ; +} + +static int iscsi_remove_host (struct transport_container *tc,struct device *dev, +struct device *cdev) +{ +struct Scsi_Host *shost =dev_to_shost (dev); +struct iscsi_cls_host *ihost = shost->shost_data; + +iscsi_bsg_remove (ihost->rqst_q);return 0 ; } static DECLARE_TRANSPORT_CLASS(iscsi_host_class,"iscsi_host" , iscsi_setup_host, -NULL , + iscsi_remove_host, NULL); static DECLARE_TRANSPORT_CLASS(iscsi_session_class,
include/scsi/Kbuild +1 -0
@@ -2,4 +2,5 @@ header-y += scsi.h header-y += scsi_netlink.h header-y += scsi_netlink_fc.h header-y += scsi_bsg_fc.h +header-y += scsi_bsg_iscsi.h header-y += fc/
include/scsi/scsi_bsg_iscsi.h new file +253 -0
253 lines not on this page · raw message
include/scsi/scsi_transport_iscsi.h +47 -0
65 lines not on this page · raw message
Signature · 1 line
1.6.4
Quoted 3 lines · Jayamohan Kallickal
This patch contains the bsg interface based on
the bsg interface in FC.
What? where? who? how? why? w*\? ...
You are proposing a new Kernel ABI/API here right. I think it is not acceptable without a detailed documentation, including every little bit. Nothing missing.
If lots of it is already written for FC then grate most of your job is done.
But for us mortals please put some text on an introductory message. I use iscsi everyday for 4 years, your mail should answer the question: "what am I missing?"
Quoted 4 lines
Developed and submitted earlier by James Smart.
Signed-off-by James Smart <[email protected]>Signed-off-by Jayamohan Kallickal <[email protected]>
Boaz
Sorry for late response. Please see our comments inline. We are trying to figure out how we can leverage this interface for our offload solution.
Thanks,
Vikas.
include/scsi/scsi_bsg_iscsi.h line 65
Quoted 6 lines
include/scsi/scsi_bsg_iscsi.h new file +6
-0+/* +* iSCSI Host Messages +*/ + +struct ip_address_subnet_format { +u16 sizeofstructure;
Why do we need this field ?
include/scsi/scsi_bsg_iscsi.h line 74
Quoted 9 lines
include/scsi/scsi_bsg_iscsi.h new file +9
-0+u8 iptype; +u8 ipv6_prefix_length; +u8 ipaddress[16 ]; +u8 subnetmask[16 ]; +u32 rsvd0; +} __packed; + +struct iscsi_ip_addr_record { +u32 action;
What's does this field implies ? Looks like be2iscsi specific ?
Review
Quoted 1 line
(quoted diff) +1
-0+ u32 interface_hndl;
Again what this interface handle for ?
include/scsi/scsi_bsg_iscsi.h line 77
Quoted 2 lines
include/scsi/scsi_bsg_iscsi.h new file +2
-0+struct ip_address_subnet_format ip_address; +u32 status;
Why do we need *status* ? And its status of what ?
Quoted 5 lines
include/scsi/scsi_bsg_iscsi.h new file +5
-0+} __packed; + +struct iscsi_ip_addr_record_params { +u32 record_entry_count; +struct iscsi_ip_addr_record ip_record[1 ];
include/scsi/scsi_bsg_iscsi.h line 92
Quoted 10 lines
include/scsi/scsi_bsg_iscsi.h new file +10
-0+} __packed; + +struct ip_address_format { +u16 sizeofstructure; +u8 reserved; +u8 ip_type; +u8 ip_address[16 ]; +u32 rsvd0; +} __packed; +
So this structure is needed to distinguish between IPV4 and IPV6. Correct ? Once we have a better understanding we can really assess whether it will work for our interface or not, or it will require further changes to make it generic so that it will work for us.
include/scsi/scsi_bsg_iscsi.h line 97
Quoted 5 lines
include/scsi/scsi_bsg_iscsi.h new file +5
-0+struct iscsi_bsg_common_format { +u8 opcode;/* dword 0 */ +u8 subsystem;/* dword 0 */ +u8 port_number;/* dword 0 */ +u8 domain;/* dword 0 */
What does *port_number* and *domain* indicate ?
include/scsi/scsi_bsg_iscsi.h line 98
Quoted 1 line
include/scsi/scsi_bsg_iscsi.h new file +1
-0+u32 timeout;/* dword 1 */
Is it bsg command time out?
include/scsi/scsi_bsg_iscsi.h line 100
Quoted 2 lines
include/scsi/scsi_bsg_iscsi.h new file +2
-0+u32 request_length;/* dword 2 */ +u32 rsvd0;/* dword 3 */
Why *rsvd0* here? Why not at end of struct.
Review
Quoted 1 line
(quoted diff) +1
-0+ u32 action;
What is expected data here ? So at the app's level what *action* is supposed to be set to ?
include/scsi/scsi_bsg_iscsi.h line 103
Quoted 2 lines
include/scsi/scsi_bsg_iscsi.h new file +2
-0+struct ip_address_format gateway; +u32 interface_hndl;
What is expected data here? Why we need *interface_hndl* here. We also have a *port_number* as struct member.
include/scsi/scsi_bsg_iscsi.h line 105
Quoted 2 lines
include/scsi/scsi_bsg_iscsi.h new file +2
-0+u32 vlan_priority; +u32 flags;
Again *flags* is supposed to be set to what ?
include/scsi/scsi_bsg_iscsi.h line 110
Quoted 5 lines
include/scsi/scsi_bsg_iscsi.h new file +5
-0+u8 iscsiname[224 ]; +u8 iscsialias[32 ]; +u32 ip_type; +u32 retry_count; +struct iscsi_ip_addr_record_params ip_params;
Can we add *u32 vendor_data[0]* for vendor specific data?
Quoted 16 lines
include/scsi/scsi_bsg_iscsi.h new file +16
-0+} __packed; + +/* ISCSI_BSG_HST_NET_CONFIG : */ + +/* bsg network parameter ids */ +enum iscsi_bsg_netparam { +BSG_NETPARAM_UNKNOWN =0x00000000 , +BSG_NETPARAM_MAC =BSG_NETPARAM_UNKNOWN +1 , +BSG_NETPARAM_MTU =BSG_NETPARAM_UNKNOWN +2 , +BSG_NETPARAM_VLAN =BSG_NETPARAM_UNKNOWN +3 , +BSG_NETPARAM_BOOTPROTO =BSG_NETPARAM_UNKNOWN +4 , +BSG_NETPARAM_IPV4_ADDR =BSG_NETPARAM_UNKNOWN +5 , +BSG_NETPARAM_IPV4_MASK =BSG_NETPARAM_UNKNOWN +6 , +BSG_NETPARAM_IPV4_GW =BSG_NETPARAM_UNKNOWN +7 , +BSG_NETPARAM_IPV6_ADDR =BSG_NETPARAM_UNKNOWN +8 , +BSG_NETPARAM_IPV6_MASK =BSG_NETPARAM_UNKNOWN +9 ,
include/scsi/scsi_bsg_iscsi.h line 136
Quoted 10 lines
include/scsi/scsi_bsg_iscsi.h new file +10
-0+BSG_NETPARAM_IPV6_GW =BSG_NETPARAM_UNKNOWN +10 , + BSG_NETPARAM_IPV6_AUTO_PARAM = BSG_NETPARAM_UNKNOWN + 11, +}; + +/* network parameter TLV (type, length, value) structure */ +struct iscsi_bsg_netparam_tlv { +uint32_t netparam; +uint32_t length; +uint8_t value[1 ]; +};
So *value[1]* you mean *value[0]*, just to be consistent ? Also the idea behind this TLV is for App's to set whatever network param needs to be set or get in one or multiple shot, can be configured and passed back & forth ? Is that how you guys envisioned it ?
Quoted 5 lines
include/scsi/scsi_bsg_iscsi.h new file +5
-0+ +/* Request: +* This message sets or gets Network configuration parameters on/from the +* iscsi host network interface. +*
include/scsi/scsi_bsg_iscsi.h line 151
Quoted 10 lines
include/scsi/scsi_bsg_iscsi.h new file +10
-0+* On set operations, the request payload buffer contains a set of +* netparam tlv's with the values to be set. +* +* On get operations, the request payload is unused. +*/ +struct iscsi_bsg_host_net_config { +/* +* Specifies the operation type: 0x0 = GET; 0x1 = SET +*/ +uint8_t set_op;
Looking at the implementation, looks like *set_op* is set to ISCSI_SET_IP_ADDR for ex, ? Basically not consistent as outlined in the comment.
Quoted 7 lines
include/scsi/scsi_bsg_iscsi.h new file +7
-0+ +/* +* If SET operation, contains the total number of netparam tlv's +* in the request payload. +*/ +uint8_t netparam_count; +};