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
|
\input{confighandout}
\subsection{Unsorted Block Images (UBI)}
\subsubsection{Was ist UBI?}
UBI kann am ehesten als Logical Volume Manager (LVM) für mtd-Devices
bezeichnet werden. Beim Start scanned UBI das mtd-Device und baut eine
Liste der vorhandenen Eraseblöcke auf. Dabei werden auch Bad Blocks
richtig behandelt, da das MTD-Subsystem, auf dem UBI aufsetzt, diese
bereits erkennt.
Dieser Gesamtvorrat an physikalischen Eraseblöcken wird jetzt im
nächsten Schritt an sogenannte UBI-Volumes verteilt.
\subsubsection{UBI-Volumes}
UBI-Volumes entsprechen etwa den Partitionen anderer Systeme. Der von
einem mtd-Device bereitgestellte Speicher wird also in mehrere unabhängige
Einheiten aufgeteilt. Im Vergleich zu den Partitionen, die das
MTD-Subsystem auf einem Flash anlegen kann, sind UBI-Volumes aber erheblich
leistungsfähiger und flexibler. Aus diesem Grund empfiehlt es sich, so
wenig wie möglich mtd-Partitionen anzulegen, und UBI eine möglichst große
Partition zur Verwaltung zu übergeben.
MTD-Partitionen haben eine strikt festgelegte Zuordnung von physikalischen
Eraseblöcken zu Partitionen. UBI kann dagegen Eraseblöcke bei Bedarf
beliebig zwischen den Volumes umverteilen. Dies geschieht beispielsweise
beim Wear-Leveling, das UBI ebenfalls durchführt. Ebenso können defekte
Eraseblöcke durch andere Blöcke ersetzt werden.
Durch den von UBI verwendeten Algorithmus ist es selbst beim Neuanlegen
eines Volumes kaum vorhersagbar, welche physikalischen Eraseblöcke auf
dem Flash es belegt. Während eine MTD-Partition immer in denselben
aufeinanderfolgenden Blöcken liegt, ändert sich die Zuordnung von
physikalischen Eraseblöcken des Flash zu logischen Eraseblöcken des
Volumes im laufenden Betrieb dynamisch. Ein UBI-Volume mit drei
Eraseblöcken kann also durchaus die Blöcke 814, 27 und 1013 belegen.
Aus diesem Ansatz ergibt sich auch der Name \emph{Unsorted Block Images}.
\emph{Hinweis:} Auf klassischen Festplatten wird ein solches Verhalten
als \emph{Fragmentierung} bezeichnet und ist dort unerwünscht, da eine
mechanische Platte signifikante Seek-Zeiten aufweist. Man versucht dort,
Daten möglichst in aufeinander folgenden Blöcken zu speichern, um zu
verhindern, dass der mechanische Schreib-/Lesekopf von einem Ende der
Platte zum anderen bewegt werden muss. Auf Flash-Speichern gibt es keine
Seek-Zeiten, was UBI hier zugunsten eines auf Flash optimierten Designs
ausnutzt.
\paragraph{Statische Volumes}
Statische Volumes sind für Anwendungen gedacht, die kein Dateisystem
benötigen. Ein praktisches Beispiel ist ein kleines Volume, das lediglich einen
Kernel enthält, der vom Bootloader geladen und gestartet wird. Mit einem
statischen Volume muss der Bootloader keinerlei Dateisystem implementieren.
Er muss lediglich aus den UBI-Headern herausfinden, welche physikalischen
Eraseblöcke zu dem statischen Volume gehören. Ausserdem erfährt er von UBI,
wie viele Bytes tatsächlich durch Daten belegt sind. Er lädt diese Daten,
im Beispiel einen Kernel, ins RAM und springt sie an. Dies ist mit relativ
wenig Code (einige kB) möglich.
Statische Volumes enthalten also immer nur einen einzigen Datenblock, der
in einem Vorgang geschrieben werden muss.
\paragraph{Dynamische Volumes}
Dynamische Volumes sind dafür gedacht, ein Dateisystem zu enthalten.
Man verwendet sie also beispielsweise für ein Root-Filesystem oder für
Volumes, die Nutzerdaten enthalten.
Dynamische Volumes verwendet man am Besten mit dem Dateisystem ubifs.
\paragraph{UBIGLUEBI}
UBIGLUEBI ist ein Aufsatz auf UBI, der für jedes Volume wieder ein
mtd-Device bereitstellt. Dies klingt zunächst überraschend, da UBI ja schon
auf einem mtd-Device aufsetzt. Der Hintergrund ist, dass ältere
Flash-Dateisysteme wie jffs2 ein mtd-Device benötigen. Um deren Einsatz
zu ermöglichen, wurde dieser Zusatz geschaffen.
Heute gibt es ubifs, das direkt auf UBI aufsetzt und folglich kein
mtd-Device benötigt. Da ubifs ohnehin in allen Bereichen deutliche Vorteile
gegenüber jffs2 hat, wird UBIGLUEBI in der Regel nicht mehr benötigt.
\includegraphics[width=8cm]{images/ubi-big-picture.png}
\subsubsection{UBI-Tools}
Da UBI von den MTD-Entwicklern implementiert wurde, sind die UBI-Tools
Bestandteil der MTD-Tool-Kollektion. Diese gibt es mittlerweile als
Paket in den meisten Distributionen. Unter Debian installiert man das
Paket \cmd{mtd-utils}:
\begin{lstlisting}
aptitude install mtd-utils
\end{lstlisting}
Hier die wichtigsten UBI-Aktionen in Kurzform:
Die Kontrolle über ein mtd-Device an UBI übertragen:
\begin{lstlisting}
ubiattach /dev/ubi_ctrl -m 2
\end{lstlisting}
Dies verbindet \cmd{/dev/mtd2} mit UBI.
Ein Volume anlegen:
\begin{lstlisting}
ubimkvol /dev/ubi0 -s 3MiB -t static -N kernel -n 1
\end{lstlisting}
Hier wird ein 3MiB großes statisches Volume mit dem Namen \cmd{kernel}
angelegt. Das Volume erhält die Nummer 1.
Daten in ein statisches Volume schreiben:
\begin{lstlisting}
ubiupdatevol /dev/ubi0_1 zImage
\end{lstlisting}
Die Daten aus der Datei \cmd{zImage} werden in das statische Volume mit der
Nummer 1 geschrieben.
Natürlich lassen sich komplette Volumes auch wieder entfernen:
\begin{lstlisting}
ubirmvol/dev/ubi0 -n 1
\end{lstlisting}
Das Volume mit der Nummer 1 wird entfernt.
Der mit Abstand aufwendigste Befehl ist \cmd{ubinize}. Damit lassen sich
vollständige UBI-Images für ein mtd-Device anfertigen. Diese können
anschließend mit \cmd{nandwrite} in das mtd-Device geschrieben werden.
Alternativ kann das Image natürlich auch mit einem geeigneten JTAGer
in das Flash übertragen werden. Da \cmd{ubinize} sehr vielseitig ist und
relativ viele Flash- und Layout-Parameter berücksichtigt werden müssen,
kann hier nur ein einfaches Beispiel gegeben werden.
Zunächst ist eine Konfigurationsdatei erforderlich, in der die einzelnen
Volumes sowie die in ihnen unterzubringenden Images beschrieben werden.
Diese Datei folgt in ihrer Syntax dem bekannten INI-Format. Ein Volume
wird beispielsweise so angegeben:
\begin{lstlisting}
[rootfs-volume]
mode=ubi
image=../my-ubifs.img
vol_id=1
vol_size=100MiB
vol_type=dynamic
vol_name=rootfs
vol_flags=autoresize
vol_alignment=1
\end{lstlisting}
Unter der Annahme, dass alle Volumes in der oben angegebenen Weise in der
Datei \cmd{ubi-cfg.ini} beschrieben wurden, kann man das UBI-Image mit
einem Befehl erzeugen, der etwa so aussieht:
\begin{lstlisting}
ubinize -o ubi.img -p 128KiB -m 2048 -s 512 ubi-cfg.ini
\end{lstlisting}
Im Beispiel wird eine Eraseblock-Größe von 128k angegeben, ausserdem eine
Pagegröße von 2048 Bytes sowie eine Subpage-Größe von 512 Bytes. Das
fertige Image landet in der Datei \cmd{ubi.img}.
Weitere Informationen sind unter folgender URL erhältlich:
\begin{lstlisting}
http://www.linux-mtd.infradead.org/doc/general.html
\end{lstlisting}
\input{tailhandout}
|