Skip to contentSkip to conversation

NVM Express driver

REVIEW Matthew Wilcox 10 participants 24 messages Mar 3, 2011 – Mar 13, 2011

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

Hi everyone,

You may have seen the recent announcement of the NVM Express spec; here's the driver. I've put a git tree up on kernel.org with all the history that's fit to be released (earlier versions were full of Intel codenames and are not suited to public consumption). If you'd rather review the history, it's at
http://git.kernel.org/?p=linux/kernel/git/willy/nvme.git;a=summary

Jens has already offered to pull my nvme tree into his block tree, so that's the route I intend to take into Linus' tree. Comments appreciated, however in a masterpiece of bad timing I am going to be travelling for the next week, so please bear with me if I don't respond quickly.

Documentation/ioctl/ioctl-number.txt +1 -0
index 63ffd78..f8159ba 100644
@@ -148,6 +148,7 @@ Code  Seq#(hex)	Include File		Comments
 'M'	01-03	drivers/scsi/megaraid/megaraid_sas.h
 'M'	00-0F	drivers/video/fsl-diu-fb.h	conflict!
 'N'	00-1F	drivers/usb/scanner.h
+'N'	40-7F	drivers/block/nvme.c
 'O'     00-06   mtd/ubi-user.h		UBI
 'P'	all	linux/soundcard.h	conflict!
 'P'	60-6F	sound/sscape_ioctl.h	conflict!
drivers/block/Kconfig +11 -0
index 4b9359a..47056c8 100644
@@ -300,6 +300,17 @@ config BLK_DEV_NBD
 
 	  If unsure, say N.
 
+config BLK_DEV_NVME
+	tristate "NVM Express block device"
+	depends on PCI
+	---help---
+	  The NVM Express driver is for solid state drives directly
+	  connected to the PCI or PCI Express bus.  If you know you
+	  don't have one of these, it is safe to answer N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called nvme.
+
 config BLK_DEV_OSD
 	tristate "OSD object-as-blkdev support"
 	depends on SCSI_OSD_ULD
drivers/block/Makefile +1 -0
index d7f463d..3309c94 100644
@@ -23,6 +23,7 @@ obj-$(CONFIG_XILINX_SYSACE)	+= xsysace.o
 obj-$(CONFIG_CDROM_PKTCDVD)	+= pktcdvd.o
 obj-$(CONFIG_MG_DISK)		+= mg_disk.o
 obj-$(CONFIG_SUNVDC)		+= sunvdc.o
+obj-$(CONFIG_BLK_DEV_NVME)	+= nvme.o
 obj-$(CONFIG_BLK_DEV_OSD)	+= osdblk.o
 
 obj-$(CONFIG_BLK_DEV_UMEM)	+= umem.o
drivers/block/nvme.c new file +1622 -0

1,622 lines not on this page · raw message

include/linux/nvme.h new file +403 -0

403 lines not on this page · raw message

#1 lore raw
Quoted 20 lines · Matthew Wilcox

Hi everyone,

You may have seen the recent announcement of the NVM Express spec; here's the driver. I've put a git tree up on kernel.org with all the history that's fit to be released (earlier versions were full of Intel codenames and are not suited to public consumption). If you'd rather review the history, it's at
http://git.kernel.org/?p=linux/kernel/git/willy/nvme.git;a=summary

Jens has already offered to pull my nvme tree into his block tree, so that's the route I intend to take into Linus' tree. Comments appreciated, however in a masterpiece of bad timing I am going to be travelling for the next week, so please bear with me if I don't respond quickly.

Documentation/ioctl/ioctl-number.txt +0 -0
index 63ffd78..f8159ba 100644

Documentation/ioctl/ioctl-number.txt Code Seq#(hex) Include File Comments · line 151

Quoted 4 lines
Documentation/ioctl/ioctl-number.txt +1 -0
@@ -148,6 +148,7 @@ Code  Seq#(hex)	Include File		Comments
 'M'	01-03	drivers/scsi/megaraid/megaraid_sas.h
 'M'	00-0F	drivers/video/fsl-diu-fb.h	conflict!
 'N'	00-1F	drivers/usb/scanner.h
+'N'	40-7F	drivers/block/nvme.c

I hate to ask this, but why do you have ioctls for this? At first glance, a number of the ioctls you have should just be sysfs files to export the information. What am I misunderstanding here?

Quoted 20 lines
Documentation/ioctl/ioctl-number.txt +20 -0
+static int nvme_download_firmware(struct nvme_ns *ns,
+						struct nvme_dlfw __user *udlfw)
+{
+	struct nvme_dev *dev = ns->dev;
+	struct nvme_dlfw dlfw;
+	struct nvme_command c;
+	int nents, status;
+	struct scatterlist *sg;
+	struct nvme_prps *prps;
+
+	if (copy_from_user(&dlfw, udlfw, sizeof(dlfw)))
+		return -EFAULT;
+	if (dlfw.length >= (1 << 30))
+		return -EINVAL;
+
+	nents = nvme_map_user_pages(dev, 1, dlfw.addr, dlfw.length * 4, &sg);
+	if (nents < 0)
+		return nents;
+
+	memset(&c, 0, sizeof(c));

Documentation/ioctl/ioctl-number.txt Code Seq#(hex) Include File Comments

Quoted 10 lines
Documentation/ioctl/ioctl-number.txt +10 -0
+	c.dlfw.opcode = nvme_admin_download_fw;
+	c.dlfw.numd = cpu_to_le32(dlfw.length);
+	c.dlfw.offset = cpu_to_le32(dlfw.offset);
+	prps = nvme_setup_prps(dev, &c.common, sg, dlfw.length * 4);
+
+	status = nvme_submit_admin_cmd(dev, &c, NULL);
+	nvme_unmap_user_pages(dev, 0, dlfw.addr, dlfw.length * 4, sg, nents);
+	nvme_free_prps(dev, prps);
+	return status;
+}

Shouldn't you be using the build-in firmware kernel interface instead of rolling your own in an ioctl?

thanks,

greg k-h

#2 lore raw
Quoted 5 lines · Greg KH
Quoted 5 lines
Documentation/ioctl/ioctl-number.txt +0 -0
index 63ffd78..f8159ba 100644
@@ -148,6 +148,7 @@ Code  Seq#(hex)	Include File		Comments
Quoted 8 lines

