124073b47SChristof Schmitt /* 224073b47SChristof Schmitt * zfcp device driver 324073b47SChristof Schmitt * 424073b47SChristof Schmitt * Fibre Channel related functions for the zfcp device driver. 524073b47SChristof Schmitt * 6615f59e0SChristof Schmitt * Copyright IBM Corporation 2008, 2010 724073b47SChristof Schmitt */ 824073b47SChristof Schmitt 9ecf39d42SChristof Schmitt #define KMSG_COMPONENT "zfcp" 10ecf39d42SChristof Schmitt #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 11ecf39d42SChristof Schmitt 129d05ce2cSChristof Schmitt #include <linux/types.h> 135a0e3ad6STejun Heo #include <linux/slab.h> 149d05ce2cSChristof Schmitt #include <scsi/fc/fc_els.h> 159d05ce2cSChristof Schmitt #include <scsi/libfc.h> 1624073b47SChristof Schmitt #include "zfcp_ext.h" 179d05ce2cSChristof Schmitt #include "zfcp_fc.h" 1824073b47SChristof Schmitt 199d05ce2cSChristof Schmitt static u32 zfcp_fc_rscn_range_mask[] = { 209d05ce2cSChristof Schmitt [ELS_ADDR_FMT_PORT] = 0xFFFFFF, 219d05ce2cSChristof Schmitt [ELS_ADDR_FMT_AREA] = 0xFFFF00, 229d05ce2cSChristof Schmitt [ELS_ADDR_FMT_DOM] = 0xFF0000, 239d05ce2cSChristof Schmitt [ELS_ADDR_FMT_FAB] = 0x000000, 24e0d7fcb5SChristof Schmitt }; 25e0d7fcb5SChristof Schmitt 262d1e547fSSven Schuetz /** 272d1e547fSSven Schuetz * zfcp_fc_post_event - post event to userspace via fc_transport 282d1e547fSSven Schuetz * @work: work struct with enqueued events 292d1e547fSSven Schuetz */ 302d1e547fSSven Schuetz void zfcp_fc_post_event(struct work_struct *work) 312d1e547fSSven Schuetz { 322d1e547fSSven Schuetz struct zfcp_fc_event *event = NULL, *tmp = NULL; 332d1e547fSSven Schuetz LIST_HEAD(tmp_lh); 342d1e547fSSven Schuetz struct zfcp_fc_events *events = container_of(work, 352d1e547fSSven Schuetz struct zfcp_fc_events, work); 362d1e547fSSven Schuetz struct zfcp_adapter *adapter = container_of(events, struct zfcp_adapter, 372d1e547fSSven Schuetz events); 382d1e547fSSven Schuetz 392d1e547fSSven Schuetz spin_lock_bh(&events->list_lock); 402d1e547fSSven Schuetz list_splice_init(&events->list, &tmp_lh); 412d1e547fSSven Schuetz spin_unlock_bh(&events->list_lock); 422d1e547fSSven Schuetz 432d1e547fSSven Schuetz list_for_each_entry_safe(event, tmp, &tmp_lh, list) { 442d1e547fSSven Schuetz fc_host_post_event(adapter->scsi_host, fc_get_event_number(), 452d1e547fSSven Schuetz event->code, event->data); 462d1e547fSSven Schuetz list_del(&event->list); 472d1e547fSSven Schuetz kfree(event); 482d1e547fSSven Schuetz } 492d1e547fSSven Schuetz 502d1e547fSSven Schuetz } 512d1e547fSSven Schuetz 522d1e547fSSven Schuetz /** 532d1e547fSSven Schuetz * zfcp_fc_enqueue_event - safely enqueue FC HBA API event from irq context 542d1e547fSSven Schuetz * @adapter: The adapter where to enqueue the event 552d1e547fSSven Schuetz * @event_code: The event code (as defined in fc_host_event_code in 562d1e547fSSven Schuetz * scsi_transport_fc.h) 572d1e547fSSven Schuetz * @event_data: The event data (e.g. n_port page in case of els) 582d1e547fSSven Schuetz */ 592d1e547fSSven Schuetz void zfcp_fc_enqueue_event(struct zfcp_adapter *adapter, 602d1e547fSSven Schuetz enum fc_host_event_code event_code, u32 event_data) 612d1e547fSSven Schuetz { 622d1e547fSSven Schuetz struct zfcp_fc_event *event; 632d1e547fSSven Schuetz 642d1e547fSSven Schuetz event = kmalloc(sizeof(struct zfcp_fc_event), GFP_ATOMIC); 652d1e547fSSven Schuetz if (!event) 662d1e547fSSven Schuetz return; 672d1e547fSSven Schuetz 682d1e547fSSven Schuetz event->code = event_code; 692d1e547fSSven Schuetz event->data = event_data; 702d1e547fSSven Schuetz 712d1e547fSSven Schuetz spin_lock(&adapter->events.list_lock); 722d1e547fSSven Schuetz list_add_tail(&event->list, &adapter->events.list); 732d1e547fSSven Schuetz spin_unlock(&adapter->events.list_lock); 742d1e547fSSven Schuetz 752d1e547fSSven Schuetz queue_work(adapter->work_queue, &adapter->events.work); 762d1e547fSSven Schuetz } 772d1e547fSSven Schuetz 78bd0072ecSChristof Schmitt static int zfcp_fc_wka_port_get(struct zfcp_fc_wka_port *wka_port) 795ab944f9SSwen Schillig { 805ab944f9SSwen Schillig if (mutex_lock_interruptible(&wka_port->mutex)) 815ab944f9SSwen Schillig return -ERESTARTSYS; 825ab944f9SSwen Schillig 83bd0072ecSChristof Schmitt if (wka_port->status == ZFCP_FC_WKA_PORT_OFFLINE || 84bd0072ecSChristof Schmitt wka_port->status == ZFCP_FC_WKA_PORT_CLOSING) { 85bd0072ecSChristof Schmitt wka_port->status = ZFCP_FC_WKA_PORT_OPENING; 865ab944f9SSwen Schillig if (zfcp_fsf_open_wka_port(wka_port)) 87bd0072ecSChristof Schmitt wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; 885ab944f9SSwen Schillig } 895ab944f9SSwen Schillig 905ab944f9SSwen Schillig mutex_unlock(&wka_port->mutex); 915ab944f9SSwen Schillig 9227f492ccSSwen Schillig wait_event(wka_port->completion_wq, 93bd0072ecSChristof Schmitt wka_port->status == ZFCP_FC_WKA_PORT_ONLINE || 94bd0072ecSChristof Schmitt wka_port->status == ZFCP_FC_WKA_PORT_OFFLINE); 955ab944f9SSwen Schillig 96bd0072ecSChristof Schmitt if (wka_port->status == ZFCP_FC_WKA_PORT_ONLINE) { 975ab944f9SSwen Schillig atomic_inc(&wka_port->refcount); 985ab944f9SSwen Schillig return 0; 995ab944f9SSwen Schillig } 1005ab944f9SSwen Schillig return -EIO; 1015ab944f9SSwen Schillig } 1025ab944f9SSwen Schillig 1036f53a2d2SSwen Schillig static void zfcp_fc_wka_port_offline(struct work_struct *work) 1045ab944f9SSwen Schillig { 105bf6aede7SJean Delvare struct delayed_work *dw = to_delayed_work(work); 106bd0072ecSChristof Schmitt struct zfcp_fc_wka_port *wka_port = 107bd0072ecSChristof Schmitt container_of(dw, struct zfcp_fc_wka_port, work); 1085ab944f9SSwen Schillig 1095ab944f9SSwen Schillig mutex_lock(&wka_port->mutex); 1105ab944f9SSwen Schillig if ((atomic_read(&wka_port->refcount) != 0) || 111bd0072ecSChristof Schmitt (wka_port->status != ZFCP_FC_WKA_PORT_ONLINE)) 1125ab944f9SSwen Schillig goto out; 1135ab944f9SSwen Schillig 114bd0072ecSChristof Schmitt wka_port->status = ZFCP_FC_WKA_PORT_CLOSING; 1155ab944f9SSwen Schillig if (zfcp_fsf_close_wka_port(wka_port)) { 116bd0072ecSChristof Schmitt wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; 1175ab944f9SSwen Schillig wake_up(&wka_port->completion_wq); 1185ab944f9SSwen Schillig } 1195ab944f9SSwen Schillig out: 1205ab944f9SSwen Schillig mutex_unlock(&wka_port->mutex); 1215ab944f9SSwen Schillig } 1225ab944f9SSwen Schillig 123bd0072ecSChristof Schmitt static void zfcp_fc_wka_port_put(struct zfcp_fc_wka_port *wka_port) 1245ab944f9SSwen Schillig { 1255ab944f9SSwen Schillig if (atomic_dec_return(&wka_port->refcount) != 0) 1265ab944f9SSwen Schillig return; 12719af5cdbSMartin Olsson /* wait 10 milliseconds, other reqs might pop in */ 1285ab944f9SSwen Schillig schedule_delayed_work(&wka_port->work, HZ / 100); 1295ab944f9SSwen Schillig } 1305ab944f9SSwen Schillig 131bd0072ecSChristof Schmitt static void zfcp_fc_wka_port_init(struct zfcp_fc_wka_port *wka_port, u32 d_id, 1329d544f2bSSven Schuetz struct zfcp_adapter *adapter) 1335ab944f9SSwen Schillig { 1345ab944f9SSwen Schillig init_waitqueue_head(&wka_port->completion_wq); 1355ab944f9SSwen Schillig 1365ab944f9SSwen Schillig wka_port->adapter = adapter; 1379d544f2bSSven Schuetz wka_port->d_id = d_id; 1385ab944f9SSwen Schillig 139bd0072ecSChristof Schmitt wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; 1405ab944f9SSwen Schillig atomic_set(&wka_port->refcount, 0); 1415ab944f9SSwen Schillig mutex_init(&wka_port->mutex); 1426f53a2d2SSwen Schillig INIT_DELAYED_WORK(&wka_port->work, zfcp_fc_wka_port_offline); 1435ab944f9SSwen Schillig } 1445ab944f9SSwen Schillig 145bd0072ecSChristof Schmitt static void zfcp_fc_wka_port_force_offline(struct zfcp_fc_wka_port *wka) 146828bc121SSwen Schillig { 147828bc121SSwen Schillig cancel_delayed_work_sync(&wka->work); 148828bc121SSwen Schillig mutex_lock(&wka->mutex); 149bd0072ecSChristof Schmitt wka->status = ZFCP_FC_WKA_PORT_OFFLINE; 150828bc121SSwen Schillig mutex_unlock(&wka->mutex); 151828bc121SSwen Schillig } 152828bc121SSwen Schillig 153bd0072ecSChristof Schmitt void zfcp_fc_wka_ports_force_offline(struct zfcp_fc_wka_ports *gs) 15455c770faSChristof Schmitt { 155f3450c7bSSwen Schillig if (!gs) 156f3450c7bSSwen Schillig return; 15755c770faSChristof Schmitt zfcp_fc_wka_port_force_offline(&gs->ms); 15855c770faSChristof Schmitt zfcp_fc_wka_port_force_offline(&gs->ts); 15955c770faSChristof Schmitt zfcp_fc_wka_port_force_offline(&gs->ds); 16055c770faSChristof Schmitt zfcp_fc_wka_port_force_offline(&gs->as); 16155c770faSChristof Schmitt } 16255c770faSChristof Schmitt 16324073b47SChristof Schmitt static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range, 1649d05ce2cSChristof Schmitt struct fc_els_rscn_page *page) 16524073b47SChristof Schmitt { 16624073b47SChristof Schmitt unsigned long flags; 167ecf0c772SSwen Schillig struct zfcp_adapter *adapter = fsf_req->adapter; 16824073b47SChristof Schmitt struct zfcp_port *port; 16924073b47SChristof Schmitt 170ecf0c772SSwen Schillig read_lock_irqsave(&adapter->port_list_lock, flags); 171ecf0c772SSwen Schillig list_for_each_entry(port, &adapter->port_list, list) { 1729d05ce2cSChristof Schmitt if ((port->d_id & range) == (ntoh24(page->rscn_fid) & range)) 1736f53a2d2SSwen Schillig zfcp_fc_test_link(port); 174ea460a81SSwen Schillig if (!port->d_id) 175ea460a81SSwen Schillig zfcp_erp_port_reopen(port, 176ea460a81SSwen Schillig ZFCP_STATUS_COMMON_ERP_FAILED, 177ea460a81SSwen Schillig "fcrscn1", NULL); 178ea460a81SSwen Schillig } 179ecf0c772SSwen Schillig read_unlock_irqrestore(&adapter->port_list_lock, flags); 18024073b47SChristof Schmitt } 18124073b47SChristof Schmitt 18224073b47SChristof Schmitt static void zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req) 18324073b47SChristof Schmitt { 18424073b47SChristof Schmitt struct fsf_status_read_buffer *status_buffer = (void *)fsf_req->data; 1859d05ce2cSChristof Schmitt struct fc_els_rscn *head; 1869d05ce2cSChristof Schmitt struct fc_els_rscn_page *page; 18724073b47SChristof Schmitt u16 i; 18824073b47SChristof Schmitt u16 no_entries; 1899d05ce2cSChristof Schmitt unsigned int afmt; 19024073b47SChristof Schmitt 1919d05ce2cSChristof Schmitt head = (struct fc_els_rscn *) status_buffer->payload.data; 1929d05ce2cSChristof Schmitt page = (struct fc_els_rscn_page *) head; 19324073b47SChristof Schmitt 19424073b47SChristof Schmitt /* see FC-FS */ 1959d05ce2cSChristof Schmitt no_entries = head->rscn_plen / sizeof(struct fc_els_rscn_page); 19624073b47SChristof Schmitt 19724073b47SChristof Schmitt for (i = 1; i < no_entries; i++) { 19824073b47SChristof Schmitt /* skip head and start with 1st element */ 1999d05ce2cSChristof Schmitt page++; 2009d05ce2cSChristof Schmitt afmt = page->rscn_page_flags & ELS_RSCN_ADDR_FMT_MASK; 2019d05ce2cSChristof Schmitt _zfcp_fc_incoming_rscn(fsf_req, zfcp_fc_rscn_range_mask[afmt], 2029d05ce2cSChristof Schmitt page); 2032d1e547fSSven Schuetz zfcp_fc_enqueue_event(fsf_req->adapter, FCH_EVT_RSCN, 2042d1e547fSSven Schuetz *(u32 *)page); 20524073b47SChristof Schmitt } 2069eae07efSSwen Schillig queue_work(fsf_req->adapter->work_queue, &fsf_req->adapter->scan_work); 20724073b47SChristof Schmitt } 20824073b47SChristof Schmitt 2097ba58c9cSSwen Schillig static void zfcp_fc_incoming_wwpn(struct zfcp_fsf_req *req, u64 wwpn) 21024073b47SChristof Schmitt { 211ecf0c772SSwen Schillig unsigned long flags; 21224073b47SChristof Schmitt struct zfcp_adapter *adapter = req->adapter; 21324073b47SChristof Schmitt struct zfcp_port *port; 21424073b47SChristof Schmitt 215ecf0c772SSwen Schillig read_lock_irqsave(&adapter->port_list_lock, flags); 216ecf0c772SSwen Schillig list_for_each_entry(port, &adapter->port_list, list) 217ecf0c772SSwen Schillig if (port->wwpn == wwpn) { 2185ffd51a5SSwen Schillig zfcp_erp_port_forced_reopen(port, 0, "fciwwp1", req); 219ecf0c772SSwen Schillig break; 220ecf0c772SSwen Schillig } 221ecf0c772SSwen Schillig read_unlock_irqrestore(&adapter->port_list_lock, flags); 22224073b47SChristof Schmitt } 22324073b47SChristof Schmitt 22424073b47SChristof Schmitt static void zfcp_fc_incoming_plogi(struct zfcp_fsf_req *req) 22524073b47SChristof Schmitt { 2269d05ce2cSChristof Schmitt struct fsf_status_read_buffer *status_buffer; 2279d05ce2cSChristof Schmitt struct fc_els_flogi *plogi; 22824073b47SChristof Schmitt 2299d05ce2cSChristof Schmitt status_buffer = (struct fsf_status_read_buffer *) req->data; 2309d05ce2cSChristof Schmitt plogi = (struct fc_els_flogi *) status_buffer->payload.data; 2319d05ce2cSChristof Schmitt zfcp_fc_incoming_wwpn(req, plogi->fl_wwpn); 23224073b47SChristof Schmitt } 23324073b47SChristof Schmitt 23424073b47SChristof Schmitt static void zfcp_fc_incoming_logo(struct zfcp_fsf_req *req) 23524073b47SChristof Schmitt { 23624073b47SChristof Schmitt struct fsf_status_read_buffer *status_buffer = 23724073b47SChristof Schmitt (struct fsf_status_read_buffer *)req->data; 2389d05ce2cSChristof Schmitt struct fc_els_logo *logo = 2399d05ce2cSChristof Schmitt (struct fc_els_logo *) status_buffer->payload.data; 24024073b47SChristof Schmitt 2419d05ce2cSChristof Schmitt zfcp_fc_incoming_wwpn(req, logo->fl_n_port_wwn); 24224073b47SChristof Schmitt } 24324073b47SChristof Schmitt 24424073b47SChristof Schmitt /** 24524073b47SChristof Schmitt * zfcp_fc_incoming_els - handle incoming ELS 24624073b47SChristof Schmitt * @fsf_req - request which contains incoming ELS 24724073b47SChristof Schmitt */ 24824073b47SChristof Schmitt void zfcp_fc_incoming_els(struct zfcp_fsf_req *fsf_req) 24924073b47SChristof Schmitt { 25024073b47SChristof Schmitt struct fsf_status_read_buffer *status_buffer = 25124073b47SChristof Schmitt (struct fsf_status_read_buffer *) fsf_req->data; 252c41f8cbdSSwen Schillig unsigned int els_type = status_buffer->payload.data[0]; 25324073b47SChristof Schmitt 2545771710bSSwen Schillig zfcp_dbf_san_incoming_els(fsf_req); 2559d05ce2cSChristof Schmitt if (els_type == ELS_PLOGI) 25624073b47SChristof Schmitt zfcp_fc_incoming_plogi(fsf_req); 2579d05ce2cSChristof Schmitt else if (els_type == ELS_LOGO) 25824073b47SChristof Schmitt zfcp_fc_incoming_logo(fsf_req); 2599d05ce2cSChristof Schmitt else if (els_type == ELS_RSCN) 26024073b47SChristof Schmitt zfcp_fc_incoming_rscn(fsf_req); 26124073b47SChristof Schmitt } 26224073b47SChristof Schmitt 2637c7dc196SChristof Schmitt static void zfcp_fc_ns_gid_pn_eval(void *data) 2645ab944f9SSwen Schillig { 2657c7dc196SChristof Schmitt struct zfcp_fc_gid_pn *gid_pn = data; 2667c7dc196SChristof Schmitt struct zfcp_fsf_ct_els *ct = &gid_pn->ct; 267dbf5dfe9SChristof Schmitt struct zfcp_fc_gid_pn_req *gid_pn_req = sg_virt(ct->req); 268dbf5dfe9SChristof Schmitt struct zfcp_fc_gid_pn_resp *gid_pn_resp = sg_virt(ct->resp); 26924073b47SChristof Schmitt struct zfcp_port *port = gid_pn->port; 27024073b47SChristof Schmitt 27124073b47SChristof Schmitt if (ct->status) 2725ab944f9SSwen Schillig return; 273dbf5dfe9SChristof Schmitt if (gid_pn_resp->ct_hdr.ct_cmd != FC_FS_ACC) 2745ab944f9SSwen Schillig return; 275a5b11ddaSChristof Schmitt 27624073b47SChristof Schmitt /* paranoia */ 277dbf5dfe9SChristof Schmitt if (gid_pn_req->gid_pn.fn_wwpn != port->wwpn) 2785ab944f9SSwen Schillig return; 27924073b47SChristof Schmitt /* looks like a valid d_id */ 280dbf5dfe9SChristof Schmitt port->d_id = ntoh24(gid_pn_resp->gid_pn.fp_fid); 28124073b47SChristof Schmitt } 28224073b47SChristof Schmitt 2837c7dc196SChristof Schmitt static void zfcp_fc_complete(void *data) 2847c7dc196SChristof Schmitt { 2857c7dc196SChristof Schmitt complete(data); 2867c7dc196SChristof Schmitt } 2877c7dc196SChristof Schmitt 288799b76d0SChristof Schmitt static int zfcp_fc_ns_gid_pn_request(struct zfcp_port *port, 289dbf5dfe9SChristof Schmitt struct zfcp_fc_gid_pn *gid_pn) 29024073b47SChristof Schmitt { 291799b76d0SChristof Schmitt struct zfcp_adapter *adapter = port->adapter; 2927c7dc196SChristof Schmitt DECLARE_COMPLETION_ONSTACK(completion); 2935ab944f9SSwen Schillig int ret; 29424073b47SChristof Schmitt 29524073b47SChristof Schmitt /* setup parameters for send generic command */ 296799b76d0SChristof Schmitt gid_pn->port = port; 2977c7dc196SChristof Schmitt gid_pn->ct.handler = zfcp_fc_complete; 2987c7dc196SChristof Schmitt gid_pn->ct.handler_data = &completion; 299dbf5dfe9SChristof Schmitt gid_pn->ct.req = &gid_pn->sg_req; 300dbf5dfe9SChristof Schmitt gid_pn->ct.resp = &gid_pn->sg_resp; 301dbf5dfe9SChristof Schmitt sg_init_one(&gid_pn->sg_req, &gid_pn->gid_pn_req, 302dbf5dfe9SChristof Schmitt sizeof(struct zfcp_fc_gid_pn_req)); 303dbf5dfe9SChristof Schmitt sg_init_one(&gid_pn->sg_resp, &gid_pn->gid_pn_resp, 304dbf5dfe9SChristof Schmitt sizeof(struct zfcp_fc_gid_pn_resp)); 30524073b47SChristof Schmitt 30624073b47SChristof Schmitt /* setup nameserver request */ 307dbf5dfe9SChristof Schmitt gid_pn->gid_pn_req.ct_hdr.ct_rev = FC_CT_REV; 308dbf5dfe9SChristof Schmitt gid_pn->gid_pn_req.ct_hdr.ct_fs_type = FC_FST_DIR; 309dbf5dfe9SChristof Schmitt gid_pn->gid_pn_req.ct_hdr.ct_fs_subtype = FC_NS_SUBTYPE; 310dbf5dfe9SChristof Schmitt gid_pn->gid_pn_req.ct_hdr.ct_options = 0; 311dbf5dfe9SChristof Schmitt gid_pn->gid_pn_req.ct_hdr.ct_cmd = FC_NS_GID_PN; 312dbf5dfe9SChristof Schmitt gid_pn->gid_pn_req.ct_hdr.ct_mr_size = ZFCP_FC_CT_SIZE_PAGE / 4; 313dbf5dfe9SChristof Schmitt gid_pn->gid_pn_req.gid_pn.fn_wwpn = port->wwpn; 31424073b47SChristof Schmitt 3157c7dc196SChristof Schmitt ret = zfcp_fsf_send_ct(&adapter->gs->ds, &gid_pn->ct, 31651375ee8SSwen Schillig adapter->pool.gid_pn_req, 31751375ee8SSwen Schillig ZFCP_FC_CTELS_TMO); 3187c7dc196SChristof Schmitt if (!ret) { 3197c7dc196SChristof Schmitt wait_for_completion(&completion); 3207c7dc196SChristof Schmitt zfcp_fc_ns_gid_pn_eval(gid_pn); 3217c7dc196SChristof Schmitt } 3225ab944f9SSwen Schillig return ret; 3235ab944f9SSwen Schillig } 3245ab944f9SSwen Schillig 3255ab944f9SSwen Schillig /** 3265ab944f9SSwen Schillig * zfcp_fc_ns_gid_pn_request - initiate GID_PN nameserver request 327799b76d0SChristof Schmitt * @port: port where GID_PN request is needed 3285ab944f9SSwen Schillig * return: -ENOMEM on error, 0 otherwise 3295ab944f9SSwen Schillig */ 330799b76d0SChristof Schmitt static int zfcp_fc_ns_gid_pn(struct zfcp_port *port) 3315ab944f9SSwen Schillig { 3325ab944f9SSwen Schillig int ret; 333dbf5dfe9SChristof Schmitt struct zfcp_fc_gid_pn *gid_pn; 334799b76d0SChristof Schmitt struct zfcp_adapter *adapter = port->adapter; 3355ab944f9SSwen Schillig 336dbf5dfe9SChristof Schmitt gid_pn = mempool_alloc(adapter->pool.gid_pn, GFP_ATOMIC); 3375ab944f9SSwen Schillig if (!gid_pn) 3385ab944f9SSwen Schillig return -ENOMEM; 3395ab944f9SSwen Schillig 3405ab944f9SSwen Schillig memset(gid_pn, 0, sizeof(*gid_pn)); 3415ab944f9SSwen Schillig 3426f53a2d2SSwen Schillig ret = zfcp_fc_wka_port_get(&adapter->gs->ds); 34324073b47SChristof Schmitt if (ret) 3445ab944f9SSwen Schillig goto out; 3455ab944f9SSwen Schillig 346799b76d0SChristof Schmitt ret = zfcp_fc_ns_gid_pn_request(port, gid_pn); 3475ab944f9SSwen Schillig 3486f53a2d2SSwen Schillig zfcp_fc_wka_port_put(&adapter->gs->ds); 3495ab944f9SSwen Schillig out: 350dbf5dfe9SChristof Schmitt mempool_free(gid_pn, adapter->pool.gid_pn); 35124073b47SChristof Schmitt return ret; 35224073b47SChristof Schmitt } 35324073b47SChristof Schmitt 354799b76d0SChristof Schmitt void zfcp_fc_port_did_lookup(struct work_struct *work) 355799b76d0SChristof Schmitt { 356799b76d0SChristof Schmitt int ret; 357799b76d0SChristof Schmitt struct zfcp_port *port = container_of(work, struct zfcp_port, 358799b76d0SChristof Schmitt gid_pn_work); 359799b76d0SChristof Schmitt 360799b76d0SChristof Schmitt ret = zfcp_fc_ns_gid_pn(port); 361799b76d0SChristof Schmitt if (ret) { 362799b76d0SChristof Schmitt /* could not issue gid_pn for some reason */ 363799b76d0SChristof Schmitt zfcp_erp_adapter_reopen(port->adapter, 0, "fcgpn_1", NULL); 364799b76d0SChristof Schmitt goto out; 365799b76d0SChristof Schmitt } 366799b76d0SChristof Schmitt 367799b76d0SChristof Schmitt if (!port->d_id) { 368*edaed859SSwen Schillig zfcp_erp_set_port_status(port, ZFCP_STATUS_COMMON_ERP_FAILED); 369799b76d0SChristof Schmitt goto out; 370799b76d0SChristof Schmitt } 371799b76d0SChristof Schmitt 372799b76d0SChristof Schmitt zfcp_erp_port_reopen(port, 0, "fcgpn_3", NULL); 373799b76d0SChristof Schmitt out: 374615f59e0SChristof Schmitt put_device(&port->dev); 375799b76d0SChristof Schmitt } 376799b76d0SChristof Schmitt 37724073b47SChristof Schmitt /** 378934aeb58SChristof Schmitt * zfcp_fc_trigger_did_lookup - trigger the d_id lookup using a GID_PN request 379934aeb58SChristof Schmitt * @port: The zfcp_port to lookup the d_id for. 380934aeb58SChristof Schmitt */ 381934aeb58SChristof Schmitt void zfcp_fc_trigger_did_lookup(struct zfcp_port *port) 382934aeb58SChristof Schmitt { 383615f59e0SChristof Schmitt get_device(&port->dev); 384934aeb58SChristof Schmitt if (!queue_work(port->adapter->work_queue, &port->gid_pn_work)) 385615f59e0SChristof Schmitt put_device(&port->dev); 386934aeb58SChristof Schmitt } 387934aeb58SChristof Schmitt 388934aeb58SChristof Schmitt /** 38924073b47SChristof Schmitt * zfcp_fc_plogi_evaluate - evaluate PLOGI playload 39024073b47SChristof Schmitt * @port: zfcp_port structure 39124073b47SChristof Schmitt * @plogi: plogi payload 39224073b47SChristof Schmitt * 39324073b47SChristof Schmitt * Evaluate PLOGI playload and copy important fields into zfcp_port structure 39424073b47SChristof Schmitt */ 3959d05ce2cSChristof Schmitt void zfcp_fc_plogi_evaluate(struct zfcp_port *port, struct fc_els_flogi *plogi) 39624073b47SChristof Schmitt { 3979d05ce2cSChristof Schmitt if (plogi->fl_wwpn != port->wwpn) { 3989d05ce2cSChristof Schmitt port->d_id = 0; 3999d05ce2cSChristof Schmitt dev_warn(&port->adapter->ccw_device->dev, 4009d05ce2cSChristof Schmitt "A port opened with WWPN 0x%016Lx returned data that " 4019d05ce2cSChristof Schmitt "identifies it as WWPN 0x%016Lx\n", 4029d05ce2cSChristof Schmitt (unsigned long long) port->wwpn, 4039d05ce2cSChristof Schmitt (unsigned long long) plogi->fl_wwpn); 4049d05ce2cSChristof Schmitt return; 4059d05ce2cSChristof Schmitt } 4069d05ce2cSChristof Schmitt 4079d05ce2cSChristof Schmitt port->wwnn = plogi->fl_wwnn; 4089d05ce2cSChristof Schmitt port->maxframe_size = plogi->fl_csp.sp_bb_data; 4099d05ce2cSChristof Schmitt 4109d05ce2cSChristof Schmitt if (plogi->fl_cssp[0].cp_class & FC_CPC_VALID) 41124073b47SChristof Schmitt port->supported_classes |= FC_COS_CLASS1; 4129d05ce2cSChristof Schmitt if (plogi->fl_cssp[1].cp_class & FC_CPC_VALID) 41324073b47SChristof Schmitt port->supported_classes |= FC_COS_CLASS2; 4149d05ce2cSChristof Schmitt if (plogi->fl_cssp[2].cp_class & FC_CPC_VALID) 41524073b47SChristof Schmitt port->supported_classes |= FC_COS_CLASS3; 4169d05ce2cSChristof Schmitt if (plogi->fl_cssp[3].cp_class & FC_CPC_VALID) 41724073b47SChristof Schmitt port->supported_classes |= FC_COS_CLASS4; 41824073b47SChristof Schmitt } 41924073b47SChristof Schmitt 4207c7dc196SChristof Schmitt static void zfcp_fc_adisc_handler(void *data) 42124073b47SChristof Schmitt { 4227c7dc196SChristof Schmitt struct zfcp_fc_els_adisc *adisc = data; 42324073b47SChristof Schmitt struct zfcp_port *port = adisc->els.port; 4249d05ce2cSChristof Schmitt struct fc_els_adisc *adisc_resp = &adisc->adisc_resp; 42524073b47SChristof Schmitt 4263968ce80SChristof Schmitt if (adisc->els.status) { 42724073b47SChristof Schmitt /* request rejected or timed out */ 4285b43e719SSwen Schillig zfcp_erp_port_forced_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, 4295b43e719SSwen Schillig "fcadh_1", NULL); 43024073b47SChristof Schmitt goto out; 43124073b47SChristof Schmitt } 43224073b47SChristof Schmitt 43324073b47SChristof Schmitt if (!port->wwnn) 4349d05ce2cSChristof Schmitt port->wwnn = adisc_resp->adisc_wwnn; 43524073b47SChristof Schmitt 4369d05ce2cSChristof Schmitt if ((port->wwpn != adisc_resp->adisc_wwpn) || 437a2fa0aedSChristof Schmitt !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_OPEN)) { 43824095490SSwen Schillig zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, 43924095490SSwen Schillig "fcadh_2", NULL); 440a2fa0aedSChristof Schmitt goto out; 441a2fa0aedSChristof Schmitt } 44224073b47SChristof Schmitt 443a2fa0aedSChristof Schmitt /* port is good, unblock rport without going through erp */ 444a2fa0aedSChristof Schmitt zfcp_scsi_schedule_rport_register(port); 44524073b47SChristof Schmitt out: 44614e242eaSChristof Schmitt atomic_clear_mask(ZFCP_STATUS_PORT_LINK_TEST, &port->status); 447615f59e0SChristof Schmitt put_device(&port->dev); 448ee744622SChristof Schmitt kmem_cache_free(zfcp_data.adisc_cache, adisc); 44924073b47SChristof Schmitt } 45024073b47SChristof Schmitt 45124073b47SChristof Schmitt static int zfcp_fc_adisc(struct zfcp_port *port) 45224073b47SChristof Schmitt { 4539d05ce2cSChristof Schmitt struct zfcp_fc_els_adisc *adisc; 45424073b47SChristof Schmitt struct zfcp_adapter *adapter = port->adapter; 455ee744622SChristof Schmitt int ret; 45624073b47SChristof Schmitt 4576e51f085SChristof Schmitt adisc = kmem_cache_zalloc(zfcp_data.adisc_cache, GFP_ATOMIC); 45824073b47SChristof Schmitt if (!adisc) 45924073b47SChristof Schmitt return -ENOMEM; 46024073b47SChristof Schmitt 4617c7dc196SChristof Schmitt adisc->els.port = port; 46224073b47SChristof Schmitt adisc->els.req = &adisc->req; 46324073b47SChristof Schmitt adisc->els.resp = &adisc->resp; 4649d05ce2cSChristof Schmitt sg_init_one(adisc->els.req, &adisc->adisc_req, 4659d05ce2cSChristof Schmitt sizeof(struct fc_els_adisc)); 4669d05ce2cSChristof Schmitt sg_init_one(adisc->els.resp, &adisc->adisc_resp, 4679d05ce2cSChristof Schmitt sizeof(struct fc_els_adisc)); 46824073b47SChristof Schmitt 46924073b47SChristof Schmitt adisc->els.handler = zfcp_fc_adisc_handler; 4707c7dc196SChristof Schmitt adisc->els.handler_data = adisc; 47124073b47SChristof Schmitt 47224073b47SChristof Schmitt /* acc. to FC-FS, hard_nport_id in ADISC should not be set for ports 47324073b47SChristof Schmitt without FC-AL-2 capability, so we don't set it */ 4749d05ce2cSChristof Schmitt adisc->adisc_req.adisc_wwpn = fc_host_port_name(adapter->scsi_host); 4759d05ce2cSChristof Schmitt adisc->adisc_req.adisc_wwnn = fc_host_node_name(adapter->scsi_host); 4767c7dc196SChristof Schmitt adisc->adisc_req.adisc_cmd = ELS_ADISC; 4779d05ce2cSChristof Schmitt hton24(adisc->adisc_req.adisc_port_id, 4789d05ce2cSChristof Schmitt fc_host_port_id(adapter->scsi_host)); 47924073b47SChristof Schmitt 48051375ee8SSwen Schillig ret = zfcp_fsf_send_els(adapter, port->d_id, &adisc->els, 48151375ee8SSwen Schillig ZFCP_FC_CTELS_TMO); 482ee744622SChristof Schmitt if (ret) 483ee744622SChristof Schmitt kmem_cache_free(zfcp_data.adisc_cache, adisc); 484ee744622SChristof Schmitt 485ee744622SChristof Schmitt return ret; 48624073b47SChristof Schmitt } 48724073b47SChristof Schmitt 4888fdf30d5SChristof Schmitt void zfcp_fc_link_test_work(struct work_struct *work) 4898fdf30d5SChristof Schmitt { 4908fdf30d5SChristof Schmitt struct zfcp_port *port = 4918fdf30d5SChristof Schmitt container_of(work, struct zfcp_port, test_link_work); 4928fdf30d5SChristof Schmitt int retval; 4938fdf30d5SChristof Schmitt 494615f59e0SChristof Schmitt get_device(&port->dev); 495a2fa0aedSChristof Schmitt port->rport_task = RPORT_DEL; 496a2fa0aedSChristof Schmitt zfcp_scsi_rport_work(&port->rport_work); 497a2fa0aedSChristof Schmitt 49814e242eaSChristof Schmitt /* only issue one test command at one time per port */ 49914e242eaSChristof Schmitt if (atomic_read(&port->status) & ZFCP_STATUS_PORT_LINK_TEST) 50014e242eaSChristof Schmitt goto out; 50114e242eaSChristof Schmitt 50214e242eaSChristof Schmitt atomic_set_mask(ZFCP_STATUS_PORT_LINK_TEST, &port->status); 50314e242eaSChristof Schmitt 5048fdf30d5SChristof Schmitt retval = zfcp_fc_adisc(port); 5058fdf30d5SChristof Schmitt if (retval == 0) 5068fdf30d5SChristof Schmitt return; 5078fdf30d5SChristof Schmitt 5088fdf30d5SChristof Schmitt /* send of ADISC was not possible */ 50914e242eaSChristof Schmitt atomic_clear_mask(ZFCP_STATUS_PORT_LINK_TEST, &port->status); 5105ffd51a5SSwen Schillig zfcp_erp_port_forced_reopen(port, 0, "fcltwk1", NULL); 511a2fa0aedSChristof Schmitt 51214e242eaSChristof Schmitt out: 513615f59e0SChristof Schmitt put_device(&port->dev); 5148fdf30d5SChristof Schmitt } 5158fdf30d5SChristof Schmitt 51624073b47SChristof Schmitt /** 5176f53a2d2SSwen Schillig * zfcp_fc_test_link - lightweight link test procedure 51824073b47SChristof Schmitt * @port: port to be tested 51924073b47SChristof Schmitt * 52024073b47SChristof Schmitt * Test status of a link to a remote port using the ELS command ADISC. 52124073b47SChristof Schmitt * If there is a problem with the remote port, error recovery steps 52224073b47SChristof Schmitt * will be triggered. 52324073b47SChristof Schmitt */ 5246f53a2d2SSwen Schillig void zfcp_fc_test_link(struct zfcp_port *port) 52524073b47SChristof Schmitt { 526615f59e0SChristof Schmitt get_device(&port->dev); 5274544683aSSwen Schillig if (!queue_work(port->adapter->work_queue, &port->test_link_work)) 528615f59e0SChristof Schmitt put_device(&port->dev); 52924073b47SChristof Schmitt } 530cc8c2829SSwen Schillig 531dbf5dfe9SChristof Schmitt static void zfcp_free_sg_env(struct zfcp_fc_gpn_ft *gpn_ft, int buf_num) 532cc8c2829SSwen Schillig { 533cc8c2829SSwen Schillig struct scatterlist *sg = &gpn_ft->sg_req; 534cc8c2829SSwen Schillig 535a4623c46SSwen Schillig kmem_cache_free(zfcp_data.gpn_ft_cache, sg_virt(sg)); 53639eb7e9aSChristof Schmitt zfcp_sg_free_table(gpn_ft->sg_resp, buf_num); 537cc8c2829SSwen Schillig 538cc8c2829SSwen Schillig kfree(gpn_ft); 539cc8c2829SSwen Schillig } 540cc8c2829SSwen Schillig 541dbf5dfe9SChristof Schmitt static struct zfcp_fc_gpn_ft *zfcp_alloc_sg_env(int buf_num) 542cc8c2829SSwen Schillig { 543dbf5dfe9SChristof Schmitt struct zfcp_fc_gpn_ft *gpn_ft; 544dbf5dfe9SChristof Schmitt struct zfcp_fc_gpn_ft_req *req; 545cc8c2829SSwen Schillig 546cc8c2829SSwen Schillig gpn_ft = kzalloc(sizeof(*gpn_ft), GFP_KERNEL); 547cc8c2829SSwen Schillig if (!gpn_ft) 548cc8c2829SSwen Schillig return NULL; 549cc8c2829SSwen Schillig 5506e51f085SChristof Schmitt req = kmem_cache_zalloc(zfcp_data.gpn_ft_cache, GFP_KERNEL); 551cc8c2829SSwen Schillig if (!req) { 552cc8c2829SSwen Schillig kfree(gpn_ft); 553cc8c2829SSwen Schillig gpn_ft = NULL; 554cc8c2829SSwen Schillig goto out; 555cc8c2829SSwen Schillig } 556cc8c2829SSwen Schillig sg_init_one(&gpn_ft->sg_req, req, sizeof(*req)); 557cc8c2829SSwen Schillig 55839eb7e9aSChristof Schmitt if (zfcp_sg_setup_table(gpn_ft->sg_resp, buf_num)) { 55939eb7e9aSChristof Schmitt zfcp_free_sg_env(gpn_ft, buf_num); 560cc8c2829SSwen Schillig gpn_ft = NULL; 561cc8c2829SSwen Schillig } 562cc8c2829SSwen Schillig out: 563cc8c2829SSwen Schillig return gpn_ft; 564cc8c2829SSwen Schillig } 565cc8c2829SSwen Schillig 566cc8c2829SSwen Schillig 567dbf5dfe9SChristof Schmitt static int zfcp_fc_send_gpn_ft(struct zfcp_fc_gpn_ft *gpn_ft, 5686f53a2d2SSwen Schillig struct zfcp_adapter *adapter, int max_bytes) 569cc8c2829SSwen Schillig { 5707c7dc196SChristof Schmitt struct zfcp_fsf_ct_els *ct = &gpn_ft->ct; 571dbf5dfe9SChristof Schmitt struct zfcp_fc_gpn_ft_req *req = sg_virt(&gpn_ft->sg_req); 5727c7dc196SChristof Schmitt DECLARE_COMPLETION_ONSTACK(completion); 573cc8c2829SSwen Schillig int ret; 574cc8c2829SSwen Schillig 575cc8c2829SSwen Schillig /* prepare CT IU for GPN_FT */ 576dbf5dfe9SChristof Schmitt req->ct_hdr.ct_rev = FC_CT_REV; 577dbf5dfe9SChristof Schmitt req->ct_hdr.ct_fs_type = FC_FST_DIR; 578dbf5dfe9SChristof Schmitt req->ct_hdr.ct_fs_subtype = FC_NS_SUBTYPE; 579dbf5dfe9SChristof Schmitt req->ct_hdr.ct_options = 0; 580dbf5dfe9SChristof Schmitt req->ct_hdr.ct_cmd = FC_NS_GPN_FT; 581dbf5dfe9SChristof Schmitt req->ct_hdr.ct_mr_size = max_bytes / 4; 582dbf5dfe9SChristof Schmitt req->gpn_ft.fn_domain_id_scope = 0; 583dbf5dfe9SChristof Schmitt req->gpn_ft.fn_area_id_scope = 0; 584dbf5dfe9SChristof Schmitt req->gpn_ft.fn_fc4_type = FC_TYPE_FCP; 585cc8c2829SSwen Schillig 586cc8c2829SSwen Schillig /* prepare zfcp_send_ct */ 5877c7dc196SChristof Schmitt ct->handler = zfcp_fc_complete; 5887c7dc196SChristof Schmitt ct->handler_data = &completion; 589cc8c2829SSwen Schillig ct->req = &gpn_ft->sg_req; 590cc8c2829SSwen Schillig ct->resp = gpn_ft->sg_resp; 591cc8c2829SSwen Schillig 59251375ee8SSwen Schillig ret = zfcp_fsf_send_ct(&adapter->gs->ds, ct, NULL, 59351375ee8SSwen Schillig ZFCP_FC_CTELS_TMO); 594cc8c2829SSwen Schillig if (!ret) 5957c7dc196SChristof Schmitt wait_for_completion(&completion); 596cc8c2829SSwen Schillig return ret; 597cc8c2829SSwen Schillig } 598cc8c2829SSwen Schillig 599f3450c7bSSwen Schillig static void zfcp_fc_validate_port(struct zfcp_port *port, struct list_head *lh) 600cc8c2829SSwen Schillig { 6016ab35c07SMartin Petermann if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_NOESC)) 6026ab35c07SMartin Petermann return; 6036ab35c07SMartin Petermann 604cc8c2829SSwen Schillig atomic_clear_mask(ZFCP_STATUS_COMMON_NOESC, &port->status); 605cc8c2829SSwen Schillig 6060406289eSChristof Schmitt if ((port->supported_classes != 0) || 607f3450c7bSSwen Schillig !list_empty(&port->unit_list)) 608cc8c2829SSwen Schillig return; 609f3450c7bSSwen Schillig 610f3450c7bSSwen Schillig list_move_tail(&port->list, lh); 611cc8c2829SSwen Schillig } 612cc8c2829SSwen Schillig 6137c7dc196SChristof Schmitt static int zfcp_fc_eval_gpn_ft(struct zfcp_fc_gpn_ft *gpn_ft, 6147c7dc196SChristof Schmitt struct zfcp_adapter *adapter, int max_entries) 615cc8c2829SSwen Schillig { 6167c7dc196SChristof Schmitt struct zfcp_fsf_ct_els *ct = &gpn_ft->ct; 617cc8c2829SSwen Schillig struct scatterlist *sg = gpn_ft->sg_resp; 618dbf5dfe9SChristof Schmitt struct fc_ct_hdr *hdr = sg_virt(sg); 619dbf5dfe9SChristof Schmitt struct fc_gpn_ft_resp *acc = sg_virt(sg); 620cc8c2829SSwen Schillig struct zfcp_port *port, *tmp; 621ecf0c772SSwen Schillig unsigned long flags; 622f3450c7bSSwen Schillig LIST_HEAD(remove_lh); 623cc8c2829SSwen Schillig u32 d_id; 62447f7bba5SChristof Schmitt int ret = 0, x, last = 0; 625cc8c2829SSwen Schillig 626cc8c2829SSwen Schillig if (ct->status) 627cc8c2829SSwen Schillig return -EIO; 628cc8c2829SSwen Schillig 629dbf5dfe9SChristof Schmitt if (hdr->ct_cmd != FC_FS_ACC) { 630dbf5dfe9SChristof Schmitt if (hdr->ct_reason == FC_BA_RJT_UNABLE) 631cc8c2829SSwen Schillig return -EAGAIN; /* might be a temporary condition */ 632cc8c2829SSwen Schillig return -EIO; 633cc8c2829SSwen Schillig } 634cc8c2829SSwen Schillig 635dbf5dfe9SChristof Schmitt if (hdr->ct_mr_size) { 63639eb7e9aSChristof Schmitt dev_warn(&adapter->ccw_device->dev, 63739eb7e9aSChristof Schmitt "The name server reported %d words residual data\n", 638dbf5dfe9SChristof Schmitt hdr->ct_mr_size); 639cc8c2829SSwen Schillig return -E2BIG; 64039eb7e9aSChristof Schmitt } 641cc8c2829SSwen Schillig 642cc8c2829SSwen Schillig /* first entry is the header */ 64339eb7e9aSChristof Schmitt for (x = 1; x < max_entries && !last; x++) { 644dbf5dfe9SChristof Schmitt if (x % (ZFCP_FC_GPN_FT_ENT_PAGE + 1)) 645cc8c2829SSwen Schillig acc++; 646cc8c2829SSwen Schillig else 647cc8c2829SSwen Schillig acc = sg_virt(++sg); 648cc8c2829SSwen Schillig 649dbf5dfe9SChristof Schmitt last = acc->fp_flags & FC_NS_FID_LAST; 650dbf5dfe9SChristof Schmitt d_id = ntoh24(acc->fp_fid); 651cc8c2829SSwen Schillig 6525ab944f9SSwen Schillig /* don't attach ports with a well known address */ 653dbf5dfe9SChristof Schmitt if (d_id >= FC_FID_WELL_KNOWN_BASE) 6545ab944f9SSwen Schillig continue; 655cc8c2829SSwen Schillig /* skip the adapter's port and known remote ports */ 656dbf5dfe9SChristof Schmitt if (acc->fp_wwpn == fc_host_port_name(adapter->scsi_host)) 657cc8c2829SSwen Schillig continue; 658cc8c2829SSwen Schillig 659dbf5dfe9SChristof Schmitt port = zfcp_port_enqueue(adapter, acc->fp_wwpn, 660cc8c2829SSwen Schillig ZFCP_STATUS_COMMON_NOESC, d_id); 661ecf0c772SSwen Schillig if (!IS_ERR(port)) 6625ffd51a5SSwen Schillig zfcp_erp_port_reopen(port, 0, "fcegpf1", NULL); 663ecf0c772SSwen Schillig else if (PTR_ERR(port) != -EEXIST) 664ecf0c772SSwen Schillig ret = PTR_ERR(port); 665cc8c2829SSwen Schillig } 666cc8c2829SSwen Schillig 667cc8c2829SSwen Schillig zfcp_erp_wait(adapter); 668ecf0c772SSwen Schillig write_lock_irqsave(&adapter->port_list_lock, flags); 669ecf0c772SSwen Schillig list_for_each_entry_safe(port, tmp, &adapter->port_list, list) 670f3450c7bSSwen Schillig zfcp_fc_validate_port(port, &remove_lh); 671ecf0c772SSwen Schillig write_unlock_irqrestore(&adapter->port_list_lock, flags); 672f3450c7bSSwen Schillig 673f3450c7bSSwen Schillig list_for_each_entry_safe(port, tmp, &remove_lh, list) { 674f3450c7bSSwen Schillig zfcp_erp_port_shutdown(port, 0, "fcegpf2", NULL); 675615f59e0SChristof Schmitt zfcp_device_unregister(&port->dev, &zfcp_sysfs_port_attrs); 676f3450c7bSSwen Schillig } 677f3450c7bSSwen Schillig 678cc8c2829SSwen Schillig return ret; 679cc8c2829SSwen Schillig } 680cc8c2829SSwen Schillig 681cc8c2829SSwen Schillig /** 6826f53a2d2SSwen Schillig * zfcp_fc_scan_ports - scan remote ports and attach new ports 6839eae07efSSwen Schillig * @work: reference to scheduled work 684cc8c2829SSwen Schillig */ 6859eae07efSSwen Schillig void zfcp_fc_scan_ports(struct work_struct *work) 686cc8c2829SSwen Schillig { 6879eae07efSSwen Schillig struct zfcp_adapter *adapter = container_of(work, struct zfcp_adapter, 6889eae07efSSwen Schillig scan_work); 689cc8c2829SSwen Schillig int ret, i; 690dbf5dfe9SChristof Schmitt struct zfcp_fc_gpn_ft *gpn_ft; 69139eb7e9aSChristof Schmitt int chain, max_entries, buf_num, max_bytes; 69239eb7e9aSChristof Schmitt 69339eb7e9aSChristof Schmitt chain = adapter->adapter_features & FSF_FEATURE_ELS_CT_CHAINED_SBALS; 694dbf5dfe9SChristof Schmitt buf_num = chain ? ZFCP_FC_GPN_FT_NUM_BUFS : 1; 695dbf5dfe9SChristof Schmitt max_entries = chain ? ZFCP_FC_GPN_FT_MAX_ENT : ZFCP_FC_GPN_FT_ENT_PAGE; 696dbf5dfe9SChristof Schmitt max_bytes = chain ? ZFCP_FC_GPN_FT_MAX_SIZE : ZFCP_FC_CT_SIZE_PAGE; 697cc8c2829SSwen Schillig 698306b6edcSSwen Schillig if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT && 699306b6edcSSwen Schillig fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPIV) 7009eae07efSSwen Schillig return; 701cc8c2829SSwen Schillig 7029eae07efSSwen Schillig if (zfcp_fc_wka_port_get(&adapter->gs->ds)) 7039eae07efSSwen Schillig return; 704cc8c2829SSwen Schillig 70539eb7e9aSChristof Schmitt gpn_ft = zfcp_alloc_sg_env(buf_num); 7069eae07efSSwen Schillig if (!gpn_ft) 7075ab944f9SSwen Schillig goto out; 708cc8c2829SSwen Schillig 709cc8c2829SSwen Schillig for (i = 0; i < 3; i++) { 7106f53a2d2SSwen Schillig ret = zfcp_fc_send_gpn_ft(gpn_ft, adapter, max_bytes); 711cc8c2829SSwen Schillig if (!ret) { 7127c7dc196SChristof Schmitt ret = zfcp_fc_eval_gpn_ft(gpn_ft, adapter, max_entries); 713cc8c2829SSwen Schillig if (ret == -EAGAIN) 714cc8c2829SSwen Schillig ssleep(1); 715cc8c2829SSwen Schillig else 716cc8c2829SSwen Schillig break; 717cc8c2829SSwen Schillig } 718cc8c2829SSwen Schillig } 71939eb7e9aSChristof Schmitt zfcp_free_sg_env(gpn_ft, buf_num); 7205ab944f9SSwen Schillig out: 7216f53a2d2SSwen Schillig zfcp_fc_wka_port_put(&adapter->gs->ds); 722cc8c2829SSwen Schillig } 723cc8c2829SSwen Schillig 7247c7dc196SChristof Schmitt static void zfcp_fc_ct_els_job_handler(void *data) 7259d544f2bSSven Schuetz { 7267c7dc196SChristof Schmitt struct fc_bsg_job *job = data; 7277c7dc196SChristof Schmitt struct zfcp_fsf_ct_els *zfcp_ct_els = job->dd_data; 7287dec9cf1SSwen Schillig struct fc_bsg_reply *jr = job->reply; 7299d544f2bSSven Schuetz 7307dec9cf1SSwen Schillig jr->reply_payload_rcv_len = job->reply_payload.payload_len; 7317dec9cf1SSwen Schillig jr->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK; 7327dec9cf1SSwen Schillig jr->result = zfcp_ct_els->status ? -EIO : 0; 7339d544f2bSSven Schuetz job->job_done(job); 7349d544f2bSSven Schuetz } 7359d544f2bSSven Schuetz 736f09d5454SChristof Schmitt static struct zfcp_fc_wka_port *zfcp_fc_job_wka_port(struct fc_bsg_job *job) 737f09d5454SChristof Schmitt { 738f09d5454SChristof Schmitt u32 preamble_word1; 739f09d5454SChristof Schmitt u8 gs_type; 740f09d5454SChristof Schmitt struct zfcp_adapter *adapter; 741f09d5454SChristof Schmitt 742f09d5454SChristof Schmitt preamble_word1 = job->request->rqst_data.r_ct.preamble_word1; 743f09d5454SChristof Schmitt gs_type = (preamble_word1 & 0xff000000) >> 24; 744f09d5454SChristof Schmitt 745f09d5454SChristof Schmitt adapter = (struct zfcp_adapter *) job->shost->hostdata[0]; 746f09d5454SChristof Schmitt 747f09d5454SChristof Schmitt switch (gs_type) { 748f09d5454SChristof Schmitt case FC_FST_ALIAS: 749f09d5454SChristof Schmitt return &adapter->gs->as; 750f09d5454SChristof Schmitt case FC_FST_MGMT: 751f09d5454SChristof Schmitt return &adapter->gs->ms; 752f09d5454SChristof Schmitt case FC_FST_TIME: 753f09d5454SChristof Schmitt return &adapter->gs->ts; 754f09d5454SChristof Schmitt break; 755f09d5454SChristof Schmitt case FC_FST_DIR: 756f09d5454SChristof Schmitt return &adapter->gs->ds; 757f09d5454SChristof Schmitt break; 758f09d5454SChristof Schmitt default: 759f09d5454SChristof Schmitt return NULL; 760f09d5454SChristof Schmitt } 761f09d5454SChristof Schmitt } 762f09d5454SChristof Schmitt 763f09d5454SChristof Schmitt static void zfcp_fc_ct_job_handler(void *data) 764f09d5454SChristof Schmitt { 765f09d5454SChristof Schmitt struct fc_bsg_job *job = data; 766f09d5454SChristof Schmitt struct zfcp_fc_wka_port *wka_port; 767f09d5454SChristof Schmitt 768f09d5454SChristof Schmitt wka_port = zfcp_fc_job_wka_port(job); 769f09d5454SChristof Schmitt zfcp_fc_wka_port_put(wka_port); 770f09d5454SChristof Schmitt 771f09d5454SChristof Schmitt zfcp_fc_ct_els_job_handler(data); 772f09d5454SChristof Schmitt } 773f09d5454SChristof Schmitt 7747c7dc196SChristof Schmitt static int zfcp_fc_exec_els_job(struct fc_bsg_job *job, 7757c7dc196SChristof Schmitt struct zfcp_adapter *adapter) 7769d544f2bSSven Schuetz { 7777c7dc196SChristof Schmitt struct zfcp_fsf_ct_els *els = job->dd_data; 7789d544f2bSSven Schuetz struct fc_rport *rport = job->rport; 7799d544f2bSSven Schuetz struct zfcp_port *port; 7807c7dc196SChristof Schmitt u32 d_id; 7819d544f2bSSven Schuetz 7829d544f2bSSven Schuetz if (rport) { 783ea945ff8SSwen Schillig port = zfcp_get_port_by_wwpn(adapter, rport->port_name); 7847c7dc196SChristof Schmitt if (!port) 7859d544f2bSSven Schuetz return -EINVAL; 786ecf0c772SSwen Schillig 7877c7dc196SChristof Schmitt d_id = port->d_id; 788615f59e0SChristof Schmitt put_device(&port->dev); 7897c7dc196SChristof Schmitt } else 7907c7dc196SChristof Schmitt d_id = ntoh24(job->request->rqst_data.h_els.port_id); 7917c7dc196SChristof Schmitt 792f09d5454SChristof Schmitt els->handler = zfcp_fc_ct_els_job_handler; 79351375ee8SSwen Schillig return zfcp_fsf_send_els(adapter, d_id, els, job->req->timeout / HZ); 7949d544f2bSSven Schuetz } 7959d544f2bSSven Schuetz 7967c7dc196SChristof Schmitt static int zfcp_fc_exec_ct_job(struct fc_bsg_job *job, 7977c7dc196SChristof Schmitt struct zfcp_adapter *adapter) 7989d544f2bSSven Schuetz { 7999d544f2bSSven Schuetz int ret; 8007c7dc196SChristof Schmitt struct zfcp_fsf_ct_els *ct = job->dd_data; 8017c7dc196SChristof Schmitt struct zfcp_fc_wka_port *wka_port; 8029d544f2bSSven Schuetz 803f09d5454SChristof Schmitt wka_port = zfcp_fc_job_wka_port(job); 804f09d5454SChristof Schmitt if (!wka_port) 805f09d5454SChristof Schmitt return -EINVAL; 8069d544f2bSSven Schuetz 8077c7dc196SChristof Schmitt ret = zfcp_fc_wka_port_get(wka_port); 8087c7dc196SChristof Schmitt if (ret) 8097c7dc196SChristof Schmitt return ret; 8107c7dc196SChristof Schmitt 811f09d5454SChristof Schmitt ct->handler = zfcp_fc_ct_job_handler; 81251375ee8SSwen Schillig ret = zfcp_fsf_send_ct(wka_port, ct, NULL, job->req->timeout / HZ); 8137c7dc196SChristof Schmitt if (ret) 8147c7dc196SChristof Schmitt zfcp_fc_wka_port_put(wka_port); 8157c7dc196SChristof Schmitt 8169d544f2bSSven Schuetz return ret; 8179d544f2bSSven Schuetz } 8189d544f2bSSven Schuetz 8197c7dc196SChristof Schmitt int zfcp_fc_exec_bsg_job(struct fc_bsg_job *job) 8207c7dc196SChristof Schmitt { 8217c7dc196SChristof Schmitt struct Scsi_Host *shost; 8227c7dc196SChristof Schmitt struct zfcp_adapter *adapter; 8237c7dc196SChristof Schmitt struct zfcp_fsf_ct_els *ct_els = job->dd_data; 8249d544f2bSSven Schuetz 8257c7dc196SChristof Schmitt shost = job->rport ? rport_to_shost(job->rport) : job->shost; 8267c7dc196SChristof Schmitt adapter = (struct zfcp_adapter *)shost->hostdata[0]; 8277c7dc196SChristof Schmitt 8287c7dc196SChristof Schmitt if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_OPEN)) 8297c7dc196SChristof Schmitt return -EINVAL; 8307c7dc196SChristof Schmitt 8317c7dc196SChristof Schmitt ct_els->req = job->request_payload.sg_list; 8327c7dc196SChristof Schmitt ct_els->resp = job->reply_payload.sg_list; 8337c7dc196SChristof Schmitt ct_els->handler_data = job; 8347c7dc196SChristof Schmitt 8357c7dc196SChristof Schmitt switch (job->request->msgcode) { 8367c7dc196SChristof Schmitt case FC_BSG_RPT_ELS: 8377c7dc196SChristof Schmitt case FC_BSG_HST_ELS_NOLOGIN: 8387c7dc196SChristof Schmitt return zfcp_fc_exec_els_job(job, adapter); 8397c7dc196SChristof Schmitt case FC_BSG_RPT_CT: 8407c7dc196SChristof Schmitt case FC_BSG_HST_CT: 8417c7dc196SChristof Schmitt return zfcp_fc_exec_ct_job(job, adapter); 8427c7dc196SChristof Schmitt default: 8437c7dc196SChristof Schmitt return -EINVAL; 8449d544f2bSSven Schuetz } 8459d544f2bSSven Schuetz } 846d5a282a1SSwen Schillig 847491ca442SSwen Schillig int zfcp_fc_timeout_bsg_job(struct fc_bsg_job *job) 848491ca442SSwen Schillig { 849491ca442SSwen Schillig /* hardware tracks timeout, reset bsg timeout to not interfere */ 850491ca442SSwen Schillig return -EAGAIN; 851491ca442SSwen Schillig } 852491ca442SSwen Schillig 853d5a282a1SSwen Schillig int zfcp_fc_gs_setup(struct zfcp_adapter *adapter) 854d5a282a1SSwen Schillig { 855bd0072ecSChristof Schmitt struct zfcp_fc_wka_ports *wka_ports; 856d5a282a1SSwen Schillig 857bd0072ecSChristof Schmitt wka_ports = kzalloc(sizeof(struct zfcp_fc_wka_ports), GFP_KERNEL); 858d5a282a1SSwen Schillig if (!wka_ports) 859d5a282a1SSwen Schillig return -ENOMEM; 860d5a282a1SSwen Schillig 861d5a282a1SSwen Schillig adapter->gs = wka_ports; 862d5a282a1SSwen Schillig zfcp_fc_wka_port_init(&wka_ports->ms, FC_FID_MGMT_SERV, adapter); 863d5a282a1SSwen Schillig zfcp_fc_wka_port_init(&wka_ports->ts, FC_FID_TIME_SERV, adapter); 864d5a282a1SSwen Schillig zfcp_fc_wka_port_init(&wka_ports->ds, FC_FID_DIR_SERV, adapter); 865d5a282a1SSwen Schillig zfcp_fc_wka_port_init(&wka_ports->as, FC_FID_ALIASES, adapter); 866d5a282a1SSwen Schillig 867d5a282a1SSwen Schillig return 0; 868d5a282a1SSwen Schillig } 869d5a282a1SSwen Schillig 870d5a282a1SSwen Schillig void zfcp_fc_gs_destroy(struct zfcp_adapter *adapter) 871d5a282a1SSwen Schillig { 872d5a282a1SSwen Schillig kfree(adapter->gs); 873d5a282a1SSwen Schillig adapter->gs = NULL; 874d5a282a1SSwen Schillig } 875d5a282a1SSwen Schillig 876