diff options
author | James Smart <James.Smart@Emulex.Com> | 2009-03-26 13:33:19 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2009-06-12 15:20:05 -0400 |
commit | 9e4f5e29610162fd426366f3b29e3cc6e575b858 (patch) | |
tree | aad7fed6b70d87bc9aae10563fa1e71535b11de9 /drivers/scsi/scsi_transport_fc.c | |
parent | e349792a385ed47390d156155b1a1e19af1bf163 (diff) |
[SCSI] FC Pass Thru support
Attached is the ELS/CT pass-thru patch for the FC Transport. The patch
creates a generic framework that lays on top of bsg and the SGIO v4 ioctl
in order to pass transaction requests to LLDD's.
The interface supports the following operations:
On an fc_host basis:
Request login to the specified N_Port_ID, creating an fc_rport.
Request logout of the specified N_Port_ID, deleting an fc_rport
Send ELS request to specified N_Port_ID w/o requiring a login, and
wait for ELS response.
Send CT request to specified N_Port_ID and wait for CT response.
Login is required, but LLDD is allowed to manage login and decide
whether it stays in place after the request is satisfied.
Vendor-Unique request. Allows a LLDD-specific request to be passed
to the LLDD, and the passing of a response back to the application.
On an fc_rport basis:
Send ELS request to nport and wait for ELS response.
Send CT request to nport and wait for CT response.
The patch also exports several headers from include/scsi such that
they can be available to user-space applications:
include/scsi/scsi.h
include/scsi/scsi_netlink.h
include/scsi/scsi_netlink_fc.h
include/scsi/scsi_bsg_fc.h
For further information, refer to the last RFC:
http://marc.info/?l=linux-scsi&m=123436574018579&w=2
Note: Documentation is still spotty and will be added later.
[bharrosh@panasas.com: update for new block API]
Signed-off-by: James Smart <james.smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi/scsi_transport_fc.c')
-rw-r--r-- | drivers/scsi/scsi_transport_fc.c | 614 |
1 files changed, 613 insertions, 1 deletions
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index a152f89ae51..3f64d93b6c8 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/netlink.h> | 35 | #include <linux/netlink.h> |
36 | #include <net/netlink.h> | 36 | #include <net/netlink.h> |
37 | #include <scsi/scsi_netlink_fc.h> | 37 | #include <scsi/scsi_netlink_fc.h> |
38 | #include <scsi/scsi_bsg_fc.h> | ||
38 | #include "scsi_priv.h" | 39 | #include "scsi_priv.h" |
39 | #include "scsi_transport_fc_internal.h" | 40 | #include "scsi_transport_fc_internal.h" |
40 | 41 | ||
@@ -43,6 +44,10 @@ static void fc_vport_sched_delete(struct work_struct *work); | |||
43 | static int fc_vport_setup(struct Scsi_Host *shost, int channel, | 44 | static int fc_vport_setup(struct Scsi_Host *shost, int channel, |
44 | struct device *pdev, struct fc_vport_identifiers *ids, | 45 | struct device *pdev, struct fc_vport_identifiers *ids, |
45 | struct fc_vport **vport); | 46 | struct fc_vport **vport); |
47 | static int fc_bsg_hostadd(struct Scsi_Host *, struct fc_host_attrs *); | ||
48 | static int fc_bsg_rportadd(struct Scsi_Host *, struct fc_rport *); | ||
49 | static void fc_bsg_remove(struct request_queue *); | ||
50 | static void fc_bsg_goose_queue(struct fc_rport *); | ||
46 | 51 | ||
47 | /* | 52 | /* |
48 | * Redefine so that we can have same named attributes in the | 53 | * Redefine so that we can have same named attributes in the |
@@ -411,13 +416,26 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev, | |||
411 | return -ENOMEM; | 416 | return -ENOMEM; |
412 | } | 417 | } |
413 | 418 | ||
419 | fc_bsg_hostadd(shost, fc_host); | ||
420 | /* ignore any bsg add error - we just can't do sgio */ | ||
421 | |||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | static int fc_host_remove(struct transport_container *tc, struct device *dev, | ||
426 | struct device *cdev) | ||
427 | { | ||
428 | struct Scsi_Host *shost = dev_to_shost(dev); | ||
429 | struct fc_host_attrs *fc_host = shost_to_fc_host(shost); | ||
430 | |||
431 | fc_bsg_remove(fc_host->rqst_q); | ||
414 | return 0; | 432 | return 0; |
415 | } | 433 | } |
416 | 434 | ||
417 | static DECLARE_TRANSPORT_CLASS(fc_host_class, | 435 | static DECLARE_TRANSPORT_CLASS(fc_host_class, |
418 | "fc_host", | 436 | "fc_host", |
419 | fc_host_setup, | 437 | fc_host_setup, |
420 | NULL, | 438 | fc_host_remove, |
421 | NULL); | 439 | NULL); |
422 | 440 | ||
423 | /* | 441 | /* |
@@ -2375,6 +2393,7 @@ fc_rport_final_delete(struct work_struct *work) | |||
2375 | scsi_flush_work(shost); | 2393 | scsi_flush_work(shost); |
2376 | 2394 | ||
2377 | fc_terminate_rport_io(rport); | 2395 | fc_terminate_rport_io(rport); |
2396 | |||
2378 | /* | 2397 | /* |
2379 | * Cancel any outstanding timers. These should really exist | 2398 | * Cancel any outstanding timers. These should really exist |
2380 | * only when rmmod'ing the LLDD and we're asking for | 2399 | * only when rmmod'ing the LLDD and we're asking for |
@@ -2407,6 +2426,8 @@ fc_rport_final_delete(struct work_struct *work) | |||
2407 | (i->f->dev_loss_tmo_callbk)) | 2426 | (i->f->dev_loss_tmo_callbk)) |
2408 | i->f->dev_loss_tmo_callbk(rport); | 2427 | i->f->dev_loss_tmo_callbk(rport); |
2409 | 2428 | ||
2429 | fc_bsg_remove(rport->rqst_q); | ||
2430 | |||
2410 | transport_remove_device(dev); | 2431 | transport_remove_device(dev); |
2411 | device_del(dev); | 2432 | device_del(dev); |
2412 | transport_destroy_device(dev); | 2433 | transport_destroy_device(dev); |
@@ -2494,6 +2515,9 @@ fc_rport_create(struct Scsi_Host *shost, int channel, | |||
2494 | transport_add_device(dev); | 2515 | transport_add_device(dev); |
2495 | transport_configure_device(dev); | 2516 | transport_configure_device(dev); |
2496 | 2517 | ||
2518 | fc_bsg_rportadd(shost, rport); | ||
2519 | /* ignore any bsg add error - we just can't do sgio */ | ||
2520 | |||
2497 | if (rport->roles & FC_PORT_ROLE_FCP_TARGET) { | 2521 | if (rport->roles & FC_PORT_ROLE_FCP_TARGET) { |
2498 | /* initiate a scan of the target */ | 2522 | /* initiate a scan of the target */ |
2499 | rport->flags |= FC_RPORT_SCAN_PENDING; | 2523 | rport->flags |= FC_RPORT_SCAN_PENDING; |
@@ -2658,6 +2682,8 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel, | |||
2658 | spin_unlock_irqrestore(shost->host_lock, | 2682 | spin_unlock_irqrestore(shost->host_lock, |
2659 | flags); | 2683 | flags); |
2660 | 2684 | ||
2685 | fc_bsg_goose_queue(rport); | ||
2686 | |||
2661 | return rport; | 2687 | return rport; |
2662 | } | 2688 | } |
2663 | } | 2689 | } |
@@ -3343,6 +3369,592 @@ fc_vport_sched_delete(struct work_struct *work) | |||
3343 | } | 3369 | } |
3344 | 3370 | ||
3345 | 3371 | ||
3372 | /* | ||
3373 | * BSG support | ||
3374 | */ | ||
3375 | |||
3376 | |||
3377 | /** | ||
3378 | * fc_destroy_bsgjob - routine to teardown/delete a fc bsg job | ||
3379 | * @job: fc_bsg_job that is to be torn down | ||
3380 | */ | ||
3381 | static void | ||
3382 | fc_destroy_bsgjob(struct fc_bsg_job *job) | ||
3383 | { | ||
3384 | unsigned long flags; | ||
3385 | |||
3386 | spin_lock_irqsave(&job->job_lock, flags); | ||
3387 | if (job->ref_cnt) { | ||
3388 | spin_unlock_irqrestore(&job->job_lock, flags); | ||
3389 | return; | ||
3390 | } | ||
3391 | spin_unlock_irqrestore(&job->job_lock, flags); | ||
3392 | |||
3393 | put_device(job->dev); /* release reference for the request */ | ||
3394 | |||
3395 | kfree(job->request_payload.sg_list); | ||
3396 | kfree(job->reply_payload.sg_list); | ||
3397 | kfree(job); | ||
3398 | } | ||
3399 | |||
3400 | |||
3401 | /** | ||
3402 | * fc_bsg_jobdone - completion routine for bsg requests that the LLD has | ||
3403 | * completed | ||
3404 | * @job: fc_bsg_job that is complete | ||
3405 | */ | ||
3406 | static void | ||
3407 | fc_bsg_jobdone(struct fc_bsg_job *job) | ||
3408 | { | ||
3409 | struct request *req = job->req; | ||
3410 | struct request *rsp = req->next_rq; | ||
3411 | unsigned long flags; | ||
3412 | int err; | ||
3413 | |||
3414 | spin_lock_irqsave(&job->job_lock, flags); | ||
3415 | job->state_flags |= FC_RQST_STATE_DONE; | ||
3416 | job->ref_cnt--; | ||
3417 | spin_unlock_irqrestore(&job->job_lock, flags); | ||
3418 | |||
3419 | err = job->req->errors = job->reply->result; | ||
3420 | if (err < 0) | ||
3421 | /* we're only returning the result field in the reply */ | ||
3422 | job->req->sense_len = sizeof(uint32_t); | ||
3423 | else | ||
3424 | job->req->sense_len = job->reply_len; | ||
3425 | |||
3426 | /* we assume all request payload was transferred, residual == 0 */ | ||
3427 | req->resid_len = 0; | ||
3428 | |||
3429 | if (rsp) { | ||
3430 | WARN_ON(job->reply->reply_payload_rcv_len > rsp->resid_len); | ||
3431 | |||
3432 | /* set reply (bidi) residual */ | ||
3433 | rsp->resid_len -= min(job->reply->reply_payload_rcv_len, | ||
3434 | rsp->resid_len); | ||
3435 | } | ||
3436 | |||
3437 | blk_end_request_all(req, err); | ||
3438 | |||
3439 | fc_destroy_bsgjob(job); | ||
3440 | } | ||
3441 | |||
3442 | |||
3443 | /** | ||
3444 | * fc_bsg_job_timeout - handler for when a bsg request timesout | ||
3445 | * @req: request that timed out | ||
3446 | */ | ||
3447 | static enum blk_eh_timer_return | ||
3448 | fc_bsg_job_timeout(struct request *req) | ||
3449 | { | ||
3450 | struct fc_bsg_job *job = (void *) req->special; | ||
3451 | struct Scsi_Host *shost = job->shost; | ||
3452 | struct fc_internal *i = to_fc_internal(shost->transportt); | ||
3453 | unsigned long flags; | ||
3454 | int err = 0, done = 0; | ||
3455 | |||
3456 | if (job->rport && job->rport->port_state == FC_PORTSTATE_BLOCKED) | ||
3457 | return BLK_EH_RESET_TIMER; | ||
3458 | |||
3459 | spin_lock_irqsave(&job->job_lock, flags); | ||
3460 | if (job->state_flags & FC_RQST_STATE_DONE) | ||
3461 | done = 1; | ||
3462 | else | ||
3463 | job->ref_cnt++; | ||
3464 | spin_unlock_irqrestore(&job->job_lock, flags); | ||
3465 | |||
3466 | if (!done && i->f->bsg_timeout) { | ||
3467 | /* call LLDD to abort the i/o as it has timed out */ | ||
3468 | err = i->f->bsg_timeout(job); | ||
3469 | if (err) | ||
3470 | printk(KERN_ERR "ERROR: FC BSG request timeout - LLD " | ||
3471 | "abort failed with status %d\n", err); | ||
3472 | } | ||
3473 | |||
3474 | if (!done) { | ||
3475 | spin_lock_irqsave(&job->job_lock, flags); | ||
3476 | job->ref_cnt--; | ||
3477 | spin_unlock_irqrestore(&job->job_lock, flags); | ||
3478 | fc_destroy_bsgjob(job); | ||
3479 | } | ||
3480 | |||
3481 | /* the blk_end_sync_io() doesn't check the error */ | ||
3482 | return BLK_EH_HANDLED; | ||
3483 | } | ||
3484 | |||
3485 | |||
3486 | |||
3487 | static int | ||
3488 | fc_bsg_map_buffer(struct fc_bsg_buffer *buf, struct request *req) | ||
3489 | { | ||
3490 | size_t sz = (sizeof(struct scatterlist) * req->nr_phys_segments); | ||
3491 | |||
3492 | BUG_ON(!req->nr_phys_segments); | ||
3493 | |||
3494 | buf->sg_list = kzalloc(sz, GFP_KERNEL); | ||
3495 | if (!buf->sg_list) | ||
3496 | return -ENOMEM; | ||
3497 | sg_init_table(buf->sg_list, req->nr_phys_segments); | ||
3498 | buf->sg_cnt = blk_rq_map_sg(req->q, req, buf->sg_list); | ||
3499 | buf->payload_len = blk_rq_bytes(req); | ||
3500 | return 0; | ||
3501 | } | ||
3502 | |||
3503 | |||
3504 | /** | ||
3505 | * fc_req_to_bsgjob - Allocate/create the fc_bsg_job structure for the | ||
3506 | * bsg request | ||
3507 | * @shost: SCSI Host corresponding to the bsg object | ||
3508 | * @rport: (optional) FC Remote Port corresponding to the bsg object | ||
3509 | * @req: BSG request that needs a job structure | ||
3510 | */ | ||
3511 | static int | ||
3512 | fc_req_to_bsgjob(struct Scsi_Host *shost, struct fc_rport *rport, | ||
3513 | struct request *req) | ||
3514 | { | ||
3515 | struct fc_internal *i = to_fc_internal(shost->transportt); | ||
3516 | struct request *rsp = req->next_rq; | ||
3517 | struct fc_bsg_job *job; | ||
3518 | int ret; | ||
3519 | |||
3520 | BUG_ON(req->special); | ||
3521 | |||
3522 | job = kzalloc(sizeof(struct fc_bsg_job) + i->f->dd_bsg_size, | ||
3523 | GFP_KERNEL); | ||
3524 | if (!job) | ||
3525 | return -ENOMEM; | ||
3526 | |||
3527 | /* | ||
3528 | * Note: this is a bit silly. | ||
3529 | * The request gets formatted as a SGIO v4 ioctl request, which | ||
3530 | * then gets reformatted as a blk request, which then gets | ||
3531 | * reformatted as a fc bsg request. And on completion, we have | ||
3532 | * to wrap return results such that SGIO v4 thinks it was a scsi | ||
3533 | * status. I hope this was all worth it. | ||
3534 | */ | ||
3535 | |||
3536 | req->special = job; | ||
3537 | job->shost = shost; | ||
3538 | job->rport = rport; | ||
3539 | job->req = req; | ||
3540 | if (i->f->dd_bsg_size) | ||
3541 | job->dd_data = (void *)&job[1]; | ||
3542 | spin_lock_init(&job->job_lock); | ||
3543 | job->request = (struct fc_bsg_request *)req->cmd; | ||
3544 | job->request_len = req->cmd_len; | ||
3545 | job->reply = req->sense; | ||
3546 | job->reply_len = SCSI_SENSE_BUFFERSIZE; /* Size of sense buffer | ||
3547 | * allocated */ | ||
3548 | if (req->bio) { | ||
3549 | ret = fc_bsg_map_buffer(&job->request_payload, req); | ||
3550 | if (ret) | ||
3551 | goto failjob_rls_job; | ||
3552 | } | ||
3553 | if (rsp && rsp->bio) { | ||
3554 | ret = fc_bsg_map_buffer(&job->reply_payload, rsp); | ||
3555 | if (ret) | ||
3556 | goto failjob_rls_rqst_payload; | ||
3557 | } | ||
3558 | job->job_done = fc_bsg_jobdone; | ||
3559 | if (rport) | ||
3560 | job->dev = &rport->dev; | ||
3561 | else | ||
3562 | job->dev = &shost->shost_gendev; | ||
3563 | get_device(job->dev); /* take a reference for the request */ | ||
3564 | |||
3565 | job->ref_cnt = 1; | ||
3566 | |||
3567 | return 0; | ||
3568 | |||
3569 | |||
3570 | failjob_rls_rqst_payload: | ||
3571 | kfree(job->request_payload.sg_list); | ||
3572 | failjob_rls_job: | ||
3573 | kfree(job); | ||
3574 | return -ENOMEM; | ||
3575 | } | ||
3576 | |||
3577 | |||
3578 | enum fc_dispatch_result { | ||
3579 | FC_DISPATCH_BREAK, /* on return, q is locked, break from q loop */ | ||
3580 | FC_DISPATCH_LOCKED, /* on return, q is locked, continue on */ | ||
3581 | FC_DISPATCH_UNLOCKED, /* on return, q is unlocked, continue on */ | ||
3582 | }; | ||
3583 | |||
3584 | |||
3585 | /** | ||
3586 | * fc_bsg_host_dispatch - process fc host bsg requests and dispatch to LLDD | ||
3587 | * @shost: scsi host rport attached to | ||
3588 | * @job: bsg job to be processed | ||
3589 | */ | ||
3590 | static enum fc_dispatch_result | ||
3591 | fc_bsg_host_dispatch(struct request_queue *q, struct Scsi_Host *shost, | ||
3592 | struct fc_bsg_job *job) | ||
3593 | { | ||
3594 | struct fc_internal *i = to_fc_internal(shost->transportt); | ||
3595 | int cmdlen = sizeof(uint32_t); /* start with length of msgcode */ | ||
3596 | int ret; | ||
3597 | |||
3598 | /* Validate the host command */ | ||
3599 | switch (job->request->msgcode) { | ||
3600 | case FC_BSG_HST_ADD_RPORT: | ||
3601 | cmdlen += sizeof(struct fc_bsg_host_add_rport); | ||
3602 | break; | ||
3603 | |||
3604 | case FC_BSG_HST_DEL_RPORT: | ||
3605 | cmdlen += sizeof(struct fc_bsg_host_del_rport); | ||
3606 | break; | ||
3607 | |||
3608 | case FC_BSG_HST_ELS_NOLOGIN: | ||
3609 | cmdlen += sizeof(struct fc_bsg_host_els); | ||
3610 | /* there better be a xmt and rcv payloads */ | ||
3611 | if ((!job->request_payload.payload_len) || | ||
3612 | (!job->reply_payload.payload_len)) { | ||
3613 | ret = -EINVAL; | ||
3614 | goto fail_host_msg; | ||
3615 | } | ||
3616 | break; | ||
3617 | |||
3618 | case FC_BSG_HST_CT: | ||
3619 | cmdlen += sizeof(struct fc_bsg_host_ct); | ||
3620 | /* there better be xmt and rcv payloads */ | ||
3621 | if ((!job->request_payload.payload_len) || | ||
3622 | (!job->reply_payload.payload_len)) { | ||
3623 | ret = -EINVAL; | ||
3624 | goto fail_host_msg; | ||
3625 | } | ||
3626 | break; | ||
3627 | |||
3628 | case FC_BSG_HST_VENDOR: | ||
3629 | cmdlen += sizeof(struct fc_bsg_host_vendor); | ||
3630 | if ((shost->hostt->vendor_id == 0L) || | ||
3631 | (job->request->rqst_data.h_vendor.vendor_id != | ||
3632 | shost->hostt->vendor_id)) { | ||
3633 | ret = -ESRCH; | ||
3634 | goto fail_host_msg; | ||
3635 | } | ||
3636 | break; | ||
3637 | |||
3638 | default: | ||
3639 | ret = -EBADR; | ||
3640 | goto fail_host_msg; | ||
3641 | } | ||
3642 | |||
3643 | /* check if we really have all the request data needed */ | ||
3644 | if (job->request_len < cmdlen) { | ||
3645 | ret = -ENOMSG; | ||
3646 | goto fail_host_msg; | ||
3647 | } | ||
3648 | |||
3649 | ret = i->f->bsg_request(job); | ||
3650 | if (!ret) | ||
3651 | return FC_DISPATCH_UNLOCKED; | ||
3652 | |||
3653 | fail_host_msg: | ||
3654 | /* return the errno failure code as the only status */ | ||
3655 | BUG_ON(job->reply_len < sizeof(uint32_t)); | ||
3656 | job->reply->result = ret; | ||
3657 | job->reply_len = sizeof(uint32_t); | ||
3658 | fc_bsg_jobdone(job); | ||
3659 | return FC_DISPATCH_UNLOCKED; | ||
3660 | } | ||
3661 | |||
3662 | |||
3663 | /* | ||
3664 | * fc_bsg_goose_queue - restart rport queue in case it was stopped | ||
3665 | * @rport: rport to be restarted | ||
3666 | */ | ||
3667 | static void | ||
3668 | fc_bsg_goose_queue(struct fc_rport *rport) | ||
3669 | { | ||
3670 | int flagset; | ||
3671 | |||
3672 | if (!rport->rqst_q) | ||
3673 | return; | ||
3674 | |||
3675 | get_device(&rport->dev); | ||
3676 | |||
3677 | spin_lock(rport->rqst_q->queue_lock); | ||
3678 | flagset = test_bit(QUEUE_FLAG_REENTER, &rport->rqst_q->queue_flags) && | ||
3679 | !test_bit(QUEUE_FLAG_REENTER, &rport->rqst_q->queue_flags); | ||
3680 | if (flagset) | ||
3681 | queue_flag_set(QUEUE_FLAG_REENTER, rport->rqst_q); | ||
3682 | __blk_run_queue(rport->rqst_q); | ||
3683 | if (flagset) | ||
3684 | queue_flag_clear(QUEUE_FLAG_REENTER, rport->rqst_q); | ||
3685 | spin_unlock(rport->rqst_q->queue_lock); | ||
3686 | |||
3687 | put_device(&rport->dev); | ||
3688 | } | ||
3689 | |||
3690 | |||
3691 | /** | ||
3692 | * fc_bsg_rport_dispatch - process rport bsg requests and dispatch to LLDD | ||
3693 | * @shost: scsi host rport attached to | ||
3694 | * @rport: rport request destined to | ||
3695 | * @job: bsg job to be processed | ||
3696 | */ | ||
3697 | static enum fc_dispatch_result | ||
3698 | fc_bsg_rport_dispatch(struct request_queue *q, struct Scsi_Host *shost, | ||
3699 | struct fc_rport *rport, struct fc_bsg_job *job) | ||
3700 | { | ||
3701 | struct fc_internal *i = to_fc_internal(shost->transportt); | ||
3702 | int cmdlen = sizeof(uint32_t); /* start with length of msgcode */ | ||
3703 | int ret; | ||
3704 | |||
3705 | /* Validate the rport command */ | ||
3706 | switch (job->request->msgcode) { | ||
3707 | case FC_BSG_RPT_ELS: | ||
3708 | cmdlen += sizeof(struct fc_bsg_rport_els); | ||
3709 | goto check_bidi; | ||
3710 | |||
3711 | case FC_BSG_RPT_CT: | ||
3712 | cmdlen += sizeof(struct fc_bsg_rport_ct); | ||
3713 | check_bidi: | ||
3714 | /* there better be xmt and rcv payloads */ | ||
3715 | if ((!job->request_payload.payload_len) || | ||
3716 | (!job->reply_payload.payload_len)) { | ||
3717 | ret = -EINVAL; | ||
3718 | goto fail_rport_msg; | ||
3719 | } | ||
3720 | break; | ||
3721 | default: | ||
3722 | ret = -EBADR; | ||
3723 | goto fail_rport_msg; | ||
3724 | } | ||
3725 | |||
3726 | /* check if we really have all the request data needed */ | ||
3727 | if (job->request_len < cmdlen) { | ||
3728 | ret = -ENOMSG; | ||
3729 | goto fail_rport_msg; | ||
3730 | } | ||
3731 | |||
3732 | ret = i->f->bsg_request(job); | ||
3733 | if (!ret) | ||
3734 | return FC_DISPATCH_UNLOCKED; | ||
3735 | |||
3736 | fail_rport_msg: | ||
3737 | /* return the errno failure code as the only status */ | ||
3738 | BUG_ON(job->reply_len < sizeof(uint32_t)); | ||
3739 | job->reply->result = ret; | ||
3740 | job->reply_len = sizeof(uint32_t); | ||
3741 | fc_bsg_jobdone(job); | ||
3742 | return FC_DISPATCH_UNLOCKED; | ||
3743 | } | ||
3744 | |||
3745 | |||
3746 | /** | ||
3747 | * fc_bsg_request_handler - generic handler for bsg requests | ||
3748 | * @q: request queue to manage | ||
3749 | * @shost: Scsi_Host related to the bsg object | ||
3750 | * @rport: FC remote port related to the bsg object (optional) | ||
3751 | * @dev: device structure for bsg object | ||
3752 | */ | ||
3753 | static void | ||
3754 | fc_bsg_request_handler(struct request_queue *q, struct Scsi_Host *shost, | ||
3755 | struct fc_rport *rport, struct device *dev) | ||
3756 | { | ||
3757 | struct request *req; | ||
3758 | struct fc_bsg_job *job; | ||
3759 | enum fc_dispatch_result ret; | ||
3760 | |||
3761 | if (!get_device(dev)) | ||
3762 | return; | ||
3763 | |||
3764 | while (!blk_queue_plugged(q)) { | ||
3765 | if (rport && (rport->port_state == FC_PORTSTATE_BLOCKED)) | ||
3766 | break; | ||
3767 | |||
3768 | req = blk_fetch_request(q); | ||
3769 | if (!req) | ||
3770 | break; | ||
3771 | |||
3772 | if (rport && (rport->port_state != FC_PORTSTATE_ONLINE)) { | ||
3773 | req->errors = -ENXIO; | ||
3774 | spin_unlock_irq(q->queue_lock); | ||
3775 | blk_end_request(req, -ENXIO, blk_rq_bytes(req)); | ||
3776 | spin_lock_irq(q->queue_lock); | ||
3777 | continue; | ||
3778 | } | ||
3779 | |||
3780 | spin_unlock_irq(q->queue_lock); | ||
3781 | |||
3782 | ret = fc_req_to_bsgjob(shost, rport, req); | ||
3783 | if (ret) { | ||
3784 | req->errors = ret; | ||
3785 | blk_end_request(req, ret, blk_rq_bytes(req)); | ||
3786 | spin_lock_irq(q->queue_lock); | ||
3787 | continue; | ||
3788 | } | ||
3789 | |||
3790 | job = req->special; | ||
3791 | |||
3792 | /* check if we have the msgcode value at least */ | ||
3793 | if (job->request_len < sizeof(uint32_t)) { | ||
3794 | BUG_ON(job->reply_len < sizeof(uint32_t)); | ||
3795 | job->reply->result = -ENOMSG; | ||
3796 | job->reply_len = sizeof(uint32_t); | ||
3797 | fc_bsg_jobdone(job); | ||
3798 | spin_lock_irq(q->queue_lock); | ||
3799 | continue; | ||
3800 | } | ||
3801 | |||
3802 | /* the dispatch routines will unlock the queue_lock */ | ||
3803 | if (rport) | ||
3804 | ret = fc_bsg_rport_dispatch(q, shost, rport, job); | ||
3805 | else | ||
3806 | ret = fc_bsg_host_dispatch(q, shost, job); | ||
3807 | |||
3808 | /* did dispatcher hit state that can't process any more */ | ||
3809 | if (ret == FC_DISPATCH_BREAK) | ||
3810 | break; | ||
3811 | |||
3812 | /* did dispatcher had released the lock */ | ||
3813 | if (ret == FC_DISPATCH_UNLOCKED) | ||
3814 | spin_lock_irq(q->queue_lock); | ||
3815 | } | ||
3816 | |||
3817 | spin_unlock_irq(q->queue_lock); | ||
3818 | put_device(dev); | ||
3819 | spin_lock_irq(q->queue_lock); | ||
3820 | } | ||
3821 | |||
3822 | |||
3823 | /** | ||
3824 | * fc_bsg_host_handler - handler for bsg requests for a fc host | ||
3825 | * @q: fc host request queue | ||
3826 | */ | ||
3827 | static void | ||
3828 | fc_bsg_host_handler(struct request_queue *q) | ||
3829 | { | ||
3830 | struct Scsi_Host *shost = q->queuedata; | ||
3831 | |||
3832 | fc_bsg_request_handler(q, shost, NULL, &shost->shost_gendev); | ||
3833 | } | ||
3834 | |||
3835 | |||
3836 | /** | ||
3837 | * fc_bsg_rport_handler - handler for bsg requests for a fc rport | ||
3838 | * @q: rport request queue | ||
3839 | */ | ||
3840 | static void | ||
3841 | fc_bsg_rport_handler(struct request_queue *q) | ||
3842 | { | ||
3843 | struct fc_rport *rport = q->queuedata; | ||
3844 | struct Scsi_Host *shost = rport_to_shost(rport); | ||
3845 | |||
3846 | fc_bsg_request_handler(q, shost, rport, &rport->dev); | ||
3847 | } | ||
3848 | |||
3849 | |||
3850 | /** | ||
3851 | * fc_bsg_hostadd - Create and add the bsg hooks so we can receive requests | ||
3852 | * @shost: shost for fc_host | ||
3853 | * @fc_host: fc_host adding the structures to | ||
3854 | */ | ||
3855 | static int | ||
3856 | fc_bsg_hostadd(struct Scsi_Host *shost, struct fc_host_attrs *fc_host) | ||
3857 | { | ||
3858 | struct device *dev = &shost->shost_gendev; | ||
3859 | struct fc_internal *i = to_fc_internal(shost->transportt); | ||
3860 | struct request_queue *q; | ||
3861 | int err; | ||
3862 | char bsg_name[BUS_ID_SIZE]; /*20*/ | ||
3863 | |||
3864 | fc_host->rqst_q = NULL; | ||
3865 | |||
3866 | if (!i->f->bsg_request) | ||
3867 | return -ENOTSUPP; | ||
3868 | |||
3869 | snprintf(bsg_name, sizeof(bsg_name), | ||
3870 | "fc_host%d", shost->host_no); | ||
3871 | |||
3872 | q = __scsi_alloc_queue(shost, fc_bsg_host_handler); | ||
3873 | if (!q) { | ||
3874 | printk(KERN_ERR "fc_host%d: bsg interface failed to " | ||
3875 | "initialize - no request queue\n", | ||
3876 | shost->host_no); | ||
3877 | return -ENOMEM; | ||
3878 | } | ||
3879 | |||
3880 | q->queuedata = shost; | ||
3881 | queue_flag_set_unlocked(QUEUE_FLAG_BIDI, q); | ||
3882 | blk_queue_rq_timed_out(q, fc_bsg_job_timeout); | ||
3883 | blk_queue_rq_timeout(q, FC_DEFAULT_BSG_TIMEOUT); | ||
3884 | |||
3885 | err = bsg_register_queue(q, dev, bsg_name, NULL); | ||
3886 | if (err) { | ||
3887 | printk(KERN_ERR "fc_host%d: bsg interface failed to " | ||
3888 | "initialize - register queue\n", | ||
3889 | shost->host_no); | ||
3890 | blk_cleanup_queue(q); | ||
3891 | return err; | ||
3892 | } | ||
3893 | |||
3894 | fc_host->rqst_q = q; | ||
3895 | return 0; | ||
3896 | } | ||
3897 | |||
3898 | |||
3899 | /** | ||
3900 | * fc_bsg_rportadd - Create and add the bsg hooks so we can receive requests | ||
3901 | * @shost: shost that rport is attached to | ||
3902 | * @rport: rport that the bsg hooks are being attached to | ||
3903 | */ | ||
3904 | static int | ||
3905 | fc_bsg_rportadd(struct Scsi_Host *shost, struct fc_rport *rport) | ||
3906 | { | ||
3907 | struct device *dev = &rport->dev; | ||
3908 | struct fc_internal *i = to_fc_internal(shost->transportt); | ||
3909 | struct request_queue *q; | ||
3910 | int err; | ||
3911 | |||
3912 | rport->rqst_q = NULL; | ||
3913 | |||
3914 | if (!i->f->bsg_request) | ||
3915 | return -ENOTSUPP; | ||
3916 | |||
3917 | q = __scsi_alloc_queue(shost, fc_bsg_rport_handler); | ||
3918 | if (!q) { | ||
3919 | printk(KERN_ERR "%s: bsg interface failed to " | ||
3920 | "initialize - no request queue\n", | ||
3921 | dev->kobj.name); | ||
3922 | return -ENOMEM; | ||
3923 | } | ||
3924 | |||
3925 | q->queuedata = rport; | ||
3926 | queue_flag_set_unlocked(QUEUE_FLAG_BIDI, q); | ||
3927 | blk_queue_rq_timed_out(q, fc_bsg_job_timeout); | ||
3928 | blk_queue_rq_timeout(q, BLK_DEFAULT_SG_TIMEOUT); | ||
3929 | |||
3930 | err = bsg_register_queue(q, dev, NULL, NULL); | ||
3931 | if (err) { | ||
3932 | printk(KERN_ERR "%s: bsg interface failed to " | ||
3933 | "initialize - register queue\n", | ||
3934 | dev->kobj.name); | ||
3935 | blk_cleanup_queue(q); | ||
3936 | return err; | ||
3937 | } | ||
3938 | |||
3939 | rport->rqst_q = q; | ||
3940 | return 0; | ||
3941 | } | ||
3942 | |||
3943 | |||
3944 | /** | ||
3945 | * fc_bsg_remove - Deletes the bsg hooks on fchosts/rports | ||
3946 | * @q: the request_queue that is to be torn down. | ||
3947 | */ | ||
3948 | static void | ||
3949 | fc_bsg_remove(struct request_queue *q) | ||
3950 | { | ||
3951 | if (q) { | ||
3952 | bsg_unregister_queue(q); | ||
3953 | blk_cleanup_queue(q); | ||
3954 | } | ||
3955 | } | ||
3956 | |||
3957 | |||
3346 | /* Original Author: Martin Hicks */ | 3958 | /* Original Author: Martin Hicks */ |
3347 | MODULE_AUTHOR("James Smart"); | 3959 | MODULE_AUTHOR("James Smart"); |
3348 | MODULE_DESCRIPTION("FC Transport Attributes"); | 3960 | MODULE_DESCRIPTION("FC Transport Attributes"); |