Documentation/ioctl/ioctl-number.txt Code Seq#(hex) Include File Comments · line 151

Quoted 4 lines
Documentation/ioctl/ioctl-number.txt +1 -0
 'M'	01-03	drivers/scsi/megaraid/megaraid_sas.h
 'M'	00-0F	drivers/video/fsl-diu-fb.h	conflict!
 'N'	00-1F	drivers/usb/scanner.h
+'N'	40-7F	drivers/block/nvme.c

I hate to ask this, but why do you have ioctls for this? At first glance, a number of the ioctls you have should just be sysfs files to export the information. What am I misunderstanding here?

I don't think you're arguing for SUBMIT_IO being done through sysfs, so some ioctls are clearly needed. I'll take a look at which ones can be moved to sysfs.

Quoted 25 lines
Quoted 25 lines
Documentation/ioctl/ioctl-number.txt +25 -0
+static int nvme_download_firmware(struct nvme_ns *ns,
+						struct nvme_dlfw __user *udlfw)
+{
+	struct nvme_dev *dev = ns->dev;
+	struct nvme_dlfw dlfw;
+	struct nvme_command c;
+	int nents, status;
+	struct scatterlist *sg;
+	struct nvme_prps *prps;
+
+	if (copy_from_user(&dlfw, udlfw, sizeof(dlfw)))
+		return -EFAULT;
+	if (dlfw.length >= (1 << 30))
+		return -EINVAL;
+
+	nents = nvme_map_user_pages(dev, 1, dlfw.addr, dlfw.length * 4, &sg);
+	if (nents < 0)
+		return nents;
+
+	memset(&c, 0, sizeof(c));
+	c.dlfw.opcode = nvme_admin_download_fw;
+	c.dlfw.numd = cpu_to_le32(dlfw.length);
+	c.dlfw.offset = cpu_to_le32(dlfw.offset);
+	prps = nvme_setup_prps(dev, &c.common, sg, dlfw.length * 4);
+
Quoted 8 lines

Documentation/ioctl/ioctl-number.txt

Quoted 5 lines
Documentation/ioctl/ioctl-number.txt +5 -0
+	status = nvme_submit_admin_cmd(dev, &c, NULL);
+	nvme_unmap_user_pages(dev, 0, dlfw.addr, dlfw.length * 4, sg, nents);
+	nvme_free_prps(dev, prps);
+	return status;
+}

Shouldn't you be using the build-in firmware kernel interface instead of rolling your own in an ioctl?

There's a bit of an impedence mismatch there. Think of this as being drive firmware instead of controller firmware. This isn't for request_firmware() kind of uses, it's for some admin tool to come along and tell the drive "Oh, here's some new firmware for you".

If you look at the spec [1], you'll see there are a number of firmware slots in the device, and it's up to the managability utility to decide which one to replace or activate. I dno't think you want to pull all that gnarly decision making code into the kernel, do you?

[1] http://download.intel.com/standards/nvmhci/NVM_Express_1_0_Gold.pdf

#3 lore raw
Quoted 15 lines · Matthew Wilcox
Quoted 5 lines · Greg KH
Quoted 5 lines
Documentation/ioctl/ioctl-number.txt +0 -0
index 63ffd78..f8159ba 100644
@@ -148,6 +148,7 @@ Code  Seq#(hex)	Include File		Comments
Quoted 8 lines

Documentation/ioctl/ioctl-number.txt Code Seq#(hex) Include File Comments · line 151

Quoted 4 lines
Documentation/ioctl/ioctl-number.txt +1 -0
 'M'	01-03	drivers/scsi/megaraid/megaraid_sas.h
 'M'	00-0F	drivers/video/fsl-diu-fb.h	conflict!
 'N'	00-1F	drivers/usb/scanner.h
+'N'	40-7F	drivers/block/nvme.c

I hate to ask this, but why do you have ioctls for this? At first glance, a number of the ioctls you have should just be sysfs files to export the information. What am I misunderstanding here?

Quoted 3 lines

I don't think you're arguing for SUBMIT_IO being done through sysfs, so some ioctls are clearly needed. I'll take a look at which ones can be moved to sysfs.

Heh, no, well, submit_io should just go through the block layer and not be a separate ioctl, right?

Quoted 34 lines
Quoted 25 lines
Quoted 25 lines
Documentation/ioctl/ioctl-number.txt +25 -0
+static int nvme_download_firmware(struct nvme_ns *ns,
+						struct nvme_dlfw __user *udlfw)
+{
+	struct nvme_dev *dev = ns->dev;
+	struct nvme_dlfw dlfw;
+	struct nvme_command c;
+	int nents, status;
+	struct scatterlist *sg;
+	struct nvme_prps *prps;
+
+	if (copy_from_user(&dlfw, udlfw, sizeof(dlfw)))
+		return -EFAULT;
+	if (dlfw.length >= (1 << 30))
+		return -EINVAL;
+
+	nents = nvme_map_user_pages(dev, 1, dlfw.addr, dlfw.length * 4, &sg);
+	if (nents < 0)
+		return nents;
+
+	memset(&c, 0, sizeof(c));
+	c.dlfw.opcode = nvme_admin_download_fw;
+	c.dlfw.numd = cpu_to_le32(dlfw.length);
+	c.dlfw.offset = cpu_to_le32(dlfw.offset);
+	prps = nvme_setup_prps(dev, &c.common, sg, dlfw.length * 4);
+
Quoted 8 lines

Documentation/ioctl/ioctl-number.txt

Quoted 5 lines
Documentation/ioctl/ioctl-number.txt +5 -0
+	status = nvme_submit_admin_cmd(dev, &c, NULL);
+	nvme_unmap_user_pages(dev, 0, dlfw.addr, dlfw.length * 4, sg, nents);
+	nvme_free_prps(dev, prps);
+	return status;
+}

Shouldn't you be using the build-in firmware kernel interface instead of rolling your own in an ioctl?

Quoted 4 lines

There's a bit of an impedence mismatch there. Think of this as being drive firmware instead of controller firmware. This isn't for request_firmware() kind of uses, it's for some admin tool to come along and tell the drive "Oh, here's some new firmware for you".

