1a61127c2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
242e9a92fSRobert Love /*
342e9a92fSRobert Love * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
442e9a92fSRobert Love *
542e9a92fSRobert Love * Maintained at www.Open-FCoE.org
642e9a92fSRobert Love */
742e9a92fSRobert Love
842e9a92fSRobert Love /*
942e9a92fSRobert Love * Target Discovery
1042e9a92fSRobert Love *
1142e9a92fSRobert Love * This block discovers all FC-4 remote ports, including FCP initiators. It
1242e9a92fSRobert Love * also handles RSCN events and re-discovery if necessary.
1342e9a92fSRobert Love */
1442e9a92fSRobert Love
1542e9a92fSRobert Love /*
1642e9a92fSRobert Love * DISC LOCKING
1742e9a92fSRobert Love *
1842e9a92fSRobert Love * The disc mutex is can be locked when acquiring rport locks, but may not
1942e9a92fSRobert Love * be held when acquiring the lport lock. Refer to fc_lport.c for more
2042e9a92fSRobert Love * details.
2142e9a92fSRobert Love */
2242e9a92fSRobert Love
2342e9a92fSRobert Love #include <linux/timer.h>
245a0e3ad6STejun Heo #include <linux/slab.h>
2542e9a92fSRobert Love #include <linux/err.h>
2609703660SPaul Gortmaker #include <linux/export.h>
27*6456ab5dSChristophe JAILLET #include <linux/list.h>
28b2d09103SIngo Molnar
2942e9a92fSRobert Love #include <asm/unaligned.h>
3042e9a92fSRobert Love
3142e9a92fSRobert Love #include <scsi/fc/fc_gs.h>
3242e9a92fSRobert Love
3342e9a92fSRobert Love #include <scsi/libfc.h>
3442e9a92fSRobert Love
358866a5d9SRobert Love #include "fc_libfc.h"
368866a5d9SRobert Love
3742e9a92fSRobert Love #define FC_DISC_RETRY_LIMIT 3 /* max retries */
3842e9a92fSRobert Love #define FC_DISC_RETRY_DELAY 500UL /* (msecs) delay */
3942e9a92fSRobert Love
4042e9a92fSRobert Love static void fc_disc_gpn_ft_req(struct fc_disc *);
4142e9a92fSRobert Love static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *);
42786681b9SJoe Eykholt static void fc_disc_done(struct fc_disc *, enum fc_disc_event);
4342e9a92fSRobert Love static void fc_disc_timeout(struct work_struct *);
442ab7e1ecSJoe Eykholt static int fc_disc_single(struct fc_lport *, struct fc_disc_port *);
4542e9a92fSRobert Love static void fc_disc_restart(struct fc_disc *);
4642e9a92fSRobert Love
4742e9a92fSRobert Love /**
483a3b42bfSRobert Love * fc_disc_stop_rports() - Delete all the remote ports associated with the lport
493a3b42bfSRobert Love * @disc: The discovery job to stop remote ports on
5042e9a92fSRobert Love */
fc_disc_stop_rports(struct fc_disc * disc)51c6b21c93SBart Van Assche static void fc_disc_stop_rports(struct fc_disc *disc)
5242e9a92fSRobert Love {
5342e90414SJoe Eykholt struct fc_rport_priv *rdata;
5442e9a92fSRobert Love
55bc3d12b7SHannes Reinecke lockdep_assert_held(&disc->disc_mutex);
5642e9a92fSRobert Love
57bc3d12b7SHannes Reinecke list_for_each_entry(rdata, &disc->rports, peers) {
58a407c593SHannes Reinecke if (kref_get_unless_zero(&rdata->kref)) {
59c96c792aSHannes Reinecke fc_rport_logoff(rdata);
60944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
61a407c593SHannes Reinecke }
62a407c593SHannes Reinecke }
6342e9a92fSRobert Love }
6442e9a92fSRobert Love
6542e9a92fSRobert Love /**
6634f42a07SRobert Love * fc_disc_recv_rscn_req() - Handle Registered State Change Notification (RSCN)
6792261156SJoe Eykholt * @disc: The discovery object to which the RSCN applies
683a3b42bfSRobert Love * @fp: The RSCN frame
6942e9a92fSRobert Love */
fc_disc_recv_rscn_req(struct fc_disc * disc,struct fc_frame * fp)7092261156SJoe Eykholt static void fc_disc_recv_rscn_req(struct fc_disc *disc, struct fc_frame *fp)
7142e9a92fSRobert Love {
7242e9a92fSRobert Love struct fc_lport *lport;
7342e9a92fSRobert Love struct fc_els_rscn *rp;
7442e9a92fSRobert Love struct fc_els_rscn_page *pp;
7542e9a92fSRobert Love struct fc_seq_els_data rjt_data;
7642e9a92fSRobert Love unsigned int len;
7742e9a92fSRobert Love int redisc = 0;
7842e9a92fSRobert Love enum fc_els_rscn_addr_fmt fmt;
7942e9a92fSRobert Love LIST_HEAD(disc_ports);
8042e9a92fSRobert Love struct fc_disc_port *dp, *next;
8142e9a92fSRobert Love
82ee35624eSHannes Reinecke lockdep_assert_held(&disc->disc_mutex);
83ee35624eSHannes Reinecke
840685230cSJoe Eykholt lport = fc_disc_lport(disc);
8542e9a92fSRobert Love
867414705eSRobert Love FC_DISC_DBG(disc, "Received an RSCN event\n");
8742e9a92fSRobert Love
8842e9a92fSRobert Love /* make sure the frame contains an RSCN message */
8942e9a92fSRobert Love rp = fc_frame_payload_get(fp, sizeof(*rp));
9042e9a92fSRobert Love if (!rp)
9142e9a92fSRobert Love goto reject;
9242e9a92fSRobert Love /* make sure the page length is as expected (4 bytes) */
9342e9a92fSRobert Love if (rp->rscn_page_len != sizeof(*pp))
9442e9a92fSRobert Love goto reject;
9542e9a92fSRobert Love /* get the RSCN payload length */
9642e9a92fSRobert Love len = ntohs(rp->rscn_plen);
9742e9a92fSRobert Love if (len < sizeof(*rp))
9842e9a92fSRobert Love goto reject;
9942e9a92fSRobert Love /* make sure the frame contains the expected payload */
10042e9a92fSRobert Love rp = fc_frame_payload_get(fp, len);
10142e9a92fSRobert Love if (!rp)
10242e9a92fSRobert Love goto reject;
10342e9a92fSRobert Love /* payload must be a multiple of the RSCN page size */
10442e9a92fSRobert Love len -= sizeof(*rp);
10542e9a92fSRobert Love if (len % sizeof(*pp))
10642e9a92fSRobert Love goto reject;
10742e9a92fSRobert Love
10842e9a92fSRobert Love for (pp = (void *)(rp + 1); len > 0; len -= sizeof(*pp), pp++) {
10942e9a92fSRobert Love fmt = pp->rscn_page_flags >> ELS_RSCN_ADDR_FMT_BIT;
11042e9a92fSRobert Love fmt &= ELS_RSCN_ADDR_FMT_MASK;
11142e9a92fSRobert Love /*
11242e9a92fSRobert Love * if we get an address format other than port
11342e9a92fSRobert Love * (area, domain, fabric), then do a full discovery
11442e9a92fSRobert Love */
11542e9a92fSRobert Love switch (fmt) {
11642e9a92fSRobert Love case ELS_ADDR_FMT_PORT:
1177414705eSRobert Love FC_DISC_DBG(disc, "Port address format for port "
118ce8b5df0SChris Leech "(%6.6x)\n", ntoh24(pp->rscn_fid));
11942e9a92fSRobert Love dp = kzalloc(sizeof(*dp), GFP_KERNEL);
12042e9a92fSRobert Love if (!dp) {
12142e9a92fSRobert Love redisc = 1;
12242e9a92fSRobert Love break;
12342e9a92fSRobert Love }
12442e9a92fSRobert Love dp->lp = lport;
1259737e6a7SRobert Love dp->port_id = ntoh24(pp->rscn_fid);
12642e9a92fSRobert Love list_add_tail(&dp->peers, &disc_ports);
12742e9a92fSRobert Love break;
12842e9a92fSRobert Love case ELS_ADDR_FMT_AREA:
12942e9a92fSRobert Love case ELS_ADDR_FMT_DOM:
13042e9a92fSRobert Love case ELS_ADDR_FMT_FAB:
13142e9a92fSRobert Love default:
1327414705eSRobert Love FC_DISC_DBG(disc, "Address format is (%d)\n", fmt);
13342e9a92fSRobert Love redisc = 1;
13442e9a92fSRobert Love break;
13542e9a92fSRobert Love }
13642e9a92fSRobert Love }
1377ab24dd1SHannes Reinecke fc_seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
1382ab7e1ecSJoe Eykholt
1392ab7e1ecSJoe Eykholt /*
1402ab7e1ecSJoe Eykholt * If not doing a complete rediscovery, do GPN_ID on
1412ab7e1ecSJoe Eykholt * the individual ports mentioned in the list.
1422ab7e1ecSJoe Eykholt * If any of these get an error, do a full rediscovery.
1432ab7e1ecSJoe Eykholt * In any case, go through the list and free the entries.
1442ab7e1ecSJoe Eykholt */
1452ab7e1ecSJoe Eykholt list_for_each_entry_safe(dp, next, &disc_ports, peers) {
1462ab7e1ecSJoe Eykholt list_del(&dp->peers);
1472ab7e1ecSJoe Eykholt if (!redisc)
1482ab7e1ecSJoe Eykholt redisc = fc_disc_single(lport, dp);
1492ab7e1ecSJoe Eykholt kfree(dp);
1502ab7e1ecSJoe Eykholt }
15142e9a92fSRobert Love if (redisc) {
1527414705eSRobert Love FC_DISC_DBG(disc, "RSCN received: rediscovering\n");
15342e9a92fSRobert Love fc_disc_restart(disc);
15442e9a92fSRobert Love } else {
1557414705eSRobert Love FC_DISC_DBG(disc, "RSCN received: not rediscovering. "
15642e9a92fSRobert Love "redisc %d state %d in_prog %d\n",
15742e9a92fSRobert Love redisc, lport->state, disc->pending);
15842e9a92fSRobert Love }
15942e9a92fSRobert Love fc_frame_free(fp);
16042e9a92fSRobert Love return;
16142e9a92fSRobert Love reject:
1627414705eSRobert Love FC_DISC_DBG(disc, "Received a bad RSCN frame\n");
16342e9a92fSRobert Love rjt_data.reason = ELS_RJT_LOGIC;
16442e9a92fSRobert Love rjt_data.explan = ELS_EXPL_NONE;
1657ab24dd1SHannes Reinecke fc_seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data);
16642e9a92fSRobert Love fc_frame_free(fp);
16742e9a92fSRobert Love }
16842e9a92fSRobert Love
16942e9a92fSRobert Love /**
17034f42a07SRobert Love * fc_disc_recv_req() - Handle incoming requests
1713a3b42bfSRobert Love * @lport: The local port receiving the request
17292261156SJoe Eykholt * @fp: The request frame
17342e9a92fSRobert Love *
17442e9a92fSRobert Love * Locking Note: This function is called from the EM and will lock
17542e9a92fSRobert Love * the disc_mutex before calling the handler for the
17642e9a92fSRobert Love * request.
17742e9a92fSRobert Love */
fc_disc_recv_req(struct fc_lport * lport,struct fc_frame * fp)17892261156SJoe Eykholt static void fc_disc_recv_req(struct fc_lport *lport, struct fc_frame *fp)
17942e9a92fSRobert Love {
18042e9a92fSRobert Love u8 op;
18142e9a92fSRobert Love struct fc_disc *disc = &lport->disc;
18242e9a92fSRobert Love
18342e9a92fSRobert Love op = fc_frame_payload_op(fp);
18442e9a92fSRobert Love switch (op) {
18542e9a92fSRobert Love case ELS_RSCN:
18642e9a92fSRobert Love mutex_lock(&disc->disc_mutex);
18792261156SJoe Eykholt fc_disc_recv_rscn_req(disc, fp);
18842e9a92fSRobert Love mutex_unlock(&disc->disc_mutex);
18942e9a92fSRobert Love break;
19042e9a92fSRobert Love default:
1917414705eSRobert Love FC_DISC_DBG(disc, "Received an unsupported request, "
1927414705eSRobert Love "the opcode is (%x)\n", op);
19383383dd1SHillf Danton fc_frame_free(fp);
19442e9a92fSRobert Love break;
19542e9a92fSRobert Love }
19642e9a92fSRobert Love }
19742e9a92fSRobert Love
19842e9a92fSRobert Love /**
19934f42a07SRobert Love * fc_disc_restart() - Restart discovery
2003a3b42bfSRobert Love * @disc: The discovery object to be restarted
20142e9a92fSRobert Love */
fc_disc_restart(struct fc_disc * disc)20242e9a92fSRobert Love static void fc_disc_restart(struct fc_disc *disc)
20342e9a92fSRobert Love {
204ee35624eSHannes Reinecke lockdep_assert_held(&disc->disc_mutex);
205ee35624eSHannes Reinecke
206935d0fceSJoe Eykholt if (!disc->disc_callback)
207935d0fceSJoe Eykholt return;
208935d0fceSJoe Eykholt
2097414705eSRobert Love FC_DISC_DBG(disc, "Restarting discovery\n");
21042e9a92fSRobert Love
21142e9a92fSRobert Love disc->requested = 1;
2120f6c6149SJoe Eykholt if (disc->pending)
2130f6c6149SJoe Eykholt return;
2140f6c6149SJoe Eykholt
2150f6c6149SJoe Eykholt /*
2160f6c6149SJoe Eykholt * Advance disc_id. This is an arbitrary non-zero number that will
2170f6c6149SJoe Eykholt * match the value in the fc_rport_priv after discovery for all
2180f6c6149SJoe Eykholt * freshly-discovered remote ports. Avoid wrapping to zero.
2190f6c6149SJoe Eykholt */
2200f6c6149SJoe Eykholt disc->disc_id = (disc->disc_id + 2) | 1;
2213667d7e7SJoe Eykholt disc->retry_count = 0;
22242e9a92fSRobert Love fc_disc_gpn_ft_req(disc);
22342e9a92fSRobert Love }
22442e9a92fSRobert Love
22542e9a92fSRobert Love /**
2263a3b42bfSRobert Love * fc_disc_start() - Start discovery on a local port
2273a3b42bfSRobert Love * @lport: The local port to have discovery started on
2283a3b42bfSRobert Love * @disc_callback: Callback function to be called when discovery is complete
22942e9a92fSRobert Love */
fc_disc_start(void (* disc_callback)(struct fc_lport *,enum fc_disc_event),struct fc_lport * lport)23042e9a92fSRobert Love static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
23142e9a92fSRobert Love enum fc_disc_event),
23242e9a92fSRobert Love struct fc_lport *lport)
23342e9a92fSRobert Love {
23442e9a92fSRobert Love struct fc_disc *disc = &lport->disc;
23542e9a92fSRobert Love
23642e9a92fSRobert Love /*
23742e9a92fSRobert Love * At this point we may have a new disc job or an existing
23842e9a92fSRobert Love * one. Either way, let's lock when we make changes to it
23942e9a92fSRobert Love * and send the GPN_FT request.
24042e9a92fSRobert Love */
24142e9a92fSRobert Love mutex_lock(&disc->disc_mutex);
24242e9a92fSRobert Love disc->disc_callback = disc_callback;
24329d898e9SJoe Eykholt fc_disc_restart(disc);
24442e9a92fSRobert Love mutex_unlock(&disc->disc_mutex);
24542e9a92fSRobert Love }
24642e9a92fSRobert Love
24742e9a92fSRobert Love /**
24834f42a07SRobert Love * fc_disc_done() - Discovery has been completed
2493a3b42bfSRobert Love * @disc: The discovery context
2503a3b42bfSRobert Love * @event: The discovery completion status
25142e9a92fSRobert Love */
fc_disc_done(struct fc_disc * disc,enum fc_disc_event event)252786681b9SJoe Eykholt static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
25342e9a92fSRobert Love {
2540685230cSJoe Eykholt struct fc_lport *lport = fc_disc_lport(disc);
2550f6c6149SJoe Eykholt struct fc_rport_priv *rdata;
25642e9a92fSRobert Love
257ee35624eSHannes Reinecke lockdep_assert_held(&disc->disc_mutex);
2587414705eSRobert Love FC_DISC_DBG(disc, "Discovery complete\n");
25942e9a92fSRobert Love
26042e9a92fSRobert Love disc->pending = 0;
2610f6c6149SJoe Eykholt if (disc->requested) {
2620f6c6149SJoe Eykholt fc_disc_restart(disc);
2630f6c6149SJoe Eykholt return;
2640f6c6149SJoe Eykholt }
2650f6c6149SJoe Eykholt
2660f6c6149SJoe Eykholt /*
2670f6c6149SJoe Eykholt * Go through all remote ports. If they were found in the latest
2680f6c6149SJoe Eykholt * discovery, reverify or log them in. Otherwise, log them out.
2690f6c6149SJoe Eykholt * Skip ports which were never discovered. These are the dNS port
2700f6c6149SJoe Eykholt * and ports which were created by PLOGI.
271fa519f70SHannes Reinecke *
272fa519f70SHannes Reinecke * We don't need to use the _rcu variant here as the rport list
273fa519f70SHannes Reinecke * is protected by the disc mutex which is already held on entry.
2740f6c6149SJoe Eykholt */
275fa519f70SHannes Reinecke list_for_each_entry(rdata, &disc->rports, peers) {
276a407c593SHannes Reinecke if (!kref_get_unless_zero(&rdata->kref))
2770f6c6149SJoe Eykholt continue;
278a407c593SHannes Reinecke if (rdata->disc_id) {
2790f6c6149SJoe Eykholt if (rdata->disc_id == disc->disc_id)
28005d7d3b0SHannes Reinecke fc_rport_login(rdata);
2810f6c6149SJoe Eykholt else
282c96c792aSHannes Reinecke fc_rport_logoff(rdata);
2830f6c6149SJoe Eykholt }
284944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
285a407c593SHannes Reinecke }
2860d228c0fSAbhijeet Joglekar mutex_unlock(&disc->disc_mutex);
2870d228c0fSAbhijeet Joglekar disc->disc_callback(lport, event);
2880d228c0fSAbhijeet Joglekar mutex_lock(&disc->disc_mutex);
28942e9a92fSRobert Love }
29042e9a92fSRobert Love
29142e9a92fSRobert Love /**
29234f42a07SRobert Love * fc_disc_error() - Handle error on dNS request
2933a3b42bfSRobert Love * @disc: The discovery context
2943a3b42bfSRobert Love * @fp: The error code encoded as a frame pointer
29542e9a92fSRobert Love */
fc_disc_error(struct fc_disc * disc,struct fc_frame * fp)29642e9a92fSRobert Love static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
29742e9a92fSRobert Love {
2980685230cSJoe Eykholt struct fc_lport *lport = fc_disc_lport(disc);
29942e9a92fSRobert Love unsigned long delay = 0;
3007414705eSRobert Love
301a9e81c29SYueHaibing FC_DISC_DBG(disc, "Error %d, retries %d/%d\n",
302a9e81c29SYueHaibing PTR_ERR_OR_ZERO(fp), disc->retry_count,
30342e9a92fSRobert Love FC_DISC_RETRY_LIMIT);
30442e9a92fSRobert Love
30542e9a92fSRobert Love if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
30642e9a92fSRobert Love /*
30742e9a92fSRobert Love * Memory allocation failure, or the exchange timed out,
30842e9a92fSRobert Love * retry after delay.
30942e9a92fSRobert Love */
31042e9a92fSRobert Love if (disc->retry_count < FC_DISC_RETRY_LIMIT) {
31142e9a92fSRobert Love /* go ahead and retry */
31242e9a92fSRobert Love if (!fp)
31342e9a92fSRobert Love delay = msecs_to_jiffies(FC_DISC_RETRY_DELAY);
31442e9a92fSRobert Love else {
31542e9a92fSRobert Love delay = msecs_to_jiffies(lport->e_d_tov);
31642e9a92fSRobert Love
31742e9a92fSRobert Love /* timeout faster first time */
31842e9a92fSRobert Love if (!disc->retry_count)
31942e9a92fSRobert Love delay /= 4;
32042e9a92fSRobert Love }
32142e9a92fSRobert Love disc->retry_count++;
32242e9a92fSRobert Love schedule_delayed_work(&disc->disc_work, delay);
323786681b9SJoe Eykholt } else
324786681b9SJoe Eykholt fc_disc_done(disc, DISC_EV_FAILED);
32500832084SBhanu Prakash Gollapudi } else if (PTR_ERR(fp) == -FC_EX_CLOSED) {
32600832084SBhanu Prakash Gollapudi /*
32700832084SBhanu Prakash Gollapudi * if discovery fails due to lport reset, clear
32800832084SBhanu Prakash Gollapudi * pending flag so that subsequent discovery can
32900832084SBhanu Prakash Gollapudi * continue
33000832084SBhanu Prakash Gollapudi */
33100832084SBhanu Prakash Gollapudi disc->pending = 0;
33242e9a92fSRobert Love }
33342e9a92fSRobert Love }
33442e9a92fSRobert Love
33542e9a92fSRobert Love /**
33634f42a07SRobert Love * fc_disc_gpn_ft_req() - Send Get Port Names by FC-4 type (GPN_FT) request
337b1987c88SLee Jones * @disc: The discovery context
33842e9a92fSRobert Love */
fc_disc_gpn_ft_req(struct fc_disc * disc)33942e9a92fSRobert Love static void fc_disc_gpn_ft_req(struct fc_disc *disc)
34042e9a92fSRobert Love {
34142e9a92fSRobert Love struct fc_frame *fp;
3420685230cSJoe Eykholt struct fc_lport *lport = fc_disc_lport(disc);
34342e9a92fSRobert Love
344ee35624eSHannes Reinecke lockdep_assert_held(&disc->disc_mutex);
345ee35624eSHannes Reinecke
34642e9a92fSRobert Love WARN_ON(!fc_lport_test_ready(lport));
34742e9a92fSRobert Love
34842e9a92fSRobert Love disc->pending = 1;
34942e9a92fSRobert Love disc->requested = 0;
35042e9a92fSRobert Love
35142e9a92fSRobert Love disc->buf_len = 0;
35242e9a92fSRobert Love disc->seq_count = 0;
35342e9a92fSRobert Love fp = fc_frame_alloc(lport,
35442e9a92fSRobert Love sizeof(struct fc_ct_hdr) +
35542e9a92fSRobert Love sizeof(struct fc_ns_gid_ft));
35642e9a92fSRobert Love if (!fp)
35742e9a92fSRobert Love goto err;
35842e9a92fSRobert Love
359a46f327aSJoe Eykholt if (lport->tt.elsct_send(lport, 0, fp,
36042e9a92fSRobert Love FC_NS_GPN_FT,
36142e9a92fSRobert Love fc_disc_gpn_ft_resp,
362b94f8951SJoe Eykholt disc, 3 * lport->r_a_tov))
36342e9a92fSRobert Love return;
36442e9a92fSRobert Love err:
3658f550f93SChris Leech fc_disc_error(disc, NULL);
36642e9a92fSRobert Love }
36742e9a92fSRobert Love
36842e9a92fSRobert Love /**
369786681b9SJoe Eykholt * fc_disc_gpn_ft_parse() - Parse the body of the dNS GPN_FT response.
370ee9ec5c9SLee Jones * @disc: The discovery context
3713a3b42bfSRobert Love * @buf: The GPN_FT response buffer
3723a3b42bfSRobert Love * @len: The size of response buffer
373786681b9SJoe Eykholt *
374786681b9SJoe Eykholt * Goes through the list of IDs and names resulting from a request.
37542e9a92fSRobert Love */
fc_disc_gpn_ft_parse(struct fc_disc * disc,void * buf,size_t len)37642e9a92fSRobert Love static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
37742e9a92fSRobert Love {
37842e9a92fSRobert Love struct fc_lport *lport;
37942e9a92fSRobert Love struct fc_gpn_ft_resp *np;
38042e9a92fSRobert Love char *bp;
38142e9a92fSRobert Love size_t plen;
38242e9a92fSRobert Love size_t tlen;
38342e9a92fSRobert Love int error = 0;
384795d86f5SJoe Eykholt struct fc_rport_identifiers ids;
385ab28f1fdSJoe Eykholt struct fc_rport_priv *rdata;
38642e9a92fSRobert Love
3870685230cSJoe Eykholt lport = fc_disc_lport(disc);
388a1c1e4e7SJoe Eykholt disc->seq_count++;
38942e9a92fSRobert Love
39042e9a92fSRobert Love /*
39142e9a92fSRobert Love * Handle partial name record left over from previous call.
39242e9a92fSRobert Love */
39342e9a92fSRobert Love bp = buf;
39442e9a92fSRobert Love plen = len;
39542e9a92fSRobert Love np = (struct fc_gpn_ft_resp *)bp;
39642e9a92fSRobert Love tlen = disc->buf_len;
39781a67b97SJoe Eykholt disc->buf_len = 0;
39842e9a92fSRobert Love if (tlen) {
39942e9a92fSRobert Love WARN_ON(tlen >= sizeof(*np));
40042e9a92fSRobert Love plen = sizeof(*np) - tlen;
40142e9a92fSRobert Love WARN_ON(plen <= 0);
40242e9a92fSRobert Love WARN_ON(plen >= sizeof(*np));
40342e9a92fSRobert Love if (plen > len)
40442e9a92fSRobert Love plen = len;
40542e9a92fSRobert Love np = &disc->partial_buf;
40642e9a92fSRobert Love memcpy((char *)np + tlen, bp, plen);
40742e9a92fSRobert Love
40842e9a92fSRobert Love /*
40942e9a92fSRobert Love * Set bp so that the loop below will advance it to the
41042e9a92fSRobert Love * first valid full name element.
41142e9a92fSRobert Love */
41242e9a92fSRobert Love bp -= tlen;
41342e9a92fSRobert Love len += tlen;
41442e9a92fSRobert Love plen += tlen;
41542e9a92fSRobert Love disc->buf_len = (unsigned char) plen;
41642e9a92fSRobert Love if (plen == sizeof(*np))
41742e9a92fSRobert Love disc->buf_len = 0;
41842e9a92fSRobert Love }
41942e9a92fSRobert Love
42042e9a92fSRobert Love /*
42142e9a92fSRobert Love * Handle full name records, including the one filled from above.
42242e9a92fSRobert Love * Normally, np == bp and plen == len, but from the partial case above,
42342e9a92fSRobert Love * bp, len describe the overall buffer, and np, plen describe the
42442e9a92fSRobert Love * partial buffer, which if would usually be full now.
42542e9a92fSRobert Love * After the first time through the loop, things return to "normal".
42642e9a92fSRobert Love */
42742e9a92fSRobert Love while (plen >= sizeof(*np)) {
428795d86f5SJoe Eykholt ids.port_id = ntoh24(np->fp_fid);
429795d86f5SJoe Eykholt ids.port_name = ntohll(np->fp_wwpn);
43042e9a92fSRobert Love
4317b2787ecSRobert Love if (ids.port_id != lport->port_id &&
432795d86f5SJoe Eykholt ids.port_name != lport->wwpn) {
4332580064bSHannes Reinecke rdata = fc_rport_create(lport, ids.port_id);
4349737e6a7SRobert Love if (rdata) {
4359737e6a7SRobert Love rdata->ids.port_name = ids.port_name;
4360f6c6149SJoe Eykholt rdata->disc_id = disc->disc_id;
4379737e6a7SRobert Love } else {
4387414705eSRobert Love printk(KERN_WARNING "libfc: Failed to allocate "
4397414705eSRobert Love "memory for the newly discovered port "
440ce8b5df0SChris Leech "(%6.6x)\n", ids.port_id);
44181a67b97SJoe Eykholt error = -ENOMEM;
44281a67b97SJoe Eykholt }
44342e9a92fSRobert Love }
44442e9a92fSRobert Love
44542e9a92fSRobert Love if (np->fp_flags & FC_NS_FID_LAST) {
446786681b9SJoe Eykholt fc_disc_done(disc, DISC_EV_SUCCESS);
44742e9a92fSRobert Love len = 0;
44842e9a92fSRobert Love break;
44942e9a92fSRobert Love }
45042e9a92fSRobert Love len -= sizeof(*np);
45142e9a92fSRobert Love bp += sizeof(*np);
45242e9a92fSRobert Love np = (struct fc_gpn_ft_resp *)bp;
45342e9a92fSRobert Love plen = len;
45442e9a92fSRobert Love }
45542e9a92fSRobert Love
45642e9a92fSRobert Love /*
45742e9a92fSRobert Love * Save any partial record at the end of the buffer for next time.
45842e9a92fSRobert Love */
45942e9a92fSRobert Love if (error == 0 && len > 0 && len < sizeof(*np)) {
46042e9a92fSRobert Love if (np != &disc->partial_buf) {
4617414705eSRobert Love FC_DISC_DBG(disc, "Partial buffer remains "
4627414705eSRobert Love "for discovery\n");
46342e9a92fSRobert Love memcpy(&disc->partial_buf, np, len);
46442e9a92fSRobert Love }
46542e9a92fSRobert Love disc->buf_len = (unsigned char) len;
46642e9a92fSRobert Love }
46742e9a92fSRobert Love return error;
46842e9a92fSRobert Love }
46942e9a92fSRobert Love
47034f42a07SRobert Love /**
4713a3b42bfSRobert Love * fc_disc_timeout() - Handler for discovery timeouts
4723a3b42bfSRobert Love * @work: Structure holding discovery context that needs to retry discovery
47342e9a92fSRobert Love */
fc_disc_timeout(struct work_struct * work)47442e9a92fSRobert Love static void fc_disc_timeout(struct work_struct *work)
47542e9a92fSRobert Love {
47642e9a92fSRobert Love struct fc_disc *disc = container_of(work,
47742e9a92fSRobert Love struct fc_disc,
47842e9a92fSRobert Love disc_work.work);
47942e9a92fSRobert Love mutex_lock(&disc->disc_mutex);
48042e9a92fSRobert Love fc_disc_gpn_ft_req(disc);
48142e9a92fSRobert Love mutex_unlock(&disc->disc_mutex);
48242e9a92fSRobert Love }
48342e9a92fSRobert Love
48442e9a92fSRobert Love /**
48534f42a07SRobert Love * fc_disc_gpn_ft_resp() - Handle a response frame from Get Port Names (GPN_FT)
4863a3b42bfSRobert Love * @sp: The sequence that the GPN_FT response was received on
4873a3b42bfSRobert Love * @fp: The GPN_FT response frame
488b1987c88SLee Jones * @disc_arg: The discovery context
48942e9a92fSRobert Love *
4900d228c0fSAbhijeet Joglekar * Locking Note: This function is called without disc mutex held, and
4910d228c0fSAbhijeet Joglekar * should do all its processing with the mutex held
49242e9a92fSRobert Love */
fc_disc_gpn_ft_resp(struct fc_seq * sp,struct fc_frame * fp,void * disc_arg)49342e9a92fSRobert Love static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
49442e9a92fSRobert Love void *disc_arg)
49542e9a92fSRobert Love {
49642e9a92fSRobert Love struct fc_disc *disc = disc_arg;
49742e9a92fSRobert Love struct fc_ct_hdr *cp;
49842e9a92fSRobert Love struct fc_frame_header *fh;
499a1c1e4e7SJoe Eykholt enum fc_disc_event event = DISC_EV_NONE;
50042e9a92fSRobert Love unsigned int seq_cnt;
50142e9a92fSRobert Love unsigned int len;
502a1c1e4e7SJoe Eykholt int error = 0;
50342e9a92fSRobert Love
5040d228c0fSAbhijeet Joglekar mutex_lock(&disc->disc_mutex);
5057414705eSRobert Love FC_DISC_DBG(disc, "Received a GPN_FT response\n");
50642e9a92fSRobert Love
50742e9a92fSRobert Love if (IS_ERR(fp)) {
50842e9a92fSRobert Love fc_disc_error(disc, fp);
5090d228c0fSAbhijeet Joglekar mutex_unlock(&disc->disc_mutex);
51042e9a92fSRobert Love return;
51142e9a92fSRobert Love }
51242e9a92fSRobert Love
51342e9a92fSRobert Love WARN_ON(!fc_frame_is_linear(fp)); /* buffer must be contiguous */
51442e9a92fSRobert Love fh = fc_frame_header_get(fp);
51542e9a92fSRobert Love len = fr_len(fp) - sizeof(*fh);
51642e9a92fSRobert Love seq_cnt = ntohs(fh->fh_seq_cnt);
517a1c1e4e7SJoe Eykholt if (fr_sof(fp) == FC_SOF_I3 && seq_cnt == 0 && disc->seq_count == 0) {
51842e9a92fSRobert Love cp = fc_frame_payload_get(fp, sizeof(*cp));
51942e9a92fSRobert Love if (!cp) {
5207414705eSRobert Love FC_DISC_DBG(disc, "GPN_FT response too short, len %d\n",
52142e9a92fSRobert Love fr_len(fp));
522883a337cSJoe Eykholt event = DISC_EV_FAILED;
52342e9a92fSRobert Love } else if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
52442e9a92fSRobert Love
52534f42a07SRobert Love /* Accepted, parse the response. */
52642e9a92fSRobert Love len -= sizeof(*cp);
527a1c1e4e7SJoe Eykholt error = fc_disc_gpn_ft_parse(disc, cp + 1, len);
52842e9a92fSRobert Love } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
5297414705eSRobert Love FC_DISC_DBG(disc, "GPN_FT rejected reason %x exp %x "
53042e9a92fSRobert Love "(check zoning)\n", cp->ct_reason,
53142e9a92fSRobert Love cp->ct_explan);
532a1c1e4e7SJoe Eykholt event = DISC_EV_FAILED;
533c762608bSJoe Eykholt if (cp->ct_reason == FC_FS_RJT_UNABL &&
534c762608bSJoe Eykholt cp->ct_explan == FC_FS_EXP_FTNR)
535c762608bSJoe Eykholt event = DISC_EV_SUCCESS;
53642e9a92fSRobert Love } else {
5377414705eSRobert Love FC_DISC_DBG(disc, "GPN_FT unexpected response code "
5387414705eSRobert Love "%x\n", ntohs(cp->ct_cmd));
539883a337cSJoe Eykholt event = DISC_EV_FAILED;
54042e9a92fSRobert Love }
541a1c1e4e7SJoe Eykholt } else if (fr_sof(fp) == FC_SOF_N3 && seq_cnt == disc->seq_count) {
542a1c1e4e7SJoe Eykholt error = fc_disc_gpn_ft_parse(disc, fh + 1, len);
54342e9a92fSRobert Love } else {
5447414705eSRobert Love FC_DISC_DBG(disc, "GPN_FT unexpected frame - out of sequence? "
54542e9a92fSRobert Love "seq_cnt %x expected %x sof %x eof %x\n",
54642e9a92fSRobert Love seq_cnt, disc->seq_count, fr_sof(fp), fr_eof(fp));
547883a337cSJoe Eykholt event = DISC_EV_FAILED;
54842e9a92fSRobert Love }
54942e9a92fSRobert Love if (error)
5506f37e210SDan Carpenter fc_disc_error(disc, ERR_PTR(error));
551a1c1e4e7SJoe Eykholt else if (event != DISC_EV_NONE)
552a1c1e4e7SJoe Eykholt fc_disc_done(disc, event);
55342e9a92fSRobert Love fc_frame_free(fp);
5540d228c0fSAbhijeet Joglekar mutex_unlock(&disc->disc_mutex);
55542e9a92fSRobert Love }
55642e9a92fSRobert Love
55742e9a92fSRobert Love /**
5582ab7e1ecSJoe Eykholt * fc_disc_gpn_id_resp() - Handle a response frame from Get Port Names (GPN_ID)
5593a3b42bfSRobert Love * @sp: The sequence the GPN_ID is on
5603a3b42bfSRobert Love * @fp: The response frame
5613a3b42bfSRobert Love * @rdata_arg: The remote port that sent the GPN_ID response
5622ab7e1ecSJoe Eykholt *
5632ab7e1ecSJoe Eykholt * Locking Note: This function is called without disc mutex held.
5642ab7e1ecSJoe Eykholt */
fc_disc_gpn_id_resp(struct fc_seq * sp,struct fc_frame * fp,void * rdata_arg)5652ab7e1ecSJoe Eykholt static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
5662ab7e1ecSJoe Eykholt void *rdata_arg)
5672ab7e1ecSJoe Eykholt {
5682ab7e1ecSJoe Eykholt struct fc_rport_priv *rdata = rdata_arg;
5692ab7e1ecSJoe Eykholt struct fc_rport_priv *new_rdata;
5702ab7e1ecSJoe Eykholt struct fc_lport *lport;
5712ab7e1ecSJoe Eykholt struct fc_disc *disc;
5722ab7e1ecSJoe Eykholt struct fc_ct_hdr *cp;
5732ab7e1ecSJoe Eykholt struct fc_ns_gid_pn *pn;
5742ab7e1ecSJoe Eykholt u64 port_name;
5752ab7e1ecSJoe Eykholt
5762ab7e1ecSJoe Eykholt lport = rdata->local_port;
5772ab7e1ecSJoe Eykholt disc = &lport->disc;
5782ab7e1ecSJoe Eykholt
5792ab7e1ecSJoe Eykholt if (PTR_ERR(fp) == -FC_EX_CLOSED)
5802ab7e1ecSJoe Eykholt goto out;
581ec007ef4SJaved Hasan if (IS_ERR(fp)) {
582ec007ef4SJaved Hasan mutex_lock(&disc->disc_mutex);
583ec007ef4SJaved Hasan fc_disc_restart(disc);
584ec007ef4SJaved Hasan mutex_unlock(&disc->disc_mutex);
585ec007ef4SJaved Hasan goto out;
586ec007ef4SJaved Hasan }
5872ab7e1ecSJoe Eykholt
5882ab7e1ecSJoe Eykholt cp = fc_frame_payload_get(fp, sizeof(*cp));
5892ab7e1ecSJoe Eykholt if (!cp)
5902ab7e1ecSJoe Eykholt goto redisc;
5912ab7e1ecSJoe Eykholt if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
5922ab7e1ecSJoe Eykholt if (fr_len(fp) < sizeof(struct fc_frame_header) +
5932ab7e1ecSJoe Eykholt sizeof(*cp) + sizeof(*pn))
5942ab7e1ecSJoe Eykholt goto redisc;
5952ab7e1ecSJoe Eykholt pn = (struct fc_ns_gid_pn *)(cp + 1);
5962ab7e1ecSJoe Eykholt port_name = get_unaligned_be64(&pn->fn_wwpn);
597a407c593SHannes Reinecke mutex_lock(&rdata->rp_mutex);
5982ab7e1ecSJoe Eykholt if (rdata->ids.port_name == -1)
5992ab7e1ecSJoe Eykholt rdata->ids.port_name = port_name;
6002ab7e1ecSJoe Eykholt else if (rdata->ids.port_name != port_name) {
6012ab7e1ecSJoe Eykholt FC_DISC_DBG(disc, "GPN_ID accepted. WWPN changed. "
6029f8f3aa6SChris Leech "Port-id %6.6x wwpn %16.16llx\n",
6032ab7e1ecSJoe Eykholt rdata->ids.port_id, port_name);
604a407c593SHannes Reinecke mutex_unlock(&rdata->rp_mutex);
605c96c792aSHannes Reinecke fc_rport_logoff(rdata);
606a407c593SHannes Reinecke mutex_lock(&lport->disc.disc_mutex);
6072580064bSHannes Reinecke new_rdata = fc_rport_create(lport, rdata->ids.port_id);
608a407c593SHannes Reinecke mutex_unlock(&lport->disc.disc_mutex);
6092ab7e1ecSJoe Eykholt if (new_rdata) {
6102ab7e1ecSJoe Eykholt new_rdata->disc_id = disc->disc_id;
61105d7d3b0SHannes Reinecke fc_rport_login(new_rdata);
6122ab7e1ecSJoe Eykholt }
613ec007ef4SJaved Hasan goto free_fp;
6142ab7e1ecSJoe Eykholt }
6152ab7e1ecSJoe Eykholt rdata->disc_id = disc->disc_id;
616a407c593SHannes Reinecke mutex_unlock(&rdata->rp_mutex);
61705d7d3b0SHannes Reinecke fc_rport_login(rdata);
6182ab7e1ecSJoe Eykholt } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
6192ab7e1ecSJoe Eykholt FC_DISC_DBG(disc, "GPN_ID rejected reason %x exp %x\n",
6202ab7e1ecSJoe Eykholt cp->ct_reason, cp->ct_explan);
621c96c792aSHannes Reinecke fc_rport_logoff(rdata);
6222ab7e1ecSJoe Eykholt } else {
6232ab7e1ecSJoe Eykholt FC_DISC_DBG(disc, "GPN_ID unexpected response code %x\n",
6242ab7e1ecSJoe Eykholt ntohs(cp->ct_cmd));
6252ab7e1ecSJoe Eykholt redisc:
626a407c593SHannes Reinecke mutex_lock(&disc->disc_mutex);
6272ab7e1ecSJoe Eykholt fc_disc_restart(disc);
628a407c593SHannes Reinecke mutex_unlock(&disc->disc_mutex);
6292ab7e1ecSJoe Eykholt }
630ec007ef4SJaved Hasan free_fp:
631ec007ef4SJaved Hasan fc_frame_free(fp);
6322ab7e1ecSJoe Eykholt out:
633944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
6342ab7e1ecSJoe Eykholt }
6352ab7e1ecSJoe Eykholt
6362ab7e1ecSJoe Eykholt /**
6372ab7e1ecSJoe Eykholt * fc_disc_gpn_id_req() - Send Get Port Names by ID (GPN_ID) request
6383a3b42bfSRobert Love * @lport: The local port to initiate discovery on
6392ab7e1ecSJoe Eykholt * @rdata: remote port private data
6402ab7e1ecSJoe Eykholt *
6412ab7e1ecSJoe Eykholt * On failure, an error code is returned.
6422ab7e1ecSJoe Eykholt */
fc_disc_gpn_id_req(struct fc_lport * lport,struct fc_rport_priv * rdata)6432ab7e1ecSJoe Eykholt static int fc_disc_gpn_id_req(struct fc_lport *lport,
6442ab7e1ecSJoe Eykholt struct fc_rport_priv *rdata)
6452ab7e1ecSJoe Eykholt {
6462ab7e1ecSJoe Eykholt struct fc_frame *fp;
6472ab7e1ecSJoe Eykholt
648ee35624eSHannes Reinecke lockdep_assert_held(&lport->disc.disc_mutex);
6492ab7e1ecSJoe Eykholt fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
6502ab7e1ecSJoe Eykholt sizeof(struct fc_ns_fid));
6512ab7e1ecSJoe Eykholt if (!fp)
6522ab7e1ecSJoe Eykholt return -ENOMEM;
6532ab7e1ecSJoe Eykholt if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, FC_NS_GPN_ID,
654b94f8951SJoe Eykholt fc_disc_gpn_id_resp, rdata,
655b94f8951SJoe Eykholt 3 * lport->r_a_tov))
6562ab7e1ecSJoe Eykholt return -ENOMEM;
6572ab7e1ecSJoe Eykholt kref_get(&rdata->kref);
6582ab7e1ecSJoe Eykholt return 0;
6592ab7e1ecSJoe Eykholt }
6602ab7e1ecSJoe Eykholt
6612ab7e1ecSJoe Eykholt /**
66234f42a07SRobert Love * fc_disc_single() - Discover the directory information for a single target
6633a3b42bfSRobert Love * @lport: The local port the remote port is associated with
66442e9a92fSRobert Love * @dp: The port to rediscover
66542e9a92fSRobert Love */
fc_disc_single(struct fc_lport * lport,struct fc_disc_port * dp)6662ab7e1ecSJoe Eykholt static int fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp)
66742e9a92fSRobert Love {
668ab28f1fdSJoe Eykholt struct fc_rport_priv *rdata;
66942e9a92fSRobert Love
670ee35624eSHannes Reinecke lockdep_assert_held(&lport->disc.disc_mutex);
671ee35624eSHannes Reinecke
6722580064bSHannes Reinecke rdata = fc_rport_create(lport, dp->port_id);
6732ab7e1ecSJoe Eykholt if (!rdata)
6742ab7e1ecSJoe Eykholt return -ENOMEM;
6752ab7e1ecSJoe Eykholt rdata->disc_id = 0;
6762ab7e1ecSJoe Eykholt return fc_disc_gpn_id_req(lport, rdata);
67742e9a92fSRobert Love }
67842e9a92fSRobert Love
67942e9a92fSRobert Love /**
68034f42a07SRobert Love * fc_disc_stop() - Stop discovery for a given lport
6813a3b42bfSRobert Love * @lport: The local port that discovery should stop on
68242e9a92fSRobert Love */
fc_disc_stop(struct fc_lport * lport)683c6b21c93SBart Van Assche static void fc_disc_stop(struct fc_lport *lport)
68442e9a92fSRobert Love {
68542e9a92fSRobert Love struct fc_disc *disc = &lport->disc;
68642e9a92fSRobert Love
687c531b9b4SBhanu Prakash Gollapudi if (disc->pending)
68842e9a92fSRobert Love cancel_delayed_work_sync(&disc->disc_work);
689bc3d12b7SHannes Reinecke mutex_lock(&disc->disc_mutex);
69042e9a92fSRobert Love fc_disc_stop_rports(disc);
691bc3d12b7SHannes Reinecke mutex_unlock(&disc->disc_mutex);
69242e9a92fSRobert Love }
69342e9a92fSRobert Love
69442e9a92fSRobert Love /**
69534f42a07SRobert Love * fc_disc_stop_final() - Stop discovery for a given lport
6963a3b42bfSRobert Love * @lport: The lport that discovery should stop on
69742e9a92fSRobert Love *
69842e9a92fSRobert Love * This function will block until discovery has been
69942e9a92fSRobert Love * completely stopped and all rports have been deleted.
70042e9a92fSRobert Love */
fc_disc_stop_final(struct fc_lport * lport)701c6b21c93SBart Van Assche static void fc_disc_stop_final(struct fc_lport *lport)
70242e9a92fSRobert Love {
70342e9a92fSRobert Love fc_disc_stop(lport);
7045922a957SHannes Reinecke fc_rport_flush_queue();
70542e9a92fSRobert Love }
70642e9a92fSRobert Love
70742e9a92fSRobert Love /**
7080807619dSRobert Love * fc_disc_config() - Configure the discovery layer for a local port
7090807619dSRobert Love * @lport: The local port that needs the discovery layer to be configured
7108a9a7138SRobert Love * @priv: Private data structre for users of the discovery layer
71142e9a92fSRobert Love */
fc_disc_config(struct fc_lport * lport,void * priv)7120807619dSRobert Love void fc_disc_config(struct fc_lport *lport, void *priv)
71342e9a92fSRobert Love {
714bc2e1299SColin Ian King struct fc_disc *disc;
71542e9a92fSRobert Love
71642e9a92fSRobert Love if (!lport->tt.disc_start)
71742e9a92fSRobert Love lport->tt.disc_start = fc_disc_start;
71842e9a92fSRobert Love
71942e9a92fSRobert Love if (!lport->tt.disc_stop)
72042e9a92fSRobert Love lport->tt.disc_stop = fc_disc_stop;
72142e9a92fSRobert Love
72242e9a92fSRobert Love if (!lport->tt.disc_stop_final)
72342e9a92fSRobert Love lport->tt.disc_stop_final = fc_disc_stop_final;
72442e9a92fSRobert Love
72542e9a92fSRobert Love if (!lport->tt.disc_recv_req)
72642e9a92fSRobert Love lport->tt.disc_recv_req = fc_disc_recv_req;
72742e9a92fSRobert Love
72842e9a92fSRobert Love disc = &lport->disc;
7290807619dSRobert Love
7300807619dSRobert Love disc->priv = priv;
7310807619dSRobert Love }
7320807619dSRobert Love EXPORT_SYMBOL(fc_disc_config);
7330807619dSRobert Love
7340807619dSRobert Love /**
7350807619dSRobert Love * fc_disc_init() - Initialize the discovery layer for a local port
7360807619dSRobert Love * @lport: The local port that needs the discovery layer to be initialized
7370807619dSRobert Love */
fc_disc_init(struct fc_lport * lport)7380807619dSRobert Love void fc_disc_init(struct fc_lport *lport)
7390807619dSRobert Love {
7400807619dSRobert Love struct fc_disc *disc = &lport->disc;
7410807619dSRobert Love
74242e9a92fSRobert Love INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
74342e9a92fSRobert Love mutex_init(&disc->disc_mutex);
74442e9a92fSRobert Love INIT_LIST_HEAD(&disc->rports);
74542e9a92fSRobert Love }
74642e9a92fSRobert Love EXPORT_SYMBOL(fc_disc_init);
747