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 | |
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')
-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 | */ |