bsg: bidi bio map failure fix
Subsystems (3)
PATCH 1/1 bsg: bidi bio map failure fix
If blk_rq_map_user requires more than one bio, and fails mapping somewhere after the first bio, it will return with rq->bio set to non-NULL, but it will have already unmapped the partial bio. The "out:" error exit section will see the non-null bio and try to unmap it again, triggering a mapcount bug via bad_page().
--- block/bsg.c | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-)
block/bsg.c +3 -1
@@ -295,8 +295,10 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) dxferp = (void *)(unsigned long )hdr->din_xferp; ret =blk_rq_map_user (q, next_rq, dxferp, hdr->din_xfer_len); -if (ret) +if (ret) { + next_rq->bio =NULL ;/* do not unmap twice */ goto out; + } }if (hdr->dout_xfer_len) {
Signature · 1 line
1.5.3.8
James Bottomley
maintainerQuoted 16 lines · Pete Wyckoff
If
blk_rq_map_userrequires more than one bio, and fails mapping somewhere after the first bio, it will return withrq->bioset to non-NULL, but it will have already unmapped the partial bio. The "out:" error exit section will see the non-null bio and try to unmap it again, triggering a mapcount bug viabad_page().Signed-off-by Pete Wyckoff <[email protected]>--- block/bsg.c | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-)block/bsg.c +0
-0
block/bsg.c bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) · line 299
Quoted 6 lines
block/bsg.c +2
-1@@ -295,8 +295,10 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) dxferp = (void *)(unsigned long )hdr->din_xferp; ret =blk_rq_map_user (q, next_rq, dxferp, hdr->din_xfer_len); -if (ret) +if (ret) { + next_rq->bio =NULL ;/* do not unmap twice */
Nice ... that's a nasty asymmetry of the blk_rq_map_user API. The map takes a request gets a ref and fills in the bio. The unmap has to be called on the bio leaving you to clear the now removed bio reference manually.
James
Quoted 18 lines · James Bottomley
Quoted 17 lines · Pete Wyckoff
If
blk_rq_map_userrequires more than one bio, and fails mapping somewhere after the first bio, it will return withrq->bioset to non-NULL, but it will have already unmapped the partial bio. The "out:" error exit section will see the non-null bio and try to unmap it again, triggering a mapcount bug viabad_page().Signed-off-by Pete Wyckoff <[email protected]>--- block/bsg.c | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-)block/bsg.c +0
-0@@ -295,8 +295,10 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr)
Quoted 10 lines
block/bsg.c bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) · line 299
Quoted 5 lines
block/bsg.c +2
-1dxferp = (void *)(unsigned long )hdr->din_xferp; ret =blk_rq_map_user (q, next_rq, dxferp, hdr->din_xfer_len); -if (ret) +if (ret) { + next_rq->bio =NULL ;/* do not unmap twice */ Nice ... that's a nasty asymmetry of the
blk_rq_map_userAPI. The map takes a request gets a ref and fills in the bio. The unmap has to be called on the bio leaving you to clear the now removed bio reference manually.
It is nasty, how about fixing that instead?
block/blk-map.c +1 -0
@@ -143,6 +143,7 @@ int blk_rq_map_user(struct request_queue *q, struct request *rq,return 0 ; unmap_rq:blk_rq_unmap_user (bio); + rq->bio =NULL ;return ret; }EXPORT_SYMBOL (blk_rq_map_user);
Signature · 1 line
Jens Axboe
Quoted 30 lines · On Mon, 18 Feb 2008 13:55:08 +0100 Jens Axboe <[email protected]> wrote:
Quoted 18 lines · James Bottomley
Quoted 17 lines · Pete Wyckoff
If
blk_rq_map_userrequires more than one bio, and fails mapping somewhere after the first bio, it will return withrq->bioset to non-NULL, but it will have already unmapped the partial bio. The "out:" error exit section will see the non-null bio and try to unmap it again, triggering a mapcount bug viabad_page().Signed-off-by Pete Wyckoff <[email protected]>--- block/bsg.c | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-)block/bsg.c +0
-0@@ -295,8 +295,10 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr)Quoted 10 lines
block/bsg.c bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) · line 299
Quoted 5 lines
block/bsg.c +2
-1dxferp = (void *)(unsigned long )hdr->din_xferp; ret =blk_rq_map_user (q, next_rq, dxferp, hdr->din_xfer_len); -if (ret) +if (ret) { + next_rq->bio =NULL ;/* do not unmap twice */ Nice ... that's a nasty asymmetry of the
blk_rq_map_userAPI. The map takes a request gets a ref and fills in the bio. The unmap has to be called on the bio leaving you to clear the now removed bio reference manually.
Quoted 1 line
It is nasty, how about fixing that instead?
Yeah, looks better for me though the blk_rq_map_user API is still a bit hacky, as James said.
James, Pete's patch is still in scsi-fixes, so how about dropping it and sending this patch via the block?
Quoted 20 lines
block/blk-map.c +1
-0@@ -143,6 +143,7 @@ int blk_rq_map_user(struct request_queue *q, struct request *rq,return 0 ; unmap_rq:blk_rq_unmap_user (bio); + rq->bio =NULL ;return ret; }EXPORT_SYMBOL (blk_rq_map_user);Signature · 7 lines
-- Jens Axboe - To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 33 lines · FUJITA Tomonori
Quoted 30 lines · On Mon, 18 Feb 2008 13:55:08 +0100 Jens Axboe <[email protected]> wrote:
Quoted 28 lines · James Bottomley
Quoted 16 lines · Pete Wyckoff
If
blk_rq_map_userrequires more than one bio, and fails mapping somewhere after the first bio, it will return withrq->bioset to non-NULL, but it will have already unmapped the partial bio. The "out:" error exit section will see the non-null bio and try to unmap it again, triggering a mapcount bug viabad_page().Signed-off-by Pete Wyckoff <[email protected]>--- block/bsg.c | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-)block/bsg.c +0
-0block/bsg.c bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) · line 299
Quoted 6 lines
block/bsg.c +2
-1@@ -295,8 +295,10 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) dxferp = (void *)(unsigned long )hdr->din_xferp; ret =blk_rq_map_user (q, next_rq, dxferp, hdr->din_xfer_len); -if (ret) +if (ret) { + next_rq->bio =NULL ;/* do not unmap twice */ Nice ... that's a nasty asymmetry of the
blk_rq_map_userAPI. The map takes a request gets a ref and fills in the bio. The unmap has to be called on the bio leaving you to clear the now removed bio reference manually.
Quoted 4 lines
Quoted 1 line
It is nasty, how about fixing that instead?
Yeah, looks better for me though the
blk_rq_map_userAPI is still a bit hacky, as James said.
Seems symmetric to me now, either we fail and everything is cleaned up, or return success. What remains?
Signature · 1 line
Jens Axboe
James Bottomley
maintainerQuoted 2 lines · Jens Axboe
Seems symmetric to me now, either we fail and everything is cleaned up, or return success. What remains?
My main symmetry complaint was the API: The map takes a request, the unmap takes a bio.
James
Quoted 6 lines · On Mon, 18 Feb 2008 09:09:07 -0600 James Bottomley…
Quoted 2 lines · Jens Axboe
Seems symmetric to me now, either we fail and everything is cleaned up, or return success. What remains?
My main symmetry complaint was the API: The map takes a request, the unmap takes a bio.
Yeah, it would be nice if we avoid such code:
blk_rq_map_user(q, rq, ...)
bio = rq->bio
blk_execute_rq(q, ...blk_rq_unmap_user(bio);
I think that none of the users of blk_rq_map_user is interested in bio, the details of how kernel manage I/Os. At least, we can remove bio stuff in bsg if blk_rq_map_user and blk_rq_unmap_user take requests.
Quoted 6 lines · James Bottomley
Quoted 2 lines · Jens Axboe
Seems symmetric to me now, either we fail and everything is cleaned up, or return success. What remains?
My main symmetry complaint was the API: The map takes a request, the unmap takes a bio.
Oh that, well that was done to make it easier for existing users. Feel free to send a patch changing that, however it was done that way on purpose at the time being.
Signature · 1 line
Jens Axboe
James Bottomley
maintainerQuoted 33 lines · FUJITA Tomonori
Quoted 30 lines · On Mon, 18 Feb 2008 13:55:08 +0100 Jens Axboe <[email protected]> wrote:
Quoted 28 lines · James Bottomley
Quoted 16 lines · Pete Wyckoff
If
blk_rq_map_userrequires more than one bio, and fails mapping somewhere after the first bio, it will return withrq->bioset to non-NULL, but it will have already unmapped the partial bio. The "out:" error exit section will see the non-null bio and try to unmap it again, triggering a mapcount bug viabad_page().Signed-off-by Pete Wyckoff <[email protected]>--- block/bsg.c | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-)block/bsg.c +0
-0block/bsg.c bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) · line 299
Quoted 6 lines
block/bsg.c +2
-1@@ -295,8 +295,10 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) dxferp = (void *)(unsigned long )hdr->din_xferp; ret =blk_rq_map_user (q, next_rq, dxferp, hdr->din_xfer_len); -if (ret) +if (ret) { + next_rq->bio =NULL ;/* do not unmap twice */ Nice ... that's a nasty asymmetry of the
blk_rq_map_userAPI. The map takes a request gets a ref and fills in the bio. The unmap has to be called on the bio leaving you to clear the now removed bio reference manually.
Quoted 7 lines
Quoted 1 line
It is nasty, how about fixing that instead?
Yeah, looks better for me though the
blk_rq_map_userAPI is still a bit hacky, as James said.James, Pete's patch is still in scsi-fixes, so how about dropping it and sending this patch via the block?
Yes, sure.
James
Quoted 17 lines · On Tue, 12 Feb 2008 15:40:24 -0500 Pete Wyckoff <[email protected]> wrote:
If
blk_rq_map_userrequires more than one bio, and fails mapping somewhere after the first bio, it will return withrq->bioset to non-NULL, but it will have already unmapped the partial bio. The "out:" error exit section will see the non-null bio and try to unmap it again, triggering a mapcount bug viabad_page().Signed-off-by Pete Wyckoff <[email protected]>--- block/bsg.c | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-)block/bsg.c +0
-0@@ -295,8 +295,10 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr)
block/bsg.c bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) · line 304
Quoted 10 lines
block/bsg.c +3
-1dxferp = (void *)(unsigned long )hdr->din_xferp; ret =blk_rq_map_user (q, next_rq, dxferp, hdr->din_xfer_len); -if (ret) +if (ret) { + next_rq->bio =NULL ;/* do not unmap twice */ goto out; + } }if (hdr->dout_xfer_len) {
Thanks!
James, please put this to the scsi-fixes tree.