aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libfc/fc_npiv.c
blob: dd2b43bb1c70887259b1bb526b219871614db918 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/*
 * Copyright(c) 2009 Intel Corporation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Maintained at www.Open-FCoE.org
 */

/*
 * NPIV VN_Port helper functions for libfc
 */

#include <scsi/libfc.h>

/**
 * fc_vport_create() - Create a new NPIV vport instance
 * @vport: fc_vport structure from scsi_transport_fc
 * @privsize: driver private data size to allocate along with the Scsi_Host
 */

struct fc_lport *libfc_vport_create(struct fc_vport *vport, int privsize)
{
	struct Scsi_Host *shost = vport_to_shost(vport);
	struct fc_lport *n_port = shost_priv(shost);
	struct fc_lport *vn_port;

	vn_port = libfc_host_alloc(shost->hostt, privsize);
	if (!vn_port)
		goto err_out;
	if (fc_exch_mgr_list_clone(n_port, vn_port))
		goto err_put;

	vn_port->vport = vport;
	vport->dd_data = vn_port;

	mutex_lock(&n_port->lp_mutex);
	list_add_tail(&vn_port->list, &n_port->vports);
	mutex_unlock(&n_port->lp_mutex);

	return vn_port;

err_put:
	scsi_host_put(vn_port->host);
err_out:
	return NULL;
}
EXPORT_SYMBOL(libfc_vport_create);

/**
 * fc_vport_id_lookup() - find NPIV lport that matches a given fabric ID
 * @n_port: Top level N_Port which may have multiple NPIV VN_Ports
 * @port_id: Fabric ID to find a match for
 *
 * Returns: matching lport pointer or NULL if there is no match
 */
struct fc_lport *fc_vport_id_lookup(struct fc_lport *n_port, u32 port_id)
{
	struct fc_lport *lport = NULL;
	struct fc_lport *vn_port;

	if (n_port->port_id == port_id)
		return n_port;

	if (port_id == FC_FID_FLOGI)
		return n_port;		/* for point-to-point */

	mutex_lock(&n_port->lp_mutex);
	list_for_each_entry(vn_port, &n_port->vports, list) {
		if (vn_port->port_id == port_id) {
			lport = vn_port;
			break;
		}
	}
	mutex_unlock(&n_port->lp_mutex);

	return lport;
}

/*
 * When setting the link state of vports during an lport state change, it's
 * necessary to hold the lp_mutex of both the N_Port and the VN_Port.
 * This tells the lockdep engine to treat the nested locking of the VN_Port
 * as a different lock class.
 */
enum libfc_lport_mutex_class {
	LPORT_MUTEX_NORMAL = 0,
	LPORT_MUTEX_VN_PORT = 1,
};

/**
 * __fc_vport_setlink() - update link and status on a VN_Port
 * @n_port: parent N_Port
 * @vn_port: VN_Port to update
 *
 * Locking: must be called with both the N_Port and VN_Port lp_mutex held
 */
static void __fc_vport_setlink(struct fc_lport *n_port,
			       struct fc_lport *vn_port)
{
	struct fc_vport *vport = vn_port->vport;

	if (vn_port->state == LPORT_ST_DISABLED)
		return;

	if (n_port->state == LPORT_ST_READY) {
		if (n_port->npiv_enabled) {
			fc_vport_set_state(vport, FC_VPORT_INITIALIZING);
			__fc_linkup(vn_port);
		} else {
			fc_vport_set_state(vport, FC_VPORT_NO_FABRIC_SUPP);
			__fc_linkdown(vn_port);
		}
	} else {
		fc_vport_set_state(vport, FC_VPORT_LINKDOWN);
		__fc_linkdown(vn_port);
	}
}

/**
 * fc_vport_setlink() - update link and status on a VN_Port
 * @vn_port: virtual port to update
 */
void fc_vport_setlink(struct fc_lport *vn_port)
{
	struct fc_vport *vport = vn_port->vport;
	struct Scsi_Host *shost = vport_to_shost(vport);
	struct fc_lport *n_port = shost_priv(shost);

	mutex_lock(&n_port->lp_mutex);
	mutex_lock_nested(&vn_port->lp_mutex, LPORT_MUTEX_VN_PORT);
	__fc_vport_setlink(n_port, vn_port);
	mutex_unlock(&vn_port->lp_mutex);
	mutex_unlock(&n_port->lp_mutex);
}
EXPORT_SYMBOL(fc_vport_setlink);

/**
 * fc_vports_linkchange() - change the link state of all vports
 * @n_port: Parent N_Port that has changed state
 *
 * Locking: called with the n_port lp_mutex held
 */
void fc_vports_linkchange(struct fc_lport *n_port)
{
	struct fc_lport *vn_port;

	list_for_each_entry(vn_port, &n_port->vports, list) {
		mutex_lock_nested(&vn_port->lp_mutex, LPORT_MUTEX_VN_PORT);
		__fc_vport_setlink(n_port, vn_port);
		mutex_unlock(&vn_port->lp_mutex);
	}
}