static int
mail_transaction_log_file_mmap(struct mail_transaction_log_file *file)
{
	if (file->buffer != NULL) {
		/* in case we just switched to mmaping */
		buffer_free(&file->buffer);
	}
	file->mmap_size = file->last_size;
	file->mmap_base = mmap(NULL, file->mmap_size, PROT_READ, MAP_SHARED,
			       file->fd, 0);
	if (file->mmap_base == MAP_FAILED) {
		file->mmap_base = NULL;
		file->mmap_size = 0;
		log_file_set_syscall_error(file, "mmap()");
		return -1;
	}

	if (file->mmap_size > mmap_get_page_size()) {
		if (madvise(file->mmap_base, file->mmap_size,
			    MADV_SEQUENTIAL) < 0)
			log_file_set_syscall_error(file, "madvise()");
	}

	buffer_create_from_const_data(&file->mmap_buffer,
				      file->mmap_base, file->mmap_size);
	file->buffer = &file->mmap_buffer;
	file->buffer_offset = 0;
	return 0;
}
Esempio n. 2
0
static void squat_uidlist_free_from_memory(struct squat_uidlist *uidlist)
{
	size_t page_size = mmap_get_page_size();

	if (uidlist->file_cache != NULL) {
		file_cache_invalidate(uidlist->file_cache,
				      page_size, (uoff_t)-1);
	} else {
		(void)madvise(uidlist->mmap_base, uidlist->mmap_size,
			      MADV_DONTNEED);
	}
}
static int
mail_transaction_log_file_map_mmap(struct mail_transaction_log_file *file,
				   uoff_t start_offset)
{
	struct stat st;
	int ret;

	/* we are going to mmap() this file, but it's not necessarily
	   mmaped currently. */
	i_assert(file->buffer_offset == 0 || file->mmap_base == NULL);
	i_assert(file->mmap_size == 0 || file->mmap_base != NULL);

	if (fstat(file->fd, &st) < 0) {
		log_file_set_syscall_error(file, "fstat()");
		return -1;
	}
	file->last_size = st.st_size;

	if ((uoff_t)st.st_size < file->sync_offset) {
		mail_transaction_log_file_set_corrupted(file,
			"file size shrank (%"PRIuUOFF_T" < %"PRIuUOFF_T")",
			(uoff_t)st.st_size, file->sync_offset);
		return 0;
	}

	if (file->buffer != NULL && file->buffer_offset <= start_offset &&
	    (uoff_t)st.st_size == file->buffer_offset + file->buffer->used) {
		/* we already have the whole file mapped */
		if ((ret = mail_transaction_log_file_sync(file)) < 0)
			return 0;
		if (ret > 0)
			return 1;
		/* size changed, re-mmap */
	}

	do {
		mail_transaction_log_file_munmap(file);

		if (file->last_size - start_offset < mmap_get_page_size()) {
			/* just reading the file is probably faster */
			return mail_transaction_log_file_read(file,
							      start_offset,
							      FALSE);
		}

		if (mail_transaction_log_file_mmap(file) < 0)
			return -1;
		if ((ret = mail_transaction_log_file_sync(file)) < 0)
			return 0;
	} while (ret == 0);

	return 1;
}
Esempio n. 4
0
struct istream *i_stream_create_mmap(int fd, size_t block_size,
				     uoff_t start_offset, uoff_t v_size,
				     bool autoclose_fd)
{
	struct mmap_istream *mstream;
        struct istream *istream;
	struct stat st;

	if (mmap_pagemask == 0)
		mmap_pagemask = mmap_get_page_size()-1;

	if (v_size == 0) {
		if (fstat(fd, &st) < 0)
			i_error("i_stream_create_mmap(): fstat() failed: %m");
		else {
			v_size = st.st_size;
			if (start_offset > v_size)
				start_offset = v_size;
			v_size -= start_offset;
		}
	}

	mstream = i_new(struct mmap_istream, 1);
	mstream->autoclose_fd = autoclose_fd;
	mstream->v_size = v_size;

	mstream->istream.iostream.close = i_stream_mmap_close;
	mstream->istream.iostream.destroy = i_stream_mmap_destroy;

	mstream->istream.max_buffer_size = block_size;
	mstream->istream.read = i_stream_mmap_read;
	mstream->istream.seek = i_stream_mmap_seek;
	mstream->istream.sync = i_stream_mmap_sync;
	mstream->istream.stat = i_stream_mmap_stat;

	mstream->istream.istream.readable_fd = TRUE;
	mstream->istream.abs_start_offset = start_offset;
	istream = i_stream_create(&mstream->istream, NULL, fd);
	istream->mmaped = TRUE;
	istream->blocking = TRUE;
	istream->seekable = TRUE;
	return istream;
}
Esempio n. 5
0
static int squat_uidlist_read_to_memory(struct squat_uidlist *uidlist)
{
	size_t i, page_size = mmap_get_page_size();

	if (uidlist->file_cache != NULL) {
		return uidlist_file_cache_read(uidlist, 0,
					       uidlist->hdr.used_file_size);
	}
	/* Tell the kernel we're going to use the uidlist data, so it loads
	   it into memory and keeps it there. */
	(void)madvise(uidlist->mmap_base, uidlist->mmap_size, MADV_WILLNEED);
	/* It also speeds up a bit for us to sequentially load everything
	   into memory, although at least Linux catches up quite fast even
	   without this code. Compiler can quite easily optimize away this
	   entire for loop, but volatile seems to help with gcc 4.2. */
	for (i = 0; i < uidlist->mmap_size; i += page_size)
		((const volatile char *)uidlist->data)[i];
	return 0;
}
static ssize_t
mail_transaction_log_file_read_header(struct mail_transaction_log_file *file)
{
	void *dest;
	size_t pos, dest_size;
	ssize_t ret;

	i_assert(file->buffer == NULL && file->mmap_base == NULL);

	memset(&file->hdr, 0, sizeof(file->hdr));
	if (file->last_size < mmap_get_page_size() && file->last_size > 0) {
		/* just read the entire transaction log to memory.
		   note that if some of the data hasn't been fully committed
		   yet (hdr.size=0), the buffer must be truncated later */
		file->buffer = buffer_create_dynamic(default_pool, 4096);
		file->buffer_offset = 0;
		dest_size = file->last_size;
		dest = buffer_append_space_unsafe(file->buffer, dest_size);
	} else {
		/* read only the header */
		dest = &file->hdr;
		dest_size = sizeof(file->hdr);
	}

	/* it's not necessarily an error to read less than wanted header size,
	   since older versions of the log format used smaller headers. */
        pos = 0;
	do {
		ret = pread(file->fd, PTR_OFFSET(dest, pos),
			    dest_size - pos, pos);
		if (ret > 0)
			pos += ret;
	} while (ret > 0 && pos < dest_size);

	if (file->buffer != NULL) {
		buffer_set_used_size(file->buffer, pos);
		memcpy(&file->hdr, file->buffer->data,
		       I_MIN(pos, sizeof(file->hdr)));
	}

	return ret < 0 ? -1 : (ssize_t)pos;
}
Esempio n. 7
0
static ssize_t i_stream_mmap_read(struct istream_private *stream)
{
	struct mmap_istream *mstream = (struct mmap_istream *) stream;
	size_t aligned_skip;
	uoff_t top;

	if (stream->pos < stream->buffer_size) {
		/* more bytes available without needing to mmap() */
		stream->pos = stream->buffer_size;
		return stream->pos - stream->skip;
	}

	if (stream->istream.v_offset >= mstream->v_size) {
		stream->istream.eof = TRUE;
		return -1;
	}

	aligned_skip = stream->skip & ~mmap_pagemask;
	if (aligned_skip == 0 && mstream->mmap_base != NULL) {
		/* didn't skip enough bytes */
		return -2;
	}

	stream->skip -= aligned_skip;
	mstream->mmap_offset += aligned_skip;

	if (mstream->mmap_base != NULL) {
		if (munmap(mstream->mmap_base, stream->buffer_size) < 0) {
			i_error("mmap_istream.munmap(%s) failed: %m",
				i_stream_get_name(&stream->istream));
		}
	}

	top = mstream->v_size - mstream->mmap_offset;
	stream->buffer_size = I_MIN(top, mstream_get_mmap_block_size(stream));

	i_assert((uoff_t)mstream->mmap_offset + stream->buffer_size <=
		 mstream->v_size);

	if (stream->buffer_size == 0) {
		/* don't bother even trying mmap */
		mstream->mmap_base = NULL;
		stream->buffer = NULL;
	} else {
		mstream->mmap_base =
			mmap(NULL, stream->buffer_size, PROT_READ, MAP_PRIVATE,
			     stream->fd, mstream->mmap_offset);
		if (mstream->mmap_base == MAP_FAILED) {
			i_assert(errno != 0);
			stream->istream.stream_errno = errno;
			mstream->mmap_base = NULL;
			stream->buffer = NULL;
			stream->buffer_size = 0;
			stream->skip = stream->pos = 0;
			i_error("mmap_istream.mmap(%s) failed: %m",
				i_stream_get_name(&stream->istream));
			return -1;
		}
		stream->buffer = mstream->mmap_base;
	}

	if (stream->buffer_size > mmap_get_page_size()) {
		if (madvise(mstream->mmap_base, stream->buffer_size,
			    MADV_SEQUENTIAL) < 0) {
			i_error("mmap_istream.madvise(%s): %m",
				i_stream_get_name(&stream->istream));
		}
	}

	stream->pos = stream->buffer_size;
	i_assert(stream->pos - stream->skip > 0);
	return stream->pos - stream->skip;
}
Esempio n. 8
0
static size_t mstream_get_mmap_block_size(struct istream_private *stream)
{
	return (stream->max_buffer_size + mmap_get_page_size() - 1) & ~
		(mmap_get_page_size() - 1);
}