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