That's fine, request_firmware will work wonderfully for that.

Quoted 6 lines

If you look at the spec [1], you'll see there are a number of firmware slots in the device, and it's up to the managability utility to decide which one to replace or activate. I dno't think you want to pull all that gnarly decision making code into the kernel, do you?

[1] http://download.intel.com/standards/nvmhci/NVM_Express_1_0_Gold.pdf

No, just export multiple "slots" as firmware devices ready to be filled in by userspace whenever it wants/needs to. The management utility can just dump the firmware to those sysfs files when it determines it needs to update the firmware, no decision making in the kernel at all.

thanks,

greg k-h

#4 lore raw
Quoted 2 lines · Greg KH

Heh, no, well, submit_io should just go through the block layer and not be a separate ioctl, right?

Just like with SG_IO, there are reasons to do I/Os without going through the block layer.

Quoted 6 lines
Quoted 4 lines

There's a bit of an impedence mismatch there. Think of this as being drive firmware instead of controller firmware. This isn't for request_firmware() kind of uses, it's for some admin tool to come along and tell the drive "Oh, here's some new firmware for you".

That's fine, request_firmware will work wonderfully for that.

How would the driver know that it should call request_firmware()? Do it every 60 seconds in case somebody's downloaded some new firmware?

Quoted 11 lines
Quoted 6 lines

If you look at the spec [1], you'll see there are a number of firmware slots in the device, and it's up to the managability utility to decide which one to replace or activate. I dno't think you want to pull all that gnarly decision making code into the kernel, do you?

[1] http://download.intel.com/standards/nvmhci/NVM_Express_1_0_Gold.pdf

No, just export multiple "slots" as firmware devices ready to be filled in by userspace whenever it wants/needs to. The management utility can just dump the firmware to those sysfs files when it determines it needs to update the firmware, no decision making in the kernel at all.

OK ... glad we decided to limit the number of slots. I still don't see (in Documentation/firmware_class/README) how this works for user-initiated firmware updates rather than kernel-initiated.

#5 lore raw
Quoted 6 lines · Matthew Wilcox
Quoted 2 lines · Greg KH

Heh, no, well, submit_io should just go through the block layer and not be a separate ioctl, right?

Just like with SG_IO, there are reasons to do I/Os without going through the block layer.

Ok, that makes sense.

Quoted 9 lines
Quoted 6 lines
Quoted 4 lines

There's a bit of an impedence mismatch there. Think of this as being drive firmware instead of controller firmware. This isn't for request_firmware() kind of uses, it's for some admin tool to come along and tell the drive "Oh, here's some new firmware for you".

That's fine, request_firmware will work wonderfully for that.

How would the driver know that it should call request_firmware()? Do it every 60 seconds in case somebody's downloaded some new firmware?

Ick, no, just use the function provided that lets you create a firmware request and be notified when it is written to, request_firmware_nowait(). That is what it is there for.

Quoted 7 lines
Quoted 7 lines
Quoted 6 lines

If you look at the spec [1], you'll see there are a number of firmware slots in the device, and it's up to the managability utility to decide which one to replace or activate. I dno't think you want to pull all that gnarly decision making code into the kernel, do you?

[1] http://download.intel.com/standards/nvmhci/NVM_Express_1_0_Gold.pdf

Quoted 8 lines
Quoted 4 lines

No, just export multiple "slots" as firmware devices ready to be filled in by userspace whenever it wants/needs to. The management utility can just dump the firmware to those sysfs files when it determines it needs to update the firmware, no decision making in the kernel at all.

OK ... glad we decided to limit the number of slots. I still don't see (in Documentation/firmware_class/README) how this works for user-initiated firmware updates rather than kernel-initiated.

I didn't even realize we had a firmware README file...

Anyway, just use request_firmware_nowait(), you will be fine.

thanks,

greg k-h

#6 lore raw
Quoted 36 lines · Greg KH
Quoted 6 lines · Matthew Wilcox
Quoted 2 lines · Greg KH

Heh, no, well, submit_io should just go through the block layer and not be a separate ioctl, right?

Just like with SG_IO, there are reasons to do I/Os without going through the block layer.

Ok, that makes sense.

Quoted 9 lines
Quoted 6 lines
Quoted 4 lines

There's a bit of an impedence mismatch there. Think of this as being drive firmware instead of controller firmware. This isn't for request_firmware() kind of uses, it's for some admin tool to come along and tell the drive "Oh, here's some new firmware for you".

That's fine, request_firmware will work wonderfully for that.

How would the driver know that it should call request_firmware()? Do it every 60 seconds in case somebody's downloaded some new firmware?

Ick, no, just use the function provided that lets you create a firmware request and be notified when it is written to, request_firmware_nowait(). That is what it is there for.

Quoted 12 lines
Quoted 11 lines
Quoted 6 lines

If you look at the spec [1], you'll see there are a number of firmware slots in the device, and it's up to the managability utility to decide which one to replace or activate. I dno't think you want to pull all that gnarly decision making code into the kernel, do you?

[1] http://download.intel.com/standards/nvmhci/NVM_Express_1_0_Gold.pdf

No, just export multiple "slots" as firmware devices ready to be filled in by userspace whenever it wants/needs to. The management utility can just dump the firmware to those sysfs files when it determines it needs to update the firmware, no decision making in the kernel at all.

Quoted 8 lines
Quoted 3 lines

OK ... glad we decided to limit the number of slots. I still don't see (in Documentation/firmware_class/README) how this works for user-initiated firmware updates rather than kernel-initiated.

I didn't even realize we had a firmware README file...

Anyway, just use request_firmware_nowait(), you will be fine.

Unless I'm misunderstanding the spec, this is for *nonvolatile* firmware updates. So if I have two of these devices and I stick a firmware file into /lib/firmware, should it update both devices?

Shouldn't reflashing the device firmware be something that the users asks for by something a little more specific than just creating a file?
(In any case, creating /lib/firmware/nvmhci-3 to flash slot 3 seems
rather silly.)

--Andy

#7 lore raw
Quoted 7 lines · Andy Lutomirski <luto <at> MIT.EDU> writes:
Quoted 6 lines

