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