in hpsa.c [5102:5449]
static int hpsa_scsi_ioaccel_raid_map(struct ctlr_info *h,
struct CommandList *c)
{
struct scsi_cmnd *cmd = c->scsi_cmd;
struct hpsa_scsi_dev_t *dev = cmd->device->hostdata;
struct raid_map_data *map = &dev->raid_map;
struct raid_map_disk_data *dd = &map->data[0];
int is_write = 0;
u32 map_index;
u64 first_block, last_block;
u32 block_cnt;
u32 blocks_per_row;
u64 first_row, last_row;
u32 first_row_offset, last_row_offset;
u32 first_column, last_column;
u64 r0_first_row, r0_last_row;
u32 r5or6_blocks_per_row;
u64 r5or6_first_row, r5or6_last_row;
u32 r5or6_first_row_offset, r5or6_last_row_offset;
u32 r5or6_first_column, r5or6_last_column;
u32 total_disks_per_row;
u32 stripesize;
u32 first_group, last_group, current_group;
u32 map_row;
u32 disk_handle;
u64 disk_block;
u32 disk_block_cnt;
u8 cdb[16];
u8 cdb_len;
u16 strip_size;
#if BITS_PER_LONG == 32
u64 tmpdiv;
#endif
int offload_to_mirror;
if (!dev)
return -1;
if (dev->in_reset)
return -1;
/* check for valid opcode, get LBA and block count */
switch (cmd->cmnd[0]) {
case WRITE_6:
is_write = 1;
fallthrough;
case READ_6:
first_block = (((cmd->cmnd[1] & 0x1F) << 16) |
(cmd->cmnd[2] << 8) |
cmd->cmnd[3]);
block_cnt = cmd->cmnd[4];
if (block_cnt == 0)
block_cnt = 256;
break;
case WRITE_10:
is_write = 1;
fallthrough;
case READ_10:
first_block =
(((u64) cmd->cmnd[2]) << 24) |
(((u64) cmd->cmnd[3]) << 16) |
(((u64) cmd->cmnd[4]) << 8) |
cmd->cmnd[5];
block_cnt =
(((u32) cmd->cmnd[7]) << 8) |
cmd->cmnd[8];
break;
case WRITE_12:
is_write = 1;
fallthrough;
case READ_12:
first_block =
(((u64) cmd->cmnd[2]) << 24) |
(((u64) cmd->cmnd[3]) << 16) |
(((u64) cmd->cmnd[4]) << 8) |
cmd->cmnd[5];
block_cnt =
(((u32) cmd->cmnd[6]) << 24) |
(((u32) cmd->cmnd[7]) << 16) |
(((u32) cmd->cmnd[8]) << 8) |
cmd->cmnd[9];
break;
case WRITE_16:
is_write = 1;
fallthrough;
case READ_16:
first_block =
(((u64) cmd->cmnd[2]) << 56) |
(((u64) cmd->cmnd[3]) << 48) |
(((u64) cmd->cmnd[4]) << 40) |
(((u64) cmd->cmnd[5]) << 32) |
(((u64) cmd->cmnd[6]) << 24) |
(((u64) cmd->cmnd[7]) << 16) |
(((u64) cmd->cmnd[8]) << 8) |
cmd->cmnd[9];
block_cnt =
(((u32) cmd->cmnd[10]) << 24) |
(((u32) cmd->cmnd[11]) << 16) |
(((u32) cmd->cmnd[12]) << 8) |
cmd->cmnd[13];
break;
default:
return IO_ACCEL_INELIGIBLE; /* process via normal I/O path */
}
last_block = first_block + block_cnt - 1;
/* check for write to non-RAID-0 */
if (is_write && dev->raid_level != 0)
return IO_ACCEL_INELIGIBLE;
/* check for invalid block or wraparound */
if (last_block >= le64_to_cpu(map->volume_blk_cnt) ||
last_block < first_block)
return IO_ACCEL_INELIGIBLE;
/* calculate stripe information for the request */
blocks_per_row = le16_to_cpu(map->data_disks_per_row) *
le16_to_cpu(map->strip_size);
strip_size = le16_to_cpu(map->strip_size);
#if BITS_PER_LONG == 32
tmpdiv = first_block;
(void) do_div(tmpdiv, blocks_per_row);
first_row = tmpdiv;
tmpdiv = last_block;
(void) do_div(tmpdiv, blocks_per_row);
last_row = tmpdiv;
first_row_offset = (u32) (first_block - (first_row * blocks_per_row));
last_row_offset = (u32) (last_block - (last_row * blocks_per_row));
tmpdiv = first_row_offset;
(void) do_div(tmpdiv, strip_size);
first_column = tmpdiv;
tmpdiv = last_row_offset;
(void) do_div(tmpdiv, strip_size);
last_column = tmpdiv;
#else
first_row = first_block / blocks_per_row;
last_row = last_block / blocks_per_row;
first_row_offset = (u32) (first_block - (first_row * blocks_per_row));
last_row_offset = (u32) (last_block - (last_row * blocks_per_row));
first_column = first_row_offset / strip_size;
last_column = last_row_offset / strip_size;
#endif
/* if this isn't a single row/column then give to the controller */
if ((first_row != last_row) || (first_column != last_column))
return IO_ACCEL_INELIGIBLE;
/* proceeding with driver mapping */
total_disks_per_row = le16_to_cpu(map->data_disks_per_row) +
le16_to_cpu(map->metadata_disks_per_row);
map_row = ((u32)(first_row >> map->parity_rotation_shift)) %
le16_to_cpu(map->row_cnt);
map_index = (map_row * total_disks_per_row) + first_column;
switch (dev->raid_level) {
case HPSA_RAID_0:
break; /* nothing special to do */
case HPSA_RAID_1:
/* Handles load balance across RAID 1 members.
* (2-drive R1 and R10 with even # of drives.)
* Appropriate for SSDs, not optimal for HDDs
* Ensure we have the correct raid_map.
*/
if (le16_to_cpu(map->layout_map_count) != 2) {
hpsa_turn_off_ioaccel_for_device(dev);
return IO_ACCEL_INELIGIBLE;
}
if (dev->offload_to_mirror)
map_index += le16_to_cpu(map->data_disks_per_row);
dev->offload_to_mirror = !dev->offload_to_mirror;
break;
case HPSA_RAID_ADM:
/* Handles N-way mirrors (R1-ADM)
* and R10 with # of drives divisible by 3.)
* Ensure we have the correct raid_map.
*/
if (le16_to_cpu(map->layout_map_count) != 3) {
hpsa_turn_off_ioaccel_for_device(dev);
return IO_ACCEL_INELIGIBLE;
}
offload_to_mirror = dev->offload_to_mirror;
raid_map_helper(map, offload_to_mirror,
&map_index, ¤t_group);
/* set mirror group to use next time */
offload_to_mirror =
(offload_to_mirror >=
le16_to_cpu(map->layout_map_count) - 1)
? 0 : offload_to_mirror + 1;
dev->offload_to_mirror = offload_to_mirror;
/* Avoid direct use of dev->offload_to_mirror within this
* function since multiple threads might simultaneously
* increment it beyond the range of dev->layout_map_count -1.
*/
break;
case HPSA_RAID_5:
case HPSA_RAID_6:
if (le16_to_cpu(map->layout_map_count) <= 1)
break;
/* Verify first and last block are in same RAID group */
r5or6_blocks_per_row =
le16_to_cpu(map->strip_size) *
le16_to_cpu(map->data_disks_per_row);
if (r5or6_blocks_per_row == 0) {
hpsa_turn_off_ioaccel_for_device(dev);
return IO_ACCEL_INELIGIBLE;
}
stripesize = r5or6_blocks_per_row *
le16_to_cpu(map->layout_map_count);
#if BITS_PER_LONG == 32
tmpdiv = first_block;
first_group = do_div(tmpdiv, stripesize);
tmpdiv = first_group;
(void) do_div(tmpdiv, r5or6_blocks_per_row);
first_group = tmpdiv;
tmpdiv = last_block;
last_group = do_div(tmpdiv, stripesize);
tmpdiv = last_group;
(void) do_div(tmpdiv, r5or6_blocks_per_row);
last_group = tmpdiv;
#else
first_group = (first_block % stripesize) / r5or6_blocks_per_row;
last_group = (last_block % stripesize) / r5or6_blocks_per_row;
#endif
if (first_group != last_group)
return IO_ACCEL_INELIGIBLE;
/* Verify request is in a single row of RAID 5/6 */
#if BITS_PER_LONG == 32
tmpdiv = first_block;
(void) do_div(tmpdiv, stripesize);
first_row = r5or6_first_row = r0_first_row = tmpdiv;
tmpdiv = last_block;
(void) do_div(tmpdiv, stripesize);
r5or6_last_row = r0_last_row = tmpdiv;
#else
first_row = r5or6_first_row = r0_first_row =
first_block / stripesize;
r5or6_last_row = r0_last_row = last_block / stripesize;
#endif
if (r5or6_first_row != r5or6_last_row)
return IO_ACCEL_INELIGIBLE;
/* Verify request is in a single column */
#if BITS_PER_LONG == 32
tmpdiv = first_block;
first_row_offset = do_div(tmpdiv, stripesize);
tmpdiv = first_row_offset;
first_row_offset = (u32) do_div(tmpdiv, r5or6_blocks_per_row);
r5or6_first_row_offset = first_row_offset;
tmpdiv = last_block;
r5or6_last_row_offset = do_div(tmpdiv, stripesize);
tmpdiv = r5or6_last_row_offset;
r5or6_last_row_offset = do_div(tmpdiv, r5or6_blocks_per_row);
tmpdiv = r5or6_first_row_offset;
(void) do_div(tmpdiv, map->strip_size);
first_column = r5or6_first_column = tmpdiv;
tmpdiv = r5or6_last_row_offset;
(void) do_div(tmpdiv, map->strip_size);
r5or6_last_column = tmpdiv;
#else
first_row_offset = r5or6_first_row_offset =
(u32)((first_block % stripesize) %
r5or6_blocks_per_row);
r5or6_last_row_offset =
(u32)((last_block % stripesize) %
r5or6_blocks_per_row);
first_column = r5or6_first_column =
r5or6_first_row_offset / le16_to_cpu(map->strip_size);
r5or6_last_column =
r5or6_last_row_offset / le16_to_cpu(map->strip_size);
#endif
if (r5or6_first_column != r5or6_last_column)
return IO_ACCEL_INELIGIBLE;
/* Request is eligible */
map_row = ((u32)(first_row >> map->parity_rotation_shift)) %
le16_to_cpu(map->row_cnt);
map_index = (first_group *
(le16_to_cpu(map->row_cnt) * total_disks_per_row)) +
(map_row * total_disks_per_row) + first_column;
break;
default:
return IO_ACCEL_INELIGIBLE;
}
if (unlikely(map_index >= RAID_MAP_MAX_ENTRIES))
return IO_ACCEL_INELIGIBLE;
c->phys_disk = dev->phys_disk[map_index];
if (!c->phys_disk)
return IO_ACCEL_INELIGIBLE;
disk_handle = dd[map_index].ioaccel_handle;
disk_block = le64_to_cpu(map->disk_starting_blk) +
first_row * le16_to_cpu(map->strip_size) +
(first_row_offset - first_column *
le16_to_cpu(map->strip_size));
disk_block_cnt = block_cnt;
/* handle differing logical/physical block sizes */
if (map->phys_blk_shift) {
disk_block <<= map->phys_blk_shift;
disk_block_cnt <<= map->phys_blk_shift;
}
BUG_ON(disk_block_cnt > 0xffff);
/* build the new CDB for the physical disk I/O */
if (disk_block > 0xffffffff) {
cdb[0] = is_write ? WRITE_16 : READ_16;
cdb[1] = 0;
cdb[2] = (u8) (disk_block >> 56);
cdb[3] = (u8) (disk_block >> 48);
cdb[4] = (u8) (disk_block >> 40);
cdb[5] = (u8) (disk_block >> 32);
cdb[6] = (u8) (disk_block >> 24);
cdb[7] = (u8) (disk_block >> 16);
cdb[8] = (u8) (disk_block >> 8);
cdb[9] = (u8) (disk_block);
cdb[10] = (u8) (disk_block_cnt >> 24);
cdb[11] = (u8) (disk_block_cnt >> 16);
cdb[12] = (u8) (disk_block_cnt >> 8);
cdb[13] = (u8) (disk_block_cnt);
cdb[14] = 0;
cdb[15] = 0;
cdb_len = 16;
} else {
cdb[0] = is_write ? WRITE_10 : READ_10;
cdb[1] = 0;
cdb[2] = (u8) (disk_block >> 24);
cdb[3] = (u8) (disk_block >> 16);
cdb[4] = (u8) (disk_block >> 8);
cdb[5] = (u8) (disk_block);
cdb[6] = 0;
cdb[7] = (u8) (disk_block_cnt >> 8);
cdb[8] = (u8) (disk_block_cnt);
cdb[9] = 0;
cdb_len = 10;
}
return hpsa_scsi_ioaccel_queue_command(h, c, disk_handle, cdb, cdb_len,
dev->scsi3addr,
dev->phys_disk[map_index]);
}