I didn't even realize we had a firmware README file...

Anyway, just use request_firmware_nowait(), you will be fine.

Quoted 9 lines

Unless I'm misunderstanding the spec, this is for *nonvolatile* firmware updates. So if I have two of these devices and I stick a firmware file into /lib/firmware, should it update both devices?

Shouldn't reflashing the device firmware be something that the users asks for by something a little more specific than just creating a file?
(In any case, creating /lib/firmware/nvmhci-3 to flash slot 3 seems
rather silly.)

_lame user warning on_

if module is built-in, maybe something like /sys/class/nvm/driveX/fw_path could receive the _path_ to firmware, so that writing to it (either/or writing to hypothetical /sys/class/nvm/driveX/fw_do_update) would trigger the flashing procedure, and reading it would provide _human readable_ flashing progress information ?

If module is complied, well, as a module, could be invoked with the fw_path parameter and the fw_do_update parameter as the safety flag.

With appropriate safety measures, like making sure drive is unmounted, queue is empty and so on, ensured in userspace?

Quoted 2 lines

--Andy

el es

#8 lore raw
In reply to Andy Lutomirski
Quoted 46 lines · Andy Lutomirski
Quoted 36 lines · Greg KH
Quoted 6 lines · Matthew Wilcox
Quoted 2 lines · Greg KH

Heh, no, well, submit_io should just go through the block layer and not be a separate ioctl, right?

Just like with SG_IO, there are reasons to do I/Os without going through the block layer.

Ok, that makes sense.

Quoted 9 lines
Quoted 6 lines
Quoted 4 lines

There's a bit of an impedence mismatch there. Think of this as being drive firmware instead of controller firmware. This isn't for request_firmware() kind of uses, it's for some admin tool to come along and tell the drive "Oh, here's some new firmware for you".

That's fine, request_firmware will work wonderfully for that.

How would the driver know that it should call request_firmware()? Do it every 60 seconds in case somebody's downloaded some new firmware?

Ick, no, just use the function provided that lets you create a firmware request and be notified when it is written to, request_firmware_nowait(). That is what it is there for.

Quoted 12 lines
Quoted 11 lines
Quoted 6 lines

If you look at the spec [1], you'll see there are a number of firmware slots in the device, and it's up to the managability utility to decide which one to replace or activate. I dno't think you want to pull all that gnarly decision making code into the kernel, do you?

[1] http://download.intel.com/standards/nvmhci/NVM_Express_1_0_Gold.pdf

No, just export multiple "slots" as firmware devices ready to be filled in by userspace whenever it wants/needs to. The management utility can just dump the firmware to those sysfs files when it determines it needs to update the firmware, no decision making in the kernel at all.

Quoted 8 lines
Quoted 3 lines

OK ... glad we decided to limit the number of slots. I still don't see (in Documentation/firmware_class/README) how this works for user-initiated firmware updates rather than kernel-initiated.

I didn't even realize we had a firmware README file...

Anyway, just use request_firmware_nowait(), you will be fine.

Quoted 8 lines

Unless I'm misunderstanding the spec, this is for *nonvolatile* firmware updates. So if I have two of these devices and I stick a firmware file into /lib/firmware, should it update both devices?

Shouldn't reflashing the device firmware be something that the users asks for by something a little more specific than just creating a file? (In any case, creating /lib/firmware/nvmhci-3 to flash slot 3 seems rather silly.)

Yes, a userspace tool should handle all of this, much like Dell does this with BIOS updates through this same interface. See their in-kernel code, and userspace tools for details on how to do this sanely.

thanks,

greg k-h

#9 lore raw
In reply to Greg KH

On Thu, 3 Mar 2011 14:22:26 -0800

Quoted 6 lines
Quoted 2 lines

How would the driver know that it should call request_firmware()? Do it every 60 seconds in case somebody's downloaded some new firmware?

Ick, no, just use the function provided that lets you create a firmware request and be notified when it is written to, request_firmware_nowait(). That is what it is there for.

Bunkum

It's there for automated loading of needed firmware

Quoted 1 line

Anyway, just use request_firmware_nowait(), you will be fine.

Not really. You've got no way with the request_firmware interface for the user space and kernel space to reliably time objects together eg to do user side work before a firmware load.

Almost all the other stuff in the kernel which is for firmware programming as opposed to automatic loading is not using request_firmware
- and for good reasons like needing to be able to specify the path to the object reliably - which cannot be done with namespaces.

Quoted 1 line

From a security perspective, a correctness perspective, for reliability

and managability request_firmware is the wrong interface for flashing new firmware.

Alan

#10 lore raw
Quoted 11 lines · Alan Cox

On Thu, 3 Mar 2011 14:22:26 -0800

Quoted 6 lines
Quoted 2 lines

How would the driver know that it should call request_firmware()? Do it every 60 seconds in case somebody's downloaded some new firmware?

Ick, no, just use the function provided that lets you create a firmware request and be notified when it is written to, request_firmware_nowait(). That is what it is there for.

Bunkum

It's there for automated loading of needed firmware

And non-automated loading of firmware as well. Dell uses this for updating their BIOSes just fine, with a userspace tool that initiates the loading of the firmware.

Quoted 5 lines
Quoted 1 line

Anyway, just use request_firmware_nowait(), you will be fine.

Not really. You've got no way with the request_firmware interface for the user space and kernel space to reliably time objects together eg to do user side work before a firmware load.

How does Dell do it?

Quoted 8 lines

Almost all the other stuff in the kernel which is for firmware programming as opposed to automatic loading is not using request_firmware
- and for good reasons like needing to be able to specify the path to the object reliably - which cannot be done with namespaces.

From a security perspective, a correctness perspective, for reliability and managability request_firmware is the wrong interface for flashing new firmware.

Tell me how you really feel now :)

So, what could be changed in the current firmware interface to fix this problem in a manner which would solve these perceived issues?

thanks,

greg k-h

#11 lore raw
Quoted 3 lines

And non-automated loading of firmware as well. Dell uses this for updating their BIOSes just fine, with a userspace tool that initiates the loading of the firmware.

Try using the Dell tool with namespaces.

Quoted 1 line

