VirtualBox

Changeset 77944 in vbox for trunk


Ignore:
Timestamp:
Mar 29, 2019 3:17:48 AM (6 years ago)
Author:
vboxsync
Message:

linux/vboxsf: Implemented our own .sendfile for 2.5.30 - 2.6.23 that does not muck around with the page cache unless there are writable mmap'ings of the file. This should be the final page cache issue I'm aware of. bugref:9172

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Additions/linux/sharedfolders/regops.c

    r77943 r77944  
    124124*   Internal Functions                                                                                                           *
    125125*********************************************************************************************************************************/
     126DECLINLINE(void) vbsf_put_page(struct page *pPage);
    126127DECLINLINE(void) vbsf_unlock_user_pages(struct page **papPages, size_t cPages, bool fSetDirty, bool fLockPgHack);
    127128static void vbsf_reg_write_sync_page_cache(struct address_space *mapping, loff_t offFile, uint32_t cbRange,
     
    10131014
    10141015#endif /* 2.6.17 <= LINUX_VERSION_CODE < 3.16.0 */
     1016
     1017#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 30) \
     1018 && LINUX_VERSION_CODE <  KERNEL_VERSION(2, 6, 23)
     1019/**
     1020 * Our own senfile implementation that does not go via the page cache like
     1021 * generic_file_sendfile() does.
     1022 */
     1023static ssize_t vbsf_reg_sendfile(struct file *pFile, loff_t *poffFile, size_t cbToSend, read_actor_t pfnActor, void *pvUser)
     1024{
     1025    struct inode           *inode = VBSF_GET_F_DENTRY(pFile)->d_inode;
     1026    struct vbsf_super_info *sf_g  = VBSF_GET_SUPER_INFO(inode->i_sb);
     1027    ssize_t                 cbRet;
     1028    SFLOGFLOW(("vbsf_reg_sendfile: pFile=%p poffFile=%p{%#RX64} cbToSend=%#zx pfnActor=%p pvUser=%p\n",
     1029               pFile, poffFile, poffFile ? *poffFile : 0, cbToSend, pfnActor, pvUser));
     1030    Assert(sf_g);
     1031
     1032    /*
     1033     * Return immediately if asked to send nothing.
     1034     */
     1035    if (cbToSend == 0)
     1036        return 0;
     1037
     1038    /*
     1039     * Like for vbsf_reg_read() and vbsf_reg_read_iter(), we allow going via
     1040     * the page cache in some cases or configs.
     1041     */
     1042    if (vbsf_should_use_cached_read(pFile, inode->i_mapping, sf_g)) {
     1043        cbRet = generic_file_sendfile(pFile, poffFile, cbToSend, pfnActor, pvUser);
     1044        SFLOGFLOW(("vbsf_reg_sendfile: returns %#zx *poffFile=%#RX64 [generic_file_sendfile]\n", cbRet, poffFile ? *poffFile : UINT64_MAX));
     1045    } else {
     1046        /*
     1047         * Allocate a request and a bunch of pages for reading from the file.
     1048         */
     1049        struct page        *apPages[16];
     1050        loff_t              offFile = poffFile ? *poffFile : 0;
     1051        size_t const        cPages  = cbToSend + ((size_t)offFile & PAGE_OFFSET_MASK) >= RT_ELEMENTS(apPages) * PAGE_SIZE
     1052                                    ? RT_ELEMENTS(apPages)
     1053                                    : RT_ALIGN_Z(cbToSend + ((size_t)offFile & PAGE_OFFSET_MASK), PAGE_SIZE) >> PAGE_SHIFT;
     1054        size_t              iPage;
     1055        VBOXSFREADPGLSTREQ *pReq    = (VBOXSFREADPGLSTREQ *)VbglR0PhysHeapAlloc(RT_UOFFSETOF_DYN(VBOXSFREADPGLSTREQ,
     1056                                                                                                 PgLst.aPages[cPages]));
     1057        if (pReq) {
     1058            Assert(cPages > 0);
     1059            cbRet = 0;
     1060            for (iPage = 0; iPage < cPages; iPage++) {
     1061                struct page *pPage;
     1062                apPages[iPage] = pPage = alloc_page(GFP_USER);
     1063                if (pPage) {
     1064                    Assert(page_count(pPage) == 1);
     1065                    pReq->PgLst.aPages[iPage] = page_to_phys(pPage);
     1066                } else {
     1067                    while (iPage-- > 0)
     1068                        vbsf_put_page(apPages[iPage]);
     1069                    cbRet = -ENOMEM;
     1070                    break;
     1071                }
     1072            }
     1073            if (cbRet == 0) {
     1074                /*
     1075                 * Do the job.
     1076                 */
     1077                struct vbsf_reg_info *sf_r = (struct vbsf_reg_info *)pFile->private_data;
     1078                read_descriptor_t     RdDesc;
     1079                RdDesc.count    = cbToSend;
     1080                RdDesc.arg.data = pvUser;
     1081                RdDesc.written  = 0;
     1082                RdDesc.error    = 0;
     1083
     1084                Assert(sf_r);
     1085                Assert((sf_r->Handle.fFlags & VBSF_HANDLE_F_MAGIC_MASK) == VBSF_HANDLE_F_MAGIC);
     1086
     1087                while (cbToSend > 0) {
     1088                    /*
     1089                     * Read another chunk.  For paranoid reasons, we keep data where the page cache
     1090                     * would keep it, i.e. page offset bits corresponds to the file offset bits.
     1091                     */
     1092                    uint32_t const offPg0       = (uint32_t)offFile & (uint32_t)PAGE_OFFSET_MASK;
     1093                    uint32_t const cbToRead     = RT_MIN((cPages << PAGE_SHIFT) - offPg0, cbToSend);
     1094                    uint32_t const cPagesToRead = RT_ALIGN_Z(cbToRead + offPg0, PAGE_SIZE) >> PAGE_SHIFT;
     1095                    int            vrc;
     1096                    pReq->PgLst.offFirstPage = (uint16_t)offPg0;
     1097                    if (!signal_pending(current))
     1098                        vrc = VbglR0SfHostReqReadPgLst(sf_g->map.root, pReq, sf_r->Handle.hHost, offFile, cbToRead, cPagesToRead);
     1099                    else
     1100                        vrc = VERR_INTERRUPTED;
     1101                    if (RT_SUCCESS(vrc)) {
     1102                        /*
     1103                         * Pass what we read to the actor.
     1104                         */
     1105                        uint32_t    off      = offPg0;
     1106                        uint32_t    cbActual = pReq->Parms.cb32Read.u.value32;
     1107                        bool const  fIsEof   = cbActual < cbToRead;
     1108                        AssertStmt(cbActual <= cbToRead, cbActual = cbToRead);
     1109                        SFLOG3(("vbsf_reg_sendfile: Read %#x bytes (offPg0=%#x), wanted %#x ...\n", cbActual, offPg0, cbToRead));
     1110
     1111                        iPage = 0;
     1112                        while (cbActual > 0) {
     1113                            uint32_t const cbPage     = RT_MIN(cbActual, PAGE_SIZE - off);
     1114                            int const      cbRetActor = pfnActor(&RdDesc, apPages[iPage], off, cbPage);
     1115                            Assert(cbRetActor >= 0); /* Returns zero on failure, with RdDesc.error holding the status code. */
     1116
     1117                            AssertMsg(iPage < cPages && iPage < cPagesToRead, ("iPage=%#x cPages=%#x cPagesToRead=%#x\n", iPage, cPages, cPagesToRead));
     1118
     1119                            offFile += cbRetActor;
     1120                            if ((uint32_t)cbRetActor == cbPage && RdDesc.count > 0) {
     1121                                cbActual -= cbPage;
     1122                                cbToSend -= cbPage;
     1123                                iPage++;
     1124                            } else {
     1125                                SFLOG3(("vbsf_reg_sendfile: cbRetActor=%#x (%d) cbPage=%#x RdDesc{count=%#lx error=%d} iPage=%#x/%#x/%#x cbToSend=%#zx\n",
     1126                                        cbRetActor, cbRetActor, cbPage, RdDesc.count, RdDesc.error, iPage, cPagesToRead, cPages, cbToSend));
     1127                                vrc = VERR_CALLBACK_RETURN;
     1128                                break;
     1129                            }
     1130                            off = 0;
     1131                        }
     1132
     1133                        /*
     1134                         * Are we done yet?
     1135                         */
     1136                        if (RT_FAILURE_NP(vrc) || cbToSend == 0 || RdDesc.error != 0 || fIsEof) {
     1137                            break;
     1138                        }
     1139
     1140                        /*
     1141                         * Replace pages held by the actor.
     1142                         */
     1143                        vrc = VINF_SUCCESS;
     1144                        for (iPage = 0; iPage < cPages; iPage++) {
     1145                            struct page *pPage = apPages[iPage];
     1146                            if (page_count(pPage) != 1) {
     1147                                struct page *pNewPage = alloc_page(GFP_USER);
     1148                                if (pNewPage) {
     1149                                    SFLOGFLOW(("vbsf_reg_sendfile: Replacing page #%x: %p -> %p\n", iPage, pPage, pNewPage));
     1150                                    vbsf_put_page(pPage);
     1151                                    apPages[iPage] = pNewPage;
     1152                                } else {
     1153                                    SFLOGFLOW(("vbsf_reg_sendfile: Failed to allocate a replacement page.\n"));
     1154                                    vrc = VERR_NO_MEMORY;
     1155                                    break;
     1156                                }
     1157                            }
     1158                        }
     1159                        if (RT_FAILURE(vrc))
     1160                            break; /* RdDesc.written should be non-zero, so don't bother with setting error. */
     1161                    } else {
     1162                        RdDesc.error = vrc == VERR_INTERRUPTED ? -ERESTARTSYS : -RTErrConvertToErrno(vrc);
     1163                        SFLOGFLOW(("vbsf_reg_sendfile: Read failed: %Rrc -> %zd (RdDesc.error=%#d)\n",
     1164                                   vrc, -RTErrConvertToErrno(vrc), RdDesc.error));
     1165                        break;
     1166                    }
     1167                }
     1168
     1169                /*
     1170                 * Free memory.
     1171                 */
     1172                for (iPage = 0; iPage < cPages; iPage++)
     1173                    vbsf_put_page(apPages[iPage]);
     1174
     1175                /*
     1176                 * Set the return values.
     1177                 */
     1178                if (RdDesc.written) {
     1179                    cbRet = RdDesc.written;
     1180                    if (poffFile)
     1181                        *poffFile = offFile;
     1182                } else {
     1183                    cbRet = RdDesc.error;
     1184                }
     1185            }
     1186            VbglR0PhysHeapFree(pReq);
     1187        } else {
     1188            cbRet = -ENOMEM;
     1189        }
     1190        SFLOGFLOW(("vbsf_reg_sendfile: returns %#zx offFile=%#RX64\n", cbRet, offFile));
     1191    }
     1192    return cbRet;
     1193}
     1194#endif /* 2.5.30 <= LINUX_VERSION_CODE < 2.6.23 */
    10151195
    10161196
     
    31923372#endif
    31933373#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)
    3194     .sendfile        = generic_file_sendfile, /**< @todo this goes thru page cache. */
     3374    .sendfile        = vbsf_reg_sendfile,
    31953375#endif
    31963376    .llseek          = vbsf_reg_llseek,
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette