diff options
| -rw-r--r-- | block/bsg.c | 74 |
1 files changed, 49 insertions, 25 deletions
diff --git a/block/bsg.c b/block/bsg.c index 5f4abc902a00..13ecc951a4c0 100644 --- a/block/bsg.c +++ b/block/bsg.c | |||
| @@ -95,6 +95,7 @@ struct bsg_command { | |||
| 95 | struct list_head list; | 95 | struct list_head list; |
| 96 | struct request *rq; | 96 | struct request *rq; |
| 97 | struct bio *bio; | 97 | struct bio *bio; |
| 98 | struct bio *bidi_bio; | ||
| 98 | int err; | 99 | int err; |
| 99 | struct sg_io_v4 hdr; | 100 | struct sg_io_v4 hdr; |
| 100 | struct sg_io_v4 __user *uhdr; | 101 | struct sg_io_v4 __user *uhdr; |
| @@ -243,16 +244,6 @@ bsg_validate_sgv4_hdr(request_queue_t *q, struct sg_io_v4 *hdr, int *rw) | |||
| 243 | if (hdr->protocol || hdr->subprotocol) | 244 | if (hdr->protocol || hdr->subprotocol) |
| 244 | return -EINVAL; | 245 | return -EINVAL; |
| 245 | 246 | ||
| 246 | /* | ||
| 247 | * looks sane, if no data then it should be fine from our POV | ||
| 248 | */ | ||
| 249 | if (!hdr->dout_xfer_len && !hdr->din_xfer_len) | ||
| 250 | return 0; | ||
| 251 | |||
| 252 | /* not supported currently */ | ||
| 253 | if (hdr->dout_xfer_len && hdr->din_xfer_len) | ||
| 254 | return -EINVAL; | ||
| 255 | |||
| 256 | *rw = hdr->dout_xfer_len ? WRITE : READ; | 247 | *rw = hdr->dout_xfer_len ? WRITE : READ; |
| 257 | 248 | ||
| 258 | return 0; | 249 | return 0; |
| @@ -265,7 +256,7 @@ static struct request * | |||
| 265 | bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) | 256 | bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) |
| 266 | { | 257 | { |
| 267 | request_queue_t *q = bd->queue; | 258 | request_queue_t *q = bd->queue; |
| 268 | struct request *rq; | 259 | struct request *rq, *next_rq = NULL; |
| 269 | int ret, rw = 0; /* shut up gcc */ | 260 | int ret, rw = 0; /* shut up gcc */ |
| 270 | unsigned int dxfer_len; | 261 | unsigned int dxfer_len; |
| 271 | void *dxferp = NULL; | 262 | void *dxferp = NULL; |
| @@ -282,11 +273,30 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) | |||
| 282 | * map scatter-gather elements seperately and string them to request | 273 | * map scatter-gather elements seperately and string them to request |
| 283 | */ | 274 | */ |
| 284 | rq = blk_get_request(q, rw, GFP_KERNEL); | 275 | rq = blk_get_request(q, rw, GFP_KERNEL); |
| 276 | if (!rq) | ||
| 277 | return ERR_PTR(-ENOMEM); | ||
| 285 | ret = blk_fill_sgv4_hdr_rq(q, rq, hdr, test_bit(BSG_F_WRITE_PERM, | 278 | ret = blk_fill_sgv4_hdr_rq(q, rq, hdr, test_bit(BSG_F_WRITE_PERM, |
| 286 | &bd->flags)); | 279 | &bd->flags)); |
| 287 | if (ret) { | 280 | if (ret) |
| 288 | blk_put_request(rq); | 281 | goto out; |
| 289 | return ERR_PTR(ret); | 282 | |
| 283 | if (rw == WRITE && hdr->din_xfer_len) { | ||
| 284 | if (!test_bit(QUEUE_FLAG_BIDI, &q->queue_flags)) { | ||
| 285 | ret = -EOPNOTSUPP; | ||
| 286 | goto out; | ||
| 287 | } | ||
| 288 | |||
| 289 | next_rq = blk_get_request(q, READ, GFP_KERNEL); | ||
| 290 | if (!next_rq) { | ||
| 291 | ret = -ENOMEM; | ||
| 292 | goto out; | ||
| 293 | } | ||
| 294 | rq->next_rq = next_rq; | ||
| 295 | |||
| 296 | dxferp = (void*)(unsigned long)hdr->din_xferp; | ||
| 297 | ret = blk_rq_map_user(q, next_rq, dxferp, hdr->din_xfer_len); | ||
| 298 | if (ret) | ||
| 299 | goto out; | ||
| 290 | } | 300 | } |
| 291 | 301 | ||
| 292 | if (hdr->dout_xfer_len) { | 302 | if (hdr->dout_xfer_len) { |
| @@ -300,14 +310,17 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) | |||
| 300 | 310 | ||
| 301 | if (dxfer_len) { | 311 | if (dxfer_len) { |
| 302 | ret = blk_rq_map_user(q, rq, dxferp, dxfer_len); | 312 | ret = blk_rq_map_user(q, rq, dxferp, dxfer_len); |
| 303 | if (ret) { | 313 | if (ret) |
| 304 | dprintk("failed map at %d\n", ret); | 314 | goto out; |
| 305 | blk_put_request(rq); | ||
| 306 | rq = ERR_PTR(ret); | ||
| 307 | } | ||
| 308 | } | 315 | } |
| 309 | |||
| 310 | return rq; | 316 | return rq; |
| 317 | out: | ||
| 318 | blk_put_request(rq); | ||
| 319 | if (next_rq) { | ||
| 320 | blk_rq_unmap_user(next_rq->bio); | ||
| 321 | blk_put_request(next_rq); | ||
| 322 | } | ||
| 323 | return ERR_PTR(ret); | ||
| 311 | } | 324 | } |
| 312 | 325 | ||
| 313 | /* | 326 | /* |
| @@ -346,6 +359,8 @@ static void bsg_add_command(struct bsg_device *bd, request_queue_t *q, | |||
| 346 | */ | 359 | */ |
| 347 | bc->rq = rq; | 360 | bc->rq = rq; |
| 348 | bc->bio = rq->bio; | 361 | bc->bio = rq->bio; |
| 362 | if (rq->next_rq) | ||
| 363 | bc->bidi_bio = rq->next_rq->bio; | ||
| 349 | bc->hdr.duration = jiffies; | 364 | bc->hdr.duration = jiffies; |
| 350 | spin_lock_irq(&bd->lock); | 365 | spin_lock_irq(&bd->lock); |
| 351 | list_add_tail(&bc->list, &bd->busy_list); | 366 | list_add_tail(&bc->list, &bd->busy_list); |
| @@ -402,7 +417,7 @@ static struct bsg_command *bsg_get_done_cmd(struct bsg_device *bd) | |||
| 402 | } | 417 | } |
| 403 | 418 | ||
| 404 | static int blk_complete_sgv4_hdr_rq(struct request *rq, struct sg_io_v4 *hdr, | 419 | static int blk_complete_sgv4_hdr_rq(struct request *rq, struct sg_io_v4 *hdr, |
| 405 | struct bio *bio) | 420 | struct bio *bio, struct bio *bidi_bio) |
| 406 | { | 421 | { |
| 407 | int ret = 0; | 422 | int ret = 0; |
| 408 | 423 | ||
| @@ -431,6 +446,11 @@ static int blk_complete_sgv4_hdr_rq(struct request *rq, struct sg_io_v4 *hdr, | |||
| 431 | ret = -EFAULT; | 446 | ret = -EFAULT; |
| 432 | } | 447 | } |
| 433 | 448 | ||
| 449 | if (rq->next_rq) { | ||
| 450 | blk_rq_unmap_user(bidi_bio); | ||
| 451 | blk_put_request(rq->next_rq); | ||
| 452 | } | ||
| 453 | |||
| 434 | blk_rq_unmap_user(bio); | 454 | blk_rq_unmap_user(bio); |
| 435 | blk_put_request(rq); | 455 | blk_put_request(rq); |
| 436 | 456 | ||
| @@ -477,7 +497,8 @@ static int bsg_complete_all_commands(struct bsg_device *bd) | |||
| 477 | if (IS_ERR(bc)) | 497 | if (IS_ERR(bc)) |
| 478 | break; | 498 | break; |
| 479 | 499 | ||
| 480 | tret = blk_complete_sgv4_hdr_rq(bc->rq, &bc->hdr, bc->bio); | 500 | tret = blk_complete_sgv4_hdr_rq(bc->rq, &bc->hdr, bc->bio, |
| 501 | bc->bidi_bio); | ||
| 481 | if (!ret) | 502 | if (!ret) |
| 482 | ret = tret; | 503 | ret = tret; |
| 483 | 504 | ||
| @@ -511,7 +532,8 @@ __bsg_read(char __user *buf, size_t count, struct bsg_device *bd, | |||
| 511 | * after completing the request. so do that here, | 532 | * after completing the request. so do that here, |
| 512 | * bsg_complete_work() cannot do that for us | 533 | * bsg_complete_work() cannot do that for us |
| 513 | */ | 534 | */ |
| 514 | ret = blk_complete_sgv4_hdr_rq(bc->rq, &bc->hdr, bc->bio); | 535 | ret = blk_complete_sgv4_hdr_rq(bc->rq, &bc->hdr, bc->bio, |
| 536 | bc->bidi_bio); | ||
| 515 | 537 | ||
| 516 | if (copy_to_user(buf, (char *) &bc->hdr, sizeof(bc->hdr))) | 538 | if (copy_to_user(buf, (char *) &bc->hdr, sizeof(bc->hdr))) |
| 517 | ret = -EFAULT; | 539 | ret = -EFAULT; |
| @@ -868,7 +890,7 @@ bsg_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | |||
| 868 | } | 890 | } |
| 869 | case SG_IO: { | 891 | case SG_IO: { |
| 870 | struct request *rq; | 892 | struct request *rq; |
| 871 | struct bio *bio; | 893 | struct bio *bio, *bidi_bio = NULL; |
| 872 | struct sg_io_v4 hdr; | 894 | struct sg_io_v4 hdr; |
| 873 | 895 | ||
| 874 | if (copy_from_user(&hdr, uarg, sizeof(hdr))) | 896 | if (copy_from_user(&hdr, uarg, sizeof(hdr))) |
| @@ -879,8 +901,10 @@ bsg_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | |||
| 879 | return PTR_ERR(rq); | 901 | return PTR_ERR(rq); |
| 880 | 902 | ||
| 881 | bio = rq->bio; | 903 | bio = rq->bio; |
| 904 | if (rq->next_rq) | ||
| 905 | bidi_bio = rq->next_rq->bio; | ||
| 882 | blk_execute_rq(bd->queue, NULL, rq, 0); | 906 | blk_execute_rq(bd->queue, NULL, rq, 0); |
| 883 | blk_complete_sgv4_hdr_rq(rq, &hdr, bio); | 907 | blk_complete_sgv4_hdr_rq(rq, &hdr, bio, bidi_bio); |
| 884 | 908 | ||
| 885 | if (copy_to_user(uarg, &hdr, sizeof(hdr))) | 909 | if (copy_to_user(uarg, &hdr, sizeof(hdr))) |
| 886 | return -EFAULT; | 910 | return -EFAULT; |
