diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2009-06-01 19:33:30 +1000 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2009-06-01 19:33:30 +1000 |
commit | c71752c9d36ccb9692a721f48b3ffdad251181c0 (patch) | |
tree | 571b4391537eb74e1192fc05b0ddcc193be771e6 | |
parent | 2fb15a8f78d1fd8930f94eb287ece53aa5a5ef40 (diff) | |
parent | add2b5e0d596c4ce1aa10f845d9ba49eb5e2f605 (diff) |
Merge commit 'scsi-post-merge/master'
Conflicts:
include/Kbuild
-rw-r--r-- | Documentation/scsi/scsi_fc_transport.txt | 14 | ||||
-rw-r--r-- | Documentation/scsi/scsi_mid_low_api.txt | 5 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_aux.c | 10 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_def.h | 18 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_erp.c | 2 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_ext.h | 6 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_fc.c | 185 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_fsf.c | 3 | ||||
-rw-r--r-- | drivers/s390/scsi/zfcp_scsi.c | 15 | ||||
-rw-r--r-- | drivers/scsi/scsi_transport_fc.c | 614 | ||||
-rw-r--r-- | include/Kbuild | 1 | ||||
-rw-r--r-- | include/scsi/Kbuild | 4 | ||||
-rw-r--r-- | include/scsi/scsi_bsg_fc.h | 322 | ||||
-rw-r--r-- | include/scsi/scsi_host.h | 9 | ||||
-rw-r--r-- | include/scsi/scsi_transport_fc.h | 52 |
15 files changed, 1234 insertions, 26 deletions
diff --git a/Documentation/scsi/scsi_fc_transport.txt b/Documentation/scsi/scsi_fc_transport.txt index e5b071d46619..d7f181701dc2 100644 --- a/Documentation/scsi/scsi_fc_transport.txt +++ b/Documentation/scsi/scsi_fc_transport.txt @@ -1,10 +1,11 @@ SCSI FC Tansport ============================================= -Date: 4/12/2007 +Date: 11/18/2008 Kernel Revisions for features: rports : <<TBS>> - vports : 2.6.22 (? TBD) + vports : 2.6.22 + bsg support : 2.6.30 (?TBD?) Introduction @@ -15,6 +16,7 @@ The FC transport can be found at: drivers/scsi/scsi_transport_fc.c include/scsi/scsi_transport_fc.h include/scsi/scsi_netlink_fc.h + include/scsi/scsi_bsg_fc.h This file is found at Documentation/scsi/scsi_fc_transport.txt @@ -472,6 +474,14 @@ int fc_vport_terminate(struct fc_vport *vport) +FC BSG support (CT & ELS passthru, and more) +======================================================================== +<< To Be Supplied >> + + + + + Credits ======= The following people have contributed to this document: diff --git a/Documentation/scsi/scsi_mid_low_api.txt b/Documentation/scsi/scsi_mid_low_api.txt index a6d5354639b2..de67229251d8 100644 --- a/Documentation/scsi/scsi_mid_low_api.txt +++ b/Documentation/scsi/scsi_mid_low_api.txt @@ -1271,6 +1271,11 @@ of interest: hostdata[0] - area reserved for LLD at end of struct Scsi_Host. Size is set by the second argument (named 'xtr_bytes') to scsi_host_alloc() or scsi_register(). + vendor_id - a unique value that identifies the vendor supplying + the LLD for the Scsi_Host. Used most often in validating + vendor-specific message requests. Value consists of an + identifier type and a vendor-specific value. + See scsi_netlink.h for a description of valid formats. The scsi_host structure is defined in include/scsi/scsi_host.h diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 3ac27ee47396..2ccbd185a5fb 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -470,6 +470,12 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device) if (!adapter) return -ENOMEM; + adapter->gs = kzalloc(sizeof(struct zfcp_wka_ports), GFP_KERNEL); + if (!adapter->gs) { + kfree(adapter); + return -ENOMEM; + } + ccw_device->handler = NULL; adapter->ccw_device = ccw_device; atomic_set(&adapter->refcount, 0); @@ -523,8 +529,7 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device) goto sysfs_failed; atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); - - zfcp_fc_nameserver_init(adapter); + zfcp_fc_wka_ports_init(adapter); if (!zfcp_adapter_scsi_register(adapter)) return 0; @@ -571,6 +576,7 @@ void zfcp_adapter_dequeue(struct zfcp_adapter *adapter) kfree(adapter->req_list); kfree(adapter->fc_stats); kfree(adapter->stats_reset_data); + kfree(adapter->gs); kfree(adapter); } diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h index 2074d45dbf6c..49d0532bca1c 100644 --- a/drivers/s390/scsi/zfcp_def.h +++ b/drivers/s390/scsi/zfcp_def.h @@ -22,6 +22,8 @@ #include <linux/syscalls.h> #include <linux/scatterlist.h> #include <linux/ioctl.h> +#include <scsi/fc/fc_fs.h> +#include <scsi/fc/fc_gs.h> #include <scsi/scsi.h> #include <scsi/scsi_tcq.h> #include <scsi/scsi_cmnd.h> @@ -29,6 +31,7 @@ #include <scsi/scsi_host.h> #include <scsi/scsi_transport.h> #include <scsi/scsi_transport_fc.h> +#include <scsi/scsi_bsg_fc.h> #include <asm/ccwdev.h> #include <asm/qdio.h> #include <asm/debug.h> @@ -228,11 +231,6 @@ struct zfcp_ls_adisc { /* FC-PH/FC-GS well-known address identifiers for generic services */ #define ZFCP_DID_WKA 0xFFFFF0 -#define ZFCP_DID_MANAGEMENT_SERVICE 0xFFFFFA -#define ZFCP_DID_TIME_SERVICE 0xFFFFFB -#define ZFCP_DID_DIRECTORY_SERVICE 0xFFFFFC -#define ZFCP_DID_ALIAS_SERVICE 0xFFFFF8 -#define ZFCP_DID_KEY_DISTRIBUTION_SERVICE 0xFFFFF7 /* remote port status */ #define ZFCP_STATUS_PORT_PHYS_OPEN 0x00000001 @@ -376,6 +374,14 @@ struct zfcp_wka_port { struct delayed_work work; }; +struct zfcp_wka_ports { + struct zfcp_wka_port ms; /* management service */ + struct zfcp_wka_port ts; /* time service */ + struct zfcp_wka_port ds; /* directory service */ + struct zfcp_wka_port as; /* alias service */ + struct zfcp_wka_port ks; /* key distribution service */ +}; + struct zfcp_qdio_queue { struct qdio_buffer *sbal[QDIO_MAX_BUFFERS_PER_Q]; u8 first; /* index of next free bfr in queue */ @@ -461,7 +467,7 @@ struct zfcp_adapter { actions */ u32 erp_low_mem_count; /* nr of erp actions waiting for memory */ - struct zfcp_wka_port nsp; /* adapter's nameserver */ + struct zfcp_wka_ports *gs; /* generic services */ debug_info_t *rec_dbf; debug_info_t *hba_dbf; debug_info_t *san_dbf; /* debug feature areas */ diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index e50ea465bc2b..8030e25152fb 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -719,7 +719,7 @@ static void zfcp_erp_adapter_strategy_close(struct zfcp_erp_action *act) zfcp_qdio_close(adapter); zfcp_fsf_req_dismiss_all(adapter); adapter->fsf_req_seq_no = 0; - zfcp_fc_wka_port_force_offline(&adapter->nsp); + zfcp_fc_wka_port_force_offline(&adapter->gs->ds); /* all ports and units are closed */ zfcp_erp_modify_adapter_status(adapter, "erascl1", NULL, ZFCP_STATUS_COMMON_OPEN, ZFCP_CLEAR); diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 120a9a1c81f7..3044c6010306 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -106,8 +106,12 @@ extern int zfcp_fc_ns_gid_pn(struct zfcp_erp_action *); extern void zfcp_fc_plogi_evaluate(struct zfcp_port *, struct fsf_plogi *); extern void zfcp_test_link(struct zfcp_port *); extern void zfcp_fc_link_test_work(struct work_struct *); -extern void zfcp_fc_nameserver_init(struct zfcp_adapter *); extern void zfcp_fc_wka_port_force_offline(struct zfcp_wka_port *); +extern void zfcp_fc_wka_ports_init(struct zfcp_adapter *); +extern int zfcp_fc_execute_els_fc_job(struct fc_bsg_job *); +extern int zfcp_fc_execute_ct_fc_job(struct fc_bsg_job *); +extern void zfcp_fc_wka_port_force_offline(struct zfcp_wka_port *); + /* zfcp_fsf.c */ extern int zfcp_fsf_open_port(struct zfcp_erp_action *); diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index 35493a82d2a8..2f0705d76b72 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -120,14 +120,13 @@ static void zfcp_wka_port_put(struct zfcp_wka_port *wka_port) schedule_delayed_work(&wka_port->work, HZ / 100); } -void zfcp_fc_nameserver_init(struct zfcp_adapter *adapter) +static void zfcp_fc_wka_port_init(struct zfcp_wka_port *wka_port, u32 d_id, + struct zfcp_adapter *adapter) { - struct zfcp_wka_port *wka_port = &adapter->nsp; - init_waitqueue_head(&wka_port->completion_wq); wka_port->adapter = adapter; - wka_port->d_id = ZFCP_DID_DIRECTORY_SERVICE; + wka_port->d_id = d_id; wka_port->status = ZFCP_WKA_PORT_OFFLINE; atomic_set(&wka_port->refcount, 0); @@ -143,6 +142,17 @@ void zfcp_fc_wka_port_force_offline(struct zfcp_wka_port *wka) mutex_unlock(&wka->mutex); } +void zfcp_fc_wka_ports_init(struct zfcp_adapter *adapter) +{ + struct zfcp_wka_ports *gs = adapter->gs; + + zfcp_fc_wka_port_init(&gs->ms, FC_FID_MGMT_SERV, adapter); + zfcp_fc_wka_port_init(&gs->ts, FC_FID_TIME_SERV, adapter); + zfcp_fc_wka_port_init(&gs->ds, FC_FID_DIR_SERV, adapter); + zfcp_fc_wka_port_init(&gs->as, FC_FID_ALIASES, adapter); + zfcp_fc_wka_port_init(&gs->ks, FC_FID_SEC_KEY, adapter); +} + static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range, struct fcp_rscn_element *elem) { @@ -282,7 +292,7 @@ int static zfcp_fc_ns_gid_pn_request(struct zfcp_erp_action *erp_action, /* setup parameters for send generic command */ gid_pn->port = erp_action->port; - gid_pn->ct.wka_port = &adapter->nsp; + gid_pn->ct.wka_port = &adapter->gs->ds; gid_pn->ct.handler = zfcp_fc_ns_handler; gid_pn->ct.handler_data = (unsigned long) &compl_rec; gid_pn->ct.timeout = ZFCP_NS_GID_PN_TIMEOUT; @@ -329,13 +339,13 @@ int zfcp_fc_ns_gid_pn(struct zfcp_erp_action *erp_action) memset(gid_pn, 0, sizeof(*gid_pn)); - ret = zfcp_wka_port_get(&adapter->nsp); + ret = zfcp_wka_port_get(&adapter->gs->ds); if (ret) goto out; ret = zfcp_fc_ns_gid_pn_request(erp_action, gid_pn); - zfcp_wka_port_put(&adapter->nsp); + zfcp_wka_port_put(&adapter->gs->ds); out: mempool_free(gid_pn, adapter->pool.data_gid_pn); return ret; @@ -525,7 +535,7 @@ static int zfcp_scan_issue_gpn_ft(struct zfcp_gpn_ft *gpn_ft, req->fc4_type = ZFCP_CT_SCSI_FCP; /* prepare zfcp_send_ct */ - ct->wka_port = &adapter->nsp; + ct->wka_port = &adapter->gs->ds; ct->handler = zfcp_fc_ns_handler; ct->handler_data = (unsigned long)&compl_rec; ct->timeout = 10; @@ -644,7 +654,7 @@ int zfcp_scan_ports(struct zfcp_adapter *adapter) fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPIV) return 0; - ret = zfcp_wka_port_get(&adapter->nsp); + ret = zfcp_wka_port_get(&adapter->gs->ds); if (ret) return ret; @@ -666,7 +676,7 @@ int zfcp_scan_ports(struct zfcp_adapter *adapter) } zfcp_free_sg_env(gpn_ft, buf_num); out: - zfcp_wka_port_put(&adapter->nsp); + zfcp_wka_port_put(&adapter->gs->ds); return ret; } @@ -675,3 +685,158 @@ void _zfcp_scan_ports_later(struct work_struct *work) { zfcp_scan_ports(container_of(work, struct zfcp_adapter, scan_work)); } + +struct zfcp_els_fc_job { + struct zfcp_send_els els; + struct fc_bsg_job *job; +}; + +static void zfcp_fc_generic_els_handler(unsigned long data) +{ + struct zfcp_els_fc_job *els_fc_job = (struct zfcp_els_fc_job *) data; + struct fc_bsg_job *job = els_fc_job->job; + struct fc_bsg_reply *reply = job->reply; + + if (els_fc_job->els.status) { + /* request rejected or timed out */ + reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_REJECT; + goto out; + } + + reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK; + reply->reply_payload_rcv_len = job->reply_payload.payload_len; + +out: + job->state_flags = FC_RQST_STATE_DONE; + job->job_done(job); + kfree(els_fc_job); +} + +int zfcp_fc_execute_els_fc_job(struct fc_bsg_job *job) +{ + struct zfcp_els_fc_job *els_fc_job; + struct fc_rport *rport = job->rport; + struct Scsi_Host *shost; + struct zfcp_adapter *adapter; + struct zfcp_port *port; + u8 *port_did; + + shost = rport ? rport_to_shost(rport) : job->shost; + adapter = (struct zfcp_adapter *)shost->hostdata[0]; + + if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_OPEN)) + return -EINVAL; + + els_fc_job = kzalloc(sizeof(struct zfcp_els_fc_job), GFP_KERNEL); + if (!els_fc_job) + return -ENOMEM; + + els_fc_job->els.adapter = adapter; + if (rport) { + read_lock_irq(&zfcp_data.config_lock); + port = rport->dd_data; + if (port) + els_fc_job->els.d_id = port->d_id; + read_unlock_irq(&zfcp_data.config_lock); + if (!port) { + kfree(els_fc_job); + return -EINVAL; + } + } else { + port_did = job->request->rqst_data.h_els.port_id; + els_fc_job->els.d_id = (port_did[0] << 16) + + (port_did[1] << 8) + port_did[2]; + } + + els_fc_job->els.req = job->request_payload.sg_list; + els_fc_job->els.resp = job->reply_payload.sg_list; + els_fc_job->els.handler = zfcp_fc_generic_els_handler; + els_fc_job->els.handler_data = (unsigned long) els_fc_job; + els_fc_job->job = job; + + return zfcp_fsf_send_els(&els_fc_job->els); +} + +struct zfcp_ct_fc_job { + struct zfcp_send_ct ct; + struct fc_bsg_job *job; +}; + +static void zfcp_fc_generic_ct_handler(unsigned long data) +{ + struct zfcp_ct_fc_job *ct_fc_job = (struct zfcp_ct_fc_job *) data; + struct fc_bsg_job *job = ct_fc_job->job; + + job->reply->reply_data.ctels_reply.status = ct_fc_job->ct.status ? + FC_CTELS_STATUS_REJECT : FC_CTELS_STATUS_OK; + job->reply->reply_payload_rcv_len = job->reply_payload.payload_len; + job->state_flags = FC_RQST_STATE_DONE; + job->job_done(job); + + zfcp_wka_port_put(ct_fc_job->ct.wka_port); + + kfree(ct_fc_job); +} + +int zfcp_fc_execute_ct_fc_job(struct fc_bsg_job *job) +{ + int ret; + u8 gs_type; + struct fc_rport *rport = job->rport; + struct Scsi_Host *shost; + struct zfcp_adapter *adapter; + struct zfcp_ct_fc_job *ct_fc_job; + u32 preamble_word1; + + shost = rport ? rport_to_shost(rport) : job->shost; + + adapter = (struct zfcp_adapter *)shost->hostdata[0]; + if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_OPEN)) + return -EINVAL; + + ct_fc_job = kzalloc(sizeof(struct zfcp_ct_fc_job), GFP_KERNEL); + if (!ct_fc_job) + return -ENOMEM; + + preamble_word1 = job->request->rqst_data.r_ct.preamble_word1; + gs_type = (preamble_word1 & 0xff000000) >> 24; + + switch (gs_type) { + case FC_FST_ALIAS: + ct_fc_job->ct.wka_port = &adapter->gs->as; + break; + case FC_FST_MGMT: + ct_fc_job->ct.wka_port = &adapter->gs->ms; + break; + case FC_FST_TIME: + ct_fc_job->ct.wka_port = &adapter->gs->ts; + break; + case FC_FST_DIR: + ct_fc_job->ct.wka_port = &adapter->gs->ds; + break; + default: + kfree(ct_fc_job); + return -EINVAL; /* no such service */ + } + + ret = zfcp_wka_port_get(ct_fc_job->ct.wka_port); + if (ret) { + kfree(ct_fc_job); + return ret; + } + + ct_fc_job->ct.req = job->request_payload.sg_list; + ct_fc_job->ct.resp = job->reply_payload.sg_list; + ct_fc_job->ct.timeout = ZFCP_FSF_REQUEST_TIMEOUT; + ct_fc_job->ct.handler = zfcp_fc_generic_ct_handler; + ct_fc_job->ct.handler_data = (unsigned long) ct_fc_job; + ct_fc_job->ct.completion = NULL; + ct_fc_job->job = job; + + ret = zfcp_fsf_send_ct(&ct_fc_job->ct, NULL, NULL); + if (ret) { + kfree(ct_fc_job); + zfcp_wka_port_put(ct_fc_job->ct.wka_port); + } + return ret; +} diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index e6dae3744e79..c57658f3d34f 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -1146,7 +1146,8 @@ static void zfcp_fsf_send_els_handler(struct zfcp_fsf_req *req) case FSF_RESPONSE_SIZE_TOO_LARGE: break; case FSF_ACCESS_DENIED: - zfcp_fsf_access_denied_port(req, port); + if (port) + zfcp_fsf_access_denied_port(req, port); break; case FSF_SBAL_MISMATCH: /* should never occure, avoided in zfcp_fsf_send_els */ diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 7d0da230eb63..967ede73f4c5 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -623,6 +623,20 @@ void zfcp_scsi_scan(struct work_struct *work) zfcp_unit_put(unit); } +static int zfcp_execute_fc_job(struct fc_bsg_job *job) +{ + switch (job->request->msgcode) { + case FC_BSG_RPT_ELS: + case FC_BSG_HST_ELS_NOLOGIN: + return zfcp_fc_execute_els_fc_job(job); + case FC_BSG_RPT_CT: + case FC_BSG_HST_CT: + return zfcp_fc_execute_ct_fc_job(job); + default: + return -EINVAL; + } +} + struct fc_function_template zfcp_transport_functions = { .show_starget_port_id = 1, .show_starget_port_name = 1, @@ -644,6 +658,7 @@ struct fc_function_template zfcp_transport_functions = { .dev_loss_tmo_callbk = zfcp_scsi_dev_loss_tmo_callbk, .terminate_rport_io = zfcp_scsi_terminate_rport_io, .show_host_port_state = 1, + .bsg_request = zfcp_execute_fc_job, /* no functions registered for following dynamic attributes but directly set by LLDD */ .show_host_port_type = 1, diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index a152f89ae51c..3f64d93b6c8b 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -35,6 +35,7 @@ #include <linux/netlink.h> #include <net/netlink.h> #include <scsi/scsi_netlink_fc.h> +#include <scsi/scsi_bsg_fc.h> #include "scsi_priv.h" #include "scsi_transport_fc_internal.h" @@ -43,6 +44,10 @@ static void fc_vport_sched_delete(struct work_struct *work); static int fc_vport_setup(struct Scsi_Host *shost, int channel, struct device *pdev, struct fc_vport_identifiers *ids, struct fc_vport **vport); +static int fc_bsg_hostadd(struct Scsi_Host *, struct fc_host_attrs *); +static int fc_bsg_rportadd(struct Scsi_Host *, struct fc_rport *); +static void fc_bsg_remove(struct request_queue *); +static void fc_bsg_goose_queue(struct fc_rport *); /* * Redefine so that we can have same named attributes in the @@ -411,13 +416,26 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev, return -ENOMEM; } + fc_bsg_hostadd(shost, fc_host); + /* ignore any bsg add error - we just can't do sgio */ + + return 0; +} + +static int fc_host_remove(struct transport_container *tc, struct device *dev, + struct device *cdev) +{ + struct Scsi_Host *shost = dev_to_shost(dev); + struct fc_host_attrs *fc_host = shost_to_fc_host(shost); + + fc_bsg_remove(fc_host->rqst_q); return 0; } static DECLARE_TRANSPORT_CLASS(fc_host_class, "fc_host", fc_host_setup, - NULL, + fc_host_remove, NULL); /* @@ -2375,6 +2393,7 @@ fc_rport_final_delete(struct work_struct *work) scsi_flush_work(shost); fc_terminate_rport_io(rport); + /* * Cancel any outstanding timers. These should really exist * only when rmmod'ing the LLDD and we're asking for @@ -2407,6 +2426,8 @@ fc_rport_final_delete(struct work_struct *work) (i->f->dev_loss_tmo_callbk)) i->f->dev_loss_tmo_callbk(rport); + fc_bsg_remove(rport->rqst_q); + transport_remove_device(dev); device_del(dev); transport_destroy_device(dev); @@ -2494,6 +2515,9 @@ fc_rport_create(struct Scsi_Host *shost, int channel, transport_add_device(dev); transport_configure_device(dev); + fc_bsg_rportadd(shost, rport); + /* ignore any bsg add error - we just can't do sgio */ + if (rport->roles & FC_PORT_ROLE_FCP_TARGET) { /* initiate a scan of the target */ rport->flags |= FC_RPORT_SCAN_PENDING; @@ -2658,6 +2682,8 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel, spin_unlock_irqrestore(shost->host_lock, flags); + fc_bsg_goose_queue(rport); + return rport; } } @@ -3343,6 +3369,592 @@ fc_vport_sched_delete(struct work_struct *work) } +/* + * BSG support + */ + + +/** + * fc_destroy_bsgjob - routine to teardown/delete a fc bsg job + * @job: fc_bsg_job that is to be torn down + */ +static void +fc_destroy_bsgjob(struct fc_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); +} + + +/** + * fc_bsg_jobdone - completion routine for bsg requests that the LLD has + * completed + * @job: fc_bsg_job that is complete + */ +static void +fc_bsg_jobdone(struct fc_bsg_job *job) +{ + struct request *req = job->req; + struct request *rsp = req->next_rq; + unsigned long flags; + int err; + + spin_lock_irqsave(&job->job_lock, flags); + job->state_flags |= FC_RQST_STATE_DONE; + job->ref_cnt--; + spin_unlock_irqrestore(&job->job_lock, flags); + + 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_end_request_all(req, err); + + fc_destroy_bsgjob(job); +} + + +/** + * fc_bsg_job_timeout - handler for when a bsg request timesout + * @req: request that timed out + */ +static enum blk_eh_timer_return +fc_bsg_job_timeout(struct request *req) +{ + struct fc_bsg_job *job = (void *) req->special; + struct Scsi_Host *shost = job->shost; + struct fc_internal *i = to_fc_internal(shost->transportt); + unsigned long flags; + int err = 0, done = 0; + + if (job->rport && job->rport->port_state == FC_PORTSTATE_BLOCKED) + return BLK_EH_RESET_TIMER; + + spin_lock_irqsave(&job->job_lock, flags); + if (job->state_flags & FC_RQST_STATE_DONE) + done = 1; + else + job->ref_cnt++; + spin_unlock_irqrestore(&job->job_lock, flags); + + if (!done && i->f->bsg_timeout) { + /* call LLDD to abort the i/o as it has timed out */ + err = i->f->bsg_timeout(job); + if (err) + printk(KERN_ERR "ERROR: FC BSG request timeout - LLD " + "abort failed with status %d\n", err); + } + + if (!done) { + spin_lock_irqsave(&job->job_lock, flags); + job->ref_cnt--; + spin_unlock_irqrestore(&job->job_lock, flags); + fc_destroy_bsgjob(job); + } + + /* the blk_end_sync_io() doesn't check the error */ + return BLK_EH_HANDLED; +} + + + +static int +fc_bsg_map_buffer(struct fc_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; +} + + +/** + * fc_req_to_bsgjob - Allocate/create the fc_bsg_job structure for the + * bsg request + * @shost: SCSI Host corresponding to the bsg object + * @rport: (optional) FC Remote Port corresponding to the bsg object + * @req: BSG request that needs a job structure + */ +static int +fc_req_to_bsgjob(struct Scsi_Host *shost, struct fc_rport *rport, + struct request *req) +{ + struct fc_internal *i = to_fc_internal(shost->transportt); + struct request *rsp = req->next_rq; + struct fc_bsg_job *job; + int ret; + + BUG_ON(req->special); + + job = kzalloc(sizeof(struct fc_bsg_job) + i->f->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 fc 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->rport = rport; + job->req = req; + if (i->f->dd_bsg_size) + job->dd_data = (void *)&job[1]; + spin_lock_init(&job->job_lock); + job->request = (struct fc_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 = fc_bsg_map_buffer(&job->request_payload, req); + if (ret) + goto failjob_rls_job; + } + if (rsp && rsp->bio) { + ret = fc_bsg_map_buffer(&job->reply_payload, rsp); + if (ret) + goto failjob_rls_rqst_payload; + } + job->job_done = fc_bsg_jobdone; + if (rport) + job->dev = &rport->dev; + else + 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 fc_dispatch_result { + FC_DISPATCH_BREAK, /* on return, q is locked, break from q loop */ + FC_DISPATCH_LOCKED, /* on return, q is locked, continue on */ + FC_DISPATCH_UNLOCKED, /* on return, q is unlocked, continue on */ +}; + + +/** + * fc_bsg_host_dispatch - process fc host bsg requests and dispatch to LLDD + * @shost: scsi host rport attached to + * @job: bsg job to be processed + */ +static enum fc_dispatch_result +fc_bsg_host_dispatch(struct request_queue *q, struct Scsi_Host *shost, + struct fc_bsg_job *job) +{ + struct fc_internal *i = to_fc_internal(shost->transportt); + int cmdlen = sizeof(uint32_t); /* start with length of msgcode */ + int ret; + + /* Validate the host command */ + switch (job->request->msgcode) { + case FC_BSG_HST_ADD_RPORT: + cmdlen += sizeof(struct fc_bsg_host_add_rport); + break; + + case FC_BSG_HST_DEL_RPORT: + cmdlen += sizeof(struct fc_bsg_host_del_rport); + break; + + case FC_BSG_HST_ELS_NOLOGIN: + cmdlen += sizeof(struct fc_bsg_host_els); + /* there better be a xmt and rcv payloads */ + if ((!job->request_payload.payload_len) || + (!job->reply_payload.payload_len)) { + ret = -EINVAL; + goto fail_host_msg; + } + break; + + case FC_BSG_HST_CT: + cmdlen += sizeof(struct fc_bsg_host_ct); + /* there better be xmt and rcv payloads */ + if ((!job->request_payload.payload_len) || + (!job->reply_payload.payload_len)) { + ret = -EINVAL; + goto fail_host_msg; + } + break; + + case FC_BSG_HST_VENDOR: + cmdlen += sizeof(struct fc_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->f->bsg_request(job); + if (!ret) + return FC_DISPATCH_UNLOCKED; + +fail_host_msg: + /* return the errno failure code as the only status */ + BUG_ON(job->reply_len < sizeof(uint32_t)); + job->reply->result = ret; + job->reply_len = sizeof(uint32_t); + fc_bsg_jobdone(job); + return FC_DISPATCH_UNLOCKED; +} + + +/* + * fc_bsg_goose_queue - restart rport queue in case it was stopped + * @rport: rport to be restarted + */ +static void +fc_bsg_goose_queue(struct fc_rport *rport) +{ + int flagset; + + if (!rport->rqst_q) + return; + + get_device(&rport->dev); + + spin_lock(rport->rqst_q->queue_lock); + flagset = test_bit(QUEUE_FLAG_REENTER, &rport->rqst_q->queue_flags) && + !test_bit(QUEUE_FLAG_REENTER, &rport->rqst_q->queue_flags); + if (flagset) + queue_flag_set(QUEUE_FLAG_REENTER, rport->rqst_q); + __blk_run_queue(rport->rqst_q); + if (flagset) + queue_flag_clear(QUEUE_FLAG_REENTER, rport->rqst_q); + spin_unlock(rport->rqst_q->queue_lock); + + put_device(&rport->dev); +} + + +/** + * fc_bsg_rport_dispatch - process rport bsg requests and dispatch to LLDD + * @shost: scsi host rport attached to + * @rport: rport request destined to + * @job: bsg job to be processed + */ +static enum fc_dispatch_result +fc_bsg_rport_dispatch(struct request_queue *q, struct Scsi_Host *shost, + struct fc_rport *rport, struct fc_bsg_job *job) +{ + struct fc_internal *i = to_fc_internal(shost->transportt); + int cmdlen = sizeof(uint32_t); /* start with length of msgcode */ + int ret; + + /* Validate the rport command */ + switch (job->request->msgcode) { + case FC_BSG_RPT_ELS: + cmdlen += sizeof(struct fc_bsg_rport_els); + goto check_bidi; + + case FC_BSG_RPT_CT: + cmdlen += sizeof(struct fc_bsg_rport_ct); +check_bidi: + /* there better be xmt and rcv payloads */ + if ((!job->request_payload.payload_len) || + (!job->reply_payload.payload_len)) { + ret = -EINVAL; + goto fail_rport_msg; + } + break; + default: + ret = -EBADR; + goto fail_rport_msg; + } + + /* check if we really have all the request data needed */ + if (job->request_len < cmdlen) { + ret = -ENOMSG; + goto fail_rport_msg; + } + + ret = i->f->bsg_request(job); + if (!ret) + return FC_DISPATCH_UNLOCKED; + +fail_rport_msg: + /* return the errno failure code as the only status */ + BUG_ON(job->reply_len < sizeof(uint32_t)); + job->reply->result = ret; + job->reply_len = sizeof(uint32_t); + fc_bsg_jobdone(job); + return FC_DISPATCH_UNLOCKED; +} + + +/** + * fc_bsg_request_handler - generic handler for bsg requests + * @q: request queue to manage + * @shost: Scsi_Host related to the bsg object + * @rport: FC remote port related to the bsg object (optional) + * @dev: device structure for bsg object + */ +static void +fc_bsg_request_handler(struct request_queue *q, struct Scsi_Host *shost, + struct fc_rport *rport, struct device *dev) +{ + struct request *req; + struct fc_bsg_job *job; + enum fc_dispatch_result ret; + + if (!get_device(dev)) + return; + + while (!blk_queue_plugged(q)) { + if (rport && (rport->port_state == FC_PORTSTATE_BLOCKED)) + break; + + req = blk_fetch_request(q); + if (!req) + break; + + if (rport && (rport->port_state != FC_PORTSTATE_ONLINE)) { + req->errors = -ENXIO; + spin_unlock_irq(q->queue_lock); + blk_end_request(req, -ENXIO, blk_rq_bytes(req)); + spin_lock_irq(q->queue_lock); + continue; + } + + spin_unlock_irq(q->queue_lock); + + ret = fc_req_to_bsgjob(shost, rport, req); + 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->result = -ENOMSG; + job->reply_len = sizeof(uint32_t); + fc_bsg_jobdone(job); + spin_lock_irq(q->queue_lock); + continue; + } + + /* the dispatch routines will unlock the queue_lock */ + if (rport) + ret = fc_bsg_rport_dispatch(q, shost, rport, job); + else + ret = fc_bsg_host_dispatch(q, shost, job); + + /* did dispatcher hit state that can't process any more */ + if (ret == FC_DISPATCH_BREAK) + break; + + /* did dispatcher had released the lock */ + if (ret == FC_DISPATCH_UNLOCKED) + spin_lock_irq(q->queue_lock); + } + + spin_unlock_irq(q->queue_lock); + put_device(dev); + spin_lock_irq(q->queue_lock); +} + + +/** + * fc_bsg_host_handler - handler for bsg requests for a fc host + * @q: fc host request queue + */ +static void +fc_bsg_host_handler(struct request_queue *q) +{ + struct Scsi_Host *shost = q->queuedata; + + fc_bsg_request_handler(q, shost, NULL, &shost->shost_gendev); +} + + +/** + * fc_bsg_rport_handler - handler for bsg requests for a fc rport + * @q: rport request queue + */ +static void +fc_bsg_rport_handler(struct request_queue *q) +{ + struct fc_rport *rport = q->queuedata; + struct Scsi_Host *shost = rport_to_shost(rport); + + fc_bsg_request_handler(q, shost, rport, &rport->dev); +} + + +/** + * fc_bsg_hostadd - Create and add the bsg hooks so we can receive requests + * @shost: shost for fc_host + * @fc_host: fc_host adding the structures to + */ +static int +fc_bsg_hostadd(struct Scsi_Host *shost, struct fc_host_attrs *fc_host) +{ + struct device *dev = &shost->shost_gendev; + struct fc_internal *i = to_fc_internal(shost->transportt); + struct request_queue *q; + int err; + char bsg_name[BUS_ID_SIZE]; /*20*/ + + fc_host->rqst_q = NULL; + + if (!i->f->bsg_request) + return -ENOTSUPP; + + snprintf(bsg_name, sizeof(bsg_name), + "fc_host%d", shost->host_no); + + q = __scsi_alloc_queue(shost, fc_bsg_host_handler); + if (!q) { + printk(KERN_ERR "fc_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_rq_timed_out(q, fc_bsg_job_timeout); + blk_queue_rq_timeout(q, FC_DEFAULT_BSG_TIMEOUT); + + err = bsg_register_queue(q, dev, bsg_name, NULL); + if (err) { + printk(KERN_ERR "fc_host%d: bsg interface failed to " + "initialize - register queue\n", + shost->host_no); + blk_cleanup_queue(q); + return err; + } + + fc_host->rqst_q = q; + return 0; +} + + +/** + * fc_bsg_rportadd - Create and add the bsg hooks so we can receive requests + * @shost: shost that rport is attached to + * @rport: rport that the bsg hooks are being attached to + */ +static int +fc_bsg_rportadd(struct Scsi_Host *shost, struct fc_rport *rport) +{ + struct device *dev = &rport->dev; + struct fc_internal *i = to_fc_internal(shost->transportt); + struct request_queue *q; + int err; + + rport->rqst_q = NULL; + + if (!i->f->bsg_request) + return -ENOTSUPP; + + q = __scsi_alloc_queue(shost, fc_bsg_rport_handler); + if (!q) { + printk(KERN_ERR "%s: bsg interface failed to " + "initialize - no request queue\n", + dev->kobj.name); + return -ENOMEM; + } + + q->queuedata = rport; + queue_flag_set_unlocked(QUEUE_FLAG_BIDI, q); + blk_queue_rq_timed_out(q, fc_bsg_job_timeout); + blk_queue_rq_timeout(q, BLK_DEFAULT_SG_TIMEOUT); + + err = bsg_register_queue(q, dev, NULL, NULL); + if (err) { + printk(KERN_ERR "%s: bsg interface failed to " + "initialize - register queue\n", + dev->kobj.name); + blk_cleanup_queue(q); + return err; + } + + rport->rqst_q = q; + return 0; +} + + +/** + * fc_bsg_remove - Deletes the bsg hooks on fchosts/rports + * @q: the request_queue that is to be torn down. + */ +static void +fc_bsg_remove(struct request_queue *q) +{ + if (q) { + bsg_unregister_queue(q); + blk_cleanup_queue(q); + } +} + + /* Original Author: Martin Hicks */ MODULE_AUTHOR("James Smart"); MODULE_DESCRIPTION("FC Transport Attributes"); diff --git a/include/Kbuild b/include/Kbuild index fe36accd4328..8d226bfa2696 100644 --- a/include/Kbuild +++ b/include/Kbuild @@ -9,3 +9,4 @@ header-y += rdma/ header-y += video/ header-y += drm/ header-y += xen/ +header-y += scsi/ diff --git a/include/scsi/Kbuild b/include/scsi/Kbuild new file mode 100644 index 000000000000..33b2750e9283 --- /dev/null +++ b/include/scsi/Kbuild @@ -0,0 +1,4 @@ +header-y += scsi.h +header-y += scsi_netlink.h +header-y += scsi_netlink_fc.h +header-y += scsi_bsg_fc.h diff --git a/include/scsi/scsi_bsg_fc.h b/include/scsi/scsi_bsg_fc.h new file mode 100644 index 000000000000..a4b233318179 --- /dev/null +++ b/include/scsi/scsi_bsg_fc.h @@ -0,0 +1,322 @@ +/* + * FC Transport BSG Interface + * + * Copyright (C) 2008 James Smart, Emulex Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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 + * + */ + +#ifndef SCSI_BSG_FC_H +#define SCSI_BSG_FC_H + +/* + * This file intended to be included by both kernel and user space + */ + +#include <scsi/scsi.h> + +/* + * FC Transport SGIO v4 BSG Message Support + */ + +/* Default BSG request timeout (in seconds) */ +#define FC_DEFAULT_BSG_TIMEOUT (10 * HZ) + + +/* + * Request Message Codes supported by the FC Transport + */ + +/* define the class masks for the message codes */ +#define FC_BSG_CLS_MASK 0xF0000000 /* find object class */ +#define FC_BSG_HST_MASK 0x80000000 /* fc host class */ +#define FC_BSG_RPT_MASK 0x40000000 /* fc rport class */ + + /* fc_host Message Codes */ +#define FC_BSG_HST_ADD_RPORT (FC_BSG_HST_MASK | 0x00000001) +#define FC_BSG_HST_DEL_RPORT (FC_BSG_HST_MASK | 0x00000002) +#define FC_BSG_HST_ELS_NOLOGIN (FC_BSG_HST_MASK | 0x00000003) +#define FC_BSG_HST_CT (FC_BSG_HST_MASK | 0x00000004) +#define FC_BSG_HST_VENDOR (FC_BSG_HST_MASK | 0x000000FF) + + /* fc_rport Message Codes */ +#define FC_BSG_RPT_ELS (FC_BSG_RPT_MASK | 0x00000001) +#define FC_BSG_RPT_CT (FC_BSG_RPT_MASK | 0x00000002) + + + +/* + * FC Address Identifiers in Message Structures : + * + * Whenever a command payload contains a FC Address Identifier + * (aka port_id), the value is effectively in big-endian + * order, thus the array elements are decoded as follows: + * element [0] is bits 23:16 of the FC Address Identifier + * element [1] is bits 15:8 of the FC Address Identifier + * element [2] is bits 7:0 of the FC Address Identifier + */ + + +/* + * FC Host Messages + */ + +/* FC_BSG_HST_ADDR_PORT : */ + +/* Request: + * This message requests the FC host to login to the remote port + * at the specified N_Port_Id. The remote port is to be enumerated + * with the transport upon completion of the login. + */ +struct fc_bsg_host_add_rport { + uint8_t reserved; + + /* FC Address Identier of the remote port to login to */ + uint8_t port_id[3]; +}; + +/* Response: + * There is no additional response data - fc_bsg_reply->result is sufficient + */ + + +/* FC_BSG_HST_DEL_RPORT : */ + +/* Request: + * This message requests the FC host to remove an enumerated + * remote port and to terminate the login to it. + * + * Note: The driver is free to reject this request if it desires to + * remain logged in with the remote port. + */ +struct fc_bsg_host_del_rport { + uint8_t reserved; + + /* FC Address Identier of the remote port to logout of */ + uint8_t port_id[3]; +}; + +/* Response: + * There is no additional response data - fc_bsg_reply->result is sufficient + */ + + +/* FC_BSG_HST_ELS_NOLOGIN : */ + +/* Request: + * This message requests the FC_Host to send an ELS to a specific + * N_Port_ID. The host does not need to log into the remote port, + * nor does it need to enumerate the rport for further traffic + * (although, the FC host is free to do so if it desires). + */ +struct fc_bsg_host_els { + /* + * ELS Command Code being sent (must be the same as byte 0 + * of the payload) + */ + uint8_t command_code; + + /* FC Address Identier of the remote port to send the ELS to */ + uint8_t port_id[3]; +}; + +/* Response: + */ +/* fc_bsg_ctels_reply->status values */ +#define FC_CTELS_STATUS_OK 0x00000000 +#define FC_CTELS_STATUS_REJECT 0x00000001 +#define FC_CTELS_STATUS_P_RJT 0x00000002 +#define FC_CTELS_STATUS_F_RJT 0x00000003 +#define FC_CTELS_STATUS_P_BSY 0x00000004 +#define FC_CTELS_STATUS_F_BSY 0x00000006 +struct fc_bsg_ctels_reply { + /* + * Note: An ELS LS_RJT may be reported in 2 ways: + * a) A status of FC_CTELS_STATUS_OK is returned. The caller + * is to look into the ELS receive payload to determine + * LS_ACC or LS_RJT (by contents of word 0). The reject + * data will be in word 1. + * b) A status of FC_CTELS_STATUS_REJECT is returned, The + * rjt_data field will contain valid data. + * + * Note: ELS LS_ACC is determined by an FC_CTELS_STATUS_OK, and + * the receive payload word 0 indicates LS_ACC + * (e.g. value is 0x02xxxxxx). + * + * Note: Similarly, a CT Reject may be reported in 2 ways: + * a) A status of FC_CTELS_STATUS_OK is returned. The caller + * is to look into the CT receive payload to determine + * Accept or Reject (by contents of word 2). The reject + * data will be in word 3. + * b) A status of FC_CTELS_STATUS_REJECT is returned, The + * rjt_data field will contain valid data. + * + * Note: x_RJT/BSY status will indicae that the rjt_data field + * is valid and contains the reason/explanation values. + */ + uint32_t status; /* See FC_CTELS_STATUS_xxx */ + + /* valid if status is not FC_CTELS_STATUS_OK */ + struct { + uint8_t action; /* fragment_id for CT REJECT */ + uint8_t reason_code; + uint8_t reason_explanation; + uint8_t vendor_unique; + } rjt_data; +}; + + +/* FC_BSG_HST_CT : */ + +/* Request: + * This message requests that a CT Request be performed with the + * indicated N_Port_ID. The driver is responsible for logging in with + * the fabric and/or N_Port_ID, etc as per FC rules. This request does + * not mandate that the driver must enumerate the destination in the + * transport. The driver is allowed to decide whether to enumerate it, + * and whether to tear it down after the request. + */ +struct fc_bsg_host_ct { + uint8_t reserved; + + /* FC Address Identier of the remote port to send the ELS to */ + uint8_t port_id[3]; + + /* + * We need words 0-2 of the generic preamble for the LLD's + */ + uint32_t preamble_word0; /* revision & IN_ID */ + uint32_t preamble_word1; /* GS_Type, GS_SubType, Options, Rsvd */ + uint32_t preamble_word2; /* Cmd Code, Max Size */ + +}; +/* Response: + * + * The reply structure is an fc_bsg_ctels_reply structure + */ + + +/* FC_BSG_HST_VENDOR : */ + +/* Request: + * Note: When specifying vendor_id, be sure to read the Vendor Type and ID + * formatting requirements specified in scsi_netlink.h + */ +struct fc_bsg_host_vendor { + /* + * Identifies the vendor that the message is formatted for. This + * should be the recipient of the message. + */ + uint64_t vendor_id; + + /* start of vendor command area */ + uint32_t vendor_cmd[0]; +}; + +/* Response: + */ +struct fc_bsg_host_vendor_reply { + /* start of vendor response area */ + uint32_t vendor_rsp[0]; +}; + + + +/* + * FC Remote Port Messages + */ + +/* FC_BSG_RPT_ELS : */ + +/* Request: + * This message requests that an ELS be performed with the rport. + */ +struct fc_bsg_rport_els { + /* + * ELS Command Code being sent (must be the same as + * byte 0 of the payload) + */ + uint8_t els_code; +}; + +/* Response: + * + * The reply structure is an fc_bsg_ctels_reply structure + */ + + +/* FC_BSG_RPT_CT : */ + +/* Request: + * This message requests that a CT Request be performed with the rport. + */ +struct fc_bsg_rport_ct { + /* + * We need words 0-2 of the generic preamble for the LLD's + */ + uint32_t preamble_word0; /* revision & IN_ID */ + uint32_t preamble_word1; /* GS_Type, GS_SubType, Options, Rsvd */ + uint32_t preamble_word2; /* Cmd Code, Max Size */ +}; +/* Response: + * + * The reply structure is an fc_bsg_ctels_reply structure + */ + + + + +/* request (CDB) structure of the sg_io_v4 */ +struct fc_bsg_request { + uint32_t msgcode; + union { + struct fc_bsg_host_add_rport h_addrport; + struct fc_bsg_host_del_rport h_delrport; + struct fc_bsg_host_els h_els; + struct fc_bsg_host_ct h_ct; + struct fc_bsg_host_vendor h_vendor; + + struct fc_bsg_rport_els r_els; + struct fc_bsg_rport_ct r_ct; + } rqst_data; +}; + + +/* response (request sense data) structure of the sg_io_v4 */ +struct fc_bsg_reply { + /* + * The completion result. Result exists in two forms: + * if negative, it is an -Exxx system errno value. There will + * be no further reply information supplied. + * else, it's the 4-byte scsi error result, with driver, host, + * msg and status fields. The per-msgcode reply structure + * will contain valid data. + */ + uint32_t result; + + /* If there was reply_payload, how much was recevied ? */ + uint32_t reply_payload_rcv_len; + + union { + struct fc_bsg_host_vendor_reply vendor_reply; + + struct fc_bsg_ctels_reply ctels_reply; + } reply_data; +}; + + +#endif /* SCSI_BSG_FC_H */ + diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index d123ca84e732..b62a097b3ecb 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -478,6 +478,15 @@ struct scsi_host_template { * module_init/module_exit. */ struct list_head legacy_hosts; + + /* + * Vendor Identifier associated with the host + * + * Note: When specifying vendor_id, be sure to read the + * Vendor Type and ID formatting requirements specified in + * scsi_netlink.h + */ + u64 vendor_id; }; /* diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h index 68a8d873bbd9..fc50bd64aa4e 100644 --- a/include/scsi/scsi_transport_fc.h +++ b/include/scsi/scsi_transport_fc.h @@ -33,7 +33,6 @@ struct scsi_transport_template; - /* * FC Port definitions - Following FC HBAAPI guidelines * @@ -352,6 +351,7 @@ struct fc_rport { /* aka fc_starget_attrs */ struct delayed_work fail_io_work; struct work_struct stgt_delete_work; struct work_struct rport_delete_work; + struct request_queue *rqst_q; /* bsg support */ } __attribute__((aligned(sizeof(unsigned long)))); /* bit field values for struct fc_rport "flags" field: */ @@ -514,6 +514,9 @@ struct fc_host_attrs { struct workqueue_struct *work_q; char devloss_work_q_name[20]; struct workqueue_struct *devloss_work_q; + + /* bsg support */ + struct request_queue *rqst_q; }; #define shost_to_fc_host(x) \ @@ -579,6 +582,47 @@ struct fc_host_attrs { (((struct fc_host_attrs *)(x)->shost_data)->devloss_work_q) +struct fc_bsg_buffer { + unsigned int payload_len; + int sg_cnt; + struct scatterlist *sg_list; +}; + +/* Values for fc_bsg_job->state_flags (bitflags) */ +#define FC_RQST_STATE_INPROGRESS 0 +#define FC_RQST_STATE_DONE 1 + +struct fc_bsg_job { + struct Scsi_Host *shost; + struct fc_rport *rport; + struct device *dev; + struct request *req; + spinlock_t job_lock; + unsigned int state_flags; + unsigned int ref_cnt; + void (*job_done)(struct fc_bsg_job *); + + struct fc_bsg_request *request; + struct fc_bsg_reply *reply; + unsigned int request_len; + unsigned int reply_len; + /* + * On entry : reply_len indicates the buffer size allocated for + * the reply. + * + * Upon completion : the message handler must set reply_len + * to indicates the size of the reply to be returned to the + * caller. + */ + + /* DMA payloads for the request/response */ + struct fc_bsg_buffer request_payload; + struct fc_bsg_buffer reply_payload; + + void *dd_data; /* Used for driver-specific storage */ +}; + + /* The functions by which the transport class and the driver communicate */ struct fc_function_template { void (*get_rport_dev_loss_tmo)(struct fc_rport *); @@ -614,9 +658,14 @@ struct fc_function_template { int (* tsk_mgmt_response)(struct Scsi_Host *, u64, u64, int); int (* it_nexus_response)(struct Scsi_Host *, u64, int); + /* bsg support */ + int (*bsg_request)(struct fc_bsg_job *); + int (*bsg_timeout)(struct fc_bsg_job *); + /* allocation lengths for host-specific data */ u32 dd_fcrport_size; u32 dd_fcvport_size; + u32 dd_bsg_size; /* * The driver sets these to tell the transport class it @@ -737,7 +786,6 @@ fc_vport_set_state(struct fc_vport *vport, enum fc_vport_state new_state) vport->vport_state = new_state; } - struct scsi_transport_template *fc_attach_transport( struct fc_function_template *); void fc_release_transport(struct scsi_transport_template *); |