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. 6
      ethash.h
  3. 53
      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) data_sizes.h)
if (MSVC) if (MSVC)
list(APPEND FILES io_win32.c) list(APPEND FILES io_win32.c mmap_win32.c)
else() else()
list(APPEND FILES io_posix.c) list(APPEND FILES io_posix.c)
endif() endif()

6
ethash.h

@ -170,6 +170,8 @@ ethash_cache *ethash_light_acquire_cache(ethash_light_t light);
/** /**
* Allocate and initialize a new ethash_full handler * 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 * @param params The parameters to initialize it with. We are interested in
* the full_size from here * the full_size from here
* @param cache A cache object to use that was allocated with @ref ethash_cache_new(). * @param cache A cache object to use that was allocated with @ref ethash_cache_new().
@ -183,7 +185,9 @@ ethash_cache *ethash_light_acquire_cache(ethash_light_t light);
* @return Newly allocated ethash_full handler or NULL in case of * @return Newly allocated ethash_full handler or NULL in case of
* ERRNOMEM or invalid parameters used for @ref ethash_compute_full_data() * ERRNOMEM or invalid parameters used for @ref ethash_compute_full_data()
*/ */
ethash_full_t ethash_full_new(ethash_params const* params, ethash_full_t ethash_full_new(char const *dirname,
const ethash_h256_t *seed_hash,
ethash_params const* params,
ethash_cache const* cache, ethash_cache const* cache,
ethash_callback_t callback); ethash_callback_t callback);
/** /**

53
internal.c

@ -23,11 +23,14 @@
#include <assert.h> #include <assert.h>
#include <inttypes.h> #include <inttypes.h>
#include <stddef.h> #include <stddef.h>
#include <errno.h>
#include "mmap.h"
#include "ethash.h" #include "ethash.h"
#include "fnv.h" #include "fnv.h"
#include "endian.h" #include "endian.h"
#include "internal.h" #include "internal.h"
#include "data_sizes.h" #include "data_sizes.h"
#include "io.h"
#ifdef WITH_CRYPTOPP #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)); uint32_t num_parent_nodes = (uint32_t) (params->cache_size / sizeof(node));
node const *cache_nodes = (node const *) cache->mem; node const *cache_nodes = (node const *) cache->mem;
node const *init = &cache_nodes[node_index % num_parent_nodes]; node const *init = &cache_nodes[node_index % num_parent_nodes];
memcpy(ret, init, sizeof(node)); memcpy(ret, init, sizeof(node));
ret->words[0] ^= node_index; ret->words[0] ^= node_index;
SHA3_512(ret->bytes, ret->bytes, sizeof(node)); SHA3_512(ret->bytes, ret->bytes, sizeof(node));
#if defined(_M_X64) && ENABLE_SSE #if defined(_M_X64) && ENABLE_SSE
__m128i const fnv_prime = _mm_set1_epi32(FNV_PRIME); __m128i const fnv_prime = _mm_set1_epi32(FNV_PRIME);
__m128i xmm0 = ret->xmm[0]; __m128i xmm0 = ret->xmm[0];
@ -176,7 +177,6 @@ bool ethash_compute_full_data(void *mem,
return false; return false;
} }
node *full_nodes = mem; node *full_nodes = mem;
// now compute full nodes // now compute full nodes
for (unsigned n = 0; n != (params->full_size / sizeof(node)); ++n) { for (unsigned n = 0; n != (params->full_size / sizeof(node)); ++n) {
ethash_calculate_dag_item(&(full_nodes[n]), n, params, cache); 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, const uint64_t nonce,
ethash_h256_t const *mix_hash) ethash_h256_t const *mix_hash)
{ {
uint8_t buf[64 + 32]; uint8_t buf[64 + 32];
memcpy(buf, header_hash, 32); memcpy(buf, header_hash, 32);
fix_endian64_same(nonce); fix_endian64_same(nonce);
@ -353,21 +352,50 @@ ethash_cache *ethash_light_acquire_cache(ethash_light_t light)
return ret; return ret;
} }
ethash_full_t ethash_full_new(ethash_params const* params, ethash_full_t ethash_full_new(char const *dirname,
const ethash_h256_t *seed_hash,
ethash_params const* params,
ethash_cache const* cache, ethash_cache const* cache,
ethash_callback_t callback) ethash_callback_t callback)
{ {
struct ethash_full *ret; struct ethash_full *ret;
int fd;
FILE *f = NULL;
bool match = false;
ret = calloc(sizeof(*ret), 1); ret = calloc(sizeof(*ret), 1);
if (!ret) { if (!ret) {
return NULL; return NULL;
} }
ret->cache = (ethash_cache*)cache; ret->cache = (ethash_cache*)cache;
ret->data = malloc((size_t)params->full_size); ret->file_size = (size_t)params->full_size;
if (!ret->data) { 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; 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)) { if (!ethash_compute_full_data(ret->data, params, cache)) {
goto fail_free_full_data; goto fail_free_full_data;
} }
@ -375,7 +403,10 @@ ethash_full_t ethash_full_new(ethash_params const* params,
return ret; return ret;
fail_free_full_data: 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: fail_free_full:
free(ret); free(ret);
return NULL; return NULL;
@ -386,7 +417,11 @@ void ethash_full_delete(ethash_full_t full)
if (full->cache) { if (full->cache) {
ethash_cache_delete(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); free(full);
} }

1
internal.h

@ -37,6 +37,7 @@ struct ethash_light {
struct ethash_full { struct ethash_full {
FILE *file; FILE *file;
size_t file_size;
ethash_cache *cache; ethash_cache *cache;
node *data; node *data;
ethash_callback_t callback; 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 * @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: * naming of the file as can be seen from the spec at:
* https://github.com/ethereum/wiki/wiki/Ethash-DAG * https://github.com/ethereum/wiki/wiki/Ethash-DAG
* @param[out] f If the hash/revision combo matched then this will point * @param[out] f If there was no failure then this will point to an open
* to an opened file handler for that file. User will then * file descriptor. User is responsible for closing it.
* have to close 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 * @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. * 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); 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, static inline bool ethash_io_mutable_name(uint32_t revision,
ethash_h256_t *seed_hash, ethash_h256_t const* seed_hash,
char *output) char* output)
{ {
uint64_t hash = *((uint64_t*)seed_hash); uint64_t hash = *((uint64_t*)seed_hash);
#if LITTLE_ENDIAN == BYTE_ORDER #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; 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, char const* filename,
size_t filename_length) 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, enum ethash_io_rc ethash_io_prepare(char const *dirname,
ethash_h256_t seedhash, ethash_h256_t const seedhash,
FILE **output_file) FILE **output_file,
size_t file_size)
{ {
char mutable_name[DAG_MUTABLE_NAME_MAX_SIZE]; char mutable_name[DAG_MUTABLE_NAME_MAX_SIZE];
enum ethash_io_rc ret = ETHASH_IO_FAIL; 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 // try to open the file
FILE *f = ethash_fopen(tmpfile, "rb"); FILE *f = ethash_fopen(tmpfile, "rb+");
if (!f) { if (f) {
// TODO: check for file size
} else {
// file does not exist, will need to be created // file does not exist, will need to be created
ret = ETHASH_IO_MEMO_MISMATCH; f = ethash_fopen(tmpfile, "wb+");
if (!f) {
goto free_memo; 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 set_file;
}
ret = ETHASH_IO_MEMO_MATCH; ret = ETHASH_IO_MEMO_MATCH;
set_file:
*output_file = f; *output_file = f;
free_memo: free_memo:
free(tmpfile); 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; 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]; char mutable_name[DAG_MUTABLE_NAME_MAX_SIZE];
enum ethash_io_rc ret = ETHASH_IO_FAIL; 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 // try to open the file
FILE *f = ethash_fopen(tmpfile, "rb"); FILE *f = ethash_fopen(tmpfile, "rb+");
if (!f) { if (!f) {
// file does not exist, will need to be created // file does not exist, will need to be created
ret = ETHASH_IO_MEMO_MISMATCH; 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; goto free_memo;
} }
fputc('\n', f);
fflush(f);
ret = ETHASH_IO_MEMO_MISMATCH;
goto set_file;
}
ret = ETHASH_IO_MEMO_MATCH; ret = ETHASH_IO_MEMO_MATCH;
set_file:
*output_file = f; *output_file = f;
free_memo: free_memo:
free(tmpfile); 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