Commit | Line | Data |
---|---|---|
174e1ebf CL |
1 | /* |
2 | * Copyright(c) 2009 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 | * NPIV VN_Port helper functions for libfc | |
22 | */ | |
23 | ||
24 | #include <scsi/libfc.h> | |
09703660 | 25 | #include <linux/export.h> |
174e1ebf CL |
26 | |
27 | /** | |
28 | * fc_vport_create() - Create a new NPIV vport instance | |
29 | * @vport: fc_vport structure from scsi_transport_fc | |
30 | * @privsize: driver private data size to allocate along with the Scsi_Host | |
31 | */ | |
32 | ||
33 | struct fc_lport *libfc_vport_create(struct fc_vport *vport, int privsize) | |
34 | { | |
35 | struct Scsi_Host *shost = vport_to_shost(vport); | |
36 | struct fc_lport *n_port = shost_priv(shost); | |
37 | struct fc_lport *vn_port; | |
38 | ||
39 | vn_port = libfc_host_alloc(shost->hostt, privsize); | |
40 | if (!vn_port) | |
72fa396b | 41 | return vn_port; |
174e1ebf CL |
42 | |
43 | vn_port->vport = vport; | |
44 | vport->dd_data = vn_port; | |
45 | ||
46 | mutex_lock(&n_port->lp_mutex); | |
47 | list_add_tail(&vn_port->list, &n_port->vports); | |
48 | mutex_unlock(&n_port->lp_mutex); | |
49 | ||
50 | return vn_port; | |
174e1ebf CL |
51 | } |
52 | EXPORT_SYMBOL(libfc_vport_create); | |
53 | ||
54 | /** | |
55 | * fc_vport_id_lookup() - find NPIV lport that matches a given fabric ID | |
56 | * @n_port: Top level N_Port which may have multiple NPIV VN_Ports | |
57 | * @port_id: Fabric ID to find a match for | |
58 | * | |
59 | * Returns: matching lport pointer or NULL if there is no match | |
60 | */ | |
61 | struct fc_lport *fc_vport_id_lookup(struct fc_lport *n_port, u32 port_id) | |
62 | { | |
63 | struct fc_lport *lport = NULL; | |
64 | struct fc_lport *vn_port; | |
65 | ||
7b2787ec | 66 | if (n_port->port_id == port_id) |
174e1ebf CL |
67 | return n_port; |
68 | ||
f4568b8b JE |
69 | if (port_id == FC_FID_FLOGI) |
70 | return n_port; /* for point-to-point */ | |
71 | ||
174e1ebf CL |
72 | mutex_lock(&n_port->lp_mutex); |
73 | list_for_each_entry(vn_port, &n_port->vports, list) { | |
7b2787ec | 74 | if (vn_port->port_id == port_id) { |
174e1ebf CL |
75 | lport = vn_port; |
76 | break; | |
77 | } | |
78 | } | |
79 | mutex_unlock(&n_port->lp_mutex); | |
80 | ||
81 | return lport; | |
82 | } | |
75a2792d | 83 | EXPORT_SYMBOL(fc_vport_id_lookup); |
174e1ebf | 84 | |
8faecddb CL |
85 | /* |
86 | * When setting the link state of vports during an lport state change, it's | |
87 | * necessary to hold the lp_mutex of both the N_Port and the VN_Port. | |
88 | * This tells the lockdep engine to treat the nested locking of the VN_Port | |
89 | * as a different lock class. | |
90 | */ | |
91 | enum libfc_lport_mutex_class { | |
92 | LPORT_MUTEX_NORMAL = 0, | |
93 | LPORT_MUTEX_VN_PORT = 1, | |
94 | }; | |
95 | ||
96 | /** | |
97 | * __fc_vport_setlink() - update link and status on a VN_Port | |
98 | * @n_port: parent N_Port | |
99 | * @vn_port: VN_Port to update | |
100 | * | |
101 | * Locking: must be called with both the N_Port and VN_Port lp_mutex held | |
102 | */ | |
103 | static void __fc_vport_setlink(struct fc_lport *n_port, | |
104 | struct fc_lport *vn_port) | |
105 | { | |
106 | struct fc_vport *vport = vn_port->vport; | |
107 | ||
108 | if (vn_port->state == LPORT_ST_DISABLED) | |
109 | return; | |
110 | ||
111 | if (n_port->state == LPORT_ST_READY) { | |
112 | if (n_port->npiv_enabled) { | |
113 | fc_vport_set_state(vport, FC_VPORT_INITIALIZING); | |
114 | __fc_linkup(vn_port); | |
115 | } else { | |
116 | fc_vport_set_state(vport, FC_VPORT_NO_FABRIC_SUPP); | |
117 | __fc_linkdown(vn_port); | |
118 | } | |
119 | } else { | |
120 | fc_vport_set_state(vport, FC_VPORT_LINKDOWN); | |
121 | __fc_linkdown(vn_port); | |
122 | } | |
123 | } | |
124 | ||
125 | /** | |
126 | * fc_vport_setlink() - update link and status on a VN_Port | |
127 | * @vn_port: virtual port to update | |
128 | */ | |
129 | void fc_vport_setlink(struct fc_lport *vn_port) | |
130 | { | |
131 | struct fc_vport *vport = vn_port->vport; | |
132 | struct Scsi_Host *shost = vport_to_shost(vport); | |
133 | struct fc_lport *n_port = shost_priv(shost); | |
134 | ||
135 | mutex_lock(&n_port->lp_mutex); | |
136 | mutex_lock_nested(&vn_port->lp_mutex, LPORT_MUTEX_VN_PORT); | |
137 | __fc_vport_setlink(n_port, vn_port); | |
138 | mutex_unlock(&vn_port->lp_mutex); | |
139 | mutex_unlock(&n_port->lp_mutex); | |
140 | } | |
141 | EXPORT_SYMBOL(fc_vport_setlink); | |
142 | ||
143 | /** | |
144 | * fc_vports_linkchange() - change the link state of all vports | |
145 | * @n_port: Parent N_Port that has changed state | |
146 | * | |
147 | * Locking: called with the n_port lp_mutex held | |
148 | */ | |
149 | void fc_vports_linkchange(struct fc_lport *n_port) | |
150 | { | |
151 | struct fc_lport *vn_port; | |
152 | ||
153 | list_for_each_entry(vn_port, &n_port->vports, list) { | |
154 | mutex_lock_nested(&vn_port->lp_mutex, LPORT_MUTEX_VN_PORT); | |
155 | __fc_vport_setlink(n_port, vn_port); | |
156 | mutex_unlock(&vn_port->lp_mutex); | |
157 | } | |
158 | } | |
159 |