* [PATCH v2 0/3] SysFS driver for QEMU fw_cfg device
From: Gabriel L. Somlo @ 2015-08-11 18:44 UTC (permalink / raw)
To: gregkh, ralf, zajec5, paul, galak, linux-api, linux-kernel
Cc: linux-efi, rjones, jordan.l.justen, x86, qemu-devel, gleb,
matt.fleming, kernelnewbies, kraxel, pbonzini, lersek
From: "Gabriel Somlo" <somlo@cmu.edu>
This patch set makes QEMU fw_cfg blobs available for viewing (read-only)
via SysFS.
New since v1:
1/3:
- renamed sysfs path components:
s/fw_cfg/qemu_fw_cfg/g, at Greg's suggestion
s/by_select/by_key/g since it feels a bit more intuitive
- attribute "select" renamed to "key", along the same train of
thought.
- renamed actual source file from "fw_cfg.c" to "qemu_fw_cfg.c"
and doc file in Documentation/ABI/testing to
"sysfs-firmware-qemu_fw_cfg"
- s/uintXX_t/uXX/g, at Greg's suggestion
- using named identifiers to initialize access mode table
(static struct fw_cfg_access fw_cfg_modes[]) per Greg's
suggestion
- removed redundant "capable(CAP_SYS_ADMIN)" checks (per Greg's
feedback)
2/3:
- EXPORT_SYMBOL(kset_find_obj) statement moved immediately
after kset_find_obj() definition.
Re. 'struct device' vs. straight-up kobject:
I dug around a bit, and found a bunch of stuff under /sys/devices
(such as e.g. /sys/devices/virtual/dmi and /sys/devices/platform/applesmc)
The first one is particularly interesting, as it presents a
somewhat different view of the same internal information displayed by
/sys/firmware/dmi/, only /sys/devices/virtual/dmi looks more "cooked",
in that it exposes individual fields of the several different DMI tables.
None of the subfolders of /sys/devices/... appears to facilitate access
to the "raw blobs" contained by the respective devices represented there.
Even applesmc (the other device with which I'm somewhat familiar) does
a lot of "cooking" of the values held by the device.
So, at the end of the day, I want the information I expose about fw_cfg
to look more like /sys/firmware/dmi than like /sys/devices/virtual/dmi
in that I have a bunch of cached metadata attributes for each of the
blobs, and want to offer access to the raw blob data, and there's no
rhyme or reason to the actual internals of any of the blobs, so offering
a "cooked" view like in /sys/devices/... doesn't feel like the best fit...
Obviously, I may be totally wrong... :)
Lastly, re. Greg's comment on patch 3/3:
> Shouldn't all of this be done in userspace with the symlinks and all?
> It seems like you are trying to duplicate the /dev/block/by-name and
> such. Policy decisions like symlinks and naming should be done there,
> in userspace, and not directly in sysfs if at all possible.
OK, so here's the "pseudo-bash" equivalent of what this patch does:
#upon successful qemu_fw_cfg.ko insertion
BY_KEY="/sys/firmware/qemu_fw_cfg/by_key"
BY_NAME="/sys/firmware/qemu_fw_cfg/by_name"
mkdir -p $BY_NAME
for KEY in $(ls $BY_KEY); do
FILE=$(cat $BY_KEY/$KEY/name)
[ mkdir -p $BY_NAME/$(dirname $FILE) ] || continue
pushd $BY_NAME/$(dirname $FILE)
ln -s $BY_KEY/$KEY $(basename $FILE)
popd
done
Note how it MAY fail to mkdir, in which case it skips to the next key,
or it MAY fail to create the symlink, in which case it just moves on
to the next key as well.
Again, the fw_cfg device doesn't have any notion of nested directories,
it just parrots back some random string someone choose as a name for a
given blob, but these strings typically contain slash-separated ASCII
which "looks like" it "could" be a path name :)
If there's a reasonable way to do this dynamically, immediately upon
insertion of the qemu_fw_cfg.ko module, patches 2/3 and 3/3 could be
dropped.
But before that, I'd appreciate a pointer to how/where this can be done
in userspace. Would it be available e.g. during early boot, early enough
to e.g. raw-dump an ASCII blob containing shell variable definitions (e.g.
to set hostname on the guest, etc ?)
Doing it as part of the qemu_fw_cfg.ko module itself has the advantage
of it being available immediately, without anything having to "watch"
for the module being inserted.
But if userspace is the standard, "canonical" place to do something like
this, I'd be happy to give it a try, once I find the right tree to bark
up on... :)
Thanks much,
Gabriel
Old "cover-letter" blurb for v1:
> Several different architectures supported by QEMU are set up with a
> "firmware configuration" (fw_cfg) device, used to pass configuration
> "blobs" into the guest by the host running QEMU.
>
> Historically, these config blobs were mostly of interest to the guest
> BIOS, but since QEMU v2.4 it is possible to insert arbitrary blobs via
> the command line, which makes them potentially interesting to userspace
> (e.g. for passing early boot environment variables, etc.).
>
> In addition to cc-ing the people and lists indicated by get-maintainer.pl,
> I've added a few extra lists suggested by Matt Fleming on the qemu-devel
> list, as well as the qemu-devel list itself.
>
> Also cc-ing kernelnewbies, as this is my very first kenel contribution,
> so please go easy on me for whatever silly n00b mistakes I might have still
> missed, in spite of trying hard to do all my homework properly... :)
>
> The series consists of three patches:
>
> 1/3 - probes for the qemu fw_cfg device in locations known to work on
> the supported architectures, in decreasing order of "likelihood".
>
> While it *may* be possible to detect the presence of fw_cfg via
> acpi or dtb (on x86 and arm, respectively), there's no way I know
> of attempting that on sun4 and ppc/mac, so I've stuck with simply
> probing (the fw_cfg_modes[] structure and fw_cfg_io_probe() function)
> in fw_cfg.c. I could use some advice on how else that could be
> done more elegantly, if needed.
>
> Upon successfully detecting a present fw_cfg device, we set up
> /sys/firmware/fw_cfg/by_select entries for each blob available
> on the fw_cfg device.
>
> 2/3 - export kset_find_obj() (in lib/kobject.c) for use with modules,
> which will come in handy in patch #3, below.
>
> 3/3 - add a "user friendly" way of listing fw_cfg blobs by name rather
> than by unique selector key.
>
> Since fw_cfg blob names are traditionally set up to look like
> path names, it would be nice to mirror that fact when displaying
> them under /sys/firmware/fw_cfg in a "by_name" subdirectory.
>
> I'm using ksets to reflect subdirectories matching "dirname"
> tokens separated by '/' within each fw_cfg blob "filename",
> and symlinks into the "by_select" subdirectory for each "basename".
>
> Since the fw_cfg device doesn't enforce that blob names have
> well-behaved and non-conflicting names, it is possible (though
> unlikely) that there will be blobs named:
>
> "etc/foo/bar"
> and
> "etc/foo"
>
> where "foo" will try to be both a subdirectory AND a symlink under
> /sys/firmware/fw_cfg/by_name/etc/. In such an event, the latter
> fw_cfg blob will simply be skipped from having an entry created
> under the user-friendly "by_name" subdirectory, remaining listed
> only as an entry under the "by_select" subdirectory.
>
> I have tested this on x86_64 and armv7hl+lpae kernels. Builds and installs
> OK as both a module and linked directly into the kernel.
Gabriel Somlo (3):
firmware: introduce sysfs driver for QEMU's fw_cfg device
kobject: export kset_find_obj() to be used from modules
firmware: fw_cfg: create directory hierarchy for fw_cfg file names
.../ABI/testing/sysfs-firmware-qemu_fw_cfg | 210 ++++++++
drivers/firmware/Kconfig | 10 +
drivers/firmware/Makefile | 1 +
drivers/firmware/qemu_fw_cfg.c | 560 +++++++++++++++++++++
lib/kobject.c | 1 +
5 files changed, 782 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-firmware-qemu_fw_cfg
create mode 100644 drivers/firmware/qemu_fw_cfg.c
--
2.4.3
^ permalink raw reply
* [PATCH v2 1/3] firmware: introduce sysfs driver for QEMU's fw_cfg device
From: Gabriel L. Somlo @ 2015-08-11 18:44 UTC (permalink / raw)
To: gregkh, ralf, zajec5, paul, galak, linux-api, linux-kernel
Cc: linux-efi, rjones, jordan.l.justen, x86, qemu-devel, gleb,
matt.fleming, kernelnewbies, kraxel, pbonzini, lersek
In-Reply-To: <cover.1439315079.git.somlo@cmu.edu>
From: "Gabriel Somlo" <somlo@cmu.edu>
Make fw_cfg entries of type "file" available via sysfs. Entries
are listed under /sys/firmware/qemu_fw_cfg/by_key, in folders
named after each entry's selector key. Filename, selector value,
and size read-only attributes are included for each entry. Also,
a "raw" attribute allows retrieval of the full binary content of
each entry.
This patch also provides a documentation file outlining the
guest-side "hardware" interface exposed by the QEMU fw_cfg device.
Signed-off-by: Gabriel Somlo <somlo@cmu.edu>
---
.../ABI/testing/sysfs-firmware-qemu_fw_cfg | 168 ++++++++
drivers/firmware/Kconfig | 10 +
drivers/firmware/Makefile | 1 +
drivers/firmware/qemu_fw_cfg.c | 456 +++++++++++++++++++++
4 files changed, 635 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-firmware-qemu_fw_cfg
create mode 100644 drivers/firmware/qemu_fw_cfg.c
diff --git a/Documentation/ABI/testing/sysfs-firmware-qemu_fw_cfg b/Documentation/ABI/testing/sysfs-firmware-qemu_fw_cfg
new file mode 100644
index 0000000..5bc2e3f
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-firmware-qemu_fw_cfg
@@ -0,0 +1,168 @@
+What: /sys/firmware/qemu_fw_cfg/
+Date: August 2015
+Contact: Gabriel Somlo <somlo@cmu.edu>
+Description:
+ Several different architectures supported by QEMU (x86, arm,
+ sun4*, ppc/mac) are provisioned with a firmware configuration
+ (fw_cfg) device, used by the host to provide configuration data
+ to the starting guest. While most of this data is meant for use
+ by the guest BIOS, starting with QEMU v2.4, guest VMs may be
+ started with arbitrary fw_cfg entries supplied directly on the
+ command line, which therefore may be of interest to userspace.
+
+ === Guest-side Hardware Interface ===
+
+ The fw_cfg device is available to guest VMs as a pair (control
+ and data) of registers, accessible as either a IO ports or as
+ MMIO addresses, depending on the architecture.
+
+ --- Control Register ---
+
+ Width: 16-bit
+ Access: Write-Only
+ Endianness: LE (if IOport) or BE (if MMIO)
+
+ A write to the control register selects the index for one of
+ the firmware configuration items (or "blobs") available on the
+ fw_cfg device, which can subsequently be read from the data
+ register.
+
+ Each time the control register is written, an data offset
+ internal to the fw_cfg device will be set to zero. This data
+ offset impacts which portion of the selected fw_cfg blob is
+ accessed by reading the data register, as explained below.
+
+ --- Data Register ---
+
+ Width: 8-bit (if IOport), or 8/16/32/64-bit (if MMIO)
+ Access: Read-Only
+ Endianness: string preserving
+
+ The data register allows access to an array of bytes which
+ represent the fw_cfg blob last selected by a write to the
+ control register.
+
+ Immediately following a write to the control register, the data
+ offset will be set to zero. Each successful read access to the
+ data register will increment the data offset by the appropriate
+ access width.
+
+ Each fw_cfg blob has a maximum associated data length. Once the
+ data offset exceeds this maximum length, any subsequent reads
+ via the data register will return 0x00.
+
+ An N-byte wide read of the data register will return the next
+ available N bytes of the selected fw_cfg blob, as a substring,
+ in increasing address order, similar to memcpy(), zero-padded
+ if necessary should the maximum data length of the selected
+ item be reached, as described above.
+
+ --- Per-arch Register Details ---
+
+ -------------------------------------------------------------
+ arch access base ctrl ctrl data data
+ mode address offset endian offset width
+ max.
+ -------------------------------------------------------------
+ x86 IOport 0x510 0 LE 1 8
+ x86_64 IOport 0x510 0 LE 1 8
+ arm MMIO 0x9020000 8 BE 0 64
+ sun4u IOport 0x510 0 LE 1 8
+ sun4m MMIO 0xd00000510 0 BE 2 8
+ ppc/mac MMIO 0xf0000510 0 BE 2 8
+ -------------------------------------------------------------
+
+ NOTE: On platforms where the fw_cfg registers are exposed as
+ IO ports, the data port number will always be one greater than
+ the port number of the control register. I.e., the two ports
+ are overlapping, and can not be mapped separately.
+
+ === Firmware Configuration Items of Interest ===
+
+ Originally, the index key, size, and formatting of blobs in
+ fw_cfg was hard coded by mutual agreement between QEMU on the
+ host side, and the BIOS running on the guest. Later on, a file
+ transfer interface was added: by reading a special blob, the
+ fw_cfg consumer can retrieve a list of records containing the
+ name, selector key, and size of further fw_cfg blobs made
+ available by the host. Below we describe three fw_cfg blobs
+ of interest to the sysfs driver.
+
+ --- Signature (Key 0x0000, FW_CFG_SIGNATURE) ---
+
+ The presence of the fw_cfg device can be verified by selecting
+ the signature blob by writing 0x0000 to the control register,
+ and reading four bytes from the data register. If the fw_cfg
+ device is present, the four bytes read will match the ASCII
+ characters "QEMU".
+
+ --- Revision (Key 0x0001, FW_CFG_ID) ---
+
+ A 32-bit little-endian unsigned integer, this item is used as
+ an interface revision number.
+
+ --- File Directory (Key 0x0019, FW_CFG_FILE_DIR) ---
+
+ Any fw_cfg blobs stored at key 0x0020 FW_CFG_FILE_FIRST() or
+ higher will have an associated entry in this "directory" blob,
+ which facilitates the discovery of available items by software
+ (e.g. BIOS) running on the guest. The format of the directory
+ blob is shown below.
+
+ NOTE: All integers are stored in big-endian format!
+
+ /* the entire file directory "blob" */
+ struct FWCfgFiles {
+ u32 count; /* total number of entries */
+ struct FWCfgFile f[]; /* entry array, see below */
+ };
+
+ /* an individual directory entry, 64 bytes total */
+ struct FWCfgFile {
+ u32 size; /* size of referenced blob */
+ u16 select; /* selector key for referenced blob */
+ u16 reserved;
+ char name[56]; /* blob name, nul-terminated ASCII */
+ };
+
+ === SysFS fw_cfg Interface ===
+
+ The fw_cfg sysfs interface described in this document is only
+ intended to display discoverable blobs (i.e., those registered
+ with the file directory), as there is no way to determine the
+ presence or size of "legacy" blobs (with selector keys between
+ 0x0002 and 0x0018) programmatically.
+
+ All fw_cfg information is shown under:
+
+ /sys/firmware/qemu_fw_cfg/
+
+ The only legacy blob displayed is the fw_cfg device revision:
+
+ /sys/firmware/qemu_fw_cfg/rev
+
+ --- Discoverable fw_cfg blobs by selector key ---
+
+ All discoverable blobs listed in the fw_cfg file directory are
+ displayed as entries named after their unique selector key
+ value, e.g.:
+
+ /sys/firmware/qemu_fw_cfg/by_key/32
+ /sys/firmware/qemu_fw_cfg/by_key/33
+ /sys/firmware/qemu_fw_cfg/by_key/34
+ ...
+
+ Each such fw_cfg sysfs entry has the following values exported
+ as attributes:
+
+ name : The 56-byte nul-terminated ASCII string used as the
+ blob's 'file name' in the fw_cfg directory.
+ size : The length of the blob, as given in the fw_cfg
+ directory.
+ key : The value of the blob's selector key as given in the
+ fw_cfg directory. This value is the same as used in
+ the parent directory name.
+ raw : The raw bytes of the blob, obtained by selecting the
+ entry via the control register, and reading a number
+ of bytes equal to the blob size from the data
+ register.
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 99c69a3..f5cbe81 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -136,6 +136,16 @@ config QCOM_SCM
bool
depends on ARM || ARM64
+config FW_CFG_SYSFS
+ tristate "QEMU fw_cfg device support in sysfs"
+ depends on SYSFS
+ default n
+ help
+ Say Y or M here to enable the exporting of the QEMU firmware
+ configuration (fw_cfg) file entries via sysfs. Entries are
+ found under /sys/firmware/fw_cfg when this option is enabled
+ and loaded.
+
source "drivers/firmware/broadcom/Kconfig"
source "drivers/firmware/google/Kconfig"
source "drivers/firmware/efi/Kconfig"
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 4a4b897..706fdc5 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o
obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
obj-$(CONFIG_QCOM_SCM) += qcom_scm.o
obj-$(CONFIG_QCOM_SCM) += qcom_scm-32.o
+obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o
CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
obj-y += broadcom/
diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c
new file mode 100644
index 0000000..cd26eee
--- /dev/null
+++ b/drivers/firmware/qemu_fw_cfg.c
@@ -0,0 +1,456 @@
+/*
+ * drivers/firmware/qemu_fw_cfg.c
+ *
+ * Expose entries from QEMU's firmware configuration (fw_cfg) device in
+ * sysfs (read-only, under "/sys/firmware/qemu_fw_cfg/...").
+ *
+ * Copyright 2015 Carnegie Mellon University
+ */
+
+#include <linux/module.h>
+#include <linux/capability.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/ctype.h>
+
+/* selector key values for "well-known" fw_cfg entries */
+#define FW_CFG_SIGNATURE 0x00
+#define FW_CFG_ID 0x01
+#define FW_CFG_FILE_DIR 0x19
+
+/* size in bytes of fw_cfg signature */
+#define FW_CFG_SIG_SIZE 4
+
+/* fw_cfg "file name" is up to 56 characters (including terminating nul) */
+#define FW_CFG_MAX_FILE_PATH 56
+
+/* fw_cfg file directory entry type */
+struct fw_cfg_file {
+ u32 size;
+ u16 select;
+ u16 reserved;
+ char name[FW_CFG_MAX_FILE_PATH];
+};
+
+/* fw_cfg device i/o access options type */
+struct fw_cfg_access {
+ const char *name;
+ phys_addr_t base;
+ u8 size;
+ u8 ctrl_offset;
+ u8 data_offset;
+ bool is_mmio;
+};
+
+/* table of fw_cfg device i/o access options for known architectures */
+static struct fw_cfg_access fw_cfg_modes[] = {
+ {
+ .name = "fw_cfg IOport on i386, sun4u",
+ .base = 0x510,
+ .size = 0x02,
+ .ctrl_offset = 0x00,
+ .data_offset = 0x01,
+ .is_mmio = false,
+ }, {
+ .name = "fw_cfg MMIO on arm",
+ .base = 0x9020000,
+ .size = 0x0a,
+ .ctrl_offset = 0x08,
+ .data_offset = 0x00,
+ .is_mmio = true,
+ }, {
+ .name = "fw_cfg MMIO on sun4m",
+ .base = 0xd00000510,
+ .size = 0x03,
+ .ctrl_offset = 0x00,
+ .data_offset = 0x02,
+ .is_mmio = true,
+ }, {
+ .name = "fw_cfg MMIO on ppc/mac",
+ .base = 0xf0000510,
+ .size = 0x03,
+ .ctrl_offset = 0x00,
+ .data_offset = 0x02,
+ .is_mmio = true,
+ }, { } /* END */
+};
+
+/* fw_cfg device i/o currently selected option set */
+static struct fw_cfg_access *fw_cfg_mode;
+
+/* fw_cfg device i/o register addresses */
+static void __iomem *fw_cfg_dev_base;
+static void __iomem *fw_cfg_dev_ctrl;
+static void __iomem *fw_cfg_dev_data;
+
+/* atomic access to fw_cfg device (potentially slow i/o, so using mutex) */
+static DEFINE_MUTEX(fw_cfg_dev_lock);
+
+/* pick apropriate endianness for selector key */
+static inline u16 fw_cfg_sel_endianness(u16 key)
+{
+ return fw_cfg_mode->is_mmio ? cpu_to_be16(key) : cpu_to_le16(key);
+}
+
+/* type for fw_cfg "directory scan" visitor/callback function */
+typedef int (*fw_cfg_file_callback)(const struct fw_cfg_file *f);
+
+/* run a given callback on each fw_cfg directory entry */
+static int fw_cfg_scan_dir(fw_cfg_file_callback callback)
+{
+ int ret = 0;
+ u32 count, i;
+ struct fw_cfg_file f;
+
+ mutex_lock(&fw_cfg_dev_lock);
+ iowrite16(fw_cfg_sel_endianness(FW_CFG_FILE_DIR), fw_cfg_dev_ctrl);
+ ioread8_rep(fw_cfg_dev_data, &count, sizeof(count));
+ for (i = 0; i < be32_to_cpu(count); i++) {
+ ioread8_rep(fw_cfg_dev_data, &f, sizeof(f));
+ ret = callback(&f);
+ if (ret)
+ break;
+ }
+ mutex_unlock(&fw_cfg_dev_lock);
+ return ret;
+}
+
+/* read chunk of given fw_cfg blob (caller responsible for sanity-check) */
+static inline void fw_cfg_read_blob(u16 key,
+ void *buf, loff_t pos, size_t count)
+{
+ mutex_lock(&fw_cfg_dev_lock);
+ iowrite16(fw_cfg_sel_endianness(key), fw_cfg_dev_ctrl);
+ while (pos-- > 0)
+ ioread8(fw_cfg_dev_data);
+ ioread8_rep(fw_cfg_dev_data, buf, count);
+ mutex_unlock(&fw_cfg_dev_lock);
+}
+
+/* clean up fw_cfg device i/o setup */
+static void fw_cfg_io_cleanup(void)
+{
+ if (fw_cfg_mode->is_mmio) {
+ iounmap(fw_cfg_dev_base);
+ release_mem_region(fw_cfg_mode->base, fw_cfg_mode->size);
+ } else {
+ ioport_unmap(fw_cfg_dev_base);
+ release_region(fw_cfg_mode->base, fw_cfg_mode->size);
+ }
+}
+
+/* probe and map fw_cfg device */
+static int __init fw_cfg_io_probe(void)
+{
+ char sig[FW_CFG_SIG_SIZE];
+
+ for (fw_cfg_mode = &fw_cfg_modes[0];
+ fw_cfg_mode->base; fw_cfg_mode++) {
+
+ phys_addr_t base = fw_cfg_mode->base;
+ u8 size = fw_cfg_mode->size;
+
+ /* reserve and map mmio or ioport region */
+ if (fw_cfg_mode->is_mmio) {
+ if (!request_mem_region(base, size, fw_cfg_mode->name))
+ continue;
+ fw_cfg_dev_base = ioremap(base, size);
+ if (!fw_cfg_dev_base) {
+ release_mem_region(base, size);
+ continue;
+ }
+ } else {
+ if (!request_region(base, size, fw_cfg_mode->name))
+ continue;
+ fw_cfg_dev_base = ioport_map(base, size);
+ if (!fw_cfg_dev_base) {
+ release_region(base, size);
+ continue;
+ }
+ }
+
+ /* set control and data register addresses */
+ fw_cfg_dev_ctrl = fw_cfg_dev_base + fw_cfg_mode->ctrl_offset;
+ fw_cfg_dev_data = fw_cfg_dev_base + fw_cfg_mode->data_offset;
+
+ /* verify fw_cfg device signature */
+ fw_cfg_read_blob(FW_CFG_SIGNATURE, sig, 0, FW_CFG_SIG_SIZE);
+ if (memcmp(sig, "QEMU", FW_CFG_SIG_SIZE) == 0)
+ /* success, we're done */
+ return 0;
+
+ /* clean up before probing next access mode */
+ fw_cfg_io_cleanup();
+ }
+
+ return -ENODEV;
+}
+
+/* fw_cfg revision attribute, in /sys/firmware/qemu_fw_cfg top-level dir. */
+static u32 fw_cfg_rev;
+
+static ssize_t fw_cfg_showrev(struct kobject *k, struct attribute *a, char *buf)
+{
+ return sprintf(buf, "%u\n", fw_cfg_rev);
+}
+
+static const struct {
+ struct attribute attr;
+ ssize_t (*show)(struct kobject *k, struct attribute *a, char *buf);
+} fw_cfg_rev_attr = {
+ .attr = { .name = "rev", .mode = S_IRUSR },
+ .show = fw_cfg_showrev,
+};
+
+/* fw_cfg_sysfs_entry type */
+struct fw_cfg_sysfs_entry {
+ struct kobject kobj;
+ struct fw_cfg_file f;
+ struct list_head list;
+};
+
+/* get fw_cfg_sysfs_entry from kobject member */
+static inline struct fw_cfg_sysfs_entry *to_entry(struct kobject *kobj)
+{
+ return container_of(kobj, struct fw_cfg_sysfs_entry, kobj);
+}
+
+/* fw_cfg_sysfs_attribute type */
+struct fw_cfg_sysfs_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct fw_cfg_sysfs_entry *entry, char *buf);
+};
+
+/* get fw_cfg_sysfs_attribute from attribute member */
+static inline struct fw_cfg_sysfs_attribute *to_attr(struct attribute *attr)
+{
+ return container_of(attr, struct fw_cfg_sysfs_attribute, attr);
+}
+
+/* global cache of fw_cfg_sysfs_entry objects */
+static LIST_HEAD(fw_cfg_entry_cache);
+
+/* kobjects removed lazily by kernel, mutual exclusion needed */
+static DEFINE_SPINLOCK(fw_cfg_cache_lock);
+
+static inline void fw_cfg_sysfs_cache_enlist(struct fw_cfg_sysfs_entry *entry)
+{
+ spin_lock(&fw_cfg_cache_lock);
+ list_add_tail(&entry->list, &fw_cfg_entry_cache);
+ spin_unlock(&fw_cfg_cache_lock);
+}
+
+static inline void fw_cfg_sysfs_cache_delist(struct fw_cfg_sysfs_entry *entry)
+{
+ spin_lock(&fw_cfg_cache_lock);
+ list_del(&entry->list);
+ spin_unlock(&fw_cfg_cache_lock);
+}
+
+static void fw_cfg_sysfs_cache_cleanup(void)
+{
+ struct fw_cfg_sysfs_entry *entry, *next;
+
+ list_for_each_entry_safe(entry, next, &fw_cfg_entry_cache, list) {
+ /* will end up invoking fw_cfg_sysfs_cache_delist()
+ * via each object's release() method (i.e. destructor) */
+ kobject_put(&entry->kobj);
+ }
+}
+
+/* default_attrs: per-entry attributes and show methods */
+
+#define FW_CFG_SYSFS_ATTR(_attr) \
+struct fw_cfg_sysfs_attribute fw_cfg_sysfs_attr_##_attr = { \
+ .attr = { .name = __stringify(_attr), .mode = S_IRUSR }, \
+ .show = fw_cfg_sysfs_show_##_attr, \
+}
+
+static ssize_t fw_cfg_sysfs_show_size(struct fw_cfg_sysfs_entry *e, char *buf)
+{
+ return sprintf(buf, "%u\n", e->f.size);
+}
+
+static ssize_t fw_cfg_sysfs_show_key(struct fw_cfg_sysfs_entry *e, char *buf)
+{
+ return sprintf(buf, "%u\n", e->f.select);
+}
+
+static ssize_t fw_cfg_sysfs_show_name(struct fw_cfg_sysfs_entry *e, char *buf)
+{
+ return sprintf(buf, "%s\n", e->f.name);
+}
+
+static FW_CFG_SYSFS_ATTR(size);
+static FW_CFG_SYSFS_ATTR(key);
+static FW_CFG_SYSFS_ATTR(name);
+
+static struct attribute *fw_cfg_sysfs_entry_attrs[] = {
+ &fw_cfg_sysfs_attr_size.attr,
+ &fw_cfg_sysfs_attr_key.attr,
+ &fw_cfg_sysfs_attr_name.attr,
+ NULL,
+};
+
+/* sysfs_ops: find fw_cfg_[entry, attribute] and call appropriate show method */
+static ssize_t fw_cfg_sysfs_attr_show(struct kobject *kobj, struct attribute *a,
+ char *buf)
+{
+ struct fw_cfg_sysfs_entry *entry = to_entry(kobj);
+ struct fw_cfg_sysfs_attribute *attr = to_attr(a);
+
+ return attr->show(entry, buf);
+}
+
+static const struct sysfs_ops fw_cfg_sysfs_attr_ops = {
+ .show = fw_cfg_sysfs_attr_show,
+};
+
+/* release: destructor, to be called via kobject_put() */
+static void fw_cfg_sysfs_release_entry(struct kobject *kobj)
+{
+ struct fw_cfg_sysfs_entry *entry = to_entry(kobj);
+
+ fw_cfg_sysfs_cache_delist(entry);
+ kfree(entry);
+}
+
+/* kobj_type: ties together all properties required to register an entry */
+static struct kobj_type fw_cfg_sysfs_entry_ktype = {
+ .default_attrs = fw_cfg_sysfs_entry_attrs,
+ .sysfs_ops = &fw_cfg_sysfs_attr_ops,
+ .release = fw_cfg_sysfs_release_entry,
+};
+
+/* raw-read method and attribute */
+static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t count)
+{
+ struct fw_cfg_sysfs_entry *entry = to_entry(kobj);
+
+ if (pos > entry->f.size)
+ return -EINVAL;
+
+ if (count > entry->f.size - pos)
+ count = entry->f.size - pos;
+
+ fw_cfg_read_blob(entry->f.select, buf, pos, count);
+ return count;
+}
+
+static struct bin_attribute fw_cfg_sysfs_attr_raw = {
+ .attr = { .name = "raw", .mode = 0400 },
+ .read = fw_cfg_sysfs_read_raw,
+};
+
+/* kobjects & kset representing top-level, by_key, and by_name folders */
+static struct kobject *fw_cfg_top_ko;
+static struct kobject *fw_cfg_sel_ko;
+
+/* callback function to register an individual fw_cfg file */
+static int __init fw_cfg_register_file(const struct fw_cfg_file *f)
+{
+ int err;
+ struct fw_cfg_sysfs_entry *entry;
+
+ /* allocate new entry */
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ /* set file entry information */
+ entry->f.size = be32_to_cpu(f->size);
+ entry->f.select = be16_to_cpu(f->select);
+ strcpy(entry->f.name, f->name);
+
+ /* register entry under "/sys/firmware/qemu_fw_cfg/by_key/" */
+ err = kobject_init_and_add(&entry->kobj, &fw_cfg_sysfs_entry_ktype,
+ fw_cfg_sel_ko, "%d", entry->f.select);
+ if (err)
+ goto err_register;
+
+ /* add raw binary content access */
+ err = sysfs_create_bin_file(&entry->kobj, &fw_cfg_sysfs_attr_raw);
+ if (err)
+ goto err_add_raw;
+
+ /* success, add entry to global cache */
+ fw_cfg_sysfs_cache_enlist(entry);
+ return 0;
+
+err_add_raw:
+ kobject_del(&entry->kobj);
+err_register:
+ kfree(entry);
+ return err;
+}
+
+/* unregister top-level or by_key folder */
+static inline void fw_cfg_kobj_cleanup(struct kobject *kobj)
+{
+ kobject_del(kobj);
+ kobject_put(kobj);
+}
+
+static int __init fw_cfg_sysfs_init(void)
+{
+ int err;
+
+ /* probe for the fw_cfg "hardware" */
+ err = fw_cfg_io_probe();
+ if (err)
+ return err;
+
+ /* create /sys/firmware/qemu_fw_cfg/ and its subdirectories */
+ err = -ENOMEM;
+ fw_cfg_top_ko = kobject_create_and_add("qemu_fw_cfg", firmware_kobj);
+ if (!fw_cfg_top_ko)
+ goto err_top;
+ fw_cfg_sel_ko = kobject_create_and_add("by_key", fw_cfg_top_ko);
+ if (!fw_cfg_sel_ko)
+ goto err_sel;
+
+ /* get revision number, add matching top-level attribute */
+ fw_cfg_read_blob(FW_CFG_ID, &fw_cfg_rev, 0, sizeof(fw_cfg_rev));
+ fw_cfg_rev = le32_to_cpu(fw_cfg_rev);
+ err = sysfs_create_file(fw_cfg_top_ko, &fw_cfg_rev_attr.attr);
+ if (err)
+ goto err_rev;
+
+ /* process fw_cfg file directory entry, registering each file */
+ err = fw_cfg_scan_dir(fw_cfg_register_file);
+ if (err)
+ goto err_scan;
+
+ /* success */
+ pr_debug("fw_cfg: loaded.\n");
+ return 0;
+
+err_scan:
+ fw_cfg_sysfs_cache_cleanup();
+ sysfs_remove_file(fw_cfg_top_ko, &fw_cfg_rev_attr.attr);
+err_rev:
+ fw_cfg_kobj_cleanup(fw_cfg_sel_ko);
+err_sel:
+ fw_cfg_kobj_cleanup(fw_cfg_top_ko);
+err_top:
+ fw_cfg_io_cleanup();
+ return err;
+}
+
+static void __exit fw_cfg_sysfs_exit(void)
+{
+ pr_debug("fw_cfg: unloading.\n");
+ fw_cfg_sysfs_cache_cleanup();
+ fw_cfg_kobj_cleanup(fw_cfg_sel_ko);
+ fw_cfg_kobj_cleanup(fw_cfg_top_ko);
+ fw_cfg_io_cleanup();
+}
+
+module_init(fw_cfg_sysfs_init);
+module_exit(fw_cfg_sysfs_exit);
+MODULE_AUTHOR("Gabriel L. Somlo <somlo@cmu.edu>");
+MODULE_DESCRIPTION("QEMU fw_cfg sysfs support");
+MODULE_LICENSE("GPL");
--
2.4.3
^ permalink raw reply related
* [PATCH v2 2/3] kobject: export kset_find_obj() to be used from modules
From: Gabriel L. Somlo @ 2015-08-11 18:44 UTC (permalink / raw)
To: gregkh, ralf, zajec5, paul, galak, linux-api, linux-kernel
Cc: linux-efi, rjones, jordan.l.justen, x86, qemu-devel, gleb,
matt.fleming, kernelnewbies, kraxel, pbonzini, lersek
In-Reply-To: <cover.1439315079.git.somlo@cmu.edu>
From: "Gabriel Somlo" <somlo@cmu.edu>
Signed-off-by: Gabriel Somlo <somlo@cmu.edu>
---
lib/kobject.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/lib/kobject.c b/lib/kobject.c
index 0554077..8f07202 100644
--- a/lib/kobject.c
+++ b/lib/kobject.c
@@ -847,6 +847,7 @@ struct kobject *kset_find_obj(struct kset *kset, const char *name)
spin_unlock(&kset->list_lock);
return ret;
}
+EXPORT_SYMBOL(kset_find_obj);
static void kset_release(struct kobject *kobj)
{
--
2.4.3
^ permalink raw reply related
* [PATCH v2 3/3] firmware: fw_cfg: create directory hierarchy for fw_cfg file names
From: Gabriel L. Somlo @ 2015-08-11 18:44 UTC (permalink / raw)
To: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
ralf-6z/3iImG2C8G8FEW9MqTrA, zajec5-Re5JQEeQqe8AvxtiuMwx3w,
paul-DWxLp4Yu+b8AvxtiuMwx3w, galak-sgV2jX0FEOL9JmXXK+q4OQ,
linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: matt.fleming-ral2JQCrhuEAvxtiuMwx3w, x86-DgEjT+Ai2ygdnm+yROfE0A,
linux-efi-u79uwXL29TY76Z2rM5mHXA,
qemu-devel-qX2TKyscuCcdnm+yROfE0A, lersek-H+wXaHxf7aLQT0dZR+AlfA,
jordan.l.justen-ral2JQCrhuEAvxtiuMwx3w,
gleb-RmZWMc9puTNJc61us3aD9laTQe2KTcn/,
pbonzini-H+wXaHxf7aLQT0dZR+AlfA, kraxel-H+wXaHxf7aLQT0dZR+AlfA,
eblake-H+wXaHxf7aLQT0dZR+AlfA, rjones-H+wXaHxf7aLQT0dZR+AlfA,
kernelnewbies-7JyXY6prKcjpASu1u0TL5ti2O/JbrIOy
In-Reply-To: <cover.1439315079.git.somlo-D+Gtc/HYRWM@public.gmane.org>
From: "Gabriel Somlo" <somlo-D+Gtc/HYRWM@public.gmane.org>
Each fw_cfg entry of type "file" has an associated 56-char,
nul-terminated ASCII string which represents its name. While
the fw_cfg device doesn't itself impose any specific naming
convention, QEMU developers have traditionally used path name
semantics (i.e. "etc/acpi/rsdp") to descriptively name the
various fw_cfg "blobs" passed into the guest.
This patch attempts, on a best effort basis, to create a
directory hierarchy representing the content of fw_cfg file
names, under /sys/firmware/qemu_fw_cfg/by_name.
Upon successful creation of all directories representing the
"dirname" portion of a fw_cfg file, a symlink will be created
to represent the "basename", pointing at the appropriate
/sys/firmware/qemu_fw_cfg/by_key entry. If a file name is not
suitable for this procedure (e.g., if its basename or dirname
components collide with an already existing dirname component
or basename, respectively) the corresponding fw_cfg blob is
skipped and will remain available in sysfs only by its selector
key value.
Signed-off-by: Gabriel Somlo <somlo-D+Gtc/HYRWM@public.gmane.org>
---
.../ABI/testing/sysfs-firmware-qemu_fw_cfg | 42 +++++++++
drivers/firmware/qemu_fw_cfg.c | 104 +++++++++++++++++++++
2 files changed, 146 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-firmware-qemu_fw_cfg b/Documentation/ABI/testing/sysfs-firmware-qemu_fw_cfg
index 5bc2e3f..db0b10d 100644
--- a/Documentation/ABI/testing/sysfs-firmware-qemu_fw_cfg
+++ b/Documentation/ABI/testing/sysfs-firmware-qemu_fw_cfg
@@ -166,3 +166,45 @@ Description:
entry via the control register, and reading a number
of bytes equal to the blob size from the data
register.
+
+ --- Listing fw_cfg blobs by file name ---
+
+ While the fw_cfg device does not impose any specific naming
+ convention on the blobs registered in the file directory,
+ QEMU developers have traditionally used path name semantics
+ to give each blob a descriptive name. For example:
+
+ "bootorder"
+ "genroms/kvmvapic.bin"
+ "etc/e820"
+ "etc/boot-fail-wait"
+ "etc/system-states"
+ "etc/table-loader"
+ "etc/acpi/rsdp"
+ "etc/acpi/tables"
+ "etc/smbios/smbios-tables"
+ "etc/smbios/smbios-anchor"
+ ...
+
+ In addition to the listing by unique selector key described
+ above, the fw_cfg sysfs driver also attempts to build a tree
+ of directories matching the path name components of fw_cfg
+ blob names, ending in symlinks to the by_key entry for each
+ "basename", as illustrated below (assume current directory is
+ /sys/firmware):
+
+ qemu_fw_cfg/by_name/bootorder -> ../by_key/38
+ qemu_fw_cfg/by_name/etc/e820 -> ../../by_key/35
+ qemu_fw_cfg/by_name/etc/acpi/rsdp -> ../../../by_key/41
+ ...
+
+ Construction of the directory tree and symlinks is done on a
+ "best-effort" basis, as there is no guarantee that components
+ of fw_cfg blob names are always "well behaved". I.e., there is
+ the possibility that a symlink (basename) will conflict with
+ a dirname component of another fw_cfg blob, in which case the
+ creation of the offending /sys/firmware/qemu_fw_cfg/by_name
+ entry will be skipped.
+
+ The authoritative list of entries will continue to be found
+ under the /sys/firmware/qemu_fw_cfg/by_key directory.
diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c
index cd26eee..b2fd222 100644
--- a/drivers/firmware/qemu_fw_cfg.c
+++ b/drivers/firmware/qemu_fw_cfg.c
@@ -345,9 +345,104 @@ static struct bin_attribute fw_cfg_sysfs_attr_raw = {
.read = fw_cfg_sysfs_read_raw,
};
+/*
+ * Create a kset subdirectory matching each '/' delimited dirname token
+ * in 'name', starting with sysfs kset/folder 'dir'; At the end, create
+ * a symlink directed at the given 'target'.
+ * NOTE: We do this on a best-effort basis, since 'name' is not guaranteed
+ * to be a well-behaved path name. Whenever a symlink vs. kset directory
+ * name collision occurs, the kernel will issue big scary warnings while
+ * refusing to add the offending link or directory. We follow up with our
+ * own, slightly less scary error messages explaining the situation :)
+ */
+static int __init fw_cfg_build_symlink(struct kset *dir,
+ struct kobject *target,
+ const char *name)
+{
+ int ret;
+ struct kset *subdir;
+ struct kobject *ko;
+ char *name_copy, *p, *tok;
+
+ if (!dir || !target || !name || !*name)
+ return -EINVAL;
+
+ /* clone a copy of name for parsing */
+ name_copy = p = kstrdup(name, GFP_KERNEL);
+ if (!name_copy)
+ return -ENOMEM;
+
+ /* create folders for each dirname token, then symlink for basename */
+ while ((tok = strsep(&p, "/")) && *tok) {
+
+ /* last (basename) token? If so, add symlink here */
+ if (!p || !*p) {
+ ret = sysfs_create_link(&dir->kobj, target, tok);
+ break;
+ }
+
+ /* does the current dir contain an item named after tok ? */
+ ko = kset_find_obj(dir, tok);
+ if (ko) {
+ /* drop reference added by kset_find_obj */
+ kobject_put(ko);
+
+ /* ko MUST be a kset - we're about to use it as one ! */
+ if (ko->ktype != dir->kobj.ktype) {
+ ret = -EINVAL;
+ break;
+ }
+
+ /* descend into already existing subdirectory */
+ dir = to_kset(ko);
+ } else {
+ /* create new subdirectory kset */
+ subdir = kzalloc(sizeof(struct kset), GFP_KERNEL);
+ if (!subdir) {
+ ret = -ENOMEM;
+ break;
+ }
+ subdir->kobj.kset = dir;
+ subdir->kobj.ktype = dir->kobj.ktype;
+ ret = kobject_set_name(&subdir->kobj, "%s", tok);
+ if (ret) {
+ kfree(subdir);
+ break;
+ }
+ ret = kset_register(subdir);
+ if (ret) {
+ kfree(subdir);
+ break;
+ }
+
+ /* descend into newly created subdirectory */
+ dir = subdir;
+ }
+ }
+
+ /* we're done with cloned copy of name */
+ kfree(name_copy);
+ return ret;
+}
+
+/* recursively unregister fw_cfg/by_name/ kset directory tree */
+static void fw_cfg_kset_unregister_recursive(struct kset *kset)
+{
+ struct kobject *k, *next;
+
+ list_for_each_entry_safe(k, next, &kset->list, entry)
+ /* all set members are ksets too, but check just in case... */
+ if (k->ktype == kset->kobj.ktype)
+ fw_cfg_kset_unregister_recursive(to_kset(k));
+
+ /* symlinks are cleanly and automatically removed with the directory */
+ kset_unregister(kset);
+}
+
/* kobjects & kset representing top-level, by_key, and by_name folders */
static struct kobject *fw_cfg_top_ko;
static struct kobject *fw_cfg_sel_ko;
+static struct kset *fw_cfg_fname_kset;
/* callback function to register an individual fw_cfg file */
static int __init fw_cfg_register_file(const struct fw_cfg_file *f)
@@ -376,6 +471,9 @@ static int __init fw_cfg_register_file(const struct fw_cfg_file *f)
if (err)
goto err_add_raw;
+ /* try adding "/sys/firmware/qemu_fw_cfg/by_name/" symlink */
+ fw_cfg_build_symlink(fw_cfg_fname_kset, &entry->kobj, entry->f.name);
+
/* success, add entry to global cache */
fw_cfg_sysfs_cache_enlist(entry);
return 0;
@@ -411,6 +509,9 @@ static int __init fw_cfg_sysfs_init(void)
fw_cfg_sel_ko = kobject_create_and_add("by_key", fw_cfg_top_ko);
if (!fw_cfg_sel_ko)
goto err_sel;
+ fw_cfg_fname_kset = kset_create_and_add("by_name", NULL, fw_cfg_top_ko);
+ if (!fw_cfg_fname_kset)
+ goto err_name;
/* get revision number, add matching top-level attribute */
fw_cfg_read_blob(FW_CFG_ID, &fw_cfg_rev, 0, sizeof(fw_cfg_rev));
@@ -432,6 +533,8 @@ err_scan:
fw_cfg_sysfs_cache_cleanup();
sysfs_remove_file(fw_cfg_top_ko, &fw_cfg_rev_attr.attr);
err_rev:
+ fw_cfg_kset_unregister_recursive(fw_cfg_fname_kset);
+err_name:
fw_cfg_kobj_cleanup(fw_cfg_sel_ko);
err_sel:
fw_cfg_kobj_cleanup(fw_cfg_top_ko);
@@ -444,6 +547,7 @@ static void __exit fw_cfg_sysfs_exit(void)
{
pr_debug("fw_cfg: unloading.\n");
fw_cfg_sysfs_cache_cleanup();
+ fw_cfg_kset_unregister_recursive(fw_cfg_fname_kset);
fw_cfg_kobj_cleanup(fw_cfg_sel_ko);
fw_cfg_kobj_cleanup(fw_cfg_top_ko);
fw_cfg_io_cleanup();
--
2.4.3
^ permalink raw reply related
* Re: [CFT][PATCH 09/10] sysfs: Create mountpoints with sysfs_create_empty_dir
From: Eric W. Biederman @ 2015-08-11 18:57 UTC (permalink / raw)
To: Tejun Heo
Cc: Linux Containers, linux-fsdevel, Linux API, Serge E. Hallyn,
Andy Lutomirski, Richard Weinberger, Kenton Varda,
Michael Kerrisk-manpages, Stéphane Graber, Eric Windisch,
Greg Kroah-Hartman
In-Reply-To: <20150811184426.GH23408@mtj.duckdns.org>
Tejun Heo <tj@kernel.org> writes:
> On Thu, May 14, 2015 at 12:36:30PM -0500, Eric W. Biederman wrote:
>>
>> This allows for better documentation in the code and
>> it allows for a simpler and fully correct version of
>> fs_fully_visible to be written.
>>
>> The mount points converted and their filesystems are:
>> /sys/hypervisor/s390/ s390_hypfs
>> /sys/kernel/config/ configfs
>> /sys/kernel/debug/ debugfs
>> /sys/firmware/efi/efivars/ efivarfs
>> /sys/fs/fuse/connections/ fusectl
>> /sys/fs/pstore/ pstore
>> /sys/kernel/tracing/ tracefs
>> /sys/fs/cgroup/ cgroup
>> /sys/kernel/security/ securityfs
>> /sys/fs/selinux/ selinuxfs
>> /sys/fs/smackfs/ smackfs
>>
>> Cc: stable@vger.kernel.org
>> Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
>
> So, this somehow ends up confusing upstart on centos6 based systems
> making it fail to mount tmpfs on /sys/fs/cgroup. It also skips sunrpc
> and other mounts are different too. No idea why at this point. Can
> we please revert this from -stable until we know what's going on?
*Boggle*
The only time this should prevent anything is when in a container when
you are not global root. And then only mounting sysfs should be
affected.
The only difference in executed code really should be setting an extra
flag on the kernfs, inode. The kernfs changes will also refuse to add
entries to these directories (but these directories are empty).
If this is causing problems I don't have a problem with a revert but
reverts take a minute, and this seems to be the first report of this
kind. Can we take a minute and attempt to get a coherent explanation.
>From what little information you given above it sounds like something
shifted and when you rebuilt the kernel and now a memory stomp is
hitting something else. It should be a matter of moments to debug this
issue (once a test environment is setup), and see what is wrong and then
we can act intelligently. Tracing a single system call is not difficult.
If there really is some weird issue I want to know what it is.
Eric
^ permalink raw reply
* Re: [CFT][PATCH 09/10] sysfs: Create mountpoints with sysfs_create_empty_dir
From: Andy Lutomirski @ 2015-08-11 19:21 UTC (permalink / raw)
To: Eric W. Biederman
Cc: Tejun Heo, Linux Containers, Linux FS Devel, Linux API,
Serge E. Hallyn, Richard Weinberger, Kenton Varda,
Michael Kerrisk-manpages, Stéphane Graber, Eric Windisch,
Greg Kroah-Hartman
In-Reply-To: <877fp1hcuj.fsf@x220.int.ebiederm.org>
On Tue, Aug 11, 2015 at 11:57 AM, Eric W. Biederman
<ebiederm@xmission.com> wrote:
> Tejun Heo <tj@kernel.org> writes:
>
>> On Thu, May 14, 2015 at 12:36:30PM -0500, Eric W. Biederman wrote:
>>>
>>> This allows for better documentation in the code and
>>> it allows for a simpler and fully correct version of
>>> fs_fully_visible to be written.
>>>
>>> The mount points converted and their filesystems are:
>>> /sys/hypervisor/s390/ s390_hypfs
>>> /sys/kernel/config/ configfs
>>> /sys/kernel/debug/ debugfs
>>> /sys/firmware/efi/efivars/ efivarfs
>>> /sys/fs/fuse/connections/ fusectl
>>> /sys/fs/pstore/ pstore
>>> /sys/kernel/tracing/ tracefs
>>> /sys/fs/cgroup/ cgroup
>>> /sys/kernel/security/ securityfs
>>> /sys/fs/selinux/ selinuxfs
>>> /sys/fs/smackfs/ smackfs
>>>
>>> Cc: stable@vger.kernel.org
>>> Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
>>
>> So, this somehow ends up confusing upstart on centos6 based systems
>> making it fail to mount tmpfs on /sys/fs/cgroup. It also skips sunrpc
>> and other mounts are different too. No idea why at this point. Can
>> we please revert this from -stable until we know what's going on?
>
> *Boggle*
>
> The only time this should prevent anything is when in a container when
> you are not global root. And then only mounting sysfs should be
> affected.
Before:
open("/sys/kernel/debug/asdf", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK,
0666) = -1 EACCES (Permission denied)
After:
open("/sys/kernel/debug/asdf", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK,
0666) = -1 ENOENT (No such file or directory)
Something broke. I don't know whether CentOS cares about that change,
but there could be other odd side effects.
--Andy
^ permalink raw reply
* Re: [CFT][PATCH 09/10] sysfs: Create mountpoints with sysfs_create_empty_dir
From: Tejun Heo @ 2015-08-11 20:11 UTC (permalink / raw)
To: Eric W. Biederman
Cc: Linux API, Linux Containers, Greg Kroah-Hartman, Andy Lutomirski,
Kenton Varda, Michael Kerrisk-manpages, Richard Weinberger,
LINUXFS-ML
In-Reply-To: <877fp1hcuj.fsf-JOvCrm2gF+uungPnsOpG7nhyD016LWXt@public.gmane.org>
Hey,
On Tue, Aug 11, 2015 at 2:57 PM, Eric W. Biederman
<ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org> wrote:
>> So, this somehow ends up confusing upstart on centos6 based systems
>> making it fail to mount tmpfs on /sys/fs/cgroup. It also skips sunrpc
>> and other mounts are different too. No idea why at this point. Can
>> we please revert this from -stable until we know what's going on?
>
> *Boggle*
>
> The only time this should prevent anything is when in a container when
> you are not global root. And then only mounting sysfs should be
> affected.
This is just plain boot. No namespace involved.
> The only difference in executed code really should be setting an extra
> flag on the kernfs, inode. The kernfs changes will also refuse to add
> entries to these directories (but these directories are empty).
Why do we have this in -stable then? Is this part of a larger fix?
> If this is causing problems I don't have a problem with a revert but
> reverts take a minute, and this seems to be the first report of this
> kind. Can we take a minute and attempt to get a coherent explanation.
>
> From what little information you given above it sounds like something
> shifted and when you rebuilt the kernel and now a memory stomp is
> hitting something else. It should be a matter of moments to debug this
I don't think it's a random memory stomping thing. I reverted the
commit from two different kernels and the result was always
consistent.
> issue (once a test environment is setup), and see what is wrong and then
> we can act intelligently. Tracing a single system call is not difficult.
I'm already out today so it'll have to wait till tomorrow.
> If there really is some weird issue I want to know what it is.
Sure, but you wanna do that in -stable?
Thanks.
--
tejun
^ permalink raw reply
* Re: [PATCH 1/6] block: cleanup blkdev_ioctl
From: Martin K. Petersen @ 2015-08-12 0:29 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Jens Axboe, linux-scsi-u79uwXL29TY76Z2rM5mHXA,
linux-nvme-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
dm-devel-H+wXaHxf7aLQT0dZR+AlfA, linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1438672271-11309-2-git-send-email-hch-jcswGhMUV9g@public.gmane.org>
>>>>> "Christoph" == Christoph Hellwig <hch-jcswGhMUV9g@public.gmane.org> writes:
Christoph> Split out helpers for all non-trivial ioctls to make this
Christoph> function simpler, and also start passing around a pointer
Christoph> version of the argument, as that's what most ioctl handlers
Christoph> actually need.
Looks good.
Reviewed-by: Martin K. Petersen <martin.petersen-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org>
--
Martin K. Petersen Oracle Linux Engineering
^ permalink raw reply
* Re: [CFT][PATCH 09/10] sysfs: Create mountpoints with sysfs_create_empty_dir
From: Eric W. Biederman @ 2015-08-12 0:37 UTC (permalink / raw)
To: Tejun Heo
Cc: Linux Containers, LINUXFS-ML, Linux API, Serge E. Hallyn,
Andy Lutomirski, Richard Weinberger, Kenton Varda,
Michael Kerrisk-manpages, Stéphane Graber, Eric Windisch,
Greg Kroah-Hartman
In-Reply-To: <CAOS58YOHU8SFv4UXeBRr4t88UU=DXQCPg2HU_dMBmgM7WBB1zQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Tejun Heo <tj-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> writes:
> Hey,
>
> On Tue, Aug 11, 2015 at 2:57 PM, Eric W. Biederman
> <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org> wrote:
>>> So, this somehow ends up confusing upstart on centos6 based systems
>>> making it fail to mount tmpfs on /sys/fs/cgroup. It also skips sunrpc
>>> and other mounts are different too. No idea why at this point. Can
>>> we please revert this from -stable until we know what's going on?
>>
>> *Boggle*
>>
>> The only time this should prevent anything is when in a container when
>> you are not global root. And then only mounting sysfs should be
>> affected.
>
> This is just plain boot. No namespace involved.
>
>> The only difference in executed code really should be setting an extra
>> flag on the kernfs, inode. The kernfs changes will also refuse to add
>> entries to these directories (but these directories are empty).
>
> Why do we have this in -stable then? Is this part of a larger fix?
It is. This patch is part of the prep work to prevent unprivileged users
not mounting sysfs (using user namespace permissions) when they should
not be allowed to.
>> If this is causing problems I don't have a problem with a revert but
>> reverts take a minute, and this seems to be the first report of this
>> kind. Can we take a minute and attempt to get a coherent explanation.
>>
>> It should be a matter of moments to debug this
>> issue (once a test environment is setup), and see what is wrong and then
>> we can act intelligently. Tracing a single system call is not difficult.
>
> I'm already out today so it'll have to wait till tomorrow.
>
>> If there really is some weird issue I want to know what it is.
>
> Sure, but you wanna do that in -stable?
Before fixing anything I want a bug report that is clear enough
to be reproducible.
I just went and attempted to reproduce this, and on RHEL6 workstation
(aka my work laptop), using the todays 4.2.0-rc6+ aka
edf15b4d4b01b565cb5f4fd2e2d08940b9f92e2f and all of the mounts in
/proc/self/mounts are the same between 4.2.0-rc6 and the RHEL6 stock
2.6.32-504.30.3.el6.x86_64, including the cgroups mounted on /cgroup.
Which means that I don't have any reason to believe that normal CentOS 6
is broken.
Which -stable kernel are you having problems with? Perhaps it was
a broken backport?
Is it possible this is a local CentOS 6 hack that is breaking?
Perhaps a patch you apply on top of your -stable kernel?
Certainly with cgroups expected to be mounted at /sys/fs/cgroup there
has clearly been at least one change from the stock configuration.
I think it is a little less serious if stock CentOS 6 doesn't have
problems. Unless it is a conflict of kernel patches I definitely think
whatever it is needs to be fixed.
Eric
^ permalink raw reply
* Re: [PATCH 2/6] block: add a API for persistent reservations
From: Martin K. Petersen @ 2015-08-12 0:45 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Jens Axboe, linux-scsi-u79uwXL29TY76Z2rM5mHXA,
linux-nvme-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
dm-devel-H+wXaHxf7aLQT0dZR+AlfA, linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1438672271-11309-3-git-send-email-hch-jcswGhMUV9g@public.gmane.org>
>>>>> "Christoph" == Christoph Hellwig <hch-jcswGhMUV9g@public.gmane.org> writes:
Happy to see a generic interface for this. I wish PR semantics were
simpler but the code looks good to me.
Reviewed-by: Martin K. Petersen <martin.petersen-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org>
--
Martin K. Petersen Oracle Linux Engineering
^ permalink raw reply
* Re: [PATCH 3/6] sd: implement the persisten reservation API
From: Martin K. Petersen @ 2015-08-12 0:57 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Jens Axboe, linux-scsi-u79uwXL29TY76Z2rM5mHXA,
linux-nvme-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
dm-devel-H+wXaHxf7aLQT0dZR+AlfA, linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1438672271-11309-4-git-send-email-hch-jcswGhMUV9g@public.gmane.org>
>>>>> "Christoph" == Christoph Hellwig <hch-jcswGhMUV9g@public.gmane.org> writes:
Slightly more verbose patch description would be nice. Code looks OK.
Reviewed-by: Martin K. Petersen <martin.petersen-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org>
--
Martin K. Petersen Oracle Linux Engineering
^ permalink raw reply
* Re: [CFT][PATCH 09/10] sysfs: Create mountpoints with sysfs_create_empty_dir
From: Eric W. Biederman @ 2015-08-12 0:58 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Tejun Heo, Linux Containers, Linux FS Devel, Linux API,
Serge E. Hallyn, Richard Weinberger, Kenton Varda,
Michael Kerrisk-manpages, Stéphane Graber, Eric Windisch,
Greg Kroah-Hartman
In-Reply-To: <CALCETrXE=fKa3XkEEo6y2=ZNtsuBfX=kaoyDwiP0C2BwqKJWjw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Andy Lutomirski <luto-kltTT9wpgjJwATOyAt5JVQ@public.gmane.org> writes:
> On Tue, Aug 11, 2015 at 11:57 AM, Eric W. Biederman
> <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org> wrote:
>>
>> *Boggle*
>>
>> The only time this should prevent anything is when in a container when
>> you are not global root. And then only mounting sysfs should be
>> affected.
>
> Before:
>
> open("/sys/kernel/debug/asdf", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK,
> 0666) = -1 EACCES (Permission denied)
>
>
> After:
>
> open("/sys/kernel/debug/asdf", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK,
> 0666) = -1 ENOENT (No such file or directory)
>
> Something broke. I don't know whether CentOS cares about that change,
> but there could be other odd side effects.
Thanks for pointing this out. I don't know if broke is quite the right
word for a change in error codes on lookup failure, but I agree it is a
difference that could have affected something.
The behavior of empty proc dirs actually return -ENOENT in this
situation and so it is a little fuzzy about which is the best behavior
to use.
Creativing a negative dentry and and then letting vfs_create fail may be
the better way to go.
Negative dentries are weird enough that I would prefer not to have code
that creates negative dentries. They could easily be a lurking trap
for some filesystems dentry operations.
The patch below is enough to change the error code if someone who can
reproduce this wants to try this.
Eric
diff --gdiff --git a/fs/libfs.c b/fs/libfs.c
index 102edfd39000..3a452a485cbf 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -1109,7 +1109,7 @@ EXPORT_SYMBOL(simple_symlink_inode_operations);
*/
static struct dentry *empty_dir_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
{
- return ERR_PTR(-ENOENT);
+ return NULL;
}
static int empty_dir_getattr(struct vfsmount *mnt, struct dentry *dentry,
^ permalink raw reply related
* Re: [CFT][PATCH 09/10] sysfs: Create mountpoints with sysfs_create_empty_dir
From: Eric W. Biederman @ 2015-08-12 3:58 UTC (permalink / raw)
To: Tejun Heo
Cc: Linux API, Linux Containers, Greg Kroah-Hartman, Andy Lutomirski,
Kenton Varda, Michael Kerrisk-manpages, Richard Weinberger,
LINUXFS-ML
In-Reply-To: <87fv3pe3zn.fsf-JOvCrm2gF+uungPnsOpG7nhyD016LWXt@public.gmane.org>
ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org (Eric W. Biederman) writes:
> I just went and attempted to reproduce this, and on RHEL6 workstation
> (aka my work laptop), using the todays 4.2.0-rc6+ aka
> edf15b4d4b01b565cb5f4fd2e2d08940b9f92e2f and all of the mounts in
> /proc/self/mounts are the same between 4.2.0-rc6 and the RHEL6 stock
> 2.6.32-504.30.3.el6.x86_64, including the cgroups mounted on /cgroup.
I built a few more kernels just to see if this was some weird backport
thing. The kernels 3.10.86, 3.14.58, 3.18.20, and 4.1.5 all boot and
mount their cgroup filesystems just fine. Granted I kept having to
smack the memory cgroup into being compiled in as the config options
kept changing but otherwise I have not seen any problems.
So I am very surprised you are having problems.
Eric
^ permalink raw reply
* Re: [CFT][PATCH 09/10] sysfs: Create mountpoints with sysfs_create_empty_dir
From: Eric W. Biederman @ 2015-08-12 4:04 UTC (permalink / raw)
To: Tejun Heo
Cc: Linux API, Linux Containers, Greg Kroah-Hartman, Andy Lutomirski,
Kenton Varda, Michael Kerrisk-manpages, Richard Weinberger,
LINUXFS-ML
In-Reply-To: <87a8txb1k8.fsf-JOvCrm2gF+uungPnsOpG7nhyD016LWXt@public.gmane.org>
ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org (Eric W. Biederman) writes:
> ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org (Eric W. Biederman) writes:
>
>> I just went and attempted to reproduce this, and on RHEL6 workstation
>> (aka my work laptop), using the todays 4.2.0-rc6+ aka
>> edf15b4d4b01b565cb5f4fd2e2d08940b9f92e2f and all of the mounts in
>> /proc/self/mounts are the same between 4.2.0-rc6 and the RHEL6 stock
>> 2.6.32-504.30.3.el6.x86_64, including the cgroups mounted on /cgroup.
>
> I built a few more kernels just to see if this was some weird backport
> thing. The kernels 3.10.86, 3.14.58, 3.18.20, and 4.1.5 all boot and
> mount their cgroup filesystems just fine. Granted I kept having to
> smack the memory cgroup into being compiled in as the config options
> kept changing but otherwise I have not seen any problems.
>
> So I am very surprised you are having problems.
Although I guess I could have saved myself some time by noticing that
4.1.5 was the only one of the kernels with the change backported into
it. *Shrug*
I don't see the problem and I don't know where to look to see why you
are having problems.
Eric
^ permalink raw reply
* Re: [Qemu-devel] [PATCH 19/23] userfaultfd: activate syscall
From: Bharata B Rao @ 2015-08-12 5:23 UTC (permalink / raw)
To: Andrea Arcangeli
Cc: Andrew Morton, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-mm-Bw31MaZKKs3YtjvyW6yDsg,
qemu-devel-qX2TKyscuCcdnm+yROfE0A, kvm-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA,
zhang.zhanghailiang-hv44wF8Li93QT0dZR+AlfA, Pavel Emelyanov,
Johannes Weiner, Hugh Dickins, Dr. David Alan Gilbert,
Sanidhya Kashyap, Dave Hansen, Andres Lagar-Cavilla, Mel Gorman,
Paolo Bonzini, Kirill A. Shutemov, Huangpeng (Peter),
Andy Lutomirski, Linus Torvalds, Peter Feiner
In-Reply-To: <20150811134826.GI4520-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
On Tue, Aug 11, 2015 at 03:48:26PM +0200, Andrea Arcangeli wrote:
> Hello Bharata,
>
> On Tue, Aug 11, 2015 at 03:37:29PM +0530, Bharata B Rao wrote:
> > May be it is a bit late to bring this up, but I needed the following fix
> > to userfault21 branch of your git tree to compile on powerpc.
>
> Not late, just in time. I increased the number of syscalls in earlier
> versions, it must have gotten lost during a rejecting rebase, sorry.
>
> I applied it to my tree and it can be applied to -mm and linux-next,
> thanks!
>
> The syscall for arm32 are also ready and on their way to the arm tree,
> the testsuite worked fine there. ppc also should work fine if you
> could confirm it'd be interesting, just beware that I got a typo in
> the testcase:
The testsuite passes on powerpc.
--------------------
running userfaultfd
--------------------
nr_pages: 2040, nr_pages_per_cpu: 170
bounces: 31, mode: rnd racing ver poll, userfaults: 80 43 23 23 15 16 12 1 2 96 13 128
bounces: 30, mode: racing ver poll, userfaults: 35 54 62 49 47 48 2 8 0 78 1 0
bounces: 29, mode: rnd ver poll, userfaults: 114 153 70 106 78 57 143 92 114 96 1 0
bounces: 28, mode: ver poll, userfaults: 96 81 5 45 83 19 98 28 1 145 23 2
bounces: 27, mode: rnd racing poll, userfaults: 54 65 60 54 45 49 1 2 1 2 71 20
bounces: 26, mode: racing poll, userfaults: 90 83 35 29 37 35 30 42 3 4 49 6
bounces: 25, mode: rnd poll, userfaults: 52 50 178 112 51 41 23 42 18 99 59 0
bounces: 24, mode: poll, userfaults: 136 101 83 260 84 29 16 88 1 6 160 57
bounces: 23, mode: rnd racing ver, userfaults: 141 197 158 183 39 49 3 52 8 3 6 0
bounces: 22, mode: racing ver, userfaults: 242 266 244 180 162 32 87 43 31 40 34 0
bounces: 21, mode: rnd ver, userfaults: 636 158 175 24 253 104 48 8 0 0 0 0
bounces: 20, mode: ver, userfaults: 531 204 225 117 129 107 11 143 76 31 1 0
bounces: 19, mode: rnd racing, userfaults: 303 169 225 145 59 219 37 0 0 0 0 0
bounces: 18, mode: racing, userfaults: 374 372 37 144 126 90 25 12 15 17 0 0
bounces: 17, mode: rnd, userfaults: 313 412 134 108 80 99 7 56 85 0 0 0
bounces: 16, mode:, userfaults: 431 58 87 167 120 113 98 60 14 8 48 0
bounces: 15, mode: rnd racing ver poll, userfaults: 41 40 25 28 37 24 0 0 0 0 180 75
bounces: 14, mode: racing ver poll, userfaults: 43 53 30 28 25 15 19 0 0 0 0 30
bounces: 13, mode: rnd ver poll, userfaults: 136 91 114 91 92 79 114 77 75 68 1 2
bounces: 12, mode: ver poll, userfaults: 92 120 114 76 153 75 132 157 83 81 10 1
bounces: 11, mode: rnd racing poll, userfaults: 50 72 69 52 53 48 46 59 57 51 37 1
bounces: 10, mode: racing poll, userfaults: 33 49 38 68 35 63 57 49 49 47 25 10
bounces: 9, mode: rnd poll, userfaults: 167 150 67 123 39 75 1 2 9 125 1 1
bounces: 8, mode: poll, userfaults: 147 102 20 87 5 27 118 14 104 40 21 28
bounces: 7, mode: rnd racing ver, userfaults: 305 254 208 74 59 96 36 14 11 7 4 5
bounces: 6, mode: racing ver, userfaults: 290 114 191 94 162 114 34 6 6 32 23 2
bounces: 5, mode: rnd ver, userfaults: 370 381 22 273 21 106 17 55 0 0 0 0
bounces: 4, mode: ver, userfaults: 328 279 179 191 74 86 95 15 13 10 0 0
bounces: 3, mode: rnd racing, userfaults: 222 215 164 70 5 20 179 0 34 3 0 0
bounces: 2, mode: racing, userfaults: 316 385 112 160 225 5 30 49 42 2 4 0
bounces: 1, mode: rnd, userfaults: 273 139 253 176 163 71 85 2 0 0 0 0
bounces: 0, mode:, userfaults: 165 212 633 13 24 66 24 27 15 0 10 1
[PASS]
Regards,
Bharata.
^ permalink raw reply
* Persistent Reservation API V2
From: Christoph Hellwig @ 2015-08-12 6:23 UTC (permalink / raw)
To: Jens Axboe
Cc: linux-scsi-u79uwXL29TY76Z2rM5mHXA,
linux-nvme-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
dm-devel-H+wXaHxf7aLQT0dZR+AlfA, linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
This series adds support for a simplified Persistent Reservation API
to the block layer. The intent is that both in-kernel and userspace
consumers can use the API instead of having to hand craft SCSI or NVMe
command through the various pass through interfaces. It also adds
DM support as getting reservations through dm-multipath is a major
pain with the current scheme.
NVMe support currently isn't included as I don't have a multihost
NVMe setup to test on, but Keith offered to test it and I'll have
a patch for it shortly.
The ioctl API is documented in Documentation/block/pr.txt, but to
fully understand the concept you'll have to read up the SPC spec,
PRs are too complicated that trying to rephrase them into different
terminology is just going to create confusion.
Note that Mike wants to include the DM patches so through the DM
tree, so they are only included for reference.
I also have a set of simple test tools available at:
git://git.infradead.org/users/hch/pr-tests.git
Changes since V1:
- rename DM ->ioctl to ->prepare_ioctl
- rename dm_get_ioctl_table to dm_get_live_table_for_ioctl
- merge two DM patches into one
- various spelling fixes
^ permalink raw reply
* [PATCH 1/5] block: cleanup blkdev_ioctl
From: Christoph Hellwig @ 2015-08-12 6:23 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-scsi, linux-nvme, dm-devel, linux-api, linux-kernel
In-Reply-To: <1439360604-16192-1-git-send-email-hch@lst.de>
Split out helpers for all non-trivial ioctls to make this function simpler,
and also start passing around a pointer version of the argument, as that's
what most ioctl handlers actually need.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
block/ioctl.c | 227 ++++++++++++++++++++++++++++++++--------------------------
1 file changed, 127 insertions(+), 100 deletions(-)
diff --git a/block/ioctl.c b/block/ioctl.c
index 8061eba..df62b47 100644
--- a/block/ioctl.c
+++ b/block/ioctl.c
@@ -193,10 +193,20 @@ int blkdev_reread_part(struct block_device *bdev)
}
EXPORT_SYMBOL(blkdev_reread_part);
-static int blk_ioctl_discard(struct block_device *bdev, uint64_t start,
- uint64_t len, int secure)
+static int blk_ioctl_discard(struct block_device *bdev, fmode_t mode,
+ unsigned long arg, unsigned long flags)
{
- unsigned long flags = 0;
+ uint64_t range[2];
+ uint64_t start, len;
+
+ if (!(mode & FMODE_WRITE))
+ return -EBADF;
+
+ if (copy_from_user(range, (void __user *)arg, sizeof(range)))
+ return -EFAULT;
+
+ start = range[0];
+ len = range[1];
if (start & 511)
return -EINVAL;
@@ -207,14 +217,24 @@ static int blk_ioctl_discard(struct block_device *bdev, uint64_t start,
if (start + len > (i_size_read(bdev->bd_inode) >> 9))
return -EINVAL;
- if (secure)
- flags |= BLKDEV_DISCARD_SECURE;
return blkdev_issue_discard(bdev, start, len, GFP_KERNEL, flags);
}
-static int blk_ioctl_zeroout(struct block_device *bdev, uint64_t start,
- uint64_t len)
+static int blk_ioctl_zeroout(struct block_device *bdev, fmode_t mode,
+ unsigned long arg)
{
+ uint64_t range[2];
+ uint64_t start, len;
+
+ if (!(mode & FMODE_WRITE))
+ return -EBADF;
+
+ if (copy_from_user(range, (void __user *)arg, sizeof(range)))
+ return -EFAULT;
+
+ start = range[0];
+ len = range[1];
+
if (start & 511)
return -EINVAL;
if (len & 511)
@@ -295,89 +315,115 @@ static inline int is_unrecognized_ioctl(int ret)
ret == -ENOIOCTLCMD;
}
-/*
- * always keep this in sync with compat_blkdev_ioctl()
- */
-int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
- unsigned long arg)
+static int blkdev_flushbuf(struct block_device *bdev, fmode_t mode,
+ unsigned cmd, unsigned long arg)
{
- struct gendisk *disk = bdev->bd_disk;
- struct backing_dev_info *bdi;
- loff_t size;
- int ret, n;
- unsigned int max_sectors;
+ int ret;
- switch(cmd) {
- case BLKFLSBUF:
- if (!capable(CAP_SYS_ADMIN))
- return -EACCES;
-
- ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
- if (!is_unrecognized_ioctl(ret))
- return ret;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
- fsync_bdev(bdev);
- invalidate_bdev(bdev);
- return 0;
+ ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
+ if (!is_unrecognized_ioctl(ret))
+ return ret;
- case BLKROSET:
- ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
- if (!is_unrecognized_ioctl(ret))
- return ret;
- if (!capable(CAP_SYS_ADMIN))
- return -EACCES;
- if (get_user(n, (int __user *)(arg)))
- return -EFAULT;
- set_device_ro(bdev, n);
- return 0;
+ fsync_bdev(bdev);
+ invalidate_bdev(bdev);
+ return 0;
+}
- case BLKDISCARD:
- case BLKSECDISCARD: {
- uint64_t range[2];
+static int blkdev_roset(struct block_device *bdev, fmode_t mode,
+ unsigned cmd, unsigned long arg)
+{
+ int ret, n;
- if (!(mode & FMODE_WRITE))
- return -EBADF;
+ ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
+ if (!is_unrecognized_ioctl(ret))
+ return ret;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ if (get_user(n, (int __user *)arg))
+ return -EFAULT;
+ set_device_ro(bdev, n);
+ return 0;
+}
- if (copy_from_user(range, (void __user *)arg, sizeof(range)))
- return -EFAULT;
+static int blkdev_getgeo(struct block_device *bdev,
+ struct hd_geometry __user *argp)
+{
+ struct gendisk *disk = bdev->bd_disk;
+ struct hd_geometry geo;
+ int ret;
- return blk_ioctl_discard(bdev, range[0], range[1],
- cmd == BLKSECDISCARD);
- }
- case BLKZEROOUT: {
- uint64_t range[2];
+ if (!argp)
+ return -EINVAL;
+ if (!disk->fops->getgeo)
+ return -ENOTTY;
+
+ /*
+ * We need to set the startsect first, the driver may
+ * want to override it.
+ */
+ memset(&geo, 0, sizeof(geo));
+ geo.start = get_start_sect(bdev);
+ ret = disk->fops->getgeo(bdev, &geo);
+ if (ret)
+ return ret;
+ if (copy_to_user(argp, &geo, sizeof(geo)))
+ return -EFAULT;
+ return 0;
+}
- if (!(mode & FMODE_WRITE))
- return -EBADF;
+/* set the logical block size */
+static int blkdev_bszset(struct block_device *bdev, fmode_t mode,
+ int __user *argp)
+{
+ int ret, n;
- if (copy_from_user(range, (void __user *)arg, sizeof(range)))
- return -EFAULT;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ if (!argp)
+ return -EINVAL;
+ if (get_user(n, argp))
+ return -EFAULT;
- return blk_ioctl_zeroout(bdev, range[0], range[1]);
+ if (!(mode & FMODE_EXCL)) {
+ bdgrab(bdev);
+ if (blkdev_get(bdev, mode | FMODE_EXCL, &bdev) < 0)
+ return -EBUSY;
}
- case HDIO_GETGEO: {
- struct hd_geometry geo;
+ ret = set_blocksize(bdev, n);
+ if (!(mode & FMODE_EXCL))
+ blkdev_put(bdev, mode | FMODE_EXCL);
+ return ret;
+}
- if (!arg)
- return -EINVAL;
- if (!disk->fops->getgeo)
- return -ENOTTY;
-
- /*
- * We need to set the startsect first, the driver may
- * want to override it.
- */
- memset(&geo, 0, sizeof(geo));
- geo.start = get_start_sect(bdev);
- ret = disk->fops->getgeo(bdev, &geo);
- if (ret)
- return ret;
- if (copy_to_user((struct hd_geometry __user *)arg, &geo,
- sizeof(geo)))
- return -EFAULT;
- return 0;
- }
+/*
+ * always keep this in sync with compat_blkdev_ioctl()
+ */
+int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
+ unsigned long arg)
+{
+ struct backing_dev_info *bdi;
+ void __user *argp = (void __user *)arg;
+ loff_t size;
+ unsigned int max_sectors;
+
+ switch (cmd) {
+ case BLKFLSBUF:
+ return blkdev_flushbuf(bdev, mode, cmd, arg);
+ case BLKROSET:
+ return blkdev_roset(bdev, mode, cmd, arg);
+ case BLKDISCARD:
+ return blk_ioctl_discard(bdev, mode, arg, 0);
+ case BLKSECDISCARD:
+ return blk_ioctl_discard(bdev, mode, arg,
+ BLKDEV_DISCARD_SECURE);
+ case BLKZEROOUT:
+ return blk_ioctl_zeroout(bdev, mode, arg);
+ case HDIO_GETGEO:
+ return blkdev_getgeo(bdev, argp);
case BLKRAGET:
case BLKFRAGET:
if (!arg)
@@ -414,28 +460,11 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE;
return 0;
case BLKBSZSET:
- /* set the logical block size */
- if (!capable(CAP_SYS_ADMIN))
- return -EACCES;
- if (!arg)
- return -EINVAL;
- if (get_user(n, (int __user *) arg))
- return -EFAULT;
- if (!(mode & FMODE_EXCL)) {
- bdgrab(bdev);
- if (blkdev_get(bdev, mode | FMODE_EXCL, &bdev) < 0)
- return -EBUSY;
- }
- ret = set_blocksize(bdev, n);
- if (!(mode & FMODE_EXCL))
- blkdev_put(bdev, mode | FMODE_EXCL);
- return ret;
+ return blkdev_bszset(bdev, mode, argp);
case BLKPG:
- ret = blkpg_ioctl(bdev, (struct blkpg_ioctl_arg __user *) arg);
- break;
+ return blkpg_ioctl(bdev, argp);
case BLKRRPART:
- ret = blkdev_reread_part(bdev);
- break;
+ return blkdev_reread_part(bdev);
case BLKGETSIZE:
size = i_size_read(bdev->bd_inode);
if ((size >> 9) > ~0UL)
@@ -447,11 +476,9 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
case BLKTRACESTOP:
case BLKTRACESETUP:
case BLKTRACETEARDOWN:
- ret = blk_trace_ioctl(bdev, cmd, (char __user *) arg);
- break;
+ return blk_trace_ioctl(bdev, cmd, argp);
default:
- ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
+ return __blkdev_driver_ioctl(bdev, mode, cmd, arg);
}
- return ret;
}
EXPORT_SYMBOL_GPL(blkdev_ioctl);
--
1.9.1
^ permalink raw reply related
* [PATCH 2/5] block: add an API for Persistent Reservations
From: Christoph Hellwig @ 2015-08-12 6:23 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-scsi, linux-nvme, dm-devel, linux-api, linux-kernel
In-Reply-To: <1439360604-16192-1-git-send-email-hch@lst.de>
This commits adds a driver API and ioctls for controlling Persistent
Reservations s/genericly/generically/ at the block layer. Persistent
Reservations are supported by SCSI and NVMe and allow controlling who gets
access to a device in a shared storage setup.
Note that we add a pr_ops structure to struct block_device_operations
instead of adding the members directly to avoid bloating all instances
of devices that will never support Persistent Reservations.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
Documentation/block/pr.txt | 107 +++++++++++++++++++++++++++++++++++++++++++++
block/ioctl.c | 85 +++++++++++++++++++++++++++++++++++
include/linux/blkdev.h | 2 +
include/linux/pr.h | 18 ++++++++
include/uapi/linux/pr.h | 45 +++++++++++++++++++
5 files changed, 257 insertions(+)
create mode 100644 Documentation/block/pr.txt
create mode 100644 include/linux/pr.h
create mode 100644 include/uapi/linux/pr.h
diff --git a/Documentation/block/pr.txt b/Documentation/block/pr.txt
new file mode 100644
index 0000000..ccd11c4
--- /dev/null
+++ b/Documentation/block/pr.txt
@@ -0,0 +1,107 @@
+
+Block layer support for Persistent Reservations
+===============================================
+
+The Linux kernel supports a user space interface for simplified
+Persistent Reservations which map to block devices that support
+these (like SCSI). Persistent Reservations allow restricting
+access to block devices to specific initiators in a shared storage
+setup.
+
+This document gives a general overview of the support ioctl commands.
+For a more detailed reference please refer the the SCSI Primary
+Commands standard, specifically the section on Reservations and the
+"PERSISTENT RESERVE IN" and "PERSISTENT RESERVE OUT" commands.
+
+All implementations are expected to ensure the reservations survive
+a power loss and cover all connections in a multi path environment.
+These behaviors are optional in SPC but will be automatically applied
+by Linux.
+
+The following types of reservations are supported:
+
+ - PR_WRITE_EXCLUSIVE
+
+ Only the initiator that owns the reservation can write to the
+ device. Any initiator can read from the device.
+
+ - PR_EXCLUSIVE_ACCESS
+
+ Only the initiator that owns the reservation can access the
+ device.
+
+ - PR_WRITE_EXCLUSIVE_REG_ONLY
+
+ Only initiators with a registered key can write to the device,
+ Any initiator can read from the device.
+
+ - PR_EXCLUSIVE_ACCESS_REG_ONLY
+
+ Only initiators with a registered key can access the device.
+
+ - PR_WRITE_EXCLUSIVE_ALL_REGS
+
+ Only initiators with a registered key can write to the device,
+ Any initiator can read from the device.
+ All initiators with a registered key are considered reservation
+ holders.
+ Please reference the SPC spec on the meaning of a reservation
+ holder if you want to use this type.
+
+ - PR_EXCLUSIVE_ACCESS_ALL_REGS
+
+ Only initiators with a registered key can access the device.
+ All initiators with a registered key are considered reservation
+ holders.
+ Please reference the SPC spec on the meaning of a reservation
+ holder if you want to use this type.
+
+
+1. IOC_PR_REGISTER
+
+This ioctl command registers a new reservation if the new_key argument
+is non-null. If no existing reservation exists old_key must be zero,
+if an existing reservation should be replaced old_key must contain
+the old reservation key.
+
+If the new_key argument is 0 it unregisters the existing reservation passed
+in old_key.
+
+
+2. IOC_PR_REGISTER_IGNORE
+
+This ioctl command registers a new reservations with the key passed in
+new_key, ignoring and replacing any existing previous reservation. The
+old_key argument is ignored.
+
+
+3. IOC_PR_RESERVE
+
+This ioctl command reserves the device and thus restricts access for other
+devices based on the type argument. The key argument must be the existing
+reservation key for the device as acquired by the IOC_PR_REGISTER,
+IOC_PR_REGISTER_IGNORE, IOC_PR_PREEMPT or IOC_PR_PREEMPT_ABORT commands.
+
+
+4. IOC_PR_RELEASE
+
+This ioctl command releases the reservation specified by key and flags
+and thus removes any access restriction implied by it.
+
+
+5. IOC_PR_PREEMPT
+
+This ioctl command releases the existing reservation referred to by
+old_key and replaces it with a a new reservation of type for the
+reservation key new_key.
+
+
+6. IOC_PR_PREEMPT_ABORT
+
+This ioctl command works like IOC_PR_PREEMPT except that it also aborts
+any outstanding command sent over a connection identified by old_key.
+
+7. IOC_PR_CLEAR
+
+This ioctl command unregisters both key and any other reservation key
+registered with the device and drops any existing reservation.
diff --git a/block/ioctl.c b/block/ioctl.c
index df62b47..34d8c77 100644
--- a/block/ioctl.c
+++ b/block/ioctl.c
@@ -7,6 +7,7 @@
#include <linux/backing-dev.h>
#include <linux/fs.h>
#include <linux/blktrace_api.h>
+#include <linux/pr.h>
#include <asm/uaccess.h>
static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user *arg)
@@ -295,6 +296,76 @@ int __blkdev_driver_ioctl(struct block_device *bdev, fmode_t mode,
*/
EXPORT_SYMBOL_GPL(__blkdev_driver_ioctl);
+static int blkdev_pr_register(struct block_device *bdev,
+ struct pr_registration __user *arg, bool ignore)
+{
+ const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
+ struct pr_registration reg;
+
+ if (!ops || !ops->pr_register)
+ return -EOPNOTSUPP;
+ if (copy_from_user(®, arg, sizeof(reg)))
+ return -EFAULT;
+
+ return ops->pr_register(bdev, reg.old_key, reg.new_key, ignore);
+}
+
+static int blkdev_pr_reserve(struct block_device *bdev,
+ struct pr_reservation __user *arg)
+{
+ const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
+ struct pr_reservation rsv;
+
+ if (!ops || !ops->pr_reserve)
+ return -EOPNOTSUPP;
+ if (copy_from_user(&rsv, arg, sizeof(rsv)))
+ return -EFAULT;
+
+ return ops->pr_reserve(bdev, rsv.key, rsv.type);
+}
+
+static int blkdev_pr_release(struct block_device *bdev,
+ struct pr_reservation __user *arg)
+{
+ const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
+ struct pr_reservation rsv;
+
+ if (!ops || !ops->pr_release)
+ return -EOPNOTSUPP;
+ if (copy_from_user(&rsv, arg, sizeof(rsv)))
+ return -EFAULT;
+
+ return ops->pr_release(bdev, rsv.key, rsv.type);
+}
+
+static int blkdev_pr_preempt(struct block_device *bdev,
+ struct pr_preempt __user *arg, bool abort)
+{
+ const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
+ struct pr_preempt p;
+
+ if (!ops || !ops->pr_preempt)
+ return -EOPNOTSUPP;
+ if (copy_from_user(&p, arg, sizeof(p)))
+ return -EFAULT;
+
+ return ops->pr_preempt(bdev, p.old_key, p.new_key, p.type, abort);
+}
+
+static int blkdev_pr_clear(struct block_device *bdev,
+ struct pr_clear __user *arg)
+{
+ const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
+ struct pr_clear c;
+
+ if (!ops || !ops->pr_clear)
+ return -EOPNOTSUPP;
+ if (copy_from_user(&c, arg, sizeof(c)))
+ return -EFAULT;
+
+ return ops->pr_clear(bdev, c.key);
+}
+
/*
* Is it an unrecognized ioctl? The correct returns are either
* ENOTTY (final) or ENOIOCTLCMD ("I don't know this one, try a
@@ -477,6 +548,20 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
case BLKTRACESETUP:
case BLKTRACETEARDOWN:
return blk_trace_ioctl(bdev, cmd, argp);
+ case IOC_PR_REGISTER:
+ return blkdev_pr_register(bdev, argp, false);
+ case IOC_PR_REGISTER_IGNORE:
+ return blkdev_pr_register(bdev, argp, true);
+ case IOC_PR_RESERVE:
+ return blkdev_pr_reserve(bdev, argp);
+ case IOC_PR_RELEASE:
+ return blkdev_pr_release(bdev, argp);
+ case IOC_PR_PREEMPT:
+ return blkdev_pr_preempt(bdev, argp, false);
+ case IOC_PR_PREEMPT_ABORT:
+ return blkdev_pr_preempt(bdev, argp, true);
+ case IOC_PR_CLEAR:
+ return blkdev_pr_clear(bdev, argp);
default:
return __blkdev_driver_ioctl(bdev, mode, cmd, arg);
}
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 243f29e..a07eec93 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -35,6 +35,7 @@ struct sg_io_hdr;
struct bsg_job;
struct blkcg_gq;
struct blk_flush_queue;
+struct pr_ops;
#define BLKDEV_MIN_RQ 4
#define BLKDEV_MAX_RQ 128 /* Default maximum */
@@ -1568,6 +1569,7 @@ struct block_device_operations {
/* this callback is with swap_lock and sometimes page table lock held */
void (*swap_slot_free_notify) (struct block_device *, unsigned long);
struct module *owner;
+ const struct pr_ops *pr_ops;
};
extern int __blkdev_driver_ioctl(struct block_device *, fmode_t, unsigned int,
diff --git a/include/linux/pr.h b/include/linux/pr.h
new file mode 100644
index 0000000..a6f0496
--- /dev/null
+++ b/include/linux/pr.h
@@ -0,0 +1,18 @@
+#ifndef LINUX_PR_H
+#define LINUX_PR_H
+
+#include <uapi/linux/pr.h>
+
+struct pr_ops {
+ int (*pr_register)(struct block_device *bdev, u64 old_key, u64 new_key,
+ bool ignore);
+ int (*pr_reserve)(struct block_device *bdev, u64 key,
+ enum pr_type type);
+ int (*pr_release)(struct block_device *bdev, u64 key,
+ enum pr_type type);
+ int (*pr_preempt)(struct block_device *bdev, u64 old_key, u64 new_key,
+ enum pr_type type, bool abort);
+ int (*pr_clear)(struct block_device *bdev, u64 key);
+};
+
+#endif /* LINUX_PR_H */
diff --git a/include/uapi/linux/pr.h b/include/uapi/linux/pr.h
new file mode 100644
index 0000000..6014d14
--- /dev/null
+++ b/include/uapi/linux/pr.h
@@ -0,0 +1,45 @@
+#ifndef _UAPI_PR_H
+#define _UAPI_PR_H
+
+enum pr_type {
+ PR_WRITE_EXCLUSIVE = 1,
+ PR_EXCLUSIVE_ACCESS = 2,
+ PR_WRITE_EXCLUSIVE_REG_ONLY = 3,
+ PR_EXCLUSIVE_ACCESS_REG_ONLY = 4,
+ PR_WRITE_EXCLUSIVE_ALL_REGS = 5,
+ PR_EXCLUSIVE_ACCESS_ALL_REGS = 6,
+};
+
+struct pr_reservation {
+ __u64 key;
+ __u32 type;
+ __u32 __pad;
+};
+
+struct pr_registration {
+ __u64 old_key;
+ __u64 new_key;
+};
+
+struct pr_preempt {
+ __u64 old_key;
+ __u64 new_key;
+ __u32 type;
+ __u32 __pad;
+};
+
+struct pr_clear {
+ __u64 key;
+};
+
+#define IOC_PR_REGISTER _IOW('p', 200, struct pr_registration)
+#define IOC_PR_REGISTER_IGNORE _IOW('p', 201, struct pr_registration)
+
+#define IOC_PR_RESERVE _IOW('p', 202, struct pr_reservation)
+#define IOC_PR_RELEASE _IOW('p', 203, struct pr_reservation)
+
+#define IOC_PR_PREEMPT _IOW('p', 204, struct pr_preempt)
+#define IOC_PR_PREEMPT_ABORT _IOW('p', 205, struct pr_preempt)
+#define IOC_PR_CLEAR _IOW('p', 206, struct pr_clear)
+
+#endif /* _UAPI_PR_H */
--
1.9.1
^ permalink raw reply related
* [PATCH 3/5] sd: implement the Persistent Reservation API
From: Christoph Hellwig @ 2015-08-12 6:23 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-scsi, linux-nvme, dm-devel, linux-api, linux-kernel
In-Reply-To: <1439360604-16192-1-git-send-email-hch@lst.de>
This is a mostly trivial mapping to the PERSISTENT RESERVE IN/OUT
commands.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
drivers/scsi/sd.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 90 insertions(+)
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 160e44e..14bc2d7 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -51,6 +51,7 @@
#include <linux/async.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
+#include <linux/pr.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>
@@ -1535,6 +1536,94 @@ static int sd_compat_ioctl(struct block_device *bdev, fmode_t mode,
}
#endif
+static char sd_pr_type(enum pr_type type)
+{
+ switch (type) {
+ case PR_WRITE_EXCLUSIVE:
+ return 0x01;
+ case PR_EXCLUSIVE_ACCESS:
+ return 0x03;
+ case PR_WRITE_EXCLUSIVE_REG_ONLY:
+ return 0x05;
+ case PR_EXCLUSIVE_ACCESS_REG_ONLY:
+ return 0x06;
+ case PR_WRITE_EXCLUSIVE_ALL_REGS:
+ return 0x07;
+ case PR_EXCLUSIVE_ACCESS_ALL_REGS:
+ return 0x08;
+ default:
+ return 0;
+ }
+};
+
+static int sd_pr_command(struct block_device *bdev, u8 sa,
+ u64 key, u64 sa_key, u8 type, u8 flags)
+{
+ struct scsi_device *sdev = scsi_disk(bdev->bd_disk)->device;
+ struct scsi_sense_hdr sshdr;
+ int result;
+ u8 cmd[16] = { 0, };
+ u8 data[24] = { 0, };
+
+ cmd[0] = PERSISTENT_RESERVE_OUT;
+ cmd[1] = sa;
+ cmd[2] = type;
+ put_unaligned_be32(sizeof(data), &cmd[5]);
+
+ put_unaligned_be64(key, &data[0]);
+ put_unaligned_be64(sa_key, &data[8]);
+ data[20] = flags;
+
+ result = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, &data, sizeof(data),
+ &sshdr, SD_TIMEOUT, SD_MAX_RETRIES, NULL);
+
+ if ((driver_byte(result) & DRIVER_SENSE) &&
+ (scsi_sense_valid(&sshdr))) {
+ sdev_printk(KERN_INFO, sdev, "PR command failed: %d\n", result);
+ scsi_print_sense_hdr(sdev, NULL, &sshdr);
+ }
+
+ return result;
+}
+
+static int sd_pr_register(struct block_device *bdev, u64 old_key, u64 new_key,
+ bool ignore)
+{
+ return sd_pr_command(bdev, ignore ? 0x06 : 0x00, old_key, new_key, 0,
+ (1 << 0) /* APTPL */ |
+ (1 << 2) /* ALL_TG_PT */);
+}
+
+static int sd_pr_reserve(struct block_device *bdev, u64 key, enum pr_type type)
+{
+ return sd_pr_command(bdev, 0x01, key, 0, sd_pr_type(type), 0);
+}
+
+static int sd_pr_release(struct block_device *bdev, u64 key, enum pr_type type)
+{
+ return sd_pr_command(bdev, 0x02, key, 0, sd_pr_type(type), 0);
+}
+
+static int sd_pr_preempt(struct block_device *bdev, u64 old_key, u64 new_key,
+ enum pr_type type, bool abort)
+{
+ return sd_pr_command(bdev, abort ? 0x05 : 0x04, old_key, new_key,
+ sd_pr_type(type), 0);
+}
+
+static int sd_pr_clear(struct block_device *bdev, u64 key)
+{
+ return sd_pr_command(bdev, 0x03, key, 0, 0, 0);
+}
+
+static const struct pr_ops sd_pr_ops = {
+ .pr_register = sd_pr_register,
+ .pr_reserve = sd_pr_reserve,
+ .pr_release = sd_pr_release,
+ .pr_preempt = sd_pr_preempt,
+ .pr_clear = sd_pr_clear,
+};
+
static const struct block_device_operations sd_fops = {
.owner = THIS_MODULE,
.open = sd_open,
@@ -1547,6 +1636,7 @@ static const struct block_device_operations sd_fops = {
.check_events = sd_check_events,
.revalidate_disk = sd_revalidate_disk,
.unlock_native_capacity = sd_unlock_native_capacity,
+ .pr_ops = &sd_pr_ops,
};
/**
--
1.9.1
^ permalink raw reply related
* [PATCH 4/5] dm: refactor ioctl handling
From: Christoph Hellwig @ 2015-08-12 6:23 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-scsi, linux-nvme, dm-devel, linux-api, linux-kernel
In-Reply-To: <1439360604-16192-1-git-send-email-hch@lst.de>
This moves the call to blkdev_ioctl and the argument checking to core code,
and only leaves a callout to find the block device to operate on it the
targets. This will simplifies the code and will allow us to pass through
ioctl-like command using other methods in the next patch.
Also split out a helper around calling the prepare_ioctl method that will
be reused for persistent reservation handling.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
drivers/md/dm-flakey.c | 16 +++++++-------
drivers/md/dm-linear.c | 14 ++++++------
drivers/md/dm-log-writes.c | 13 ++++++-----
drivers/md/dm-mpath.c | 32 +++++++++++----------------
drivers/md/dm-switch.c | 21 ++++++++----------
drivers/md/dm-verity.c | 15 ++++++-------
drivers/md/dm.c | 50 ++++++++++++++++++++++++++++++++-----------
include/linux/device-mapper.h | 6 +++---
8 files changed, 89 insertions(+), 78 deletions(-)
diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c
index 0448124..e314e49 100644
--- a/drivers/md/dm-flakey.c
+++ b/drivers/md/dm-flakey.c
@@ -371,20 +371,20 @@ static void flakey_status(struct dm_target *ti, status_type_t type,
}
}
-static int flakey_ioctl(struct dm_target *ti, unsigned int cmd, unsigned long arg)
+static int flakey_prepare_ioctl(struct dm_target *ti,
+ struct block_device **bdev, fmode_t *mode)
{
struct flakey_c *fc = ti->private;
- struct dm_dev *dev = fc->dev;
- int r = 0;
+
+ *bdev = fc->dev->bdev;
/*
* Only pass ioctls through if the device sizes match exactly.
*/
if (fc->start ||
- ti->len != i_size_read(dev->bdev->bd_inode) >> SECTOR_SHIFT)
- r = scsi_verify_blk_ioctl(NULL, cmd);
-
- return r ? : __blkdev_driver_ioctl(dev->bdev, dev->mode, cmd, arg);
+ ti->len != i_size_read((*bdev)->bd_inode) >> SECTOR_SHIFT)
+ return 1;
+ return 0;
}
static int flakey_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
@@ -418,7 +418,7 @@ static struct target_type flakey_target = {
.map = flakey_map,
.end_io = flakey_end_io,
.status = flakey_status,
- .ioctl = flakey_ioctl,
+ .prepare_ioctl = flakey_prepare_ioctl,
.merge = flakey_merge,
.iterate_devices = flakey_iterate_devices,
};
diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c
index 53e848c..f81f68e 100644
--- a/drivers/md/dm-linear.c
+++ b/drivers/md/dm-linear.c
@@ -113,21 +113,21 @@ static void linear_status(struct dm_target *ti, status_type_t type,
}
}
-static int linear_ioctl(struct dm_target *ti, unsigned int cmd,
- unsigned long arg)
+static int linear_prepare_ioctl(struct dm_target *ti,
+ struct block_device **bdev, fmode_t *mode)
{
struct linear_c *lc = (struct linear_c *) ti->private;
struct dm_dev *dev = lc->dev;
- int r = 0;
+
+ *bdev = dev->bdev;
/*
* Only pass ioctls through if the device sizes match exactly.
*/
if (lc->start ||
ti->len != i_size_read(dev->bdev->bd_inode) >> SECTOR_SHIFT)
- r = scsi_verify_blk_ioctl(NULL, cmd);
-
- return r ? : __blkdev_driver_ioctl(dev->bdev, dev->mode, cmd, arg);
+ return 1;
+ return 0;
}
static int linear_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
@@ -161,7 +161,7 @@ static struct target_type linear_target = {
.dtr = linear_dtr,
.map = linear_map,
.status = linear_status,
- .ioctl = linear_ioctl,
+ .prepare_ioctl = linear_prepare_ioctl,
.merge = linear_merge,
.iterate_devices = linear_iterate_devices,
};
diff --git a/drivers/md/dm-log-writes.c b/drivers/md/dm-log-writes.c
index e9d1748..9601d9d 100644
--- a/drivers/md/dm-log-writes.c
+++ b/drivers/md/dm-log-writes.c
@@ -709,20 +709,19 @@ static void log_writes_status(struct dm_target *ti, status_type_t type,
}
}
-static int log_writes_ioctl(struct dm_target *ti, unsigned int cmd,
- unsigned long arg)
+static int log_writes_prepare_ioctl(struct dm_target *ti,
+ struct block_device **bdev, fmode_t *mode)
{
struct log_writes_c *lc = ti->private;
struct dm_dev *dev = lc->dev;
- int r = 0;
+ *bdev = dev->bdev;
/*
* Only pass ioctls through if the device sizes match exactly.
*/
if (ti->len != i_size_read(dev->bdev->bd_inode) >> SECTOR_SHIFT)
- r = scsi_verify_blk_ioctl(NULL, cmd);
-
- return r ? : __blkdev_driver_ioctl(dev->bdev, dev->mode, cmd, arg);
+ return 1;
+ return 0;
}
static int log_writes_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
@@ -792,7 +791,7 @@ static struct target_type log_writes_target = {
.map = log_writes_map,
.end_io = normal_end_io,
.status = log_writes_status,
- .ioctl = log_writes_ioctl,
+ .prepare_ioctl = log_writes_prepare_ioctl,
.merge = log_writes_merge,
.message = log_writes_message,
.iterate_devices = log_writes_iterate_devices,
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index eff7bdd..97cf835 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -1548,18 +1548,14 @@ out:
return r;
}
-static int multipath_ioctl(struct dm_target *ti, unsigned int cmd,
- unsigned long arg)
+static int multipath_prepare_ioctl(struct dm_target *ti,
+ struct block_device **bdev, fmode_t *mode)
{
struct multipath *m = ti->private;
struct pgpath *pgpath;
- struct block_device *bdev;
- fmode_t mode;
unsigned long flags;
int r;
- bdev = NULL;
- mode = 0;
r = 0;
spin_lock_irqsave(&m->lock, flags);
@@ -1570,26 +1566,17 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd,
pgpath = m->current_pgpath;
if (pgpath) {
- bdev = pgpath->path.dev->bdev;
- mode = pgpath->path.dev->mode;
+ *bdev = pgpath->path.dev->bdev;
+ *mode = pgpath->path.dev->mode;
}
if ((pgpath && m->queue_io) || (!pgpath && m->queue_if_no_path))
r = -ENOTCONN;
- else if (!bdev)
+ else if (!*bdev)
r = -EIO;
spin_unlock_irqrestore(&m->lock, flags);
- /*
- * Only pass ioctls through if the device sizes match exactly.
- */
- if (!bdev || ti->len != i_size_read(bdev->bd_inode) >> SECTOR_SHIFT) {
- int err = scsi_verify_blk_ioctl(NULL, cmd);
- if (err)
- r = err;
- }
-
if (r == -ENOTCONN && !fatal_signal_pending(current)) {
spin_lock_irqsave(&m->lock, flags);
if (!m->current_pg) {
@@ -1602,7 +1589,12 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd,
dm_table_run_md_queue_async(m->ti->table);
}
- return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg);
+ /*
+ * Only pass ioctls through if the device sizes match exactly.
+ */
+ if (!r && ti->len != i_size_read((*bdev)->bd_inode) >> SECTOR_SHIFT)
+ return 1;
+ return r;
}
static int multipath_iterate_devices(struct dm_target *ti,
@@ -1718,7 +1710,7 @@ static struct target_type multipath_target = {
.resume = multipath_resume,
.status = multipath_status,
.message = multipath_message,
- .ioctl = multipath_ioctl,
+ .prepare_ioctl = multipath_prepare_ioctl,
.iterate_devices = multipath_iterate_devices,
.busy = multipath_busy,
};
diff --git a/drivers/md/dm-switch.c b/drivers/md/dm-switch.c
index 50fca46..b128575 100644
--- a/drivers/md/dm-switch.c
+++ b/drivers/md/dm-switch.c
@@ -511,27 +511,24 @@ static void switch_status(struct dm_target *ti, status_type_t type,
*
* Passthrough all ioctls to the path for sector 0
*/
-static int switch_ioctl(struct dm_target *ti, unsigned cmd,
- unsigned long arg)
+static int switch_prepare_ioctl(struct dm_target *ti,
+ struct block_device **bdev, fmode_t *mode)
{
struct switch_ctx *sctx = ti->private;
- struct block_device *bdev;
- fmode_t mode;
unsigned path_nr;
- int r = 0;
path_nr = switch_get_path_nr(sctx, 0);
- bdev = sctx->path_list[path_nr].dmdev->bdev;
- mode = sctx->path_list[path_nr].dmdev->mode;
+ *bdev = sctx->path_list[path_nr].dmdev->bdev;
+ *mode = sctx->path_list[path_nr].dmdev->mode;
/*
* Only pass ioctls through if the device sizes match exactly.
*/
- if (ti->len + sctx->path_list[path_nr].start != i_size_read(bdev->bd_inode) >> SECTOR_SHIFT)
- r = scsi_verify_blk_ioctl(NULL, cmd);
-
- return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg);
+ if (ti->len + sctx->path_list[path_nr].start !=
+ i_size_read((*bdev)->bd_inode) >> SECTOR_SHIFT)
+ return 1;
+ return 0;
}
static int switch_iterate_devices(struct dm_target *ti,
@@ -560,7 +557,7 @@ static struct target_type switch_target = {
.map = switch_map,
.message = switch_message,
.status = switch_status,
- .ioctl = switch_ioctl,
+ .prepare_ioctl = switch_prepare_ioctl,
.iterate_devices = switch_iterate_devices,
};
diff --git a/drivers/md/dm-verity.c b/drivers/md/dm-verity.c
index 4b34df8..ce1764c 100644
--- a/drivers/md/dm-verity.c
+++ b/drivers/md/dm-verity.c
@@ -635,18 +635,17 @@ static void verity_status(struct dm_target *ti, status_type_t type,
}
}
-static int verity_ioctl(struct dm_target *ti, unsigned cmd,
- unsigned long arg)
+static int verity_prepare_ioctl(struct dm_target *ti,
+ struct block_device **bdev, fmode_t *mode)
{
struct dm_verity *v = ti->private;
- int r = 0;
+
+ *bdev = v->data_dev->bdev;
if (v->data_start ||
ti->len != i_size_read(v->data_dev->bdev->bd_inode) >> SECTOR_SHIFT)
- r = scsi_verify_blk_ioctl(NULL, cmd);
-
- return r ? : __blkdev_driver_ioctl(v->data_dev->bdev, v->data_dev->mode,
- cmd, arg);
+ return 1;
+ return 0;
}
static int verity_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
@@ -995,7 +994,7 @@ static struct target_type verity_target = {
.dtr = verity_dtr,
.map = verity_map,
.status = verity_status,
- .ioctl = verity_ioctl,
+ .prepare_ioctl = verity_prepare_ioctl,
.merge = verity_merge,
.iterate_devices = verity_iterate_devices,
.io_hints = verity_io_hints,
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 7f367fc..49e4719 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -556,18 +556,16 @@ static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
return dm_get_geometry(md, geo);
}
-static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
- unsigned int cmd, unsigned long arg)
+static int dm_get_live_table_for_ioctl(struct mapped_device *md,
+ struct dm_target **tgt, struct block_device **bdev,
+ fmode_t *mode, int *srcu_idx)
{
- struct mapped_device *md = bdev->bd_disk->private_data;
- int srcu_idx;
struct dm_table *map;
- struct dm_target *tgt;
- int r = -ENOTTY;
+ int r;
retry:
- map = dm_get_live_table(md, &srcu_idx);
-
+ r = -ENOTTY;
+ map = dm_get_live_table(md, srcu_idx);
if (!map || !dm_table_get_size(map))
goto out;
@@ -575,8 +573,9 @@ retry:
if (dm_table_get_num_targets(map) != 1)
goto out;
- tgt = dm_table_get_target(map, 0);
- if (!tgt->type->ioctl)
+ *tgt = dm_table_get_target(map, 0);
+
+ if (!(*tgt)->type->prepare_ioctl)
goto out;
if (dm_suspended_md(md)) {
@@ -584,16 +583,41 @@ retry:
goto out;
}
- r = tgt->type->ioctl(tgt, cmd, arg);
+ r = (*tgt)->type->prepare_ioctl(*tgt, bdev, mode);
+ if (r < 0)
+ goto out;
-out:
- dm_put_live_table(md, srcu_idx);
+ return r;
+out:
+ dm_put_live_table(md, *srcu_idx);
if (r == -ENOTCONN) {
msleep(10);
goto retry;
}
+ return r;
+}
+
+static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long arg)
+{
+ struct mapped_device *md = bdev->bd_disk->private_data;
+ struct dm_target *tgt;
+ int srcu_idx, r;
+
+ r = dm_get_live_table_for_ioctl(md, &tgt, &bdev, &mode, &srcu_idx);
+ if (r < 0)
+ return r;
+
+ if (r > 0) {
+ r = scsi_verify_blk_ioctl(NULL, cmd);
+ if (r)
+ goto out;
+ }
+ r = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
+out:
+ dm_put_live_table(md, srcu_idx);
return r;
}
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 51cc1de..9f1e7e9 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -79,8 +79,8 @@ typedef void (*dm_status_fn) (struct dm_target *ti, status_type_t status_type,
typedef int (*dm_message_fn) (struct dm_target *ti, unsigned argc, char **argv);
-typedef int (*dm_ioctl_fn) (struct dm_target *ti, unsigned int cmd,
- unsigned long arg);
+typedef int (*dm_prepare_ioctl_fn) (struct dm_target *ti,
+ struct block_device **bdev, fmode_t *mode);
typedef int (*dm_merge_fn) (struct dm_target *ti, struct bvec_merge_data *bvm,
struct bio_vec *biovec, int max_size);
@@ -159,7 +159,7 @@ struct target_type {
dm_resume_fn resume;
dm_status_fn status;
dm_message_fn message;
- dm_ioctl_fn ioctl;
+ dm_prepare_ioctl_fn prepare_ioctl;
dm_merge_fn merge;
dm_busy_fn busy;
dm_iterate_devices_fn iterate_devices;
--
1.9.1
^ permalink raw reply related
* [PATCH 5/5] dm: add support for passing through persistent reservations
From: Christoph Hellwig @ 2015-08-12 6:23 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-scsi, linux-nvme, dm-devel, linux-api, linux-kernel
In-Reply-To: <1439360604-16192-1-git-send-email-hch@lst.de>
This adds support to pass through persistent reservation requests
similar to the existing ioctl handling, and with the same limitations,
e.g. devices may only have a single target attached.
This is mostly intended for multipathing.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
drivers/md/dm.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 122 insertions(+)
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 49e4719..a9f6f39 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -24,6 +24,7 @@
#include <linux/ktime.h>
#include <linux/elevator.h> /* for rq_end_sector() */
#include <linux/blk-mq.h>
+#include <linux/pr.h>
#include <trace/events/block.h>
@@ -3669,11 +3670,132 @@ void dm_free_md_mempools(struct dm_md_mempools *pools)
kfree(pools);
}
+static int dm_pr_register(struct block_device *bdev, u64 old_key, u64 new_key,
+ bool ignore)
+{
+ struct mapped_device *md = bdev->bd_disk->private_data;
+ const struct pr_ops *ops;
+ struct dm_target *tgt;
+ fmode_t mode;
+ int srcu_idx, r;
+
+ r = dm_get_live_table_for_ioctl(md, &tgt, &bdev, &mode, &srcu_idx);
+ if (r < 0)
+ return r;
+
+ ops = bdev->bd_disk->fops->pr_ops;
+ if (ops && ops->pr_register)
+ r = ops->pr_register(bdev, old_key, new_key, ignore);
+ else
+ r = -EOPNOTSUPP;
+
+ dm_put_live_table(md, srcu_idx);
+ return r;
+}
+
+static int dm_pr_reserve(struct block_device *bdev, u64 key, enum pr_type type)
+{
+ struct mapped_device *md = bdev->bd_disk->private_data;
+ const struct pr_ops *ops;
+ struct dm_target *tgt;
+ fmode_t mode;
+ int srcu_idx, r;
+
+ r = dm_get_live_table_for_ioctl(md, &tgt, &bdev, &mode, &srcu_idx);
+ if (r < 0)
+ return r;
+
+ ops = bdev->bd_disk->fops->pr_ops;
+ if (ops && ops->pr_reserve)
+ r = ops->pr_reserve(bdev, key, type);
+ else
+ r = -EOPNOTSUPP;
+
+ dm_put_live_table(md, srcu_idx);
+ return r;
+}
+
+static int dm_pr_release(struct block_device *bdev, u64 key, enum pr_type type)
+{
+ struct mapped_device *md = bdev->bd_disk->private_data;
+ const struct pr_ops *ops;
+ struct dm_target *tgt;
+ fmode_t mode;
+ int srcu_idx, r;
+
+ r = dm_get_live_table_for_ioctl(md, &tgt, &bdev, &mode, &srcu_idx);
+ if (r < 0)
+ return r;
+
+ ops = bdev->bd_disk->fops->pr_ops;
+ if (ops && ops->pr_release)
+ r = ops->pr_release(bdev, key, type);
+ else
+ r = -EOPNOTSUPP;
+
+ dm_put_live_table(md, srcu_idx);
+ return r;
+}
+
+static int dm_pr_preempt(struct block_device *bdev, u64 old_key, u64 new_key,
+ enum pr_type type, bool abort)
+{
+ struct mapped_device *md = bdev->bd_disk->private_data;
+ const struct pr_ops *ops;
+ struct dm_target *tgt;
+ fmode_t mode;
+ int srcu_idx, r;
+
+ r = dm_get_live_table_for_ioctl(md, &tgt, &bdev, &mode, &srcu_idx);
+ if (r < 0)
+ return r;
+
+ ops = bdev->bd_disk->fops->pr_ops;
+ if (ops && ops->pr_preempt)
+ r = ops->pr_preempt(bdev, old_key, new_key, type, abort);
+ else
+ r = -EOPNOTSUPP;
+
+ dm_put_live_table(md, srcu_idx);
+ return r;
+}
+
+static int dm_pr_clear(struct block_device *bdev, u64 key)
+{
+ struct mapped_device *md = bdev->bd_disk->private_data;
+ const struct pr_ops *ops;
+ struct dm_target *tgt;
+ fmode_t mode;
+ int srcu_idx, r;
+
+ r = dm_get_live_table_for_ioctl(md, &tgt, &bdev, &mode, &srcu_idx);
+ if (r < 0)
+ return r;
+
+ ops = bdev->bd_disk->fops->pr_ops;
+ if (ops && ops->pr_clear)
+ r = ops->pr_clear(bdev, key);
+ else
+ r = -EOPNOTSUPP;
+
+ dm_put_live_table(md, srcu_idx);
+ return r;
+}
+
+static const struct pr_ops dm_pr_ops = {
+ .pr_register = dm_pr_register,
+ .pr_reserve = dm_pr_reserve,
+ .pr_release = dm_pr_release,
+ .pr_preempt = dm_pr_preempt,
+ .pr_clear = dm_pr_clear,
+};
+
static const struct block_device_operations dm_blk_dops = {
.open = dm_blk_open,
.release = dm_blk_close,
.ioctl = dm_blk_ioctl,
.getgeo = dm_blk_getgeo,
+ .pr_ops = &dm_pr_ops,
.owner = THIS_MODULE
};
--
1.9.1
^ permalink raw reply related
* Re: [PATCH v7 2/6] mm: mlock: Add new mlock system call
From: Michal Hocko @ 2015-08-12 9:45 UTC (permalink / raw)
To: Eric B Munson
Cc: Andrew Morton, Vlastimil Babka, Heiko Carstens,
Geert Uytterhoeven, Catalin Marinas, Stephen Rothwell,
Guenter Roeck, Andrea Arcangeli, linux-alpha, linux-kernel,
linux-arm-kernel, adi-buildroot-devel, linux-cris-kernel,
linux-ia64, linux-m68k, linux-am33-list, linux-parisc,
linuxppc-dev, linux-s390, linux-sh, sparclinux, linux-xtensa,
linux-api, linux-arch, linux-mm
In-Reply-To: <1439097776-27695-3-git-send-email-emunson@akamai.com>
On Sun 09-08-15 01:22:52, Eric B Munson wrote:
> With the refactored mlock code, introduce a new system call for mlock.
> The new call will allow the user to specify what lock states are being
> added. mlock2 is trivial at the moment, but a follow on patch will add
> a new mlock state making it useful.
Looks good to me
Acked-by: Michal Hocko <mhocko@suse.com>
> Signed-off-by: Eric B Munson <emunson@akamai.com>
> Acked-by: Vlastimil Babka <vbabka@suse.cz>
> Cc: Michal Hocko <mhocko@suse.cz>
> Cc: Vlastimil Babka <vbabka@suse.cz>
> Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
> Cc: Geert Uytterhoeven <geert@linux-m68k.org>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Stephen Rothwell <sfr@canb.auug.org.au>
> Cc: Guenter Roeck <linux@roeck-us.net>
> Cc: Andrea Arcangeli <aarcange@redhat.com>
> Cc: linux-alpha@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-arm-kernel@lists.infradead.org
> Cc: adi-buildroot-devel@lists.sourceforge.net
> Cc: linux-cris-kernel@axis.com
> Cc: linux-ia64@vger.kernel.org
> Cc: linux-m68k@lists.linux-m68k.org
> Cc: linux-am33-list@redhat.com
> Cc: linux-parisc@vger.kernel.org
> Cc: linuxppc-dev@lists.ozlabs.org
> Cc: linux-s390@vger.kernel.org
> Cc: linux-sh@vger.kernel.org
> Cc: sparclinux@vger.kernel.org
> Cc: linux-xtensa@linux-xtensa.org
> Cc: linux-api@vger.kernel.org
> Cc: linux-arch@vger.kernel.org
> Cc: linux-mm@kvack.org
> ---
> arch/x86/entry/syscalls/syscall_32.tbl | 1 +
> arch/x86/entry/syscalls/syscall_64.tbl | 1 +
> include/linux/syscalls.h | 2 ++
> include/uapi/asm-generic/unistd.h | 4 +++-
> kernel/sys_ni.c | 1 +
> mm/mlock.c | 8 ++++++++
> 6 files changed, 16 insertions(+), 1 deletion(-)
>
> diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
> index ef8187f..8e06da6 100644
> --- a/arch/x86/entry/syscalls/syscall_32.tbl
> +++ b/arch/x86/entry/syscalls/syscall_32.tbl
> @@ -365,3 +365,4 @@
> 356 i386 memfd_create sys_memfd_create
> 357 i386 bpf sys_bpf
> 358 i386 execveat sys_execveat stub32_execveat
> +360 i386 mlock2 sys_mlock2
> diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
> index 9ef32d5..67601e7 100644
> --- a/arch/x86/entry/syscalls/syscall_64.tbl
> +++ b/arch/x86/entry/syscalls/syscall_64.tbl
> @@ -329,6 +329,7 @@
> 320 common kexec_file_load sys_kexec_file_load
> 321 common bpf sys_bpf
> 322 64 execveat stub_execveat
> +324 common mlock2 sys_mlock2
>
> #
> # x32-specific system call numbers start at 512 to avoid cache impact
> diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
> index b45c45b..56a3d59 100644
> --- a/include/linux/syscalls.h
> +++ b/include/linux/syscalls.h
> @@ -884,4 +884,6 @@ asmlinkage long sys_execveat(int dfd, const char __user *filename,
> const char __user *const __user *argv,
> const char __user *const __user *envp, int flags);
>
> +asmlinkage long sys_mlock2(unsigned long start, size_t len, int flags);
> +
> #endif
> diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
> index e016bd9..14a6013 100644
> --- a/include/uapi/asm-generic/unistd.h
> +++ b/include/uapi/asm-generic/unistd.h
> @@ -709,9 +709,11 @@ __SYSCALL(__NR_memfd_create, sys_memfd_create)
> __SYSCALL(__NR_bpf, sys_bpf)
> #define __NR_execveat 281
> __SC_COMP(__NR_execveat, sys_execveat, compat_sys_execveat)
> +#define __NR_mlock2 282
> +__SYSCALL(__NR_mlock2, sys_mlock2)
>
> #undef __NR_syscalls
> -#define __NR_syscalls 282
> +#define __NR_syscalls 283
>
> /*
> * All syscalls below here should go away really,
> diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
> index 7995ef5..4818b71 100644
> --- a/kernel/sys_ni.c
> +++ b/kernel/sys_ni.c
> @@ -193,6 +193,7 @@ cond_syscall(sys_mlock);
> cond_syscall(sys_munlock);
> cond_syscall(sys_mlockall);
> cond_syscall(sys_munlockall);
> +cond_syscall(sys_mlock2);
> cond_syscall(sys_mincore);
> cond_syscall(sys_madvise);
> cond_syscall(sys_mremap);
> diff --git a/mm/mlock.c b/mm/mlock.c
> index 5692ee5..3094f27 100644
> --- a/mm/mlock.c
> +++ b/mm/mlock.c
> @@ -643,6 +643,14 @@ SYSCALL_DEFINE2(mlock, unsigned long, start, size_t, len)
> return do_mlock(start, len, VM_LOCKED);
> }
>
> +SYSCALL_DEFINE3(mlock2, unsigned long, start, size_t, len, int, flags)
> +{
> + if (flags)
> + return -EINVAL;
> +
> + return do_mlock(start, len, VM_LOCKED);
> +}
> +
> SYSCALL_DEFINE2(munlock, unsigned long, start, size_t, len)
> {
> int ret;
> --
> 1.9.1
--
Michal Hocko
SUSE Labs
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* [PATCH 0/2] Documentation: iio-trig-sysfs: Add missing ABI
From: Cristina Opriceana @ 2015-08-12 11:26 UTC (permalink / raw)
To: jic23-DgEjT+Ai2ygdnm+yROfE0A
Cc: daniel.baluta-ral2JQCrhuEAvxtiuMwx3w,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
Add the ABI documentation for the add_trigger and remove_trigger
attributes, found in iio-trig-sysfs.
Cristina Opriceana (2):
Documentation: iio-trig-sysfs: Document add_trigger attribute
Documentation: iio-trig-sysfs: Add remove_trigger attribute ABI
.../ABI/testing/sysfs-bus-iio-trigger-sysfs | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
--
1.9.1
^ permalink raw reply
* [PATCH 1/2] Documentation: iio-trig-sysfs: Document add_trigger attribute
From: Cristina Opriceana @ 2015-08-12 11:28 UTC (permalink / raw)
To: jic23-DgEjT+Ai2ygdnm+yROfE0A
Cc: daniel.baluta-ral2JQCrhuEAvxtiuMwx3w,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <cover.1439378363.git.cristina.opriceana-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
This patch adds the ABI documentation for the add_trigger
attribute, which is provided by the iio-trig-sysfs stand-alone driver.
Signed-off-by: Cristina Opriceana <cristina.opriceana-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs b/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs
index 5235e6c..887aa92 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs
+++ b/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs
@@ -9,3 +9,14 @@ Description:
automated testing or in situations, where other trigger methods
are not applicable. For example no RTC or spare GPIOs.
X is the IIO index of the trigger.
+
+What: /sys/bus/iio/devices/iio_sysfs_trigger/add_trigger
+KernelVersion: 2.6.39
+Contact: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
+Description:
+ This attribute is provided by the iio-trig-sysfs stand-alone
+ driver and it is used to activate the creation of a new trigger.
+ In order to achieve this, one should write a positive integer
+ into the associated file, which will serve as the id of the
+ trigger. If the trigger with the specified id is already present
+ in the system, an invalid argument message will be returned.
--
1.9.1
^ permalink raw reply related
* [PATCH 2/2] Documentation: iio-trig-sysfs: Add remove_trigger attribute ABI
From: Cristina Opriceana @ 2015-08-12 11:29 UTC (permalink / raw)
To: jic23-DgEjT+Ai2ygdnm+yROfE0A
Cc: daniel.baluta-ral2JQCrhuEAvxtiuMwx3w,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <cover.1439378363.git.cristina.opriceana-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
This patch adds the documentation ABI for the remove_trigger
attribute, provided by the iio-trig-sysfs stand-alone driver.
Signed-off-by: Cristina Opriceana <cristina.opriceana-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs b/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs
index 887aa92..6de6e6b 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs
+++ b/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs
@@ -20,3 +20,14 @@ Description:
into the associated file, which will serve as the id of the
trigger. If the trigger with the specified id is already present
in the system, an invalid argument message will be returned.
+
+What: /sys/bus/iio/devices/iio_sysfs_trigger/remove_trigger
+KernelVersion: 2.6.39
+Contact: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
+Description:
+ This attribute is used to unregister and delete a previously
+ created trigger from the list of available triggers. In order to
+ achieve this, one should write a positive integer into the
+ associated file, representing the id of the trigger that needs
+ to be removed. If the trigger can't be found, an invalid
+ argument message will be returned to the user.
--
1.9.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox