-
Notifications
You must be signed in to change notification settings - Fork 48
/
Copy pathmodule.go
173 lines (141 loc) · 3.83 KB
/
module.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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package main
import (
"bufio"
"os"
"path/filepath"
"strings"
"sync"
"golang.org/x/sys/unix"
)
const imageModulesDir = "/usr/lib/modules"
var (
loadedModules = make(map[string]bool)
loadingModules = make(map[string][]*sync.WaitGroup)
loadingModulesWg sync.WaitGroup // total number of modules being loaded
modulesMutex sync.Mutex
)
type alias struct{ pattern, module string } // module alias info
var (
aliases []alias // all aliases from initramfs
processedAliases = sync.Map{} // aliases that have been seen/processed by booster
)
func loadModalias(alias string) error {
if _, existed := processedAliases.LoadOrStore(alias, true); existed {
return nil
}
mods := matchAlias(alias)
if len(mods) == 0 {
debug("no match found for alias %s", alias)
return nil
}
_ = loadModules(mods...)
return nil
}
func readAliases() error {
f, err := os.Open(imageModulesDir + "/booster.alias")
if err != nil {
return err
}
defer f.Close()
s := bufio.NewScanner(f)
for s.Scan() {
line := s.Text()
parts := strings.Split(line, " ")
aliases = append(aliases, alias{parts[0], parts[1]})
}
return s.Err()
}
// loadModuleUnlocked asynchronously loads specified modules
func loadModuleUnlocked(wg *sync.WaitGroup, modules ...string) {
// TODO: assert that modulesMutex is locked
loadModule := func(mod string, depsWg *sync.WaitGroup) {
defer loadingModulesWg.Done()
depsWg.Wait()
if err := finitModule(mod); err != nil {
info("finit(%v): %v", mod, err)
}
modulesMutex.Lock()
defer modulesMutex.Unlock()
for _, w := range loadingModules[mod] {
// signal waiters that the module is loaded
w.Done()
}
delete(loadingModules, mod)
loadedModules[mod] = true
// post deps are loaded independently if finit() call successful or not
var postDepsWg sync.WaitGroup
if deps, ok := config.ModuleDependencies[mod]; ok {
loadModuleUnlocked(&postDepsWg, deps...)
}
}
for _, module := range modules {
module := module
if _, ok := loadedModules[module]; ok {
continue // the module is already loaded
}
if ok := config.BuiltinModules[module]; ok {
continue // no need to load builtin module
}
_, alreadyLoading := loadingModules[module]
wg.Add(1)
loadingModules[module] = append(loadingModules[module], wg)
if alreadyLoading {
// we already incremented 'wg' counter
// now wait till the module is loaded
continue
}
var depsWg sync.WaitGroup
if deps, ok := config.ModuleDependencies[module]; ok {
loadModuleUnlocked(&depsWg, deps...)
}
loadingModulesWg.Add(1)
go loadModule(module, &depsWg)
}
return
}
func finitModule(module string) error {
f, err := os.Open(imageModulesDir + "/" + module + ".ko")
if err != nil {
return err
}
defer f.Close()
// these are module parameters coming from modprobe
var opts []string
// I am not sure if ordering is important but we add modprobe params first and then cmdline
if v, ok := config.ModprobeOptions[module]; ok {
opts = append(opts, v)
}
opts = append(opts, moduleParams[module]...)
params := strings.Join(opts, " ")
if params == "" {
debug("loading module %s", module)
} else {
debug("loading module %s params=\"%s\"", module, params)
}
return unix.FinitModule(int(f.Fd()), params, 0)
}
func loadModules(modules ...string) *sync.WaitGroup {
var wg sync.WaitGroup
modulesMutex.Lock()
defer modulesMutex.Unlock()
loadModuleUnlocked(&wg, modules...)
return &wg
}
// returns all module names that match given alias
func matchAlias(tofind ...string) []string {
var result []string
for _, a := range aliases {
for _, f := range tofind {
match, err := filepath.Match(a.pattern, f)
if err != nil {
warning("unable to use modalias %s", a.pattern)
continue
}
if match {
debug("modalias %v matched module %v", f, a.module)
result = append(result, a.module)
}
}
}
return result
}