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