diff options
author | Alex Elder <elder@inktank.com> | 2012-08-09 13:33:26 -0400 |
---|---|---|
committer | Alex Elder <elder@inktank.com> | 2012-10-01 15:30:50 -0400 |
commit | 65ccfe21dd8fb402547bb1c50bbc2737c4ef37b8 (patch) | |
tree | dc2406bae46fd943deee2ca03e4edacaa0cd65bf /drivers/block | |
parent | df111be6310fc41d059a485368e3c51a684859c2 (diff) |
rbd: split up rbd_get_segment()
There are two places where rbd_get_segment() is called. One, in
rbd_rq_fn(), only needs to know the length within a segment that an
I/O request should be. The other, in rbd_do_op(), also needs the
name of the object and the offset within it for the I/O request.
Split out rbd_segment_name() into three dedicated functions:
- rbd_segment_name() allocates and formats the name of the
object for a segment containing a given rbd image offset
- rbd_segment_offset() computes the offset within a segment for
a given rbd image offset
- rbd_segment_length() computes the length to use for I/O within
a segment for a request, not to exceed the end of a segment
object.
In the new functions be a bit more careful, checking for possible
error conditions:
- watch for errors or overflows returned by snprintf()
- catch (using BUG_ON()) potential overflow conditions
when computing segment length
Signed-off-by: Alex Elder <elder@inktank.com>
Reviewed-by: Yehuda Sadeh <yehuda@inktank.com>
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/rbd.c | 66 |
1 files changed, 40 insertions, 26 deletions
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 6da6990a7b57..7ba70c49bbdb 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c | |||
@@ -669,27 +669,47 @@ static void rbd_header_free(struct rbd_image_header *header) | |||
669 | header->snapc = NULL; | 669 | header->snapc = NULL; |
670 | } | 670 | } |
671 | 671 | ||
672 | /* | 672 | static char *rbd_segment_name(struct rbd_device *rbd_dev, u64 offset) |
673 | * get the actual striped segment name, offset and length | 673 | { |
674 | */ | 674 | char *name; |
675 | static u64 rbd_get_segment(struct rbd_image_header *header, | 675 | u64 segment; |
676 | const char *object_prefix, | 676 | int ret; |
677 | u64 ofs, u64 len, | 677 | |
678 | char *seg_name, u64 *segofs) | 678 | name = kmalloc(RBD_MAX_SEG_NAME_LEN + 1, GFP_NOIO); |
679 | if (!name) | ||
680 | return NULL; | ||
681 | segment = offset >> rbd_dev->header.obj_order; | ||
682 | ret = snprintf(name, RBD_MAX_SEG_NAME_LEN, "%s.%012llx", | ||
683 | rbd_dev->header.object_prefix, segment); | ||
684 | if (ret < 0 || ret >= RBD_MAX_SEG_NAME_LEN) { | ||
685 | pr_err("error formatting segment name for #%llu (%d)\n", | ||
686 | segment, ret); | ||
687 | kfree(name); | ||
688 | name = NULL; | ||
689 | } | ||
690 | |||
691 | return name; | ||
692 | } | ||
693 | |||
694 | static u64 rbd_segment_offset(struct rbd_device *rbd_dev, u64 offset) | ||
679 | { | 695 | { |
680 | u64 seg = ofs >> header->obj_order; | 696 | u64 segment_size = (u64) 1 << rbd_dev->header.obj_order; |
681 | 697 | ||
682 | if (seg_name) | 698 | return offset & (segment_size - 1); |
683 | snprintf(seg_name, RBD_MAX_SEG_NAME_LEN, | 699 | } |
684 | "%s.%012llx", object_prefix, seg); | 700 | |
701 | static u64 rbd_segment_length(struct rbd_device *rbd_dev, | ||
702 | u64 offset, u64 length) | ||
703 | { | ||
704 | u64 segment_size = (u64) 1 << rbd_dev->header.obj_order; | ||
685 | 705 | ||
686 | ofs = ofs & ((1 << header->obj_order) - 1); | 706 | offset &= segment_size - 1; |
687 | len = min_t(u64, len, (1 << header->obj_order) - ofs); | ||
688 | 707 | ||
689 | if (segofs) | 708 | BUG_ON(length > U64_MAX - offset); |
690 | *segofs = ofs; | 709 | if (offset + length > segment_size) |
710 | length = segment_size - offset; | ||
691 | 711 | ||
692 | return len; | 712 | return length; |
693 | } | 713 | } |
694 | 714 | ||
695 | static int rbd_get_num_segments(struct rbd_image_header *header, | 715 | static int rbd_get_num_segments(struct rbd_image_header *header, |
@@ -1127,14 +1147,11 @@ static int rbd_do_op(struct request *rq, | |||
1127 | struct ceph_osd_req_op *ops; | 1147 | struct ceph_osd_req_op *ops; |
1128 | u32 payload_len; | 1148 | u32 payload_len; |
1129 | 1149 | ||
1130 | seg_name = kmalloc(RBD_MAX_SEG_NAME_LEN + 1, GFP_NOIO); | 1150 | seg_name = rbd_segment_name(rbd_dev, ofs); |
1131 | if (!seg_name) | 1151 | if (!seg_name) |
1132 | return -ENOMEM; | 1152 | return -ENOMEM; |
1133 | 1153 | seg_len = rbd_segment_length(rbd_dev, ofs, len); | |
1134 | seg_len = rbd_get_segment(&rbd_dev->header, | 1154 | seg_ofs = rbd_segment_offset(rbd_dev, ofs); |
1135 | rbd_dev->header.object_prefix, | ||
1136 | ofs, len, | ||
1137 | seg_name, &seg_ofs); | ||
1138 | 1155 | ||
1139 | payload_len = (flags & CEPH_OSD_FLAG_WRITE ? seg_len : 0); | 1156 | payload_len = (flags & CEPH_OSD_FLAG_WRITE ? seg_len : 0); |
1140 | 1157 | ||
@@ -1545,10 +1562,7 @@ static void rbd_rq_fn(struct request_queue *q) | |||
1545 | do { | 1562 | do { |
1546 | /* a bio clone to be passed down to OSD req */ | 1563 | /* a bio clone to be passed down to OSD req */ |
1547 | dout("rq->bio->bi_vcnt=%hu\n", rq->bio->bi_vcnt); | 1564 | dout("rq->bio->bi_vcnt=%hu\n", rq->bio->bi_vcnt); |
1548 | op_size = rbd_get_segment(&rbd_dev->header, | 1565 | op_size = rbd_segment_length(rbd_dev, ofs, size); |
1549 | rbd_dev->header.object_prefix, | ||
1550 | ofs, size, | ||
1551 | NULL, NULL); | ||
1552 | kref_get(&coll->kref); | 1566 | kref_get(&coll->kref); |
1553 | bio = bio_chain_clone(&rq_bio, &next_bio, &bp, | 1567 | bio = bio_chain_clone(&rq_bio, &next_bio, &bp, |
1554 | op_size, GFP_ATOMIC); | 1568 | op_size, GFP_ATOMIC); |