2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2001-2003 Sistina Software (UK) Limited.
|
|
|
|
*
|
|
|
|
* This file is released under the GPL.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "dm.h"
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/blkdev.h>
|
|
|
|
#include <linux/bio.h>
|
|
|
|
#include <linux/slab.h>
|
2008-10-21 23:44:59 +07:00
|
|
|
#include <linux/device-mapper.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-06-26 14:27:35 +07:00
|
|
|
#define DM_MSG_PREFIX "linear"
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* Linear: maps a linear range of a device.
|
|
|
|
*/
|
|
|
|
struct linear_c {
|
|
|
|
struct dm_dev *dev;
|
|
|
|
sector_t start;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Construct a linear mapping: <dev_path> <offset>
|
|
|
|
*/
|
|
|
|
static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
|
|
|
|
{
|
|
|
|
struct linear_c *lc;
|
2006-03-27 16:17:48 +07:00
|
|
|
unsigned long long tmp;
|
dm: reject trailing characters in sccanf input
Device mapper uses sscanf to convert arguments to numbers. The problem is that
the way we use it ignores additional unmatched characters in the scanned string.
For example, this `if (sscanf(string, "%d", &number) == 1)' will match a number,
but also it will match number with some garbage appended, like "123abc".
As a result, device mapper accepts garbage after some numbers. For example
the command `dmsetup create vg1-new --table "0 16384 linear 254:1bla 34816bla"'
will pass without an error.
This patch fixes all sscanf uses in device mapper. It appends "%c" with
a pointer to a dummy character variable to every sscanf statement.
The construct `if (sscanf(string, "%d%c", &number, &dummy) == 1)' succeeds
only if string is a null-terminated number (optionally preceded by some
whitespace characters). If there is some character appended after the number,
sscanf matches "%c", writes the character to the dummy variable and returns 2.
We check the return value for 1 and consequently reject numbers with some
garbage appended.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Acked-by: Mike Snitzer <snitzer@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
2012-03-29 00:41:26 +07:00
|
|
|
char dummy;
|
2015-07-31 20:20:36 +07:00
|
|
|
int ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (argc != 2) {
|
2006-06-26 14:27:35 +07:00
|
|
|
ti->error = "Invalid argument count";
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
lc = kmalloc(sizeof(*lc), GFP_KERNEL);
|
|
|
|
if (lc == NULL) {
|
2015-10-28 02:38:58 +07:00
|
|
|
ti->error = "Cannot allocate linear context";
|
2005-04-17 05:20:36 +07:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2015-07-31 20:20:36 +07:00
|
|
|
ret = -EINVAL;
|
dm: reject trailing characters in sccanf input
Device mapper uses sscanf to convert arguments to numbers. The problem is that
the way we use it ignores additional unmatched characters in the scanned string.
For example, this `if (sscanf(string, "%d", &number) == 1)' will match a number,
but also it will match number with some garbage appended, like "123abc".
As a result, device mapper accepts garbage after some numbers. For example
the command `dmsetup create vg1-new --table "0 16384 linear 254:1bla 34816bla"'
will pass without an error.
This patch fixes all sscanf uses in device mapper. It appends "%c" with
a pointer to a dummy character variable to every sscanf statement.
The construct `if (sscanf(string, "%d%c", &number, &dummy) == 1)' succeeds
only if string is a null-terminated number (optionally preceded by some
whitespace characters). If there is some character appended after the number,
sscanf matches "%c", writes the character to the dummy variable and returns 2.
We check the return value for 1 and consequently reject numbers with some
garbage appended.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Acked-by: Mike Snitzer <snitzer@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
2012-03-29 00:41:26 +07:00
|
|
|
if (sscanf(argv[1], "%llu%c", &tmp, &dummy) != 1) {
|
2015-10-28 02:38:58 +07:00
|
|
|
ti->error = "Invalid device sector";
|
2005-04-17 05:20:36 +07:00
|
|
|
goto bad;
|
|
|
|
}
|
2006-03-27 16:17:48 +07:00
|
|
|
lc->start = tmp;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2015-07-31 20:20:36 +07:00
|
|
|
ret = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &lc->dev);
|
|
|
|
if (ret) {
|
2015-10-28 02:38:58 +07:00
|
|
|
ti->error = "Device lookup failed";
|
2005-04-17 05:20:36 +07:00
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
2013-03-02 05:45:47 +07:00
|
|
|
ti->num_flush_bios = 1;
|
|
|
|
ti->num_discard_bios = 1;
|
|
|
|
ti->num_write_same_bios = 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
ti->private = lc;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
bad:
|
|
|
|
kfree(lc);
|
2015-07-31 20:20:36 +07:00
|
|
|
return ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void linear_dtr(struct dm_target *ti)
|
|
|
|
{
|
|
|
|
struct linear_c *lc = (struct linear_c *) ti->private;
|
|
|
|
|
|
|
|
dm_put_device(ti, lc->dev);
|
|
|
|
kfree(lc);
|
|
|
|
}
|
|
|
|
|
2008-07-21 18:00:38 +07:00
|
|
|
static sector_t linear_map_sector(struct dm_target *ti, sector_t bi_sector)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-07-21 18:00:38 +07:00
|
|
|
struct linear_c *lc = ti->private;
|
|
|
|
|
2010-08-12 10:14:11 +07:00
|
|
|
return lc->start + dm_target_offset(ti, bi_sector);
|
2008-07-21 18:00:38 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void linear_map_bio(struct dm_target *ti, struct bio *bio)
|
|
|
|
{
|
|
|
|
struct linear_c *lc = ti->private;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
bio->bi_bdev = lc->dev->bdev;
|
2009-06-22 16:12:22 +07:00
|
|
|
if (bio_sectors(bio))
|
2013-10-12 05:44:27 +07:00
|
|
|
bio->bi_iter.bi_sector =
|
|
|
|
linear_map_sector(ti, bio->bi_iter.bi_sector);
|
2008-07-21 18:00:38 +07:00
|
|
|
}
|
|
|
|
|
2012-12-22 03:23:41 +07:00
|
|
|
static int linear_map(struct dm_target *ti, struct bio *bio)
|
2008-07-21 18:00:38 +07:00
|
|
|
{
|
|
|
|
linear_map_bio(ti, bio);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-12-08 17:41:06 +07:00
|
|
|
return DM_MAPIO_REMAPPED;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2013-03-02 05:45:44 +07:00
|
|
|
static void linear_status(struct dm_target *ti, status_type_t type,
|
|
|
|
unsigned status_flags, char *result, unsigned maxlen)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct linear_c *lc = (struct linear_c *) ti->private;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case STATUSTYPE_INFO:
|
|
|
|
result[0] = '\0';
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STATUSTYPE_TABLE:
|
2006-03-27 16:17:48 +07:00
|
|
|
snprintf(result, maxlen, "%s %llu", lc->dev->name,
|
|
|
|
(unsigned long long)lc->start);
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-15 19:10:50 +07:00
|
|
|
static int linear_prepare_ioctl(struct dm_target *ti,
|
|
|
|
struct block_device **bdev, fmode_t *mode)
|
2006-10-03 15:15:18 +07:00
|
|
|
{
|
|
|
|
struct linear_c *lc = (struct linear_c *) ti->private;
|
2012-01-12 22:01:29 +07:00
|
|
|
struct dm_dev *dev = lc->dev;
|
2015-10-15 19:10:50 +07:00
|
|
|
|
|
|
|
*bdev = dev->bdev;
|
2012-01-12 22:01:29 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Only pass ioctls through if the device sizes match exactly.
|
|
|
|
*/
|
|
|
|
if (lc->start ||
|
|
|
|
ti->len != i_size_read(dev->bdev->bd_inode) >> SECTOR_SHIFT)
|
2015-10-15 19:10:50 +07:00
|
|
|
return 1;
|
|
|
|
return 0;
|
2006-10-03 15:15:18 +07:00
|
|
|
}
|
|
|
|
|
2009-06-22 16:12:33 +07:00
|
|
|
static int linear_iterate_devices(struct dm_target *ti,
|
|
|
|
iterate_devices_callout_fn fn, void *data)
|
|
|
|
{
|
|
|
|
struct linear_c *lc = ti->private;
|
|
|
|
|
2009-07-24 02:30:42 +07:00
|
|
|
return fn(ti, lc->dev, lc->start, ti->len, data);
|
2009-06-22 16:12:33 +07:00
|
|
|
}
|
|
|
|
|
2016-06-23 06:54:54 +07:00
|
|
|
static long linear_direct_access(struct dm_target *ti, sector_t sector,
|
libnvdimm for 4.8
1/ Replace pcommit with ADR / directed-flushing:
The pcommit instruction, which has not shipped on any product, is
deprecated. Instead, the requirement is that platforms implement either
ADR, or provide one or more flush addresses per nvdimm. ADR
(Asynchronous DRAM Refresh) flushes data in posted write buffers to the
memory controller on a power-fail event. Flush addresses are defined in
ACPI 6.x as an NVDIMM Firmware Interface Table (NFIT) sub-structure:
"Flush Hint Address Structure". A flush hint is an mmio address that
when written and fenced assures that all previous posted writes
targeting a given dimm have been flushed to media.
2/ On-demand ARS (address range scrub):
Linux uses the results of the ACPI ARS commands to track bad blocks
in pmem devices. When latent errors are detected we re-scrub the media
to refresh the bad block list, userspace can also request a re-scrub at
any time.
3/ Support for the Microsoft DSM (device specific method) command format.
4/ Support for EDK2/OVMF virtual disk device memory ranges.
5/ Various fixes and cleanups across the subsystem.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iQIcBAABAgAGBQJXmXBsAAoJEB7SkWpmfYgCEwwP/1IOt9ocP+iHLMDH9KE7VaTZ
NmUDR+Zy6g5cRQM7SgcuU5BXUcx+OsSrSrUTVF1cW994o9Gbz1mFotkv0ZAsPcYY
ZVRQxo2oqHrssyOcg+PsgKWiXn68rJOCgmpEyzaJywl5qTMst7pzsT1s1f7rSh6h
trCf4VaJJwxZR8fARGtlHUnnhPe2Orp99EZRKEWprAsIv2kPuWpPHSjRjuEgN1JG
KW8AYwWqFTtiLRUk86I4KBB0wcDrfctsjgN9Ogd6+aHyQBRnVSr2U+vDCFkC8KLu
qiDCpYp+yyxBjclnljz7tRRT3GtzfCUWd4v2KVWqgg2IaobUc0Lbukp/rmikUXQP
WLikT2OCQ994eFK5OX3Q3cIU/4j459TQnof8q14yVSpjAKrNUXVSR5puN7Hxa+V7
41wKrAsnsyY1oq+Yd/rMR8VfH7PHx3bFkrmRCGZCufLX1UQm4aYj+sWagDKiV3yA
DiudghbOnhfurfGsnXUVw7y7GKs+gNWNBmB6ndAD6ZEHmKoGUhAEbJDLCc3DnANl
b/2mv1MIdIcC1DlCmnbbcn6fv6bICe/r8poK3VrCK3UgOq/EOvKIWl7giP+k1JuC
6DdVYhlNYIVFXUNSLFAwz8OkLu8byx7WDm36iEqrKHtPw+8qa/2bWVgOU6OBgpjV
cN3edFVIdxvZeMgM5Ubq
=xCBG
-----END PGP SIGNATURE-----
Merge tag 'libnvdimm-for-4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm
Pull libnvdimm updates from Dan Williams:
- Replace pcommit with ADR / directed-flushing.
The pcommit instruction, which has not shipped on any product, is
deprecated. Instead, the requirement is that platforms implement
either ADR, or provide one or more flush addresses per nvdimm.
ADR (Asynchronous DRAM Refresh) flushes data in posted write buffers
to the memory controller on a power-fail event.
Flush addresses are defined in ACPI 6.x as an NVDIMM Firmware
Interface Table (NFIT) sub-structure: "Flush Hint Address Structure".
A flush hint is an mmio address that when written and fenced assures
that all previous posted writes targeting a given dimm have been
flushed to media.
- On-demand ARS (address range scrub).
Linux uses the results of the ACPI ARS commands to track bad blocks
in pmem devices. When latent errors are detected we re-scrub the
media to refresh the bad block list, userspace can also request a
re-scrub at any time.
- Support for the Microsoft DSM (device specific method) command
format.
- Support for EDK2/OVMF virtual disk device memory ranges.
- Various fixes and cleanups across the subsystem.
* tag 'libnvdimm-for-4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm: (41 commits)
libnvdimm-btt: Delete an unnecessary check before the function call "__nd_device_register"
nfit: do an ARS scrub on hitting a latent media error
nfit: move to nfit/ sub-directory
nfit, libnvdimm: allow an ARS scrub to be triggered on demand
libnvdimm: register nvdimm_bus devices with an nd_bus driver
pmem: clarify a debug print in pmem_clear_poison
x86/insn: remove pcommit
Revert "KVM: x86: add pcommit support"
nfit, tools/testing/nvdimm/: unify shutdown paths
libnvdimm: move ->module to struct nvdimm_bus_descriptor
nfit: cleanup acpi_nfit_init calling convention
nfit: fix _FIT evaluation memory leak + use after free
tools/testing/nvdimm: add manufacturing_{date|location} dimm properties
tools/testing/nvdimm: add virtual ramdisk range
acpi, nfit: treat virtual ramdisk SPA as pmem region
pmem: kill __pmem address space
pmem: kill wmb_pmem()
libnvdimm, pmem: use nvdimm_flush() for namespace I/O writes
fs/dax: remove wmb_pmem()
libnvdimm, pmem: flush posted-write queues on shutdown
...
2016-07-29 07:22:07 +07:00
|
|
|
void **kaddr, pfn_t *pfn, long size)
|
2016-06-23 06:54:54 +07:00
|
|
|
{
|
|
|
|
struct linear_c *lc = ti->private;
|
|
|
|
struct block_device *bdev = lc->dev->bdev;
|
|
|
|
struct blk_dax_ctl dax = {
|
|
|
|
.sector = linear_map_sector(ti, sector),
|
|
|
|
.size = size,
|
|
|
|
};
|
|
|
|
long ret;
|
|
|
|
|
|
|
|
ret = bdev_direct_access(bdev, &dax);
|
|
|
|
*kaddr = dax.addr;
|
|
|
|
*pfn = dax.pfn;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static struct target_type linear_target = {
|
|
|
|
.name = "linear",
|
2016-06-23 06:54:54 +07:00
|
|
|
.version = {1, 3, 0},
|
2005-04-17 05:20:36 +07:00
|
|
|
.module = THIS_MODULE,
|
|
|
|
.ctr = linear_ctr,
|
|
|
|
.dtr = linear_dtr,
|
|
|
|
.map = linear_map,
|
|
|
|
.status = linear_status,
|
2015-10-15 19:10:50 +07:00
|
|
|
.prepare_ioctl = linear_prepare_ioctl,
|
2009-06-22 16:12:33 +07:00
|
|
|
.iterate_devices = linear_iterate_devices,
|
2016-06-23 06:54:54 +07:00
|
|
|
.direct_access = linear_direct_access,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
int __init dm_linear_init(void)
|
|
|
|
{
|
|
|
|
int r = dm_register_target(&linear_target);
|
|
|
|
|
|
|
|
if (r < 0)
|
2006-06-26 14:27:35 +07:00
|
|
|
DMERR("register failed %d", r);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
void dm_linear_exit(void)
|
|
|
|
{
|
2009-01-06 10:04:58 +07:00
|
|
|
dm_unregister_target(&linear_target);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|