-
Notifications
You must be signed in to change notification settings - Fork 2
/
main.go
136 lines (115 loc) · 4.05 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright (C) 2021 Vojtech Aschenbrenner <v@asch.cz>
// bs3 is a userspace daemon using BUSE for creating a block device and S3
// protocol to communicate with object backend. It is designed for easy
// extension of all the important parts. Hence the S3 protocol can be easily
// replaced by RADOS or any other protocol.
//
// Project structure is following:
//
// - internal contains all packages used by this program. The name "internal"
// is reserved by go compiler and disallows its imports from different
// projects. Since we don't provide any reusable packages, we use internal
// directory.
//
// - internal/bs3 contains all packages related only to the bs3 implementation.
// See the package descriptions in the source code for more details.
//
// - internal/null contains trivial implementation of block device which does
// nothing but correctly. It can be used for benchmarking underlying buse
// library and kernel module. The null implementation is part of bs3 because it
// shares configuration and makes benchmarking easier and without code
// duplication.
//
// - internal/config contains configuration package which is common for both,
// bs3 and null implementations.
package main
import (
"fmt"
"net/http"
_ "net/http/pprof"
"os"
"os/signal"
"syscall"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/asch/bs3/internal/bs3"
"github.com/asch/bs3/internal/config"
"github.com/asch/bs3/internal/null"
"github.com/asch/buse/lib/go/buse"
)
// Parse configuration from file and environment variables, creates a
// BuseReadWriter and creates new buse device with it. The device is ran until
// it is signaled by SIGINT or SIGTERM to gracefully finish.
func main() {
err := config.Configure()
if err != nil {
log.Panic().Err(err).Send()
}
loggerSetup(config.Cfg.Log.Pretty, config.Cfg.Log.Level)
log.Info().Msgf("Configuration for block device buse%d loaded from %s",
config.Cfg.Major, config.Cfg.ConfigPath)
if config.Cfg.Profiler {
log.Info().Msg("Running profiler.")
runProfiler(config.Cfg.ProfilerPort)
}
buseReadWriter, err := getBuseReadWriter(config.Cfg.Null)
if err != nil {
log.Panic().Err(err).Send()
}
buse, err := buse.New(buseReadWriter, buse.Options{
Durable: config.Cfg.Write.Durable,
WriteChunkSize: int64(config.Cfg.Write.ChunkSize),
BlockSize: int64(config.Cfg.BlockSize),
IOMin: int64(config.Cfg.IOMin),
Threads: int(config.Cfg.Threads),
Major: int64(config.Cfg.Major),
WriteShmSize: int64(config.Cfg.Write.BufSize),
ReadShmSize: int64(config.Cfg.Read.BufSize),
Size: int64(config.Cfg.Size),
CollisionArea: int64(config.Cfg.Write.CollisionSize),
QueueDepth: int64(config.Cfg.QueueDepth),
Scheduler: config.Cfg.Scheduler,
CPUsPerNode: config.Cfg.CPUsPerNode,
})
if err != nil {
log.Panic().Msg(err.Error())
}
log.Info().Msgf("Block device buse%d registered.", config.Cfg.Major)
registerSigHandlers(buse)
buse.Run()
log.Info().Msgf("Block device buse%d stopped.", config.Cfg.Major)
buse.RemoveDevice()
log.Info().Msgf("Block device buse%d removed.", config.Cfg.Major)
}
// Return null device if user wants it, otherwise returns bs3 device, which is
// default.
func getBuseReadWriter(wantNullDevice bool) (buse.BuseReadWriter, error) {
if wantNullDevice {
return null.NewNull(), nil
}
bs3, err := bs3.NewWithDefaults()
return bs3, err
}
// Register handler for graceful stop when SIGINT or SIGTERM came in.
func registerSigHandlers(buse buse.Buse) {
stopChan := make(chan os.Signal, 1)
signal.Notify(stopChan, os.Interrupt)
signal.Notify(stopChan, syscall.SIGTERM)
go func() {
<-stopChan
log.Info().Msg("Stopping bs3 device.")
buse.StopDevice()
}()
}
func loggerSetup(pretty bool, level int) {
if pretty {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
}
zerolog.SetGlobalLevel(zerolog.Level(level))
}
// Enables remote profiling support. Useful for perfomance debugging.
func runProfiler(port int) {
go func() {
log.Info().Err(http.ListenAndServe(fmt.Sprintf("localhost:%d", port), nil)).Send()
}()
}