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