Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
0004  *
0005  * Maintained at www.Open-FCoE.org
0006  */
0007 
0008 /*
0009  * Target Discovery
0010  *
0011  * This block discovers all FC-4 remote ports, including FCP initiators. It
0012  * also handles RSCN events and re-discovery if necessary.
0013  */
0014 
0015 /*
0016  * DISC LOCKING
0017  *
0018  * The disc mutex is can be locked when acquiring rport locks, but may not
0019  * be held when acquiring the lport lock. Refer to fc_lport.c for more
0020  * details.
0021  */
0022 
0023 #include <linux/timer.h>
0024 #include <linux/slab.h>
0025 #include <linux/err.h>
0026 #include <linux/export.h>
0027 #include <linux/rculist.h>
0028 
0029 #include <asm/unaligned.h>
0030 
0031 #include <scsi/fc/fc_gs.h>
0032 
0033 #include <scsi/libfc.h>
0034 
0035 #include "fc_libfc.h"
0036 
0037 #define FC_DISC_RETRY_LIMIT 3   /* max retries */
0038 #define FC_DISC_RETRY_DELAY 500UL   /* (msecs) delay */
0039 
0040 static void fc_disc_gpn_ft_req(struct fc_disc *);
0041 static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *);
0042 static void fc_disc_done(struct fc_disc *, enum fc_disc_event);
0043 static void fc_disc_timeout(struct work_struct *);
0044 static int fc_disc_single(struct fc_lport *, struct fc_disc_port *);
0045 static void fc_disc_restart(struct fc_disc *);
0046 
0047 /**
0048  * fc_disc_stop_rports() - Delete all the remote ports associated with the lport
0049  * @disc: The discovery job to stop remote ports on
0050  */
0051 static void fc_disc_stop_rports(struct fc_disc *disc)
0052 {
0053     struct fc_rport_priv *rdata;
0054 
0055     lockdep_assert_held(&disc->disc_mutex);
0056 
0057     list_for_each_entry(rdata, &disc->rports, peers) {
0058         if (kref_get_unless_zero(&rdata->kref)) {
0059             fc_rport_logoff(rdata);
0060             kref_put(&rdata->kref, fc_rport_destroy);
0061         }
0062     }
0063 }
0064 
0065 /**
0066  * fc_disc_recv_rscn_req() - Handle Registered State Change Notification (RSCN)
0067  * @disc:  The discovery object to which the RSCN applies
0068  * @fp:    The RSCN frame
0069  */
0070 static void fc_disc_recv_rscn_req(struct fc_disc *disc, struct fc_frame *fp)
0071 {
0072     struct fc_lport *lport;
0073     struct fc_els_rscn *rp;
0074     struct fc_els_rscn_page *pp;
0075     struct fc_seq_els_data rjt_data;
0076     unsigned int len;
0077     int redisc = 0;
0078     enum fc_els_rscn_ev_qual ev_qual;
0079     enum fc_els_rscn_addr_fmt fmt;
0080     LIST_HEAD(disc_ports);
0081     struct fc_disc_port *dp, *next;
0082 
0083     lockdep_assert_held(&disc->disc_mutex);
0084 
0085     lport = fc_disc_lport(disc);
0086 
0087     FC_DISC_DBG(disc, "Received an RSCN event\n");
0088 
0089     /* make sure the frame contains an RSCN message */
0090     rp = fc_frame_payload_get(fp, sizeof(*rp));
0091     if (!rp)
0092         goto reject;
0093     /* make sure the page length is as expected (4 bytes) */
0094     if (rp->rscn_page_len != sizeof(*pp))
0095         goto reject;
0096     /* get the RSCN payload length */
0097     len = ntohs(rp->rscn_plen);
0098     if (len < sizeof(*rp))
0099         goto reject;
0100     /* make sure the frame contains the expected payload */
0101     rp = fc_frame_payload_get(fp, len);
0102     if (!rp)
0103         goto reject;
0104     /* payload must be a multiple of the RSCN page size */
0105     len -= sizeof(*rp);
0106     if (len % sizeof(*pp))
0107         goto reject;
0108 
0109     for (pp = (void *)(rp + 1); len > 0; len -= sizeof(*pp), pp++) {
0110         ev_qual = pp->rscn_page_flags >> ELS_RSCN_EV_QUAL_BIT;
0111         ev_qual &= ELS_RSCN_EV_QUAL_MASK;
0112         fmt = pp->rscn_page_flags >> ELS_RSCN_ADDR_FMT_BIT;
0113         fmt &= ELS_RSCN_ADDR_FMT_MASK;
0114         /*
0115          * if we get an address format other than port
0116          * (area, domain, fabric), then do a full discovery
0117          */
0118         switch (fmt) {
0119         case ELS_ADDR_FMT_PORT:
0120             FC_DISC_DBG(disc, "Port address format for port "
0121                     "(%6.6x)\n", ntoh24(pp->rscn_fid));
0122             dp = kzalloc(sizeof(*dp), GFP_KERNEL);
0123             if (!dp) {
0124                 redisc = 1;
0125                 break;
0126             }
0127             dp->lp = lport;
0128             dp->port_id = ntoh24(pp->rscn_fid);
0129             list_add_tail(&dp->peers, &disc_ports);
0130             break;
0131         case ELS_ADDR_FMT_AREA:
0132         case ELS_ADDR_FMT_DOM:
0133         case ELS_ADDR_FMT_FAB:
0134         default:
0135             FC_DISC_DBG(disc, "Address format is (%d)\n", fmt);
0136             redisc = 1;
0137             break;
0138         }
0139     }
0140     fc_seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
0141 
0142     /*
0143      * If not doing a complete rediscovery, do GPN_ID on
0144      * the individual ports mentioned in the list.
0145      * If any of these get an error, do a full rediscovery.
0146      * In any case, go through the list and free the entries.
0147      */
0148     list_for_each_entry_safe(dp, next, &disc_ports, peers) {
0149         list_del(&dp->peers);
0150         if (!redisc)
0151             redisc = fc_disc_single(lport, dp);
0152         kfree(dp);
0153     }
0154     if (redisc) {
0155         FC_DISC_DBG(disc, "RSCN received: rediscovering\n");
0156         fc_disc_restart(disc);
0157     } else {
0158         FC_DISC_DBG(disc, "RSCN received: not rediscovering. "
0159                 "redisc %d state %d in_prog %d\n",
0160                 redisc, lport->state, disc->pending);
0161     }
0162     fc_frame_free(fp);
0163     return;
0164 reject:
0165     FC_DISC_DBG(disc, "Received a bad RSCN frame\n");
0166     rjt_data.reason = ELS_RJT_LOGIC;
0167     rjt_data.explan = ELS_EXPL_NONE;
0168     fc_seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data);
0169     fc_frame_free(fp);
0170 }
0171 
0172 /**
0173  * fc_disc_recv_req() - Handle incoming requests
0174  * @lport: The local port receiving the request
0175  * @fp:    The request frame
0176  *
0177  * Locking Note: This function is called from the EM and will lock
0178  *       the disc_mutex before calling the handler for the
0179  *       request.
0180  */
0181 static void fc_disc_recv_req(struct fc_lport *lport, struct fc_frame *fp)
0182 {
0183     u8 op;
0184     struct fc_disc *disc = &lport->disc;
0185 
0186     op = fc_frame_payload_op(fp);
0187     switch (op) {
0188     case ELS_RSCN:
0189         mutex_lock(&disc->disc_mutex);
0190         fc_disc_recv_rscn_req(disc, fp);
0191         mutex_unlock(&disc->disc_mutex);
0192         break;
0193     default:
0194         FC_DISC_DBG(disc, "Received an unsupported request, "
0195                 "the opcode is (%x)\n", op);
0196         fc_frame_free(fp);
0197         break;
0198     }
0199 }
0200 
0201 /**
0202  * fc_disc_restart() - Restart discovery
0203  * @disc: The discovery object to be restarted
0204  */
0205 static void fc_disc_restart(struct fc_disc *disc)
0206 {
0207     lockdep_assert_held(&disc->disc_mutex);
0208 
0209     if (!disc->disc_callback)
0210         return;
0211 
0212     FC_DISC_DBG(disc, "Restarting discovery\n");
0213 
0214     disc->requested = 1;
0215     if (disc->pending)
0216         return;
0217 
0218     /*
0219      * Advance disc_id.  This is an arbitrary non-zero number that will
0220      * match the value in the fc_rport_priv after discovery for all
0221      * freshly-discovered remote ports.  Avoid wrapping to zero.
0222      */
0223     disc->disc_id = (disc->disc_id + 2) | 1;
0224     disc->retry_count = 0;
0225     fc_disc_gpn_ft_req(disc);
0226 }
0227 
0228 /**
0229  * fc_disc_start() - Start discovery on a local port
0230  * @lport:     The local port to have discovery started on
0231  * @disc_callback: Callback function to be called when discovery is complete
0232  */
0233 static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
0234                         enum fc_disc_event),
0235               struct fc_lport *lport)
0236 {
0237     struct fc_disc *disc = &lport->disc;
0238 
0239     /*
0240      * At this point we may have a new disc job or an existing
0241      * one. Either way, let's lock when we make changes to it
0242      * and send the GPN_FT request.
0243      */
0244     mutex_lock(&disc->disc_mutex);
0245     disc->disc_callback = disc_callback;
0246     fc_disc_restart(disc);
0247     mutex_unlock(&disc->disc_mutex);
0248 }
0249 
0250 /**
0251  * fc_disc_done() - Discovery has been completed
0252  * @disc:  The discovery context
0253  * @event: The discovery completion status
0254  */
0255 static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
0256 {
0257     struct fc_lport *lport = fc_disc_lport(disc);
0258     struct fc_rport_priv *rdata;
0259 
0260     lockdep_assert_held(&disc->disc_mutex);
0261     FC_DISC_DBG(disc, "Discovery complete\n");
0262 
0263     disc->pending = 0;
0264     if (disc->requested) {
0265         fc_disc_restart(disc);
0266         return;
0267     }
0268 
0269     /*
0270      * Go through all remote ports.  If they were found in the latest
0271      * discovery, reverify or log them in.  Otherwise, log them out.
0272      * Skip ports which were never discovered.  These are the dNS port
0273      * and ports which were created by PLOGI.
0274      *
0275      * We don't need to use the _rcu variant here as the rport list
0276      * is protected by the disc mutex which is already held on entry.
0277      */
0278     list_for_each_entry(rdata, &disc->rports, peers) {
0279         if (!kref_get_unless_zero(&rdata->kref))
0280             continue;
0281         if (rdata->disc_id) {
0282             if (rdata->disc_id == disc->disc_id)
0283                 fc_rport_login(rdata);
0284             else
0285                 fc_rport_logoff(rdata);
0286         }
0287         kref_put(&rdata->kref, fc_rport_destroy);
0288     }
0289     mutex_unlock(&disc->disc_mutex);
0290     disc->disc_callback(lport, event);
0291     mutex_lock(&disc->disc_mutex);
0292 }
0293 
0294 /**
0295  * fc_disc_error() - Handle error on dNS request
0296  * @disc: The discovery context
0297  * @fp:   The error code encoded as a frame pointer
0298  */
0299 static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
0300 {
0301     struct fc_lport *lport = fc_disc_lport(disc);
0302     unsigned long delay = 0;
0303 
0304     FC_DISC_DBG(disc, "Error %d, retries %d/%d\n",
0305             PTR_ERR_OR_ZERO(fp), disc->retry_count,
0306             FC_DISC_RETRY_LIMIT);
0307 
0308     if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
0309         /*
0310          * Memory allocation failure, or the exchange timed out,
0311          * retry after delay.
0312          */
0313         if (disc->retry_count < FC_DISC_RETRY_LIMIT) {
0314             /* go ahead and retry */
0315             if (!fp)
0316                 delay = msecs_to_jiffies(FC_DISC_RETRY_DELAY);
0317             else {
0318                 delay = msecs_to_jiffies(lport->e_d_tov);
0319 
0320                 /* timeout faster first time */
0321                 if (!disc->retry_count)
0322                     delay /= 4;
0323             }
0324             disc->retry_count++;
0325             schedule_delayed_work(&disc->disc_work, delay);
0326         } else
0327             fc_disc_done(disc, DISC_EV_FAILED);
0328     } else if (PTR_ERR(fp) == -FC_EX_CLOSED) {
0329         /*
0330          * if discovery fails due to lport reset, clear
0331          * pending flag so that subsequent discovery can
0332          * continue
0333          */
0334         disc->pending = 0;
0335     }
0336 }
0337 
0338 /**
0339  * fc_disc_gpn_ft_req() - Send Get Port Names by FC-4 type (GPN_FT) request
0340  * @disc: The discovery context
0341  */
0342 static void fc_disc_gpn_ft_req(struct fc_disc *disc)
0343 {
0344     struct fc_frame *fp;
0345     struct fc_lport *lport = fc_disc_lport(disc);
0346 
0347     lockdep_assert_held(&disc->disc_mutex);
0348 
0349     WARN_ON(!fc_lport_test_ready(lport));
0350 
0351     disc->pending = 1;
0352     disc->requested = 0;
0353 
0354     disc->buf_len = 0;
0355     disc->seq_count = 0;
0356     fp = fc_frame_alloc(lport,
0357                 sizeof(struct fc_ct_hdr) +
0358                 sizeof(struct fc_ns_gid_ft));
0359     if (!fp)
0360         goto err;
0361 
0362     if (lport->tt.elsct_send(lport, 0, fp,
0363                  FC_NS_GPN_FT,
0364                  fc_disc_gpn_ft_resp,
0365                  disc, 3 * lport->r_a_tov))
0366         return;
0367 err:
0368     fc_disc_error(disc, NULL);
0369 }
0370 
0371 /**
0372  * fc_disc_gpn_ft_parse() - Parse the body of the dNS GPN_FT response.
0373  * @disc:  The discovery context
0374  * @buf:   The GPN_FT response buffer
0375  * @len:   The size of response buffer
0376  *
0377  * Goes through the list of IDs and names resulting from a request.
0378  */
0379 static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
0380 {
0381     struct fc_lport *lport;
0382     struct fc_gpn_ft_resp *np;
0383     char *bp;
0384     size_t plen;
0385     size_t tlen;
0386     int error = 0;
0387     struct fc_rport_identifiers ids;
0388     struct fc_rport_priv *rdata;
0389 
0390     lport = fc_disc_lport(disc);
0391     disc->seq_count++;
0392 
0393     /*
0394      * Handle partial name record left over from previous call.
0395      */
0396     bp = buf;
0397     plen = len;
0398     np = (struct fc_gpn_ft_resp *)bp;
0399     tlen = disc->buf_len;
0400     disc->buf_len = 0;
0401     if (tlen) {
0402         WARN_ON(tlen >= sizeof(*np));
0403         plen = sizeof(*np) - tlen;
0404         WARN_ON(plen <= 0);
0405         WARN_ON(plen >= sizeof(*np));
0406         if (plen > len)
0407             plen = len;
0408         np = &disc->partial_buf;
0409         memcpy((char *)np + tlen, bp, plen);
0410 
0411         /*
0412          * Set bp so that the loop below will advance it to the
0413          * first valid full name element.
0414          */
0415         bp -= tlen;
0416         len += tlen;
0417         plen += tlen;
0418         disc->buf_len = (unsigned char) plen;
0419         if (plen == sizeof(*np))
0420             disc->buf_len = 0;
0421     }
0422 
0423     /*
0424      * Handle full name records, including the one filled from above.
0425      * Normally, np == bp and plen == len, but from the partial case above,
0426      * bp, len describe the overall buffer, and np, plen describe the
0427      * partial buffer, which if would usually be full now.
0428      * After the first time through the loop, things return to "normal".
0429      */
0430     while (plen >= sizeof(*np)) {
0431         ids.port_id = ntoh24(np->fp_fid);
0432         ids.port_name = ntohll(np->fp_wwpn);
0433 
0434         if (ids.port_id != lport->port_id &&
0435             ids.port_name != lport->wwpn) {
0436             rdata = fc_rport_create(lport, ids.port_id);
0437             if (rdata) {
0438                 rdata->ids.port_name = ids.port_name;
0439                 rdata->disc_id = disc->disc_id;
0440             } else {
0441                 printk(KERN_WARNING "libfc: Failed to allocate "
0442                        "memory for the newly discovered port "
0443                        "(%6.6x)\n", ids.port_id);
0444                 error = -ENOMEM;
0445             }
0446         }
0447 
0448         if (np->fp_flags & FC_NS_FID_LAST) {
0449             fc_disc_done(disc, DISC_EV_SUCCESS);
0450             len = 0;
0451             break;
0452         }
0453         len -= sizeof(*np);
0454         bp += sizeof(*np);
0455         np = (struct fc_gpn_ft_resp *)bp;
0456         plen = len;
0457     }
0458 
0459     /*
0460      * Save any partial record at the end of the buffer for next time.
0461      */
0462     if (error == 0 && len > 0 && len < sizeof(*np)) {
0463         if (np != &disc->partial_buf) {
0464             FC_DISC_DBG(disc, "Partial buffer remains "
0465                     "for discovery\n");
0466             memcpy(&disc->partial_buf, np, len);
0467         }
0468         disc->buf_len = (unsigned char) len;
0469     }
0470     return error;
0471 }
0472 
0473 /**
0474  * fc_disc_timeout() - Handler for discovery timeouts
0475  * @work: Structure holding discovery context that needs to retry discovery
0476  */
0477 static void fc_disc_timeout(struct work_struct *work)
0478 {
0479     struct fc_disc *disc = container_of(work,
0480                         struct fc_disc,
0481                         disc_work.work);
0482     mutex_lock(&disc->disc_mutex);
0483     fc_disc_gpn_ft_req(disc);
0484     mutex_unlock(&disc->disc_mutex);
0485 }
0486 
0487 /**
0488  * fc_disc_gpn_ft_resp() - Handle a response frame from Get Port Names (GPN_FT)
0489  * @sp:     The sequence that the GPN_FT response was received on
0490  * @fp:     The GPN_FT response frame
0491  * @disc_arg: The discovery context
0492  *
0493  * Locking Note: This function is called without disc mutex held, and
0494  *       should do all its processing with the mutex held
0495  */
0496 static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
0497                 void *disc_arg)
0498 {
0499     struct fc_disc *disc = disc_arg;
0500     struct fc_ct_hdr *cp;
0501     struct fc_frame_header *fh;
0502     enum fc_disc_event event = DISC_EV_NONE;
0503     unsigned int seq_cnt;
0504     unsigned int len;
0505     int error = 0;
0506 
0507     mutex_lock(&disc->disc_mutex);
0508     FC_DISC_DBG(disc, "Received a GPN_FT response\n");
0509 
0510     if (IS_ERR(fp)) {
0511         fc_disc_error(disc, fp);
0512         mutex_unlock(&disc->disc_mutex);
0513         return;
0514     }
0515 
0516     WARN_ON(!fc_frame_is_linear(fp));   /* buffer must be contiguous */
0517     fh = fc_frame_header_get(fp);
0518     len = fr_len(fp) - sizeof(*fh);
0519     seq_cnt = ntohs(fh->fh_seq_cnt);
0520     if (fr_sof(fp) == FC_SOF_I3 && seq_cnt == 0 && disc->seq_count == 0) {
0521         cp = fc_frame_payload_get(fp, sizeof(*cp));
0522         if (!cp) {
0523             FC_DISC_DBG(disc, "GPN_FT response too short, len %d\n",
0524                     fr_len(fp));
0525             event = DISC_EV_FAILED;
0526         } else if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
0527 
0528             /* Accepted, parse the response. */
0529             len -= sizeof(*cp);
0530             error = fc_disc_gpn_ft_parse(disc, cp + 1, len);
0531         } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
0532             FC_DISC_DBG(disc, "GPN_FT rejected reason %x exp %x "
0533                     "(check zoning)\n", cp->ct_reason,
0534                     cp->ct_explan);
0535             event = DISC_EV_FAILED;
0536             if (cp->ct_reason == FC_FS_RJT_UNABL &&
0537                 cp->ct_explan == FC_FS_EXP_FTNR)
0538                 event = DISC_EV_SUCCESS;
0539         } else {
0540             FC_DISC_DBG(disc, "GPN_FT unexpected response code "
0541                     "%x\n", ntohs(cp->ct_cmd));
0542             event = DISC_EV_FAILED;
0543         }
0544     } else if (fr_sof(fp) == FC_SOF_N3 && seq_cnt == disc->seq_count) {
0545         error = fc_disc_gpn_ft_parse(disc, fh + 1, len);
0546     } else {
0547         FC_DISC_DBG(disc, "GPN_FT unexpected frame - out of sequence? "
0548                 "seq_cnt %x expected %x sof %x eof %x\n",
0549                 seq_cnt, disc->seq_count, fr_sof(fp), fr_eof(fp));
0550         event = DISC_EV_FAILED;
0551     }
0552     if (error)
0553         fc_disc_error(disc, ERR_PTR(error));
0554     else if (event != DISC_EV_NONE)
0555         fc_disc_done(disc, event);
0556     fc_frame_free(fp);
0557     mutex_unlock(&disc->disc_mutex);
0558 }
0559 
0560 /**
0561  * fc_disc_gpn_id_resp() - Handle a response frame from Get Port Names (GPN_ID)
0562  * @sp:        The sequence the GPN_ID is on
0563  * @fp:        The response frame
0564  * @rdata_arg: The remote port that sent the GPN_ID response
0565  *
0566  * Locking Note: This function is called without disc mutex held.
0567  */
0568 static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
0569                 void *rdata_arg)
0570 {
0571     struct fc_rport_priv *rdata = rdata_arg;
0572     struct fc_rport_priv *new_rdata;
0573     struct fc_lport *lport;
0574     struct fc_disc *disc;
0575     struct fc_ct_hdr *cp;
0576     struct fc_ns_gid_pn *pn;
0577     u64 port_name;
0578 
0579     lport = rdata->local_port;
0580     disc = &lport->disc;
0581 
0582     if (PTR_ERR(fp) == -FC_EX_CLOSED)
0583         goto out;
0584     if (IS_ERR(fp)) {
0585         mutex_lock(&disc->disc_mutex);
0586         fc_disc_restart(disc);
0587         mutex_unlock(&disc->disc_mutex);
0588         goto out;
0589     }
0590 
0591     cp = fc_frame_payload_get(fp, sizeof(*cp));
0592     if (!cp)
0593         goto redisc;
0594     if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
0595         if (fr_len(fp) < sizeof(struct fc_frame_header) +
0596             sizeof(*cp) + sizeof(*pn))
0597             goto redisc;
0598         pn = (struct fc_ns_gid_pn *)(cp + 1);
0599         port_name = get_unaligned_be64(&pn->fn_wwpn);
0600         mutex_lock(&rdata->rp_mutex);
0601         if (rdata->ids.port_name == -1)
0602             rdata->ids.port_name = port_name;
0603         else if (rdata->ids.port_name != port_name) {
0604             FC_DISC_DBG(disc, "GPN_ID accepted.  WWPN changed. "
0605                     "Port-id %6.6x wwpn %16.16llx\n",
0606                     rdata->ids.port_id, port_name);
0607             mutex_unlock(&rdata->rp_mutex);
0608             fc_rport_logoff(rdata);
0609             mutex_lock(&lport->disc.disc_mutex);
0610             new_rdata = fc_rport_create(lport, rdata->ids.port_id);
0611             mutex_unlock(&lport->disc.disc_mutex);
0612             if (new_rdata) {
0613                 new_rdata->disc_id = disc->disc_id;
0614                 fc_rport_login(new_rdata);
0615             }
0616             goto free_fp;
0617         }
0618         rdata->disc_id = disc->disc_id;
0619         mutex_unlock(&rdata->rp_mutex);
0620         fc_rport_login(rdata);
0621     } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
0622         FC_DISC_DBG(disc, "GPN_ID rejected reason %x exp %x\n",
0623                 cp->ct_reason, cp->ct_explan);
0624         fc_rport_logoff(rdata);
0625     } else {
0626         FC_DISC_DBG(disc, "GPN_ID unexpected response code %x\n",
0627                 ntohs(cp->ct_cmd));
0628 redisc:
0629         mutex_lock(&disc->disc_mutex);
0630         fc_disc_restart(disc);
0631         mutex_unlock(&disc->disc_mutex);
0632     }
0633 free_fp:
0634     fc_frame_free(fp);
0635 out:
0636     kref_put(&rdata->kref, fc_rport_destroy);
0637 }
0638 
0639 /**
0640  * fc_disc_gpn_id_req() - Send Get Port Names by ID (GPN_ID) request
0641  * @lport: The local port to initiate discovery on
0642  * @rdata: remote port private data
0643  *
0644  * On failure, an error code is returned.
0645  */
0646 static int fc_disc_gpn_id_req(struct fc_lport *lport,
0647                   struct fc_rport_priv *rdata)
0648 {
0649     struct fc_frame *fp;
0650 
0651     lockdep_assert_held(&lport->disc.disc_mutex);
0652     fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
0653                 sizeof(struct fc_ns_fid));
0654     if (!fp)
0655         return -ENOMEM;
0656     if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, FC_NS_GPN_ID,
0657                   fc_disc_gpn_id_resp, rdata,
0658                   3 * lport->r_a_tov))
0659         return -ENOMEM;
0660     kref_get(&rdata->kref);
0661     return 0;
0662 }
0663 
0664 /**
0665  * fc_disc_single() - Discover the directory information for a single target
0666  * @lport: The local port the remote port is associated with
0667  * @dp:    The port to rediscover
0668  */
0669 static int fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp)
0670 {
0671     struct fc_rport_priv *rdata;
0672 
0673     lockdep_assert_held(&lport->disc.disc_mutex);
0674 
0675     rdata = fc_rport_create(lport, dp->port_id);
0676     if (!rdata)
0677         return -ENOMEM;
0678     rdata->disc_id = 0;
0679     return fc_disc_gpn_id_req(lport, rdata);
0680 }
0681 
0682 /**
0683  * fc_disc_stop() - Stop discovery for a given lport
0684  * @lport: The local port that discovery should stop on
0685  */
0686 static void fc_disc_stop(struct fc_lport *lport)
0687 {
0688     struct fc_disc *disc = &lport->disc;
0689 
0690     if (disc->pending)
0691         cancel_delayed_work_sync(&disc->disc_work);
0692     mutex_lock(&disc->disc_mutex);
0693     fc_disc_stop_rports(disc);
0694     mutex_unlock(&disc->disc_mutex);
0695 }
0696 
0697 /**
0698  * fc_disc_stop_final() - Stop discovery for a given lport
0699  * @lport: The lport that discovery should stop on
0700  *
0701  * This function will block until discovery has been
0702  * completely stopped and all rports have been deleted.
0703  */
0704 static void fc_disc_stop_final(struct fc_lport *lport)
0705 {
0706     fc_disc_stop(lport);
0707     fc_rport_flush_queue();
0708 }
0709 
0710 /**
0711  * fc_disc_config() - Configure the discovery layer for a local port
0712  * @lport: The local port that needs the discovery layer to be configured
0713  * @priv: Private data structre for users of the discovery layer
0714  */
0715 void fc_disc_config(struct fc_lport *lport, void *priv)
0716 {
0717     struct fc_disc *disc;
0718 
0719     if (!lport->tt.disc_start)
0720         lport->tt.disc_start = fc_disc_start;
0721 
0722     if (!lport->tt.disc_stop)
0723         lport->tt.disc_stop = fc_disc_stop;
0724 
0725     if (!lport->tt.disc_stop_final)
0726         lport->tt.disc_stop_final = fc_disc_stop_final;
0727 
0728     if (!lport->tt.disc_recv_req)
0729         lport->tt.disc_recv_req = fc_disc_recv_req;
0730 
0731     disc = &lport->disc;
0732 
0733     disc->priv = priv;
0734 }
0735 EXPORT_SYMBOL(fc_disc_config);
0736 
0737 /**
0738  * fc_disc_init() - Initialize the discovery layer for a local port
0739  * @lport: The local port that needs the discovery layer to be initialized
0740  */
0741 void fc_disc_init(struct fc_lport *lport)
0742 {
0743     struct fc_disc *disc = &lport->disc;
0744 
0745     INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
0746     mutex_init(&disc->disc_mutex);
0747     INIT_LIST_HEAD(&disc->rports);
0748 }
0749 EXPORT_SYMBOL(fc_disc_init);