libnvdimm: fix namespace object confusion in is_uuid_busy()
When btt devices were re-worked to be child devices of regions this routine was overlooked. It mistakenly attempts to_nd_namespace_pmem() or to_nd_namespace_blk() conversions on btt and pfn devices. By luck to date we have happened to be hitting valid memory leading to a uuid miscompare, but a recent change to struct nd_namespace_common causes: BUG: unable to handle kernel NULL pointer dereference at 0000000000000001 IP: [<ffffffff814610dc>] memcmp+0xc/0x40 [..] Call Trace: [<ffffffffa0028631>] is_uuid_busy+0xc1/0x2a0 [libnvdimm] [<ffffffffa0028570>] ? to_nd_blk_region+0x50/0x50 [libnvdimm] [<ffffffff8158c9c0>] device_for_each_child+0x50/0x90 Cc: <stable@vger.kernel.org> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
This commit is contained in:
parent
0731de0dd9
commit
e07ecd76d4
@ -77,6 +77,59 @@ static bool is_namespace_io(struct device *dev)
|
|||||||
return dev ? dev->type == &namespace_io_device_type : false;
|
return dev ? dev->type == &namespace_io_device_type : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int is_uuid_busy(struct device *dev, void *data)
|
||||||
|
{
|
||||||
|
u8 *uuid1 = data, *uuid2 = NULL;
|
||||||
|
|
||||||
|
if (is_namespace_pmem(dev)) {
|
||||||
|
struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
|
||||||
|
|
||||||
|
uuid2 = nspm->uuid;
|
||||||
|
} else if (is_namespace_blk(dev)) {
|
||||||
|
struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
|
||||||
|
|
||||||
|
uuid2 = nsblk->uuid;
|
||||||
|
} else if (is_nd_btt(dev)) {
|
||||||
|
struct nd_btt *nd_btt = to_nd_btt(dev);
|
||||||
|
|
||||||
|
uuid2 = nd_btt->uuid;
|
||||||
|
} else if (is_nd_pfn(dev)) {
|
||||||
|
struct nd_pfn *nd_pfn = to_nd_pfn(dev);
|
||||||
|
|
||||||
|
uuid2 = nd_pfn->uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uuid2 && memcmp(uuid1, uuid2, NSLABEL_UUID_LEN) == 0)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_namespace_uuid_busy(struct device *dev, void *data)
|
||||||
|
{
|
||||||
|
if (is_nd_pmem(dev) || is_nd_blk(dev))
|
||||||
|
return device_for_each_child(dev, data, is_uuid_busy);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nd_is_uuid_unique - verify that no other namespace has @uuid
|
||||||
|
* @dev: any device on a nvdimm_bus
|
||||||
|
* @uuid: uuid to check
|
||||||
|
*/
|
||||||
|
bool nd_is_uuid_unique(struct device *dev, u8 *uuid)
|
||||||
|
{
|
||||||
|
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
|
||||||
|
|
||||||
|
if (!nvdimm_bus)
|
||||||
|
return false;
|
||||||
|
WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm_bus->dev));
|
||||||
|
if (device_for_each_child(&nvdimm_bus->dev, uuid,
|
||||||
|
is_namespace_uuid_busy) != 0)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool pmem_should_map_pages(struct device *dev)
|
bool pmem_should_map_pages(struct device *dev)
|
||||||
{
|
{
|
||||||
struct nd_region *nd_region = to_nd_region(dev->parent);
|
struct nd_region *nd_region = to_nd_region(dev->parent);
|
||||||
|
@ -134,62 +134,6 @@ int nd_region_to_nstype(struct nd_region *nd_region)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(nd_region_to_nstype);
|
EXPORT_SYMBOL(nd_region_to_nstype);
|
||||||
|
|
||||||
static int is_uuid_busy(struct device *dev, void *data)
|
|
||||||
{
|
|
||||||
struct nd_region *nd_region = to_nd_region(dev->parent);
|
|
||||||
u8 *uuid = data;
|
|
||||||
|
|
||||||
switch (nd_region_to_nstype(nd_region)) {
|
|
||||||
case ND_DEVICE_NAMESPACE_PMEM: {
|
|
||||||
struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
|
|
||||||
|
|
||||||
if (!nspm->uuid)
|
|
||||||
break;
|
|
||||||
if (memcmp(uuid, nspm->uuid, NSLABEL_UUID_LEN) == 0)
|
|
||||||
return -EBUSY;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ND_DEVICE_NAMESPACE_BLK: {
|
|
||||||
struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
|
|
||||||
|
|
||||||
if (!nsblk->uuid)
|
|
||||||
break;
|
|
||||||
if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) == 0)
|
|
||||||
return -EBUSY;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int is_namespace_uuid_busy(struct device *dev, void *data)
|
|
||||||
{
|
|
||||||
if (is_nd_pmem(dev) || is_nd_blk(dev))
|
|
||||||
return device_for_each_child(dev, data, is_uuid_busy);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* nd_is_uuid_unique - verify that no other namespace has @uuid
|
|
||||||
* @dev: any device on a nvdimm_bus
|
|
||||||
* @uuid: uuid to check
|
|
||||||
*/
|
|
||||||
bool nd_is_uuid_unique(struct device *dev, u8 *uuid)
|
|
||||||
{
|
|
||||||
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
|
|
||||||
|
|
||||||
if (!nvdimm_bus)
|
|
||||||
return false;
|
|
||||||
WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm_bus->dev));
|
|
||||||
if (device_for_each_child(&nvdimm_bus->dev, uuid,
|
|
||||||
is_namespace_uuid_busy) != 0)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t size_show(struct device *dev,
|
static ssize_t size_show(struct device *dev,
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user