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 <asm/unaligned.h>
  39
  40#include <scsi/fc/fc_gs.h>
  41
  42#include <scsi/libfc.h>
  43
  44#include "fc_libfc.h"
  45
  46#define FC_DISC_RETRY_LIMIT     3       /* max retries */
  47#define FC_DISC_RETRY_DELAY     500UL   /* (msecs) delay */
  48
  49static void fc_disc_gpn_ft_req(struct fc_disc *);
  50static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *);
  51static void fc_disc_done(struct fc_disc *, enum fc_disc_event);
  52static void fc_disc_timeout(struct work_struct *);
  53static int fc_disc_single(struct fc_lport *, struct fc_disc_port *);
  54static void fc_disc_restart(struct fc_disc *);
  55
  56/**
  57 * fc_disc_stop_rports() - Delete all the remote ports associated with the lport
  58 * @disc: The discovery job to stop remote ports on
  59 *
  60 * Locking Note: This function expects that the lport mutex is locked before
  61 * calling it.
  62 */
  63void fc_disc_stop_rports(struct fc_disc *disc)
  64{
  65        struct fc_lport *lport;
  66        struct fc_rport_priv *rdata;
  67
  68        lport = fc_disc_lport(disc);
  69
  70        mutex_lock(&disc->disc_mutex);
  71        list_for_each_entry_rcu(rdata, &disc->rports, peers)
  72                lport->tt.rport_logoff(rdata);
  73        mutex_unlock(&disc->disc_mutex);
  74}
  75
  76/**
  77 * fc_disc_recv_rscn_req() - Handle Registered State Change Notification (RSCN)
  78 * @disc:  The discovery object to which the RSCN applies
  79 * @fp:    The RSCN frame
  80 *
  81 * Locking Note: This function expects that the disc_mutex is locked
  82 *               before it is called.
  83 */
  84static void fc_disc_recv_rscn_req(struct fc_disc *disc, struct fc_frame *fp)
  85{
  86        struct fc_lport *lport;
  87        struct fc_els_rscn *rp;
  88        struct fc_els_rscn_page *pp;
  89        struct fc_seq_els_data rjt_data;
  90        unsigned int len;
  91        int redisc = 0;
  92        enum fc_els_rscn_ev_qual ev_qual;
  93        enum fc_els_rscn_addr_fmt fmt;
  94        LIST_HEAD(disc_ports);
  95        struct fc_disc_port *dp, *next;
  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                break;
 209        }
 210}
 211
 212/**
 213 * fc_disc_restart() - Restart discovery
 214 * @disc: The discovery object to be restarted
 215 *
 216 * Locking Note: This function expects that the disc mutex
 217 *               is already locked.
 218 */
 219static void fc_disc_restart(struct fc_disc *disc)
 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 *
 267 * Locking Note: This function expects that the disc mutex is locked before
 268 * it is called. The discovery callback is then made with the lock released,
 269 * and the lock is re-taken before returning from this function
 270 */
 271static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
 272{
 273        struct fc_lport *lport = fc_disc_lport(disc);
 274        struct fc_rport_priv *rdata;
 275
 276        FC_DISC_DBG(disc, "Discovery complete\n");
 277
 278        disc->pending = 0;
 279        if (disc->requested) {
 280                fc_disc_restart(disc);
 281                return;
 282        }
 283
 284        /*
 285         * Go through all remote ports.  If they were found in the latest
 286         * discovery, reverify or log them in.  Otherwise, log them out.
 287         * Skip ports which were never discovered.  These are the dNS port
 288         * and ports which were created by PLOGI.
 289         */
 290        list_for_each_entry_rcu(rdata, &disc->rports, peers) {
 291                if (!rdata->disc_id)
 292                        continue;
 293                if (rdata->disc_id == disc->disc_id)
 294                        lport->tt.rport_login(rdata);
 295                else
 296                        lport->tt.rport_logoff(rdata);
 297        }
 298
 299        mutex_unlock(&disc->disc_mutex);
 300        disc->disc_callback(lport, event);
 301        mutex_lock(&disc->disc_mutex);
 302}
 303
 304/**
 305 * fc_disc_error() - Handle error on dNS request
 306 * @disc: The discovery context
 307 * @fp:   The error code encoded as a frame pointer
 308 */
 309static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
 310{
 311        struct fc_lport *lport = fc_disc_lport(disc);
 312        unsigned long delay = 0;
 313
 314        FC_DISC_DBG(disc, "Error %ld, retries %d/%d\n",
 315                    PTR_ERR(fp), disc->retry_count,
 316                    FC_DISC_RETRY_LIMIT);
 317
 318        if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
 319                /*
 320                 * Memory allocation failure, or the exchange timed out,
 321                 * retry after delay.
 322                 */
 323                if (disc->retry_count < FC_DISC_RETRY_LIMIT) {
 324                        /* go ahead and retry */
 325                        if (!fp)
 326                                delay = msecs_to_jiffies(FC_DISC_RETRY_DELAY);
 327                        else {
 328                                delay = msecs_to_jiffies(lport->e_d_tov);
 329
 330                                /* timeout faster first time */
 331                                if (!disc->retry_count)
 332                                        delay /= 4;
 333                        }
 334                        disc->retry_count++;
 335                        schedule_delayed_work(&disc->disc_work, delay);
 336                } else
 337                        fc_disc_done(disc, DISC_EV_FAILED);
 338        }
 339}
 340
 341/**
 342 * fc_disc_gpn_ft_req() - Send Get Port Names by FC-4 type (GPN_FT) request
 343 * @lport: The discovery context
 344 *
 345 * Locking Note: This function expects that the disc_mutex is locked
 346 *               before it is called.
 347 */
 348static void fc_disc_gpn_ft_req(struct fc_disc *disc)
 349{
 350        struct fc_frame *fp;
 351        struct fc_lport *lport = fc_disc_lport(disc);
 352
 353        WARN_ON(!fc_lport_test_ready(lport));
 354
 355        disc->pending = 1;
 356        disc->requested = 0;
 357
 358        disc->buf_len = 0;
 359        disc->seq_count = 0;
 360        fp = fc_frame_alloc(lport,
 361                            sizeof(struct fc_ct_hdr) +
 362                            sizeof(struct fc_ns_gid_ft));
 363        if (!fp)
 364                goto err;
 365
 366        if (lport->tt.elsct_send(lport, 0, fp,
 367                                 FC_NS_GPN_FT,
 368                                 fc_disc_gpn_ft_resp,
 369                                 disc, 3 * lport->r_a_tov))
 370                return;
 371err:
 372        fc_disc_error(disc, NULL);
 373}
 374
 375/**
 376 * fc_disc_gpn_ft_parse() - Parse the body of the dNS GPN_FT response.
 377 * @lport: The local port the GPN_FT was received on
 378 * @buf:   The GPN_FT response buffer
 379 * @len:   The size of response buffer
 380 *
 381 * Goes through the list of IDs and names resulting from a request.
 382 */
 383static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
 384{
 385        struct fc_lport *lport;
 386        struct fc_gpn_ft_resp *np;
 387        char *bp;
 388        size_t plen;
 389        size_t tlen;
 390        int error = 0;
 391        struct fc_rport_identifiers ids;
 392        struct fc_rport_priv *rdata;
 393
 394        lport = fc_disc_lport(disc);
 395        disc->seq_count++;
 396
 397        /*
 398         * Handle partial name record left over from previous call.
 399         */
 400        bp = buf;
 401        plen = len;
 402        np = (struct fc_gpn_ft_resp *)bp;
 403        tlen = disc->buf_len;
 404        disc->buf_len = 0;
 405        if (tlen) {
 406                WARN_ON(tlen >= sizeof(*np));
 407                plen = sizeof(*np) - tlen;
 408                WARN_ON(plen <= 0);
 409                WARN_ON(plen >= sizeof(*np));
 410                if (plen > len)
 411                        plen = len;
 412                np = &disc->partial_buf;
 413                memcpy((char *)np + tlen, bp, plen);
 414
 415                /*
 416                 * Set bp so that the loop below will advance it to the
 417                 * first valid full name element.
 418                 */
 419                bp -= tlen;
 420                len += tlen;
 421                plen += tlen;
 422                disc->buf_len = (unsigned char) plen;
 423                if (plen == sizeof(*np))
 424                        disc->buf_len = 0;
 425        }
 426
 427        /*
 428         * Handle full name records, including the one filled from above.
 429         * Normally, np == bp and plen == len, but from the partial case above,
 430         * bp, len describe the overall buffer, and np, plen describe the
 431         * partial buffer, which if would usually be full now.
 432         * After the first time through the loop, things return to "normal".
 433         */
 434        while (plen >= sizeof(*np)) {
 435                ids.port_id = ntoh24(np->fp_fid);
 436                ids.port_name = ntohll(np->fp_wwpn);
 437
 438                if (ids.port_id != lport->port_id &&
 439                    ids.port_name != lport->wwpn) {
 440                        rdata = lport->tt.rport_create(lport, ids.port_id);
 441                        if (rdata) {
 442                                rdata->ids.port_name = ids.port_name;
 443                                rdata->disc_id = disc->disc_id;
 444                        } else {
 445                                printk(KERN_WARNING "libfc: Failed to allocate "
 446                                       "memory for the newly discovered port "
 447                                       "(%6.6x)\n", ids.port_id);
 448                                error = -ENOMEM;
 449                        }
 450                }
 451
 452                if (np->fp_flags & FC_NS_FID_LAST) {
 453                        fc_disc_done(disc, DISC_EV_SUCCESS);
 454                        len = 0;
 455                        break;
 456                }
 457                len -= sizeof(*np);
 458                bp += sizeof(*np);
 459                np = (struct fc_gpn_ft_resp *)bp;
 460                plen = len;
 461        }
 462
 463        /*
 464         * Save any partial record at the end of the buffer for next time.
 465         */
 466        if (error == 0 && len > 0 && len < sizeof(*np)) {
 467                if (np != &disc->partial_buf) {
 468                        FC_DISC_DBG(disc, "Partial buffer remains "
 469                                    "for discovery\n");
 470                        memcpy(&disc->partial_buf, np, len);
 471                }
 472                disc->buf_len = (unsigned char) len;
 473        }
 474        return error;
 475}
 476
 477/**
 478 * fc_disc_timeout() - Handler for discovery timeouts
 479 * @work: Structure holding discovery context that needs to retry discovery
 480 */
 481static void fc_disc_timeout(struct work_struct *work)
 482{
 483        struct fc_disc *disc = container_of(work,
 484                                            struct fc_disc,
 485                                            disc_work.work);
 486        mutex_lock(&disc->disc_mutex);
 487        fc_disc_gpn_ft_req(disc);
 488        mutex_unlock(&disc->disc_mutex);
 489}
 490
 491/**
 492 * fc_disc_gpn_ft_resp() - Handle a response frame from Get Port Names (GPN_FT)
 493 * @sp:     The sequence that the GPN_FT response was received on
 494 * @fp:     The GPN_FT response frame
 495 * @lp_arg: The discovery context
 496 *
 497 * Locking Note: This function is called without disc mutex held, and
 498 *               should do all its processing with the mutex held
 499 */
 500static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
 501                                void *disc_arg)
 502{
 503        struct fc_disc *disc = disc_arg;
 504        struct fc_ct_hdr *cp;
 505        struct fc_frame_header *fh;
 506        enum fc_disc_event event = DISC_EV_NONE;
 507        unsigned int seq_cnt;
 508        unsigned int len;
 509        int error = 0;
 510
 511        mutex_lock(&disc->disc_mutex);
 512        FC_DISC_DBG(disc, "Received a GPN_FT response\n");
 513
 514        if (IS_ERR(fp)) {
 515                fc_disc_error(disc, fp);
 516                mutex_unlock(&disc->disc_mutex);
 517                return;
 518        }
 519
 520        WARN_ON(!fc_frame_is_linear(fp));       /* buffer must be contiguous */
 521        fh = fc_frame_header_get(fp);
 522        len = fr_len(fp) - sizeof(*fh);
 523        seq_cnt = ntohs(fh->fh_seq_cnt);
 524        if (fr_sof(fp) == FC_SOF_I3 && seq_cnt == 0 && disc->seq_count == 0) {
 525                cp = fc_frame_payload_get(fp, sizeof(*cp));
 526                if (!cp) {
 527                        FC_DISC_DBG(disc, "GPN_FT response too short, len %d\n",
 528                                    fr_len(fp));
 529                        event = DISC_EV_FAILED;
 530                } else if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
 531
 532                        /* Accepted, parse the response. */
 533                        len -= sizeof(*cp);
 534                        error = fc_disc_gpn_ft_parse(disc, cp + 1, len);
 535                } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
 536                        FC_DISC_DBG(disc, "GPN_FT rejected reason %x exp %x "
 537                                    "(check zoning)\n", cp->ct_reason,
 538                                    cp->ct_explan);
 539                        event = DISC_EV_FAILED;
 540                        if (cp->ct_reason == FC_FS_RJT_UNABL &&
 541                            cp->ct_explan == FC_FS_EXP_FTNR)
 542                                event = DISC_EV_SUCCESS;
 543                } else {
 544                        FC_DISC_DBG(disc, "GPN_FT unexpected response code "
 545                                    "%x\n", ntohs(cp->ct_cmd));
 546                        event = DISC_EV_FAILED;
 547                }
 548        } else if (fr_sof(fp) == FC_SOF_N3 && seq_cnt == disc->seq_count) {
 549                error = fc_disc_gpn_ft_parse(disc, fh + 1, len);
 550        } else {
 551                FC_DISC_DBG(disc, "GPN_FT unexpected frame - out of sequence? "
 552                            "seq_cnt %x expected %x sof %x eof %x\n",
 553                            seq_cnt, disc->seq_count, fr_sof(fp), fr_eof(fp));
 554                event = DISC_EV_FAILED;
 555        }
 556        if (error)
 557                fc_disc_error(disc, fp);
 558        else if (event != DISC_EV_NONE)
 559                fc_disc_done(disc, event);
 560        fc_frame_free(fp);
 561        mutex_unlock(&disc->disc_mutex);
 562}
 563
 564/**
 565 * fc_disc_gpn_id_resp() - Handle a response frame from Get Port Names (GPN_ID)
 566 * @sp:        The sequence the GPN_ID is on
 567 * @fp:        The response frame
 568 * @rdata_arg: The remote port that sent the GPN_ID response
 569 *
 570 * Locking Note: This function is called without disc mutex held.
 571 */
 572static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
 573                                void *rdata_arg)
 574{
 575        struct fc_rport_priv *rdata = rdata_arg;
 576        struct fc_rport_priv *new_rdata;
 577        struct fc_lport *lport;
 578        struct fc_disc *disc;
 579        struct fc_ct_hdr *cp;
 580        struct fc_ns_gid_pn *pn;
 581        u64 port_name;
 582
 583        lport = rdata->local_port;
 584        disc = &lport->disc;
 585
 586        mutex_lock(&disc->disc_mutex);
 587        if (PTR_ERR(fp) == -FC_EX_CLOSED)
 588                goto out;
 589        if (IS_ERR(fp))
 590                goto redisc;
 591
 592        cp = fc_frame_payload_get(fp, sizeof(*cp));
 593        if (!cp)
 594                goto redisc;
 595        if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
 596                if (fr_len(fp) < sizeof(struct fc_frame_header) +
 597                    sizeof(*cp) + sizeof(*pn))
 598                        goto redisc;
 599                pn = (struct fc_ns_gid_pn *)(cp + 1);
 600                port_name = get_unaligned_be64(&pn->fn_wwpn);
 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                        lport->tt.rport_logoff(rdata);
 608
 609                        new_rdata = lport->tt.rport_create(lport,
 610                                                           rdata->ids.port_id);
 611                        if (new_rdata) {
 612                                new_rdata->disc_id = disc->disc_id;
 613                                lport->tt.rport_login(new_rdata);
 614                        }
 615                        goto out;
 616                }
 617                rdata->disc_id = disc->disc_id;
 618                lport->tt.rport_login(rdata);
 619        } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
 620                FC_DISC_DBG(disc, "GPN_ID rejected reason %x exp %x\n",
 621                            cp->ct_reason, cp->ct_explan);
 622                lport->tt.rport_logoff(rdata);
 623        } else {
 624                FC_DISC_DBG(disc, "GPN_ID unexpected response code %x\n",
 625                            ntohs(cp->ct_cmd));
 626redisc:
 627                fc_disc_restart(disc);
 628        }
 629out:
 630        mutex_unlock(&disc->disc_mutex);
 631        kref_put(&rdata->kref, lport->tt.rport_destroy);
 632}
 633
 634/**
 635 * fc_disc_gpn_id_req() - Send Get Port Names by ID (GPN_ID) request
 636 * @lport: The local port to initiate discovery on
 637 * @rdata: remote port private data
 638 *
 639 * Locking Note: This function expects that the disc_mutex is locked
 640 *               before it is called.
 641 * On failure, an error code is returned.
 642 */
 643static int fc_disc_gpn_id_req(struct fc_lport *lport,
 644                              struct fc_rport_priv *rdata)
 645{
 646        struct fc_frame *fp;
 647
 648        fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
 649                            sizeof(struct fc_ns_fid));
 650        if (!fp)
 651                return -ENOMEM;
 652        if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, FC_NS_GPN_ID,
 653                                  fc_disc_gpn_id_resp, rdata,
 654                                  3 * lport->r_a_tov))
 655                return -ENOMEM;
 656        kref_get(&rdata->kref);
 657        return 0;
 658}
 659
 660/**
 661 * fc_disc_single() - Discover the directory information for a single target
 662 * @lport: The local port the remote port is associated with
 663 * @dp:    The port to rediscover
 664 *
 665 * Locking Note: This function expects that the disc_mutex is locked
 666 *               before it is called.
 667 */
 668static int fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp)
 669{
 670        struct fc_rport_priv *rdata;
 671
 672        rdata = lport->tt.rport_create(lport, dp->port_id);
 673        if (!rdata)
 674                return -ENOMEM;
 675        rdata->disc_id = 0;
 676        return fc_disc_gpn_id_req(lport, rdata);
 677}
 678
 679/**
 680 * fc_disc_stop() - Stop discovery for a given lport
 681 * @lport: The local port that discovery should stop on
 682 */
 683void fc_disc_stop(struct fc_lport *lport)
 684{
 685        struct fc_disc *disc = &lport->disc;
 686
 687        if (disc->pending)
 688                cancel_delayed_work_sync(&disc->disc_work);
 689        fc_disc_stop_rports(disc);
 690}
 691
 692/**
 693 * fc_disc_stop_final() - Stop discovery for a given lport
 694 * @lport: The lport that discovery should stop on
 695 *
 696 * This function will block until discovery has been
 697 * completely stopped and all rports have been deleted.
 698 */
 699void fc_disc_stop_final(struct fc_lport *lport)
 700{
 701        fc_disc_stop(lport);
 702        lport->tt.rport_flush_queue();
 703}
 704
 705/**
 706 * fc_disc_init() - Initialize the discovery layer for a local port
 707 * @lport: The local port that needs the discovery layer to be initialized
 708 */
 709int fc_disc_init(struct fc_lport *lport)
 710{
 711        struct fc_disc *disc;
 712
 713        if (!lport->tt.disc_start)
 714                lport->tt.disc_start = fc_disc_start;
 715
 716        if (!lport->tt.disc_stop)
 717                lport->tt.disc_stop = fc_disc_stop;
 718
 719        if (!lport->tt.disc_stop_final)
 720                lport->tt.disc_stop_final = fc_disc_stop_final;
 721
 722        if (!lport->tt.disc_recv_req)
 723                lport->tt.disc_recv_req = fc_disc_recv_req;
 724
 725        disc = &lport->disc;
 726        INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
 727        mutex_init(&disc->disc_mutex);
 728        INIT_LIST_HEAD(&disc->rports);
 729
 730        disc->priv = lport;
 731
 732        return 0;
 733}
 734EXPORT_SYMBOL(fc_disc_init);
 735