summaryrefslogtreecommitdiff
path: root/drivers/scsi
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2020-10-14 10:32:10 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2020-10-14 10:32:10 -0700
commit4907a43da83184d4e88009654c9b31f5e091f709 (patch)
treebb2325f84e9ed0e7faf258db75a90508efa4e56a /drivers/scsi
parentda9803dfd3955bd2f9909d55e23f188ad76dbe58 (diff)
parent1f3aed01473c41c9f896fbf4c30d330655e8aa7c (diff)
Merge tag 'hyperv-next-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux
Pull Hyper-V updates from Wei Liu: - a series from Boqun Feng to support page size larger than 4K - a few miscellaneous clean-ups * tag 'hyperv-next-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux: hv: clocksource: Add notrace attribute to read_hv_sched_clock_*() functions x86/hyperv: Remove aliases with X64 in their name PCI: hv: Document missing hv_pci_protocol_negotiation() parameter scsi: storvsc: Support PAGE_SIZE larger than 4K Driver: hv: util: Use VMBUS_RING_SIZE() for ringbuffer sizes HID: hyperv: Use VMBUS_RING_SIZE() for ringbuffer sizes Input: hyperv-keyboard: Use VMBUS_RING_SIZE() for ringbuffer sizes hv_netvsc: Use HV_HYP_PAGE_SIZE for Hyper-V communication hv: hyperv.h: Introduce some hvpfn helper functions Drivers: hv: vmbus: Move virt_to_hvpfn() to hyperv header Drivers: hv: Use HV_HYP_PAGE in hv_synic_enable_regs() Drivers: hv: vmbus: Introduce types of GPADL Drivers: hv: vmbus: Move __vmbus_open() Drivers: hv: vmbus: Always use HV_HYP_PAGE_SIZE for gpadl drivers: hv: remove cast from hyperv_die_event
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/storvsc_drv.c56
1 files changed, 49 insertions, 7 deletions
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index 8f5f5dc863a4..0c65fbd41035 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -1739,23 +1739,65 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd)
payload_sz = sizeof(cmd_request->mpb);
if (sg_count) {
- if (sg_count > MAX_PAGE_BUFFER_COUNT) {
+ unsigned int hvpgoff = 0;
+ unsigned long offset_in_hvpg = sgl->offset & ~HV_HYP_PAGE_MASK;
+ unsigned int hvpg_count = HVPFN_UP(offset_in_hvpg + length);
+ u64 hvpfn;
- payload_sz = (sg_count * sizeof(u64) +
+ if (hvpg_count > MAX_PAGE_BUFFER_COUNT) {
+
+ payload_sz = (hvpg_count * sizeof(u64) +
sizeof(struct vmbus_packet_mpb_array));
payload = kzalloc(payload_sz, GFP_ATOMIC);
if (!payload)
return SCSI_MLQUEUE_DEVICE_BUSY;
}
+ /*
+ * sgl is a list of PAGEs, and payload->range.pfn_array
+ * expects the page number in the unit of HV_HYP_PAGE_SIZE (the
+ * page size that Hyper-V uses, so here we need to divide PAGEs
+ * into HV_HYP_PAGE in case that PAGE_SIZE > HV_HYP_PAGE_SIZE.
+ * Besides, payload->range.offset should be the offset in one
+ * HV_HYP_PAGE.
+ */
payload->range.len = length;
- payload->range.offset = sgl[0].offset;
+ payload->range.offset = offset_in_hvpg;
+ hvpgoff = sgl->offset >> HV_HYP_PAGE_SHIFT;
cur_sgl = sgl;
- for (i = 0; i < sg_count; i++) {
- payload->range.pfn_array[i] =
- page_to_pfn(sg_page((cur_sgl)));
- cur_sgl = sg_next(cur_sgl);
+ for (i = 0; i < hvpg_count; i++) {
+ /*
+ * 'i' is the index of hv pages in the payload and
+ * 'hvpgoff' is the offset (in hv pages) of the first
+ * hv page in the the first page. The relationship
+ * between the sum of 'i' and 'hvpgoff' and the offset
+ * (in hv pages) in a payload page ('hvpgoff_in_page')
+ * is as follow:
+ *
+ * |------------------ PAGE -------------------|
+ * | NR_HV_HYP_PAGES_IN_PAGE hvpgs in total |
+ * |hvpg|hvpg| ... |hvpg|... |hvpg|
+ * ^ ^ ^ ^
+ * +-hvpgoff-+ +-hvpgoff_in_page-+
+ * ^ |
+ * +--------------------- i ---------------------------+
+ */
+ unsigned int hvpgoff_in_page =
+ (i + hvpgoff) % NR_HV_HYP_PAGES_IN_PAGE;
+
+ /*
+ * Two cases that we need to fetch a page:
+ * 1) i == 0, the first step or
+ * 2) hvpgoff_in_page == 0, when we reach the boundary
+ * of a page.
+ */
+ if (hvpgoff_in_page == 0 || i == 0) {
+ hvpfn = page_to_hvpfn(sg_page(cur_sgl));
+ cur_sgl = sg_next(cur_sgl);
+ }
+
+ payload->range.pfn_array[i] = hvpfn + hvpgoff_in_page;
}
}