How does Dell do it?

How do most other apps do it.

Quoted 2 lines

So, what could be changed in the current firmware interface to fix this problem in a manner which would solve these perceived issues?

I'm not sure it can. The basis of the interface is driver requests firmware. That is done by using a pathname which in a namespaced environment isn't global and has races

(Several things break quite spectacularly if you request_firmware while updating a package, but of course nobody considered such details even for automatic stuff in many cases. Really there is some locking needed).

For manual updating of a block of firmware the interface most stuff wants is

copy_from_user()

or if you wanted to wrap it up nicely

	x = vmalloc_from_user(void __user *ptr, ssize_t len);

The app knows which firmware it is talking about and can inspect and compare it while holding an open file handle on the deivce. That protects against hotplug races and getting the wrong device second access, and ensures that the firmware, device and userspace are all talking about exactly the same thing.

It would nice to say "its an obscure corner case that will just error", but far too much hardware still gets semi permanently (or permanently) converted into junk if you goof a permanent flash firmware update.

#12 lore raw
Quoted 5 lines · Alan Cox
Quoted 3 lines

And non-automated loading of firmware as well. Dell uses this for updating their BIOSes just fine, with a userspace tool that initiates the loading of the firmware.

Try using the Dell tool with namespaces.

Heck, try using _any_ tool that talks to sysfs with namespaces, don't try to claim that this driver using its own custom firmware loader is a solution to the namespace/sysfs issue please. That's very disingenuous.

Quoted 3 lines
Quoted 1 line

How does Dell do it?

How do most other apps do it.

They write to the sysfs file with the firmware when they feel like it.

Quoted 6 lines
Quoted 2 lines

So, what could be changed in the current firmware interface to fix this problem in a manner which would solve these perceived issues?

I'm not sure it can. The basis of the interface is driver requests firmware. That is done by using a pathname which in a namespaced environment isn't global and has races

Of course, but those races don't show up in real systems, right?

Quoted 3 lines

(Several things break quite spectacularly if you request_firmware while updating a package, but of course nobody considered such details even for automatic stuff in many cases. Really there is some locking needed).

I've never heard of this race before as you usually do firmware upload either at boot time, or when a user specifically asks for it. Neither of those times is when packages are usually getting updated.

Quoted 7 lines

For manual updating of a block of firmware the interface most stuff wants is

copy_from_user()

or if you wanted to wrap it up nicely

Quoted 7 lines
	x = vmalloc_from_user(void __user *ptr, ssize_t len);

The app knows which firmware it is talking about and can inspect and compare it while holding an open file handle on the deivce. That protects against hotplug races and getting the wrong device second access, and ensures that the firmware, device and userspace are all talking about exactly the same thing.

But you do get this type of buffer from the firware interface to your driver today, right?

Quoted 3 lines

It would nice to say "its an obscure corner case that will just error", but far too much hardware still gets semi permanently (or permanently) converted into junk if you goof a permanent flash firmware update.

One would hope that the hardware was a bit more resiliant than that, but I know how hardware is designed :(

Still, I don't want this to all of a sudden be "open season" for every individual driver deciding to want to create an ioctl call for firmware updating just because the authors don't like the existing in-kernel interface. Please fix up the in-kernel one instead to meet your needs.

thanks,

greg k-h

#13 lore raw
Quoted 3 lines
Quoted 1 line

How do most other apps do it.

They write to the sysfs file with the firmware when they feel like it.

Very few seem to do this. Quite a lot also have a much more complex interaction anyway than 'firmware please', 'zap' , 'ok' to be fair.

Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

So, what could be changed in the current firmware interface to fix this problem in a manner which would solve these perceived issues?

I'm not sure it can. The basis of the interface is driver requests firmware. That is done by using a pathname which in a namespaced environment isn't global and has races

Of course, but those races don't show up in real systems, right?

They do. That's why I know they exist. If enough people use your distro then eventually someone catches it.

Quoted 7 lines
Quoted 3 lines

(Several things break quite spectacularly if you request_firmware while updating a package, but of course nobody considered such details even for automatic stuff in many cases. Really there is some locking needed).

I've never heard of this race before as you usually do firmware upload either at boot time, or when a user specifically asks for it. Neither of those times is when packages are usually getting updated.

Usually is not a good answer for correctness and security with firmware. It's really really not a good answer when flashing new firmware. "Usually your expensive hardware is not turned into a valueless brick" lacks a certain something.

For dynamically requested firmware the request_firmware interface is excellent stuff, and the right way to fix the races is to lock between the daemon and package manager. For flashing stuff it's not up to the job.

Quoted 10 lines
Quoted 10 lines

For manual updating of a block of firmware the interface most stuff wants is

copy_from_user()

or if you wanted to wrap it up nicely

	x = vmalloc_from_user(void __user *ptr, ssize_t len);
Quoted 8 lines
Quoted 5 lines

The app knows which firmware it is talking about and can inspect and compare it while holding an open file handle on the deivce. That protects against hotplug races and getting the wrong device second access, and ensures that the firmware, device and userspace are all talking about exactly the same thing.

But you do get this type of buffer from the firware interface to your driver today, right?

Providing you jump through a small army of hoops, get the right sysfs nodes, dodge any race conditions, hotplug and the like yes. But it should be robust and simple. Using request_firmware for this case is neither robust nor simple, nor is it easy to get the sysfs/driver open/hotplug race handling right so instead of the kernel code being very occasionally buggy (which we can spot) the user apps will be horribly buggy and we don't read those so your push for a complex mis-standard in the kernel actually makes the problem far worse than it would be without.

Quoted 4 lines

Still, I don't want this to all of a sudden be "open season" for every individual driver deciding to want to create an ioctl call for firmware updating just because the authors don't like the existing in-kernel interface. Please fix up the in-kernel one instead to meet your needs.

You are not creating an open season. The every driver having its own ioctl for firmware updating is the norm, and its in some ways a rather good norm because when it comes to flash updating there isn't much standardisation and you don't want standardisation as it just asks for the wrong tool to work in an unfortunate race or user mix-up. Flash updating is special - it's good that one app doesn't work on another device !

You could have a standardised helper easily enough I guess. Pick a standard struct and firmware descriptor and provide a

request_firmware_from_user(struct firmware_something __user *fw)

which hands back stuff in the form the rest of the firmware logic likes to play and has a standard user side struct format.

Alan

#14 lore raw
Quoted 10 lines · Alan Cox
Quoted 3 lines
Quoted 1 line

How do most other apps do it.

They write to the sysfs file with the firmware when they feel like it.

Very few seem to do this. Quite a lot also have a much more complex interaction anyway than 'firmware please', 'zap' , 'ok' to be fair.

Quoted 3 lines
Quoted 3 lines
Quoted 2 lines

So, what could be changed in the current firmware interface to fix this problem in a manner which would solve these perceived issues?

Quoted 8 lines
Quoted 5 lines
Quoted 3 lines

I'm not sure it can. The basis of the interface is driver requests firmware. That is done by using a pathname which in a namespaced environment isn't global and has races

Of course, but those races don't show up in real systems, right?

They do. That's why I know they exist. If enough people use your distro then eventually someone catches it.

Ok, I guess I must be doing support for distros that never get used :)

