diff options
| author | Nicholas Bellinger <nab@linux-iscsi.org> | 2013-06-21 17:32:04 -0400 |
|---|---|---|
| committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2013-09-09 17:29:19 -0400 |
| commit | 3aee26b4ae91048c933dc622f00b7bb01f7f0ff1 (patch) | |
| tree | c20fcf58cf50fc2803601872aecb866de860c28e /drivers/vhost | |
| parent | 4824d3bfb9097ac59cf7633c0bc4fb7c5b549a80 (diff) | |
vhost/scsi: Add pre-allocation for tv_cmd SGL + upages memory
This patch adds support for pre-allocation of per tv_cmd descriptor
scatterlist + user-space page pointer memory using se_sess->sess_cmd_map
within tcm_vhost_make_nexus() code.
This includes sanity checks within vhost_scsi_map_to_sgl()
to reject I/O that exceeds these initial hardcoded values, and
the necessary cleanup in tcm_vhost_make_nexus() failure path +
tcm_vhost_drop_nexus().
v3 changes:
- Rebase to v3.11-rc5 code
Cc: Michael S. Tsirkin <mst@redhat.com>
Cc: Asias He <asias@redhat.com>
Cc: Kent Overstreet <kmo@daterainc.com>
Reviewed-by: Asias He <asias@redhat.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/vhost')
| -rw-r--r-- | drivers/vhost/scsi.c | 99 |
1 files changed, 80 insertions, 19 deletions
diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 8cd545ad3f51..d52a3a063be0 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c | |||
| @@ -56,6 +56,8 @@ | |||
| 56 | #define TCM_VHOST_NAMELEN 256 | 56 | #define TCM_VHOST_NAMELEN 256 |
| 57 | #define TCM_VHOST_MAX_CDB_SIZE 32 | 57 | #define TCM_VHOST_MAX_CDB_SIZE 32 |
| 58 | #define TCM_VHOST_DEFAULT_TAGS 256 | 58 | #define TCM_VHOST_DEFAULT_TAGS 256 |
| 59 | #define TCM_VHOST_PREALLOC_SGLS 2048 | ||
| 60 | #define TCM_VHOST_PREALLOC_PAGES 2048 | ||
| 59 | 61 | ||
| 60 | struct vhost_scsi_inflight { | 62 | struct vhost_scsi_inflight { |
| 61 | /* Wait for the flush operation to finish */ | 63 | /* Wait for the flush operation to finish */ |
| @@ -81,6 +83,7 @@ struct tcm_vhost_cmd { | |||
| 81 | u32 tvc_lun; | 83 | u32 tvc_lun; |
| 82 | /* Pointer to the SGL formatted memory from virtio-scsi */ | 84 | /* Pointer to the SGL formatted memory from virtio-scsi */ |
| 83 | struct scatterlist *tvc_sgl; | 85 | struct scatterlist *tvc_sgl; |
| 86 | struct page **tvc_upages; | ||
| 84 | /* Pointer to response */ | 87 | /* Pointer to response */ |
| 85 | struct virtio_scsi_cmd_resp __user *tvc_resp; | 88 | struct virtio_scsi_cmd_resp __user *tvc_resp; |
| 86 | /* Pointer to vhost_scsi for our device */ | 89 | /* Pointer to vhost_scsi for our device */ |
| @@ -458,8 +461,6 @@ static void tcm_vhost_release_cmd(struct se_cmd *se_cmd) | |||
| 458 | u32 i; | 461 | u32 i; |
| 459 | for (i = 0; i < tv_cmd->tvc_sgl_count; i++) | 462 | for (i = 0; i < tv_cmd->tvc_sgl_count; i++) |
| 460 | put_page(sg_page(&tv_cmd->tvc_sgl[i])); | 463 | put_page(sg_page(&tv_cmd->tvc_sgl[i])); |
| 461 | |||
| 462 | kfree(tv_cmd->tvc_sgl); | ||
| 463 | } | 464 | } |
| 464 | 465 | ||
| 465 | tcm_vhost_put_inflight(tv_cmd->inflight); | 466 | tcm_vhost_put_inflight(tv_cmd->inflight); |
| @@ -716,6 +717,8 @@ vhost_scsi_get_tag(struct vhost_virtqueue *vq, | |||
| 716 | struct tcm_vhost_cmd *cmd; | 717 | struct tcm_vhost_cmd *cmd; |
| 717 | struct tcm_vhost_nexus *tv_nexus; | 718 | struct tcm_vhost_nexus *tv_nexus; |
| 718 | struct se_session *se_sess; | 719 | struct se_session *se_sess; |
| 720 | struct scatterlist *sg; | ||
| 721 | struct page **pages; | ||
| 719 | int tag; | 722 | int tag; |
| 720 | 723 | ||
| 721 | tv_nexus = tpg->tpg_nexus; | 724 | tv_nexus = tpg->tpg_nexus; |
| @@ -727,8 +730,12 @@ vhost_scsi_get_tag(struct vhost_virtqueue *vq, | |||
| 727 | 730 | ||
| 728 | tag = percpu_ida_alloc(&se_sess->sess_tag_pool, GFP_KERNEL); | 731 | tag = percpu_ida_alloc(&se_sess->sess_tag_pool, GFP_KERNEL); |
| 729 | cmd = &((struct tcm_vhost_cmd *)se_sess->sess_cmd_map)[tag]; | 732 | cmd = &((struct tcm_vhost_cmd *)se_sess->sess_cmd_map)[tag]; |
| 733 | sg = cmd->tvc_sgl; | ||
| 734 | pages = cmd->tvc_upages; | ||
| 730 | memset(cmd, 0, sizeof(struct tcm_vhost_cmd)); | 735 | memset(cmd, 0, sizeof(struct tcm_vhost_cmd)); |
| 731 | 736 | ||
| 737 | cmd->tvc_sgl = sg; | ||
| 738 | cmd->tvc_upages = pages; | ||
| 732 | cmd->tvc_se_cmd.map_tag = tag; | 739 | cmd->tvc_se_cmd.map_tag = tag; |
| 733 | cmd->tvc_tag = v_req->tag; | 740 | cmd->tvc_tag = v_req->tag; |
| 734 | cmd->tvc_task_attr = v_req->task_attr; | 741 | cmd->tvc_task_attr = v_req->task_attr; |
| @@ -746,7 +753,8 @@ vhost_scsi_get_tag(struct vhost_virtqueue *vq, | |||
| 746 | * Returns the number of scatterlist entries used or -errno on error. | 753 | * Returns the number of scatterlist entries used or -errno on error. |
| 747 | */ | 754 | */ |
| 748 | static int | 755 | static int |
| 749 | vhost_scsi_map_to_sgl(struct scatterlist *sgl, | 756 | vhost_scsi_map_to_sgl(struct tcm_vhost_cmd *tv_cmd, |
| 757 | struct scatterlist *sgl, | ||
| 750 | unsigned int sgl_count, | 758 | unsigned int sgl_count, |
| 751 | struct iovec *iov, | 759 | struct iovec *iov, |
| 752 | int write) | 760 | int write) |
| @@ -758,13 +766,25 @@ vhost_scsi_map_to_sgl(struct scatterlist *sgl, | |||
| 758 | struct page **pages; | 766 | struct page **pages; |
| 759 | int ret, i; | 767 | int ret, i; |
| 760 | 768 | ||
| 769 | if (sgl_count > TCM_VHOST_PREALLOC_SGLS) { | ||
| 770 | pr_err("vhost_scsi_map_to_sgl() psgl_count: %u greater than" | ||
| 771 | " preallocated TCM_VHOST_PREALLOC_SGLS: %u\n", | ||
| 772 | sgl_count, TCM_VHOST_PREALLOC_SGLS); | ||
| 773 | return -ENOBUFS; | ||
| 774 | } | ||
| 775 | |||
| 761 | pages_nr = iov_num_pages(iov); | 776 | pages_nr = iov_num_pages(iov); |
| 762 | if (pages_nr > sgl_count) | 777 | if (pages_nr > sgl_count) |
| 763 | return -ENOBUFS; | 778 | return -ENOBUFS; |
| 764 | 779 | ||
| 765 | pages = kmalloc(pages_nr * sizeof(struct page *), GFP_KERNEL); | 780 | if (pages_nr > TCM_VHOST_PREALLOC_PAGES) { |
| 766 | if (!pages) | 781 | pr_err("vhost_scsi_map_to_sgl() pages_nr: %u greater than" |
| 767 | return -ENOMEM; | 782 | " preallocated TCM_VHOST_PREALLOC_PAGES: %u\n", |
| 783 | pages_nr, TCM_VHOST_PREALLOC_PAGES); | ||
| 784 | return -ENOBUFS; | ||
| 785 | } | ||
| 786 | |||
| 787 | pages = tv_cmd->tvc_upages; | ||
| 768 | 788 | ||
| 769 | ret = get_user_pages_fast((unsigned long)ptr, pages_nr, write, pages); | 789 | ret = get_user_pages_fast((unsigned long)ptr, pages_nr, write, pages); |
| 770 | /* No pages were pinned */ | 790 | /* No pages were pinned */ |
| @@ -789,7 +809,6 @@ vhost_scsi_map_to_sgl(struct scatterlist *sgl, | |||
| 789 | } | 809 | } |
| 790 | 810 | ||
| 791 | out: | 811 | out: |
| 792 | kfree(pages); | ||
| 793 | return ret; | 812 | return ret; |
| 794 | } | 813 | } |
| 795 | 814 | ||
| @@ -813,24 +832,20 @@ vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *cmd, | |||
| 813 | 832 | ||
| 814 | /* TODO overflow checking */ | 833 | /* TODO overflow checking */ |
| 815 | 834 | ||
| 816 | sg = kmalloc(sizeof(cmd->tvc_sgl[0]) * sgl_count, GFP_ATOMIC); | 835 | sg = cmd->tvc_sgl; |
| 817 | if (!sg) | 836 | pr_debug("%s sg %p sgl_count %u\n", __func__, sg, sgl_count); |
| 818 | return -ENOMEM; | ||
| 819 | pr_debug("%s sg %p sgl_count %u is_err %d\n", __func__, | ||
| 820 | sg, sgl_count, !sg); | ||
| 821 | sg_init_table(sg, sgl_count); | 837 | sg_init_table(sg, sgl_count); |
| 822 | 838 | ||
| 823 | cmd->tvc_sgl = sg; | ||
| 824 | cmd->tvc_sgl_count = sgl_count; | 839 | cmd->tvc_sgl_count = sgl_count; |
| 825 | 840 | ||
| 826 | pr_debug("Mapping %u iovecs for %u pages\n", niov, sgl_count); | 841 | pr_debug("Mapping %u iovecs for %u pages\n", niov, sgl_count); |
| 827 | for (i = 0; i < niov; i++) { | 842 | for (i = 0; i < niov; i++) { |
| 828 | ret = vhost_scsi_map_to_sgl(sg, sgl_count, &iov[i], write); | 843 | ret = vhost_scsi_map_to_sgl(cmd, sg, sgl_count, &iov[i], |
| 844 | write); | ||
| 829 | if (ret < 0) { | 845 | if (ret < 0) { |
| 830 | for (i = 0; i < cmd->tvc_sgl_count; i++) | 846 | for (i = 0; i < cmd->tvc_sgl_count; i++) |
| 831 | put_page(sg_page(&cmd->tvc_sgl[i])); | 847 | put_page(sg_page(&cmd->tvc_sgl[i])); |
| 832 | kfree(cmd->tvc_sgl); | 848 | |
| 833 | cmd->tvc_sgl = NULL; | ||
| 834 | cmd->tvc_sgl_count = 0; | 849 | cmd->tvc_sgl_count = 0; |
| 835 | return ret; | 850 | return ret; |
| 836 | } | 851 | } |
| @@ -1660,11 +1675,31 @@ static void tcm_vhost_drop_nodeacl(struct se_node_acl *se_acl) | |||
| 1660 | kfree(nacl); | 1675 | kfree(nacl); |
| 1661 | } | 1676 | } |
| 1662 | 1677 | ||
| 1678 | static void tcm_vhost_free_cmd_map_res(struct tcm_vhost_nexus *nexus, | ||
| 1679 | struct se_session *se_sess) | ||
| 1680 | { | ||
| 1681 | struct tcm_vhost_cmd *tv_cmd; | ||
| 1682 | unsigned int i; | ||
| 1683 | |||
| 1684 | if (!se_sess->sess_cmd_map) | ||
| 1685 | return; | ||
| 1686 | |||
| 1687 | for (i = 0; i < TCM_VHOST_DEFAULT_TAGS; i++) { | ||
| 1688 | tv_cmd = &((struct tcm_vhost_cmd *)se_sess->sess_cmd_map)[i]; | ||
| 1689 | |||
| 1690 | kfree(tv_cmd->tvc_sgl); | ||
| 1691 | kfree(tv_cmd->tvc_upages); | ||
| 1692 | } | ||
| 1693 | } | ||
| 1694 | |||
| 1663 | static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tpg, | 1695 | static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tpg, |
| 1664 | const char *name) | 1696 | const char *name) |
| 1665 | { | 1697 | { |
| 1666 | struct se_portal_group *se_tpg; | 1698 | struct se_portal_group *se_tpg; |
| 1699 | struct se_session *se_sess; | ||
| 1667 | struct tcm_vhost_nexus *tv_nexus; | 1700 | struct tcm_vhost_nexus *tv_nexus; |
| 1701 | struct tcm_vhost_cmd *tv_cmd; | ||
| 1702 | unsigned int i; | ||
| 1668 | 1703 | ||
| 1669 | mutex_lock(&tpg->tv_tpg_mutex); | 1704 | mutex_lock(&tpg->tv_tpg_mutex); |
| 1670 | if (tpg->tpg_nexus) { | 1705 | if (tpg->tpg_nexus) { |
| @@ -1692,6 +1727,26 @@ static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tpg, | |||
| 1692 | kfree(tv_nexus); | 1727 | kfree(tv_nexus); |
| 1693 | return -ENOMEM; | 1728 | return -ENOMEM; |
| 1694 | } | 1729 | } |
| 1730 | se_sess = tv_nexus->tvn_se_sess; | ||
| 1731 | for (i = 0; i < TCM_VHOST_DEFAULT_TAGS; i++) { | ||
| 1732 | tv_cmd = &((struct tcm_vhost_cmd *)se_sess->sess_cmd_map)[i]; | ||
| 1733 | |||
| 1734 | tv_cmd->tvc_sgl = kzalloc(sizeof(struct scatterlist) * | ||
| 1735 | TCM_VHOST_PREALLOC_SGLS, GFP_KERNEL); | ||
| 1736 | if (!tv_cmd->tvc_sgl) { | ||
| 1737 | mutex_unlock(&tpg->tv_tpg_mutex); | ||
| 1738 | pr_err("Unable to allocate tv_cmd->tvc_sgl\n"); | ||
| 1739 | goto out; | ||
| 1740 | } | ||
| 1741 | |||
| 1742 | tv_cmd->tvc_upages = kzalloc(sizeof(struct page *) * | ||
| 1743 | TCM_VHOST_PREALLOC_PAGES, GFP_KERNEL); | ||
| 1744 | if (!tv_cmd->tvc_upages) { | ||
| 1745 | mutex_unlock(&tpg->tv_tpg_mutex); | ||
| 1746 | pr_err("Unable to allocate tv_cmd->tvc_upages\n"); | ||
| 1747 | goto out; | ||
| 1748 | } | ||
| 1749 | } | ||
| 1695 | /* | 1750 | /* |
| 1696 | * Since we are running in 'demo mode' this call with generate a | 1751 | * Since we are running in 'demo mode' this call with generate a |
| 1697 | * struct se_node_acl for the tcm_vhost struct se_portal_group with | 1752 | * struct se_node_acl for the tcm_vhost struct se_portal_group with |
| @@ -1703,9 +1758,7 @@ static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tpg, | |||
| 1703 | mutex_unlock(&tpg->tv_tpg_mutex); | 1758 | mutex_unlock(&tpg->tv_tpg_mutex); |
| 1704 | pr_debug("core_tpg_check_initiator_node_acl() failed" | 1759 | pr_debug("core_tpg_check_initiator_node_acl() failed" |
| 1705 | " for %s\n", name); | 1760 | " for %s\n", name); |
| 1706 | transport_free_session(tv_nexus->tvn_se_sess); | 1761 | goto out; |
| 1707 | kfree(tv_nexus); | ||
| 1708 | return -ENOMEM; | ||
| 1709 | } | 1762 | } |
| 1710 | /* | 1763 | /* |
| 1711 | * Now register the TCM vhost virtual I_T Nexus as active with the | 1764 | * Now register the TCM vhost virtual I_T Nexus as active with the |
| @@ -1717,6 +1770,12 @@ static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tpg, | |||
| 1717 | 1770 | ||
| 1718 | mutex_unlock(&tpg->tv_tpg_mutex); | 1771 | mutex_unlock(&tpg->tv_tpg_mutex); |
| 1719 | return 0; | 1772 | return 0; |
| 1773 | |||
| 1774 | out: | ||
| 1775 | tcm_vhost_free_cmd_map_res(tv_nexus, se_sess); | ||
| 1776 | transport_free_session(se_sess); | ||
| 1777 | kfree(tv_nexus); | ||
| 1778 | return -ENOMEM; | ||
| 1720 | } | 1779 | } |
| 1721 | 1780 | ||
| 1722 | static int tcm_vhost_drop_nexus(struct tcm_vhost_tpg *tpg) | 1781 | static int tcm_vhost_drop_nexus(struct tcm_vhost_tpg *tpg) |
| @@ -1756,6 +1815,8 @@ static int tcm_vhost_drop_nexus(struct tcm_vhost_tpg *tpg) | |||
| 1756 | pr_debug("TCM_vhost_ConfigFS: Removing I_T Nexus to emulated" | 1815 | pr_debug("TCM_vhost_ConfigFS: Removing I_T Nexus to emulated" |
| 1757 | " %s Initiator Port: %s\n", tcm_vhost_dump_proto_id(tpg->tport), | 1816 | " %s Initiator Port: %s\n", tcm_vhost_dump_proto_id(tpg->tport), |
| 1758 | tv_nexus->tvn_se_sess->se_node_acl->initiatorname); | 1817 | tv_nexus->tvn_se_sess->se_node_acl->initiatorname); |
| 1818 | |||
| 1819 | tcm_vhost_free_cmd_map_res(tv_nexus, se_sess); | ||
| 1759 | /* | 1820 | /* |
| 1760 | * Release the SCSI I_T Nexus to the emulated vhost Target Port | 1821 | * Release the SCSI I_T Nexus to the emulated vhost Target Port |
| 1761 | */ | 1822 | */ |
