read_rom() obtained a fresh new fw_device.generation for each read
transaction.  Hence it was able to continue reading in the middle of the
ROM even if a bus reset happened.  However the device may have modified
the ROM during the reset.  We would end up with a corrupt fetched ROM
image then.

Although all of this is quite unlikely, it is not impossible.
Therefore we now restart reading the ROM if the bus generation changed.

Side note:  The barrier in read_rom(), inserted by patch "firewire:
enforce access order between generation and node ID" is not necessary
anymore because the sequence of calls
        fw_device_init() ->
                read_bus_info_block() ->
                        read_rom()
                        read_rom()
                        read_rom()
                        ...
will take care that generation is read before node_id, won't it?

Signed-off-by: Stefan Richter <[EMAIL PROTECTED]>
---

Refreshed version of the patch from November 1 2007.

 drivers/firewire/fw-device.c |   28 +++++++++++++++++-----------
 1 file changed, 17 insertions(+), 11 deletions(-)

Index: linux/drivers/firewire/fw-device.c
===================================================================
--- linux.orig/drivers/firewire/fw-device.c
+++ linux/drivers/firewire/fw-device.c
@@ -388,15 +388,12 @@ complete_transaction(struct fw_card *car
        complete(&callback_data->done);
 }
 
-static int read_rom(struct fw_device *device, int index, u32 * data)
+static int
+read_rom(struct fw_device *device, int generation, int index, u32 *data)
 {
        struct read_quadlet_callback_data callback_data;
        struct fw_transaction t;
        u64 offset;
-       int generation = device->generation;
-
-       /* device->node_id, accessed below, must not be older than generation */
-       smp_rmb();
 
        init_completion(&callback_data.done);
 
@@ -412,7 +409,14 @@ static int read_rom(struct fw_device *de
        return callback_data.rcode;
 }
 
-static int read_bus_info_block(struct fw_device *device)
+/*
+ * Read the bus info block, perform a speed probe, and read all of the rest of
+ * the config ROM.  We do all this with a cached bus generation.  If the bus
+ * generation changes under us, read_bus_info_block will fail and get retried.
+ * It's better to start all over in this case because the node from which we
+ * are reading the ROM may have changed the ROM during the reset.
+ */
+static int read_bus_info_block(struct fw_device *device, int generation)
 {
        static u32 rom[256];
        u32 stack[16], sp, key;
@@ -422,7 +426,7 @@ static int read_bus_info_block(struct fw
 
        /* First read the bus info block. */
        for (i = 0; i < 5; i++) {
-               if (read_rom(device, i, &rom[i]) != RCODE_COMPLETE)
+               if (read_rom(device, generation, i, &rom[i]) != RCODE_COMPLETE)
                        return -1;
                /*
                 * As per IEEE1212 7.2, during power-up, devices can
@@ -457,7 +461,8 @@ static int read_bus_info_block(struct fw
                        device->max_speed = device->card->link_speed;
 
                while (device->max_speed > SCODE_100) {
-                       if (read_rom(device, 0, &dummy) == RCODE_COMPLETE)
+                       if (read_rom(device, generation, 0, &dummy) ==
+                           RCODE_COMPLETE)
                                break;
                        device->max_speed--;
                }
@@ -490,7 +495,7 @@ static int read_bus_info_block(struct fw
                        return -1;
 
                /* Read header quadlet for the block to get the length. */
-               if (read_rom(device, i, &rom[i]) != RCODE_COMPLETE)
+               if (read_rom(device, generation, i, &rom[i]) != RCODE_COMPLETE)
                        return -1;
                end = i + (rom[i] >> 16) + 1;
                i++;
@@ -509,7 +514,8 @@ static int read_bus_info_block(struct fw
                 * it references another block, and push it in that case.
                 */
                while (i < end) {
-                       if (read_rom(device, i, &rom[i]) != RCODE_COMPLETE)
+                       if (read_rom(device, generation, i, &rom[i]) !=
+                           RCODE_COMPLETE)
                                return -1;
                        if ((key >> 30) == 3 && (rom[i] >> 30) > 1 &&
                            sp < ARRAY_SIZE(stack))
@@ -656,7 +662,7 @@ static void fw_device_init(struct work_s
         * device.
         */
 
-       if (read_bus_info_block(device) < 0) {
+       if (read_bus_info_block(device, device->generation) < 0) {
                if (device->config_rom_retries < MAX_RETRIES) {
                        device->config_rom_retries++;
                        schedule_delayed_work(&device->work, RETRY_DELAY);

-- 
Stefan Richter
-=====-==--- ---= ==---
http://arcgraph.de/sr/

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to