Have pointers to bug reports?

Quoted 8 lines
Quoted 7 lines
Quoted 3 lines

(Several things break quite spectacularly if you request_firmware while updating a package, but of course nobody considered such details even for automatic stuff in many cases. Really there is some locking needed).

I've never heard of this race before as you usually do firmware upload either at boot time, or when a user specifically asks for it. Neither of those times is when packages are usually getting updated.

Quoted 8 lines

Usually is not a good answer for correctness and security with firmware. It's really really not a good answer when flashing new firmware. "Usually your expensive hardware is not turned into a valueless brick" lacks a certain something.

For dynamically requested firmware the request_firmware interface is excellent stuff, and the right way to fix the races is to lock between the daemon and package manager. For flashing stuff it's not up to the job.

Fair enough.

Quoted 40 lines
Quoted 9 lines
Quoted 9 lines

For manual updating of a block of firmware the interface most stuff wants is

copy_from_user()

or if you wanted to wrap it up nicely

	x = vmalloc_from_user(void __user *ptr, ssize_t len);
Quoted 8 lines
Quoted 5 lines

The app knows which firmware it is talking about and can inspect and compare it while holding an open file handle on the deivce. That protects against hotplug races and getting the wrong device second access, and ensures that the firmware, device and userspace are all talking about exactly the same thing.

But you do get this type of buffer from the firware interface to your driver today, right?

Providing you jump through a small army of hoops, get the right sysfs nodes, dodge any race conditions, hotplug and the like yes. But it should be robust and simple. Using request_firmware for this case is neither robust nor simple, nor is it easy to get the sysfs/driver open/hotplug race handling right so instead of the kernel code being very occasionally buggy (which we can spot) the user apps will be horribly buggy and we don't read those so your push for a complex mis-standard in the kernel actually makes the problem far worse than it would be without.

Quoted 4 lines

Still, I don't want this to all of a sudden be "open season" for every individual driver deciding to want to create an ioctl call for firmware updating just because the authors don't like the existing in-kernel interface. Please fix up the in-kernel one instead to meet your needs.

You are not creating an open season. The every driver having its own ioctl for firmware updating is the norm, and its in some ways a rather good norm because when it comes to flash updating there isn't much standardisation and you don't want standardisation as it just asks for the wrong tool to work in an unfortunate race or user mix-up. Flash updating is special - it's good that one app doesn't work on another device !

Quoted 7 lines

You could have a standardised helper easily enough I guess. Pick a standard struct and firmware descriptor and provide a

request_firmware_from_user(struct firmware_something __user *fw)

which hands back stuff in the form the rest of the firmware logic likes to play and has a standard user side struct format.

Ok, patches gladly accepted :)

As well as the general "The firmware subsystem needs an active maintainer" plea, as the previous maintainer passed away a number of years ago :(

thanks,

greg k-h

#15 lore raw
Quoted 3 lines

Ok, I guess I must be doing support for distros that never get used :)

Have pointers to bug reports?

Not to hand it was some time ago at a previous employer and the fix we agree ona anyway I think ?

Quoted 9 lines
Quoted 7 lines

You could have a standardised helper easily enough I guess. Pick a standard struct and firmware descriptor and provide a

request_firmware_from_user(struct firmware_something __user *fw)

which hands back stuff in the form the rest of the firmware logic likes to play and has a standard user side struct format.

Ok, patches gladly accepted :)

I'll have a look next week if I get a moment, it doesn't look hard.

#16 lore raw
In reply to Matthew Wilcox
Quoted 4 lines · Matthew Wilcox

There's a bit of an impedence mismatch there. Think of this as being drive firmware instead of controller firmware. This isn't for request_firmware() kind of uses, it's for some admin tool to come along and tell the drive "Oh, here's some new firmware for you".

What some other devices I've seen do for this is expose the firmware storage as a MTD device. Dunno if that matches the hardware at all here, though.

#17 lore raw
In reply to Matthew Wilcox
Quoted 20 lines · On Thu, 3 Mar 2011 15:47:49 -0500 Matthew Wilcox wrote:
drivers/block/nvme.c new file +14 -0
new file mode 100644
index 0000000..a8549df
@@ -0,0 +1,1622 @@
+/*
+ * NVM Express device driver
+ * Copyright (c) 2011, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with

drivers/block/nvme.c line 52

Quoted 10 lines
drivers/block/nvme.c new file +8 -0
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
 
 
+static int nvme_major;
+module_param(nvme_major, int, 0);
+
+static int use_threaded_interrupts;
+module_param(use_threaded_interrupts, int, 0);

Please add MODULE_PARM_DESC() for the parameters.

Quoted 6 lines

+/**
+ * alloc_cmdid - Allocate a Command ID
+ * @param nvmeq The queue that will be used for this command
+ * @param ctx A pointer that will be passed to the handler
+ * @param handler The ID of the handler to call
+ *

That's not quite kernel-doc notation, but /** in kernel code signifies beginning of kernel-doc notation, so please change it to kernel-doc notation (or drop the /**). [in multiple places]

Quoted 29 lines

+ * Allocate a Command ID for a queue. The data passed in will
+ * be passed to the completion handler. This is implemented by using
+ * the bottom two bits of the ctx pointer to store the handler ID.
+ * Passing in a pointer that's not 4-byte aligned will cause a BUG.
+ * We can change this if it becomes a problem.
+ */

