diff options
author | Bhanu Prakash Gollapudi <bprakash@broadcom.com> | 2011-05-16 19:45:24 -0400 |
---|---|---|
committer | James Bottomley <jbottomley@parallels.com> | 2011-05-24 12:36:17 -0400 |
commit | c051ad2e57e379e07e4ec28b2a54eeb0d04c5d59 (patch) | |
tree | e210bbdc9319229f36988448b65139b698d132af /drivers/scsi/fcoe | |
parent | 4f788dce0baf44295a8d9708d3f124587158c061 (diff) |
[SCSI] libfcoe: Incorrect CVL handling for NPIV ports
Host doesnt handle CVL to NPIV instantiated ports correctly.
- As per FC-BB-5 Rev 2 CVLs with no VN_Port descriptors shall be treated as
implicit logout of ALL vn_ports.
- CVL for NPIV ports should be handled before physical port even if descriptor
for physical port appears before NPIV ports
Signed-off-by: Bhanu Prakash Gollapudi <bprakash@broadcom.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
Signed-off-by: James Bottomley <jbottomley@parallels.com>
Diffstat (limited to 'drivers/scsi/fcoe')
-rw-r--r-- | drivers/scsi/fcoe/fcoe_ctlr.c | 133 |
1 files changed, 90 insertions, 43 deletions
diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c index 229e4af5508a..c74c4b8e71ef 100644 --- a/drivers/scsi/fcoe/fcoe_ctlr.c +++ b/drivers/scsi/fcoe/fcoe_ctlr.c | |||
@@ -1173,7 +1173,9 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, | |||
1173 | struct fc_lport *lport = fip->lp; | 1173 | struct fc_lport *lport = fip->lp; |
1174 | struct fc_lport *vn_port = NULL; | 1174 | struct fc_lport *vn_port = NULL; |
1175 | u32 desc_mask; | 1175 | u32 desc_mask; |
1176 | int is_vn_port = 0; | 1176 | int num_vlink_desc; |
1177 | int reset_phys_port = 0; | ||
1178 | struct fip_vn_desc **vlink_desc_arr = NULL; | ||
1177 | 1179 | ||
1178 | LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n"); | 1180 | LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n"); |
1179 | 1181 | ||
@@ -1183,70 +1185,73 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, | |||
1183 | /* | 1185 | /* |
1184 | * mask of required descriptors. Validating each one clears its bit. | 1186 | * mask of required descriptors. Validating each one clears its bit. |
1185 | */ | 1187 | */ |
1186 | desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) | BIT(FIP_DT_VN_ID); | 1188 | desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME); |
1187 | 1189 | ||
1188 | rlen = ntohs(fh->fip_dl_len) * FIP_BPW; | 1190 | rlen = ntohs(fh->fip_dl_len) * FIP_BPW; |
1189 | desc = (struct fip_desc *)(fh + 1); | 1191 | desc = (struct fip_desc *)(fh + 1); |
1192 | |||
1193 | /* | ||
1194 | * Actually need to subtract 'sizeof(*mp) - sizeof(*wp)' from 'rlen' | ||
1195 | * before determining max Vx_Port descriptor but a buggy FCF could have | ||
1196 | * omited either or both MAC Address and Name Identifier descriptors | ||
1197 | */ | ||
1198 | num_vlink_desc = rlen / sizeof(*vp); | ||
1199 | if (num_vlink_desc) | ||
1200 | vlink_desc_arr = kmalloc(sizeof(vp) * num_vlink_desc, | ||
1201 | GFP_ATOMIC); | ||
1202 | if (!vlink_desc_arr) | ||
1203 | return; | ||
1204 | num_vlink_desc = 0; | ||
1205 | |||
1190 | while (rlen >= sizeof(*desc)) { | 1206 | while (rlen >= sizeof(*desc)) { |
1191 | dlen = desc->fip_dlen * FIP_BPW; | 1207 | dlen = desc->fip_dlen * FIP_BPW; |
1192 | if (dlen > rlen) | 1208 | if (dlen > rlen) |
1193 | return; | 1209 | goto err; |
1194 | /* Drop CVL if there are duplicate critical descriptors */ | 1210 | /* Drop CVL if there are duplicate critical descriptors */ |
1195 | if ((desc->fip_dtype < 32) && | 1211 | if ((desc->fip_dtype < 32) && |
1212 | (desc->fip_dtype != FIP_DT_VN_ID) && | ||
1196 | !(desc_mask & 1U << desc->fip_dtype)) { | 1213 | !(desc_mask & 1U << desc->fip_dtype)) { |
1197 | LIBFCOE_FIP_DBG(fip, "Duplicate Critical " | 1214 | LIBFCOE_FIP_DBG(fip, "Duplicate Critical " |
1198 | "Descriptors in FIP CVL\n"); | 1215 | "Descriptors in FIP CVL\n"); |
1199 | return; | 1216 | goto err; |
1200 | } | 1217 | } |
1201 | switch (desc->fip_dtype) { | 1218 | switch (desc->fip_dtype) { |
1202 | case FIP_DT_MAC: | 1219 | case FIP_DT_MAC: |
1203 | mp = (struct fip_mac_desc *)desc; | 1220 | mp = (struct fip_mac_desc *)desc; |
1204 | if (dlen < sizeof(*mp)) | 1221 | if (dlen < sizeof(*mp)) |
1205 | return; | 1222 | goto err; |
1206 | if (compare_ether_addr(mp->fd_mac, fcf->fcf_mac)) | 1223 | if (compare_ether_addr(mp->fd_mac, fcf->fcf_mac)) |
1207 | return; | 1224 | goto err; |
1208 | desc_mask &= ~BIT(FIP_DT_MAC); | 1225 | desc_mask &= ~BIT(FIP_DT_MAC); |
1209 | break; | 1226 | break; |
1210 | case FIP_DT_NAME: | 1227 | case FIP_DT_NAME: |
1211 | wp = (struct fip_wwn_desc *)desc; | 1228 | wp = (struct fip_wwn_desc *)desc; |
1212 | if (dlen < sizeof(*wp)) | 1229 | if (dlen < sizeof(*wp)) |
1213 | return; | 1230 | goto err; |
1214 | if (get_unaligned_be64(&wp->fd_wwn) != fcf->switch_name) | 1231 | if (get_unaligned_be64(&wp->fd_wwn) != fcf->switch_name) |
1215 | return; | 1232 | goto err; |
1216 | desc_mask &= ~BIT(FIP_DT_NAME); | 1233 | desc_mask &= ~BIT(FIP_DT_NAME); |
1217 | break; | 1234 | break; |
1218 | case FIP_DT_VN_ID: | 1235 | case FIP_DT_VN_ID: |
1219 | vp = (struct fip_vn_desc *)desc; | 1236 | vp = (struct fip_vn_desc *)desc; |
1220 | if (dlen < sizeof(*vp)) | 1237 | if (dlen < sizeof(*vp)) |
1221 | return; | 1238 | goto err; |
1222 | if (compare_ether_addr(vp->fd_mac, | 1239 | vlink_desc_arr[num_vlink_desc++] = vp; |
1223 | fip->get_src_addr(lport)) == 0 && | 1240 | vn_port = fc_vport_id_lookup(lport, |
1224 | get_unaligned_be64(&vp->fd_wwpn) == lport->wwpn && | 1241 | ntoh24(vp->fd_fc_id)); |
1225 | ntoh24(vp->fd_fc_id) == lport->port_id) { | 1242 | if (vn_port && (vn_port == lport)) { |
1226 | desc_mask &= ~BIT(FIP_DT_VN_ID); | 1243 | mutex_lock(&fip->ctlr_mutex); |
1227 | break; | 1244 | per_cpu_ptr(lport->dev_stats, |
1245 | get_cpu())->VLinkFailureCount++; | ||
1246 | put_cpu(); | ||
1247 | fcoe_ctlr_reset(fip); | ||
1248 | mutex_unlock(&fip->ctlr_mutex); | ||
1228 | } | 1249 | } |
1229 | /* check if clr_vlink is for NPIV port */ | ||
1230 | mutex_lock(&lport->lp_mutex); | ||
1231 | list_for_each_entry(vn_port, &lport->vports, list) { | ||
1232 | if (compare_ether_addr(vp->fd_mac, | ||
1233 | fip->get_src_addr(vn_port)) == 0 && | ||
1234 | (get_unaligned_be64(&vp->fd_wwpn) | ||
1235 | == vn_port->wwpn) && | ||
1236 | (ntoh24(vp->fd_fc_id) == | ||
1237 | fc_host_port_id(vn_port->host))) { | ||
1238 | desc_mask &= ~BIT(FIP_DT_VN_ID); | ||
1239 | is_vn_port = 1; | ||
1240 | break; | ||
1241 | } | ||
1242 | } | ||
1243 | mutex_unlock(&lport->lp_mutex); | ||
1244 | |||
1245 | break; | 1250 | break; |
1246 | default: | 1251 | default: |
1247 | /* standard says ignore unknown descriptors >= 128 */ | 1252 | /* standard says ignore unknown descriptors >= 128 */ |
1248 | if (desc->fip_dtype < FIP_DT_VENDOR_BASE) | 1253 | if (desc->fip_dtype < FIP_DT_VENDOR_BASE) |
1249 | return; | 1254 | goto err; |
1250 | break; | 1255 | break; |
1251 | } | 1256 | } |
1252 | desc = (struct fip_desc *)((char *)desc + dlen); | 1257 | desc = (struct fip_desc *)((char *)desc + dlen); |
@@ -1256,26 +1261,68 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, | |||
1256 | /* | 1261 | /* |
1257 | * reset only if all required descriptors were present and valid. | 1262 | * reset only if all required descriptors were present and valid. |
1258 | */ | 1263 | */ |
1259 | if (desc_mask) { | 1264 | if (desc_mask) |
1260 | LIBFCOE_FIP_DBG(fip, "missing descriptors mask %x\n", | 1265 | LIBFCOE_FIP_DBG(fip, "missing descriptors mask %x\n", |
1261 | desc_mask); | 1266 | desc_mask); |
1267 | else if (!num_vlink_desc) { | ||
1268 | LIBFCOE_FIP_DBG(fip, "CVL: no Vx_Port descriptor found\n"); | ||
1269 | /* | ||
1270 | * No Vx_Port description. Clear all NPIV ports, | ||
1271 | * followed by physical port | ||
1272 | */ | ||
1273 | mutex_lock(&lport->lp_mutex); | ||
1274 | list_for_each_entry(vn_port, &lport->vports, list) | ||
1275 | fc_lport_reset(vn_port); | ||
1276 | mutex_unlock(&lport->lp_mutex); | ||
1277 | |||
1278 | mutex_lock(&fip->ctlr_mutex); | ||
1279 | per_cpu_ptr(lport->dev_stats, | ||
1280 | get_cpu())->VLinkFailureCount++; | ||
1281 | put_cpu(); | ||
1282 | fcoe_ctlr_reset(fip); | ||
1283 | mutex_unlock(&fip->ctlr_mutex); | ||
1284 | |||
1285 | fc_lport_reset(fip->lp); | ||
1286 | fcoe_ctlr_solicit(fip, NULL); | ||
1262 | } else { | 1287 | } else { |
1263 | LIBFCOE_FIP_DBG(fip, "performing Clear Virtual Link\n"); | 1288 | int i; |
1264 | 1289 | ||
1265 | if (is_vn_port) | 1290 | LIBFCOE_FIP_DBG(fip, "performing Clear Virtual Link\n"); |
1266 | fc_lport_reset(vn_port); | 1291 | for (i = 0; i < num_vlink_desc; i++) { |
1267 | else { | 1292 | vp = vlink_desc_arr[i]; |
1268 | mutex_lock(&fip->ctlr_mutex); | 1293 | vn_port = fc_vport_id_lookup(lport, |
1269 | per_cpu_ptr(lport->dev_stats, | 1294 | ntoh24(vp->fd_fc_id)); |
1270 | get_cpu())->VLinkFailureCount++; | 1295 | if (!vn_port) |
1271 | put_cpu(); | 1296 | continue; |
1272 | fcoe_ctlr_reset(fip); | 1297 | |
1273 | mutex_unlock(&fip->ctlr_mutex); | 1298 | /* |
1299 | * 'port_id' is already validated, check MAC address and | ||
1300 | * wwpn | ||
1301 | */ | ||
1302 | if (compare_ether_addr(fip->get_src_addr(vn_port), | ||
1303 | vp->fd_mac) != 0 || | ||
1304 | get_unaligned_be64(&vp->fd_wwpn) != | ||
1305 | vn_port->wwpn) | ||
1306 | continue; | ||
1307 | |||
1308 | if (vn_port == lport) | ||
1309 | /* | ||
1310 | * Physical port, defer processing till all | ||
1311 | * listed NPIV ports are cleared | ||
1312 | */ | ||
1313 | reset_phys_port = 1; | ||
1314 | else /* NPIV port */ | ||
1315 | fc_lport_reset(vn_port); | ||
1316 | } | ||
1274 | 1317 | ||
1318 | if (reset_phys_port) { | ||
1275 | fc_lport_reset(fip->lp); | 1319 | fc_lport_reset(fip->lp); |
1276 | fcoe_ctlr_solicit(fip, NULL); | 1320 | fcoe_ctlr_solicit(fip, NULL); |
1277 | } | 1321 | } |
1278 | } | 1322 | } |
1323 | |||
1324 | err: | ||
1325 | kfree(vlink_desc_arr); | ||
1279 | } | 1326 | } |
1280 | 1327 | ||
1281 | /** | 1328 | /** |