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