Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[SCSI] mptfc: race between mptfc_register_dev and mptfc_target_alloc

A race condition exists in mptfc between the thread registering a device
with the fc transport and the scan work generated by the transport.
This race existed prior to the application of the mptfc bug fix patch.

mptfc_register_dev() calls fc_remote_port_add() with the FC_RPORT_ROLE_TARGET
bit set in the rport ids passed to the function. Having this bit set causes
fc_remote_port_add() to schedule a scan of the device.

This scan can execute before mptfc_register_dev() can fill in the dd_data
in the rport structure. When this happens, mptfc_target_alloc() will fail
because dd_data is null.

Attached is a patch which fixes the problem. The patch changes the rport ids
passed to fc_remote_port_add() to not have the TARGET bit set. This prevents
the scan from being scheduled. After mptfc_register_dev() fills in the rport
dd_data field, fc_remote_port_rolechg() is called, changing the role of the
rport to TARGET. Thus, the scan is scheduled after dd_data is filled
in which prevents the failure in mptfc_target_alloc().

Signed-off-by: Michael Reed <mdr@sgi.com>
Signed-off-by: Eric Moore <Eric.Moore@lsil.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>

authored by

mdr@sgi.com and committed by
James Bottomley
6dd727da 0b18ac42

+7 -4
+7 -4
drivers/message/fusion/mptfc.c
··· 341 341 rid->port_name = ((u64)pg0->WWPN.High) << 32 | (u64)pg0->WWPN.Low; 342 342 rid->port_id = pg0->PortIdentifier; 343 343 rid->roles = FC_RPORT_ROLE_UNKNOWN; 344 - rid->roles |= FC_RPORT_ROLE_FCP_TARGET; 345 - if (pg0->Protocol & MPI_FC_DEVICE_PAGE0_PROT_FCP_INITIATOR) 346 - rid->roles |= FC_RPORT_ROLE_FCP_INITIATOR; 347 344 348 345 return 0; 349 346 } ··· 354 357 int new_ri = 1; 355 358 u64 pn, nn; 356 359 VirtTarget *vtarget; 360 + u32 roles = FC_RPORT_ROLE_UNKNOWN; 357 361 358 362 if (mptfc_generate_rport_ids(pg0, &rport_ids) < 0) 359 363 return; 364 + 365 + roles |= FC_RPORT_ROLE_FCP_TARGET; 366 + if (pg0->Protocol & MPI_FC_DEVICE_PAGE0_PROT_FCP_INITIATOR) 367 + roles |= FC_RPORT_ROLE_FCP_INITIATOR; 360 368 361 369 /* scan list looking for a match */ 362 370 list_for_each_entry(ri, &ioc->fc_rports, list) { ··· 402 400 vtarget->bus_id = pg0->CurrentBus; 403 401 } 404 402 } 405 - /* once dd_data is filled in, commands will issue to hardware */ 406 403 *((struct mptfc_rport_info **)rport->dd_data) = ri; 404 + /* scan will be scheduled once rport becomes a target */ 405 + fc_remote_port_rolechg(rport,roles); 407 406 408 407 pn = (u64)ri->pg0.WWPN.High << 32 | (u64)ri->pg0.WWPN.Low; 409 408 nn = (u64)ri->pg0.WWNN.High << 32 | (u64)ri->pg0.WWNN.Low;