Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xen/gndev: Xen backend support for paged out grant targets V4.

Since Xen-4.2, hvm domains may have portions of their memory paged out. When a
foreign domain (such as dom0) attempts to map these frames, the map will
initially fail. The hypervisor returns a suitable errno, and kicks an
asynchronous page-in operation carried out by a helper. The foreign domain is
expected to retry the mapping operation until it eventually succeeds. The
foreign domain is not put to sleep because itself could be the one running the
pager assist (typical scenario for dom0).

This patch adds support for this mechanism for backend drivers using grant
mapping and copying operations. Specifically, this covers the blkback and
gntdev drivers (which map foreign grants), and the netback driver (which copies
foreign grants).

* Add a retry method for grants that fail with GNTST_eagain (i.e. because the
target foreign frame is paged out).
* Insert hooks with appropriate wrappers in the aforementioned drivers.

The retry loop is only invoked if the grant operation status is GNTST_eagain.
It guarantees to leave a new status code different from GNTST_eagain. Any other
status code results in identical code execution as before.

The retry loop performs 256 attempts with increasing time intervals through a
32 second period. It uses msleep to yield while waiting for the next retry.

V2 after feedback from David Vrabel:
* Explicit MAX_DELAY instead of wrap-around delay into zero
* Abstract GNTST_eagain check into core grant table code for netback module.

V3 after feedback from Ian Campbell:
* Add placeholder in array of grant table error descriptions for unrelated
error code we jump over.
* Eliminate single map and retry macro in favor of a generic batch flavor.
* Some renaming.
* Bury most implementation in grant_table.c, cleaner interface.

V4 rebased on top of sync of Xen grant table interface headers.

Signed-off-by: Andres Lagar-Cavilla <andres@lagarcavilla.org>
Acked-by: Ian Campbell <ian.campbell@citrix.com>
[v5: Fixed whitespace issues]
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>

authored by

Andres Lagar-Cavilla and committed by
Konrad Rzeszutek Wilk
c571898f c3cb4709

+70 -12
+3 -8
drivers/net/xen-netback/netback.c
··· 635 635 return; 636 636 637 637 BUG_ON(npo.copy_prod > ARRAY_SIZE(netbk->grant_copy_op)); 638 - ret = HYPERVISOR_grant_table_op(GNTTABOP_copy, &netbk->grant_copy_op, 639 - npo.copy_prod); 640 - BUG_ON(ret != 0); 638 + gnttab_batch_copy(netbk->grant_copy_op, npo.copy_prod); 641 639 642 640 while ((skb = __skb_dequeue(&rxq)) != NULL) { 643 641 sco = (struct skb_cb_overlay *)skb->cb; ··· 1458 1460 static void xen_netbk_tx_action(struct xen_netbk *netbk) 1459 1461 { 1460 1462 unsigned nr_gops; 1461 - int ret; 1462 1463 1463 1464 nr_gops = xen_netbk_tx_build_gops(netbk); 1464 1465 1465 1466 if (nr_gops == 0) 1466 1467 return; 1467 - ret = HYPERVISOR_grant_table_op(GNTTABOP_copy, 1468 - netbk->tx_copy_ops, nr_gops); 1469 - BUG_ON(ret); 1468 + 1469 + gnttab_batch_copy(netbk->tx_copy_ops, nr_gops); 1470 1470 1471 1471 xen_netbk_tx_submit(netbk); 1472 - 1473 1472 } 1474 1473 1475 1474 static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx)
+53
drivers/xen/grant-table.c
··· 38 38 #include <linux/vmalloc.h> 39 39 #include <linux/uaccess.h> 40 40 #include <linux/io.h> 41 + #include <linux/delay.h> 41 42 #include <linux/hardirq.h> 42 43 43 44 #include <xen/xen.h> ··· 824 823 } 825 824 EXPORT_SYMBOL_GPL(gnttab_max_grant_frames); 826 825 826 + /* Handling of paged out grant targets (GNTST_eagain) */ 827 + #define MAX_DELAY 256 828 + static inline void 829 + gnttab_retry_eagain_gop(unsigned int cmd, void *gop, int16_t *status, 830 + const char *func) 831 + { 832 + unsigned delay = 1; 833 + 834 + do { 835 + BUG_ON(HYPERVISOR_grant_table_op(cmd, gop, 1)); 836 + if (*status == GNTST_eagain) 837 + msleep(delay++); 838 + } while ((*status == GNTST_eagain) && (delay < MAX_DELAY)); 839 + 840 + if (delay >= MAX_DELAY) { 841 + printk(KERN_ERR "%s: %s eagain grant\n", func, current->comm); 842 + *status = GNTST_bad_page; 843 + } 844 + } 845 + 846 + void gnttab_batch_map(struct gnttab_map_grant_ref *batch, unsigned count) 847 + { 848 + struct gnttab_map_grant_ref *op; 849 + 850 + if (HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, batch, count)) 851 + BUG(); 852 + for (op = batch; op < batch + count; op++) 853 + if (op->status == GNTST_eagain) 854 + gnttab_retry_eagain_gop(GNTTABOP_map_grant_ref, op, 855 + &op->status, __func__); 856 + } 857 + EXPORT_SYMBOL_GPL(gnttab_batch_map); 858 + 859 + void gnttab_batch_copy(struct gnttab_copy *batch, unsigned count) 860 + { 861 + struct gnttab_copy *op; 862 + 863 + if (HYPERVISOR_grant_table_op(GNTTABOP_copy, batch, count)) 864 + BUG(); 865 + for (op = batch; op < batch + count; op++) 866 + if (op->status == GNTST_eagain) 867 + gnttab_retry_eagain_gop(GNTTABOP_copy, op, 868 + &op->status, __func__); 869 + } 870 + EXPORT_SYMBOL_GPL(gnttab_batch_copy); 871 + 827 872 int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops, 828 873 struct gnttab_map_grant_ref *kmap_ops, 829 874 struct page **pages, unsigned int count) ··· 882 835 ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, map_ops, count); 883 836 if (ret) 884 837 return ret; 838 + 839 + /* Retry eagain maps */ 840 + for (i = 0; i < count; i++) 841 + if (map_ops[i].status == GNTST_eagain) 842 + gnttab_retry_eagain_gop(GNTTABOP_map_grant_ref, map_ops + i, 843 + &map_ops[i].status, __func__); 885 844 886 845 if (xen_feature(XENFEAT_auto_translated_physmap)) 887 846 return ret;
+2 -4
drivers/xen/xenbus/xenbus_client.c
··· 490 490 491 491 op.host_addr = arbitrary_virt_to_machine(pte).maddr; 492 492 493 - if (HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1)) 494 - BUG(); 493 + gnttab_batch_map(&op, 1); 495 494 496 495 if (op.status != GNTST_okay) { 497 496 free_vm_area(area); ··· 571 572 gnttab_set_map_op(&op, (unsigned long)vaddr, GNTMAP_host_map, gnt_ref, 572 573 dev->otherend_id); 573 574 574 - if (HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1)) 575 - BUG(); 575 + gnttab_batch_map(&op, 1); 576 576 577 577 if (op.status != GNTST_okay) { 578 578 xenbus_dev_fatal(dev, op.status,
+12
include/xen/grant_table.h
··· 189 189 int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops, 190 190 struct page **pages, unsigned int count, bool clear_pte); 191 191 192 + /* Perform a batch of grant map/copy operations. Retry every batch slot 193 + * for which the hypervisor returns GNTST_eagain. This is typically due 194 + * to paged out target frames. 195 + * 196 + * Will retry for 1, 2, ... 255 ms, i.e. 256 times during 32 seconds. 197 + * 198 + * Return value in each iand every status field of the batch guaranteed 199 + * to not be GNTST_eagain. 200 + */ 201 + void gnttab_batch_map(struct gnttab_map_grant_ref *batch, unsigned count); 202 + void gnttab_batch_copy(struct gnttab_copy *batch, unsigned count); 203 + 192 204 #endif /* __ASM_GNTTAB_H__ */