From: Greg Kroah-Hartman Date: Mon, 16 Mar 2015 11:57:18 +0000 (+0100) Subject: 3.19-stable patches X-Git-Tag: v3.10.72~13 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f24163ee0eb26099e37ebcd3a9f1e5b0c40f22d9;p=thirdparty%2Fkernel%2Fstable-queue.git 3.19-stable patches added patches: dm-fix-a-race-condition-in-dm_get_md.patch dm-io-reject-unsupported-discard-requests-with-eopnotsupp.patch dm-mirror-do-not-degrade-the-mirror-on-discard-error.patch dm-snapshot-fix-a-possible-invalid-memory-access-on-unload.patch firmware-dmi_scan-fix-dmi-scan-to-handle-end-of-table.patch firmware-dmi_scan-fix-dmi_len-type.patch staging-comedi-cb_pcidas64-fix-incorrect-ai-range-code-handling.patch --- diff --git a/queue-3.19/dm-fix-a-race-condition-in-dm_get_md.patch b/queue-3.19/dm-fix-a-race-condition-in-dm_get_md.patch new file mode 100644 index 00000000000..55436048222 --- /dev/null +++ b/queue-3.19/dm-fix-a-race-condition-in-dm_get_md.patch @@ -0,0 +1,84 @@ +From 2bec1f4a8832e74ebbe859f176d8a9cb20dd97f4 Mon Sep 17 00:00:00 2001 +From: Mikulas Patocka +Date: Tue, 17 Feb 2015 14:30:53 -0500 +Subject: dm: fix a race condition in dm_get_md + +From: Mikulas Patocka + +commit 2bec1f4a8832e74ebbe859f176d8a9cb20dd97f4 upstream. + +The function dm_get_md finds a device mapper device with a given dev_t, +increases the reference count and returns the pointer. + +dm_get_md calls dm_find_md, dm_find_md takes _minor_lock, finds the +device, tests that the device doesn't have DMF_DELETING or DMF_FREEING +flag, drops _minor_lock and returns pointer to the device. dm_get_md then +calls dm_get. dm_get calls BUG if the device has the DMF_FREEING flag, +otherwise it increments the reference count. + +There is a possible race condition - after dm_find_md exits and before +dm_get is called, there are no locks held, so the device may disappear or +DMF_FREEING flag may be set, which results in BUG. + +To fix this bug, we need to call dm_get while we hold _minor_lock. This +patch renames dm_find_md to dm_get_md and changes it so that it calls +dm_get while holding the lock. + +Signed-off-by: Mikulas Patocka +Signed-off-by: Mike Snitzer +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/md/dm.c | 27 ++++++++++----------------- + 1 file changed, 10 insertions(+), 17 deletions(-) + +--- a/drivers/md/dm.c ++++ b/drivers/md/dm.c +@@ -2462,7 +2462,7 @@ int dm_setup_md_queue(struct mapped_devi + return 0; + } + +-static struct mapped_device *dm_find_md(dev_t dev) ++struct mapped_device *dm_get_md(dev_t dev) + { + struct mapped_device *md; + unsigned minor = MINOR(dev); +@@ -2473,12 +2473,15 @@ static struct mapped_device *dm_find_md( + spin_lock(&_minor_lock); + + md = idr_find(&_minor_idr, minor); +- if (md && (md == MINOR_ALLOCED || +- (MINOR(disk_devt(dm_disk(md))) != minor) || +- dm_deleting_md(md) || +- test_bit(DMF_FREEING, &md->flags))) { +- md = NULL; +- goto out; ++ if (md) { ++ if ((md == MINOR_ALLOCED || ++ (MINOR(disk_devt(dm_disk(md))) != minor) || ++ dm_deleting_md(md) || ++ test_bit(DMF_FREEING, &md->flags))) { ++ md = NULL; ++ goto out; ++ } ++ dm_get(md); + } + + out: +@@ -2486,16 +2489,6 @@ out: + + return md; + } +- +-struct mapped_device *dm_get_md(dev_t dev) +-{ +- struct mapped_device *md = dm_find_md(dev); +- +- if (md) +- dm_get(md); +- +- return md; +-} + EXPORT_SYMBOL_GPL(dm_get_md); + + void *dm_get_mdptr(struct mapped_device *md) diff --git a/queue-3.19/dm-io-reject-unsupported-discard-requests-with-eopnotsupp.patch b/queue-3.19/dm-io-reject-unsupported-discard-requests-with-eopnotsupp.patch new file mode 100644 index 00000000000..b06e7425211 --- /dev/null +++ b/queue-3.19/dm-io-reject-unsupported-discard-requests-with-eopnotsupp.patch @@ -0,0 +1,62 @@ +From 37527b869207ad4c208b1e13967d69b8bba1fbf9 Mon Sep 17 00:00:00 2001 +From: "Darrick J. Wong" +Date: Fri, 13 Feb 2015 11:05:37 -0800 +Subject: dm io: reject unsupported DISCARD requests with EOPNOTSUPP + +From: "Darrick J. Wong" + +commit 37527b869207ad4c208b1e13967d69b8bba1fbf9 upstream. + +I created a dm-raid1 device backed by a device that supports DISCARD +and another device that does NOT support DISCARD with the following +dm configuration: + + # echo '0 2048 mirror core 1 512 2 /dev/sda 0 /dev/sdb 0' | dmsetup create moo + # lsblk -D + NAME DISC-ALN DISC-GRAN DISC-MAX DISC-ZERO + sda 0 4K 1G 0 + `-moo (dm-0) 0 4K 1G 0 + sdb 0 0B 0B 0 + `-moo (dm-0) 0 4K 1G 0 + +Notice that the mirror device /dev/mapper/moo advertises DISCARD +support even though one of the mirror halves doesn't. + +If I issue a DISCARD request (via fstrim, mount -o discard, or ioctl +BLKDISCARD) through the mirror, kmirrord gets stuck in an infinite +loop in do_region() when it tries to issue a DISCARD request to sdb. +The problem is that when we call do_region() against sdb, num_sectors +is set to zero because q->limits.max_discard_sectors is zero. +Therefore, "remaining" never decreases and the loop never terminates. + +To fix this: before entering the loop, check for the combination of +REQ_DISCARD and no discard and return -EOPNOTSUPP to avoid hanging up +the mirror device. + +This bug was found by the unfortunate coincidence of pvmove and a +discard operation in the RHEL 6.5 kernel; upstream is also affected. + +Signed-off-by: Darrick J. Wong +Acked-by: "Martin K. Petersen" +Signed-off-by: Mike Snitzer +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/md/dm-io.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/md/dm-io.c ++++ b/drivers/md/dm-io.c +@@ -290,6 +290,12 @@ static void do_region(int rw, unsigned r + unsigned short logical_block_size = queue_logical_block_size(q); + sector_t num_sectors; + ++ /* Reject unsupported discard requests */ ++ if ((rw & REQ_DISCARD) && !blk_queue_discard(q)) { ++ dec_count(io, region, -EOPNOTSUPP); ++ return; ++ } ++ + /* + * where->count may be zero if rw holds a flush and we need to + * send a zero-sized flush. diff --git a/queue-3.19/dm-mirror-do-not-degrade-the-mirror-on-discard-error.patch b/queue-3.19/dm-mirror-do-not-degrade-the-mirror-on-discard-error.patch new file mode 100644 index 00000000000..36766eb46e5 --- /dev/null +++ b/queue-3.19/dm-mirror-do-not-degrade-the-mirror-on-discard-error.patch @@ -0,0 +1,47 @@ +From f2ed51ac64611d717d1917820a01930174c2f236 Mon Sep 17 00:00:00 2001 +From: Mikulas Patocka +Date: Thu, 12 Feb 2015 10:09:20 -0500 +Subject: dm mirror: do not degrade the mirror on discard error + +From: Mikulas Patocka + +commit f2ed51ac64611d717d1917820a01930174c2f236 upstream. + +It may be possible that a device claims discard support but it rejects +discards with -EOPNOTSUPP. It happens when using loopback on ext2/ext3 +filesystem driven by the ext4 driver. It may also happen if the +underlying devices are moved from one disk on another. + +If discard error happens, we reject the bio with -EOPNOTSUPP, but we do +not degrade the array. + +This patch fixes failed test shell/lvconvert-repair-transient.sh in the +lvm2 testsuite if the testsuite is extracted on an ext2 or ext3 +filesystem and it is being driven by the ext4 driver. + +Signed-off-by: Mikulas Patocka +Signed-off-by: Mike Snitzer +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/md/dm-raid1.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +--- a/drivers/md/dm-raid1.c ++++ b/drivers/md/dm-raid1.c +@@ -604,6 +604,15 @@ static void write_callback(unsigned long + return; + } + ++ /* ++ * If the bio is discard, return an error, but do not ++ * degrade the array. ++ */ ++ if (bio->bi_rw & REQ_DISCARD) { ++ bio_endio(bio, -EOPNOTSUPP); ++ return; ++ } ++ + for (i = 0; i < ms->nr_mirrors; i++) + if (test_bit(i, &error)) + fail_mirror(ms->mirror + i, DM_RAID1_WRITE_ERROR); diff --git a/queue-3.19/dm-snapshot-fix-a-possible-invalid-memory-access-on-unload.patch b/queue-3.19/dm-snapshot-fix-a-possible-invalid-memory-access-on-unload.patch new file mode 100644 index 00000000000..3f4b3dff0ac --- /dev/null +++ b/queue-3.19/dm-snapshot-fix-a-possible-invalid-memory-access-on-unload.patch @@ -0,0 +1,52 @@ +From 22aa66a3ee5b61e0f4a0bfeabcaa567861109ec3 Mon Sep 17 00:00:00 2001 +From: Mikulas Patocka +Date: Tue, 17 Feb 2015 14:34:00 -0500 +Subject: dm snapshot: fix a possible invalid memory access on unload + +From: Mikulas Patocka + +commit 22aa66a3ee5b61e0f4a0bfeabcaa567861109ec3 upstream. + +When the snapshot target is unloaded, snapshot_dtr() waits until +pending_exceptions_count drops to zero. Then, it destroys the snapshot. +Therefore, the function that decrements pending_exceptions_count +should not touch the snapshot structure after the decrement. + +pending_complete() calls free_pending_exception(), which decrements +pending_exceptions_count, and then it performs up_write(&s->lock) and it +calls retry_origin_bios() which dereferences s->origin. These two +memory accesses to the fields of the snapshot may touch the dm_snapshot +struture after it is freed. + +This patch moves the call to free_pending_exception() to the end of +pending_complete(), so that the snapshot will not be destroyed while +pending_complete() is in progress. + +Signed-off-by: Mikulas Patocka +Signed-off-by: Mike Snitzer +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/md/dm-snap.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/md/dm-snap.c ++++ b/drivers/md/dm-snap.c +@@ -1432,8 +1432,6 @@ out: + full_bio->bi_private = pe->full_bio_private; + atomic_inc(&full_bio->bi_remaining); + } +- free_pending_exception(pe); +- + increment_pending_exceptions_done_count(); + + up_write(&s->lock); +@@ -1450,6 +1448,8 @@ out: + } + + retry_origin_bios(s, origin_bios); ++ ++ free_pending_exception(pe); + } + + static void commit_callback(void *context, int success) diff --git a/queue-3.19/firmware-dmi_scan-fix-dmi-scan-to-handle-end-of-table.patch b/queue-3.19/firmware-dmi_scan-fix-dmi-scan-to-handle-end-of-table.patch new file mode 100644 index 00000000000..4f68756659f --- /dev/null +++ b/queue-3.19/firmware-dmi_scan-fix-dmi-scan-to-handle-end-of-table.patch @@ -0,0 +1,57 @@ +From ce204e9a4bd82e9e6e7479bca8057e45aaac5c42 Mon Sep 17 00:00:00 2001 +From: Ivan Khoronzhuk +Date: Wed, 18 Feb 2015 15:51:41 +0200 +Subject: firmware: dmi_scan: Fix dmi scan to handle "End of Table" + structure + +From: Ivan Khoronzhuk + +commit ce204e9a4bd82e9e6e7479bca8057e45aaac5c42 upstream. + +The dmi-sysfs should create "End of Table" entry, that is type 127. But +after adding initial SMBIOS v3 support fc43026278b2 ("dmi: add support +for SMBIOS 3.0 64-bit entry point") the 127-0 entry is not handled any +more, as result it's not created in dmi sysfs for instance. This is +important because the size of whole DMI table must correspond to sum of +all DMI entry sizes. + +So move the end-of-table check after it's handled by dmi_table. + +Reviewed-by: Ard Biesheuvel +Signed-off-by: Ivan Khoronzhuk +Signed-off-by: Matt Fleming +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/firmware/dmi_scan.c | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +--- a/drivers/firmware/dmi_scan.c ++++ b/drivers/firmware/dmi_scan.c +@@ -93,12 +93,6 @@ static void dmi_table(u8 *buf, u32 len, + const struct dmi_header *dm = (const struct dmi_header *)data; + + /* +- * 7.45 End-of-Table (Type 127) [SMBIOS reference spec v3.0.0] +- */ +- if (dm->type == DMI_ENTRY_END_OF_TABLE) +- break; +- +- /* + * We want to know the total length (formatted area and + * strings) before decoding to make sure we won't run off the + * table in dmi_decode or dmi_string +@@ -108,6 +102,13 @@ static void dmi_table(u8 *buf, u32 len, + data++; + if (data - buf < len - 1) + decode(dm, private_data); ++ ++ /* ++ * 7.45 End-of-Table (Type 127) [SMBIOS reference spec v3.0.0] ++ */ ++ if (dm->type == DMI_ENTRY_END_OF_TABLE) ++ break; ++ + data += 2; + i++; + } diff --git a/queue-3.19/firmware-dmi_scan-fix-dmi_len-type.patch b/queue-3.19/firmware-dmi_scan-fix-dmi_len-type.patch new file mode 100644 index 00000000000..259d2d66150 --- /dev/null +++ b/queue-3.19/firmware-dmi_scan-fix-dmi_len-type.patch @@ -0,0 +1,47 @@ +From 6d9ff473317245e3e5cd9922b4520411c2296388 Mon Sep 17 00:00:00 2001 +From: Ivan Khoronzhuk +Date: Wed, 18 Feb 2015 13:33:19 +0200 +Subject: firmware: dmi_scan: Fix dmi_len type + +From: Ivan Khoronzhuk + +commit 6d9ff473317245e3e5cd9922b4520411c2296388 upstream. + +According to SMBIOSv3 specification the length of DMI table can be +up to 32bits wide. So use appropriate type to avoid overflow. + +It's obvious that dmi_num theoretically can be more than u16 also, +so it's can be changed to u32 or at least it's better to use int +instead of u16, but on that moment I cannot imagine dmi structure +count more than 65535 and it can require changing type of vars that +work with it. So I didn't correct it. + +Acked-by: Ard Biesheuvel +Signed-off-by: Ivan Khoronzhuk +Signed-off-by: Matt Fleming +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/firmware/dmi_scan.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/firmware/dmi_scan.c ++++ b/drivers/firmware/dmi_scan.c +@@ -78,7 +78,7 @@ static const char * __init dmi_string(co + * We have to be cautious here. We have seen BIOSes with DMI pointers + * pointing to completely the wrong place for example + */ +-static void dmi_table(u8 *buf, int len, int num, ++static void dmi_table(u8 *buf, u32 len, int num, + void (*decode)(const struct dmi_header *, void *), + void *private_data) + { +@@ -114,7 +114,7 @@ static void dmi_table(u8 *buf, int len, + } + + static phys_addr_t dmi_base; +-static u16 dmi_len; ++static u32 dmi_len; + static u16 dmi_num; + + static int __init dmi_walk_early(void (*decode)(const struct dmi_header *, diff --git a/queue-3.19/series b/queue-3.19/series index 4dead3e4912..fdfc416a2da 100644 --- a/queue-3.19/series +++ b/queue-3.19/series @@ -131,3 +131,10 @@ clk-zynq-force-cpu_2x-clock-to-be-ungated.patch clk-fix-debugfs-clk-removal-before-inited.patch sunxi-clk-set-sun6i-pll1-n_start-1.patch staging-comedi-comedi_compat32.c-fix-comedi_cmd-copy-back.patch +dm-mirror-do-not-degrade-the-mirror-on-discard-error.patch +dm-io-reject-unsupported-discard-requests-with-eopnotsupp.patch +dm-fix-a-race-condition-in-dm_get_md.patch +dm-snapshot-fix-a-possible-invalid-memory-access-on-unload.patch +firmware-dmi_scan-fix-dmi_len-type.patch +firmware-dmi_scan-fix-dmi-scan-to-handle-end-of-table.patch +staging-comedi-cb_pcidas64-fix-incorrect-ai-range-code-handling.patch diff --git a/queue-3.19/staging-comedi-cb_pcidas64-fix-incorrect-ai-range-code-handling.patch b/queue-3.19/staging-comedi-cb_pcidas64-fix-incorrect-ai-range-code-handling.patch new file mode 100644 index 00000000000..9dc3d289a22 --- /dev/null +++ b/queue-3.19/staging-comedi-cb_pcidas64-fix-incorrect-ai-range-code-handling.patch @@ -0,0 +1,388 @@ +From be8e89087ec2d2c8a1ad1e3db64bf4efdfc3c298 Mon Sep 17 00:00:00 2001 +From: Ian Abbott +Date: Mon, 19 Jan 2015 14:47:27 +0000 +Subject: staging: comedi: cb_pcidas64: fix incorrect AI range code handling + +From: Ian Abbott + +commit be8e89087ec2d2c8a1ad1e3db64bf4efdfc3c298 upstream. + +The hardware range code values and list of valid ranges for the AI +subdevice is incorrect for several supported boards. The hardware range +code values for all boards except PCI-DAS4020/12 is determined by +calling `ai_range_bits_6xxx()` based on the maximum voltage of the range +and whether it is bipolar or unipolar, however it only returns the +correct hardware range code for the PCI-DAS60xx boards. For +PCI-DAS6402/16 (and /12) it returns the wrong code for the unipolar +ranges. For PCI-DAS64/Mx/16 it returns the wrong code for all the +ranges and the comedi range table is incorrect. + +Change `ai_range_bits_6xxx()` to use a look-up table pointed to by new +member `ai_range_codes` of `struct pcidas64_board` to map the comedi +range table indices to the hardware range codes. Use a new comedi range +table for the PCI-DAS64/Mx/16 boards (and the commented out variants). + +Signed-off-by: Ian Abbott +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/staging/comedi/drivers/cb_pcidas64.c | 124 ++++++++++++++++----------- + 1 file changed, 76 insertions(+), 48 deletions(-) + +--- a/drivers/staging/comedi/drivers/cb_pcidas64.c ++++ b/drivers/staging/comedi/drivers/cb_pcidas64.c +@@ -439,6 +439,29 @@ static const struct comedi_lrange ai_ran + } + }; + ++static const uint8_t ai_range_code_64xx[8] = { ++ 0x0, 0x1, 0x2, 0x3, /* bipolar 10, 5, 2,5, 1.25 */ ++ 0x8, 0x9, 0xa, 0xb /* unipolar 10, 5, 2.5, 1.25 */ ++}; ++ ++/* analog input ranges for 64-Mx boards */ ++static const struct comedi_lrange ai_ranges_64_mx = { ++ 7, { ++ BIP_RANGE(5), ++ BIP_RANGE(2.5), ++ BIP_RANGE(1.25), ++ BIP_RANGE(0.625), ++ UNI_RANGE(5), ++ UNI_RANGE(2.5), ++ UNI_RANGE(1.25) ++ } ++}; ++ ++static const uint8_t ai_range_code_64_mx[7] = { ++ 0x0, 0x1, 0x2, 0x3, /* bipolar 5, 2.5, 1.25, 0.625 */ ++ 0x9, 0xa, 0xb /* unipolar 5, 2.5, 1.25 */ ++}; ++ + /* analog input ranges for 60xx boards */ + static const struct comedi_lrange ai_ranges_60xx = { + 4, { +@@ -449,6 +472,10 @@ static const struct comedi_lrange ai_ran + } + }; + ++static const uint8_t ai_range_code_60xx[4] = { ++ 0x0, 0x1, 0x4, 0x7 /* bipolar 10, 5, 0.5, 0.05 */ ++}; ++ + /* analog input ranges for 6030, etc boards */ + static const struct comedi_lrange ai_ranges_6030 = { + 14, { +@@ -469,6 +496,11 @@ static const struct comedi_lrange ai_ran + } + }; + ++static const uint8_t ai_range_code_6030[14] = { ++ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, /* bip 10, 5, 2, 1, 0.5, 0.2, 0.1 */ ++ 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf /* uni 10, 5, 2, 1, 0.5, 0.2, 0.1 */ ++}; ++ + /* analog input ranges for 6052, etc boards */ + static const struct comedi_lrange ai_ranges_6052 = { + 15, { +@@ -490,6 +522,11 @@ static const struct comedi_lrange ai_ran + } + }; + ++static const uint8_t ai_range_code_6052[15] = { ++ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, /* bipolar 10 ... 0.05 */ ++ 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf /* unipolar 10 ... 0.1 */ ++}; ++ + /* analog input ranges for 4020 board */ + static const struct comedi_lrange ai_ranges_4020 = { + 2, { +@@ -593,6 +630,7 @@ struct pcidas64_board { + int ai_bits; /* analog input resolution */ + int ai_speed; /* fastest conversion period in ns */ + const struct comedi_lrange *ai_range_table; ++ const uint8_t *ai_range_code; + int ao_nchan; /* number of analog out channels */ + int ao_bits; /* analog output resolution */ + int ao_scan_speed; /* analog output scan speed */ +@@ -651,6 +689,7 @@ static const struct pcidas64_board pcida + .ao_scan_speed = 10000, + .layout = LAYOUT_64XX, + .ai_range_table = &ai_ranges_64xx, ++ .ai_range_code = ai_range_code_64xx, + .ao_range_table = &ao_ranges_64xx, + .ao_range_code = ao_range_code_64xx, + .ai_fifo = &ai_fifo_64xx, +@@ -666,6 +705,7 @@ static const struct pcidas64_board pcida + .ao_scan_speed = 10000, + .layout = LAYOUT_64XX, + .ai_range_table = &ai_ranges_64xx, ++ .ai_range_code = ai_range_code_64xx, + .ao_range_table = &ao_ranges_64xx, + .ao_range_code = ao_range_code_64xx, + .ai_fifo = &ai_fifo_64xx, +@@ -680,7 +720,8 @@ static const struct pcidas64_board pcida + .ao_bits = 16, + .ao_scan_speed = 10000, + .layout = LAYOUT_64XX, +- .ai_range_table = &ai_ranges_64xx, ++ .ai_range_table = &ai_ranges_64_mx, ++ .ai_range_code = ai_range_code_64_mx, + .ao_range_table = &ao_ranges_64xx, + .ao_range_code = ao_range_code_64xx, + .ai_fifo = &ai_fifo_64xx, +@@ -695,7 +736,8 @@ static const struct pcidas64_board pcida + .ao_bits = 16, + .ao_scan_speed = 10000, + .layout = LAYOUT_64XX, +- .ai_range_table = &ai_ranges_64xx, ++ .ai_range_table = &ai_ranges_64_mx, ++ .ai_range_code = ai_range_code_64_mx, + .ao_range_table = &ao_ranges_64xx, + .ao_range_code = ao_range_code_64xx, + .ai_fifo = &ai_fifo_64xx, +@@ -710,7 +752,8 @@ static const struct pcidas64_board pcida + .ao_bits = 16, + .ao_scan_speed = 10000, + .layout = LAYOUT_64XX, +- .ai_range_table = &ai_ranges_64xx, ++ .ai_range_table = &ai_ranges_64_mx, ++ .ai_range_code = ai_range_code_64_mx, + .ao_range_table = &ao_ranges_64xx, + .ao_range_code = ao_range_code_64xx, + .ai_fifo = &ai_fifo_64xx, +@@ -725,6 +768,7 @@ static const struct pcidas64_board pcida + .ao_bits = 16, + .layout = LAYOUT_60XX, + .ai_range_table = &ai_ranges_60xx, ++ .ai_range_code = ai_range_code_60xx, + .ao_range_table = &range_bipolar10, + .ao_range_code = ao_range_code_60xx, + .ai_fifo = &ai_fifo_60xx, +@@ -740,6 +784,7 @@ static const struct pcidas64_board pcida + .ao_scan_speed = 100000, + .layout = LAYOUT_60XX, + .ai_range_table = &ai_ranges_60xx, ++ .ai_range_code = ai_range_code_60xx, + .ao_range_table = &range_bipolar10, + .ao_range_code = ao_range_code_60xx, + .ai_fifo = &ai_fifo_60xx, +@@ -754,6 +799,7 @@ static const struct pcidas64_board pcida + .ao_scan_speed = 100000, + .layout = LAYOUT_60XX, + .ai_range_table = &ai_ranges_60xx, ++ .ai_range_code = ai_range_code_60xx, + .ao_range_table = &range_bipolar10, + .ao_range_code = ao_range_code_60xx, + .ai_fifo = &ai_fifo_60xx, +@@ -769,6 +815,7 @@ static const struct pcidas64_board pcida + .ao_scan_speed = 100000, + .layout = LAYOUT_60XX, + .ai_range_table = &ai_ranges_60xx, ++ .ai_range_code = ai_range_code_60xx, + .ao_range_table = &range_bipolar10, + .ao_range_code = ao_range_code_60xx, + .ai_fifo = &ai_fifo_60xx, +@@ -784,6 +831,7 @@ static const struct pcidas64_board pcida + .ao_scan_speed = 10000, + .layout = LAYOUT_60XX, + .ai_range_table = &ai_ranges_6030, ++ .ai_range_code = ai_range_code_6030, + .ao_range_table = &ao_ranges_6030, + .ao_range_code = ao_range_code_6030, + .ai_fifo = &ai_fifo_60xx, +@@ -799,6 +847,7 @@ static const struct pcidas64_board pcida + .ao_scan_speed = 10000, + .layout = LAYOUT_60XX, + .ai_range_table = &ai_ranges_6030, ++ .ai_range_code = ai_range_code_6030, + .ao_range_table = &ao_ranges_6030, + .ao_range_code = ao_range_code_6030, + .ai_fifo = &ai_fifo_60xx, +@@ -812,6 +861,7 @@ static const struct pcidas64_board pcida + .ao_nchan = 0, + .layout = LAYOUT_60XX, + .ai_range_table = &ai_ranges_6030, ++ .ai_range_code = ai_range_code_6030, + .ai_fifo = &ai_fifo_60xx, + .has_8255 = 0, + }, +@@ -823,6 +873,7 @@ static const struct pcidas64_board pcida + .ao_nchan = 0, + .layout = LAYOUT_60XX, + .ai_range_table = &ai_ranges_6030, ++ .ai_range_code = ai_range_code_6030, + .ai_fifo = &ai_fifo_60xx, + .has_8255 = 0, + }, +@@ -835,6 +886,7 @@ static const struct pcidas64_board pcida + .ao_scan_speed = 0, + .layout = LAYOUT_60XX, + .ai_range_table = &ai_ranges_60xx, ++ .ai_range_code = ai_range_code_60xx, + .ai_fifo = &ai_fifo_60xx, + .has_8255 = 0, + }, +@@ -848,6 +900,7 @@ static const struct pcidas64_board pcida + .ao_scan_speed = 100000, + .layout = LAYOUT_60XX, + .ai_range_table = &ai_ranges_60xx, ++ .ai_range_code = ai_range_code_60xx, + .ao_range_table = &range_bipolar10, + .ao_range_code = ao_range_code_60xx, + .ai_fifo = &ai_fifo_60xx, +@@ -863,6 +916,7 @@ static const struct pcidas64_board pcida + .ao_scan_speed = 100000, + .layout = LAYOUT_60XX, + .ai_range_table = &ai_ranges_60xx, ++ .ai_range_code = ai_range_code_60xx, + .ao_range_table = &range_bipolar10, + .ao_range_code = ao_range_code_60xx, + .ai_fifo = &ai_fifo_60xx, +@@ -878,6 +932,7 @@ static const struct pcidas64_board pcida + .ao_scan_speed = 1000, + .layout = LAYOUT_60XX, + .ai_range_table = &ai_ranges_6052, ++ .ai_range_code = ai_range_code_6052, + .ao_range_table = &ao_ranges_6030, + .ao_range_code = ao_range_code_6030, + .ai_fifo = &ai_fifo_60xx, +@@ -893,6 +948,7 @@ static const struct pcidas64_board pcida + .ao_scan_speed = 3333, + .layout = LAYOUT_60XX, + .ai_range_table = &ai_ranges_6052, ++ .ai_range_code = ai_range_code_6052, + .ao_range_table = &ao_ranges_6030, + .ao_range_code = ao_range_code_6030, + .ai_fifo = &ai_fifo_60xx, +@@ -908,6 +964,7 @@ static const struct pcidas64_board pcida + .ao_scan_speed = 1000, + .layout = LAYOUT_60XX, + .ai_range_table = &ai_ranges_6052, ++ .ai_range_code = ai_range_code_6052, + .ao_range_table = &ao_ranges_6030, + .ao_range_code = ao_range_code_6030, + .ai_fifo = &ai_fifo_60xx, +@@ -923,6 +980,7 @@ static const struct pcidas64_board pcida + .ao_scan_speed = 1000, + .layout = LAYOUT_60XX, + .ai_range_table = &ai_ranges_6052, ++ .ai_range_code = ai_range_code_6052, + .ao_range_table = &ao_ranges_6030, + .ao_range_code = ao_range_code_6030, + .ai_fifo = &ai_fifo_60xx, +@@ -957,6 +1015,7 @@ static const struct pcidas64_board pcida + .ao_scan_speed = 10000, + .layout = LAYOUT_64XX, + .ai_range_table = &ai_ranges_64xx, ++ .ai_range_code = ai_range_code_64xx, + .ai_fifo = ai_fifo_64xx, + .has_8255 = 1, + }, +@@ -968,7 +1027,8 @@ static const struct pcidas64_board pcida + .ao_nchan = 0, + .ao_scan_speed = 10000, + .layout = LAYOUT_64XX, +- .ai_range_table = &ai_ranges_64xx, ++ .ai_range_table = &ai_ranges_64_mx, ++ .ai_range_code = ai_range_code_64_mx, + .ai_fifo = ai_fifo_64xx, + .has_8255 = 1, + }, +@@ -980,7 +1040,8 @@ static const struct pcidas64_board pcida + .ao_nchan = 0, + .ao_scan_speed = 10000, + .layout = LAYOUT_64XX, +- .ai_range_table = &ai_ranges_64xx, ++ .ai_range_table = &ai_ranges_64_mx, ++ .ai_range_code = ai_range_code_64_mx, + .ai_fifo = ai_fifo_64xx, + .has_8255 = 1, + }, +@@ -992,7 +1053,8 @@ static const struct pcidas64_board pcida + .ao_nchan = 0, + .ao_scan_speed = 10000, + .layout = LAYOUT_64XX, +- .ai_range_table = &ai_ranges_64xx, ++ .ai_range_table = &ai_ranges_64_mx, ++ .ai_range_code = ai_range_code_64_mx, + .ai_fifo = ai_fifo_64xx, + .has_8255 = 1, + }, +@@ -1004,7 +1066,8 @@ static const struct pcidas64_board pcida + .ao_nchan = 2, + .ao_scan_speed = 10000, + .layout = LAYOUT_64XX, +- .ai_range_table = &ai_ranges_64xx, ++ .ai_range_table = &ai_ranges_64_mx, ++ .ai_range_code = ai_range_code_64_mx, + .ai_fifo = ai_fifo_64xx, + .has_8255 = 1, + }, +@@ -1016,7 +1079,8 @@ static const struct pcidas64_board pcida + .ao_nchan = 2, + .ao_scan_speed = 10000, + .layout = LAYOUT_64XX, +- .ai_range_table = &ai_ranges_64xx, ++ .ai_range_table = &ai_ranges_64_mx, ++ .ai_range_code = ai_range_code_64_mx, + .ai_fifo = ai_fifo_64xx, + .has_8255 = 1, + }, +@@ -1028,7 +1092,8 @@ static const struct pcidas64_board pcida + .ao_nchan = 2, + .ao_scan_speed = 10000, + .layout = LAYOUT_64XX, +- .ai_range_table = &ai_ranges_64xx, ++ .ai_range_table = &ai_ranges_64_mx, ++ .ai_range_code = ai_range_code_64_mx, + .ai_fifo = ai_fifo_64xx, + .has_8255 = 1, + }, +@@ -1115,45 +1180,8 @@ static unsigned int ai_range_bits_6xxx(c + unsigned int range_index) + { + const struct pcidas64_board *thisboard = dev->board_ptr; +- const struct comedi_krange *range = +- &thisboard->ai_range_table->range[range_index]; +- unsigned int bits = 0; +- +- switch (range->max) { +- case 10000000: +- bits = 0x000; +- break; +- case 5000000: +- bits = 0x100; +- break; +- case 2000000: +- case 2500000: +- bits = 0x200; +- break; +- case 1000000: +- case 1250000: +- bits = 0x300; +- break; +- case 500000: +- bits = 0x400; +- break; +- case 200000: +- case 250000: +- bits = 0x500; +- break; +- case 100000: +- bits = 0x600; +- break; +- case 50000: +- bits = 0x700; +- break; +- default: +- dev_err(dev->class_dev, "bug! in %s\n", __func__); +- break; +- } +- if (range->min == 0) +- bits += 0x900; +- return bits; ++ ++ return thisboard->ai_range_code[range_index] << 8; + } + + static unsigned int hw_revision(const struct comedi_device *dev,