consensus/ethash: avoid runtime errors due to OOD on mmap writes (#23799)
When we map a file for generating the DAG, we do a simple truncate to e.g. 1Gb. This is fine, even if we have nowhere near 1Gb disk available, as the actual file doesn't take up the full 1Gb, merely a few bytes. When we start generating into it, however, it eventually crashes with a unexpected fault address . This change fixes it (on linux systems) by using the Fallocate syscall, which preallocates suffcient space on disk to avoid that situation. Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
parent
2e8b58f076
commit
178debe435
@ -136,13 +136,16 @@ func memoryMapAndGenerate(path string, size uint64, lock bool, generator func(bu
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
if err = dump.Truncate(int64(len(dumpMagic))*4 + int64(size)); err != nil {
|
if err = ensureSize(dump, int64(len(dumpMagic))*4+int64(size)); err != nil {
|
||||||
|
dump.Close()
|
||||||
|
os.Remove(temp)
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
// Memory map the file for writing and fill it with the generator
|
// Memory map the file for writing and fill it with the generator
|
||||||
mem, buffer, err := memoryMapFile(dump, true)
|
mem, buffer, err := memoryMapFile(dump, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dump.Close()
|
dump.Close()
|
||||||
|
os.Remove(temp)
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
copy(buffer, dumpMagic)
|
copy(buffer, dumpMagic)
|
||||||
@ -358,7 +361,7 @@ func (d *dataset) generate(dir string, limit int, lock bool, test bool) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failed to generate mapped ethash dataset", "err", err)
|
logger.Error("Failed to generate mapped ethash dataset", "err", err)
|
||||||
|
|
||||||
d.dataset = make([]uint32, dsize/2)
|
d.dataset = make([]uint32, dsize/4)
|
||||||
generateDataset(d.dataset, d.epoch, cache)
|
generateDataset(d.dataset, d.epoch, cache)
|
||||||
}
|
}
|
||||||
// Iterate over all previous instances and delete old ones
|
// Iterate over all previous instances and delete old ones
|
||||||
|
35
consensus/ethash/mmap_help_linux.go
Normal file
35
consensus/ethash/mmap_help_linux.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright 2021 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library 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 Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//go:build linux
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package ethash
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ensureSize expands the file to the given size. This is to prevent runtime
|
||||||
|
// errors later on, if the underlying file expands beyond the disk capacity,
|
||||||
|
// even though it ostensibly is already expanded, but due to being sparse
|
||||||
|
// does not actually occupy the full declared size on disk.
|
||||||
|
func ensureSize(f *os.File, size int64) error {
|
||||||
|
// Docs: https://www.man7.org/linux/man-pages/man2/fallocate.2.html
|
||||||
|
return unix.Fallocate(int(f.Fd()), 0, 0, size)
|
||||||
|
}
|
36
consensus/ethash/mmap_help_other.go
Normal file
36
consensus/ethash/mmap_help_other.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2021 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library 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 Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//go:build !linux
|
||||||
|
// +build !linux
|
||||||
|
|
||||||
|
package ethash
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ensureSize expands the file to the given size. This is to prevent runtime
|
||||||
|
// errors later on, if the underlying file expands beyond the disk capacity,
|
||||||
|
// even though it ostensibly is already expanded, but due to being sparse
|
||||||
|
// does not actually occupy the full declared size on disk.
|
||||||
|
func ensureSize(f *os.File, size int64) error {
|
||||||
|
// On systems which do not support fallocate, we merely truncate it.
|
||||||
|
// More robust alternatives would be to
|
||||||
|
// - Use posix_fallocate, or
|
||||||
|
// - explicitly fill the file with zeroes.
|
||||||
|
return f.Truncate(size)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user