drivers/block/nvme.c new file +23 -0
+static int alloc_cmdid(struct nvme_queue *nvmeq, void *ctx, int handler,
+							unsigned timeout)
+{
+	int depth = nvmeq->q_depth - 1;
+	struct nvme_cmd_info *info = nvme_cmd_info(nvmeq);
+	int cmdid;
+
+	BUG_ON((unsigned long)ctx & 3);
+
+	do {
+		cmdid = find_first_zero_bit(nvmeq->cmdid_data, depth);
+		if (cmdid >= depth)
+			return -EBUSY;
+	} while (test_and_set_bit(cmdid, nvmeq->cmdid_data));
+
+	info[cmdid].ctx = (unsigned long)ctx | handler;
+	info[cmdid].timeout = jiffies + timeout;
+	return cmdid;
+}
+
+static int alloc_cmdid_killable(struct nvme_queue *nvmeq, void *ctx,
+						int handler, unsigned timeout)
+{

drivers/block/nvme.c line 188

Quoted 10 lines
drivers/block/nvme.c new file +10 -0
+	int cmdid;
+	wait_event_killable(nvmeq->sq_full,
+		(cmdid = alloc_cmdid(nvmeq, ctx, handler, timeout)) >= 0);
+	return (cmdid < 0) ? -EINTR : cmdid;
+}
+
+/* If you need more than four handlers, you'll need to change how
+ * alloc_cmdid and nvme_process_cq work.  Consider using a special
+ * CMD_CTX value instead, if that works for your situation.
+ */

Please use the common multi-line comment format: [in multiple places]

/*
* If you need more than four handlers, you'll need to change how
* alloc_cmdid and nvme_process_cq work. Consider using a special
* CMD_CTX value instead, if that works for your situation. */

---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***

#18 lore raw
In reply to Matthew Wilcox

What's the reason to make this a make_request based driver? That loses all the intelligence that has been put into the queueing layer, like I/O bandwith controlling and fair scheduling. Is it just queue_lock contention? If so it would be great if you could help with testing Jens' stack-plug and request allocation scalability patches.

#19 lore raw
Quoted 5 lines · Christoph Hellwig

What's the reason to make this a make_request based driver? That loses all the intelligence that has been put into the queueing layer, like I/O bandwith controlling and fair scheduling. Is it just queue_lock contention? If so it would be great if you could help with testing Jens' stack-plug and request allocation scalability patches.

The hardware has the ability to do I/O bandwidth control and fair scheduling (albeit on a fairly coarse granularity). By doing it in software, all we're doing for these devices is increasing I/O latency and burning CPU cycles.

#20 lore raw
In reply to Matthew Wilcox

drivers/block/nvme.c

