From owner-svn-src-head@freebsd.org Thu Nov 17 21:02:56 2016 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 98100C47F78; Thu, 17 Nov 2016 21:02:56 +0000 (UTC) (envelope-from glebius@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 7230E145E; Thu, 17 Nov 2016 21:02:56 +0000 (UTC) (envelope-from glebius@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id uAHL2tfq012216; Thu, 17 Nov 2016 21:02:55 GMT (envelope-from glebius@FreeBSD.org) Received: (from glebius@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id uAHL2tbS012213; Thu, 17 Nov 2016 21:02:55 GMT (envelope-from glebius@FreeBSD.org) Message-Id: <201611172102.uAHL2tbS012213@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: glebius set sender to glebius@FreeBSD.org using -f From: Gleb Smirnoff Date: Thu, 17 Nov 2016 21:02:55 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r308783 - in head: sys/kern sys/sys usr.bin/netstat X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 17 Nov 2016 21:02:56 -0000 Author: glebius Date: Thu Nov 17 21:02:55 2016 New Revision: 308783 URL: https://svnweb.freebsd.org/changeset/base/308783 Log: Use bogus_page to properly reduce number of I/Os in sendfile(2). The new sendfile_swapin() loop works this way: - Find first invalid page in the request. - Do vm_pager_has_page() and get count of pages, that can be taken in single I/O. - Trim valid pages from the end of the request. - Cycle through the request and substitute to bogus_page all valid pages that are in the middle of the request. - After I/O launched (pager copies array of pages into buf(9), it is important to restore proper page pointers with help vm_page_lookup(). Count bogus pages used and report them in sendfile stats. Modified: head/sys/kern/kern_sendfile.c head/sys/sys/sf_buf.h head/usr.bin/netstat/mbuf.c Modified: head/sys/kern/kern_sendfile.c ============================================================================== --- head/sys/kern/kern_sendfile.c Thu Nov 17 21:01:27 2016 (r308782) +++ head/sys/kern/kern_sendfile.c Thu Nov 17 21:02:55 2016 (r308783) @@ -62,6 +62,8 @@ __FBSDID("$FreeBSD$"); #include #include +extern vm_page_t bogus_page; + /* * Structure describing a single sendfile(2) I/O, which may consist of * several underlying pager I/Os. @@ -258,7 +260,8 @@ sendfile_iodone(void *arg, vm_page_t *pg struct socket *so; for (int i = 0; i < count; i++) - vm_page_xunbusy(pg[i]); + if (pg[i] != bogus_page) + vm_page_xunbusy(pg[i]); if (error) sfio->error = error; @@ -341,51 +344,53 @@ sendfile_swapin(vm_object_t obj, struct } /* - * Now 'i' points to first invalid page, iterate further - * to make 'j' point at first valid after a bunch of - * invalid ones. - */ - for (j = i + 1; j < npages; j++) - if (vm_page_is_valid(pa[j], vmoff(j, off) & PAGE_MASK, - xfsize(j, npages, off, len))) { - SFSTAT_INC(sf_pages_valid); - break; - } - - /* - * Now we got region of invalid pages between 'i' and 'j'. - * Check that they belong to pager. They may not be there, - * which is a regular situation for shmem pager. For vnode - * pager this happens only in case of sparse file. + * Next page is invalid. Check if it belongs to pager. It + * may not be there, which is a regular situation for shmem + * pager. For vnode pager this happens only in case of + * a sparse file. * * Important feature of vm_pager_has_page() is the hint * stored in 'a', about how many pages we can pagein after * this page in a single I/O. */ - while (!vm_pager_has_page(obj, OFF_TO_IDX(vmoff(i, off)), - NULL, &a) && i < j) { + if (!vm_pager_has_page(obj, OFF_TO_IDX(vmoff(i, off)), NULL, + &a)) { pmap_zero_page(pa[i]); pa[i]->valid = VM_PAGE_BITS_ALL; pa[i]->dirty = 0; vm_page_xunbusy(pa[i]); i++; - } - if (i == j) continue; + } /* * We want to pagein as many pages as possible, limited only * by the 'a' hint and actual request. - * - * We should not pagein into already valid page, thus if - * 'j' didn't reach last page, trim by that page. - * - * When the pagein fulfils the request, also specify readahead. */ - if (j < npages) - a = min(a, j - i - 1); count = min(a + 1, npages - i); + /* + * We should not pagein into a valid page, thus we first trim + * any valid pages off the end of request, and substitute + * to bogus_page those, that are in the middle. + */ + for (j = i + count - 1; j > i; j--) { + if (vm_page_is_valid(pa[j], vmoff(j, off) & PAGE_MASK, + xfsize(j, npages, off, len))) { + count--; + rhpages = 0; + } else + break; + } + for (j = i + 1; j < i + count - 1; j++) + if (vm_page_is_valid(pa[j], vmoff(j, off) & PAGE_MASK, + xfsize(j, npages, off, len))) { + vm_page_xunbusy(pa[j]); + SFSTAT_INC(sf_pages_valid); + SFSTAT_INC(sf_pages_bogus); + pa[j] = bogus_page; + } + refcount_acquire(&sfio->nios); rv = vm_pager_get_pages_async(obj, pa + i, count, NULL, i + count == npages ? &rhpages : NULL, @@ -398,13 +403,18 @@ sendfile_swapin(vm_object_t obj, struct if (i + count == npages) SFSTAT_ADD(sf_rhpages_read, rhpages); -#ifdef INVARIANTS - for (j = i; j < i + count && j < npages; j++) - KASSERT(pa[j] == vm_page_lookup(obj, - OFF_TO_IDX(vmoff(j, off))), - ("pa[j] %p lookup %p\n", pa[j], - vm_page_lookup(obj, OFF_TO_IDX(vmoff(j, off))))); -#endif + /* + * Restore the valid page pointers. They are already + * unbusied, but still wired. + */ + for (j = i; j < i + count; j++) + if (pa[j] == bogus_page) { + pa[j] = vm_page_lookup(obj, + OFF_TO_IDX(vmoff(j, off))); + KASSERT(pa[j], ("%s: page %p[%d] disappeared", + __func__, pa, j)); + + } i += count; nios++; } Modified: head/sys/sys/sf_buf.h ============================================================================== --- head/sys/sys/sf_buf.h Thu Nov 17 21:01:27 2016 (r308782) +++ head/sys/sys/sf_buf.h Thu Nov 17 21:02:55 2016 (r308783) @@ -41,6 +41,7 @@ struct sfstat { /* sendfile statistic uint64_t sf_busy; /* times aborted on a busy page */ uint64_t sf_allocfail; /* times sfbuf allocation failed */ uint64_t sf_allocwait; /* times sfbuf allocation had to wait */ + uint64_t sf_pages_bogus; /* times bogus page was used */ }; #ifdef _KERNEL Modified: head/usr.bin/netstat/mbuf.c ============================================================================== --- head/usr.bin/netstat/mbuf.c Thu Nov 17 21:01:27 2016 (r308782) +++ head/usr.bin/netstat/mbuf.c Thu Nov 17 21:02:55 2016 (r308783) @@ -340,6 +340,9 @@ mbpr(void *kvmd, u_long mbaddr) xo_emit("{:sendfile-pages-valid/%ju} " "{N:pages were valid at time of a sendfile request}\n", (uintmax_t)sfstat.sf_pages_valid); + xo_emit("{:sendfile-pages-bogus/%ju} " + "{N:pages were valid and substituted to bogus page}\n", + (uintmax_t)sfstat.sf_pages_bogus); xo_emit("{:sendfile-requested-readahead/%ju} " "{N:pages were requested for read ahead by applications}\n", (uintmax_t)sfstat.sf_rhpages_requested);