Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 154272
b: refs/heads/master
c: 754c5fc
h: refs/heads/master
v: v3
  • Loading branch information
Mike Snitzer authored and Alasdair G Kergon committed Jun 22, 2009
1 parent 5ea473e commit f08de7e
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 96 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 18d8594dd93a1ae2fafd591ec026e87d743292bf
refs/heads/master: 754c5fc7ebb417b23601a6222a6005cc2e7f2913
185 changes: 102 additions & 83 deletions trunk/drivers/md/dm-table.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,6 @@ struct dm_table {
/* a list of devices used by this table */
struct list_head devices;

/*
* These are optimistic limits taken from all the
* targets, some targets will need smaller limits.
*/
struct queue_limits limits;

/* events get handed up using this callback */
void (*event_fn)(void *);
void *event_context;
Expand Down Expand Up @@ -346,18 +340,21 @@ static void close_dev(struct dm_dev_internal *d, struct mapped_device *md)
/*
* If possible, this checks an area of a destination device is valid.
*/
static int device_area_is_valid(struct dm_target *ti, struct block_device *bdev,
sector_t start, sector_t len)
static int device_area_is_valid(struct dm_target *ti, struct dm_dev *dev,
sector_t start, void *data)
{
sector_t dev_size = i_size_read(bdev->bd_inode) >> SECTOR_SHIFT;
struct queue_limits *limits = data;
struct block_device *bdev = dev->bdev;
sector_t dev_size =
i_size_read(bdev->bd_inode) >> SECTOR_SHIFT;
unsigned short logical_block_size_sectors =
ti->limits.logical_block_size >> SECTOR_SHIFT;
limits->logical_block_size >> SECTOR_SHIFT;
char b[BDEVNAME_SIZE];

if (!dev_size)
return 1;

if ((start >= dev_size) || (start + len > dev_size)) {
if ((start >= dev_size) || (start + ti->len > dev_size)) {
DMWARN("%s: %s too small for target",
dm_device_name(ti->table->md), bdevname(bdev, b));
return 0;
Expand All @@ -371,16 +368,16 @@ static int device_area_is_valid(struct dm_target *ti, struct block_device *bdev,
"logical block size %hu of %s",
dm_device_name(ti->table->md),
(unsigned long long)start,
ti->limits.logical_block_size, bdevname(bdev, b));
limits->logical_block_size, bdevname(bdev, b));
return 0;
}

if (len & (logical_block_size_sectors - 1)) {
if (ti->len & (logical_block_size_sectors - 1)) {
DMWARN("%s: len=%llu not aligned to h/w "
"logical block size %hu of %s",
dm_device_name(ti->table->md),
(unsigned long long)len,
ti->limits.logical_block_size, bdevname(bdev, b));
(unsigned long long)ti->len,
limits->logical_block_size, bdevname(bdev, b));
return 0;
}

Expand Down Expand Up @@ -479,18 +476,21 @@ static int __table_get_device(struct dm_table *t, struct dm_target *ti,
*/
#define min_not_zero(l, r) (l == 0) ? r : ((r == 0) ? l : min(l, r))

void dm_set_device_limits(struct dm_target *ti, struct block_device *bdev)
int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
sector_t start, void *data)
{
struct queue_limits *limits = data;
struct block_device *bdev = dev->bdev;
struct request_queue *q = bdev_get_queue(bdev);
char b[BDEVNAME_SIZE];

if (unlikely(!q)) {
DMWARN("%s: Cannot set limits for nonexistent device %s",
dm_device_name(ti->table->md), bdevname(bdev, b));
return;
return 0;
}

if (blk_stack_limits(&ti->limits, &q->limits, 0) < 0)
if (blk_stack_limits(limits, &q->limits, start) < 0)
DMWARN("%s: target device %s is misaligned",
dm_device_name(ti->table->md), bdevname(bdev, b));

Expand All @@ -501,32 +501,21 @@ void dm_set_device_limits(struct dm_target *ti, struct block_device *bdev)
*/

if (q->merge_bvec_fn && !ti->type->merge)
ti->limits.max_sectors =
min_not_zero(ti->limits.max_sectors,
limits->max_sectors =
min_not_zero(limits->max_sectors,
(unsigned int) (PAGE_SIZE >> 9));
return 0;
}
EXPORT_SYMBOL_GPL(dm_set_device_limits);

int dm_get_device(struct dm_target *ti, const char *path, sector_t start,
sector_t len, fmode_t mode, struct dm_dev **result)
{
int r = __table_get_device(ti->table, ti, path,
start, len, mode, result);

if (r)
return r;

dm_set_device_limits(ti, (*result)->bdev);

if (!device_area_is_valid(ti, (*result)->bdev, start, len)) {
dm_put_device(ti, *result);
*result = NULL;
return -EINVAL;
}

return r;
return __table_get_device(ti->table, ti, path,
start, len, mode, result);
}


/*
* Decrement a devices use count and remove it if necessary.
*/
Expand Down Expand Up @@ -641,49 +630,22 @@ int dm_split_args(int *argc, char ***argvp, char *input)
return 0;
}

static void init_valid_queue_limits(struct queue_limits *limits)
{
if (!limits->max_sectors)
limits->max_sectors = SAFE_MAX_SECTORS;
if (!limits->max_hw_sectors)
limits->max_hw_sectors = SAFE_MAX_SECTORS;
if (!limits->max_phys_segments)
limits->max_phys_segments = MAX_PHYS_SEGMENTS;
if (!limits->max_hw_segments)
limits->max_hw_segments = MAX_HW_SEGMENTS;
if (!limits->logical_block_size)
limits->logical_block_size = 1 << SECTOR_SHIFT;
if (!limits->physical_block_size)
limits->physical_block_size = 1 << SECTOR_SHIFT;
if (!limits->io_min)
limits->io_min = 1 << SECTOR_SHIFT;
if (!limits->max_segment_size)
limits->max_segment_size = MAX_SEGMENT_SIZE;
if (!limits->seg_boundary_mask)
limits->seg_boundary_mask = BLK_SEG_BOUNDARY_MASK;
if (!limits->bounce_pfn)
limits->bounce_pfn = -1;
/*
* The other fields (alignment_offset, io_opt, misaligned)
* hold 0 from the kzalloc().
*/
}

/*
* Impose necessary and sufficient conditions on a devices's table such
* that any incoming bio which respects its logical_block_size can be
* processed successfully. If it falls across the boundary between
* two or more targets, the size of each piece it gets split into must
* be compatible with the logical_block_size of the target processing it.
*/
static int validate_hardware_logical_block_alignment(struct dm_table *table)
static int validate_hardware_logical_block_alignment(struct dm_table *table,
struct queue_limits *limits)
{
/*
* This function uses arithmetic modulo the logical_block_size
* (in units of 512-byte sectors).
*/
unsigned short device_logical_block_size_sects =
table->limits.logical_block_size >> SECTOR_SHIFT;
limits->logical_block_size >> SECTOR_SHIFT;

/*
* Offset of the start of the next table entry, mod logical_block_size.
Expand All @@ -697,6 +659,7 @@ static int validate_hardware_logical_block_alignment(struct dm_table *table)
unsigned short remaining = 0;

struct dm_target *uninitialized_var(ti);
struct queue_limits ti_limits;
unsigned i = 0;

/*
Expand All @@ -705,12 +668,19 @@ static int validate_hardware_logical_block_alignment(struct dm_table *table)
while (i < dm_table_get_num_targets(table)) {
ti = dm_table_get_target(table, i++);

blk_set_default_limits(&ti_limits);

/* combine all target devices' limits */
if (ti->type->iterate_devices)
ti->type->iterate_devices(ti, dm_set_device_limits,
&ti_limits);

/*
* If the remaining sectors fall entirely within this
* table entry are they compatible with its logical_block_size?
*/
if (remaining < ti->len &&
remaining & ((ti->limits.logical_block_size >>
remaining & ((ti_limits.logical_block_size >>
SECTOR_SHIFT) - 1))
break; /* Error */

Expand All @@ -723,11 +693,11 @@ static int validate_hardware_logical_block_alignment(struct dm_table *table)

if (remaining) {
DMWARN("%s: table line %u (start sect %llu len %llu) "
"not aligned to hardware logical block size %hu",
"not aligned to h/w logical block size %hu",
dm_device_name(table->md), i,
(unsigned long long) ti->begin,
(unsigned long long) ti->len,
table->limits.logical_block_size);
limits->logical_block_size);
return -EINVAL;
}

Expand Down Expand Up @@ -786,12 +756,6 @@ int dm_table_add_target(struct dm_table *t, const char *type,

t->highs[t->num_targets++] = tgt->begin + tgt->len - 1;

if (blk_stack_limits(&t->limits, &tgt->limits, 0) < 0)
DMWARN("%s: target device (start sect %llu len %llu) "
"is misaligned",
dm_device_name(t->md),
(unsigned long long) tgt->begin,
(unsigned long long) tgt->len);
return 0;

bad:
Expand Down Expand Up @@ -834,12 +798,6 @@ int dm_table_complete(struct dm_table *t)
int r = 0;
unsigned int leaf_nodes;

init_valid_queue_limits(&t->limits);

r = validate_hardware_logical_block_alignment(t);
if (r)
return r;

/* how many indexes will the btree have ? */
leaf_nodes = dm_div_up(t->num_targets, KEYS_PER_NODE);
t->depth = 1 + int_log(leaf_nodes, CHILDREN_PER_NODE);
Expand Down Expand Up @@ -914,6 +872,57 @@ struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector)
return &t->targets[(KEYS_PER_NODE * n) + k];
}

/*
* Establish the new table's queue_limits and validate them.
*/
int dm_calculate_queue_limits(struct dm_table *table,
struct queue_limits *limits)
{
struct dm_target *uninitialized_var(ti);
struct queue_limits ti_limits;
unsigned i = 0;

blk_set_default_limits(limits);

while (i < dm_table_get_num_targets(table)) {
blk_set_default_limits(&ti_limits);

ti = dm_table_get_target(table, i++);

if (!ti->type->iterate_devices)
goto combine_limits;

/*
* Combine queue limits of all the devices this target uses.
*/
ti->type->iterate_devices(ti, dm_set_device_limits,
&ti_limits);

/*
* Check each device area is consistent with the target's
* overall queue limits.
*/
if (!ti->type->iterate_devices(ti, device_area_is_valid,
&ti_limits))
return -EINVAL;

combine_limits:
/*
* Merge this target's queue limits into the overall limits
* for the table.
*/
if (blk_stack_limits(limits, &ti_limits, 0) < 0)
DMWARN("%s: target device "
"(start sect %llu len %llu) "
"is misaligned",
dm_device_name(table->md),
(unsigned long long) ti->begin,
(unsigned long long) ti->len);
}

return validate_hardware_logical_block_alignment(table, limits);
}

/*
* Set the integrity profile for this device if all devices used have
* matching profiles.
Expand Down Expand Up @@ -953,14 +962,24 @@ static void dm_table_set_integrity(struct dm_table *t)
return;
}

void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q)
void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
struct queue_limits *limits)
{
/*
* Each target device in the table has a data area that should normally
* be aligned such that the DM device's alignment_offset is 0.
* FIXME: Propagate alignment_offsets up the stack and warn of
* sub-optimal or inconsistent settings.
*/
limits->alignment_offset = 0;
limits->misaligned = 0;

/*
* Copy table's limits to the DM device's request_queue
*/
q->limits = t->limits;
q->limits = *limits;

if (t->limits.no_cluster)
if (limits->no_cluster)
queue_flag_clear_unlocked(QUEUE_FLAG_CLUSTER, q);
else
queue_flag_set_unlocked(QUEUE_FLAG_CLUSTER, q);
Expand Down
12 changes: 9 additions & 3 deletions trunk/drivers/md/dm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1313,7 +1313,8 @@ static void __set_size(struct mapped_device *md, sector_t size)
mutex_unlock(&md->bdev->bd_inode->i_mutex);
}

static int __bind(struct mapped_device *md, struct dm_table *t)
static int __bind(struct mapped_device *md, struct dm_table *t,
struct queue_limits *limits)
{
struct request_queue *q = md->queue;
sector_t size;
Expand All @@ -1337,7 +1338,7 @@ static int __bind(struct mapped_device *md, struct dm_table *t)

write_lock(&md->map_lock);
md->map = t;
dm_table_set_restrictions(t, q);
dm_table_set_restrictions(t, q, limits);
write_unlock(&md->map_lock);

return 0;
Expand Down Expand Up @@ -1562,6 +1563,7 @@ static void dm_queue_flush(struct mapped_device *md)
*/
int dm_swap_table(struct mapped_device *md, struct dm_table *table)
{
struct queue_limits limits;
int r = -EINVAL;

mutex_lock(&md->suspend_lock);
Expand All @@ -1570,8 +1572,12 @@ int dm_swap_table(struct mapped_device *md, struct dm_table *table)
if (!dm_suspended(md))
goto out;

r = dm_calculate_queue_limits(table, &limits);
if (r)
goto out;

__unbind(md);
r = __bind(md, table);
r = __bind(md, table, &limits);

out:
mutex_unlock(&md->suspend_lock);
Expand Down
5 changes: 4 additions & 1 deletion trunk/drivers/md/dm.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ void dm_table_event_callback(struct dm_table *t,
void (*fn)(void *), void *context);
struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index);
struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector);
void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q);
int dm_calculate_queue_limits(struct dm_table *table,
struct queue_limits *limits);
void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
struct queue_limits *limits);
struct list_head *dm_table_get_devices(struct dm_table *t);
void dm_table_presuspend_targets(struct dm_table *t);
void dm_table_postsuspend_targets(struct dm_table *t);
Expand Down
Loading

0 comments on commit f08de7e

Please sign in to comment.