| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * hosts.c Copyright (C) 1992 Drew Eckhardt |
|---|
| 3 | 4 | * Copyright (C) 1993, 1994, 1995 Eric Youngdale |
|---|
| .. | .. |
|---|
| 37 | 38 | #include <scsi/scsi_device.h> |
|---|
| 38 | 39 | #include <scsi/scsi_host.h> |
|---|
| 39 | 40 | #include <scsi/scsi_transport.h> |
|---|
| 41 | +#include <scsi/scsi_cmnd.h> |
|---|
| 40 | 42 | |
|---|
| 41 | 43 | #include "scsi_priv.h" |
|---|
| 42 | 44 | #include "scsi_logging.h" |
|---|
| .. | .. |
|---|
| 226 | 228 | if (error) |
|---|
| 227 | 229 | goto fail; |
|---|
| 228 | 230 | |
|---|
| 229 | | - if (shost_use_blk_mq(shost)) { |
|---|
| 230 | | - error = scsi_mq_setup_tags(shost); |
|---|
| 231 | | - if (error) |
|---|
| 232 | | - goto fail; |
|---|
| 233 | | - } else { |
|---|
| 234 | | - shost->bqt = blk_init_tags(shost->can_queue, |
|---|
| 235 | | - shost->hostt->tag_alloc_policy); |
|---|
| 236 | | - if (!shost->bqt) { |
|---|
| 237 | | - error = -ENOMEM; |
|---|
| 238 | | - goto fail; |
|---|
| 239 | | - } |
|---|
| 240 | | - } |
|---|
| 231 | + error = scsi_mq_setup_tags(shost); |
|---|
| 232 | + if (error) |
|---|
| 233 | + goto fail; |
|---|
| 241 | 234 | |
|---|
| 242 | 235 | if (!shost->shost_gendev.parent) |
|---|
| 243 | 236 | shost->shost_gendev.parent = dev ? dev : &platform_bus; |
|---|
| .. | .. |
|---|
| 282 | 275 | if (shost->transportt->create_work_queue) { |
|---|
| 283 | 276 | snprintf(shost->work_q_name, sizeof(shost->work_q_name), |
|---|
| 284 | 277 | "scsi_wq_%d", shost->host_no); |
|---|
| 285 | | - shost->work_q = create_singlethread_workqueue( |
|---|
| 286 | | - shost->work_q_name); |
|---|
| 278 | + shost->work_q = alloc_workqueue("%s", |
|---|
| 279 | + WQ_SYSFS | __WQ_LEGACY | WQ_MEM_RECLAIM | WQ_UNBOUND, |
|---|
| 280 | + 1, shost->work_q_name); |
|---|
| 281 | + |
|---|
| 287 | 282 | if (!shost->work_q) { |
|---|
| 288 | 283 | error = -EINVAL; |
|---|
| 289 | | - goto out_free_shost_data; |
|---|
| 284 | + goto out_del_dev; |
|---|
| 290 | 285 | } |
|---|
| 291 | 286 | } |
|---|
| 292 | 287 | |
|---|
| 293 | 288 | error = scsi_sysfs_add_host(shost); |
|---|
| 294 | 289 | if (error) |
|---|
| 295 | | - goto out_destroy_host; |
|---|
| 290 | + goto out_del_dev; |
|---|
| 296 | 291 | |
|---|
| 297 | 292 | scsi_proc_host_add(shost); |
|---|
| 298 | 293 | scsi_autopm_put_host(shost); |
|---|
| 299 | 294 | return error; |
|---|
| 300 | 295 | |
|---|
| 301 | | - out_destroy_host: |
|---|
| 302 | | - if (shost->work_q) |
|---|
| 303 | | - destroy_workqueue(shost->work_q); |
|---|
| 304 | | - out_free_shost_data: |
|---|
| 305 | | - kfree(shost->shost_data); |
|---|
| 296 | + /* |
|---|
| 297 | + * Any host allocation in this function will be freed in |
|---|
| 298 | + * scsi_host_dev_release(). |
|---|
| 299 | + */ |
|---|
| 306 | 300 | out_del_dev: |
|---|
| 307 | 301 | device_del(&shost->shost_dev); |
|---|
| 308 | 302 | out_del_gendev: |
|---|
| .. | .. |
|---|
| 317 | 311 | pm_runtime_disable(&shost->shost_gendev); |
|---|
| 318 | 312 | pm_runtime_set_suspended(&shost->shost_gendev); |
|---|
| 319 | 313 | pm_runtime_put_noidle(&shost->shost_gendev); |
|---|
| 320 | | - if (shost_use_blk_mq(shost)) |
|---|
| 321 | | - scsi_mq_destroy_tags(shost); |
|---|
| 322 | 314 | fail: |
|---|
| 323 | 315 | return error; |
|---|
| 324 | 316 | } |
|---|
| .. | .. |
|---|
| 352 | 344 | kfree(dev_name(&shost->shost_dev)); |
|---|
| 353 | 345 | } |
|---|
| 354 | 346 | |
|---|
| 355 | | - if (shost_use_blk_mq(shost)) { |
|---|
| 356 | | - if (shost->tag_set.tags) |
|---|
| 357 | | - scsi_mq_destroy_tags(shost); |
|---|
| 358 | | - } else { |
|---|
| 359 | | - if (shost->bqt) |
|---|
| 360 | | - blk_free_tags(shost->bqt); |
|---|
| 361 | | - } |
|---|
| 347 | + if (shost->tag_set.tags) |
|---|
| 348 | + scsi_mq_destroy_tags(shost); |
|---|
| 362 | 349 | |
|---|
| 363 | 350 | kfree(shost->shost_data); |
|---|
| 364 | 351 | |
|---|
| .. | .. |
|---|
| 441 | 428 | shost->sg_prot_tablesize = sht->sg_prot_tablesize; |
|---|
| 442 | 429 | shost->cmd_per_lun = sht->cmd_per_lun; |
|---|
| 443 | 430 | shost->unchecked_isa_dma = sht->unchecked_isa_dma; |
|---|
| 444 | | - shost->use_clustering = sht->use_clustering; |
|---|
| 445 | 431 | shost->no_write_same = sht->no_write_same; |
|---|
| 432 | + shost->host_tagset = sht->host_tagset; |
|---|
| 446 | 433 | |
|---|
| 447 | 434 | if (shost_eh_deadline == -1 || !sht->eh_host_reset_handler) |
|---|
| 448 | 435 | shost->eh_deadline = -1; |
|---|
| .. | .. |
|---|
| 474 | 461 | else |
|---|
| 475 | 462 | shost->max_sectors = SCSI_DEFAULT_MAX_SECTORS; |
|---|
| 476 | 463 | |
|---|
| 464 | + if (sht->max_segment_size) |
|---|
| 465 | + shost->max_segment_size = sht->max_segment_size; |
|---|
| 466 | + else |
|---|
| 467 | + shost->max_segment_size = BLK_MAX_SEGMENT_SIZE; |
|---|
| 468 | + |
|---|
| 477 | 469 | /* |
|---|
| 478 | 470 | * assume a 4GB boundary, if not set |
|---|
| 479 | 471 | */ |
|---|
| .. | .. |
|---|
| 482 | 474 | else |
|---|
| 483 | 475 | shost->dma_boundary = 0xffffffff; |
|---|
| 484 | 476 | |
|---|
| 485 | | - shost->use_blk_mq = scsi_use_blk_mq || shost->hostt->force_blk_mq; |
|---|
| 477 | + if (sht->virt_boundary_mask) |
|---|
| 478 | + shost->virt_boundary_mask = sht->virt_boundary_mask; |
|---|
| 486 | 479 | |
|---|
| 487 | 480 | device_initialize(&shost->shost_gendev); |
|---|
| 488 | 481 | dev_set_name(&shost->shost_gendev, "host%d", shost->host_no); |
|---|
| .. | .. |
|---|
| 506 | 499 | } |
|---|
| 507 | 500 | |
|---|
| 508 | 501 | shost->tmf_work_q = alloc_workqueue("scsi_tmf_%d", |
|---|
| 509 | | - WQ_UNBOUND | WQ_MEM_RECLAIM, |
|---|
| 502 | + WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS, |
|---|
| 510 | 503 | 1, shost->host_no); |
|---|
| 511 | 504 | if (!shost->tmf_work_q) { |
|---|
| 512 | 505 | shost_printk(KERN_WARNING, shost, |
|---|
| .. | .. |
|---|
| 575 | 568 | } |
|---|
| 576 | 569 | EXPORT_SYMBOL(scsi_host_get); |
|---|
| 577 | 570 | |
|---|
| 571 | +static bool scsi_host_check_in_flight(struct request *rq, void *data, |
|---|
| 572 | + bool reserved) |
|---|
| 573 | +{ |
|---|
| 574 | + int *count = data; |
|---|
| 575 | + struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq); |
|---|
| 576 | + |
|---|
| 577 | + if (test_bit(SCMD_STATE_INFLIGHT, &cmd->state)) |
|---|
| 578 | + (*count)++; |
|---|
| 579 | + |
|---|
| 580 | + return true; |
|---|
| 581 | +} |
|---|
| 582 | + |
|---|
| 578 | 583 | /** |
|---|
| 579 | 584 | * scsi_host_busy - Return the host busy counter |
|---|
| 580 | 585 | * @shost: Pointer to Scsi_Host to inc. |
|---|
| 581 | 586 | **/ |
|---|
| 582 | 587 | int scsi_host_busy(struct Scsi_Host *shost) |
|---|
| 583 | 588 | { |
|---|
| 584 | | - return atomic_read(&shost->host_busy); |
|---|
| 589 | + int cnt = 0; |
|---|
| 590 | + |
|---|
| 591 | + blk_mq_tagset_busy_iter(&shost->tag_set, |
|---|
| 592 | + scsi_host_check_in_flight, &cnt); |
|---|
| 593 | + return cnt; |
|---|
| 585 | 594 | } |
|---|
| 586 | 595 | EXPORT_SYMBOL(scsi_host_busy); |
|---|
| 587 | 596 | |
|---|
| .. | .. |
|---|
| 654 | 663 | flush_workqueue(shost->work_q); |
|---|
| 655 | 664 | } |
|---|
| 656 | 665 | EXPORT_SYMBOL_GPL(scsi_flush_work); |
|---|
| 666 | + |
|---|
| 667 | +static bool complete_all_cmds_iter(struct request *rq, void *data, bool rsvd) |
|---|
| 668 | +{ |
|---|
| 669 | + struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq); |
|---|
| 670 | + int status = *(int *)data; |
|---|
| 671 | + |
|---|
| 672 | + scsi_dma_unmap(scmd); |
|---|
| 673 | + scmd->result = status << 16; |
|---|
| 674 | + scmd->scsi_done(scmd); |
|---|
| 675 | + return true; |
|---|
| 676 | +} |
|---|
| 677 | + |
|---|
| 678 | +/** |
|---|
| 679 | + * scsi_host_complete_all_commands - Terminate all running commands |
|---|
| 680 | + * @shost: Scsi Host on which commands should be terminated |
|---|
| 681 | + * @status: Status to be set for the terminated commands |
|---|
| 682 | + * |
|---|
| 683 | + * There is no protection against modification of the number |
|---|
| 684 | + * of outstanding commands. It is the responsibility of the |
|---|
| 685 | + * caller to ensure that concurrent I/O submission and/or |
|---|
| 686 | + * completion is stopped when calling this function. |
|---|
| 687 | + */ |
|---|
| 688 | +void scsi_host_complete_all_commands(struct Scsi_Host *shost, int status) |
|---|
| 689 | +{ |
|---|
| 690 | + blk_mq_tagset_busy_iter(&shost->tag_set, complete_all_cmds_iter, |
|---|
| 691 | + &status); |
|---|
| 692 | +} |
|---|
| 693 | +EXPORT_SYMBOL_GPL(scsi_host_complete_all_commands); |
|---|
| 694 | + |
|---|
| 695 | +struct scsi_host_busy_iter_data { |
|---|
| 696 | + bool (*fn)(struct scsi_cmnd *, void *, bool); |
|---|
| 697 | + void *priv; |
|---|
| 698 | +}; |
|---|
| 699 | + |
|---|
| 700 | +static bool __scsi_host_busy_iter_fn(struct request *req, void *priv, |
|---|
| 701 | + bool reserved) |
|---|
| 702 | +{ |
|---|
| 703 | + struct scsi_host_busy_iter_data *iter_data = priv; |
|---|
| 704 | + struct scsi_cmnd *sc = blk_mq_rq_to_pdu(req); |
|---|
| 705 | + |
|---|
| 706 | + return iter_data->fn(sc, iter_data->priv, reserved); |
|---|
| 707 | +} |
|---|
| 708 | + |
|---|
| 709 | +/** |
|---|
| 710 | + * scsi_host_busy_iter - Iterate over all busy commands |
|---|
| 711 | + * @shost: Pointer to Scsi_Host. |
|---|
| 712 | + * @fn: Function to call on each busy command |
|---|
| 713 | + * @priv: Data pointer passed to @fn |
|---|
| 714 | + * |
|---|
| 715 | + * If locking against concurrent command completions is required |
|---|
| 716 | + * ithas to be provided by the caller |
|---|
| 717 | + **/ |
|---|
| 718 | +void scsi_host_busy_iter(struct Scsi_Host *shost, |
|---|
| 719 | + bool (*fn)(struct scsi_cmnd *, void *, bool), |
|---|
| 720 | + void *priv) |
|---|
| 721 | +{ |
|---|
| 722 | + struct scsi_host_busy_iter_data iter_data = { |
|---|
| 723 | + .fn = fn, |
|---|
| 724 | + .priv = priv, |
|---|
| 725 | + }; |
|---|
| 726 | + |
|---|
| 727 | + blk_mq_tagset_busy_iter(&shost->tag_set, __scsi_host_busy_iter_fn, |
|---|
| 728 | + &iter_data); |
|---|
| 729 | +} |
|---|
| 730 | +EXPORT_SYMBOL_GPL(scsi_host_busy_iter); |
|---|