diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/haiku_file_cache.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/haiku_file_cache.cpp index 95fcab7997..682c7eacf6 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/haiku_file_cache.cpp +++ b/src/add-ons/kernel/file_systems/userlandfs/server/haiku_file_cache.cpp @@ -81,6 +81,7 @@ using std::nothrow; // maximum number of iovecs per request #define MAX_IO_VECS 64 // 256 kB #define MAX_FILE_IO_VECS 32 +#define MAX_TEMP_IO_VECS 8 #define CACHED_FILE_EXTENTS 2 // must be smaller than MAX_FILE_IO_VECS @@ -1058,7 +1059,7 @@ add_to_iovec(iovec *vecs, int32 &index, int32 max, addr_t address, size_t size) static file_extent * find_file_extent(file_cache_ref *ref, off_t offset, uint32 *_index) { - // ToDo: do binary search + // TODO: do binary search for (uint32 index = 0; index < ref->map.count; index++) { file_extent *extent = ref->map[index]; @@ -1171,37 +1172,45 @@ get_file_map(file_cache_ref *ref, off_t offset, size_t size, } +/*! + Does the dirty work of translating the request into actual disk offsets + and reads to or writes from the supplied iovecs as specified by \a doWrite. +*/ static status_t pages_io(file_cache_ref *ref, off_t offset, const iovec *vecs, size_t count, size_t *_numBytes, bool doWrite) { - TRACE(("pages_io: ref = %p, offset = %Ld, size = %lu, %s\n", ref, offset, - *_numBytes, doWrite ? "write" : "read")); + TRACE(("pages_io: ref = %p, offset = %Ld, size = %lu, vecCount = %lu, %s\n", ref, offset, + *_numBytes, count, doWrite ? "write" : "read")); // translate the iovecs into direct device accesses file_io_vec fileVecs[MAX_FILE_IO_VECS]; size_t fileVecCount = MAX_FILE_IO_VECS; size_t numBytes = *_numBytes; - status_t status = get_file_map(ref, offset, numBytes, fileVecs, &fileVecCount); + status_t status = get_file_map(ref, offset, numBytes, fileVecs, + &fileVecCount); if (status < B_OK) { TRACE(("get_file_map(offset = %Ld, numBytes = %lu) failed\n", offset, numBytes)); return status; } - // ToDo: handle array overflow gracefully! + // TODO: handle array overflow gracefully! #ifdef TRACE_FILE_CACHE dprintf("got %lu file vecs for %Ld:%lu:\n", fileVecCount, offset, numBytes); - for (size_t i = 0; i < fileVecCount; i++) - dprintf("[%lu] offset = %Ld, size = %Ld\n", i, fileVecs[i].offset, fileVecs[i].length); + for (size_t i = 0; i < fileVecCount; i++) { + dprintf(" [%lu] offset = %Ld, size = %Ld\n", + i, fileVecs[i].offset, fileVecs[i].length); + } #endif if (fileVecCount == 0) { // There are no file vecs at this offset, so we're obviously trying // to access the file outside of its bounds - TRACE(("pages_io: access outside of vnode %p at offset %Ld\n", ref->vnode, offset)); + TRACE(("pages_io: access outside of vnode %p at offset %Ld\n", + ref->vnode, offset)); return B_BAD_VALUE; } @@ -1221,9 +1230,10 @@ pages_io(file_cache_ref *ref, off_t offset, const iovec *vecs, size_t count, if (status < B_OK) return status; - // ToDo: this is a work-around for buggy device drivers! + // TODO: this is a work-around for buggy device drivers! // When our own drivers honour the length, we can: - // a) also use this direct I/O for writes (otherwise, it would overwrite precious data) + // a) also use this direct I/O for writes (otherwise, it would + // overwrite precious data) // b) panic if the term below is true (at least for writes) if (size > fileVecs[0].length) { //dprintf("warning: device driver %p doesn't respect total length in read_pages() call!\n", ref->device); @@ -1241,7 +1251,7 @@ pages_io(file_cache_ref *ref, off_t offset, const iovec *vecs, size_t count, *_numBytes = size; return B_OK; } - + fileVecIndex = 1; } else { fileVecIndex = 0; @@ -1255,73 +1265,96 @@ pages_io(file_cache_ref *ref, off_t offset, const iovec *vecs, size_t count, // first, find out where we have to continue in our iovecs uint32 i = 0; for (; i < count; i++) { - if (size <= vecs[i].iov_len) + if (size < vecs[i].iov_len) break; size -= vecs[i].iov_len; } size_t vecOffset = size; + size_t bytesLeft = numBytes - size; for (; fileVecIndex < fileVecCount; fileVecIndex++) { file_io_vec &fileVec = fileVecs[fileVecIndex]; - iovec tempVecs[8]; - uint32 tempCount = 1; + off_t fileOffset = fileVec.offset; + off_t fileLeft = fileVec.length; - tempVecs[0].iov_base = (void *)((addr_t)vecs[i].iov_base + vecOffset); + fileLeft = min_c(fileVec.length, bytesLeft); - size = min_c(vecs[i].iov_len - vecOffset, fileVec.length); - tempVecs[0].iov_len = size; + TRACE(("FILE VEC [%lu] length %Ld\n", fileVecIndex, fileLeft)); - TRACE(("fill vec %ld, offset = %lu, size = %lu\n", i, vecOffset, size)); + // process the complete fileVec + while (fileLeft > 0) { + iovec tempVecs[MAX_TEMP_IO_VECS]; + uint32 tempCount = 0; - if (size >= fileVec.length) - vecOffset += size; - else - vecOffset = 0; + // size tracks how much of what is left of the current fileVec + // (fileLeft) has been assigned to tempVecs + size = 0; - while (size < fileVec.length && ++i < count) { - tempVecs[tempCount].iov_base = vecs[i].iov_base; - tempCount++; + // assign what is left of the current fileVec to the tempVecs + while (size < fileLeft && i < count + && tempCount < MAX_TEMP_IO_VECS) { + // try to satisfy one iovec per iteration (or as much as + // possible) + TRACE(("fill vec %ld, offset = %lu, size = %lu\n", + i, vecOffset, size)); - // is this iovec larger than the file_io_vec? - if (vecs[i].iov_len + size > fileVec.length) { - size += tempVecs[tempCount].iov_len = vecOffset = fileVec.length - size; - break; + // bytes left of the current iovec + size_t vecLeft = vecs[i].iov_len - vecOffset; + if (vecLeft == 0) { + i++; + vecOffset = 0; + continue; + } + + // actually available bytes + size_t tempVecSize = min_c(vecLeft, fileLeft - size); + + tempVecs[tempCount].iov_base + = (void *)((addr_t)vecs[i].iov_base + vecOffset); + tempVecs[tempCount].iov_len = tempVecSize; + tempCount++; + + size += tempVecSize; + vecOffset += tempVecSize; } - size += tempVecs[tempCount].iov_len = vecs[i].iov_len; - } + size_t bytes = size; + if (doWrite) { + status = vfs_write_pages(ref->deviceFD, fileOffset, + tempVecs, tempCount, &bytes, false); + } else { + status = vfs_read_pages(ref->deviceFD, fileOffset, + tempVecs, tempCount, &bytes, false); + } + if (status < B_OK) + return status; - size_t bytes = size; - if (doWrite) { - status = vfs_write_pages(ref->deviceFD, fileVec.offset, tempVecs, - tempCount, &bytes, false); - } else { - status = vfs_read_pages(ref->deviceFD, fileVec.offset, tempVecs, - tempCount, &bytes, false); - } - if (status < B_OK) - return status; + totalSize += bytes; + bytesLeft -= size; + fileOffset += size; + fileLeft -= size; + //dprintf("-> file left = %Lu\n", fileLeft); - totalSize += size; - - if (size != bytes) { - // there are no more bytes, let's bail out - *_numBytes = totalSize; - return B_OK; + if (size != bytes || i >= count) { + // there are no more bytes or iovecs, let's bail out + *_numBytes = totalSize; + return B_OK; + } } } + *_numBytes = totalSize; return B_OK; } -/** This function is called by read_into_cache() (and from there only) - it - * can only handle a certain amount of bytes, and read_into_cache() makes - * sure that it matches that criterion. - */ - +/*! + This function is called by read_into_cache() (and from there only) - it + can only handle a certain amount of bytes, and read_into_cache() makes + sure that it matches that criterion. +*/ static inline status_t read_chunk_into_cache(file_cache_ref *ref, off_t offset, size_t size, int32 pageOffset, addr_t buffer, size_t bufferSize) @@ -1400,14 +1433,14 @@ read_chunk_into_cache(file_cache_ref *ref, off_t offset, size_t size, } -/** This function reads \a size bytes directly from the file into the cache. - * If \a bufferSize does not equal zero, \a bufferSize bytes from the data - * read in are also copied to the provided \a buffer. - * This function always allocates all pages; it is the responsibility of the - * calling function to only ask for yet uncached ranges. - * The cache_ref lock must be hold when calling this function. - */ - +/*! + This function reads \a size bytes directly from the file into the cache. + If \a bufferSize does not equal zero, \a bufferSize bytes from the data + read in are also copied to the provided \a buffer. + This function always allocates all pages; it is the responsibility of the + calling function to only ask for yet uncached ranges. + The cache_ref lock must be hold when calling this function. +*/ static status_t read_into_cache(file_cache_ref *ref, off_t offset, size_t size, addr_t buffer, size_t bufferSize) { @@ -1750,8 +1783,7 @@ cache_io(void *_cacheRef, off_t offset, addr_t buffer, size_t *_size, bool doWri } -// #pragma mark - -// public FS API +// #pragma mark - public FS API void *