|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCHv5 12/14] xen-blkback: safely unmap grants in case they are still in use
Hi David,
Recently I met an issue which is likely related with this patch. It
happened when running block benchmark on domU, the backend was an iSCSI
disk connected to dom0. I got below panic at put_page_testzero() on
dom0, at that time the ixgbe network card was freeing skb pages in
__skb_frag_unref() but the page->_count was already 0.
Do you think is it possiable that page was already freed by blkback?
Thanks,
-Bob
------------[ cut here ]------------
kernel BUG at include/linux/mm.h:288!
invalid opcode: 0000 [#1] SMP
Modules linked in: dm_queue_length ib_iser rdma_cm ib_cm iw_cm ib_sa
ib_mad ib_core ib_addr iscsi_tcp xt_mac xt_nat nf_conntrack_netlink
xt_conntrack ipt_REJECT xt_TCPMSS xt_comment xt_connmark iptable_raw
xt_REDIRECT ext4 jbd2 xt_state xt_NFQUEUE iptable_nat nf_conntrack_ipv4
nf_defrag_ipv4 nf_nat_ipv4 nf_nat nf_conntrack ip_gre gre
nfnetlink_queue nfnetlink ip6table_filter ip6_tables ebtable_nat
ebtables softdog iptable_filter ip_tables xen_pciback xen_netback
xen_blkback xen_gntalloc xen_gntdev xen_evtchn xenfs xen_privcmd 8021q
garp bridge stp llc sunrpc bonding mlx4_en mlx4_core ipv6 ipmi_devintf
ipmi_si ipmi_msghandler vhost_net macvtap macvlan tun iTCO_wdt
iTCO_vendor_support coretemp freq_table mperf intel_powerclamp
ghash_clmulni_intel microcode pcspkr i2c_i801 i2c_core lpc_ich mfd_core
shpchp ioatdma sg ext3 jbd mbcache dm_round_robin sd_mod crc_t10dif
aesni_intel ablk_helper cryptd lrw aes_x86_64 xts gf128mul ahci libahci
megaraid_sas ixgbe hwmon dca dm_multipath dm_mirror dm_region_hash
dm_log dm_mod crc32c_intel be2iscsi bnx2i cnic uio cxgb4i cxgb4 cxgb3i
libcxgbi cxgb3 mdio libiscsi_tcp qla4xxx iscsi_boot_sysfs libiscsi
scsi_transport_iscsi [last unloaded: bonding]
CPU 0
Pid: 31309, comm: kworker/0:0 Tainted: G W
3.8.13-48.el6uek.20150306.x86_64 #2 Oracle Corporation SUN FIRE X4170 M3
/ASSY,MOTHERBOARD,1U
RIP: e030:[<ffffffff8113d481>] [<ffffffff8113d481>] put_page+0x31/0x50
RSP: e02b:ffff880278e03d10 EFLAGS: 00010246
RAX: 0000000000000000 RBX: ffff8802692257b8 RCX: 00000000ffffffff
RDX: ffff88026ea4b2c0 RSI: 0000000000000200 RDI: ffffea00088670c0
RBP: ffff880278e03d10 R08: ffff88026a6e4500 R09: ffff880270a25098
R10: 0000000000000001 R11: ffff880278e03cf0 R12: 0000000000000006
R13: 00000000ffffff8e R14: ffff880270a25098 R15: ffff88026c95d9f0
FS: 00007fbb497cf700(0000) GS:ffff880278e00000(0000) knlGS:0000000000000000
CS: e033 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00000000007aaed0 CR3: 00000002703c2000 CR4: 0000000000042660
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Process kworker/0:0 (pid: 31309, threadinfo ffff88020c608000, task
ffff880200e24140)
Stack:
ffff880278e03d30 ffffffff814c3855 ffff8802692257b8 ffff8802692257b8
ffff880278e03d50 ffffffff814c38ee 0000000000000000 ffffc9001188faa0
ffff880278e03d70 ffffffff814c3ff1 ffffc9001188faa0 ffff88026c95d8e0
Call Trace:
<IRQ>
[<ffffffff814c3855>] skb_release_data+0x75/0xf0
[<ffffffff814c38ee>] __kfree_skb+0x1e/0xa0
[<ffffffff814c3ff1>] consume_skb+0x31/0x70
[<ffffffff814ce6ed>] dev_kfree_skb_any+0x3d/0x50
[<ffffffffa01d0bdc>] ixgbe_clean_tx_irq+0xac/0x530 [ixgbe]
[<ffffffffa01d10b3>] ixgbe_poll+0x53/0x1a0 [ixgbe]
[<ffffffff814d3d05>] net_rx_action+0x105/0x2b0
[<ffffffff81066587>] __do_softirq+0xd7/0x240
[<ffffffff815a7c5c>] call_softirq+0x1c/0x30
[<ffffffff810174b5>] do_softirq+0x65/0xa0
[<ffffffff8106636d>] irq_exit+0xbd/0xe0
[<ffffffff8133d3e5>] xen_evtchn_do_upcall+0x35/0x50
[<ffffffff815a7cbe>] xen_do_hypervisor_callback+0x1e/0x30
<EOI>
[<ffffffff8100128a>] ? xen_hypercall_grant_table_op+0xa/0x20
[<ffffffff8100128a>] ? xen_hypercall_grant_table_op+0xa/0x20
[<ffffffff8133a4e6>] ? gnttab_unmap_refs+0x26/0x70
[<ffffffff8133a5ba>] ? __gnttab_unmap_refs_async+0x8a/0xb0
[<ffffffff8133a672>] ? gnttab_unmap_work+0x22/0x30
[<ffffffff8107bf10>] ? process_one_work+0x180/0x420
[<ffffffff8107df4e>] ? worker_thread+0x12e/0x390
[<ffffffff8107de20>] ? manage_workers+0x180/0x180
[<ffffffff8108329e>] ? kthread+0xce/0xe0
[<ffffffff810039ee>] ? xen_end_context_switch+0x1e/0x30
[<ffffffff810831d0>] ? kthread_freezable_should_stop+0x70/0x70
[<ffffffff815a682c>] ? ret_from_fork+0x7c/0xb0
[<ffffffff810831d0>] ? kthread_freezable_should_stop+0x70/0x70
Code: 66 66 90 66 f7 07 00 c0 75 25 8b 47 1c 85 c0 74 1a f0 ff 4f 1c 0f
94 c0 84 c0 75 06 c9 c3 0f 1f 40 00 e8 43 fd ff ff c9 66 90 c3 <0f> 0b
eb fe 66 66 2e 0f 1f 84 00 00 00 00 00 e8 5b fd ff ff c9
RIP [<ffffffff8113d481>] put_page+0x31/0x50
RSP <ffff880278e03d10>
---[ end trace 9f93fe018444fc09 ]---
Kernel panic - not syncing: Fatal exception in interrupt
(XEN) Domain 0 crashed: rebooting machine in 5 seconds.
(XEN) Resetting with ACPI MEMORY or I/O RESET_REG.
On 01/28/2015 12:44 AM, David Vrabel wrote:
> From: Jennifer Herbert <jennifer.herbert@xxxxxxxxxx>
>
> Use gnttab_unmap_refs_async() to wait until the mapped pages are no
> longer in use before unmapping them.
>
> This allows blkback to use network storage which may retain refs to
> pages in queued skbs after the block I/O has completed.
>
> Signed-off-by: Jennifer Herbert <jennifer.herbert@xxxxxxxxxx>
> Acked-by: Roger Pau Monnà <roger.pau@xxxxxxxxxx>
> Acked-by: Jens Axboe <axboe@xxxxxxxxx>
> Signed-off-by: David Vrabel <david.vrabel@xxxxxxxxxx>
> ---
> drivers/block/xen-blkback/blkback.c | 169
> ++++++++++++++++++++++++-----------
> drivers/block/xen-blkback/common.h | 3 +
> 2 files changed, 122 insertions(+), 50 deletions(-)
>
> diff --git a/drivers/block/xen-blkback/blkback.c
> b/drivers/block/xen-blkback/blkback.c
> index 908e630..2a04d34 100644
> --- a/drivers/block/xen-blkback/blkback.c
> +++ b/drivers/block/xen-blkback/blkback.c
> @@ -47,6 +47,7 @@
> #include <asm/xen/hypervisor.h>
> #include <asm/xen/hypercall.h>
> #include <xen/balloon.h>
> +#include <xen/grant_table.h>
> #include "common.h"
>
> /*
> @@ -262,6 +263,17 @@ static void put_persistent_gnt(struct xen_blkif *blkif,
> atomic_dec(&blkif->persistent_gnt_in_use);
> }
>
> +static void free_persistent_gnts_unmap_callback(int result,
> + struct gntab_unmap_queue_data
> *data)
> +{
> + struct completion *c = data->data;
> +
> + /* BUG_ON used to reproduce existing behaviour,
> + but is this the best way to deal with this? */
> + BUG_ON(result);
> + complete(c);
> +}
> +
> static void free_persistent_gnts(struct xen_blkif *blkif, struct rb_root
> *root,
> unsigned int num)
> {
> @@ -269,8 +281,17 @@ static void free_persistent_gnts(struct xen_blkif
> *blkif, struct rb_root *root,
> struct page *pages[BLKIF_MAX_SEGMENTS_PER_REQUEST];
> struct persistent_gnt *persistent_gnt;
> struct rb_node *n;
> - int ret = 0;
> int segs_to_unmap = 0;
> + struct gntab_unmap_queue_data unmap_data;
> + struct completion unmap_completion;
> +
> + init_completion(&unmap_completion);
> +
> + unmap_data.data = &unmap_completion;
> + unmap_data.done = &free_persistent_gnts_unmap_callback;
> + unmap_data.pages = pages;
> + unmap_data.unmap_ops = unmap;
> + unmap_data.kunmap_ops = NULL;
>
> foreach_grant_safe(persistent_gnt, n, root, node) {
> BUG_ON(persistent_gnt->handle ==
> @@ -285,9 +306,11 @@ static void free_persistent_gnts(struct xen_blkif
> *blkif, struct rb_root *root,
>
> if (++segs_to_unmap == BLKIF_MAX_SEGMENTS_PER_REQUEST ||
> !rb_next(&persistent_gnt->node)) {
> - ret = gnttab_unmap_refs(unmap, NULL, pages,
> - segs_to_unmap);
> - BUG_ON(ret);
> +
> + unmap_data.count = segs_to_unmap;
> + gnttab_unmap_refs_async(&unmap_data);
> + wait_for_completion(&unmap_completion);
> +
> put_free_pages(blkif, pages, segs_to_unmap);
> segs_to_unmap = 0;
> }
> @@ -653,18 +676,14 @@ void xen_blkbk_free_caches(struct xen_blkif *blkif)
> shrink_free_pagepool(blkif, 0 /* All */);
> }
>
> -/*
> - * Unmap the grant references, and also remove the M2P over-rides
> - * used in the 'pending_req'.
> - */
> -static void xen_blkbk_unmap(struct xen_blkif *blkif,
> - struct grant_page *pages[],
> - int num)
> +static unsigned int xen_blkbk_unmap_prepare(
> + struct xen_blkif *blkif,
> + struct grant_page **pages,
> + unsigned int num,
> + struct gnttab_unmap_grant_ref *unmap_ops,
> + struct page **unmap_pages)
> {
> - struct gnttab_unmap_grant_ref unmap[BLKIF_MAX_SEGMENTS_PER_REQUEST];
> - struct page *unmap_pages[BLKIF_MAX_SEGMENTS_PER_REQUEST];
> unsigned int i, invcount = 0;
> - int ret;
>
> for (i = 0; i < num; i++) {
> if (pages[i]->persistent_gnt != NULL) {
> @@ -674,21 +693,95 @@ static void xen_blkbk_unmap(struct xen_blkif *blkif,
> if (pages[i]->handle == BLKBACK_INVALID_HANDLE)
> continue;
> unmap_pages[invcount] = pages[i]->page;
> - gnttab_set_unmap_op(&unmap[invcount], vaddr(pages[i]->page),
> + gnttab_set_unmap_op(&unmap_ops[invcount], vaddr(pages[i]->page),
> GNTMAP_host_map, pages[i]->handle);
> pages[i]->handle = BLKBACK_INVALID_HANDLE;
> - if (++invcount == BLKIF_MAX_SEGMENTS_PER_REQUEST) {
> - ret = gnttab_unmap_refs(unmap, NULL, unmap_pages,
> - invcount);
> + invcount++;
> + }
> +
> + return invcount;
> +}
> +
> +static void xen_blkbk_unmap_and_respond_callback(int result, struct
> gntab_unmap_queue_data *data)
> +{
> + struct pending_req* pending_req = (struct pending_req*) (data->data);
> + struct xen_blkif *blkif = pending_req->blkif;
> +
> + /* BUG_ON used to reproduce existing behaviour,
> + but is this the best way to deal with this? */
> + BUG_ON(result);
> +
> + put_free_pages(blkif, data->pages, data->count);
> + make_response(blkif, pending_req->id,
> + pending_req->operation, pending_req->status);
> + free_req(blkif, pending_req);
> + /*
> + * Make sure the request is freed before releasing blkif,
> + * or there could be a race between free_req and the
> + * cleanup done in xen_blkif_free during shutdown.
> + *
> + * NB: The fact that we might try to wake up pending_free_wq
> + * before drain_complete (in case there's a drain going on)
> + * it's not a problem with our current implementation
> + * because we can assure there's no thread waiting on
> + * pending_free_wq if there's a drain going on, but it has
> + * to be taken into account if the current model is changed.
> + */
> + if (atomic_dec_and_test(&blkif->inflight) &&
> atomic_read(&blkif->drain)) {
> + complete(&blkif->drain_complete);
> + }
> + xen_blkif_put(blkif);
> +}
> +
> +static void xen_blkbk_unmap_and_respond(struct pending_req *req)
> +{
> + struct gntab_unmap_queue_data* work = &req->gnttab_unmap_data;
> + struct xen_blkif *blkif = req->blkif;
> + struct grant_page **pages = req->segments;
> + unsigned int invcount;
> +
> + invcount = xen_blkbk_unmap_prepare(blkif, pages, req->nr_pages,
> + req->unmap, req->unmap_pages);
> +
> + work->data = req;
> + work->done = xen_blkbk_unmap_and_respond_callback;
> + work->unmap_ops = req->unmap;
> + work->kunmap_ops = NULL;
> + work->pages = req->unmap_pages;
> + work->count = invcount;
> +
> + gnttab_unmap_refs_async(&req->gnttab_unmap_data);
> +}
> +
> +
> +/*
> + * Unmap the grant references.
> + *
> + * This could accumulate ops up to the batch size to reduce the number
> + * of hypercalls, but since this is only used in error paths there's
> + * no real need.
> + */
> +static void xen_blkbk_unmap(struct xen_blkif *blkif,
> + struct grant_page *pages[],
> + int num)
> +{
> + struct gnttab_unmap_grant_ref unmap[BLKIF_MAX_SEGMENTS_PER_REQUEST];
> + struct page *unmap_pages[BLKIF_MAX_SEGMENTS_PER_REQUEST];
> + unsigned int invcount = 0;
> + int ret;
> +
> + while (num) {
> + unsigned int batch = min(num, BLKIF_MAX_SEGMENTS_PER_REQUEST);
> +
> + invcount = xen_blkbk_unmap_prepare(blkif, pages, batch,
> + unmap, unmap_pages);
> + if (invcount) {
> + ret = gnttab_unmap_refs(unmap, NULL, unmap_pages,
> invcount);
> BUG_ON(ret);
> put_free_pages(blkif, unmap_pages, invcount);
> - invcount = 0;
> }
> - }
> - if (invcount) {
> - ret = gnttab_unmap_refs(unmap, NULL, unmap_pages, invcount);
> - BUG_ON(ret);
> - put_free_pages(blkif, unmap_pages, invcount);
> + pages += batch;
> + num -= batch;
> }
> }
>
> @@ -982,32 +1075,8 @@ static void __end_block_io_op(struct pending_req
> *pending_req, int error)
> * the grant references associated with 'request' and provide
> * the proper response on the ring.
> */
> - if (atomic_dec_and_test(&pending_req->pendcnt)) {
> - struct xen_blkif *blkif = pending_req->blkif;
> -
> - xen_blkbk_unmap(blkif,
> - pending_req->segments,
> - pending_req->nr_pages);
> - make_response(blkif, pending_req->id,
> - pending_req->operation, pending_req->status);
> - free_req(blkif, pending_req);
> - /*
> - * Make sure the request is freed before releasing blkif,
> - * or there could be a race between free_req and the
> - * cleanup done in xen_blkif_free during shutdown.
> - *
> - * NB: The fact that we might try to wake up pending_free_wq
> - * before drain_complete (in case there's a drain going on)
> - * it's not a problem with our current implementation
> - * because we can assure there's no thread waiting on
> - * pending_free_wq if there's a drain going on, but it has
> - * to be taken into account if the current model is changed.
> - */
> - if (atomic_dec_and_test(&blkif->inflight) &&
> atomic_read(&blkif->drain)) {
> - complete(&blkif->drain_complete);
> - }
> - xen_blkif_put(blkif);
> - }
> + if (atomic_dec_and_test(&pending_req->pendcnt))
> + xen_blkbk_unmap_and_respond(pending_req);
> }
>
> /*
> diff --git a/drivers/block/xen-blkback/common.h
> b/drivers/block/xen-blkback/common.h
> index f65b807..cc90a84 100644
> --- a/drivers/block/xen-blkback/common.h
> +++ b/drivers/block/xen-blkback/common.h
> @@ -350,6 +350,9 @@ struct pending_req {
> struct grant_page *indirect_pages[MAX_INDIRECT_PAGES];
> struct seg_buf seg[MAX_INDIRECT_SEGMENTS];
> struct bio *biolist[MAX_INDIRECT_SEGMENTS];
> + struct gnttab_unmap_grant_ref unmap[MAX_INDIRECT_SEGMENTS];
> + struct page *unmap_pages[MAX_INDIRECT_SEGMENTS];
> + struct gntab_unmap_queue_data gnttab_unmap_data;
> };
>
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |