Skip to content

Commit e2c87f4

Browse files
committed
firewire: core: update fw_device outside of device_find_child()
When detecting updates of bus topology, the data of fw_device is newly allocated and caches the content of configuration ROM from the corresponding node. Then, the tree of device is sought to find the previous data of fw_device corresponding to the node. If found, the previous data is updated and reused and the data of fw_device newly allocated is going to be released. The above procedure is done in the call of device_find_child(), however it is a bit abusing against the intention of the helper function, since it is preferable to find only without updating. This commit splits the update outside of the call. Cc: Zijun Hu <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Takashi Sakamoto <[email protected]>
1 parent 52f9fcb commit e2c87f4

File tree

1 file changed

+59
-57
lines changed

1 file changed

+59
-57
lines changed

drivers/firewire/core-device.c

Lines changed: 59 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -928,56 +928,6 @@ static void fw_device_update(struct work_struct *work)
928928
device_for_each_child(&device->device, NULL, update_unit);
929929
}
930930

931-
/*
932-
* If a device was pending for deletion because its node went away but its
933-
* bus info block and root directory header matches that of a newly discovered
934-
* device, revive the existing fw_device.
935-
* The newly allocated fw_device becomes obsolete instead.
936-
*/
937-
static int lookup_existing_device(struct device *dev, void *data)
938-
{
939-
struct fw_device *old = fw_device(dev);
940-
struct fw_device *new = data;
941-
struct fw_card *card = new->card;
942-
int match = 0;
943-
944-
if (!is_fw_device(dev))
945-
return 0;
946-
947-
guard(rwsem_read)(&fw_device_rwsem); // serialize config_rom access
948-
guard(spinlock_irq)(&card->lock); // serialize node access
949-
950-
if (memcmp(old->config_rom, new->config_rom, 6 * 4) == 0 &&
951-
atomic_cmpxchg(&old->state,
952-
FW_DEVICE_GONE,
953-
FW_DEVICE_RUNNING) == FW_DEVICE_GONE) {
954-
struct fw_node *current_node = new->node;
955-
struct fw_node *obsolete_node = old->node;
956-
957-
new->node = obsolete_node;
958-
new->node->data = new;
959-
old->node = current_node;
960-
old->node->data = old;
961-
962-
old->max_speed = new->max_speed;
963-
old->node_id = current_node->node_id;
964-
smp_wmb(); /* update node_id before generation */
965-
old->generation = card->generation;
966-
old->config_rom_retries = 0;
967-
fw_notice(card, "rediscovered device %s\n", dev_name(dev));
968-
969-
old->workfn = fw_device_update;
970-
fw_schedule_device_work(old, 0);
971-
972-
if (current_node == card->root_node)
973-
fw_schedule_bm_work(card, 0);
974-
975-
match = 1;
976-
}
977-
978-
return match;
979-
}
980-
981931
enum { BC_UNKNOWN = 0, BC_UNIMPLEMENTED, BC_IMPLEMENTED, };
982932

983933
static void set_broadcast_channel(struct fw_device *device, int generation)
@@ -1038,12 +988,24 @@ int fw_device_set_broadcast_channel(struct device *dev, void *gen)
1038988
return 0;
1039989
}
1040990

991+
static int compare_configuration_rom(struct device *dev, void *data)
992+
{
993+
const struct fw_device *old = fw_device(dev);
994+
const u32 *config_rom = data;
995+
996+
if (!is_fw_device(dev))
997+
return 0;
998+
999+
// Compare the bus information block and root_length/root_crc.
1000+
return !memcmp(old->config_rom, config_rom, 6 * 4);
1001+
}
1002+
10411003
static void fw_device_init(struct work_struct *work)
10421004
{
10431005
struct fw_device *device =
10441006
container_of(work, struct fw_device, work.work);
10451007
struct fw_card *card = device->card;
1046-
struct device *revived_dev;
1008+
struct device *found;
10471009
u32 minor;
10481010
int ret;
10491011

@@ -1071,13 +1033,53 @@ static void fw_device_init(struct work_struct *work)
10711033
return;
10721034
}
10731035

1074-
revived_dev = device_find_child(card->device,
1075-
device, lookup_existing_device);
1076-
if (revived_dev) {
1077-
put_device(revived_dev);
1078-
fw_device_release(&device->device);
1036+
// If a device was pending for deletion because its node went away but its bus info block
1037+
// and root directory header matches that of a newly discovered device, revive the
1038+
// existing fw_device. The newly allocated fw_device becomes obsolete instead.
1039+
//
1040+
// serialize config_rom access.
1041+
scoped_guard(rwsem_read, &fw_device_rwsem) {
1042+
found = device_find_child(card->device, (void *)device->config_rom,
1043+
compare_configuration_rom);
1044+
}
1045+
if (found) {
1046+
struct fw_device *reused = fw_device(found);
1047+
1048+
if (atomic_cmpxchg(&reused->state,
1049+
FW_DEVICE_GONE,
1050+
FW_DEVICE_RUNNING) == FW_DEVICE_GONE) {
1051+
// serialize node access
1052+
scoped_guard(spinlock_irq, &card->lock) {
1053+
struct fw_node *current_node = device->node;
1054+
struct fw_node *obsolete_node = reused->node;
1055+
1056+
device->node = obsolete_node;
1057+
device->node->data = device;
1058+
reused->node = current_node;
1059+
reused->node->data = reused;
1060+
1061+
reused->max_speed = device->max_speed;
1062+
reused->node_id = current_node->node_id;
1063+
smp_wmb(); /* update node_id before generation */
1064+
reused->generation = card->generation;
1065+
reused->config_rom_retries = 0;
1066+
fw_notice(card, "rediscovered device %s\n",
1067+
dev_name(found));
1068+
1069+
reused->workfn = fw_device_update;
1070+
fw_schedule_device_work(reused, 0);
1071+
1072+
if (current_node == card->root_node)
1073+
fw_schedule_bm_work(card, 0);
1074+
}
10791075

1080-
return;
1076+
put_device(found);
1077+
fw_device_release(&device->device);
1078+
1079+
return;
1080+
}
1081+
1082+
put_device(found);
10811083
}
10821084

10831085
device_initialize(&device->device);

0 commit comments

Comments
 (0)