Quoted 9 lines · Matthew Wilcox
drivers/block/nvme.c new file +9 -0
+
+static struct nvme_queue *get_nvmeq(struct nvme_ns *ns)
+{
+	int qid, cpu = get_cpu();
+	if (cpu < ns->dev->queue_count)
+		qid = cpu + 1;
+	else
+		qid = (cpu % rounddown_pow_of_two(ns->dev->queue_count))
+ 1;

This will be likely a full divide, better use a mask.

drivers/block/nvme.c line 367

Quoted 7 lines
drivers/block/nvme.c new file +7 -0
+	nprps = DIV_ROUND_UP(length, PAGE_SIZE);
+	npages = DIV_ROUND_UP(8 * nprps, PAGE_SIZE);
+	prps = kmalloc(sizeof(*prps) + sizeof(__le64 *) * npages, GFP_ATOMIC);
+	prp_page = 0;
+	if (nprps <= (256 / 8)) {
+		pool = dev->prp_small_pool;
+		prps->npages = 0;

Unchecked GFP_ATOMIC allocation? That will oops soon. Besides GFP_ATOMIC a very risky thing to do on a low memory situation, which can trigger writeouts.

drivers/block/nvme.c line 374

Quoted 7 lines
drivers/block/nvme.c new file +7 -0
+	} else {
+		pool = dev->prp_page_pool;
+		prps->npages = npages;
+	}
+
+	prp_list = dma_pool_alloc(pool, GFP_ATOMIC, &prp_dma);
+	prps->list[prp_page++] = prp_list;

And another one.

Didn't read all of it.

-Andi

Signature · 1 line
[email protected] -- Speaking for myself only
#21 lore raw
Quoted 12 lines · Andi Kleen

drivers/block/nvme.c

Quoted 9 lines · Matthew Wilcox
drivers/block/nvme.c new file +9 -0
+
+static struct nvme_queue *get_nvmeq(struct nvme_ns *ns)
+{
+	int qid, cpu = get_cpu();
+	if (cpu < ns->dev->queue_count)
+		qid = cpu + 1;
+	else
+		qid = (cpu % rounddown_pow_of_two(ns->dev->queue_count))
+ 1;

This will be likely a full divide, better use a mask.

I have a TODO to replace this calculation with a lookup; I've discovered that not even all Intel systems number the CPUs in the logical fashion (eg on a two-socket system; cpu 0 in socket 0, cpu 1 in socket 1, cpu 2 in socket 0, etc; some two socket systems have cpus 0-3 in socket 0; 4-7 in socket 1; and 8-15 are the HT siblings of 0-7).

Is there a good API to iterate through each socket, then each core in a socket, then each HT sibling? eg, if I have 20 queues and 2x6x2 CPUs, I want to assign at least one queue to each core; some threads will get their own queues and others will have to share with their HT sibling.

Quoted 4 lines
Quoted 4 lines
drivers/block/nvme.c new file +4 -0
+	nprps = DIV_ROUND_UP(length, PAGE_SIZE);
+	npages = DIV_ROUND_UP(8 * nprps, PAGE_SIZE);
+	prps = kmalloc(sizeof(*prps) + sizeof(__le64 *) * npages, GFP_ATOMIC);
+	prp_page = 0;
Quoted 8 lines

drivers/block/nvme.c line 367

Quoted 3 lines
drivers/block/nvme.c new file +3 -0
+	if (nprps <= (256 / 8)) {
+		pool = dev->prp_small_pool;
+		prps->npages = 0;

Unchecked GFP_ATOMIC allocation? That will oops soon. Besides GFP_ATOMIC a very risky thing to do on a low memory situation, which can trigger writeouts.

Ah yes, thank you. There are a few other places like this. Bizarrely, they've not oopsed during the xfstests runs.

My plan for this is, instead of using a mempool, to submit partial I/Os in the rare cases where a write cannot allocate memory. I have the design in my head, just not committed to code yet. The design also avoids allocating any memory in the driver for I/Os that do not cross a page boundary.

#22 lore raw
Quoted 2 lines · Matthew Wilcox

Is there a good API to iterate through each socket, then each core in a socket, then each HT sibling? eg, if I have 20 queues and 2x6x2 CPUs,

Not for this particular order. And also you have to handle hotplug in any case anyways.

And whatever you do, don't add NR_CPUS arrays.

Quoted 2 lines

I want to assign at least one queue to each core; some threads will get their own queues and others will have to share with their HT sibling.

Please write a generic library function for this if you do this.

Quoted 14 lines
Quoted 13 lines

drivers/block/nvme.c line 367

Quoted 8 lines
drivers/block/nvme.c new file +7 -0
+	nprps = DIV_ROUND_UP(length, PAGE_SIZE);
+	npages = DIV_ROUND_UP(8 * nprps, PAGE_SIZE);
+	prps = kmalloc(sizeof(*prps) + sizeof(__le64 *) * npages, GFP_ATOMIC);
+	prp_page = 0;
+	if (nprps <= (256 / 8)) {
+		pool = dev->prp_small_pool;
+		prps->npages = 0;

Unchecked GFP_ATOMIC allocation? That will oops soon. Besides GFP_ATOMIC a very risky thing to do on a low memory situation, which can trigger writeouts.

Quoted 2 lines

Ah yes, thank you. There are a few other places like this. Bizarrely, they've not oopsed during the xfstests runs.

You need suitable background load. If you run it in LTP the harness has support for background load. For GFP_ATOMIC exhaustion you typically need something interrupt intensive, like a lot of networking.

Quoted 6 lines

My plan for this is, instead of using a mempool, to submit partial I/Os in the rare cases where a write cannot allocate memory. I have the design in my head, just not committed to code yet. The design also avoids allocating any memory in the driver for I/Os that do not cross a page boundary.

I forgot the latest status, but there were a lot of improvements with dirty pages handling since that "no memory allocation on writeout" rule was introduced. It may not be as big a problem as it used to be with GFP_NOFS.

Copying linux-mm in case there are deep thoughts on this there.

Just GFP_ATOMIC is definitely still a bad idea there.

-Andi

Signature · 1 line
[email protected] -- Speaking for myself only.
#23 lore raw
In reply to Matthew Wilcox
Quoted 6 lines · On Thursday 03 March 2011 21:47:49 Matthew Wilcox wrote:

You may have seen the recent announcement of the NVM Express spec; here's the driver. I've put a git tree up on kernel.org with all the history that's fit to be released (earlier versions were full of Intel codenames and are not suited to public consumption). If you'd rather review the history, it's at
http://git.kernel.org/?p=linux/kernel/git/willy/nvme.git;a=summary

Looks really nice overall, I only have a few comments about the ioctl interface. As Greg already commented, it would be good to reduce the number of ioctl commands to a minimum.

Quoted 12 lines
drivers/block/nvme.c new file +12 -0
+static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
+                                                       unsigned long arg)
+{
+       struct nvme_ns *ns = bdev->bd_disk->private_data;
+
+       switch (cmd) {
+       case NVME_IOCTL_IDENTIFY_NS:
+               return nvme_identify(ns, arg, 0);
+       case NVME_IOCTL_IDENTIFY_CTRL:
+               return nvme_identify(ns, arg, 1);
+       case NVME_IOCTL_GET_RANGE_TYPE:
+               return nvme_get_range_type(ns, arg);

drivers/block/nvme.c line 1147

Quoted 10 lines
drivers/block/nvme.c new file +10 -0
+       case NVME_IOCTL_SUBMIT_IO:
+               return nvme_submit_io(ns, (void __user *)arg);
+       case NVME_IOCTL_DOWNLOAD_FW:
+               return nvme_download_firmware(ns, (void __user *)arg);
+       case NVME_IOCTL_ACTIVATE_FW:
+               return nvme_activate_firmware(ns, arg);
+       default:
+               return -ENOTTY;
+       }
+}

The argument is a pointer for all of these, as far as I can tell, so you could do the typecast here once instead of doing it for two of them.

drivers/block/nvme.c line 1152

Quoted 4 lines
drivers/block/nvme.c new file +4 -0
+static const struct block_device_operations nvme_fops = {
+       .owner          = THIS_MODULE,
+       .ioctl          = nvme_ioctl,
+};

This is missing a .compat_ioctl pointer to work with 32 bit user space. The data structures all seem compatible, so that should be a trivial change.

include/linux/nvme.h line 401

Quoted 6 lines
include/linux/nvme.h new file +6 -0
+#define NVME_IOCTL_IDENTIFY_NS _IOW('N', 0x40, struct nvme_id_ns)
+#define NVME_IOCTL_IDENTIFY_CTRL _IOW('N', 0x41, struct nvme_id_ctrl)
+#define NVME_IOCTL_GET_RANGE_TYPE _IOW('N', 0x42, struct nvme_lba_range_type)
+#define NVME_IOCTL_SUBMIT_IO   _IOWR('N', 0x43, struct nvme_rw_command)
+#define NVME_IOCTL_DOWNLOAD_FW _IOR('N', 0x44, struct nvme_dlfw)
+#define NVME_IOCTL_ACTIVATE_FW _IO('N', 0x45)

I believe you have confused read and write here. IIRC, _IOW means copy_from_user and _IOR means copy_to_user.

The code for NVME_IOCTL_SUBMIT_IO takes a nvme_user_io argument, not nvme_rw_command.

You might want to have an upper bound on the amount of user data that one can pass to SUBMIT_IO and others.

Arnd