]>
git.proxmox.com Git - ceph.git/blob - ceph/src/common/blkdev.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
15 #include "include/compat.h"
18 #include <sys/param.h>
19 #include <geom/geom_disk.h>
25 #include <sys/ioctl.h>
27 #include <sys/types.h>
31 #include <boost/algorithm/string/replace.hpp>
32 //#include "common/debug.h"
33 #include "include/scope_guard.h"
34 #include "include/uuid.h"
35 #include "include/stringify.h"
39 #include "json_spirit/json_spirit_reader.h"
41 int get_device_by_path ( const char * path
, char * partition
, char * device
,
44 int fd
= :: open ( path
, O_RDONLY
| O_DIRECTORY
);
48 auto close_fd
= make_scope_guard ([ fd
] {
52 if ( auto ret
= blkdev
. partition ( partition
, max
); ret
) {
55 if ( auto ret
= blkdev
. wholedisk ( device
, max
); ret
) {
62 #include "common/blkdev.h"
67 #include <linux/kdev_t.h>
68 #include <blkid/blkid.h>
72 #include "common/SubProcess.h"
73 #include "common/errno.h"
85 BlkDev :: BlkDev ( const std :: string
& devname
)
89 int BlkDev :: get_devid ( dev_t
* id
) const
97 snprintf ( path
, sizeof ( path
), "/dev/%s" , devname
. c_str ());
103 * id
= S_ISBLK ( st
. st_mode
) ? st
. st_rdev
: st
. st_dev
;
109 const char * BlkDev :: sysfsdir () const {
113 int BlkDev :: get_size ( int64_t * psize
) const
116 int ret
= :: ioctl ( fd
, BLKGETSIZE64
, psize
);
117 #elif defined(BLKGETSIZE)
118 unsigned long sectors
= 0 ;
119 int ret
= :: ioctl ( fd
, BLKGETSIZE
, & sectors
);
120 * psize
= sectors
* 512ULL ;
122 // cppcheck-suppress preprocessorErrorDirective
123 # error "Linux configuration error (get_size)"
131 * get a block device property as a string
133 * store property in *val, up to maxlen chars
134 * return 0 on success
135 * return negative error on error
137 int64_t BlkDev :: get_string_property ( const char * prop
,
138 char * val
, size_t maxlen
) const
140 char filename
[ PATH_MAX
], wd
[ PATH_MAX
];
141 const char * dev
= nullptr ;
144 // sysfs isn't fully populated for partitions, so we need to lookup the sysfs
145 // entry for the underlying whole disk.
146 if ( int r
= wholedisk ( wd
, sizeof ( wd
)); r
< 0 )
150 dev
= devname
. c_str ();
152 if ( snprintf ( filename
, sizeof ( filename
), "%s/block/%s/%s" , sysfsdir (), dev
,
153 prop
) >= static_cast < int >( sizeof ( filename
))) {
157 FILE * fp
= fopen ( filename
, "r" );
163 if ( fgets ( val
, maxlen
- 1 , fp
)) {
164 // truncate at newline
166 while (* p
&& * p
!= ' \n ' )
177 * get a block device property
179 * return the value (we assume it is positive)
180 * return negative error on error
182 int64_t BlkDev :: get_int_property ( const char * prop
) const
184 char buff
[ 256 ] = { 0 };
185 int r
= get_string_property ( prop
, buff
, sizeof ( buff
));
189 for ( char * p
= buff
; * p
; ++ p
) {
196 r
= strtoll ( buff
, & endptr
, 10 );
197 if ( endptr
!= buff
+ strlen ( buff
))
202 bool BlkDev :: support_discard () const
204 return get_int_property ( "queue/discard_granularity" ) > 0 ;
207 int BlkDev :: discard ( int64_t offset
, int64_t len
) const
209 uint64_t range
[ 2 ] = {( uint64_t ) offset
, ( uint64_t ) len
};
210 return ioctl ( fd
, BLKDISCARD
, range
);
213 bool BlkDev :: is_rotational () const
215 return get_int_property ( "queue/rotational" ) > 0 ;
218 int BlkDev :: get_numa_node ( int * node
) const
220 int numa
= get_int_property ( "device/device/numa_node" );
227 int BlkDev :: dev ( char * dev
, size_t max
) const
229 return get_string_property ( "dev" , dev
, max
);
232 int BlkDev :: vendor ( char * vendor
, size_t max
) const
234 return get_string_property ( "device/device/vendor" , vendor
, max
);
237 int BlkDev :: model ( char * model
, size_t max
) const
239 return get_string_property ( "device/model" , model
, max
);
242 int BlkDev :: serial ( char * serial
, size_t max
) const
244 return get_string_property ( "device/serial" , serial
, max
);
247 int BlkDev :: partition ( char * partition
, size_t max
) const
250 int r
= get_devid (& id
);
252 return - EINVAL
; // hrm.
254 char * t
= blkid_devno_to_devname ( id
);
258 strncpy ( partition
, t
, max
);
263 int BlkDev :: wholedisk ( char * device
, size_t max
) const
266 int r
= get_devid (& id
);
268 return - EINVAL
; // hrm.
270 r
= blkid_devno_to_wholedisk ( id
, device
, max
, nullptr );
277 static int easy_readdir ( const std :: string
& dir
, std :: set
< std :: string
> * out
)
279 DIR * h
= :: opendir ( dir
. c_str ());
283 struct dirent
* de
= nullptr ;
284 while (( de
= :: readdir ( h
))) {
285 if ( strcmp ( de
-> d_name
, "." ) == 0 ||
286 strcmp ( de
-> d_name
, ".." ) == 0 ) {
289 out
-> insert ( de
-> d_name
);
295 void get_dm_parents ( const std :: string
& dev
, std :: set
< std :: string
> * ls
)
297 std :: string p
= std :: string ( "/sys/block/" ) + dev
+ "/slaves" ;
298 std :: set
< std :: string
> parents
;
299 easy_readdir ( p
, & parents
);
300 for ( auto & d
: parents
) {
302 // recurse in case it is dm-on-dm
303 if ( d
. find ( "dm-" ) == 0 ) {
304 get_dm_parents ( d
, ls
);
309 void get_raw_devices ( const std :: string
& in
,
310 std :: set
< std :: string
> * ls
)
312 if ( in
. substr ( 0 , 3 ) == "dm-" ) {
313 std :: set
< std :: string
> o
;
314 get_dm_parents ( in
, & o
);
316 get_raw_devices ( d
, ls
);
320 std :: string wholedisk
;
321 if ( d
. wholedisk (& wholedisk
) == 0 ) {
322 ls
-> insert ( wholedisk
);
329 int _get_vdo_stats_handle ( const char * devname
, std :: string
* vdo_name
)
333 // we need to go from the raw devname (e.g., dm-4) to the VDO volume name.
334 // currently the best way seems to be to look at /dev/mapper/* ...
335 std :: string expect
= std :: string ( "../" ) + devname
; // expected symlink target
336 DIR * dir
= :: opendir ( "/dev/mapper" );
340 struct dirent
* de
= nullptr ;
341 while (( de
= :: readdir ( dir
))) {
342 if ( de
-> d_name
[ 0 ] == '.' )
344 char fn
[ 4096 ], target
[ 4096 ];
345 snprintf ( fn
, sizeof ( fn
), "/dev/mapper/%s" , de
-> d_name
);
346 int r
= readlink ( fn
, target
, sizeof ( target
));
347 if ( r
< 0 || r
>= ( int ) sizeof ( target
))
350 if ( expect
== target
) {
351 snprintf ( fn
, sizeof ( fn
), "/sys/kvdo/%s/statistics" , de
-> d_name
);
352 vdo_fd
= :: open ( fn
, O_RDONLY
| O_CLOEXEC
); //DIRECTORY);
354 * vdo_name
= de
-> d_name
;
363 int get_vdo_stats_handle ( const char * devname
, std :: string
* vdo_name
)
365 std :: set
< std :: string
> devs
= { devname
};
366 while (! devs
. empty ()) {
367 std :: string dev
= * devs
. begin ();
368 devs
. erase ( devs
. begin ());
369 int fd
= _get_vdo_stats_handle ( dev
. c_str (), vdo_name
);
374 // ok, see if there are constituent devices
375 if ( dev
. find ( "dm-" ) == 0 ) {
376 get_dm_parents ( dev
, & devs
);
382 int64_t get_vdo_stat ( int vdo_fd
, const char * property
)
385 int fd
= :: openat ( vdo_fd
, property
, O_RDONLY
| O_CLOEXEC
);
390 int r
= :: read ( fd
, buf
, sizeof ( buf
) - 1 );
395 TEMP_FAILURE_RETRY (:: close ( fd
));
399 bool get_vdo_utilization ( int fd
, uint64_t * total
, uint64_t * avail
)
401 int64_t block_size
= get_vdo_stat ( fd
, "block_size" );
402 int64_t physical_blocks
= get_vdo_stat ( fd
, "physical_blocks" );
403 int64_t overhead_blocks_used
= get_vdo_stat ( fd
, "overhead_blocks_used" );
404 int64_t data_blocks_used
= get_vdo_stat ( fd
, "data_blocks_used" );
407 || ! overhead_blocks_used
408 || ! data_blocks_used
) {
411 int64_t avail_blocks
=
412 physical_blocks
- overhead_blocks_used
- data_blocks_used
;
413 * total
= block_size
* physical_blocks
;
414 * avail
= block_size
* avail_blocks
;
418 std :: string
_decode_model_enc ( const std :: string
& in
)
420 auto v
= boost :: replace_all_copy ( in
, " \\ x20" , " " );
421 if ( auto found
= v
. find_last_not_of ( " " ); found
!= v
. npos
) {
424 std :: replace ( v
. begin (), v
. end (), ' ' , '_' );
428 // trying to use udev first, and if it doesn't work, we fall back to
429 // reading /sys/block/$devname/device/(vendor/model/serial).
430 std :: string
get_device_id ( const std :: string
& devname
,
433 struct udev_device
* dev
;
434 static struct udev
* udev
;
440 * err
= "udev_new failed" ;
444 dev
= udev_device_new_from_subsystem_sysname ( udev
, "block" , devname
. c_str ());
447 * err
= std :: string ( "udev_device_new_from_subsystem_sysname failed on '" )
455 // NOTE: please keep this implementation in sync with _get_device_id() in
456 // src/ceph-volume/ceph_volume/util/device.py
459 std :: string id_vendor
, id_model
, id_serial
, id_serial_short
, id_scsi_serial
;
460 data
= udev_device_get_property_value ( dev
, "ID_VENDOR" );
464 data
= udev_device_get_property_value ( dev
, "ID_MODEL" );
467 // sometimes, ID_MODEL is "LVM ..." but ID_MODEL_ENC is correct (but
468 // encoded with \x20 for space).
469 if ( id_model
. substr ( 0 , 7 ) == "LVM PV " ) {
470 const char * enc
= udev_device_get_property_value ( dev
, "ID_MODEL_ENC" );
472 id_model
= _decode_model_enc ( enc
);
474 // ignore ID_MODEL then
479 data
= udev_device_get_property_value ( dev
, "ID_SERIAL_SHORT" );
481 id_serial_short
= data
;
483 data
= udev_device_get_property_value ( dev
, "ID_SCSI_SERIAL" );
485 id_scsi_serial
= data
;
487 data
= udev_device_get_property_value ( dev
, "ID_SERIAL" );
491 udev_device_unref ( dev
);
494 // ID_SERIAL is usually $vendor_$model_$serial, but not always
495 // ID_SERIAL_SHORT is mostly always just the serial
496 // ID_MODEL is sometimes $vendor_$model, but
497 // ID_VENDOR is sometimes $vendor and ID_MODEL just $model and ID_SCSI_SERIAL the real serial number, with ID_SERIAL and ID_SERIAL_SHORT gibberish (ick)
498 std :: string device_id
;
499 if ( id_vendor
. size () && id_model
. size () && id_scsi_serial
. size ()) {
500 device_id
= id_vendor
+ '_' + id_model
+ '_' + id_scsi_serial
;
501 } else if ( id_model
. size () && id_serial_short
. size ()) {
502 device_id
= id_model
+ '_' + id_serial_short
;
503 } else if ( id_serial
. size ()) {
504 device_id
= id_serial
;
505 if ( device_id
. substr ( 0 , 4 ) == "MTFD" ) {
506 // Micron NVMes hide the vendor
507 device_id
= "Micron_" + device_id
;
510 if ( device_id
. size ()) {
511 std :: replace ( device_id
. begin (), device_id
. end (), ' ' , '_' );
515 // either udev_device_get_property_value() failed, or succeeded but
516 // returned nothing; trying to read from files. note that the 'vendor'
517 // file rarely contains the actual vendor; it's usually 'ATA'.
518 std :: string model
, serial
;
519 char buf
[ 1024 ] = { 0 };
520 BlkDev
blkdev ( devname
);
521 if (! blkdev
. model ( buf
, sizeof ( buf
))) {
524 if (! blkdev
. serial ( buf
, sizeof ( buf
))) {
528 if ( model
. empty () && serial
. empty ()) {
529 * err
= std :: string ( "fallback method has no model nor serial'" );
531 } else if ( model
. empty ()) {
532 * err
= std :: string ( "fallback method has serial '" ) + serial
535 } else if ( serial
. empty ()) {
536 * err
= std :: string ( "fallback method has model '" ) + model
537 + "' but no serial'" ;
542 device_id
= model
+ "_" + serial
;
543 std :: replace ( device_id
. begin (), device_id
. end (), ' ' , '_' );
547 static std :: string
get_device_vendor ( const std :: string
& devname
)
549 struct udev_device
* dev
;
550 static struct udev
* udev
;
557 dev
= udev_device_new_from_subsystem_sysname ( udev
, "block" , devname
. c_str ());
563 std :: string id_vendor
, id_model
;
564 data
= udev_device_get_property_value ( dev
, "ID_VENDOR" );
568 data
= udev_device_get_property_value ( dev
, "ID_MODEL" );
572 udev_device_unref ( dev
);
575 std :: transform ( id_vendor
. begin (), id_vendor
. end (), id_vendor
. begin (),
577 std :: transform ( id_model
. begin (), id_model
. end (), id_model
. begin (),
580 if ( id_vendor
. size ()) {
583 if ( id_model
. size ()) {
584 int pos
= id_model
. find ( " " );
586 return id_model
. substr ( 0 , pos
);
592 std :: string vendor
, model
;
593 char buf
[ 1024 ] = { 0 };
594 BlkDev
blkdev ( devname
);
595 if (! blkdev
. vendor ( buf
, sizeof ( buf
))) {
598 if (! blkdev
. model ( buf
, sizeof ( buf
))) {
605 int pos
= model
. find ( " " );
607 return model
. substr ( 0 , pos
);
616 static int block_device_run_vendor_nvme (
617 const string
& devname
, const string
& vendor
, int timeout
,
620 string device
= "/dev/" + devname
;
622 SubProcessTimed
nvmecli (
623 "sudo" , SubProcess :: CLOSE
, SubProcess :: PIPE
, SubProcess :: CLOSE
,
625 nvmecli
. add_cmd_args (
632 int ret
= nvmecli
. spawn ();
634 * result
= std :: string ( "error spawning nvme command: " ) + nvmecli
. err ();
639 ret
= output
. read_fd ( nvmecli
. get_stdout (), 100 * 1024 );
642 err
. read_fd ( nvmecli
. get_stderr (), 100 * 1024 );
643 * result
= std :: string ( "failed to execute nvme: " ) + err
. to_str ();
646 * result
= output
. to_str ();
649 if ( nvmecli
. join () != 0 ) {
650 * result
= std :: string ( "nvme returned an error: " ) + nvmecli
. err ();
657 std :: string
get_device_path ( const std :: string
& devname
,
660 std :: set
< std :: string
> links
;
661 int r
= easy_readdir ( "/dev/disk/by-path" , & links
);
663 * err
= "unable to list contents of /dev/disk/by-path: " s
+
667 for ( auto & i
: links
) {
669 char target
[ PATH_MAX
+ 1 ];
670 snprintf ( fn
, sizeof ( fn
), "/dev/disk/by-path/%s" , i
. c_str ());
671 int r
= readlink ( fn
, target
, sizeof ( target
));
672 if ( r
< 0 || r
>= ( int ) sizeof ( target
))
675 if (( unsigned ) r
> devname
. size () + 1 &&
676 strncmp ( target
+ r
- devname
. size (), devname
. c_str (), r
) == 0 &&
677 target
[ r
- devname
. size () - 1 ] == '/' ) {
681 * err
= "no symlink to " s
+ devname
+ " in /dev/disk/by-path" ;
685 static int block_device_run_smartctl ( const string
& devname
, int timeout
,
688 string device
= "/dev/" + devname
;
690 // when using --json, smartctl will report its errors in JSON format to stdout
691 SubProcessTimed
smartctl (
692 "sudo" , SubProcess :: CLOSE
, SubProcess :: PIPE
, SubProcess :: CLOSE
,
694 smartctl
. add_cmd_args (
702 int ret
= smartctl
. spawn ();
704 * result
= std :: string ( "error spawning smartctl: " ) + smartctl
. err ();
709 ret
= output
. read_fd ( smartctl
. get_stdout (), 100 * 1024 );
711 * result
= std :: string ( "failed read smartctl output: " ) + cpp_strerror (- ret
);
714 * result
= output
. to_str ();
717 int joinerr
= smartctl
. join ();
718 // Bit 0: Command line did not parse.
719 // Bit 1: Device open failed, device did not return an IDENTIFY DEVICE structure, or device is in a low-power mode (see '-n' option above).
720 // Bit 2: Some SMART or other ATA command to the disk failed, or there was a checksum error in a SMART data structure (see '-b' option above).
721 // Bit 3: SMART status check returned "DISK FAILING".
722 // Bit 4: We found prefail Attributes <= threshold.
723 // Bit 5: SMART status check returned "DISK OK" but we found that some (usage or prefail) Attributes have been <= threshold at some time in the past.
724 // Bit 6: The device error log contains records of errors.
725 // Bit 7: The device self-test log contains records of errors. [ATA only] Failed self-tests outdated by a newer successful extended self-test are ignored.
727 * result
= "smartctl returned an error (" s
+ stringify ( joinerr
) +
728 "): stderr: \n " s
+ smartctl
. err () + " \n stdout: \n " s
+ * result
;
735 static std :: string
escape_quotes ( const std :: string
& s
)
738 auto pos
= r
. find ( " \" " );
739 while ( pos
!= std :: string :: npos
) {
740 r
. replace ( pos
, 1 , " \" " );
741 pos
= r
. find ( " \" " , pos
+ 1 );
746 int block_device_get_metrics ( const string
& devname
, int timeout
,
747 json_spirit :: mValue
* result
)
752 if ( int r
= block_device_run_smartctl ( devname
, timeout
, & s
);
755 s
= "{ \" error \" : \" smartctl failed \" , \" dev \" : \" /dev/" ;
757 s
+= " \" , \" smartctl_error_code \" : " + stringify ( r
);
758 s
+= ", \" smartctl_output \" : \" " + escape_quotes ( orig
);
760 } else if (! json_spirit :: read ( s
, * result
)) {
762 s
= "{ \" error \" : \" smartctl returned invalid JSON \" , \" dev \" : \" /dev/" ;
764 s
+= " \" , \" output \" : \" " ;
765 s
+= escape_quotes ( orig
);
768 if (! json_spirit :: read ( s
, * result
)) {
772 json_spirit :: mObject
& base
= result
-> get_obj ();
773 string vendor
= get_device_vendor ( devname
);
775 base
[ "nvme_vendor" ] = vendor
;
777 json_spirit :: mValue nvme_json
;
778 if ( int r
= block_device_run_vendor_nvme ( devname
, vendor
, timeout
, & s
);
780 if ( json_spirit :: read ( s
, nvme_json
) != 0 ) {
781 base
[ "nvme_smart_health_information_add_log" ] = nvme_json
;
783 base
[ "nvme_smart_health_information_add_log_error" ] = "bad json output: "
787 base
[ "nvme_smart_health_information_add_log_error_code" ] = r
;
788 base
[ "nvme_smart_health_information_add_log_error" ] = s
;
791 base
[ "nvme_vendor" ] = "unknown" ;
797 #elif defined(__APPLE__)
798 #include <sys/disk.h>
800 const char * BlkDev :: sysfsdir () const {
801 assert ( false ); // Should never be called on Apple
805 int BlkDev :: dev ( char * dev
, size_t max
) const
809 if ( fstat ( fd
, & sb
) < 0 )
812 snprintf ( dev
, max
, "%" PRIu64
, ( uint64_t ) sb
. st_rdev
);
817 int BlkDev :: get_size ( int64_t * psize
) const
819 unsigned long blocksize
= 0 ;
820 int ret
= :: ioctl ( fd
, DKIOCGETBLOCKSIZE
, & blocksize
);
822 unsigned long nblocks
;
823 ret
= :: ioctl ( fd
, DKIOCGETBLOCKCOUNT
, & nblocks
);
825 * psize
= ( int64_t ) nblocks
* blocksize
;
832 int64_t BlkDev :: get_int_property ( const char * prop
) const
837 bool BlkDev :: support_discard () const
842 int BlkDev :: discard ( int64_t offset
, int64_t len
) const
847 bool BlkDev :: is_rotational () const
852 int BlkDev :: get_numa_node ( int * node
) const
857 int BlkDev :: model ( char * model
, size_t max
) const
862 int BlkDev :: serial ( char * serial
, size_t max
) const
867 int BlkDev :: partition ( char * partition
, size_t max
) const
872 int BlkDev :: wholedisk ( char * device
, size_t max
) const
877 void get_dm_parents ( const std :: string
& dev
, std :: set
< std :: string
> * ls
)
881 void get_raw_devices ( const std :: string
& in
,
882 std :: set
< std :: string
> * ls
)
886 int get_vdo_stats_handle ( const char * devname
, std :: string
* vdo_name
)
891 int64_t get_vdo_stat ( int fd
, const char * property
)
896 bool get_vdo_utilization ( int fd
, uint64_t * total
, uint64_t * avail
)
901 std :: string
get_device_id ( const std :: string
& devname
,
904 // FIXME: implement me
906 * err
= "not implemented" ;
908 return std :: string ();
911 std :: string
get_device_path ( const std :: string
& devname
,
914 // FIXME: implement me
916 * err
= "not implemented" ;
918 return std :: string ();
921 #elif defined(__FreeBSD__)
923 const char * BlkDev :: sysfsdir () const {
924 assert ( false ); // Should never be called on FreeBSD
928 int BlkDev :: dev ( char * dev
, size_t max
) const
932 if ( fstat ( fd
, & sb
) < 0 )
935 snprintf ( dev
, max
, "%" PRIu64
, ( uint64_t ) sb
. st_rdev
);
940 int BlkDev :: get_size ( int64_t * psize
) const
942 int ret
= :: ioctl ( fd
, DIOCGMEDIASIZE
, psize
);
948 int64_t BlkDev :: get_int_property ( const char * prop
) const
953 bool BlkDev :: support_discard () const
955 #ifdef FREEBSD_WITH_TRIM
956 // there is no point to claim support of discard, but
958 struct diocgattr_arg arg
;
960 strlcpy ( arg
. name
, "GEOM::candelete" , sizeof ( arg
. name
));
961 arg
. len
= sizeof ( arg
. value
. i
);
962 if ( ioctl ( fd
, DIOCGATTR
, & arg
) == 0 ) {
963 return ( arg
. value
. i
!= 0 );
971 int BlkDev :: discard ( int64_t offset
, int64_t len
) const
976 bool BlkDev :: is_rotational () const
978 #if __FreeBSD_version >= 1200049
979 struct diocgattr_arg arg
;
981 strlcpy ( arg
. name
, "GEOM::rotation_rate" , sizeof ( arg
. name
));
982 arg
. len
= sizeof ( arg
. value
. u16
);
984 int ioctl_ret
= ioctl ( fd
, DIOCGATTR
, & arg
);
986 if ( ioctl_ret
< 0 || arg
. value
. u16
== DISK_RR_UNKNOWN
)
987 // DISK_RR_UNKNOWN usually indicates an old drive, which is usually spinny
989 else if ( arg
. value
. u16
== DISK_RR_NON_ROTATING
)
991 else if ( arg
. value
. u16
>= DISK_RR_MIN
&& arg
. value
. u16
<= DISK_RR_MAX
)
994 ret
= true ; // Invalid value. Probably spinny?
998 return true ; // When in doubt, it's probably spinny
1002 int BlkDev :: get_numa_node ( int * node
) const
1004 int numa
= get_int_property ( "device/device/numa_node" );
1011 int BlkDev :: model ( char * model
, size_t max
) const
1013 struct diocgattr_arg arg
;
1015 strlcpy ( arg
. name
, "GEOM::descr" , sizeof ( arg
. name
));
1016 arg
. len
= sizeof ( arg
. value
. str
);
1017 if ( ioctl ( fd
, DIOCGATTR
, & arg
) < 0 ) {
1021 // The GEOM description is of the form "vendor product" for SCSI disks
1022 // and "ATA device_model" for ATA disks. Some vendors choose to put the
1023 // vendor name in device_model, and some don't. Strip the first bit.
1024 char * p
= arg
. value
. str
;
1025 if ( p
== NULL
|| * p
== '\0' ) {
1028 ( void ) strsep (& p
, " " );
1029 snprintf ( model
, max
, "%s" , p
);
1035 int BlkDev :: serial ( char * serial
, size_t max
) const
1037 char ident
[ DISK_IDENT_SIZE
];
1039 if ( ioctl ( fd
, DIOCGIDENT
, ident
) < 0 )
1042 snprintf ( serial
, max
, "%s" , ident
);
1047 void get_dm_parents ( const std :: string
& dev
, std :: set
< std :: string
> * ls
)
1051 void get_raw_devices ( const std :: string
& in
,
1052 std :: set
< std :: string
> * ls
)
1056 int get_vdo_stats_handle ( const char * devname
, std :: string
* vdo_name
)
1061 int64_t get_vdo_stat ( int fd
, const char * property
)
1066 bool get_vdo_utilization ( int fd
, uint64_t * total
, uint64_t * avail
)
1071 std :: string
get_device_id ( const std :: string
& devname
,
1074 // FIXME: implement me for freebsd
1076 * err
= "not implemented for FreeBSD" ;
1078 return std :: string ();
1081 std :: string
get_device_path ( const std :: string
& devname
,
1084 // FIXME: implement me for freebsd
1086 * err
= "not implemented for FreeBSD" ;
1088 return std :: string ();
1091 int block_device_run_smartctl ( const char * device
, int timeout
,
1092 std :: string
* result
)
1094 // FIXME: implement me for freebsd
1098 int block_device_get_metrics ( const string
& devname
, int timeout
,
1099 json_spirit :: mValue
* result
)
1101 // FIXME: implement me for freebsd
1105 int block_device_run_nvme ( const char * device
, const char * vendor
, int timeout
,
1106 std :: string
* result
)
1111 static int block_device_devname ( int fd
, char * devname
, size_t max
)
1113 struct fiodgname_arg arg
;
1117 if ( ioctl ( fd
, FIODGNAME
, & arg
) < 0 )
1122 int BlkDev :: partition ( char * partition
, size_t max
) const
1124 char devname
[ PATH_MAX
];
1126 if ( block_device_devname ( fd
, devname
, sizeof ( devname
)) < 0 )
1128 snprintf ( partition
, max
, "/dev/%s" , devname
);
1132 int BlkDev :: wholedisk ( char * wd
, size_t max
) const
1134 char devname
[ PATH_MAX
];
1136 if ( block_device_devname ( fd
, devname
, sizeof ( devname
)) < 0 )
1139 size_t first_digit
= strcspn ( devname
, "0123456789" );
1140 // first_digit now indexes the first digit or null character of devname
1141 size_t next_nondigit
= strspn (& devname
[ first_digit
], "0123456789" );
1142 next_nondigit
+= first_digit
;
1143 // next_nondigit now indexes the first alphabetic or null character after the
1145 strlcpy ( wd
, devname
, next_nondigit
+ 1 );
1151 const char * BlkDev :: sysfsdir () const {
1152 assert ( false ); // Should never be called on non-Linux
1156 int BlkDev :: dev ( char * dev
, size_t max
) const
1161 int BlkDev :: get_size ( int64_t * psize
) const
1166 bool BlkDev :: support_discard () const
1171 int BlkDev :: discard ( int fd
, int64_t offset
, int64_t len
) const
1176 bool BlkDev :: is_rotational ( const char * devname
) const
1181 int BlkDev :: model ( char * model
, size_t max
) const
1186 int BlkDev :: serial ( char * serial
, size_t max
) const
1191 int BlkDev :: partition ( char * partition
, size_t max
) const
1196 int BlkDev :: wholedisk ( char * wd
, size_t max
) const
1201 void get_dm_parents ( const std :: string
& dev
, std :: set
< std :: string
> * ls
)
1205 void get_raw_devices ( const std :: string
& in
,
1206 std :: set
< std :: string
> * ls
)
1210 int get_vdo_stats_handle ( const char * devname
, std :: string
* vdo_name
)
1215 int64_t get_vdo_stat ( int fd
, const char * property
)
1220 bool get_vdo_utilization ( int fd
, uint64_t * total
, uint64_t * avail
)
1225 std :: string
get_device_id ( const std :: string
& devname
,
1230 * err
= "not implemented" ;
1232 return std :: string ();
1235 std :: string
get_device_path ( const std :: string
& devname
,
1240 * err
= "not implemented" ;
1242 return std :: string ();
1245 int block_device_run_smartctl ( const char * device
, int timeout
,
1246 std :: string
* result
)
1251 int block_device_get_metrics ( const string
& devname
, int timeout
,
1252 json_spirit :: mValue
* result
)
1257 int block_device_run_nvme ( const char * device
, const char * vendor
, int timeout
,
1258 std :: string
* result
)
1267 void get_device_metadata (
1268 const std :: set
< std :: string
>& devnames
,
1269 std :: map
< std :: string
, std :: string
> * pm
,
1270 std :: map
< std :: string
, std :: string
> * errs
)
1272 (* pm
)[ "devices" ] = stringify ( devnames
);
1273 string
& devids
= (* pm
)[ "device_ids" ];
1274 string
& devpaths
= (* pm
)[ "device_paths" ];
1275 for ( auto & dev
: devnames
) {
1277 string id
= get_device_id ( dev
, & err
);
1279 if (! devids
. empty ()) {
1282 devids
+= dev
+ "=" + id
;
1284 (* errs
)[ dev
] = " no unique device id for " s
+ dev
+ ": " + err
;
1286 string path
= get_device_path ( dev
, & err
);
1288 if (! devpaths
. empty ()) {
1291 devpaths
+= dev
+ "=" + path
;
1293 (* errs
)[ dev
] + " no unique device path for " s
+ dev
+ ": " + err
;