1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-21 13:34:40 +03:00

device id wwid adjustments

Move the functions handling dev wwids.

Add dev flags indicating that wwids have been read from
sysfs wwid file or sysfs vpd_pg83 file.  This can be
used to avoid rereading these.

Improve filter-mpath search for a device's wwid in
/etc/multipath/wwids, to avoid unnecessary rereading
of wwids from sysfs files.

Type 8 wwids from vpd_pg83 with naa or eui names should be
saved as those types.
This commit is contained in:
David Teigland 2022-08-30 15:10:52 -05:00
parent 39e6c4f749
commit 380ab3f45c
5 changed files with 250 additions and 198 deletions

View File

@ -601,114 +601,12 @@ static int _dev_is_mpath_component_sysfs(struct cmd_context *cmd, struct device
return is_mpath_component;
}
static int _read_sys_wwid(struct cmd_context *cmd, struct device *dev,
char *idbuf, int idbufsize)
{
char idtmp[DEV_WWID_SIZE];
if (!read_sys_block(cmd, dev, "device/wwid", idbuf, idbufsize)) {
/* the wwid file is not under device for nvme devs */
if (!read_sys_block(cmd, dev, "wwid", idbuf, idbufsize))
return 0;
}
if (!idbuf[0])
return 0;
/* in t10 id, replace series of spaces with one _ like multipath */
if (!strncmp(idbuf, "t10.", 4) && strchr(idbuf, ' ')) {
if (idbufsize < DEV_WWID_SIZE)
return 0;
memcpy(idtmp, idbuf, DEV_WWID_SIZE);
memset(idbuf, 0, idbufsize);
format_t10_id((const unsigned char *)idtmp, DEV_WWID_SIZE, (unsigned char *)idbuf, idbufsize);
}
return 1;
}
#define VPD_SIZE 4096
static int _read_sys_vpd_wwids(struct cmd_context *cmd, struct device *dev,
struct dm_list *ids)
{
unsigned char vpd_data[VPD_SIZE] = { 0 };
int vpd_datalen = 0;
if (!read_sys_block_binary(cmd, dev, "device/vpd_pg83", (char *)vpd_data, VPD_SIZE, &vpd_datalen))
return 0;
if (!vpd_datalen)
return 0;
/* adds dev_wwid entry to dev->wwids for each id in vpd data */
parse_vpd_ids(vpd_data, vpd_datalen, ids);
return 1;
}
void free_wwids(struct dm_list *ids)
{
struct dev_wwid *dw, *safe;
dm_list_iterate_items_safe(dw, safe, ids) {
dm_list_del(&dw->list);
free(dw);
}
}
static int _wwid_type_num(char *id)
{
if (!strncmp(id, "naa.", 4))
return 3;
else if (!strncmp(id, "eui.", 4))
return 2;
else if (!strncmp(id, "t10.", 4))
return 1;
else
return -1;
}
/*
* TODO: if each of the different wwid types (naa/eui/t10) were
* represented by different DEV_ID_TYPE_FOO values, and used
* as device_id types, then we could drop struct dev_wwid and
* drop dev->wwids, and just use dev->ids for each of the
* different wwids found in vpd_pg83. This would also require
* the ability to handle both the original method of replacing
* every space in the id string with _ and the new/multipath
* format_t10_id replacing series of spaces with one _.
*/
struct dev_wwid *add_wwid(char *id, int id_type, struct dm_list *ids)
{
struct dev_wwid *dw;
int len;
if (!id_type) {
id_type = _wwid_type_num(id);
if (id_type == -1)
log_debug("unknown wwid type %s", id);
}
if (!(dw = zalloc(sizeof(struct dev_wwid))))
return NULL;
len = strlen(id);
if (len >= DEV_WWID_SIZE)
len = DEV_WWID_SIZE - 1;
memcpy(dw->id, id, len);
dw->type = id_type;
dm_list_add(ids, &dw->list);
return dw;
}
/*
* we save ids with format: naa.<value>, eui.<value>, t10.<value>.
* multipath wwids file uses format: 3<value>, 2<value>, 1<value>.
* The values are saved in wwid_hash_tab without the type prefix.
*/
static int _dev_in_wwid_file(struct cmd_context *cmd, struct device *dev,
int primary_result, dev_t primary_dev)
{
char idbuf[DEV_WWID_SIZE] = { 0 };
struct dev_wwid *dw;
char *wwid;
char *wwid, *full_wwid;
if (!_wwid_hash_tab)
return 0;
@ -724,29 +622,17 @@ static int _dev_in_wwid_file(struct cmd_context *cmd, struct device *dev,
}
/*
* This function may be called multiple times for the same device, in
* particular if partitioned for each partition.
* sysfs wwid uses format: naa.<value>, eui.<value>, t10.<value>.
* multipath wwids file uses format: 3<value>, 2<value>, 1<value>.
*
* We omit the type prefix before looking up. The multipath/wwids
* values in the wwid_hash_tab have the initial character removed.
*
* There's no type prefix for "scsi name string" type 8 ids.
*
* First try looking up any wwids that have already been read.
*/
if (!dm_list_empty(&dev->wwids))
goto lookup;
/*
* Get all the ids for the device from vpd_pg83 and check if any of
* those are in /etc/multipath/wwids. These ids should include the
* value printed from the sysfs wwid file.
*/
_read_sys_vpd_wwids(cmd, dev, &dev->wwids);
if (!dm_list_empty(&dev->wwids))
goto lookup;
/*
* This will read the sysfs wwid file, nvme devices in particular have
* a wwid file but not a vpd_pg83 file.
*/
if (_read_sys_wwid(cmd, dev, idbuf, sizeof(idbuf)))
add_wwid(idbuf, 0, &dev->wwids);
lookup:
lookup:
dm_list_iterate_items(dw, &dev->wwids) {
if (dw->type == 1 || dw->type == 2 || dw->type == 3)
wwid = &dw->id[4];
@ -754,12 +640,36 @@ static int _dev_in_wwid_file(struct cmd_context *cmd, struct device *dev,
wwid = dw->id;
if (dm_hash_lookup_binary(_wwid_hash_tab, wwid, strlen(wwid))) {
log_debug_devs("dev_is_mpath_component %s %s in wwids file", dev_name(dev), dw->id);
return 1;
full_wwid = dw->id;
goto found;
}
}
/*
* The id from sysfs wwid may not be the id used by multipath,
* or a device may not have a vpd_pg83 file (e.g. nvme).
*/
if (!(dev->flags & DEV_ADDED_VPD_WWIDS) && dev_read_vpd_wwids(cmd, dev))
goto lookup;
if (!(dev->flags & DEV_ADDED_SYS_WWID) && dev_read_sys_wwid(cmd, dev, idbuf, sizeof(idbuf), &dw)) {
if (dw->type == 1 || dw->type == 2 || dw->type == 3)
wwid = &dw->id[4];
else
wwid = dw->id;
if (dm_hash_lookup_binary(_wwid_hash_tab, wwid, strlen(wwid))) {
full_wwid = dw->id;
goto found;
}
}
return 0;
found:
log_debug_devs("dev_is_mpath_component %s %s in wwids file", dev_name(dev), full_wwid);
return 1;
}
int dev_is_mpath_component(struct cmd_context *cmd, struct device *dev, dev_t *holder_devno)

View File

@ -32,8 +32,8 @@
#define DEV_NOT_O_NOATIME 0x00000400 /* Don't use O_NOATIME */
#define DEV_IN_BCACHE 0x00000800 /* dev fd is open and used in bcache */
#define DEV_BCACHE_EXCL 0x00001000 /* bcache_fd should be open EXCL */
/* unused 0x00002000 */
/* unused 0x00004000 */
#define DEV_ADDED_SYS_WWID 0x00002000 /* wwid has been added from sysfs wwid file */
#define DEV_ADDED_VPD_WWIDS 0x00004000 /* wwids have been added from vpd_pg83 */
#define DEV_BCACHE_WRITE 0x00008000 /* bcache_fd is open with RDWR */
#define DEV_SCAN_FOUND_LABEL 0x00010000 /* label scan read dev and found label */
#define DEV_IS_MD_COMPONENT 0x00020000 /* device is an md component */
@ -59,22 +59,29 @@ struct dev_ext {
void *handle;
};
#define DEV_ID_TYPE_SYS_WWID 1
#define DEV_ID_TYPE_SYS_SERIAL 2
#define DEV_ID_TYPE_MPATH_UUID 3
#define DEV_ID_TYPE_MD_UUID 4
#define DEV_ID_TYPE_LOOP_FILE 5
#define DEV_ID_TYPE_CRYPT_UUID 6
#define DEV_ID_TYPE_LVMLV_UUID 7
#define DEV_ID_TYPE_DEVNAME 8
#define DEV_WWID_SIZE 128
struct dev_wwid {
struct dm_list list;
int type;
char id[DEV_WWID_SIZE];
};
/*
* A wwid read from:
* /sys/dev/block/%d:%d/device/wwid
* /sys/dev/block/%d:%d/wwid
* /sys/dev/block/%d:%d/device/vpd_pg83
*/
#define DEV_ID_TYPE_SYS_WWID 0x0001
#define DEV_ID_TYPE_SYS_SERIAL 0x0002
#define DEV_ID_TYPE_MPATH_UUID 0x0003
#define DEV_ID_TYPE_MD_UUID 0x0004
#define DEV_ID_TYPE_LOOP_FILE 0x0005
#define DEV_ID_TYPE_CRYPT_UUID 0x0006
#define DEV_ID_TYPE_LVMLV_UUID 0x0007
#define DEV_ID_TYPE_DEVNAME 0x0008
struct dev_wwid {
struct dm_list list; /* dev->wwids */
int type; /* 1,2,3 for NAA,EUI,T10 */
char id[DEV_WWID_SIZE]; /* includes prefix naa.,eui.,t10. */
};
/*
* A device ID of a certain type for a device.
@ -83,7 +90,7 @@ struct dev_wwid {
*/
struct dev_id {
struct dm_list list;
struct dm_list list; /* dev->ids */
struct device *dev;
uint16_t idtype; /* DEV_ID_TYPE_ */
char *idname; /* id string determined by idtype */
@ -215,8 +222,6 @@ void dev_destroy_file(struct device *dev);
int dev_mpath_init(const char *config_wwids_file);
void dev_mpath_exit(void);
struct dev_wwid *add_wwid(char *id, int id_type, struct dm_list *ids);
void free_wwids(struct dm_list *ids);
int parse_vpd_ids(const unsigned char *vpd_data, int vpd_datalen, struct dm_list *ids);
int format_t10_id(const unsigned char *in, int in_bytes, unsigned char *out, int out_bytes);

View File

@ -168,7 +168,8 @@ void free_dus(struct dm_list *dus)
void free_did(struct dev_id *id)
{
free(id->idname);
if (strlen(id->idname))
free(id->idname); /* idname = "" when id type doesn't exist */
free(id);
}
@ -321,6 +322,106 @@ static int _dev_has_lvmlv_uuid(struct cmd_context *cmd, struct device *dev, cons
return 1;
}
/*
* The numbers 1,2,3 for NAA,EUI,T10 are part of the standard
* and are used in the vpd data.
*/
static int _wwid_type_num(char *id)
{
if (!strncmp(id, "naa.", 4))
return 3;
else if (!strncmp(id, "eui.", 4))
return 2;
else if (!strncmp(id, "t10.", 4))
return 1;
else
return -1;
}
void free_wwids(struct dm_list *ids)
{
struct dev_wwid *dw, *safe;
dm_list_iterate_items_safe(dw, safe, ids) {
dm_list_del(&dw->list);
free(dw);
}
}
/*
* wwid type 8 "scsi name string" (which includes "iqn" names) is
* included in vpd_pg83, but we currently do not use these for
* device ids (maybe in the future.)
* They can still be checked by dev-mpath when looking for a device
* in /etc/multipath/wwids.
*/
struct dev_wwid *dev_add_wwid(char *id, int id_type, struct dm_list *ids)
{
struct dev_wwid *dw;
int len;
if (!id_type) {
id_type = _wwid_type_num(id);
if (id_type == -1)
log_debug("unknown wwid type %s", id);
}
if (!(dw = zalloc(sizeof(struct dev_wwid))))
return NULL;
len = strlen(id);
if (len >= DEV_WWID_SIZE)
len = DEV_WWID_SIZE - 1;
memcpy(dw->id, id, len);
dw->type = id_type;
dm_list_add(ids, &dw->list);
return dw;
}
#define VPD_SIZE 4096
int dev_read_vpd_wwids(struct cmd_context *cmd, struct device *dev)
{
unsigned char vpd_data[VPD_SIZE] = { 0 };
int vpd_datalen = 0;
dev->flags |= DEV_ADDED_VPD_WWIDS;
if (!read_sys_block_binary(cmd, dev, "device/vpd_pg83", (char *)vpd_data, VPD_SIZE, &vpd_datalen))
return 0;
if (!vpd_datalen)
return 0;
/* adds dev_wwid entry to dev->wwids for each id in vpd data */
parse_vpd_ids(vpd_data, vpd_datalen, &dev->wwids);
return 1;
}
int dev_read_sys_wwid(struct cmd_context *cmd, struct device *dev,
char *buf, int bufsize, struct dev_wwid **dw_out)
{
struct dev_wwid *dw;
int ret;
dev->flags |= DEV_ADDED_SYS_WWID;
ret = read_sys_block(cmd, dev, "device/wwid", buf, bufsize);
if (!ret || !buf[0]) {
/* the wwid file is not under device for nvme devs */
ret = read_sys_block(cmd, dev, "wwid", buf, bufsize);
}
if (!ret || !buf[0])
return 0;
/* Note, if wwids are also read from vpd, this same wwid will be added again. */
if (!(dw = dev_add_wwid(buf, 0, &dev->wwids)))
return_0;
if (dw_out)
*dw_out = dw;
return 1;
}
const char *device_id_system_read(struct cmd_context *cmd, struct device *dev, uint16_t idtype)
{
char sysbuf[PATH_MAX] = { 0 };
@ -328,10 +429,9 @@ const char *device_id_system_read(struct cmd_context *cmd, struct device *dev, u
int i;
if (idtype == DEV_ID_TYPE_SYS_WWID) {
read_sys_block(cmd, dev, "device/wwid", sysbuf, sizeof(sysbuf));
dev_read_sys_wwid(cmd, dev, sysbuf, sizeof(sysbuf), NULL);
if (!sysbuf[0])
read_sys_block(cmd, dev, "wwid", sysbuf, sizeof(sysbuf));
/* FIXME: enable these QEMU t10 wwids */
/* qemu wwid begins "t10.ATA QEMU HARDDISK ..." */
if (strstr(sysbuf, "QEMU HARDDISK"))
@ -450,6 +550,11 @@ static int _dev_has_stable_id(struct cmd_context *cmd, struct device *dev)
read_sys_block(cmd, dev, "md/uuid", sysbuf, sizeof(sysbuf)))
return 1;
if (!(dev->flags & DEV_ADDED_VPD_WWIDS))
dev_read_vpd_wwids(cmd, dev);
if (!dm_list_empty(&dev->wwids))
return 1;
out:
/* DEV_ID_TYPE_DEVNAME would be used for this dev. */
return 0;
@ -1012,14 +1117,18 @@ int device_id_add(struct cmd_context *cmd, struct device *dev, const char *pvid_
/*
* Choose the device_id type for the device being added.
*
* 0. use an idtype specified by the user
* 1. use an idtype specific to a special/virtual device type
* e.g. loop, mpath, crypt, lvmlv, md, etc.
* 2. use an idtype specified by user option.
* 3. use sys_wwid, if it exists.
* 4. use sys_serial, if it exists.
* 5. use devname as the last resort.
* possible breakage:
* . if the kernel changes what it prints from sys/wwid (e.g. from
* the t10 value to the naa value for the dev), this would break
* matching du to dev unless lvm tries to match all of the dev's
* different wwids from vpd_pg83 against sys_wwid entries.
* . adding a new device_id type into the devices file breaks prior
* lvm versions that attempt to use the devices file from the new
* lvm version.
* . using a value for sys_wwid that comes from vpd_pg83 and not
* sys/wwid (e.g. taking a naa wwid from vpd_pg83 when sys/wwid
* is printing the t10 wwid) would break prior lvm versions that
* only match a du against the sys/wwid values.
*/
if (idtype_arg) {
@ -1060,36 +1169,44 @@ int device_id_add(struct cmd_context *cmd, struct device *dev, const char *pvid_
/* TODO: kpartx partitions on loop devs. */
if (MAJOR(dev->dev) == cmd->dev_types->loop_major) {
idtype = DEV_ID_TYPE_LOOP_FILE;
goto id_name;
if ((idname = device_id_system_read(cmd, dev, idtype)))
goto id_done;
goto id_last;
}
if (MAJOR(dev->dev) == cmd->dev_types->md_major) {
idtype = DEV_ID_TYPE_MD_UUID;
goto id_name;
if ((idname = device_id_system_read(cmd, dev, idtype)))
goto id_done;
goto id_last;
}
if (MAJOR(dev->dev) == cmd->dev_types->drbd_major) {
/* TODO */
log_warn("Missing support for DRBD idtype");
goto id_last;
}
/*
* No device-specific, existing, or user-specified idtypes,
* so use first available of sys_wwid / sys_serial / devname.
*/
idtype = DEV_ID_TYPE_SYS_WWID;
id_name:
if (!(idname = device_id_system_read(cmd, dev, idtype))) {
if (idtype == DEV_ID_TYPE_SYS_WWID) {
idtype = DEV_ID_TYPE_SYS_SERIAL;
goto id_name;
}
idtype = DEV_ID_TYPE_DEVNAME;
goto id_name;
}
idtype = DEV_ID_TYPE_SYS_WWID;
if ((idname = device_id_system_read(cmd, dev, idtype)))
goto id_done;
idtype = DEV_ID_TYPE_SYS_SERIAL;
if ((idname = device_id_system_read(cmd, dev, idtype)))
goto id_done;
id_last:
idtype = DEV_ID_TYPE_DEVNAME;
if ((idname = device_id_system_read(cmd, dev, idtype)))
goto id_done;
id_done:
if (!idname)
return_0;
/*
* Create a dev_id struct for the new idtype on dev->ids.
@ -1101,7 +1218,9 @@ id_done:
}
}
if (found_id && idname && strcmp(id->idname, idname)) {
if (found_id && idname && (!id->idname || strcmp(id->idname, idname))) {
log_debug("Replacing device id %s old %s new %s",
idtype_to_str(id->idtype), id->idname ?: ".", idname);
dm_list_del(&id->list);
free_did(id);
found_id = 0;
@ -1433,8 +1552,13 @@ static int _match_dm_devnames(struct cmd_context *cmd, struct device *dev,
}
/*
* check for dev->ids entry with du->idtype, if found compare it,
* if not, system_read of this type and add entry to dev->ids, compare it.
* du is a devices file entry. dev is any device on the system.
* check if du is for dev by comparing the device's ids to du->idname.
*
* check for a dev->ids entry with du->idtype, if found compare it,
* if not, system_read idtype for the dev, add entry to dev->ids,
* compare it to du to check if it matches.
*
* When a match is found, set up links among du/id/dev.
*/
@ -1489,6 +1613,10 @@ static int _match_du_to_dev(struct cmd_context *cmd, struct dev_use *du, struct
return 0;
}
/*
* Try to match du with ids that have already been read for the dev
* (and saved on dev->ids to avoid rereading.)
*/
dm_list_iterate_items(id, &dev->ids) {
if (id->idtype == du->idtype) {
if ((id->idtype == DEV_ID_TYPE_DEVNAME) && _match_dm_devnames(cmd, dev, id, du)) {
@ -1521,32 +1649,20 @@ static int _match_du_to_dev(struct cmd_context *cmd, struct dev_use *du, struct
if (!(id = zalloc(sizeof(struct dev_id))))
return_0;
if (!(idname = device_id_system_read(cmd, dev, du->idtype))) {
/*
* Save a new id in dev->ids for this type to indicate no match
* to avoid repeated system_read, since this called many times.
* Setting idtype and NULL idname means no id of this type.
*/
id->idtype = du->idtype;
id->dev = dev;
dm_list_add(&dev->ids, &id->list);
/*
log_debug("Mismatch device_id %s %s to %s: no idtype",
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev));
*/
return 0;
}
idname = device_id_system_read(cmd, dev, du->idtype);
/*
* Save this id for the device (so it can be quickly checked again), even
* if it's not the idtype used to identify the dev in device_id_file.
* Save this id for the dev, even if it doesn't exist (NULL)
* or doesn't match du. This avoids system_read of this idtype
* repeatedly, and the saved id will be found in the loop
* over dev->ids above.
*/
id->idtype = du->idtype;
id->idname = (char *)idname;
id->idname = (char *)idname ?: (char *)"";
id->dev = dev;
dm_list_add(&dev->ids, &id->list);
if (!strcmp(idname, du->idname)) {
if (idname && !strcmp(idname, du->idname)) {
du->dev = dev;
dev->id = id;
dev->flags |= DEV_MATCHED_USE_ID;
@ -1557,8 +1673,9 @@ static int _match_du_to_dev(struct cmd_context *cmd, struct dev_use *du, struct
/*
log_debug("Mismatch device_id %s %s to %s: idname %s",
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev), idname);
idtype_to_str(du->idtype), du->idname ?: ".", dev_name(dev), idname ?: ".");
*/
return 0;
}

View File

@ -63,4 +63,10 @@ int read_sys_block_binary(struct cmd_context *cmd, struct device *dev,
int dev_has_mpath_uuid(struct cmd_context *cmd, struct device *dev, const char **idname_out);
void free_wwids(struct dm_list *ids);
struct dev_wwid *dev_add_wwid(char *id, int id_type, struct dm_list *ids);
int dev_read_vpd_wwids(struct cmd_context *cmd, struct device *dev);
int dev_read_sys_wwid(struct cmd_context *cmd, struct device *dev,
char *buf, int bufsize, struct dev_wwid **dw_out);
#endif

View File

@ -14,7 +14,9 @@
#include "base/memory/zalloc.h"
#include "lib/misc/lib.h"
#include "lib/commands/toolcontext.h"
#include "lib/device/device.h"
#include "lib/device/device_id.h"
#include <stdio.h>
#include <unistd.h>
@ -98,6 +100,7 @@ int parse_vpd_ids(const unsigned char *vpd_data, int vpd_datalen, struct dm_list
const unsigned char *d, *cur_id_str;
size_t id_len = ID_BUFSIZE;
int id_size = -1;
int type;
uint8_t cur_id_size = 0;
memset(id, 0, ID_BUFSIZE);
@ -119,7 +122,7 @@ int parse_vpd_ids(const unsigned char *vpd_data, int vpd_datalen, struct dm_list
break;
if (id_size >= ID_BUFSIZE)
id_size = ID_BUFSIZE - 1;
add_wwid(id, 1, ids);
dev_add_wwid(id, 1, ids);
break;
case 0x2:
/* EUI-64 */
@ -145,7 +148,7 @@ int parse_vpd_ids(const unsigned char *vpd_data, int vpd_datalen, struct dm_list
break;
if (id_size >= ID_BUFSIZE)
id_size = ID_BUFSIZE - 1;
add_wwid(id, 2, ids);
dev_add_wwid(id, 2, ids);
break;
case 0x3:
/* NAA */
@ -167,7 +170,7 @@ int parse_vpd_ids(const unsigned char *vpd_data, int vpd_datalen, struct dm_list
break;
if (id_size >= ID_BUFSIZE)
id_size = ID_BUFSIZE - 1;
add_wwid(id, 3, ids);
dev_add_wwid(id, 3, ids);
break;
case 0x8:
/* SCSI name string */
@ -178,17 +181,28 @@ int parse_vpd_ids(const unsigned char *vpd_data, int vpd_datalen, struct dm_list
memcpy(id, cur_id_str, cur_id_size);
id_size = cur_id_size;
/*
* if naa or eui ids are provided as scsi names,
* consider them to be naa/eui types.
*/
if (!memcmp(id, "eui.", 4))
type = 2;
else if (!memcmp(id, "naa.", 4))
type = 3;
else
type = 8;
/*
* Not in the kernel version, copying multipath code,
* which checks if this string begins with naa or eui
* and if so does tolower() on the chars.
*/
if (!strncmp(id, "naa.", 4) || !strncmp(id, "eui.", 4)) {
if ((type == 2) || (type == 3)) {
int i;
for (i = 0; i < id_size; i++)
for (i = 0; i < strlen(id); i++)
id[i] = tolower(id[i]);
}
add_wwid(id, 8, ids);
dev_add_wwid(id, type, ids);
break;
default:
break;