Browse Source

Adding memory mapped I/O to ethash_full_new

cl-refactor
Lefteris Karapetsas 10 years ago
parent
commit
0c4e19be7d
  1. 2
      CMakeLists.txt
  2. 12
      ethash.h
  3. 57
      internal.c
  4. 1
      internal.h
  5. 20
      io.h
  6. 25
      io_posix.c
  7. 21
      io_win32.c
  8. 28
      mmap.h
  9. 101
      mmap_win32.c

2
CMakeLists.txt

@ -21,7 +21,7 @@ set(FILES util.c
data_sizes.h)
if (MSVC)
list(APPEND FILES io_win32.c)
list(APPEND FILES io_win32.c mmap_win32.c)
else()
list(APPEND FILES io_posix.c)
endif()

12
ethash.h

@ -170,6 +170,8 @@ ethash_cache *ethash_light_acquire_cache(ethash_light_t light);
/**
* Allocate and initialize a new ethash_full handler
*
* @param dirname The directory in which to put the DAG file.
* @param seedhash The seed hash of the block. Used in the DAG file naming.
* @param params The parameters to initialize it with. We are interested in
* the full_size from here
* @param cache A cache object to use that was allocated with @ref ethash_cache_new().
@ -183,9 +185,11 @@ ethash_cache *ethash_light_acquire_cache(ethash_light_t light);
* @return Newly allocated ethash_full handler or NULL in case of
* ERRNOMEM or invalid parameters used for @ref ethash_compute_full_data()
*/
ethash_full_t ethash_full_new(ethash_params const* params,
ethash_cache const* cache,
ethash_callback_t callback);
ethash_full_t ethash_full_new(char const *dirname,
const ethash_h256_t *seed_hash,
ethash_params const* params,
ethash_cache const* cache,
ethash_callback_t callback);
/**
* Frees a previously allocated ethash_full handler
* @param full The light handler to free
@ -198,7 +202,7 @@ void ethash_full_delete(ethash_full_t full);
* @param full The full client handler
* @param params The parameters to use
* @param header_hash The header hash to pack into the mix
* @param nonce The nonce to pack into the mix
* @param nonce The nonce to pack into the mix
* @return true if all went well and false if there were invalid
* parameters given or if there was a callback given and
* at some point return a non-zero value

57
internal.c

@ -23,11 +23,14 @@
#include <assert.h>
#include <inttypes.h>
#include <stddef.h>
#include <errno.h>
#include "mmap.h"
#include "ethash.h"
#include "fnv.h"
#include "endian.h"
#include "internal.h"
#include "data_sizes.h"
#include "io.h"
#ifdef WITH_CRYPTOPP
@ -121,11 +124,9 @@ void ethash_calculate_dag_item(node *const ret,
uint32_t num_parent_nodes = (uint32_t) (params->cache_size / sizeof(node));
node const *cache_nodes = (node const *) cache->mem;
node const *init = &cache_nodes[node_index % num_parent_nodes];
memcpy(ret, init, sizeof(node));
ret->words[0] ^= node_index;
SHA3_512(ret->bytes, ret->bytes, sizeof(node));
#if defined(_M_X64) && ENABLE_SSE
__m128i const fnv_prime = _mm_set1_epi32(FNV_PRIME);
__m128i xmm0 = ret->xmm[0];
@ -176,7 +177,6 @@ bool ethash_compute_full_data(void *mem,
return false;
}
node *full_nodes = mem;
// now compute full nodes
for (unsigned n = 0; n != (params->full_size / sizeof(node)); ++n) {
ethash_calculate_dag_item(&(full_nodes[n]), n, params, cache);
@ -277,7 +277,6 @@ void ethash_quick_hash(ethash_h256_t *return_hash,
const uint64_t nonce,
ethash_h256_t const *mix_hash)
{
uint8_t buf[64 + 32];
memcpy(buf, header_hash, 32);
fix_endian64_same(nonce);
@ -353,21 +352,50 @@ ethash_cache *ethash_light_acquire_cache(ethash_light_t light)
return ret;
}
ethash_full_t ethash_full_new(ethash_params const* params,
ethash_cache const* cache,
ethash_callback_t callback)
ethash_full_t ethash_full_new(char const *dirname,
const ethash_h256_t *seed_hash,
ethash_params const* params,
ethash_cache const* cache,
ethash_callback_t callback)
{
struct ethash_full *ret;
int fd;
FILE *f = NULL;
bool match = false;
ret = calloc(sizeof(*ret), 1);
if (!ret) {
return NULL;
}
ret->cache = (ethash_cache*)cache;
ret->data = malloc((size_t)params->full_size);
if (!ret->data) {
ret->file_size = (size_t)params->full_size;
switch (ethash_io_prepare(dirname, *seed_hash, &f, (size_t)params->full_size)) {
case ETHASH_IO_FAIL:
goto fail_free_full;
case ETHASH_IO_MEMO_MATCH:
match = true;
case ETHASH_IO_MEMO_MISMATCH:
ret->file = f;
if ((fd = fileno(ret->file)) == -1) {
goto fail_free_full;
}
ret->data = mmap(
NULL,
(size_t)params->full_size,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
0
);
if (ret->data == MAP_FAILED) {
goto fail_close_file;
}
if (match) {
return ret;
}
break;
}
if (!ethash_compute_full_data(ret->data, params, cache)) {
goto fail_free_full_data;
}
@ -375,7 +403,10 @@ ethash_full_t ethash_full_new(ethash_params const* params,
return ret;
fail_free_full_data:
free(ret->data);
// could check that munmap(..) == 0 but even if it did not can't really do anything here
munmap(ret->data, (size_t)params->full_size);
fail_close_file:
fclose(ret->file);
fail_free_full:
free(ret);
return NULL;
@ -386,7 +417,11 @@ void ethash_full_delete(ethash_full_t full)
if (full->cache) {
ethash_cache_delete(full->cache);
}
free(full->data);
// could check that munmap(..) == 0 but even if it did not can't really do anything here
munmap(full->data, full->file_size);
if (full->file) {
fclose(full->file);
}
free(full);
}

1
internal.h

@ -37,6 +37,7 @@ struct ethash_light {
struct ethash_full {
FILE *file;
size_t file_size;
ethash_cache *cache;
node *data;
ethash_callback_t callback;

20
io.h

@ -52,12 +52,18 @@ enum ethash_io_rc {
* @param[in] seedhash The seedhash of the current block number, used in the
* naming of the file as can be seen from the spec at:
* https://github.com/ethereum/wiki/wiki/Ethash-DAG
* @param[out] f If the hash/revision combo matched then this will point
* to an opened file handler for that file. User will then
* have to close it.
* @param[out] f If there was no failure then this will point to an open
* file descriptor. User is responsible for closing it.
* In the case of memo match then the file is open on read
* mode, while on the case of mismatch a new file is created
* on write mode
* @param[in] file_size The size that the DAG file should have on disk
* @return For possible return values @see enum ethash_io_rc
*/
enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_h256_t seedhash, FILE **f);
enum ethash_io_rc ethash_io_prepare(char const *dirname,
ethash_h256_t const seedhash,
FILE **f,
size_t file_size);
/**
* An fopen wrapper for no-warnings crossplatform fopen.
@ -91,8 +97,8 @@ FILE *ethash_fopen(const char *file_name, const char *mode);
char *ethash_strncat(char *dest, size_t dest_size, const char *src, size_t count);
static inline bool ethash_io_mutable_name(uint32_t revision,
ethash_h256_t *seed_hash,
char *output)
ethash_h256_t const* seed_hash,
char* output)
{
uint64_t hash = *((uint64_t*)seed_hash);
#if LITTLE_ENDIAN == BYTE_ORDER
@ -101,7 +107,7 @@ static inline bool ethash_io_mutable_name(uint32_t revision,
return snprintf(output, DAG_MUTABLE_NAME_MAX_SIZE, "%u_%016lx", revision, hash) >= 0;
}
static inline char *ethash_io_create_filename(char const *dirname,
static inline char *ethash_io_create_filename(char const* dirname,
char const* filename,
size_t filename_length)
{

25
io_posix.c

@ -38,8 +38,9 @@ char *ethash_strncat(char *dest, size_t dest_size, const char *src, size_t count
}
enum ethash_io_rc ethash_io_prepare(char const *dirname,
ethash_h256_t seedhash,
FILE **output_file)
ethash_h256_t const seedhash,
FILE **output_file,
size_t file_size)
{
char mutable_name[DAG_MUTABLE_NAME_MAX_SIZE];
enum ethash_io_rc ret = ETHASH_IO_FAIL;
@ -57,14 +58,28 @@ enum ethash_io_rc ethash_io_prepare(char const *dirname,
}
// try to open the file
FILE *f = ethash_fopen(tmpfile, "rb");
if (!f) {
FILE *f = ethash_fopen(tmpfile, "rb+");
if (f) {
// TODO: check for file size
} else {
// file does not exist, will need to be created
f = ethash_fopen(tmpfile, "wb+");
if (!f) {
goto free_memo;
}
// make sure it's of the proper size
if (fseek(f, file_size - 1, SEEK_SET) != 0) {
fclose(f);
goto free_memo;
}
fputc('\n', f);
fflush(f);
ret = ETHASH_IO_MEMO_MISMATCH;
goto free_memo;
goto set_file;
}
ret = ETHASH_IO_MEMO_MATCH;
set_file:
*output_file = f;
free_memo:
free(tmpfile);

21
io_win32.c

@ -35,7 +35,10 @@ char *ethash_strncat(char *dest, size_t dest_size, const char *src, size_t count
return strncat_s(dest, dest_size, src, count) == 0 ? dest : NULL;
}
enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_h256_t seedhash)
enum ethash_io_rc ethash_io_prepare(char const *dirname,
ethash_h256_t const seedhash,
FILE **output_file,
size_t file_size)
{
char mutable_name[DAG_MUTABLE_NAME_MAX_SIZE];
enum ethash_io_rc ret = ETHASH_IO_FAIL;
@ -53,14 +56,26 @@ enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_h256_t seedhash)
}
// try to open the file
FILE *f = ethash_fopen(tmpfile, "rb");
FILE *f = ethash_fopen(tmpfile, "rb+");
if (!f) {
// file does not exist, will need to be created
f = ethash_fopen(tmpfile, "wb+");
if (!f) {
goto free_memo;
}
// make sure it's of the proper size
if (fseek(f, file_size - 1, SEEK_SET) != 0) {
fclose(f);
goto free_memo;
}
fputc('\n', f);
fflush(f);
ret = ETHASH_IO_MEMO_MISMATCH;
goto free_memo;
goto set_file;
}
ret = ETHASH_IO_MEMO_MATCH;
set_file:
*output_file = f;
free_memo:
free(tmpfile);

28
mmap.h

@ -0,0 +1,28 @@
/*
This file is part of ethash.
ethash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ethash. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file mmap.h
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#pragma once
#if defined(__MINGW32__) || defined(_WIN32)
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
#else
#include <sys/mman.h>
#endif

101
mmap_win32.c

@ -0,0 +1,101 @@
/* mmap() replacement for Windows
*
* Author: Mike Frysinger <vapier@gentoo.org>
* Placed into the public domain
*/
/* References:
* CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx
* CloseHandle: http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx
* MapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx
* UnmapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx
*/
#include <io.h>
#include <windows.h>
#include <sys/types.h>
#include "mmap.h"
#define PROT_READ 0x1
#define PROT_WRITE 0x2
/* This flag is only available in WinXP+ */
#ifdef FILE_MAP_EXECUTE
#define PROT_EXEC 0x4
#else
#define PROT_EXEC 0x0
#define FILE_MAP_EXECUTE 0
#endif
#define MAP_SHARED 0x01
#define MAP_PRIVATE 0x02
#define MAP_ANONYMOUS 0x20
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FAILED ((void *) -1)
#ifdef __USE_FILE_OFFSET64
# define DWORD_HI(x) (x >> 32)
# define DWORD_LO(x) ((x) & 0xffffffff)
#else
# define DWORD_HI(x) (0)
# define DWORD_LO(x) (x)
#endif
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
{
if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
return MAP_FAILED;
if (fd == -1) {
if (!(flags & MAP_ANON) || offset)
return MAP_FAILED;
} else if (flags & MAP_ANON)
return MAP_FAILED;
DWORD flProtect;
if (prot & PROT_WRITE) {
if (prot & PROT_EXEC)
flProtect = PAGE_EXECUTE_READWRITE;
else
flProtect = PAGE_READWRITE;
} else if (prot & PROT_EXEC) {
if (prot & PROT_READ)
flProtect = PAGE_EXECUTE_READ;
else if (prot & PROT_EXEC)
flProtect = PAGE_EXECUTE;
} else
flProtect = PAGE_READONLY;
off_t end = length + offset;
HANDLE mmap_fd, h;
if (fd == -1)
mmap_fd = INVALID_HANDLE_VALUE;
else
mmap_fd = (HANDLE)_get_osfhandle(fd);
h = CreateFileMapping(mmap_fd, NULL, flProtect, DWORD_HI(end), DWORD_LO(end), NULL);
if (h == NULL)
return MAP_FAILED;
DWORD dwDesiredAccess;
if (prot & PROT_WRITE)
dwDesiredAccess = FILE_MAP_WRITE;
else
dwDesiredAccess = FILE_MAP_READ;
if (prot & PROT_EXEC)
dwDesiredAccess |= FILE_MAP_EXECUTE;
if (flags & MAP_PRIVATE)
dwDesiredAccess |= FILE_MAP_COPY;
void *ret = MapViewOfFile(h, dwDesiredAccess, DWORD_HI(offset), DWORD_LO(offset), length);
if (ret == NULL) {
CloseHandle(h);
ret = MAP_FAILED;
}
return ret;
}
static void munmap(void *addr, size_t length)
{
UnmapViewOfFile(addr);
/* ruh-ro, we leaked handle from CreateFileMapping() ... */
}
#undef DWORD_HI
#undef DWORD_LO
Loading…
Cancel
Save