* Added a new flag for write stat: B_STAT_SIZE_INSECURE that allows a

file system to not fill newly created space with zeros. 
  BFile::SetSize() now uses this, while [f]truncate() does not. This
  is only a temporary work-around until BFS supports sparse files.
* Apps that want to reserve space to fill up later should use 
  BFile::SetSize() for now, as this will be a lot faster than 
  [f]truncate().
* cache_io() and the functions below now use a special mode when you 
  pass in a NULL buffer: for read access, the cache is only populated
  (useful for prefetching), for write access, the file is filled with
  zeros.
* Implemented BFS's Inode::FillGapWithZeros() using this method now.
* Removed extraneous white space.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24555 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2008-03-24 13:39:02 +00:00
parent c8ee6ba8fb
commit a121b8c83c
7 changed files with 123 additions and 135 deletions
+4 -2
View File
@@ -27,8 +27,10 @@ typedef void *fs_cookie;
typedef void *fs_vnode; typedef void *fs_vnode;
/* additional flags passed to write_stat() (see NodeMonitor.h for the others) */ /* additional flags passed to write_stat() (see NodeMonitor.h for the others) */
// NOTE: Changing the constants here or in NodeMonitor.h will break // NOTE: Changing the constants here or in NodeMonitor.h will break
// src/kits/storage/LibBeAdapter.cpp:_kern_write_stat(). // src/kits/storage/LibBeAdapter.cpp:_kern_write_stat().
#define B_STAT_SIZE_INSECURE 0x2000
// TODO: this should be faded out once BFS supports sparse files
/* passed to write_fs_info() */ /* passed to write_fs_info() */
#define FS_WRITE_FSINFO_NAME 0x0001 #define FS_WRITE_FSINFO_NAME 0x0001
@@ -874,6 +874,9 @@
#define fs_cookie fssh_fs_cookie #define fs_cookie fssh_fs_cookie
#define fs_vnode fssh_fs_vnode #define fs_vnode fssh_fs_vnode
/* additional flags passed to write_stat() */
#define B_STAT_SIZE_INSECURE FSSH_B_STAT_SIZE_INSECURE
/* passed to write_fs_info() */ /* passed to write_fs_info() */
#define FS_WRITE_FSINFO_NAME FSSH_FS_WRITE_FSINFO_NAME #define FS_WRITE_FSINFO_NAME FSSH_FS_WRITE_FSINFO_NAME
@@ -29,6 +29,7 @@ typedef void *fssh_fs_cookie;
typedef void *fssh_fs_vnode; typedef void *fssh_fs_vnode;
/* additional flags passed to write_stat() */ /* additional flags passed to write_stat() */
#define FSSH_B_STAT_SIZE_INSECURE 0x2000
/* passed to write_fs_info() */ /* passed to write_fs_info() */
#define FSSH_FS_WRITE_FSINFO_NAME 0x0001 #define FSSH_FS_WRITE_FSINFO_NAME 0x0001
+11 -78
View File
@@ -407,7 +407,7 @@ Inode::InitCheck(bool checkNode)
} }
if (IsContainer()) { if (IsContainer()) {
// inodes that have a // inodes that have a B+tree
if (fTree == NULL) if (fTree == NULL)
RETURN_ERROR(B_NO_MEMORY); RETURN_ERROR(B_NO_MEMORY);
@@ -1426,8 +1426,6 @@ Inode::WriteAt(Transaction &transaction, off_t pos, const uint8 *buffer,
locker.Lock(); locker.Lock();
if (pos + length > Size()) { if (pos + length > Size()) {
off_t oldSize = Size();
// let's grow the data stream to the size needed // let's grow the data stream to the size needed
status_t status = SetFileSize(transaction, pos + length); status_t status = SetFileSize(transaction, pos + length);
if (status < B_OK) { if (status < B_OK) {
@@ -1438,11 +1436,6 @@ Inode::WriteAt(Transaction &transaction, off_t pos, const uint8 *buffer,
// index here as part of the current transaction - this might just // index here as part of the current transaction - this might just
// be a bit too expensive, but worth a try. // be a bit too expensive, but worth a try.
// If the position of the write was beyond the file size, we
// have to fill the gap between that position and the old file
// size with zeros.
FillGapWithZeros(oldSize, pos);
// we need to write back the inode here because it has to // we need to write back the inode here because it has to
// go into this transaction (we cannot wait until the file // go into this transaction (we cannot wait until the file
// is closed) // is closed)
@@ -1472,81 +1465,21 @@ Inode::WriteAt(Transaction &transaction, off_t pos, const uint8 *buffer,
status_t status_t
Inode::FillGapWithZeros(off_t pos, off_t newSize) Inode::FillGapWithZeros(off_t pos, off_t newSize)
{ {
// ToDo: we currently do anything here, same as original BFS! while (pos < newSize) {
//if (pos >= newSize) size_t size;
return B_OK; if (newSize > pos + 1024 * 1024 * 1024)
#if 0 size = 1024 * 1024 * 1024;
block_run run; else
off_t offset; size = newSize - pos;
if (FindBlockRun(pos, run, offset) < B_OK)
RETURN_ERROR(B_BAD_VALUE);
off_t length = newSize - pos; status_t status = file_cache_write(FileCache(), NULL, pos, NULL, &size);
uint32 bytesWritten = 0; if (status < B_OK)
uint32 blockSize = fVolume->BlockSize(); return status;
uint32 blockShift = fVolume->BlockShift();
uint8 *block;
// the first block_run we write could not be aligned to the block_size boundary pos += size;
// (write partial block at the beginning)
// pos % block_size == (pos - offset) % block_size, offset % block_size == 0
if (pos % blockSize != 0) {
run.start += (pos - offset) / blockSize;
run.length -= (pos - offset) / blockSize;
CachedBlock cached(fVolume,run);
if ((block = cached.Block()) == NULL)
RETURN_ERROR(B_BAD_VALUE);
bytesWritten = blockSize - (pos % blockSize);
if (length < bytesWritten)
bytesWritten = length;
memset(block + (pos % blockSize), 0, bytesWritten);
if (fVolume->WriteBlocks(cached.BlockNumber(), block, 1) < B_OK)
RETURN_ERROR(B_IO_ERROR);
pos += bytesWritten;
length -= bytesWritten;
if (length == 0)
return B_OK;
if (FindBlockRun(pos, run, offset) < B_OK)
RETURN_ERROR(B_BAD_VALUE);
} }
while (length > 0) {
// offset is the offset to the current pos in the block_run
run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift));
run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift));
CachedBlock cached(fVolume);
off_t blockNumber = fVolume->ToBlock(run);
for (int32 i = 0; i < run.Length(); i++) {
if ((block = cached.SetTo(blockNumber + i, true)) == NULL)
RETURN_ERROR(B_IO_ERROR);
if (fVolume->WriteBlocks(cached.BlockNumber(), block, 1) < B_OK)
RETURN_ERROR(B_IO_ERROR);
}
int32 bytes = run.Length() << blockShift;
length -= bytes;
bytesWritten += bytes;
// since we don't respect a last partial block, length can be lower
if (length <= 0)
break;
pos += bytes;
if (FindBlockRun(pos, run, offset) < B_OK)
RETURN_ERROR(B_BAD_VALUE);
}
return B_OK; return B_OK;
#endif
} }
@@ -734,6 +734,7 @@ bfs_write_stat(void *_ns, void *_node, const struct stat *stat, uint32 mask)
return status; return status;
// fill the new blocks (if any) with zeros // fill the new blocks (if any) with zeros
if ((mask & B_STAT_SIZE_INSECURE) == 0)
inode->FillGapWithZeros(inode->OldSize(), inode->Size()); inode->FillGapWithZeros(inode->OldSize(), inode->Size());
if (!inode->IsDeleted()) { if (!inode->IsDeleted()) {
+2 -1
View File
@@ -14,6 +14,7 @@
#include <Directory.h> #include <Directory.h>
#include <Entry.h> #include <Entry.h>
#include <File.h> #include <File.h>
#include <fs_interface.h>
#include <NodeMonitor.h> #include <NodeMonitor.h>
#include <syscalls.h> #include <syscalls.h>
@@ -453,7 +454,7 @@ BFile::SetSize(off_t size)
return B_BAD_VALUE; return B_BAD_VALUE;
struct stat statData; struct stat statData;
statData.st_size = size; statData.st_size = size;
return set_stat(statData, B_STAT_SIZE); return set_stat(statData, B_STAT_SIZE | B_STAT_SIZE_INSECURE);
} }
+66 -19
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@@ -51,7 +51,7 @@ struct file_cache_ref {
}; };
typedef status_t (*cache_func)(file_cache_ref *ref, void *cookie, off_t offset, typedef status_t (*cache_func)(file_cache_ref *ref, void *cookie, off_t offset,
int32 pageOffset, addr_t buffer, size_t bufferSize, int32 pageOffset, addr_t buffer, size_t bufferSize, bool useBuffer,
size_t lastReservedPages, size_t reservePages); size_t lastReservedPages, size_t reservePages);
@@ -169,7 +169,7 @@ reserve_pages(file_cache_ref *ref, size_t reservePages, bool isWrite)
*/ */
static status_t static status_t
read_into_cache(file_cache_ref *ref, void *cookie, off_t offset, read_into_cache(file_cache_ref *ref, void *cookie, off_t offset,
int32 pageOffset, addr_t buffer, size_t bufferSize, int32 pageOffset, addr_t buffer, size_t bufferSize, bool useBuffer,
size_t lastReservedPages, size_t reservePages) size_t lastReservedPages, size_t reservePages)
{ {
TRACE(("read_into_cache(offset = %Ld, pageOffset = %ld, buffer = %#lx, " TRACE(("read_into_cache(offset = %Ld, pageOffset = %ld, buffer = %#lx, "
@@ -240,14 +240,14 @@ read_into_cache(file_cache_ref *ref, void *cookie, off_t offset,
return status; return status;
} }
// copy the pages and unmap them again // copy the pages if needed and unmap them again
for (int32 i = 0; i < vecCount; i++) { for (int32 i = 0; i < vecCount; i++) {
addr_t base = (addr_t)vecs[i].iov_base; addr_t base = (addr_t)vecs[i].iov_base;
size_t size = vecs[i].iov_len; size_t size = vecs[i].iov_len;
// copy to user buffer if necessary // copy to user buffer if necessary
if (bufferSize != 0) { if (useBuffer && bufferSize != 0) {
size_t bytes = min_c(bufferSize, size - pageOffset); size_t bytes = min_c(bufferSize, size - pageOffset);
user_memcpy((void *)buffer, (void *)(base + pageOffset), bytes); user_memcpy((void *)buffer, (void *)(base + pageOffset), bytes);
@@ -278,12 +278,15 @@ read_into_cache(file_cache_ref *ref, void *cookie, off_t offset,
static status_t static status_t
read_from_file(file_cache_ref *ref, void *cookie, off_t offset, read_from_file(file_cache_ref *ref, void *cookie, off_t offset,
int32 pageOffset, addr_t buffer, size_t bufferSize, int32 pageOffset, addr_t buffer, size_t bufferSize, bool useBuffer,
size_t lastReservedPages, size_t reservePages) size_t lastReservedPages, size_t reservePages)
{ {
TRACE(("read_from_file(offset = %Ld, pageOffset = %ld, buffer = %#lx, " TRACE(("read_from_file(offset = %Ld, pageOffset = %ld, buffer = %#lx, "
"bufferSize = %lu\n", offset, pageOffset, buffer, bufferSize)); "bufferSize = %lu\n", offset, pageOffset, buffer, bufferSize));
if (!useBuffer)
return B_OK;
iovec vec; iovec vec;
vec.iov_base = (void *)buffer; vec.iov_base = (void *)buffer;
vec.iov_len = bufferSize; vec.iov_len = bufferSize;
@@ -310,7 +313,7 @@ read_from_file(file_cache_ref *ref, void *cookie, off_t offset,
*/ */
static status_t static status_t
write_to_cache(file_cache_ref *ref, void *cookie, off_t offset, write_to_cache(file_cache_ref *ref, void *cookie, off_t offset,
int32 pageOffset, addr_t buffer, size_t bufferSize, int32 pageOffset, addr_t buffer, size_t bufferSize, bool useBuffer,
size_t lastReservedPages, size_t reservePages) size_t lastReservedPages, size_t reservePages)
{ {
// TODO: We're using way too much stack! Rather allocate a sufficiently // TODO: We're using way too much stack! Rather allocate a sufficiently
@@ -400,8 +403,13 @@ write_to_cache(file_cache_ref *ref, void *cookie, off_t offset,
addr_t base = (addr_t)vecs[i].iov_base; addr_t base = (addr_t)vecs[i].iov_base;
size_t bytes = min_c(bufferSize, size_t(vecs[i].iov_len - pageOffset)); size_t bytes = min_c(bufferSize, size_t(vecs[i].iov_len - pageOffset));
if (useBuffer) {
// copy data from user buffer // copy data from user buffer
user_memcpy((void *)(base + pageOffset), (void *)buffer, bytes); user_memcpy((void *)(base + pageOffset), (void *)buffer, bytes);
} else {
// clear buffer instead
memset((void *)(base + pageOffset), 0, bytes);
}
bufferSize -= bytes; bufferSize -= bytes;
if (bufferSize == 0) if (bufferSize == 0)
@@ -454,9 +462,21 @@ write_to_cache(file_cache_ref *ref, void *cookie, off_t offset,
static status_t static status_t
write_to_file(file_cache_ref *ref, void *cookie, off_t offset, int32 pageOffset, write_to_file(file_cache_ref *ref, void *cookie, off_t offset, int32 pageOffset,
addr_t buffer, size_t bufferSize, size_t lastReservedPages, addr_t buffer, size_t bufferSize, bool useBuffer, size_t lastReservedPages,
size_t reservePages) size_t reservePages)
{ {
size_t chunkSize;
if (!useBuffer) {
// we need to allocate a zero buffer
// TODO: use smaller buffers if this fails
chunkSize = min_c(bufferSize, B_PAGE_SIZE);
buffer = (addr_t)malloc(chunkSize);
if (buffer == 0)
return B_NO_MEMORY;
memset((void *)buffer, 0, chunkSize);
}
iovec vec; iovec vec;
vec.iov_base = (void *)buffer; vec.iov_base = (void *)buffer;
vec.iov_len = bufferSize; vec.iov_len = bufferSize;
@@ -465,8 +485,26 @@ write_to_file(file_cache_ref *ref, void *cookie, off_t offset, int32 pageOffset,
mutex_unlock(&ref->cache->lock); mutex_unlock(&ref->cache->lock);
vm_page_unreserve_pages(lastReservedPages); vm_page_unreserve_pages(lastReservedPages);
status_t status = vfs_write_pages(ref->vnode, cookie, offset + pageOffset, status_t status;
if (!useBuffer) {
while (bufferSize > 0) {
if (bufferSize < chunkSize)
chunkSize = bufferSize;
status = vfs_write_pages(ref->vnode, cookie, offset + pageOffset,
&vec, 1, &chunkSize, false);
if (status < B_OK)
break;
bufferSize -= chunkSize;
pageOffset += chunkSize;
}
} else {
status = vfs_write_pages(ref->vnode, cookie, offset + pageOffset,
&vec, 1, &bufferSize, false); &vec, 1, &bufferSize, false);
}
if (status == B_OK) if (status == B_OK)
reserve_pages(ref, reservePages, true); reserve_pages(ref, reservePages, true);
@@ -478,9 +516,10 @@ write_to_file(file_cache_ref *ref, void *cookie, off_t offset, int32 pageOffset,
static inline status_t static inline status_t
satisfy_cache_io(file_cache_ref *ref, void *cookie, cache_func function, satisfy_cache_io(file_cache_ref *ref, void *cookie, cache_func function,
off_t offset, addr_t buffer, int32 &pageOffset, size_t bytesLeft, off_t offset, addr_t buffer, bool useBuffer, int32 &pageOffset,
size_t &reservePages, off_t &lastOffset, addr_t &lastBuffer, size_t bytesLeft, size_t &reservePages, off_t &lastOffset,
int32 &lastPageOffset, size_t &lastLeft, size_t &lastReservedPages) addr_t &lastBuffer, int32 &lastPageOffset, size_t &lastLeft,
size_t &lastReservedPages)
{ {
if (lastBuffer == buffer) if (lastBuffer == buffer)
return B_OK; return B_OK;
@@ -490,7 +529,7 @@ satisfy_cache_io(file_cache_ref *ref, void *cookie, cache_func function,
+ lastPageOffset + B_PAGE_SIZE - 1) >> PAGE_SHIFT); + lastPageOffset + B_PAGE_SIZE - 1) >> PAGE_SHIFT);
status_t status = function(ref, cookie, lastOffset, lastPageOffset, status_t status = function(ref, cookie, lastOffset, lastPageOffset,
lastBuffer, requestSize, lastReservedPages, reservePages); lastBuffer, requestSize, useBuffer, lastReservedPages, reservePages);
if (status == B_OK) { if (status == B_OK) {
lastReservedPages = reservePages; lastReservedPages = reservePages;
lastBuffer = buffer; lastBuffer = buffer;
@@ -513,6 +552,7 @@ cache_io(void *_cacheRef, void *cookie, off_t offset, addr_t buffer,
file_cache_ref *ref = (file_cache_ref *)_cacheRef; file_cache_ref *ref = (file_cache_ref *)_cacheRef;
vm_cache *cache = ref->cache; vm_cache *cache = ref->cache;
off_t fileSize = cache->virtual_size; off_t fileSize = cache->virtual_size;
bool useBuffer = buffer != 0;
TRACE(("cache_io(ref = %p, offset = %Ld, buffer = %p, size = %lu, %s)\n", TRACE(("cache_io(ref = %p, offset = %Ld, buffer = %p, size = %lu, %s)\n",
ref, offset, (void *)buffer, *_size, doWrite ? "write" : "read")); ref, offset, (void *)buffer, *_size, doWrite ? "write" : "read"));
@@ -575,8 +615,9 @@ cache_io(void *_cacheRef, void *cookie, off_t offset, addr_t buffer,
// we didn't get yet (to make sure no one else interferes in the // we didn't get yet (to make sure no one else interferes in the
// mean time). // mean time).
status_t status = satisfy_cache_io(ref, cookie, function, offset, status_t status = satisfy_cache_io(ref, cookie, function, offset,
buffer, pageOffset, bytesLeft, reservePages, lastOffset, buffer, useBuffer, pageOffset, bytesLeft, reservePages,
lastBuffer, lastPageOffset, lastLeft, lastReservedPages); lastOffset, lastBuffer, lastPageOffset, lastLeft,
lastReservedPages);
if (status != B_OK) if (status != B_OK)
return status; return status;
@@ -606,13 +647,18 @@ cache_io(void *_cacheRef, void *cookie, off_t offset, addr_t buffer,
// and copy the contents of the page already in memory // and copy the contents of the page already in memory
if (doWrite) { if (doWrite) {
if (useBuffer) {
user_memcpy((void *)(virtualAddress + pageOffset), user_memcpy((void *)(virtualAddress + pageOffset),
(void *)buffer, bytesInPage); (void *)buffer, bytesInPage);
} else {
user_memset((void *)(virtualAddress + pageOffset),
0, bytesInPage);
}
// make sure the page is in the modified list // make sure the page is in the modified list
if (page->state != PAGE_STATE_MODIFIED) if (page->state != PAGE_STATE_MODIFIED)
vm_page_set_state(page, PAGE_STATE_MODIFIED); vm_page_set_state(page, PAGE_STATE_MODIFIED);
} else { } else if (useBuffer) {
user_memcpy((void *)buffer, user_memcpy((void *)buffer,
(void *)(virtualAddress + pageOffset), bytesInPage); (void *)(virtualAddress + pageOffset), bytesInPage);
} }
@@ -643,8 +689,9 @@ cache_io(void *_cacheRef, void *cookie, off_t offset, addr_t buffer,
if (buffer - lastBuffer + lastPageOffset >= kMaxChunkSize) { if (buffer - lastBuffer + lastPageOffset >= kMaxChunkSize) {
status_t status = satisfy_cache_io(ref, cookie, function, offset, status_t status = satisfy_cache_io(ref, cookie, function, offset,
buffer, pageOffset, bytesLeft, reservePages, lastOffset, buffer, useBuffer, pageOffset, bytesLeft, reservePages,
lastBuffer, lastPageOffset, lastLeft, lastReservedPages); lastOffset, lastBuffer, lastPageOffset, lastLeft,
lastReservedPages);
if (status != B_OK) if (status != B_OK)
return status; return status;
} }
@@ -653,7 +700,7 @@ cache_io(void *_cacheRef, void *cookie, off_t offset, addr_t buffer,
// fill the last remaining bytes of the request (either write or read) // fill the last remaining bytes of the request (either write or read)
return function(ref, cookie, lastOffset, lastPageOffset, lastBuffer, return function(ref, cookie, lastOffset, lastPageOffset, lastBuffer,
lastLeft, lastReservedPages, 0); lastLeft, useBuffer, lastReservedPages, 0);
} }