1 /* 2 * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms and conditions of the GNU General Public License, 6 * version 2, as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope it will be useful, but WITHOUT 9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 * more details. 12 * 13 * You should have received a copy of the GNU General Public License along with 14 * this program; if not, write to the Free Software Foundation, Inc., 15 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 16 * 17 * Maintained at www.Open-FCoE.org 18 */ 19 20 /* 21 * Target Discovery 22 * 23 * This block discovers all FC-4 remote ports, including FCP initiators. It 24 * also handles RSCN events and re-discovery if necessary. 25 */ 26 27 /* 28 * DISC LOCKING 29 * 30 * The disc mutex is can be locked when acquiring rport locks, but may not 31 * be held when acquiring the lport lock. Refer to fc_lport.c for more 32 * details. 33 */ 34 35 #include <linux/timer.h> 36 #include <linux/err.h> 37 #include <asm/unaligned.h> 38 39 #include <scsi/fc/fc_gs.h> 40 41 #include <scsi/libfc.h> 42 43 #define FC_DISC_RETRY_LIMIT 3 /* max retries */ 44 #define FC_DISC_RETRY_DELAY 500UL /* (msecs) delay */ 45 46 static void fc_disc_gpn_ft_req(struct fc_disc *); 47 static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *); 48 static int fc_disc_new_target(struct fc_disc *, struct fc_rport_priv *, 49 struct fc_rport_identifiers *); 50 static void fc_disc_done(struct fc_disc *, enum fc_disc_event); 51 static void fc_disc_timeout(struct work_struct *); 52 static void fc_disc_single(struct fc_disc *, struct fc_disc_port *); 53 static void fc_disc_restart(struct fc_disc *); 54 55 /** 56 * fc_disc_stop_rports() - delete all the remote ports associated with the lport 57 * @disc: The discovery job to stop rports on 58 * 59 * Locking Note: This function expects that the lport mutex is locked before 60 * calling it. 61 */ 62 void fc_disc_stop_rports(struct fc_disc *disc) 63 { 64 struct fc_lport *lport; 65 struct fc_rport_priv *rdata, *next; 66 67 lport = disc->lport; 68 69 mutex_lock(&disc->disc_mutex); 70 list_for_each_entry_safe(rdata, next, &disc->rports, peers) 71 lport->tt.rport_logoff(rdata); 72 mutex_unlock(&disc->disc_mutex); 73 } 74 75 /** 76 * fc_disc_recv_rscn_req() - Handle Registered State Change Notification (RSCN) 77 * @sp: Current sequence of the RSCN exchange 78 * @fp: RSCN Frame 79 * @lport: Fibre Channel host port instance 80 * 81 * Locking Note: This function expects that the disc_mutex is locked 82 * before it is called. 83 */ 84 static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp, 85 struct fc_disc *disc) 86 { 87 struct fc_lport *lport; 88 struct fc_rport_priv *rdata; 89 struct fc_els_rscn *rp; 90 struct fc_els_rscn_page *pp; 91 struct fc_seq_els_data rjt_data; 92 unsigned int len; 93 int redisc = 0; 94 enum fc_els_rscn_ev_qual ev_qual; 95 enum fc_els_rscn_addr_fmt fmt; 96 LIST_HEAD(disc_ports); 97 struct fc_disc_port *dp, *next; 98 99 lport = disc->lport; 100 101 FC_DISC_DBG(disc, "Received an RSCN event\n"); 102 103 /* make sure the frame contains an RSCN message */ 104 rp = fc_frame_payload_get(fp, sizeof(*rp)); 105 if (!rp) 106 goto reject; 107 /* make sure the page length is as expected (4 bytes) */ 108 if (rp->rscn_page_len != sizeof(*pp)) 109 goto reject; 110 /* get the RSCN payload length */ 111 len = ntohs(rp->rscn_plen); 112 if (len < sizeof(*rp)) 113 goto reject; 114 /* make sure the frame contains the expected payload */ 115 rp = fc_frame_payload_get(fp, len); 116 if (!rp) 117 goto reject; 118 /* payload must be a multiple of the RSCN page size */ 119 len -= sizeof(*rp); 120 if (len % sizeof(*pp)) 121 goto reject; 122 123 for (pp = (void *)(rp + 1); len > 0; len -= sizeof(*pp), pp++) { 124 ev_qual = pp->rscn_page_flags >> ELS_RSCN_EV_QUAL_BIT; 125 ev_qual &= ELS_RSCN_EV_QUAL_MASK; 126 fmt = pp->rscn_page_flags >> ELS_RSCN_ADDR_FMT_BIT; 127 fmt &= ELS_RSCN_ADDR_FMT_MASK; 128 /* 129 * if we get an address format other than port 130 * (area, domain, fabric), then do a full discovery 131 */ 132 switch (fmt) { 133 case ELS_ADDR_FMT_PORT: 134 FC_DISC_DBG(disc, "Port address format for port " 135 "(%6x)\n", ntoh24(pp->rscn_fid)); 136 dp = kzalloc(sizeof(*dp), GFP_KERNEL); 137 if (!dp) { 138 redisc = 1; 139 break; 140 } 141 dp->lp = lport; 142 dp->ids.port_id = ntoh24(pp->rscn_fid); 143 dp->ids.port_name = -1; 144 dp->ids.node_name = -1; 145 dp->ids.roles = FC_RPORT_ROLE_UNKNOWN; 146 list_add_tail(&dp->peers, &disc_ports); 147 break; 148 case ELS_ADDR_FMT_AREA: 149 case ELS_ADDR_FMT_DOM: 150 case ELS_ADDR_FMT_FAB: 151 default: 152 FC_DISC_DBG(disc, "Address format is (%d)\n", fmt); 153 redisc = 1; 154 break; 155 } 156 } 157 lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); 158 if (redisc) { 159 FC_DISC_DBG(disc, "RSCN received: rediscovering\n"); 160 fc_disc_restart(disc); 161 } else { 162 FC_DISC_DBG(disc, "RSCN received: not rediscovering. " 163 "redisc %d state %d in_prog %d\n", 164 redisc, lport->state, disc->pending); 165 list_for_each_entry_safe(dp, next, &disc_ports, peers) { 166 list_del(&dp->peers); 167 rdata = lport->tt.rport_lookup(lport, dp->ids.port_id); 168 if (rdata) { 169 lport->tt.rport_logoff(rdata); 170 } 171 fc_disc_single(disc, dp); 172 } 173 } 174 fc_frame_free(fp); 175 return; 176 reject: 177 FC_DISC_DBG(disc, "Received a bad RSCN frame\n"); 178 rjt_data.fp = NULL; 179 rjt_data.reason = ELS_RJT_LOGIC; 180 rjt_data.explan = ELS_EXPL_NONE; 181 lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); 182 fc_frame_free(fp); 183 } 184 185 /** 186 * fc_disc_recv_req() - Handle incoming requests 187 * @sp: Current sequence of the request exchange 188 * @fp: The frame 189 * @lport: The FC local port 190 * 191 * Locking Note: This function is called from the EM and will lock 192 * the disc_mutex before calling the handler for the 193 * request. 194 */ 195 static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp, 196 struct fc_lport *lport) 197 { 198 u8 op; 199 struct fc_disc *disc = &lport->disc; 200 201 op = fc_frame_payload_op(fp); 202 switch (op) { 203 case ELS_RSCN: 204 mutex_lock(&disc->disc_mutex); 205 fc_disc_recv_rscn_req(sp, fp, disc); 206 mutex_unlock(&disc->disc_mutex); 207 break; 208 default: 209 FC_DISC_DBG(disc, "Received an unsupported request, " 210 "the opcode is (%x)\n", op); 211 break; 212 } 213 } 214 215 /** 216 * fc_disc_restart() - Restart discovery 217 * @lport: FC discovery context 218 * 219 * Locking Note: This function expects that the disc mutex 220 * is already locked. 221 */ 222 static void fc_disc_restart(struct fc_disc *disc) 223 { 224 FC_DISC_DBG(disc, "Restarting discovery\n"); 225 226 disc->requested = 1; 227 if (disc->pending) 228 return; 229 230 /* 231 * Advance disc_id. This is an arbitrary non-zero number that will 232 * match the value in the fc_rport_priv after discovery for all 233 * freshly-discovered remote ports. Avoid wrapping to zero. 234 */ 235 disc->disc_id = (disc->disc_id + 2) | 1; 236 disc->retry_count = 0; 237 fc_disc_gpn_ft_req(disc); 238 } 239 240 /** 241 * fc_disc_start() - Fibre Channel Target discovery 242 * @lport: FC local port 243 * 244 * Returns non-zero if discovery cannot be started. 245 */ 246 static void fc_disc_start(void (*disc_callback)(struct fc_lport *, 247 enum fc_disc_event), 248 struct fc_lport *lport) 249 { 250 struct fc_rport_priv *rdata; 251 struct fc_disc *disc = &lport->disc; 252 253 /* 254 * At this point we may have a new disc job or an existing 255 * one. Either way, let's lock when we make changes to it 256 * and send the GPN_FT request. 257 */ 258 mutex_lock(&disc->disc_mutex); 259 260 disc->disc_callback = disc_callback; 261 262 /* 263 * If not ready, or already running discovery, just set request flag. 264 */ 265 disc->requested = 1; 266 267 if (disc->pending) { 268 mutex_unlock(&disc->disc_mutex); 269 return; 270 } 271 272 /* 273 * Handle point-to-point mode as a simple discovery 274 * of the remote port. Yucky, yucky, yuck, yuck! 275 */ 276 rdata = disc->lport->ptp_rp; 277 if (rdata) { 278 kref_get(&rdata->kref); 279 if (!fc_disc_new_target(disc, rdata, &rdata->ids)) { 280 fc_disc_done(disc, DISC_EV_SUCCESS); 281 } 282 kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy); 283 } else { 284 disc->disc_id = (disc->disc_id + 2) | 1; 285 fc_disc_gpn_ft_req(disc); /* get ports by FC-4 type */ 286 } 287 288 mutex_unlock(&disc->disc_mutex); 289 } 290 291 /** 292 * fc_disc_new_target() - Handle new target found by discovery 293 * @lport: FC local port 294 * @rdata: The previous FC remote port priv (NULL if new remote port) 295 * @ids: Identifiers for the new FC remote port 296 * 297 * Locking Note: This function expects that the disc_mutex is locked 298 * before it is called. 299 */ 300 static int fc_disc_new_target(struct fc_disc *disc, 301 struct fc_rport_priv *rdata, 302 struct fc_rport_identifiers *ids) 303 { 304 struct fc_lport *lport = disc->lport; 305 int error = 0; 306 307 if (rdata && ids->port_name) { 308 if (rdata->ids.port_name == -1) { 309 /* 310 * Set WWN and fall through to notify of create. 311 */ 312 rdata->ids.port_name = ids->port_name; 313 rdata->ids.node_name = ids->node_name; 314 } else if (rdata->ids.port_name != ids->port_name) { 315 /* 316 * This is a new port with the same FCID as 317 * a previously-discovered port. Presumably the old 318 * port logged out and a new port logged in and was 319 * assigned the same FCID. This should be rare. 320 * Delete the old one and fall thru to re-create. 321 */ 322 lport->tt.rport_logoff(rdata); 323 rdata = NULL; 324 } 325 } 326 if (((ids->port_name != -1) || (ids->port_id != -1)) && 327 ids->port_id != fc_host_port_id(lport->host) && 328 ids->port_name != lport->wwpn) { 329 if (!rdata) { 330 rdata = lport->tt.rport_create(lport, ids); 331 if (!rdata) 332 error = -ENOMEM; 333 } 334 if (rdata) 335 lport->tt.rport_login(rdata); 336 } 337 return error; 338 } 339 340 /** 341 * fc_disc_done() - Discovery has been completed 342 * @disc: FC discovery context 343 * @event: discovery completion status 344 * 345 * Locking Note: This function expects that the disc mutex is locked before 346 * it is called. The discovery callback is then made with the lock released, 347 * and the lock is re-taken before returning from this function 348 */ 349 static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event) 350 { 351 struct fc_lport *lport = disc->lport; 352 struct fc_rport_priv *rdata; 353 354 FC_DISC_DBG(disc, "Discovery complete\n"); 355 356 disc->pending = 0; 357 if (disc->requested) { 358 fc_disc_restart(disc); 359 return; 360 } 361 362 /* 363 * Go through all remote ports. If they were found in the latest 364 * discovery, reverify or log them in. Otherwise, log them out. 365 * Skip ports which were never discovered. These are the dNS port 366 * and ports which were created by PLOGI. 367 */ 368 list_for_each_entry(rdata, &disc->rports, peers) { 369 if (!rdata->disc_id) 370 continue; 371 if (rdata->disc_id == disc->disc_id) 372 lport->tt.rport_login(rdata); 373 else 374 lport->tt.rport_logoff(rdata); 375 } 376 377 mutex_unlock(&disc->disc_mutex); 378 disc->disc_callback(lport, event); 379 mutex_lock(&disc->disc_mutex); 380 } 381 382 /** 383 * fc_disc_error() - Handle error on dNS request 384 * @disc: FC discovery context 385 * @fp: The frame pointer 386 */ 387 static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp) 388 { 389 struct fc_lport *lport = disc->lport; 390 unsigned long delay = 0; 391 392 FC_DISC_DBG(disc, "Error %ld, retries %d/%d\n", 393 PTR_ERR(fp), disc->retry_count, 394 FC_DISC_RETRY_LIMIT); 395 396 if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) { 397 /* 398 * Memory allocation failure, or the exchange timed out, 399 * retry after delay. 400 */ 401 if (disc->retry_count < FC_DISC_RETRY_LIMIT) { 402 /* go ahead and retry */ 403 if (!fp) 404 delay = msecs_to_jiffies(FC_DISC_RETRY_DELAY); 405 else { 406 delay = msecs_to_jiffies(lport->e_d_tov); 407 408 /* timeout faster first time */ 409 if (!disc->retry_count) 410 delay /= 4; 411 } 412 disc->retry_count++; 413 schedule_delayed_work(&disc->disc_work, delay); 414 } else 415 fc_disc_done(disc, DISC_EV_FAILED); 416 } 417 } 418 419 /** 420 * fc_disc_gpn_ft_req() - Send Get Port Names by FC-4 type (GPN_FT) request 421 * @lport: FC discovery context 422 * 423 * Locking Note: This function expects that the disc_mutex is locked 424 * before it is called. 425 */ 426 static void fc_disc_gpn_ft_req(struct fc_disc *disc) 427 { 428 struct fc_frame *fp; 429 struct fc_lport *lport = disc->lport; 430 431 WARN_ON(!fc_lport_test_ready(lport)); 432 433 disc->pending = 1; 434 disc->requested = 0; 435 436 disc->buf_len = 0; 437 disc->seq_count = 0; 438 fp = fc_frame_alloc(lport, 439 sizeof(struct fc_ct_hdr) + 440 sizeof(struct fc_ns_gid_ft)); 441 if (!fp) 442 goto err; 443 444 if (lport->tt.elsct_send(lport, 0, fp, 445 FC_NS_GPN_FT, 446 fc_disc_gpn_ft_resp, 447 disc, lport->e_d_tov)) 448 return; 449 err: 450 fc_disc_error(disc, fp); 451 } 452 453 /** 454 * fc_disc_gpn_ft_parse() - Parse the body of the dNS GPN_FT response. 455 * @lport: Fibre Channel host port instance 456 * @buf: GPN_FT response buffer 457 * @len: size of response buffer 458 * 459 * Goes through the list of IDs and names resulting from a request. 460 */ 461 static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len) 462 { 463 struct fc_lport *lport; 464 struct fc_gpn_ft_resp *np; 465 char *bp; 466 size_t plen; 467 size_t tlen; 468 int error = 0; 469 struct fc_rport_identifiers ids; 470 struct fc_rport_priv *rdata; 471 472 lport = disc->lport; 473 disc->seq_count++; 474 475 /* 476 * Handle partial name record left over from previous call. 477 */ 478 bp = buf; 479 plen = len; 480 np = (struct fc_gpn_ft_resp *)bp; 481 tlen = disc->buf_len; 482 if (tlen) { 483 WARN_ON(tlen >= sizeof(*np)); 484 plen = sizeof(*np) - tlen; 485 WARN_ON(plen <= 0); 486 WARN_ON(plen >= sizeof(*np)); 487 if (plen > len) 488 plen = len; 489 np = &disc->partial_buf; 490 memcpy((char *)np + tlen, bp, plen); 491 492 /* 493 * Set bp so that the loop below will advance it to the 494 * first valid full name element. 495 */ 496 bp -= tlen; 497 len += tlen; 498 plen += tlen; 499 disc->buf_len = (unsigned char) plen; 500 if (plen == sizeof(*np)) 501 disc->buf_len = 0; 502 } 503 504 /* 505 * Handle full name records, including the one filled from above. 506 * Normally, np == bp and plen == len, but from the partial case above, 507 * bp, len describe the overall buffer, and np, plen describe the 508 * partial buffer, which if would usually be full now. 509 * After the first time through the loop, things return to "normal". 510 */ 511 while (plen >= sizeof(*np)) { 512 ids.port_id = ntoh24(np->fp_fid); 513 ids.port_name = ntohll(np->fp_wwpn); 514 ids.node_name = -1; 515 ids.roles = FC_RPORT_ROLE_UNKNOWN; 516 517 if (ids.port_id != fc_host_port_id(lport->host) && 518 ids.port_name != lport->wwpn) { 519 rdata = lport->tt.rport_create(lport, &ids); 520 if (rdata) 521 rdata->disc_id = disc->disc_id; 522 else 523 printk(KERN_WARNING "libfc: Failed to allocate " 524 "memory for the newly discovered port " 525 "(%6x)\n", ids.port_id); 526 } 527 528 if (np->fp_flags & FC_NS_FID_LAST) { 529 fc_disc_done(disc, DISC_EV_SUCCESS); 530 len = 0; 531 break; 532 } 533 len -= sizeof(*np); 534 bp += sizeof(*np); 535 np = (struct fc_gpn_ft_resp *)bp; 536 plen = len; 537 } 538 539 /* 540 * Save any partial record at the end of the buffer for next time. 541 */ 542 if (error == 0 && len > 0 && len < sizeof(*np)) { 543 if (np != &disc->partial_buf) { 544 FC_DISC_DBG(disc, "Partial buffer remains " 545 "for discovery\n"); 546 memcpy(&disc->partial_buf, np, len); 547 } 548 disc->buf_len = (unsigned char) len; 549 } else { 550 disc->buf_len = 0; 551 } 552 return error; 553 } 554 555 /** 556 * fc_disc_timeout() - Retry handler for the disc component 557 * @work: Structure holding disc obj that needs retry discovery 558 * 559 * Handle retry of memory allocation for remote ports. 560 */ 561 static void fc_disc_timeout(struct work_struct *work) 562 { 563 struct fc_disc *disc = container_of(work, 564 struct fc_disc, 565 disc_work.work); 566 mutex_lock(&disc->disc_mutex); 567 fc_disc_gpn_ft_req(disc); 568 mutex_unlock(&disc->disc_mutex); 569 } 570 571 /** 572 * fc_disc_gpn_ft_resp() - Handle a response frame from Get Port Names (GPN_FT) 573 * @sp: Current sequence of GPN_FT exchange 574 * @fp: response frame 575 * @lp_arg: Fibre Channel host port instance 576 * 577 * Locking Note: This function is called without disc mutex held, and 578 * should do all its processing with the mutex held 579 */ 580 static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp, 581 void *disc_arg) 582 { 583 struct fc_disc *disc = disc_arg; 584 struct fc_ct_hdr *cp; 585 struct fc_frame_header *fh; 586 enum fc_disc_event event = DISC_EV_NONE; 587 unsigned int seq_cnt; 588 unsigned int len; 589 int error = 0; 590 591 mutex_lock(&disc->disc_mutex); 592 FC_DISC_DBG(disc, "Received a GPN_FT response\n"); 593 594 if (IS_ERR(fp)) { 595 fc_disc_error(disc, fp); 596 mutex_unlock(&disc->disc_mutex); 597 return; 598 } 599 600 WARN_ON(!fc_frame_is_linear(fp)); /* buffer must be contiguous */ 601 fh = fc_frame_header_get(fp); 602 len = fr_len(fp) - sizeof(*fh); 603 seq_cnt = ntohs(fh->fh_seq_cnt); 604 if (fr_sof(fp) == FC_SOF_I3 && seq_cnt == 0 && disc->seq_count == 0) { 605 cp = fc_frame_payload_get(fp, sizeof(*cp)); 606 if (!cp) { 607 FC_DISC_DBG(disc, "GPN_FT response too short, len %d\n", 608 fr_len(fp)); 609 event = DISC_EV_FAILED; 610 } else if (ntohs(cp->ct_cmd) == FC_FS_ACC) { 611 612 /* Accepted, parse the response. */ 613 len -= sizeof(*cp); 614 error = fc_disc_gpn_ft_parse(disc, cp + 1, len); 615 } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) { 616 FC_DISC_DBG(disc, "GPN_FT rejected reason %x exp %x " 617 "(check zoning)\n", cp->ct_reason, 618 cp->ct_explan); 619 event = DISC_EV_FAILED; 620 if (cp->ct_reason == FC_FS_RJT_UNABL && 621 cp->ct_explan == FC_FS_EXP_FTNR) 622 event = DISC_EV_SUCCESS; 623 } else { 624 FC_DISC_DBG(disc, "GPN_FT unexpected response code " 625 "%x\n", ntohs(cp->ct_cmd)); 626 event = DISC_EV_FAILED; 627 } 628 } else if (fr_sof(fp) == FC_SOF_N3 && seq_cnt == disc->seq_count) { 629 error = fc_disc_gpn_ft_parse(disc, fh + 1, len); 630 } else { 631 FC_DISC_DBG(disc, "GPN_FT unexpected frame - out of sequence? " 632 "seq_cnt %x expected %x sof %x eof %x\n", 633 seq_cnt, disc->seq_count, fr_sof(fp), fr_eof(fp)); 634 event = DISC_EV_FAILED; 635 } 636 if (error) 637 fc_disc_error(disc, fp); 638 else if (event != DISC_EV_NONE) 639 fc_disc_done(disc, event); 640 fc_frame_free(fp); 641 mutex_unlock(&disc->disc_mutex); 642 } 643 644 /** 645 * fc_disc_single() - Discover the directory information for a single target 646 * @lport: FC local port 647 * @dp: The port to rediscover 648 * 649 * Locking Note: This function expects that the disc_mutex is locked 650 * before it is called. 651 */ 652 static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp) 653 { 654 struct fc_lport *lport; 655 struct fc_rport_priv *rdata; 656 657 lport = disc->lport; 658 659 if (dp->ids.port_id == fc_host_port_id(lport->host)) 660 goto out; 661 662 rdata = lport->tt.rport_create(lport, &dp->ids); 663 if (rdata) { 664 rdata->disc_id = disc->disc_id; 665 kfree(dp); 666 lport->tt.rport_login(rdata); 667 } 668 return; 669 out: 670 kfree(dp); 671 } 672 673 /** 674 * fc_disc_stop() - Stop discovery for a given lport 675 * @lport: The lport that discovery should stop for 676 */ 677 void fc_disc_stop(struct fc_lport *lport) 678 { 679 struct fc_disc *disc = &lport->disc; 680 681 if (disc) { 682 cancel_delayed_work_sync(&disc->disc_work); 683 fc_disc_stop_rports(disc); 684 } 685 } 686 687 /** 688 * fc_disc_stop_final() - Stop discovery for a given lport 689 * @lport: The lport that discovery should stop for 690 * 691 * This function will block until discovery has been 692 * completely stopped and all rports have been deleted. 693 */ 694 void fc_disc_stop_final(struct fc_lport *lport) 695 { 696 fc_disc_stop(lport); 697 lport->tt.rport_flush_queue(); 698 } 699 700 /** 701 * fc_disc_init() - Initialize the discovery block 702 * @lport: FC local port 703 */ 704 int fc_disc_init(struct fc_lport *lport) 705 { 706 struct fc_disc *disc; 707 708 if (!lport->tt.disc_start) 709 lport->tt.disc_start = fc_disc_start; 710 711 if (!lport->tt.disc_stop) 712 lport->tt.disc_stop = fc_disc_stop; 713 714 if (!lport->tt.disc_stop_final) 715 lport->tt.disc_stop_final = fc_disc_stop_final; 716 717 if (!lport->tt.disc_recv_req) 718 lport->tt.disc_recv_req = fc_disc_recv_req; 719 720 disc = &lport->disc; 721 INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout); 722 mutex_init(&disc->disc_mutex); 723 INIT_LIST_HEAD(&disc->rports); 724 725 disc->lport = lport; 726 727 return 0; 728 } 729 EXPORT_SYMBOL(fc_disc_init); 730