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 fc_disc_gpn_ft_req(disc); 237 } 238 239 /** 240 * fc_disc_start() - Fibre Channel Target discovery 241 * @lport: FC local port 242 * 243 * Returns non-zero if discovery cannot be started. 244 */ 245 static void fc_disc_start(void (*disc_callback)(struct fc_lport *, 246 enum fc_disc_event), 247 struct fc_lport *lport) 248 { 249 struct fc_rport_priv *rdata; 250 struct fc_disc *disc = &lport->disc; 251 252 /* 253 * At this point we may have a new disc job or an existing 254 * one. Either way, let's lock when we make changes to it 255 * and send the GPN_FT request. 256 */ 257 mutex_lock(&disc->disc_mutex); 258 259 disc->disc_callback = disc_callback; 260 261 /* 262 * If not ready, or already running discovery, just set request flag. 263 */ 264 disc->requested = 1; 265 266 if (disc->pending) { 267 mutex_unlock(&disc->disc_mutex); 268 return; 269 } 270 271 /* 272 * Handle point-to-point mode as a simple discovery 273 * of the remote port. Yucky, yucky, yuck, yuck! 274 */ 275 rdata = disc->lport->ptp_rp; 276 if (rdata) { 277 kref_get(&rdata->kref); 278 if (!fc_disc_new_target(disc, rdata, &rdata->ids)) { 279 fc_disc_done(disc, DISC_EV_SUCCESS); 280 } 281 kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy); 282 } else { 283 disc->disc_id = (disc->disc_id + 2) | 1; 284 fc_disc_gpn_ft_req(disc); /* get ports by FC-4 type */ 285 } 286 287 mutex_unlock(&disc->disc_mutex); 288 } 289 290 /** 291 * fc_disc_new_target() - Handle new target found by discovery 292 * @lport: FC local port 293 * @rdata: The previous FC remote port priv (NULL if new remote port) 294 * @ids: Identifiers for the new FC remote port 295 * 296 * Locking Note: This function expects that the disc_mutex is locked 297 * before it is called. 298 */ 299 static int fc_disc_new_target(struct fc_disc *disc, 300 struct fc_rport_priv *rdata, 301 struct fc_rport_identifiers *ids) 302 { 303 struct fc_lport *lport = disc->lport; 304 int error = 0; 305 306 if (rdata && ids->port_name) { 307 if (rdata->ids.port_name == -1) { 308 /* 309 * Set WWN and fall through to notify of create. 310 */ 311 rdata->ids.port_name = ids->port_name; 312 rdata->ids.node_name = ids->node_name; 313 } else if (rdata->ids.port_name != ids->port_name) { 314 /* 315 * This is a new port with the same FCID as 316 * a previously-discovered port. Presumably the old 317 * port logged out and a new port logged in and was 318 * assigned the same FCID. This should be rare. 319 * Delete the old one and fall thru to re-create. 320 */ 321 lport->tt.rport_logoff(rdata); 322 rdata = NULL; 323 } 324 } 325 if (((ids->port_name != -1) || (ids->port_id != -1)) && 326 ids->port_id != fc_host_port_id(lport->host) && 327 ids->port_name != lport->wwpn) { 328 if (!rdata) { 329 rdata = lport->tt.rport_create(lport, ids); 330 if (!rdata) 331 error = -ENOMEM; 332 } 333 if (rdata) 334 lport->tt.rport_login(rdata); 335 } 336 return error; 337 } 338 339 /** 340 * fc_disc_done() - Discovery has been completed 341 * @disc: FC discovery context 342 * @event: discovery completion status 343 * 344 * Locking Note: This function expects that the disc mutex is locked before 345 * it is called. The discovery callback is then made with the lock released, 346 * and the lock is re-taken before returning from this function 347 */ 348 static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event) 349 { 350 struct fc_lport *lport = disc->lport; 351 struct fc_rport_priv *rdata; 352 353 FC_DISC_DBG(disc, "Discovery complete\n"); 354 355 disc->pending = 0; 356 if (disc->requested) { 357 fc_disc_restart(disc); 358 return; 359 } 360 361 /* 362 * Go through all remote ports. If they were found in the latest 363 * discovery, reverify or log them in. Otherwise, log them out. 364 * Skip ports which were never discovered. These are the dNS port 365 * and ports which were created by PLOGI. 366 */ 367 list_for_each_entry(rdata, &disc->rports, peers) { 368 if (!rdata->disc_id) 369 continue; 370 if (rdata->disc_id == disc->disc_id) 371 lport->tt.rport_login(rdata); 372 else 373 lport->tt.rport_logoff(rdata); 374 } 375 376 mutex_unlock(&disc->disc_mutex); 377 disc->disc_callback(lport, event); 378 mutex_lock(&disc->disc_mutex); 379 } 380 381 /** 382 * fc_disc_error() - Handle error on dNS request 383 * @disc: FC discovery context 384 * @fp: The frame pointer 385 */ 386 static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp) 387 { 388 struct fc_lport *lport = disc->lport; 389 unsigned long delay = 0; 390 391 FC_DISC_DBG(disc, "Error %ld, retries %d/%d\n", 392 PTR_ERR(fp), disc->retry_count, 393 FC_DISC_RETRY_LIMIT); 394 395 if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) { 396 /* 397 * Memory allocation failure, or the exchange timed out, 398 * retry after delay. 399 */ 400 if (disc->retry_count < FC_DISC_RETRY_LIMIT) { 401 /* go ahead and retry */ 402 if (!fp) 403 delay = msecs_to_jiffies(FC_DISC_RETRY_DELAY); 404 else { 405 delay = msecs_to_jiffies(lport->e_d_tov); 406 407 /* timeout faster first time */ 408 if (!disc->retry_count) 409 delay /= 4; 410 } 411 disc->retry_count++; 412 schedule_delayed_work(&disc->disc_work, delay); 413 } else 414 fc_disc_done(disc, DISC_EV_FAILED); 415 } 416 } 417 418 /** 419 * fc_disc_gpn_ft_req() - Send Get Port Names by FC-4 type (GPN_FT) request 420 * @lport: FC discovery context 421 * 422 * Locking Note: This function expects that the disc_mutex is locked 423 * before it is called. 424 */ 425 static void fc_disc_gpn_ft_req(struct fc_disc *disc) 426 { 427 struct fc_frame *fp; 428 struct fc_lport *lport = disc->lport; 429 430 WARN_ON(!fc_lport_test_ready(lport)); 431 432 disc->pending = 1; 433 disc->requested = 0; 434 435 disc->buf_len = 0; 436 disc->seq_count = 0; 437 fp = fc_frame_alloc(lport, 438 sizeof(struct fc_ct_hdr) + 439 sizeof(struct fc_ns_gid_ft)); 440 if (!fp) 441 goto err; 442 443 if (lport->tt.elsct_send(lport, 0, fp, 444 FC_NS_GPN_FT, 445 fc_disc_gpn_ft_resp, 446 disc, lport->e_d_tov)) 447 return; 448 err: 449 fc_disc_error(disc, fp); 450 } 451 452 /** 453 * fc_disc_gpn_ft_parse() - Parse the body of the dNS GPN_FT response. 454 * @lport: Fibre Channel host port instance 455 * @buf: GPN_FT response buffer 456 * @len: size of response buffer 457 * 458 * Goes through the list of IDs and names resulting from a request. 459 */ 460 static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len) 461 { 462 struct fc_lport *lport; 463 struct fc_gpn_ft_resp *np; 464 char *bp; 465 size_t plen; 466 size_t tlen; 467 int error = 0; 468 struct fc_rport_identifiers ids; 469 struct fc_rport_priv *rdata; 470 471 lport = disc->lport; 472 disc->seq_count++; 473 474 /* 475 * Handle partial name record left over from previous call. 476 */ 477 bp = buf; 478 plen = len; 479 np = (struct fc_gpn_ft_resp *)bp; 480 tlen = disc->buf_len; 481 if (tlen) { 482 WARN_ON(tlen >= sizeof(*np)); 483 plen = sizeof(*np) - tlen; 484 WARN_ON(plen <= 0); 485 WARN_ON(plen >= sizeof(*np)); 486 if (plen > len) 487 plen = len; 488 np = &disc->partial_buf; 489 memcpy((char *)np + tlen, bp, plen); 490 491 /* 492 * Set bp so that the loop below will advance it to the 493 * first valid full name element. 494 */ 495 bp -= tlen; 496 len += tlen; 497 plen += tlen; 498 disc->buf_len = (unsigned char) plen; 499 if (plen == sizeof(*np)) 500 disc->buf_len = 0; 501 } 502 503 /* 504 * Handle full name records, including the one filled from above. 505 * Normally, np == bp and plen == len, but from the partial case above, 506 * bp, len describe the overall buffer, and np, plen describe the 507 * partial buffer, which if would usually be full now. 508 * After the first time through the loop, things return to "normal". 509 */ 510 while (plen >= sizeof(*np)) { 511 ids.port_id = ntoh24(np->fp_fid); 512 ids.port_name = ntohll(np->fp_wwpn); 513 ids.node_name = -1; 514 ids.roles = FC_RPORT_ROLE_UNKNOWN; 515 516 if (ids.port_id != fc_host_port_id(lport->host) && 517 ids.port_name != lport->wwpn) { 518 rdata = lport->tt.rport_create(lport, &ids); 519 if (rdata) 520 rdata->disc_id = disc->disc_id; 521 else 522 printk(KERN_WARNING "libfc: Failed to allocate " 523 "memory for the newly discovered port " 524 "(%6x)\n", ids.port_id); 525 } 526 527 if (np->fp_flags & FC_NS_FID_LAST) { 528 fc_disc_done(disc, DISC_EV_SUCCESS); 529 len = 0; 530 break; 531 } 532 len -= sizeof(*np); 533 bp += sizeof(*np); 534 np = (struct fc_gpn_ft_resp *)bp; 535 plen = len; 536 } 537 538 /* 539 * Save any partial record at the end of the buffer for next time. 540 */ 541 if (error == 0 && len > 0 && len < sizeof(*np)) { 542 if (np != &disc->partial_buf) { 543 FC_DISC_DBG(disc, "Partial buffer remains " 544 "for discovery\n"); 545 memcpy(&disc->partial_buf, np, len); 546 } 547 disc->buf_len = (unsigned char) len; 548 } else { 549 disc->buf_len = 0; 550 } 551 return error; 552 } 553 554 /** 555 * fc_disc_timeout() - Retry handler for the disc component 556 * @work: Structure holding disc obj that needs retry discovery 557 * 558 * Handle retry of memory allocation for remote ports. 559 */ 560 static void fc_disc_timeout(struct work_struct *work) 561 { 562 struct fc_disc *disc = container_of(work, 563 struct fc_disc, 564 disc_work.work); 565 mutex_lock(&disc->disc_mutex); 566 if (disc->requested && !disc->pending) 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 } else if (ntohs(cp->ct_cmd) == FC_FS_ACC) { 610 611 /* Accepted, parse the response. */ 612 len -= sizeof(*cp); 613 error = fc_disc_gpn_ft_parse(disc, cp + 1, len); 614 } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) { 615 FC_DISC_DBG(disc, "GPN_FT rejected reason %x exp %x " 616 "(check zoning)\n", cp->ct_reason, 617 cp->ct_explan); 618 event = DISC_EV_FAILED; 619 } else { 620 FC_DISC_DBG(disc, "GPN_FT unexpected response code " 621 "%x\n", ntohs(cp->ct_cmd)); 622 } 623 } else if (fr_sof(fp) == FC_SOF_N3 && seq_cnt == disc->seq_count) { 624 error = fc_disc_gpn_ft_parse(disc, fh + 1, len); 625 } else { 626 FC_DISC_DBG(disc, "GPN_FT unexpected frame - out of sequence? " 627 "seq_cnt %x expected %x sof %x eof %x\n", 628 seq_cnt, disc->seq_count, fr_sof(fp), fr_eof(fp)); 629 } 630 if (error) 631 fc_disc_error(disc, fp); 632 else if (event != DISC_EV_NONE) 633 fc_disc_done(disc, event); 634 fc_frame_free(fp); 635 mutex_unlock(&disc->disc_mutex); 636 } 637 638 /** 639 * fc_disc_single() - Discover the directory information for a single target 640 * @lport: FC local port 641 * @dp: The port to rediscover 642 * 643 * Locking Note: This function expects that the disc_mutex is locked 644 * before it is called. 645 */ 646 static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp) 647 { 648 struct fc_lport *lport; 649 struct fc_rport_priv *rdata; 650 651 lport = disc->lport; 652 653 if (dp->ids.port_id == fc_host_port_id(lport->host)) 654 goto out; 655 656 rdata = lport->tt.rport_create(lport, &dp->ids); 657 if (rdata) { 658 rdata->disc_id = disc->disc_id; 659 kfree(dp); 660 lport->tt.rport_login(rdata); 661 } 662 return; 663 out: 664 kfree(dp); 665 } 666 667 /** 668 * fc_disc_stop() - Stop discovery for a given lport 669 * @lport: The lport that discovery should stop for 670 */ 671 void fc_disc_stop(struct fc_lport *lport) 672 { 673 struct fc_disc *disc = &lport->disc; 674 675 if (disc) { 676 cancel_delayed_work_sync(&disc->disc_work); 677 fc_disc_stop_rports(disc); 678 } 679 } 680 681 /** 682 * fc_disc_stop_final() - Stop discovery for a given lport 683 * @lport: The lport that discovery should stop for 684 * 685 * This function will block until discovery has been 686 * completely stopped and all rports have been deleted. 687 */ 688 void fc_disc_stop_final(struct fc_lport *lport) 689 { 690 fc_disc_stop(lport); 691 lport->tt.rport_flush_queue(); 692 } 693 694 /** 695 * fc_disc_init() - Initialize the discovery block 696 * @lport: FC local port 697 */ 698 int fc_disc_init(struct fc_lport *lport) 699 { 700 struct fc_disc *disc; 701 702 if (!lport->tt.disc_start) 703 lport->tt.disc_start = fc_disc_start; 704 705 if (!lport->tt.disc_stop) 706 lport->tt.disc_stop = fc_disc_stop; 707 708 if (!lport->tt.disc_stop_final) 709 lport->tt.disc_stop_final = fc_disc_stop_final; 710 711 if (!lport->tt.disc_recv_req) 712 lport->tt.disc_recv_req = fc_disc_recv_req; 713 714 disc = &lport->disc; 715 INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout); 716 mutex_init(&disc->disc_mutex); 717 INIT_LIST_HEAD(&disc->rports); 718 719 disc->lport = lport; 720 721 return 0; 722 } 723 EXPORT_SYMBOL(fc_disc_init); 724