/src/util-linux/libblkid/src/topology/topology.c
Line | Count | Source |
1 | | /* |
2 | | * topology - gathers information about device topology |
3 | | * |
4 | | * Copyright 2009 Red Hat, Inc. All rights reserved. |
5 | | * |
6 | | * This file may be redistributed under the terms of the |
7 | | * GNU Lesser General Public License. |
8 | | */ |
9 | | |
10 | | #include <stdio.h> |
11 | | #include <string.h> |
12 | | #include <stdlib.h> |
13 | | #include <stddef.h> |
14 | | #include <inttypes.h> |
15 | | |
16 | | #include "topology.h" |
17 | | |
18 | | /** |
19 | | * SECTION:topology |
20 | | * @title: Topology information |
21 | | * @short_description: block device topology information. |
22 | | * |
23 | | * The topology chain provides details about Linux block devices, for more |
24 | | * information see: |
25 | | * |
26 | | * Linux kernel Documentation/ABI/testing/sysfs-block |
27 | | * |
28 | | * NAME=value (tags) interface is enabled by blkid_probe_enable_topology(), |
29 | | * and provides: |
30 | | * |
31 | | * @LOGICAL_SECTOR_SIZE: this is the smallest unit the storage device can |
32 | | * address. It is typically 512 bytes. |
33 | | * |
34 | | * @PHYSICAL_SECTOR_SIZE: this is the smallest unit a physical storage device |
35 | | * can write atomically. It is usually the same as the |
36 | | * logical sector size but may be bigger. |
37 | | * |
38 | | * @MINIMUM_IO_SIZE: minimum size which is the device's preferred unit of I/O. |
39 | | * For RAID arrays it is often the stripe chunk size. |
40 | | * |
41 | | * @OPTIMAL_IO_SIZE: usually the stripe width for RAID or zero. For RAID arrays |
42 | | * it is usually the stripe width or the internal track size. |
43 | | * |
44 | | * @ALIGNMENT_OFFSET: indicates how many bytes the beginning of the device is |
45 | | * offset from the disk's natural alignment. |
46 | | * |
47 | | * The NAME=value tags are not defined when the corresponding topology value |
48 | | * is zero. The MINIMUM_IO_SIZE should be always defined if kernel provides |
49 | | * topology information. |
50 | | * |
51 | | * Binary interface: |
52 | | * |
53 | | * blkid_probe_get_topology() |
54 | | * |
55 | | * blkid_topology_get_'VALUENAME'() |
56 | | */ |
57 | | static int topology_probe(blkid_probe pr, struct blkid_chain *chn); |
58 | | static void topology_free(blkid_probe pr, void *data); |
59 | | static int topology_is_complete(blkid_probe pr); |
60 | | static int topology_set_logical_sector_size(blkid_probe pr); |
61 | | |
62 | | /* |
63 | | * Binary interface |
64 | | */ |
65 | | struct blkid_struct_topology { |
66 | | unsigned long alignment_offset; |
67 | | unsigned long minimum_io_size; |
68 | | unsigned long optimal_io_size; |
69 | | unsigned long logical_sector_size; |
70 | | unsigned long physical_sector_size; |
71 | | unsigned long dax; |
72 | | uint64_t diskseq; |
73 | | }; |
74 | | |
75 | | /* |
76 | | * Topology chain probing functions |
77 | | */ |
78 | | static const struct blkid_idinfo *idinfos[] = |
79 | | { |
80 | | #ifdef __linux__ |
81 | | &sysfs_tp_idinfo, |
82 | | &ioctl_tp_idinfo, |
83 | | &md_tp_idinfo, |
84 | | &dm_tp_idinfo, |
85 | | &lvm_tp_idinfo, |
86 | | &evms_tp_idinfo |
87 | | #endif |
88 | | }; |
89 | | |
90 | | |
91 | | /* |
92 | | * Driver definition |
93 | | */ |
94 | | const struct blkid_chaindrv topology_drv = { |
95 | | .id = BLKID_CHAIN_TOPLGY, |
96 | | .name = "topology", |
97 | | .dflt_enabled = FALSE, |
98 | | .idinfos = idinfos, |
99 | | .nidinfos = ARRAY_SIZE(idinfos), |
100 | | .probe = topology_probe, |
101 | | .safeprobe = topology_probe, |
102 | | .free_data = topology_free |
103 | | }; |
104 | | |
105 | | /** |
106 | | * blkid_probe_enable_topology: |
107 | | * @pr: probe |
108 | | * @enable: TRUE/FALSE |
109 | | * |
110 | | * Enables/disables the topology probing for non-binary interface. |
111 | | * |
112 | | * Returns: 0 on success, or -1 in case of error. |
113 | | */ |
114 | | int blkid_probe_enable_topology(blkid_probe pr, int enable) |
115 | 0 | { |
116 | 0 | pr->chains[BLKID_CHAIN_TOPLGY].enabled = enable; |
117 | 0 | return 0; |
118 | 0 | } |
119 | | |
120 | | /** |
121 | | * blkid_probe_get_topology: |
122 | | * @pr: probe |
123 | | * |
124 | | * This is a binary interface for topology values. See also blkid_topology_* |
125 | | * functions. |
126 | | * |
127 | | * This function is independent on blkid_do_[safe,full]probe() and |
128 | | * blkid_probe_enable_topology() calls. |
129 | | * |
130 | | * WARNING: the returned object will be overwritten by the next |
131 | | * blkid_probe_get_topology() call for the same @pr. If you want to |
132 | | * use more blkid_topology objects in the same time you have to create |
133 | | * more blkid_probe handlers (see blkid_new_probe()). |
134 | | * |
135 | | * Returns: blkid_topology, or NULL in case of error. |
136 | | */ |
137 | | blkid_topology blkid_probe_get_topology(blkid_probe pr) |
138 | 0 | { |
139 | 0 | return (blkid_topology) blkid_probe_get_binary_data(pr, |
140 | 0 | &pr->chains[BLKID_CHAIN_TOPLGY]); |
141 | 0 | } |
142 | | |
143 | | /* |
144 | | * The blkid_do_probe() backend. |
145 | | */ |
146 | | static int topology_probe(blkid_probe pr, struct blkid_chain *chn) |
147 | 0 | { |
148 | 0 | size_t i; |
149 | 0 | int rc; |
150 | |
|
151 | 0 | if (chn->idx < -1) |
152 | 0 | return -1; |
153 | | |
154 | 0 | if (!S_ISBLK(pr->mode)) |
155 | 0 | return -EINVAL; /* nothing, works with block devices only */ |
156 | | |
157 | 0 | if (chn->binary) { |
158 | 0 | DBG(LOWPROBE, ul_debug("initialize topology binary data")); |
159 | |
|
160 | 0 | if (chn->data) |
161 | | /* reset binary data */ |
162 | 0 | memset(chn->data, 0, |
163 | 0 | sizeof(struct blkid_struct_topology)); |
164 | 0 | else { |
165 | 0 | chn->data = calloc(1, |
166 | 0 | sizeof(struct blkid_struct_topology)); |
167 | 0 | if (!chn->data) |
168 | 0 | return -ENOMEM; |
169 | 0 | } |
170 | 0 | } |
171 | | |
172 | 0 | blkid_probe_chain_reset_values(pr, chn); |
173 | |
|
174 | 0 | DBG(LOWPROBE, ul_debug("--> starting probing loop [TOPOLOGY idx=%d]", |
175 | 0 | chn->idx)); |
176 | |
|
177 | 0 | i = chn->idx < 0 ? 0 : chn->idx + 1U; |
178 | |
|
179 | 0 | for ( ; i < ARRAY_SIZE(idinfos); i++) { |
180 | 0 | const struct blkid_idinfo *id = idinfos[i]; |
181 | |
|
182 | 0 | chn->idx = i; |
183 | |
|
184 | 0 | if (id->probefunc) { |
185 | 0 | DBG(LOWPROBE, ul_debug("%s: call probefunc()", id->name)); |
186 | 0 | errno = 0; |
187 | 0 | rc = id->probefunc(pr, NULL); |
188 | 0 | blkid_probe_prune_buffers(pr); |
189 | 0 | if (rc != 0) |
190 | 0 | continue; |
191 | 0 | } |
192 | | |
193 | 0 | if (!topology_is_complete(pr)) |
194 | 0 | continue; |
195 | | |
196 | | /* generic for all probing drivers */ |
197 | 0 | topology_set_logical_sector_size(pr); |
198 | |
|
199 | 0 | DBG(LOWPROBE, ul_debug("<-- leaving probing loop (type=%s) [TOPOLOGY idx=%d]", |
200 | 0 | id->name, chn->idx)); |
201 | 0 | return BLKID_PROBE_OK; |
202 | 0 | } |
203 | | |
204 | 0 | DBG(LOWPROBE, ul_debug("<-- leaving probing loop (failed) [TOPOLOGY idx=%d]", |
205 | 0 | chn->idx)); |
206 | 0 | return BLKID_PROBE_NONE; |
207 | 0 | } |
208 | | |
209 | | static void topology_free(blkid_probe pr __attribute__((__unused__)), |
210 | | void *data) |
211 | 5.74k | { |
212 | 5.74k | free(data); |
213 | 5.74k | } |
214 | | |
215 | | static int topology_set_value(blkid_probe pr, const char *name, |
216 | | size_t structoff, unsigned long data) |
217 | 0 | { |
218 | 0 | struct blkid_chain *chn = blkid_probe_get_chain(pr); |
219 | |
|
220 | 0 | if (!chn) |
221 | 0 | return -1; |
222 | 0 | if (!data) |
223 | 0 | return 0; /* ignore zeros */ |
224 | | |
225 | 0 | if (chn->binary) { |
226 | 0 | memcpy((char *) chn->data + structoff, &data, sizeof(data)); |
227 | 0 | return 0; |
228 | 0 | } |
229 | 0 | return blkid_probe_sprintf_value(pr, name, "%lu", data); |
230 | 0 | } |
231 | | |
232 | | static int topology_set_value64(blkid_probe pr, const char *name, |
233 | | size_t structoff, uint64_t data) |
234 | 0 | { |
235 | 0 | struct blkid_chain *chn = blkid_probe_get_chain(pr); |
236 | |
|
237 | 0 | if (!chn) |
238 | 0 | return -1; |
239 | 0 | if (!data) |
240 | 0 | return 0; /* ignore zeros */ |
241 | | |
242 | 0 | if (chn->binary) { |
243 | 0 | memcpy((char *) chn->data + structoff, &data, sizeof(data)); |
244 | 0 | return 0; |
245 | 0 | } |
246 | 0 | return blkid_probe_sprintf_value(pr, name, "%"PRIu64, data); |
247 | 0 | } |
248 | | |
249 | | |
250 | | /* the topology info is complete when we have at least "minimum_io_size" which |
251 | | * is provided by all blkid topology drivers */ |
252 | | static int topology_is_complete(blkid_probe pr) |
253 | 0 | { |
254 | 0 | struct blkid_chain *chn = blkid_probe_get_chain(pr); |
255 | |
|
256 | 0 | if (!chn) |
257 | 0 | return FALSE; |
258 | | |
259 | 0 | if (chn->binary && chn->data) { |
260 | 0 | blkid_topology tp = (blkid_topology) chn->data; |
261 | 0 | if (tp->minimum_io_size) |
262 | 0 | return TRUE; |
263 | 0 | } |
264 | | |
265 | 0 | return __blkid_probe_lookup_value(pr, "MINIMUM_IO_SIZE") ? TRUE : FALSE; |
266 | 0 | } |
267 | | |
268 | | int blkid_topology_set_alignment_offset(blkid_probe pr, int val) |
269 | 0 | { |
270 | 0 | unsigned long xval; |
271 | | |
272 | | /* Welcome to Hell. The kernel is able to return -1 as an |
273 | | * alignment_offset if no compatible sizes and alignments |
274 | | * exist for stacked devices. |
275 | | * |
276 | | * There is no way how libblkid caller can respond to the value -1, so |
277 | | * we will hide this corner case... |
278 | | * |
279 | | * (TODO: maybe we can export an extra boolean value 'misaligned' rather |
280 | | * then complete hide this problem.) |
281 | | */ |
282 | 0 | xval = val < 0 ? 0 : val; |
283 | |
|
284 | 0 | return topology_set_value(pr, |
285 | 0 | "ALIGNMENT_OFFSET", |
286 | 0 | offsetof(struct blkid_struct_topology, alignment_offset), |
287 | 0 | xval); |
288 | 0 | } |
289 | | |
290 | | int blkid_topology_set_minimum_io_size(blkid_probe pr, unsigned long val) |
291 | 0 | { |
292 | 0 | return topology_set_value(pr, |
293 | 0 | "MINIMUM_IO_SIZE", |
294 | 0 | offsetof(struct blkid_struct_topology, minimum_io_size), |
295 | 0 | val); |
296 | 0 | } |
297 | | |
298 | | int blkid_topology_set_optimal_io_size(blkid_probe pr, unsigned long val) |
299 | 0 | { |
300 | 0 | return topology_set_value(pr, |
301 | 0 | "OPTIMAL_IO_SIZE", |
302 | 0 | offsetof(struct blkid_struct_topology, optimal_io_size), |
303 | 0 | val); |
304 | 0 | } |
305 | | |
306 | | /* BLKSSZGET is provided on all systems since 2.3.3 -- so we don't have to |
307 | | * waste time with sysfs. |
308 | | */ |
309 | | static int topology_set_logical_sector_size(blkid_probe pr) |
310 | 0 | { |
311 | 0 | unsigned long val = blkid_probe_get_sectorsize(pr); |
312 | |
|
313 | 0 | if (!val) |
314 | 0 | return -1; |
315 | | |
316 | 0 | return topology_set_value(pr, |
317 | 0 | "LOGICAL_SECTOR_SIZE", |
318 | 0 | offsetof(struct blkid_struct_topology, logical_sector_size), |
319 | 0 | val); |
320 | 0 | } |
321 | | |
322 | | int blkid_topology_set_physical_sector_size(blkid_probe pr, unsigned long val) |
323 | 0 | { |
324 | 0 | return topology_set_value(pr, |
325 | 0 | "PHYSICAL_SECTOR_SIZE", |
326 | 0 | offsetof(struct blkid_struct_topology, physical_sector_size), |
327 | 0 | val); |
328 | 0 | } |
329 | | |
330 | | int blkid_topology_set_dax(blkid_probe pr, unsigned long val) |
331 | 0 | { |
332 | 0 | return topology_set_value(pr, |
333 | 0 | "DAX", |
334 | 0 | offsetof(struct blkid_struct_topology, dax), |
335 | 0 | val); |
336 | 0 | } |
337 | | |
338 | | int blkid_topology_set_diskseq(blkid_probe pr, uint64_t val) |
339 | 0 | { |
340 | 0 | return topology_set_value64(pr, |
341 | 0 | "DISKSEQ", |
342 | 0 | offsetof(struct blkid_struct_topology, diskseq), |
343 | 0 | val); |
344 | 0 | } |
345 | | |
346 | | /** |
347 | | * blkid_topology_get_alignment_offset: |
348 | | * @tp: topology |
349 | | * |
350 | | * Returns: alignment offset in bytes or 0. |
351 | | */ |
352 | | unsigned long blkid_topology_get_alignment_offset(blkid_topology tp) |
353 | 0 | { |
354 | 0 | return tp->alignment_offset; |
355 | 0 | } |
356 | | |
357 | | /** |
358 | | * blkid_topology_get_minimum_io_size: |
359 | | * @tp: topology |
360 | | * |
361 | | * Returns: minimum io size in bytes or 0. |
362 | | */ |
363 | | unsigned long blkid_topology_get_minimum_io_size(blkid_topology tp) |
364 | 0 | { |
365 | 0 | return tp->minimum_io_size; |
366 | 0 | } |
367 | | |
368 | | /** |
369 | | * blkid_topology_get_optimal_io_size |
370 | | * @tp: topology |
371 | | * |
372 | | * Returns: optimal io size in bytes or 0. |
373 | | */ |
374 | | unsigned long blkid_topology_get_optimal_io_size(blkid_topology tp) |
375 | 0 | { |
376 | 0 | return tp->optimal_io_size; |
377 | 0 | } |
378 | | |
379 | | /** |
380 | | * blkid_topology_get_logical_sector_size |
381 | | * @tp: topology |
382 | | * |
383 | | * Returns: logical sector size (BLKSSZGET ioctl) in bytes or 0. |
384 | | */ |
385 | | unsigned long blkid_topology_get_logical_sector_size(blkid_topology tp) |
386 | 0 | { |
387 | 0 | return tp->logical_sector_size; |
388 | 0 | } |
389 | | |
390 | | /** |
391 | | * blkid_topology_get_physical_sector_size |
392 | | * @tp: topology |
393 | | * |
394 | | * Returns: logical sector size (BLKSSZGET ioctl) in bytes or 0. |
395 | | */ |
396 | | unsigned long blkid_topology_get_physical_sector_size(blkid_topology tp) |
397 | 0 | { |
398 | 0 | return tp->physical_sector_size; |
399 | 0 | } |
400 | | |
401 | | /** |
402 | | * blkid_topology_get_dax |
403 | | * @tp: topology |
404 | | * |
405 | | * Returns: 1 if dax is supported, 0 otherwise. |
406 | | * |
407 | | * Since: 2.36 |
408 | | */ |
409 | | unsigned long blkid_topology_get_dax(blkid_topology tp) |
410 | 0 | { |
411 | 0 | return tp->dax; |
412 | 0 | } |
413 | | |
414 | | /** |
415 | | * blkid_topology_get_diskseq |
416 | | * @tp: topology |
417 | | * |
418 | | * Returns: disk sequence number |
419 | | * |
420 | | * Since: 2.39 |
421 | | */ |
422 | | uint64_t blkid_topology_get_diskseq(blkid_topology tp) |
423 | 0 | { |
424 | 0 | return tp->diskseq; |
425 | 0 | } |