Skip to contentSkip to conversation

Cell patches for 2.6.27, version 2

PATCH In review [email protected] 6 participants 25 messages Jul 15, 2008 – Jul 22, 2008

Subsystems (5)
Documentation VFS PowerPC EDAC-core +1 more subsystems
lore
Share & tools
lore
#0 lore raw

cover Cell patches for 2.6.27, version 2

Hi Ben,

These are the remaining patches I have lined up for 2.6.27. Thanks for including the others already. There were a few comments in the last round that I hope all got addressed now.

I'm not sure about the final two patches for the IOMMU, I think you wanted to investigate the situation more, but I haven't seen an update from you any more. I'm convinced going to weak ordering is the right move for the cell IOMMU, and even if it is not, it will not break anything on current machines because the firmware already overrides this setting.

I have left the copy_to_user in AZFS in place, I think we need to follow up on this again, to either document that copy_fromto_user needs to be able to work on uncached memory, or to do an extra copy, but I think this should not hold up the AZFS merge. All other comments for it have been addressed.

You can pull from the usual location at

master.kernel.org:/pub/scm/linux/kernel/git/arnd/cell-2.6.git cell-next

	Arnd <><

--

#1 lore raw

PATCH 1/9 powerpc/cell/edac: log a syndrome code in case of correctable error

From: Maxim Shchetynin <[email protected]>

If correctable error occurs the syndrome code was logged as 0. This patch lets EDAC to log a correct syndrome code to make problem investigation easier.

---
 drivers/edac/cell_edac.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)
drivers/edac/cell_edac.c +3 -2
index b54112f..0e024fe 100644
@@ -33,7 +33,7 @@ static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar)
 {
 	struct cell_edac_priv		*priv = mci->pvt_info;
 	struct csrow_info		*csrow = &mci->csrows[0];
-	unsigned long			address, pfn, offset;
+	unsigned long			address, pfn, offset, syndrome;
 
 	dev_dbg(mci->dev, "ECC CE err on node %d, channel %d, ar = 0x%016lx\n",
 		priv->node, chan, ar);
@@ -44,10 +44,11 @@ static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar)
 		address = (address << 1) | chan;
 	pfn = address >> PAGE_SHIFT;
 	offset = address & ~PAGE_MASK;
+	syndrome = (ar & 0x000000001fe00000ul) >> 21;
 
 	/* TODO: Decoding of the error addresss */
 	edac_mc_handle_ce(mci, csrow->first_page + pfn, offset,
-			  0, 0, chan, "");
+			  syndrome, 0, chan, "");
 }
 
 static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar)
Signature · 3 lines
1.5.4.3

-- 
#2 lore raw

Re: [patch 1/9] powerpc/cell/edac: log a syndrome code in case of correctable error

Arnd, Maxim, please, next time, send that patch or at least CC the bluesmoke-devel list for EDAC related bits.

Doug, if you are ok with this patch, I'll merge it via the powerpc tree.

Cheers,
Ben.

On Tue, 2008-07-15 at 21:51 +0200, [email protected]

Quoted 41 lines

From: Maxim Shchetynin <[email protected]>

If correctable error occurs the syndrome code was logged as 0. This patch lets EDAC to log a correct syndrome code to make problem investigation easier.

---
 drivers/edac/cell_edac.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)
drivers/edac/cell_edac.c +3 -2
index b54112f..0e024fe 100644
@@ -33,7 +33,7 @@ static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar)
 {
 	struct cell_edac_priv		*priv = mci->pvt_info;
 	struct csrow_info		*csrow = &mci->csrows[0];
-	unsigned long			address, pfn, offset;
+	unsigned long			address, pfn, offset, syndrome;
 
 	dev_dbg(mci->dev, "ECC CE err on node %d, channel %d, ar = 0x%016lx\n",
 		priv->node, chan, ar);
@@ -44,10 +44,11 @@ static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar)
 		address = (address << 1) | chan;
 	pfn = address >> PAGE_SHIFT;
 	offset = address & ~PAGE_MASK;
+	syndrome = (ar & 0x000000001fe00000ul) >> 21;
 
 	/* TODO: Decoding of the error addresss */
 	edac_mc_handle_ce(mci, csrow->first_page + pfn, offset,
-			  0, 0, chan, "");
+			  syndrome, 0, chan, "");
 }
 
 static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar)
Signature · 1 line
1.5.4.3
Acked-by
#3 lore raw

Re: [patch 1/9] powerpc/cell/edac: log a syndrome code in case of correctable error

Quoted 4 lines · --- Benjamin Herrenschmidt

Arnd, Maxim, please, next time, send that patch or at least CC the bluesmoke-devel list for EDAC related bits.

Doug, if you are ok with this patch, I'll merge it via the powerpc

fine with me, acked below

doug t

Quoted 15 lines

tree.

Cheers,
Ben.

On Tue, 2008-07-15 at 21:51 +0200, [email protected]

Quoted 8 lines

From: Maxim Shchetynin <[email protected]>

If correctable error occurs the syndrome code was logged as 0. This patch lets EDAC to log a correct syndrome code to make problem investigation easier.

Quoted 29 lines
Quoted 29 lines
---
 drivers/edac/cell_edac.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)
drivers/edac/cell_edac.c +3 -2
index b54112f..0e024fe 100644
@@ -33,7 +33,7 @@ static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar)
 {
 	struct cell_edac_priv		*priv = mci->pvt_info;
 	struct csrow_info		*csrow = &mci->csrows[0];
-	unsigned long			address, pfn, offset;
+	unsigned long			address, pfn, offset, syndrome;
 
 	dev_dbg(mci->dev, "ECC CE err on node %d, channel %d, ar = 0x%016lx\n",
 		priv->node, chan, ar);
@@ -44,10 +44,11 @@ static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar)
 		address = (address << 1) | chan;
 	pfn = address >> PAGE_SHIFT;
 	offset = address & ~PAGE_MASK;
+	syndrome = (ar & 0x000000001fe00000ul) >> 21;
 
 	/* TODO: Decoding of the error addresss */
 	edac_mc_handle_ce(mci, csrow->first_page + pfn, offset,
-			  0, 0, chan, "");
+			  syndrome, 0, chan, "");
 }
 
Quoted 6 lines
Quoted 4 lines
drivers/edac/cell_edac.c +0 -0
 static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar)
Signature · 1 line
1.5.4.3

W1DUG

#4 lore raw

PATCH 2/9 powerpc/axonram: use only one block device major number

In reply to [email protected]

From: Maxim Shchetynin <[email protected]>

Axonram module registers one block device for each DDR2 DIMM found on a system. This means that each DDR2 DIMM becomes its own block device major number. This patch lets axonram module to register the only one block device for all DDR2 DIMMs which also spares kernel resources.

---
 arch/powerpc/sysdev/axonram.c |   23 +++++++++++++++--------
 1 files changed, 15 insertions(+), 8 deletions(-)
arch/powerpc/sysdev/axonram.c +15 -8
index 7f59188..9b639ed 100644
@@ -57,6 +57,8 @@
 #define AXON_RAM_SECTOR_SIZE		1 << AXON_RAM_SECTOR_SHIFT
 #define AXON_RAM_IRQ_FLAGS		IRQF_SHARED | IRQF_TRIGGER_RISING
 
+static int azfs_major, azfs_minor;
+
 struct axon_ram_bank {
 	struct of_device	*device;
 	struct gendisk		*disk;
@@ -227,19 +229,14 @@ axon_ram_probe(struct of_device *device, const struct of_device_id *device_id)
 		goto failed;
 	}
 
-	bank->disk->first_minor = 0;
+	bank->disk->major = azfs_major;
+	bank->disk->first_minor = azfs_minor;
 	bank->disk->fops = &axon_ram_devops;
 	bank->disk->private_data = bank;
 	bank->disk->driverfs_dev = &device->dev;
 
 	sprintf(bank->disk->disk_name, "%s%d",
 			AXON_RAM_DEVICE_NAME, axon_ram_bank_id);
-	bank->disk->major = register_blkdev(0, bank->disk->disk_name);
-	if (bank->disk->major < 0) {
-		dev_err(&device->dev, "Cannot register block device\n");
-		rc = -EFAULT;
-		goto failed;
-	}
 
 	bank->disk->queue = blk_alloc_queue(GFP_KERNEL);
 	if (bank->disk->queue == NULL) {
@@ -276,6 +273,8 @@ axon_ram_probe(struct of_device *device, const struct of_device_id *device_id)
 		goto failed;
 	}
 
+	azfs_minor += bank->disk->minors;
+
 	return 0;
 
 failed:
@@ -310,7 +309,6 @@ axon_ram_remove(struct of_device *device)
 
 	device_remove_file(&device->dev, &dev_attr_ecc);
 	free_irq(bank->irq_id, device);
-	unregister_blkdev(bank->disk->major, bank->disk->disk_name);
 	del_gendisk(bank->disk);
 	iounmap((void __iomem *) bank->io_addr);
 	kfree(bank);
@@ -341,6 +339,14 @@ static struct of_platform_driver axon_ram_driver = {
 static int __init
 axon_ram_init(void)
 {
+	azfs_major = register_blkdev(azfs_major, AXON_RAM_DEVICE_NAME);
+	if (azfs_major < 0) {
+		printk(KERN_ERR "%s cannot become block device major number\n",
+				AXON_RAM_MODULE_NAME);
+		return -EFAULT;
+	}
+	azfs_minor = 0;
+
 	return of_register_platform_driver(&axon_ram_driver);
 }
 
@@ -351,6 +357,7 @@ static void __exit
 axon_ram_exit(void)
 {
 	of_unregister_platform_driver(&axon_ram_driver);
+	unregister_blkdev(azfs_major, AXON_RAM_DEVICE_NAME);
 }
 
 module_init(axon_ram_init);
Signature · 3 lines
1.5.4.3

-- 
#5 lore raw

PATCH 3/9 powerpc/axonram: enable partitioning of the Axons DDR2 DIMMs

In reply to [email protected]

From: Maxim Shchetynin <[email protected]>

DDR2 memory DIMMs on the Axon could be accessed only as one partition when using file system drivers which are using the direct_access() method. This patch enables for such file system drivers to access Axon's DDR2 memory even if it is splitted in several partitions.

---
 arch/powerpc/sysdev/axonram.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)
arch/powerpc/sysdev/axonram.c +4 -1
index 9b639ed..9e105cb 100644
@@ -150,7 +150,10 @@ axon_ram_direct_access(struct block_device *device, sector_t sector,
 	struct axon_ram_bank *bank = device->bd_disk->private_data;
 	loff_t offset;
 
-	offset = sector << AXON_RAM_SECTOR_SHIFT;
+	offset = sector;
+	if (device->bd_part != NULL)
+		offset += device->bd_part->start_sect;
+	offset <<= AXON_RAM_SECTOR_SHIFT;
 	if (offset >= bank->size) {
 		dev_err(&bank->device->dev, "Access outside of address space\n");
 		return -ERANGE;
Signature · 3 lines
1.5.4.3

-- 
#6 lore raw

PATCH 4/9 powerpc/cell/cpufreq: add spu aware cpufreq governor

In reply to [email protected]

From: Christian Krafft <[email protected]>

This patch adds a cpufreq governor that takes the number of running spus into account. It's very similar to the ondemand governor, but not as complex. Instead of hacking spu load into the ondemand governor it might be easier to have cpufreq accepting multiple governors per cpu in future. Don't know if this is the right way, but it would keep the governors simple.

---
 arch/powerpc/platforms/cell/Kconfig             |    9 +
 arch/powerpc/platforms/cell/Makefile            |    1 +
 arch/powerpc/platforms/cell/cpufreq_spudemand.c |  184 +++++++++++++++++++++++
 3 files changed, 194 insertions(+), 0 deletions(-)
 create mode 100644 arch/powerpc/platforms/cell/cpufreq_spudemand.c
arch/powerpc/platforms/cell/Kconfig +9 -0
index 3959fcf..6ee571f 100644
@@ -107,6 +107,15 @@ config CBE_CPUFREQ_PMI
 	  processor will not only be able to run at lower speed,
 	  but also at lower core voltage.
 
+config CBE_CPUFREQ_SPU_GOVERNOR
+	tristate "CBE frequency scaling based on SPU usage"
+	depends on SPU_FS && CPU_FREQ
+	default m
+	help
+	  This governor checks for spu usage to adjust the cpu frequency.
+	  If no spu is running on a given cpu, that cpu will be throttled to
+	  the minimal possible frequency.
+
 endmenu
 
 config OPROFILE_CELL
arch/powerpc/platforms/cell/Makefile +1 -0
index c2a7e4e..7fcf09b 100644
@@ -8,6 +8,7 @@ obj-$(CONFIG_CBE_THERM)			+= cbe_thermal.o
 obj-$(CONFIG_CBE_CPUFREQ_PMI)		+= cbe_cpufreq_pmi.o
 obj-$(CONFIG_CBE_CPUFREQ)		+= cbe-cpufreq.o
 cbe-cpufreq-y				+= cbe_cpufreq_pervasive.o cbe_cpufreq.o
+obj-$(CONFIG_CBE_CPUFREQ_SPU_GOVERNOR)	+= cpufreq_spudemand.o
 
 ifeq ($(CONFIG_SMP),y)
 obj-$(CONFIG_PPC_CELL_NATIVE)		+= smp.o
arch/powerpc/platforms/cell/cpufreq_spudemand.c new file +184 -0

184 lines not on this page · raw message

Signature · 3 lines
1.5.4.3

-- 
#7 lore raw

PATCH 5/9 powerpc/cell: cleanup sysreset_hack for IBM cell blades

In reply to [email protected]

From: Christian Krafft <[email protected]>

This patch adds a config option for the sysreset_hack used for IBM Cell blades. The code is moves from pervasive.c into ras.c and gets it's own init method.

---
 arch/powerpc/platforms/cell/Kconfig     |    8 +++++
 arch/powerpc/platforms/cell/pervasive.c |   27 +-----------------
 arch/powerpc/platforms/cell/pervasive.h |    9 ++++++
 arch/powerpc/platforms/cell/ras.c       |   46 +++++++++++++++++++++++++++++++
 4 files changed, 64 insertions(+), 26 deletions(-)
arch/powerpc/platforms/cell/Kconfig +8 -0
index 6ee571f..2d1957b 100644
@@ -83,6 +83,14 @@ config CBE_RAS
 	depends on PPC_CELL_NATIVE
 	default y
 
+config PPC_IBM_CELL_RESETBUTTON
+	bool "IBM Cell Blade Pinhole reset button"
+	depends on CBE_RAS && PPC_IBM_CELL_BLADE
+	default y
+	help
+	  Support Pinhole Resetbutton on IBM Cell blades.
+	  This adds a method to trigger system reset via front panel pinhole button.
+
 config CBE_THERM
 	tristate "CBE thermal support"
 	default m
arch/powerpc/platforms/cell/pervasive.c +1 -26
index 8a3631c..efdacc8 100644
@@ -38,8 +38,6 @@
 
 #include "pervasive.h"
 
-static int sysreset_hack;
-
 static void cbe_power_save(void)
 {
 	unsigned long ctrl, thread_switch_control;
@@ -87,9 +85,6 @@ static void cbe_power_save(void)
 
 static int cbe_system_reset_exception(struct pt_regs *regs)
 {
-	int cpu;
-	struct cbe_pmd_regs __iomem *pmd;
-
 	switch (regs->msr & SRR1_WAKEMASK) {
 	case SRR1_WAKEEE:
 		do_IRQ(regs);
@@ -98,19 +93,7 @@ static int cbe_system_reset_exception(struct pt_regs *regs)
 		timer_interrupt(regs);
 		break;
 	case SRR1_WAKEMT:
-		/*
-		 * The BMC can inject user triggered system reset exceptions,
-		 * but cannot set the system reset reason in srr1,
-		 * so check an extra register here.
-		 */
-		if (sysreset_hack && (cpu = smp_processor_id()) == 0) {
-			pmd = cbe_get_cpu_pmd_regs(cpu);
-			if (in_be64(&pmd->ras_esc_0) & 0xffff) {
-				out_be64(&pmd->ras_esc_0, 0);
-				return 0;
-			}
-		}
-		break;
+		return cbe_sysreset_hack();
 #ifdef CONFIG_CBE_RAS
 	case SRR1_WAKESYSERR:
 		cbe_system_error_exception(regs);
@@ -134,8 +117,6 @@ void __init cbe_pervasive_init(void)
 	if (!cpu_has_feature(CPU_FTR_PAUSE_ZERO))
 		return;
 
-	sysreset_hack = machine_is_compatible("IBM,CBPLUS-1.0");
-
 	for_each_possible_cpu(cpu) {
 		struct cbe_pmd_regs __iomem *regs = cbe_get_cpu_pmd_regs(cpu);
 		if (!regs)
@@ -144,12 +125,6 @@ void __init cbe_pervasive_init(void)
 		 /* Enable Pause(0) control bit */
 		out_be64(&regs->pmcr, in_be64(&regs->pmcr) |
 					    CBE_PMD_PAUSE_ZERO_CONTROL);
-
-		/* Enable JTAG system-reset hack */
-		if (sysreset_hack)
-			out_be32(&regs->fir_mode_reg,
-				in_be32(&regs->fir_mode_reg) |
-				CBE_PMD_FIR_MODE_M8);
 	}
 
 	ppc_md.power_save = cbe_power_save;
arch/powerpc/platforms/cell/pervasive.h +9 -0
index 7b50947..fd4d7b7 100644
@@ -30,4 +30,13 @@ extern void cbe_system_error_exception(struct pt_regs *regs);
 extern void cbe_maintenance_exception(struct pt_regs *regs);
 extern void cbe_thermal_exception(struct pt_regs *regs);
 
+#ifdef CONFIG_PPC_IBM_CELL_RESETBUTTON
+extern int cbe_sysreset_hack(void);
+#else
+static inline int cbe_sysreset_hack(void)
+{
+	return 1;
+}
+#endif /* CONFIG_PPC_IBM_CELL_RESETBUTTON */
+
 #endif
arch/powerpc/platforms/cell/ras.c +46 -0

52 lines not on this page · raw message

Signature · 3 lines
1.5.4.3

-- 
#8 lore raw

PATCH 6/9 powerpc/cell: add support for power button of future IBM cell blades

In reply to [email protected]

From: Christian Krafft <[email protected]>

This patch adds support for the power button on future IBM cell blades. It actually doesn't shut down the machine. Instead it exposes an input device /dev/input/event0 to userspace which sends KEY_POWER if power button has been pressed.
haldaemon actually recognizes the button, so a plattform independent acpid replacement should handle it correctly.

---
 arch/powerpc/platforms/cell/Kconfig           |    8 ++
 arch/powerpc/platforms/cell/Makefile          |    2 +
 arch/powerpc/platforms/cell/cbe_powerbutton.c |  117 +++++++++++++++++++++++++
 include/asm-powerpc/pmi.h                     |    1 +
 4 files changed, 128 insertions(+), 0 deletions(-)
 create mode 100644 arch/powerpc/platforms/cell/cbe_powerbutton.c
arch/powerpc/platforms/cell/Kconfig +8 -0
index 2d1957b..c14d7d8 100644
@@ -91,6 +91,14 @@ config PPC_IBM_CELL_RESETBUTTON
 	  Support Pinhole Resetbutton on IBM Cell blades.
 	  This adds a method to trigger system reset via front panel pinhole button.
 
+config PPC_IBM_CELL_POWERBUTTON
+	tristate "IBM Cell Blade power button"
+	depends on PPC_IBM_CELL_BLADE && PPC_PMI && INPUT_EVDEV
+	default y
+	help
+	  Support Powerbutton on IBM Cell blades.
+	  This will enable the powerbutton as an input device.
+
 config CBE_THERM
 	tristate "CBE thermal support"
 	default m
arch/powerpc/platforms/cell/Makefile +2 -0
index 7fcf09b..7fd8308 100644
@@ -10,6 +10,8 @@ obj-$(CONFIG_CBE_CPUFREQ)		+= cbe-cpufreq.o
 cbe-cpufreq-y				+= cbe_cpufreq_pervasive.o cbe_cpufreq.o
 obj-$(CONFIG_CBE_CPUFREQ_SPU_GOVERNOR)	+= cpufreq_spudemand.o
 
+obj-$(CONFIG_PPC_IBM_CELL_POWERBUTTON)	+= cbe_powerbutton.o
+
 ifeq ($(CONFIG_SMP),y)
 obj-$(CONFIG_PPC_CELL_NATIVE)		+= smp.o
 endif
arch/powerpc/platforms/cell/cbe_powerbutton.c new file +117 -0

117 lines not on this page · raw message

include/asm-powerpc/pmi.h +1 -0
index e1dc090..b4e91fb 100644
@@ -30,6 +30,7 @@
 #ifdef __KERNEL__
 
 #define PMI_TYPE_FREQ_CHANGE	0x01
+#define PMI_TYPE_POWER_BUTTON	0x02
 #define PMI_READ_TYPE		0
 #define PMI_READ_DATA0		1
 #define PMI_READ_DATA1		2
Signature · 3 lines
1.5.4.3

-- 
#9 lore raw

PATCH 7/9 azfs: initial submit of azfs, a non-buffered filesystem

In reply to [email protected]

From: Maxim Shchetynin <[email protected]>

AZFS is a file system which keeps all files on memory mapped random access storage. It was designed to work on the axonram device driver for IBM QS2x blade servers, but can operate on any block device that exports a direct_access method.

---
 Documentation/filesystems/azfs.txt  |   22 +
 arch/powerpc/configs/cell_defconfig |    1 +
 fs/Kconfig                          |   15 +
 fs/Makefile                         |    1 +
 fs/azfs/Makefile                    |    7 +
 fs/azfs/inode.c                     | 1184 +++++++++++++++++++++++++++++++++++
 6 files changed, 1230 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/filesystems/azfs.txt
 create mode 100644 fs/azfs/Makefile
 create mode 100644 fs/azfs/inode.c
Documentation/filesystems/azfs.txt new file +22 -0
new file mode 100644
index 0000000..c4bf659
@@ -0,0 +1,22 @@
+AZFS is a file system which keeps all files on memory backed random
+access storage. It was designed to work on the axonram device driver
+for IBM QS2x blade servers, but can operate on any block device
+that exports a direct_access method.
+
+Everything in AZFS is temporary in the sense that all the data stored
+therein is lost when you switch off or reboot a system. If you unmount
+an AZFS instance, all the data will be kept on device as long your system
+is not shut down or rebooted. You can later mount AZFS on from device again
+to get access to your files.
+
+AZFS uses a block device only for data but not for file information.
+All inodes (file and directory information) is kept in RAM.
+
+When you mount AZFS you are able to specify a file system block size with
+'-o bs=<size in bytes>' option. There are no software limitations for
+a block size but you would not be able to mmap files on AZFS if block size
+is less than a system page size. If no '-o bs' option is specified on mount
+a block size of the used block device is used as a default block size for AZFS.
+
+Other available mount options for AZFS are '-o uid=<id>' and '-o gid=<id>',
+which allow you to set the owner and group of the root of the file system.
arch/powerpc/configs/cell_defconfig +1 -0
index c420e47..235a0c8 100644
@@ -240,6 +240,7 @@ CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
 # CPU Frequency drivers
 #
 CONFIG_AXON_RAM=m
+CONFIG_AZ_FS=m
 # CONFIG_FSL_ULI1575 is not set
 
 #
fs/Kconfig +15 -0
index 2694648..2d4e42b 100644
@@ -1017,6 +1017,21 @@ config HUGETLBFS
 config HUGETLB_PAGE
 	def_bool HUGETLBFS
 
+config AZ_FS
+	tristate "AZFS filesystem support"
+	help
+	  azfs is a file system for I/O attached memory backing. It requires
+	  a block device with direct_access capability, e.g. axonram.
+	  Mounting such device with azfs gives memory mapped access to the
+	  underlying memory to user space.
+
+	  Read <file:Documentation/filesystems/azfs.txt> for details.
+
+	  To compile this file system support as a module, choose M here: the
+	  module will be called azfs.
+
+	  If unsure, say N.
+
 config CONFIGFS_FS
 	tristate "Userspace-driven configuration filesystem"
 	depends on SYSFS
fs/Makefile +1 -0
index 1e7a11b..20e3253 100644
@@ -119,3 +119,4 @@ obj-$(CONFIG_HPPFS)		+= hppfs/
 obj-$(CONFIG_DEBUG_FS)		+= debugfs/
 obj-$(CONFIG_OCFS2_FS)		+= ocfs2/
 obj-$(CONFIG_GFS2_FS)           += gfs2/
+obj-$(CONFIG_AZ_FS)		+= azfs/
fs/azfs/Makefile new file +7 -0
new file mode 100644
index 0000000..ff04d41
@@ -0,0 +1,7 @@
+#
+# Makefile for azfs routines
+#
+
+obj-$(CONFIG_AZ_FS) += azfs.o
+
+azfs-y := inode.o
fs/azfs/inode.c new file +1184 -0

1,184 lines not on this page · raw message

Signature · 3 lines
1.5.4.3

-- 
#10 lore raw

Re: [patch 7/9] azfs: initial submit of azfs, a non-buffered filesystem

Quoted 4 lines · On Tue, 2008-07-15 at 21:51 +0200, [email protected] wrote:

plain text document attachment (0007-azfs-initial-submit-of-azfs-a-non-buffered-filesys.patch)
From: Maxim Shchetynin <[email protected]>

Quoted 8 lines

AZFS is a file system which keeps all files on memory mapped random access storage. It was designed to work on the axonram device driver for IBM QS2x blade servers, but can operate on any block device that exports a direct_access method.

---

Can I get this acked by some filesystem person like Al ?

Cheers,
Ben.

Quoted 1,292 lines
 Documentation/filesystems/azfs.txt  |   22 +
 arch/powerpc/configs/cell_defconfig |    1 +
 fs/Kconfig                          |   15 +
 fs/Makefile                         |    1 +
 fs/azfs/Makefile                    |    7 +
 fs/azfs/inode.c                     | 1184 +++++++++++++++++++++++++++++++++++
 6 files changed, 1230 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/filesystems/azfs.txt
 create mode 100644 fs/azfs/Makefile
 create mode 100644 fs/azfs/inode.c
Documentation/filesystems/azfs.txt new file +22 -0
new file mode 100644
index 0000000..c4bf659
@@ -0,0 +1,22 @@
+AZFS is a file system which keeps all files on memory backed random
+access storage. It was designed to work on the axonram device driver
+for IBM QS2x blade servers, but can operate on any block device
+that exports a direct_access method.
+
+Everything in AZFS is temporary in the sense that all the data stored
+therein is lost when you switch off or reboot a system. If you unmount
+an AZFS instance, all the data will be kept on device as long your system
+is not shut down or rebooted. You can later mount AZFS on from device again
+to get access to your files.
+
+AZFS uses a block device only for data but not for file information.
+All inodes (file and directory information) is kept in RAM.
+
+When you mount AZFS you are able to specify a file system block size with
+'-o bs=<size in bytes>' option. There are no software limitations for
+a block size but you would not be able to mmap files on AZFS if block size
+is less than a system page size. If no '-o bs' option is specified on mount
+a block size of the used block device is used as a default block size for AZFS.
+
+Other available mount options for AZFS are '-o uid=<id>' and '-o gid=<id>',
+which allow you to set the owner and group of the root of the file system.
arch/powerpc/configs/cell_defconfig +1 -0
index c420e47..235a0c8 100644
@@ -240,6 +240,7 @@ CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
 # CPU Frequency drivers
 #
 CONFIG_AXON_RAM=m
+CONFIG_AZ_FS=m
 # CONFIG_FSL_ULI1575 is not set
 
 #
fs/Kconfig +15 -0
index 2694648..2d4e42b 100644
@@ -1017,6 +1017,21 @@ config HUGETLBFS
 config HUGETLB_PAGE
 	def_bool HUGETLBFS
 
+config AZ_FS
+	tristate "AZFS filesystem support"
+	help
+	  azfs is a file system for I/O attached memory backing. It requires
+	  a block device with direct_access capability, e.g. axonram.
+	  Mounting such device with azfs gives memory mapped access to the
+	  underlying memory to user space.
+
+	  Read <file:Documentation/filesystems/azfs.txt> for details.
+
+	  To compile this file system support as a module, choose M here: the
+	  module will be called azfs.
+
+	  If unsure, say N.
+
 config CONFIGFS_FS
 	tristate "Userspace-driven configuration filesystem"
 	depends on SYSFS
fs/Makefile +1 -0
index 1e7a11b..20e3253 100644
@@ -119,3 +119,4 @@ obj-$(CONFIG_HPPFS)		+= hppfs/
 obj-$(CONFIG_DEBUG_FS)		+= debugfs/
 obj-$(CONFIG_OCFS2_FS)		+= ocfs2/
 obj-$(CONFIG_GFS2_FS)           += gfs2/
+obj-$(CONFIG_AZ_FS)		+= azfs/
fs/azfs/Makefile new file +7 -0
new file mode 100644
index 0000000..ff04d41
@@ -0,0 +1,7 @@
+#
+# Makefile for azfs routines
+#
+
+obj-$(CONFIG_AZ_FS) += azfs.o
+
+azfs-y := inode.o
fs/azfs/inode.c new file +1184 -0
new file mode 100644
index 0000000..00dc2af
@@ -0,0 +1,1184 @@
+/*
+ * (C) Copyright IBM Deutschland Entwicklung GmbH 2007
+ *
+ * Author: Maxim Shchetynin <[email protected]>
+ *
+ * Non-buffered filesystem driver.
+ * It registers a filesystem which may be used for all kind of block devices
+ * which have a direct_access() method in block_device_operations.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/backing-dev.h>
+#include <linux/blkdev.h>
+#include <linux/cache.h>
+#include <linux/dcache.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/limits.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <linux/mm.h>
+#include <linux/mm_types.h>
+#include <linux/mutex.h>
+#include <linux/namei.h>
+#include <linux/pagemap.h>
+#include <linux/parser.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+#include <linux/statfs.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/aio.h>
+#include <linux/uio.h>
+#include <asm/bug.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/string.h>
+
+#define AZFS_FILESYSTEM_NAME		"azfs"
+#define AZFS_FILESYSTEM_FLAGS		FS_REQUIRES_DEV
+
+#define AZFS_SUPERBLOCK_MAGIC		0xABBA1972
+#define AZFS_SUPERBLOCK_FLAGS		MS_SYNCHRONOUS | \
+					MS_DIRSYNC | \
+					MS_ACTIVE
+
+#define AZFS_BDI_CAPABILITIES		BDI_CAP_NO_ACCT_DIRTY | \
+					BDI_CAP_NO_WRITEBACK | \
+					BDI_CAP_MAP_COPY | \
+					BDI_CAP_MAP_DIRECT | \
+					BDI_CAP_VMFLAGS
+
+#define AZFS_CACHE_FLAGS		SLAB_HWCACHE_ALIGN | \
+					SLAB_RECLAIM_ACCOUNT | \
+					SLAB_MEM_SPREAD
+
+struct azfs_super {
+	struct list_head		list;
+	unsigned long			media_size;
+	unsigned long			block_size;
+	unsigned short			block_shift;
+	unsigned long			sector_size;
+	unsigned short			sector_shift;
+	uid_t				uid;
+	gid_t				gid;
+	unsigned long			ph_addr;
+	unsigned long			io_addr;
+	struct block_device		*blkdev;
+	struct dentry			*root;
+	struct list_head		block_list;
+	rwlock_t			lock;
+};
+
+struct azfs_super_list {
+	struct list_head		head;
+	spinlock_t			lock;
+};
+
+struct azfs_block {
+	struct list_head		list;
+	unsigned long			id;
+	unsigned long			count;
+};
+
+struct azfs_znode {
+	struct list_head		block_list;
+	rwlock_t			lock;
+	loff_t				size;
+	struct inode			vfs_inode;
+};
+
+static struct azfs_super_list		super_list;
+static struct kmem_cache		*azfs_znode_cache __read_mostly = NULL;
+static struct kmem_cache		*azfs_block_cache __read_mostly = NULL;
+
+#define I2S(inode) \
+	inode->i_sb->s_fs_info
+#define I2Z(inode) \
+	container_of(inode, struct azfs_znode, vfs_inode)
+
+#define for_each_block(block, block_list) \
+	list_for_each_entry(block, block_list, list)
+#define for_each_block_reverse(block, block_list) \
+	list_for_each_entry_reverse(block, block_list, list)
+#define for_each_block_safe(block, temp, block_list) \
+	list_for_each_entry_safe(block, temp, block_list, list)
+#define for_each_block_safe_reverse(block, temp, block_list) \
+	list_for_each_entry_safe_reverse(block, temp, block_list, list)
+
+/**
+ * azfs_block_init - create and initialise a new block in a list
+ * @block_list: destination list
+ * @id: block id
+ * @count: size of a block
+ */
+static inline struct azfs_block*
+azfs_block_init(struct list_head *block_list,
+		unsigned long id, unsigned long count)
+{
+	struct azfs_block *block;
+
+	block = kmem_cache_alloc(azfs_block_cache, GFP_KERNEL);
+	if (!block)
+		return NULL;
+
+	block->id = id;
+	block->count = count;
+
+	INIT_LIST_HEAD(&block->list);
+	list_add_tail(&block->list, block_list);
+
+	return block;
+}
+
+/**
+ * azfs_block_free - remove block from a list and free it back in cache
+ * @block: block to be removed
+ */
+static inline void
+azfs_block_free(struct azfs_block *block)
+{
+	list_del(&block->list);
+	kmem_cache_free(azfs_block_cache, block);
+}
+
+/**
+ * azfs_block_move - move block to another list
+ * @block: block to be moved
+ * @block_list: destination list
+ */
+static inline void
+azfs_block_move(struct azfs_block *block, struct list_head *block_list)
+{
+	list_move_tail(&block->list, block_list);
+}
+
+/**
+ * azfs_block_find - get a block id of a part of a file
+ * @inode: inode
+ * @from: offset for read/write operation
+ * @size: pointer to a value of the amount of data to be read/written
+ */
+static unsigned long
+azfs_block_find(struct inode *inode, unsigned long from, unsigned long *size)
+{
+	struct azfs_super *super;
+	struct azfs_znode *znode;
+	struct azfs_block *block;
+	unsigned long block_id, west, east;
+
+	super = I2S(inode);
+	znode = I2Z(inode);
+
+	read_lock(&znode->lock);
+
+	while (from + *size > znode->size) {
+		read_unlock(&znode->lock);
+		i_size_write(inode, from + *size);
+		inode->i_op->truncate(inode);
+		read_lock(&znode->lock);
+	}
+
+	if (list_empty(&znode->block_list)) {
+		read_unlock(&znode->lock);
+		*size = 0;
+		return 0;
+	}
+
+	block_id = from >> super->block_shift;
+
+	for_each_block(block, &znode->block_list) {
+		if (block->count > block_id)
+			break;
+		block_id -= block->count;
+	}
+
+	west = from % super->block_size;
+	east = ((block->count - block_id) << super->block_shift) - west;
+
+	if (*size > east)
+		*size = east;
+
+	block_id = ((block->id + block_id) << super->block_shift) + west;
+
+	read_unlock(&znode->lock);
+
+	return block_id;
+}
+
+static struct inode*
+azfs_new_inode(struct super_block *, struct inode *, int, dev_t);
+
+/**
+ * azfs_mknod - mknod() method for inode_operations
+ * @dir, @dentry, @mode, @dev: see inode_operations methods
+ */
+static int
+azfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+{
+	struct inode *inode;
+
+	inode = azfs_new_inode(dir->i_sb, dir, mode, dev);
+	if (!inode)
+		return -ENOSPC;
+
+	if (S_ISREG(mode))
+		I2Z(inode)->size = 0;
+
+	dget(dentry);
+	d_instantiate(dentry, inode);
+
+	return 0;
+}
+
+/**
+ * azfs_create - create() method for inode_operations
+ * @dir, @dentry, @mode, @nd: see inode_operations methods
+ */
+static int
+azfs_create(struct inode *dir, struct dentry *dentry, int mode,
+	    struct nameidata *nd)
+{
+	return azfs_mknod(dir, dentry, mode | S_IFREG, 0);
+}
+
+/**
+ * azfs_mkdir - mkdir() method for inode_operations
+ * @dir, @dentry, @mode: see inode_operations methods
+ */
+static int
+azfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	int rc;
+
+	rc = azfs_mknod(dir, dentry, mode | S_IFDIR, 0);
+	if (!rc)
+		inc_nlink(dir);
+
+	return rc;
+}
+
+/**
+ * azfs_symlink - symlink() method for inode_operations
+ * @dir, @dentry, @name: see inode_operations methods
+ */
+static int
+azfs_symlink(struct inode *dir, struct dentry *dentry, const char *name)
+{
+	struct inode *inode;
+	int rc;
+
+	inode = azfs_new_inode(dir->i_sb, dir, S_IFLNK | S_IRWXUGO, 0);
+	if (!inode)
+		return -ENOSPC;
+
+	rc = page_symlink(inode, name, strlen(name) + 1);
+	if (rc) {
+		iput(inode);
+		return rc;
+	}
+
+	dget(dentry);
+	d_instantiate(dentry, inode);
+
+	return 0;
+}
+
+/**
+ * azfs_aio_read - aio_read() method for file_operations
+ * @iocb, @iov, @nr_segs, @pos: see file_operations methods
+ */
+static ssize_t
+azfs_aio_read(struct kiocb *iocb, const struct iovec *iov,
+	      unsigned long nr_segs, loff_t pos)
+{
+	struct azfs_super *super;
+	struct inode *inode;
+	void *target;
+	unsigned long pin;
+	unsigned long size, todo, step;
+	ssize_t rc;
+
+	inode = iocb->ki_filp->f_mapping->host;
+	super = I2S(inode);
+
+	mutex_lock(&inode->i_mutex);
+
+	if (pos >= i_size_read(inode)) {
+		rc = 0;
+		goto out;
+	}
+
+	target = iov->iov_base;
+	todo = min((loff_t) iov->iov_len, i_size_read(inode) - pos);
+
+	for (step = todo; step; step -= size) {
+		size = step;
+		pin = azfs_block_find(inode, pos, &size);
+		if (!size) {
+			rc = -ENOSPC;
+			goto out;
+		}
+		pin += super->io_addr;
+		/*
+		 * FIXME: pin is actually an __iomem pointer, is
+		 * that safe? -arnd
+		 */
+		if (copy_to_user(target, (void*) pin, size)) {
+			rc = -EFAULT;
+			goto out;
+		}
+
+		iocb->ki_pos += size;
+		pos += size;
+		target += size;
+	}
+
+	rc = todo;
+
+out:
+	mutex_unlock(&inode->i_mutex);
+
+	return rc;
+}
+
+/**
+ * azfs_aio_write - aio_write() method for file_operations
+ * @iocb, @iov, @nr_segs, @pos: see file_operations methods
+ */
+static ssize_t
+azfs_aio_write(struct kiocb *iocb, const struct iovec *iov,
+	       unsigned long nr_segs, loff_t pos)
+{
+	struct azfs_super *super;
+	struct inode *inode;
+	void *source;
+	unsigned long pin;
+	unsigned long size, todo, step;
+	ssize_t rc;
+
+	inode = iocb->ki_filp->f_mapping->host;
+	super = I2S(inode);
+
+	source = iov->iov_base;
+	todo = iov->iov_len;
+
+	mutex_lock(&inode->i_mutex);
+
+	for (step = todo; step; step -= size) {
+		size = step;
+		pin = azfs_block_find(inode, pos, &size);
+		if (!size) {
+			rc = -ENOSPC;
+			goto out;
+		}
+		pin += super->io_addr;
+		/*
+		 * FIXME: pin is actually an __iomem pointer, is
+		 * that safe? -arnd
+		 */
+		if (copy_from_user((void*) pin, source, size)) {
+			rc = -EFAULT;
+			goto out;
+		}
+
+		iocb->ki_pos += size;
+		pos += size;
+		source += size;
+	}
+
+	rc = todo;
+
+out:
+	mutex_unlock(&inode->i_mutex);
+
+	return rc;
+}
+
+/**
+ * azfs_open - open() method for file_operations
+ * @inode, @file: see file_operations methods
+ */
+static int
+azfs_open(struct inode *inode, struct file *file)
+{
+	if (file->f_flags & O_TRUNC) {
+		i_size_write(inode, 0);
+		inode->i_op->truncate(inode);
+	}
+	if (file->f_flags & O_APPEND)
+		inode->i_fop->llseek(file, 0, SEEK_END);
+
+	return 0;
+}
+
+/**
+ * azfs_mmap - mmap() method for file_operations
+ * @file, @vm: see file_operations methods
+ */
+static int
+azfs_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct azfs_super *super;
+	struct azfs_znode *znode;
+	struct inode *inode;
+	unsigned long cursor, pin;
+	unsigned long todo, size, vm_start;
+	pgprot_t page_prot;
+
+	inode = file->f_dentry->d_inode;
+	znode = I2Z(inode);
+	super = I2S(inode);
+
+	if (super->block_size < PAGE_SIZE)
+		return -EINVAL;
+
+	cursor = vma->vm_pgoff << super->block_shift;
+	todo = vma->vm_end - vma->vm_start;
+
+	if (cursor + todo > i_size_read(inode))
+		return -EINVAL;
+
+	page_prot = pgprot_val(vma->vm_page_prot);
+#ifdef CONFIG_PPC
+	page_prot |= (_PAGE_NO_CACHE | _PAGE_RW);
+	page_prot &= ~_PAGE_GUARDED;
+#else
+	page_prot = pgprot_noncached(page_prot);
+#endif
+	vma->vm_page_prot = __pgprot(page_prot);
+
+	vm_start = vma->vm_start;
+	for (size = todo; todo; todo -= size, size = todo) {
+		pin = azfs_block_find(inode, cursor, &size);
+		if (!size)
+			return -EAGAIN;
+		pin += super->ph_addr;
+		pin >>= PAGE_SHIFT;
+		if (remap_pfn_range(vma, vm_start, pin, size, vma->vm_page_prot))
+			return -EAGAIN;
+
+		vm_start += size;
+		cursor += size;
+	}
+
+	return 0;
+}
+
+/**
+ * azfs_truncate - truncate() method for inode_operations
+ * @inode: see inode_operations methods
+ */
+static void
+azfs_truncate(struct inode *inode)
+{
+	struct azfs_super *super;
+	struct azfs_znode *znode;
+	struct azfs_block *block, *tmp_block, *temp, *west, *east;
+	unsigned long id, count;
+	signed long delta;
+
+	super = I2S(inode);
+	znode = I2Z(inode);
+
+	delta = i_size_read(inode) + (super->block_size - 1);
+	delta >>= super->block_shift;
+	delta -= inode->i_blocks;
+
+	if (delta == 0) {
+		znode->size = i_size_read(inode);
+		return;
+	}
+
+	write_lock(&znode->lock);
+
+	while (delta > 0) {
+		west = east = NULL;
+
+		write_lock(&super->lock);
+
+		if (list_empty(&super->block_list)) {
+			write_unlock(&super->lock);
+			break;
+		}
+
+		for (count = delta; count; count--) {
+			for_each_block(block, &super->block_list)
+				if (block->count >= count) {
+					east = block;
+					break;
+				}
+			if (east)
+				break;
+		}
+
+		for_each_block_reverse(block, &znode->block_list) {
+			if (block->id + block->count == east->id)
+				west = block;
+			break;
+		}
+
+		if (east->count == count) {
+			if (west) {
+				west->count += east->count;
+				azfs_block_free(east);
+			} else {
+				azfs_block_move(east, &znode->block_list);
+			}
+		} else {
+			if (west) {
+				west->count += count;
+			} else {
+				if (!azfs_block_init(&znode->block_list,
+						east->id, count)) {
+					write_unlock(&super->lock);
+					break;
+				}
+			}
+
+			east->id += count;
+			east->count -= count;
+		}
+
+		write_unlock(&super->lock);
+
+		inode->i_blocks += count;
+
+		delta -= count;
+	}
+
+	while (delta < 0) {
+		for_each_block_safe_reverse(block, tmp_block, &znode->block_list) {
+			id = block->id;
+			count = block->count;
+			if ((signed long) count + delta > 0) {
+				block->count += delta;
+				id += block->count;
+				count -= block->count;
+				block = NULL;
+			}
+
+			west = east = NULL;
+
+			write_lock(&super->lock);
+
+			for_each_block(temp, &super->block_list) {
+				if (!west && (temp->id + temp->count == id))
+					west = temp;
+				else if (!east && (id + count == temp->id))
+					east = temp;
+				if (west && east)
+					break;
+			}
+
+			if (west && east) {
+				west->count += count + east->count;
+				azfs_block_free(east);
+				if (block)
+					azfs_block_free(block);
+			} else if (west) {
+				west->count += count;
+				if (block)
+					azfs_block_free(block);
+			} else if (east) {
+				east->id -= count;
+				east->count += count;
+				if (block)
+					azfs_block_free(block);
+			} else {
+				if (!block) {
+					if (!azfs_block_init(&super->block_list,
+							id, count)) {
+						write_unlock(&super->lock);
+						break;
+					}
+				} else {
+					azfs_block_move(block, &super->block_list);
+				}
+			}
+
+			write_unlock(&super->lock);
+
+			inode->i_blocks -= count;
+
+			delta += count;
+
+			break;
+		}
+	}
+
+	write_unlock(&znode->lock);
+
+	znode->size = min(i_size_read(inode),
+			(loff_t) inode->i_blocks << super->block_shift);
+}
+
+/**
+ * azfs_getattr - getattr() method for inode_operations
+ * @mnt, @dentry, @stat: see inode_operations methods
+ */
+static int
+azfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
+{
+	struct azfs_super *super;
+	struct inode *inode;
+	unsigned short shift;
+
+	inode = dentry->d_inode;
+	super = I2S(inode);
+
+	generic_fillattr(inode, stat);
+	stat->blocks = inode->i_blocks;
+	shift = super->block_shift - super->sector_shift;
+	if (shift)
+		stat->blocks <<= shift;
+
+	return 0;
+}
+
+static const struct address_space_operations azfs_aops = {
+	.write_begin	= simple_write_begin,
+	.write_end	= simple_write_end
+};
+
+static struct backing_dev_info azfs_bdi = {
+	.ra_pages	= 0,
+	.capabilities	= AZFS_BDI_CAPABILITIES
+};
+
+static struct inode_operations azfs_dir_iops = {
+	.create		= azfs_create,
+	.lookup		= simple_lookup,
+	.link		= simple_link,
+	.unlink		= simple_unlink,
+	.symlink	= azfs_symlink,
+	.mkdir		= azfs_mkdir,
+	.rmdir		= simple_rmdir,
+	.mknod		= azfs_mknod,
+	.rename		= simple_rename
+};
+
+static const struct file_operations azfs_reg_fops = {
+	.llseek		= generic_file_llseek,
+	.aio_read	= azfs_aio_read,
+	.aio_write	= azfs_aio_write,
+	.open		= azfs_open,
+	.mmap		= azfs_mmap,
+	.fsync		= simple_sync_file,
+};
+
+static struct inode_operations azfs_reg_iops = {
+	.truncate	= azfs_truncate,
+	.getattr	= azfs_getattr
+};
+
+/**
+ * azfs_new_inode - cook a new inode
+ * @sb: super-block
+ * @dir: parent directory
+ * @mode: file mode
+ * @dev: to be forwarded to init_special_inode()
+ */
+static struct inode*
+azfs_new_inode(struct super_block *sb, struct inode *dir, int mode, dev_t dev)
+{
+	struct azfs_super *super;
+	struct inode *inode;
+
+	inode = new_inode(sb);
+	if (!inode)
+		return NULL;
+
+	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+
+	inode->i_mode = mode;
+	if (dir) {
+		dir->i_mtime = dir->i_ctime = inode->i_mtime;
+		inode->i_uid = current->fsuid;
+		if (dir->i_mode & S_ISGID) {
+			if (S_ISDIR(mode))
+				inode->i_mode |= S_ISGID;
+			inode->i_gid = dir->i_gid;
+		} else {
+			inode->i_gid = current->fsgid;
+		}
+	} else {
+		super = sb->s_fs_info;
+		inode->i_uid = super->uid;
+		inode->i_gid = super->gid;
+	}
+
+	inode->i_blocks = 0;
+	inode->i_mapping->a_ops = &azfs_aops;
+	inode->i_mapping->backing_dev_info = &azfs_bdi;
+
+	switch (mode & S_IFMT) {
+	case S_IFDIR:
+		inode->i_op = &azfs_dir_iops;
+		inode->i_fop = &simple_dir_operations;
+		inc_nlink(inode);
+		break;
+
+	case S_IFREG:
+		inode->i_op = &azfs_reg_iops;
+		inode->i_fop = &azfs_reg_fops;
+		break;
+
+	case S_IFLNK:
+		inode->i_op = &page_symlink_inode_operations;
+		break;
+
+	default:
+		init_special_inode(inode, mode, dev);
+		break;
+	}
+
+	return inode;
+}
+
+/**
+ * azfs_alloc_inode - alloc_inode() method for super_operations
+ * @sb: see super_operations methods
+ */
+static struct inode*
+azfs_alloc_inode(struct super_block *sb)
+{
+	struct azfs_znode *znode;
+
+	znode = kmem_cache_alloc(azfs_znode_cache, GFP_KERNEL);
+	if (znode) {
+		INIT_LIST_HEAD(&znode->block_list);
+		rwlock_init(&znode->lock);
+
+		inode_init_once(&znode->vfs_inode);
+
+		return &znode->vfs_inode;
+	}
+
+	return NULL;
+}
+
+/**
+ * azfs_destroy_inode - destroy_inode() method for super_operations
+ * @inode: see super_operations methods
+ */
+static void
+azfs_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(azfs_znode_cache, I2Z(inode));
+}
+
+/**
+ * azfs_delete_inode - delete_inode() method for super_operations
+ * @inode: see super_operations methods
+ */
+static void
+azfs_delete_inode(struct inode *inode)
+{
+	if (S_ISREG(inode->i_mode)) {
+		i_size_write(inode, 0);
+		azfs_truncate(inode);
+	}
+	truncate_inode_pages(&inode->i_data, 0);
+	clear_inode(inode);
+}
+
+/**
+ * azfs_statfs - statfs() method for super_operations
+ * @dentry, @stat: see super_operations methods
+ */
+static int
+azfs_statfs(struct dentry *dentry, struct kstatfs *stat)
+{
+	struct super_block *sb;
+	struct azfs_super *super;
+	struct inode *inode;
+	unsigned long inodes, blocks;
+
+	sb = dentry->d_sb;
+	super = sb->s_fs_info;
+
+	inodes = blocks = 0;
+	mutex_lock(&sb->s_lock);
+	list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
+		inodes++;
+		blocks += inode->i_blocks;
+	}
+	mutex_unlock(&sb->s_lock);
+
+	stat->f_type = AZFS_SUPERBLOCK_MAGIC;
+	stat->f_bsize = super->block_size;
+	stat->f_blocks = super->media_size >> super->block_shift;
+	stat->f_bfree = stat->f_blocks - blocks;
+	stat->f_bavail = stat->f_blocks - blocks;
+	stat->f_files = inodes + blocks;
+	stat->f_ffree = blocks + 1;
+	stat->f_namelen = NAME_MAX;
+
+	return 0;
+}
+
+static struct super_operations azfs_ops = {
+	.alloc_inode	= azfs_alloc_inode,
+	.destroy_inode	= azfs_destroy_inode,
+	.drop_inode	= generic_delete_inode,
+	.delete_inode	= azfs_delete_inode,
+	.statfs		= azfs_statfs
+};
+
+enum {
+	Opt_blocksize_short,
+	Opt_blocksize_long,
+	Opt_uid,
+	Opt_gid,
+	Opt_err
+};
+
+static match_table_t tokens = {
+	{Opt_blocksize_short, "bs=%u"},
+	{Opt_blocksize_long, "blocksize=%u"},
+	{Opt_uid, "uid=%u"},
+	{Opt_gid, "gid=%u"},
+	{Opt_err, NULL}
+};
+
+/**
+ * azfs_parse_mount_parameters - parse options given to mount with -o
+ * @super: azfs super block extension
+ * @options: comma separated options
+ */
+static int
+azfs_parse_mount_parameters(struct azfs_super *super, char *options)
+{
+	char *option;
+	int token, value;
+	substring_t args[MAX_OPT_ARGS];
+
+	if (!options)
+		return 1;
+
+	while ((option = strsep(&options, ",")) != NULL) {
+		if (!*option)
+			continue;
+
+		token = match_token(option, tokens, args);
+		switch (token) {
+		case Opt_blocksize_short:
+		case Opt_blocksize_long:
+			if (match_int(&args[0], &value))
+				goto syntax_error;
+			super->block_size = value;
+			break;
+
+		case Opt_uid:
+			if (match_int(&args[0], &value))
+				goto syntax_error;
+			super->uid = value;
+			break;
+
+		case Opt_gid:
+			if (match_int(&args[0], &value))
+				goto syntax_error;
+			super->gid = value;
+			break;
+
+		default:
+			goto syntax_error;
+		}
+	}
+
+	return 1;
+
+syntax_error:
+	printk(KERN_ERR "%s: invalid mount option\n",
+			AZFS_FILESYSTEM_NAME);
+
+	return 0;
+}
+
+/**
+ * azfs_fill_super - fill_super routine for get_sb
+ * @sb, @data, @silent: see file_system_type methods
+ */
+static int
+azfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+	struct gendisk *disk;
+	struct azfs_super *super = NULL, *tmp_super;
+	struct azfs_block *block = NULL;
+	struct inode *inode = NULL;
+	void *kaddr;
+	unsigned long pfn;
+	int rc;
+
+	BUG_ON(!sb->s_bdev);
+
+	disk = sb->s_bdev->bd_disk;
+
+	BUG_ON(!disk || !disk->queue);
+
+	if (!disk->fops->direct_access) {
+		printk(KERN_ERR "%s needs a block device with a "
+				"direct_access() method\n",
+				AZFS_FILESYSTEM_NAME);
+		return -ENOSYS;
+	}
+
+	get_device(disk->driverfs_dev);
+
+	sb->s_magic = AZFS_SUPERBLOCK_MAGIC;
+	sb->s_flags = AZFS_SUPERBLOCK_FLAGS;
+	sb->s_op = &azfs_ops;
+	sb->s_maxbytes = get_capacity(disk) * disk->queue->hardsect_size;
+	sb->s_time_gran = 1;
+
+	spin_lock(&super_list.lock);
+	list_for_each_entry(tmp_super, &super_list.head, list)
+		if (tmp_super->blkdev == sb->s_bdev) {
+			super = tmp_super;
+			break;
+		}
+	spin_unlock(&super_list.lock);
+
+	if (super) {
+		if (data && strlen((char*) data))
+			printk(KERN_WARNING "/dev/%s was already mounted with "
+					"%s before, it will be mounted with "
+					"mount options used last time, "
+					"options just given would be ignored\n",
+					disk->disk_name, AZFS_FILESYSTEM_NAME);
+		sb->s_fs_info = super;
+	} else {
+		super = kzalloc(sizeof(struct azfs_super), GFP_KERNEL);
+		if (!super) {
+			rc = -ENOMEM;
+			goto failed;
+		}
+		sb->s_fs_info = super;
+
+		if (!azfs_parse_mount_parameters(super, (char*) data)) {
+			rc = -EINVAL;
+			goto failed;
+		}
+
+		inode = azfs_new_inode(sb, NULL, S_IFDIR | S_IRWXUGO, 0);
+		if (!inode) {
+			rc = -ENOMEM;
+			goto failed;
+		}
+
+		super->root = d_alloc_root(inode);
+		if (!super->root) {
+			rc = -ENOMEM;
+			goto failed;
+		}
+		dget(super->root);
+
+		INIT_LIST_HEAD(&super->list);
+		INIT_LIST_HEAD(&super->block_list);
+		rwlock_init(&super->lock);
+
+		super->media_size = sb->s_maxbytes;
+
+		if (!super->block_size)
+			super->block_size = sb->s_blocksize;
+		super->block_shift = blksize_bits(super->block_size);
+
+		super->sector_size = disk->queue->hardsect_size;
+		super->sector_shift = blksize_bits(super->sector_size);
+
+		super->blkdev = sb->s_bdev;
+
+		block = azfs_block_init(&super->block_list,
+				0, super->media_size >> super->block_shift);
+		if (!block) {
+			rc = -ENOMEM;
+			goto failed;
+		}
+
+		rc = disk->fops->direct_access(super->blkdev, 0, &kaddr, &pfn);
+		if (rc < 0) {
+			rc = -EFAULT;
+			goto failed;
+		}
+		super->ph_addr = (unsigned long) kaddr;
+
+		super->io_addr = (unsigned long) ioremap_flags(
+				super->ph_addr, super->media_size, _PAGE_NO_CACHE);
+		if (!super->io_addr) {
+			rc = -EFAULT;
+			goto failed;
+		}
+
+		spin_lock(&super_list.lock);
+		list_add(&super->list, &super_list.head);
+		spin_unlock(&super_list.lock);
+	}
+
+	sb->s_root = super->root;
+	disk->driverfs_dev->driver_data = super;
+	disk->driverfs_dev->platform_data = sb;
+
+	if (super->block_size < PAGE_SIZE)
+		printk(KERN_INFO "Block size on %s is smaller then system "
+				"page size: mmap() would not be supported\n",
+				disk->disk_name);
+
+	return 0;
+
+failed:
+	if (super) {
+		sb->s_root = NULL;
+		sb->s_fs_info = NULL;
+		if (block)
+			azfs_block_free(block);
+		if (super->root)
+			dput(super->root);
+		if (inode)
+			iput(inode);
+		disk->driverfs_dev->driver_data = NULL;
+		kfree(super);
+		disk->driverfs_dev->platform_data = NULL;
+		put_device(disk->driverfs_dev);
+	}
+
+	return rc;
+}
+
+/**
+ * azfs_get_sb - get_sb() method for file_system_type
+ * @fs_type, @flags, @dev_name, @data, @mount: see file_system_type methods
+ */
+static int
+azfs_get_sb(struct file_system_type *fs_type, int flags,
+	    const char *dev_name, void *data, struct vfsmount *mount)
+{
+	return get_sb_bdev(fs_type, flags,
+			dev_name, data, azfs_fill_super, mount);
+}
+
+/**
+ * azfs_kill_sb - kill_sb() method for file_system_type
+ * @sb: see file_system_type methods
+ */
+static void
+azfs_kill_sb(struct super_block *sb)
+{
+	sb->s_root = NULL;
+	kill_block_super(sb);
+}
+
+static struct file_system_type azfs_fs = {
+	.owner		= THIS_MODULE,
+	.name		= AZFS_FILESYSTEM_NAME,
+	.get_sb		= azfs_get_sb,
+	.kill_sb	= azfs_kill_sb,
+	.fs_flags	= AZFS_FILESYSTEM_FLAGS
+};
+
+/**
+ * azfs_init
+ */
+static int __init
+azfs_init(void)
+{
+	int rc;
+
+	INIT_LIST_HEAD(&super_list.head);
+	spin_lock_init(&super_list.lock);
+
+	azfs_znode_cache = kmem_cache_create("azfs_znode_cache",
+			sizeof(struct azfs_znode), 0, AZFS_CACHE_FLAGS, NULL);
+	if (!azfs_znode_cache) {
+		printk(KERN_ERR "Could not allocate inode cache for %s\n",
+				AZFS_FILESYSTEM_NAME);
+		rc = -ENOMEM;
+		goto failed;
+	}
+
+	azfs_block_cache = kmem_cache_create("azfs_block_cache",
+			sizeof(struct azfs_block), 0, AZFS_CACHE_FLAGS, NULL);
+	if (!azfs_block_cache) {
+		printk(KERN_ERR "Could not allocate block cache for %s\n",
+				AZFS_FILESYSTEM_NAME);
+		rc = -ENOMEM;
+		goto failed;
+	}
+
+	rc = register_filesystem(&azfs_fs);
+	if (rc != 0) {
+		printk(KERN_ERR "Could not register %s\n",
+				AZFS_FILESYSTEM_NAME);
+		goto failed;
+	}
+
+	return 0;
+
+failed:
+	if (azfs_block_cache)
+		kmem_cache_destroy(azfs_block_cache);
+
+	if (azfs_znode_cache)
+		kmem_cache_destroy(azfs_znode_cache);
+
+	return rc;
+}
+
+/**
+ * azfs_exit
+ */
+static void __exit
+azfs_exit(void)
+{
+	struct azfs_super *super, *tmp_super;
+	struct azfs_block *block, *tmp_block;
+	struct gendisk *disk;
+
+	spin_lock(&super_list.lock);
+	list_for_each_entry_safe(super, tmp_super, &super_list.head, list) {
+		disk = super->blkdev->bd_disk;
+		list_del(&super->list);
+		iounmap((void*) super->io_addr);
+		write_lock(&super->lock);
+		for_each_block_safe(block, tmp_block, &super->block_list)
+			azfs_block_free(block);
+		write_unlock(&super->lock);
+		disk->driverfs_dev->driver_data = NULL;
+		disk->driverfs_dev->platform_data = NULL;
+		kfree(super);
+		put_device(disk->driverfs_dev);
+	}
+	spin_unlock(&super_list.lock);
+
+	unregister_filesystem(&azfs_fs);
+
+	kmem_cache_destroy(azfs_block_cache);
+	kmem_cache_destroy(azfs_znode_cache);
+}
+
+module_init(azfs_init);
+module_exit(azfs_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Maxim Shchetynin <[email protected]>");
+MODULE_DESCRIPTION("Non-buffered file system for IO devices");
Signature · 1 line
1.5.4.3
#11 lore raw

Re: [Cbe-oss-dev] [patch 7/9] azfs: initial submit of azfs, a non-buffered filesystem

In reply to [email protected]
Quoted 6 lines · On Tue, Jul 15, 2008 at 09:51:46PM +0200, [email protected] wrote:

From: Maxim Shchetynin <[email protected]>

AZFS is a file system which keeps all files on memory mapped random access storage. It was designed to work on the axonram device driver for IBM QS2x blade servers, but can operate on any block device that exports a direct_access method.

I don't thinks it's quite ready yet. I've had another look through the code and here's some issues I came up with:

- first thing is that it's not sparse clean, which is a bit of a red flag. The __iomem and __user annotations are there for a reason.
- then even with annotations we still have the issue of copy_{from,to}_user into mmio regions. According to benh that's still an open issue, but it at least needs very good explanations in comments.
- the aio_read and aio_write methods don't handle actually vectored writes. They need to iterate over all iovecs. Or just implement plain read/write given that it's not actually asynchronous.
- the persistent superblock hack needs to go away. Just clean up everything in ->put_super. If we want a fully persistant fs we should just be using ext2 + xip
- azfs_open looks very fishy. there's never a need to do seeks inside open. if O_APPEND is set the VFS makes sure the read and write methods get the right ppos pointer passed.
And truncation is done by the VFS for O_TRUNC opens through ->setattr
- azfs_znode should not have a size field of it's own, but the filesystem should only use the inode one
- the lists and inode_init_once should be called from the slab constructor as in other filesystems
- I don't think there is any point of having a slab cache for the azfs_block structures
- disk->driverfs_dev is not writeable to the filesystem, but for driver use. The information azfs stores in there is not used anyway, so it could easily be removed.
- lots of duplicated field in azfs_super where the superblock ones should be used:

	media_size	-> sb->s_maxbytes
	sector_size	-> not needed at all
	blkdev		-> sb->s_bdev
	root		-> sb->s_root
#12 lore raw

PATCH 8/9 powerpc/dma: use the struct dma_attrs in iommu code

In reply to [email protected]

From: Mark Nelson <[email protected]>

Update iommu_alloc() to take the struct dma_attrs and pass them on to tce_build(). This change propagates down to the tce_build functions of all the platforms.

---
 arch/powerpc/kernel/iommu.c            |   13 ++++++++-----
 arch/powerpc/platforms/cell/iommu.c    |    5 +++--
 arch/powerpc/platforms/iseries/iommu.c |    3 ++-
 arch/powerpc/platforms/pasemi/iommu.c  |    3 ++-
 arch/powerpc/platforms/pseries/iommu.c |   14 +++++++++-----
 arch/powerpc/sysdev/dart_iommu.c       |    3 ++-
 include/asm-powerpc/machdep.h          |    3 ++-
 7 files changed, 28 insertions(+), 16 deletions(-)
arch/powerpc/kernel/iommu.c +8 -5
index 8c68ee9..2385f68 100644
@@ -186,7 +186,8 @@ static unsigned long iommu_range_alloc(struct device *dev,
 static dma_addr_t iommu_alloc(struct device *dev, struct iommu_table *tbl,
 			      void *page, unsigned int npages,
 			      enum dma_data_direction direction,
-			      unsigned long mask, unsigned int align_order)
+			      unsigned long mask, unsigned int align_order,
+			      struct dma_attrs *attrs)
 {
 	unsigned long entry, flags;
 	dma_addr_t ret = DMA_ERROR_CODE;
@@ -205,7 +206,7 @@ static dma_addr_t iommu_alloc(struct device *dev, struct iommu_table *tbl,
 
 	/* Put the TCEs in the HW table */
 	ppc_md.tce_build(tbl, entry, npages, (unsigned long)page & IOMMU_PAGE_MASK,
-			 direction);
+			 direction, attrs);
 
 
 	/* Flush/invalidate TLB caches if necessary */
@@ -336,7 +337,8 @@ int iommu_map_sg(struct device *dev, struct iommu_table *tbl,
 			    npages, entry, dma_addr);
 
 		/* Insert into HW table */
-		ppc_md.tce_build(tbl, entry, npages, vaddr & IOMMU_PAGE_MASK, direction);
+		ppc_md.tce_build(tbl, entry, npages, vaddr & IOMMU_PAGE_MASK,
+				 direction, attrs);
 
 		/* If we are in an open segment, try merging */
 		if (segstart != s) {
@@ -573,7 +575,8 @@ dma_addr_t iommu_map_single(struct device *dev, struct iommu_table *tbl,
 			align = PAGE_SHIFT - IOMMU_PAGE_SHIFT;
 
 		dma_handle = iommu_alloc(dev, tbl, vaddr, npages, direction,
-					 mask >> IOMMU_PAGE_SHIFT, align);
+					 mask >> IOMMU_PAGE_SHIFT, align,
+					 attrs);
 		if (dma_handle == DMA_ERROR_CODE) {
 			if (printk_ratelimit())  {
 				printk(KERN_INFO "iommu_alloc failed, "
@@ -642,7 +645,7 @@ void *iommu_alloc_coherent(struct device *dev, struct iommu_table *tbl,
 	nio_pages = size >> IOMMU_PAGE_SHIFT;
 	io_order = get_iommu_order(size);
 	mapping = iommu_alloc(dev, tbl, ret, nio_pages, DMA_BIDIRECTIONAL,
-			      mask >> IOMMU_PAGE_SHIFT, io_order);
+			      mask >> IOMMU_PAGE_SHIFT, io_order, NULL);
 	if (mapping == DMA_ERROR_CODE) {
 		free_pages((unsigned long)ret, order);
 		return NULL;
arch/powerpc/platforms/cell/iommu.c +3 -2
index eeacb3a..3b70784 100644
@@ -173,7 +173,8 @@ static void invalidate_tce_cache(struct cbe_iommu *iommu, unsigned long *pte,
 }
 
 static void tce_build_cell(struct iommu_table *tbl, long index, long npages,
-		unsigned long uaddr, enum dma_data_direction direction)
+		unsigned long uaddr, enum dma_data_direction direction,
+		struct dma_attrs *attrs)
 {
 	int i;
 	unsigned long *io_pte, base_pte;
@@ -519,7 +520,7 @@ cell_iommu_setup_window(struct cbe_iommu *iommu, struct device_node *np,
 
 	__set_bit(0, window->table.it_map);
 	tce_build_cell(&window->table, window->table.it_offset, 1,
-		       (unsigned long)iommu->pad_page, DMA_TO_DEVICE);
+		       (unsigned long)iommu->pad_page, DMA_TO_DEVICE, NULL);
 	window->table.it_hint = window->table.it_blocksize;
 
 	return window;
arch/powerpc/platforms/iseries/iommu.c +2 -1
index ab5d868..bc818e4 100644
@@ -42,7 +42,8 @@
 #include <asm/iseries/iommu.h>
 
 static void tce_build_iSeries(struct iommu_table *tbl, long index, long npages,
-		unsigned long uaddr, enum dma_data_direction direction)
+		unsigned long uaddr, enum dma_data_direction direction,
+		struct dma_attrs *attrs)
 {
 	u64 rc;
 	u64 tce, rpn;
arch/powerpc/platforms/pasemi/iommu.c +2 -1
index 86967bd..70541b7 100644
@@ -85,7 +85,8 @@ static int iommu_table_iobmap_inited;
 
 static void iobmap_build(struct iommu_table *tbl, long index,
 			 long npages, unsigned long uaddr,
-			 enum dma_data_direction direction)
+			 enum dma_data_direction direction,
+			 struct dma_attrs *attrs)
 {
 	u32 *ip;
 	u32 rpn;
arch/powerpc/platforms/pseries/iommu.c +9 -5
index 9a12908..5377dd4 100644
@@ -50,7 +50,8 @@
 
 static void tce_build_pSeries(struct iommu_table *tbl, long index,
 			      long npages, unsigned long uaddr,
-			      enum dma_data_direction direction)
+			      enum dma_data_direction direction,
+			      struct dma_attrs *attrs)
 {
 	u64 proto_tce;
 	u64 *tcep;
@@ -95,7 +96,8 @@ static unsigned long tce_get_pseries(struct iommu_table *tbl, long index)
 
 static void tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum,
 				long npages, unsigned long uaddr,
-				enum dma_data_direction direction)
+				enum dma_data_direction direction,
+				struct dma_attrs *attrs)
 {
 	u64 rc;
 	u64 proto_tce, tce;
@@ -127,7 +129,8 @@ static DEFINE_PER_CPU(u64 *, tce_page) = NULL;
 
 static void tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
 				     long npages, unsigned long uaddr,
-				     enum dma_data_direction direction)
+				     enum dma_data_direction direction,
+				     struct dma_attrs *attrs)
 {
 	u64 rc;
 	u64 proto_tce;
@@ -136,7 +139,8 @@ static void tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
 	long l, limit;
 
 	if (npages == 1) {
-		tce_build_pSeriesLP(tbl, tcenum, npages, uaddr, direction);
+		tce_build_pSeriesLP(tbl, tcenum, npages, uaddr,
+				    direction, attrs);
 		return;
 	}
 
@@ -150,7 +154,7 @@ static void tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
 		/* If allocation fails, fall back to the loop implementation */
 		if (!tcep) {
 			tce_build_pSeriesLP(tbl, tcenum, npages, uaddr,
-					    direction);
+					    direction, attrs);
 			return;
 		}
 		__get_cpu_var(tce_page) = tcep;
arch/powerpc/sysdev/dart_iommu.c +2 -1
index 005c2ec..de8c8b5 100644
@@ -149,7 +149,8 @@ static void dart_flush(struct iommu_table *tbl)
 
 static void dart_build(struct iommu_table *tbl, long index,
 		       long npages, unsigned long uaddr,
-		       enum dma_data_direction direction)
+		       enum dma_data_direction direction,
+		       struct dma_attrs *attrs)
 {
 	unsigned int *dp;
 	unsigned int rpn;
include/asm-powerpc/machdep.h +2 -1
index 9899226..1233d73 100644
@@ -80,7 +80,8 @@ struct machdep_calls {
 				     long index,
 				     long npages,
 				     unsigned long uaddr,
-				     enum dma_data_direction direction);
+				     enum dma_data_direction direction,
+				     struct dma_attrs *attrs);
 	void		(*tce_free)(struct iommu_table *tbl,
 				    long index,
 				    long npages);
Signature · 3 lines
1.5.4.3

-- 
#13 lore raw

PATCH 9/9 powerpc/cell: Add DMA_ATTR_STRONG_ORDERING dma attribute and use in IOMMU code

In reply to [email protected]

From: Mark Nelson <[email protected]>

Introduce a new dma attriblue DMA_ATTR_STRONG_ORDERING to use strong ordering on DMA mappings in the Cell processor. Add the code to the Cell's IOMMU implementation to use this.

The current Cell IOMMU implementation sets the IOPTE_SO_RW bits in all IOTPEs (for both the dynamic and fixed mappings) which enforces strong ordering of both reads and writes. This patch makes the default behaviour weak ordering (the IOPTE_SO_RW bits not set) and to request a strongly ordered mapping the new DMA_ATTR_STRONG_ORDERING needs to be used.

Dynamic mappings can be weakly or strongly ordered on an individual basis but the fixed mapping is always weakly ordered.

---
 Documentation/DMA-attributes.txt    |   12 +++++
 arch/powerpc/platforms/cell/iommu.c |   93 ++++++++++++++++++++++++++++++++---
 include/linux/dma-attrs.h           |    1 +
 3 files changed, 99 insertions(+), 7 deletions(-)
Documentation/DMA-attributes.txt +12 -0
index 6d772f8..f2d2800 100644
@@ -22,3 +22,15 @@ ready and available in memory.  The DMA of the "completion indication"
 could race with data DMA.  Mapping the memory used for completion
 indications with DMA_ATTR_WRITE_BARRIER would prevent the race.
 
+
+DMA_ATTR_STRONG_ORDERING
+----------------------
+
+DMA_ATTR_STRONG_ORDERING specifies that previous reads and writes are
+performed in the order in which they're received by the IOMMU; thus
+reads and writes may not pass each other.
+
+Platforms that are strongly ordered by default will ignore this new
+attribute but platforms that are weakly ordered by default should not
+ignore this new attribute. Instead, they should return an error if a
+strongly ordered mapping cannot be used when one is requested.
arch/powerpc/platforms/cell/iommu.c +86 -7

128 lines not on this page · raw message

include/linux/dma-attrs.h +1 -0
index 1677e2b..600fea7 100644
@@ -12,6 +12,7 @@
  */
 enum dma_attr {
 	DMA_ATTR_WRITE_BARRIER,
+	DMA_ATTR_STRONG_ORDERING,
 	DMA_ATTR_MAX,
 };
 
Signature · 3 lines
1.5.4.3

-- 
#14 lore raw

Re: [patch 9/9] powerpc/cell: Add DMA_ATTR_STRONG_ORDERING dma attribute and use in IOMMU code

Sorry for the late comments, I missed this when it went by before.

Documentation/DMA-attributes.txt ready and available in memory. The DMA of the "completion indication" · line 31

Quoted 6 lines
Documentation/DMA-attributes.txt +6 -0
+DMA_ATTR_STRONG_ORDERING
+----------------------
+
+DMA_ATTR_STRONG_ORDERING specifies that previous reads and writes are
+performed in the order in which they're received by the IOMMU; thus
+reads and writes may not pass each other.

I don't understand what this is trying to say. What is "previous" referring to? What does "received by the IOMMU" mean -- do you mean issued onto the bus by the CPU? When you say "reads and writes may not pass each other," do you mean just that reads may not pass writes and writes may not pass reads, or do you mean that reads also can't pass reads and writes can't pass writes?

Since I don't know exactly what this attribute does, I can't be sure, but it seems that making weak ordering the default is dangerous in that it breaks drivers that expect usual memory ordering semantics. Would it be safer/better to make strong ordering the default and then add a "WEAK_ORDERING" attribute that drivers can use as an optimization?

- R.

#15 lore raw

Re: [patch 9/9] powerpc/cell: Add DMA_ATTR_STRONG_ORDERING dma attribute and use in IOMMU code

Quoted 6 lines · Roland Dreier

Sorry for the late comments, I missed this when it went by before. =20
=C2=A0> +DMA_ATTR_STRONG_ORDERING
=C2=A0> +----------------------
=C2=A0> +
=C2=A0> +DMA_ATTR_STRONG_ORDERING specifies that previous reads and write=

s are

Quoted 1 line

=C2=A0> +performed in the order in which they're received by the IOMMU; t=

hus

Quoted 5 lines

=C2=A0> +reads and writes may not pass each other.
=20
I don't understand what this is trying to say. =C2=A0What is "previous" referring to? What does "received by the IOMMU" mean -- do you mean=20 issued onto the bus by the CPU?

This is all about inbound transfers, i.e. DMAs coming from the I/O bridge into the CPU, both DMA read and DMA write.

The relevant paragraph in the specification is=20
"If the SO bits in the I/O page table entry =3D =E2=80=9811=E2=80=99 and th= e IOIF S-bit is =E2=80=981=E2=80=99,
this READ or WRITE cannot be placed on the EIB until all previous READs and WRITEs from this CVCID and IOID have gotten an ACK or NULL type snoop respo= nse."

Normally, this is only true for accesses going to the same cache line, accesses from one device to different cache lines that are issued in order also send their response in-order (unless you get an I/O exception, which m= eans
you're toast), but can arrive at the I/O location out of order.

My interpretation is that strong ordering basically turns our whole I/O subsystem into single-issue non-posted accesses (all devices are currently configured to use the same CVCID and IOID), so we really should not do that.

Quoted 8 lines

When you say "reads and writes may not=20
pass each other," do you mean just that reads may not pass writes and writes may not pass reads, or do you mean that reads also can't pass reads and writes can't pass writes?
=20
Since I don't know exactly what this attribute does, I can't be sure, but it seems that making weak ordering the default is dangerous in that it breaks drivers that expect usual memory ordering semantics. =C2=A0Woul=

d it

Quoted 2 lines

be safer/better to make strong ordering the default and then add a "WEAK_ORDERING" attribute that drivers can use as an optimization?

With all our existing hardware, the I/O bridge overrides the setting to select weak ordering. The purpose of this patch is to change the default so that the bridge does not force weak ordering any more and some drivers are free to set strong ordering without impacting performance on the other drivers.

Strong ordering is only active when both the bridge and the IOMMU enable it, but for correctly written drivers, this only results in a slowdown.

	Arnd <><
#16 lore raw

Re: [patch 9/9] powerpc/cell: Add DMA_ATTR_STRONG_ORDERING dma attribute and use in IOMMU code

Quoted 2 lines

This is all about inbound transfers, i.e. DMAs coming from the I/O bridge into the CPU, both DMA read and DMA write.

OK -- at a minimum I think the documentation should make that clear. As it stands the description of what this does is pretty much impossible to parse and understand.

Quoted 2 lines

Strong ordering is only active when both the bridge and the IOMMU enable it, but for correctly written drivers, this only results in a slowdown.

So when would someone use this dma attribute? As a hack to fix drivers where the real fix is too complicated?

- R.

#17 lore raw

Re: [patch 9/9] powerpc/cell: Add DMA_ATTR_STRONG_ORDERING dma attribute and use in IOMMU code

Quoted 1 line · Roland Dreier

=A0> Strong ordering is only active when both the bridge and the IOMMU en=

able

Quoted 1 line

=A0> it, but for correctly written drivers, this only results in a slowdo=

wn.

Quoted 3 lines

=20
So when would someone use this dma attribute? =A0As a hack to fix drivers where the real fix is too complicated?

This is used in the Axon PCIe endpoint drivers, e.g. in the Roadrunner machine. The reason was to improve roundtrip latency by doing only mmio stores, not loads, on each side of the PCIe connection, which turn into posted DMA operations on the other end. With relaxed ordering, the posted writes may be observed out of order. Strong ordering makes sure they arrive in-order without having to do a non-posted mmio read or eieio operation on the receiver side.

	Arnd <><
#18 lore raw

Re: [Cbe-oss-dev] [patch 9/9] powerpc/cell: Add DMA_ATTR_STRONG_ORDERING dma attribute and use in IOMMU code

Quoted 7 lines · Arnd Bergmann
Quoted 5 lines · Roland Dreier
Quoted 2 lines

Strong ordering is only active when both the bridge and the IOMMU enable it, but for correctly written drivers, this only results in a slowdown.

So when would someone use this dma attribute? As a hack to fix drivers where the real fix is too complicated?

Quoted 7 lines

This is used in the Axon PCIe endpoint drivers, e.g. in the Roadrunner machine. The reason was to improve roundtrip latency by doing only mmio stores, not loads, on each side of the PCIe connection, which turn into posted DMA operations on the other end. With relaxed ordering, the posted writes may be observed out of order. Strong ordering makes sure they arrive in-order without having to do a non-posted mmio read or eieio operation on the receiver side.

I don't think it's legal for writes from a given initiator to arrive to memory out of order.

Some drivers, notably network drivers, for example, rely on the "OWN" bit being written last in memory when writing back ring buffer status.

If the bit arrives before the actual data, then data corruption will occur.

Ben.

#19 lore raw

Re: [Cbe-oss-dev] [patch 9/9] powerpc/cell: Add DMA_ATTR_STRONG_ORDERING dma attribute and use in IOMMU code

Quoted 17 lines · Benjamin Herrenschmidt
Quoted 8 lines · Arnd Bergmann
Quoted 6 lines · Roland Dreier
Quoted 3 lines

Strong ordering is only active when both the bridge and the IOMMU enable it, but for correctly written drivers, this only results in a slowdown.

So when would someone use this dma attribute? As a hack to fix drivers where the real fix is too complicated?

Quoted 7 lines

This is used in the Axon PCIe endpoint drivers, e.g. in the Roadrunner machine. The reason was to improve roundtrip latency by doing only mmio stores, not loads, on each side of the PCIe connection, which turn into posted DMA operations on the other end. With relaxed ordering, the posted writes may be observed out of order. Strong ordering makes sure they arrive in-order without having to do a non-posted mmio read or eieio operation on the receiver side.

Quoted 8 lines

I don't think it's legal for writes from a given initiator to arrive to memory out of order.

Some drivers, notably network drivers, for example, rely on the "OWN" bit being written last in memory when writing back ring buffer status.

If the bit arrives before the actual data, then data corruption will occur.

Ok, this makes sense. I've followed the bit down in the specification, and now it seems like we can't just set relaxed ordering in the IOMMU but should use the value that comes from the PCIe device.

The flow of the order bit in this machine is as follows:

1. The device can select relaxed (weak) or non-relaxed (strong) ordering for a DMA transfer. PCI-X is always strong, DMAx can be configured globally, and PCIe is device specific.
2. The PCIe root complex can override the order bit and force it to strong ordering (which we don't).
3. The PLB5-to-C3PO bridge can override the bit and force it to weak or strong or leave it alone (we force it to weak).
4. The IOMMU can force the bit to weak on a per-page base (we don't without the patch, but do with the patch).

Peter and Hans were involved in the discussion that led to the decision to change step 3 from per-transfer default to always weak ordering. I think they verified that this is safe for all the peripherals that we have on the QS21 and QS22 blades (tg3, ehci, mthca, mptsas), but that doesn't mean that it is safe in general, so I guess you are right that we should not make it the default in the kernel for Cell systems. Hans, can you confirm this?

	Arnd <><
#20 lore raw

Re: [Cbe-oss-dev] [patch 9/9] powerpc/cell: Add DMA_ATTR_STRONG_ORDERING dma attribute and use in IOMMU code

Quoted 10 lines · Arnd Bergmann

Peter and Hans were involved in the discussion that led to the decision
to change step 3 from per-transfer default to always weak ordering. I think they verified that this is safe for all the peripherals that we
have on the QS21 and QS22 blades (tg3, ehci, mthca, mptsas), but that doesn't mean that it is safe in general, so I guess you are right that we should not make it the default in the kernel for Cell systems. Hans, can you confirm this?

I'm surprised with tg3. We need to make sure that updates to the hw status block are properly ordered vs writes to the ring, among other things.

Ben.

#21 lore raw

Re: [Cbe-oss-dev] [patch 9/9] powerpc/cell: Add DMA_ATTR_STRONG_ORDERING dma attribute and use in IOMMU code

In reply to Arnd Bergmann
Quoted 17 lines

Ok, this makes sense. I've followed the bit down in the specification, and now it seems like we can't just set relaxed ordering in the IOMMU but should use the value that comes from the PCIe device.

The flow of the order bit in this machine is as follows:

1. The device can select relaxed (weak) or non-relaxed (strong) ordering for a DMA transfer. PCI-X is always strong, DMAx can be configured globally, and PCIe is device specific.
2. The PCIe root complex can override the order bit and force it to strong ordering (which we don't).
3. The PLB5-to-C3PO bridge can override the bit and force it to weak or strong or leave it alone (we force it to weak).
4. The IOMMU can force the bit to weak on a per-page base (we don't without the patch, but do with the patch).

Quoted 7 lines

Peter and Hans were involved in the discussion that led to the decision to change step 3 from per-transfer default to always weak ordering. I think they verified that this is safe for all the peripherals that we have on the QS21 and QS22 blades (tg3, ehci, mthca, mptsas), but that doesn't mean that it is safe in general, so I guess you are right that we should not make it the default in the kernel for Cell systems. Hans, can you confirm this?

In the meantime, send a patch that defaults to strong with explicit weak, we can easily fixup after that.

Ben.

#22 lore raw

PATCH 1/1 Add DMA_ATTR_WEAK_ORDERING dma attribute and use in Cell IOMMU code

Introduce a new dma attriblue DMA_ATTR_WEAK_ORDERING to use weak ordering on DMA mappings in the Cell processor. Add the code to the Cell's IOMMU implementation to use this code.

Dynamic mappings can be weakly or strongly ordered on an individual basis but the fixed mapping has to be either completely strong or completely weak. This is currently decided by a kernel boot option (pass iommu_fixed=weak for a weakly ordered fixed linear mapping, strongly ordered is the default).

---

Quoted 3 lines · Benjamin Herrenschmidt

In the meantime, send a patch that defaults to strong with explicit weak, we can easily fixup after that.

Ok, this is the previous version of the patch from Mark, which does exactly that. Please use this one instead.

 Documentation/DMA-attributes.txt    |    9 ++
 arch/powerpc/platforms/cell/iommu.c |  113 ++++++++++++++++++++++++++++++++++--
 include/linux/dma-attrs.h           |    1 
 3 files changed, 118 insertions(+), 5 deletions(-)
arch/powerpc/platforms/cell/iommu.c +108 -5
Index: linux-2.6/arch/powerpc/platforms/cell/iommu.c
===================================================================
@@ -198,6 +198,8 @@ static void tce_build_cell(struct iommu_
 	base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW |
 		(window->ioid & IOPTE_IOID_Mask);
 #endif
+	if (unlikely(dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)))
+		base_pte &= ~IOPTE_SO_RW;
 
 	io_pte = (unsigned long *)tbl->it_base + (index - tbl->it_offset);
 
@@ -538,7 +540,9 @@ static struct cbe_iommu *cell_iommu_for_
 static unsigned long cell_dma_direct_offset;
 
 static unsigned long dma_iommu_fixed_base;
-struct dma_mapping_ops dma_iommu_fixed_ops;
+
+/* iommu_fixed_is_weak is set if booted with iommu_fixed=weak */
+static int iommu_fixed_is_weak;
 
 static struct iommu_table *cell_get_iommu_table(struct device *dev)
 {
@@ -562,6 +566,98 @@ static struct iommu_table *cell_get_iomm
 	return &window->table;
 }
 
+/* A coherent allocation implies strong ordering */
+
+static void *dma_fixed_alloc_coherent(struct device *dev, size_t size,
+				      dma_addr_t *dma_handle, gfp_t flag)
+{
+	if (iommu_fixed_is_weak)
+		return iommu_alloc_coherent(dev, cell_get_iommu_table(dev),
+					    size, dma_handle,
+					    device_to_mask(dev), flag,
+					    dev->archdata.numa_node);
+	else
+		return dma_direct_ops.alloc_coherent(dev, size, dma_handle,
+						     flag);
+}
+
+static void dma_fixed_free_coherent(struct device *dev, size_t size,
+				    void *vaddr, dma_addr_t dma_handle)
+{
+	if (iommu_fixed_is_weak)
+		iommu_free_coherent(cell_get_iommu_table(dev), size, vaddr,
+				    dma_handle);
+	else
+		dma_direct_ops.free_coherent(dev, size, vaddr, dma_handle);
+}
+
+static dma_addr_t dma_fixed_map_single(struct device *dev, void *ptr,
+				       size_t size,
+				       enum dma_data_direction direction,
+				       struct dma_attrs *attrs)
+{
+	if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
+		return dma_direct_ops.map_single(dev, ptr, size, direction,
+						 attrs);
+	else
+		return iommu_map_single(dev, cell_get_iommu_table(dev), ptr,
+					size, device_to_mask(dev), direction,
+					attrs);
+}
+
+static void dma_fixed_unmap_single(struct device *dev, dma_addr_t dma_addr,
+				   size_t size,
+				   enum dma_data_direction direction,
+				   struct dma_attrs *attrs)
+{
+	if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
+		dma_direct_ops.unmap_single(dev, dma_addr, size, direction,
+					    attrs);
+	else
+		iommu_unmap_single(cell_get_iommu_table(dev), dma_addr, size,
+				   direction, attrs);
+}
+
+static int dma_fixed_map_sg(struct device *dev, struct scatterlist *sg,
+			   int nents, enum dma_data_direction direction,
+			   struct dma_attrs *attrs)
+{
+	if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
+		return dma_direct_ops.map_sg(dev, sg, nents, direction, attrs);
+	else
+		return iommu_map_sg(dev, cell_get_iommu_table(dev), sg, nents,
+				    device_to_mask(dev), direction, attrs);
+}
+
+static void dma_fixed_unmap_sg(struct device *dev, struct scatterlist *sg,
+			       int nents, enum dma_data_direction direction,
+			       struct dma_attrs *attrs)
+{
+	if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
+		dma_direct_ops.unmap_sg(dev, sg, nents, direction, attrs);
+	else
+		iommu_unmap_sg(cell_get_iommu_table(dev), sg, nents, direction,
+			       attrs);
+}
+
+static int dma_fixed_dma_supported(struct device *dev, u64 mask)
+{
+	return mask == DMA_64BIT_MASK;
+}
+
+static int dma_set_mask_and_switch(struct device *dev, u64 dma_mask);
+
+struct dma_mapping_ops dma_iommu_fixed_ops = {
+	.alloc_coherent = dma_fixed_alloc_coherent,
+	.free_coherent  = dma_fixed_free_coherent,
+	.map_single     = dma_fixed_map_single,
+	.unmap_single   = dma_fixed_unmap_single,
+	.map_sg         = dma_fixed_map_sg,
+	.unmap_sg       = dma_fixed_unmap_sg,
+	.dma_supported  = dma_fixed_dma_supported,
+	.set_dma_mask   = dma_set_mask_and_switch,
+};
+
 static void cell_dma_dev_setup_fixed(struct device *dev);
 
 static void cell_dma_dev_setup(struct device *dev)
@@ -918,9 +1014,16 @@ static void cell_iommu_setup_fixed_ptab(
 
 	pr_debug("iommu: mapping 0x%lx pages from 0x%lx\n", fsize, fbase);
 
-	base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW
+	base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M
 		    | (cell_iommu_get_ioid(np) & IOPTE_IOID_Mask);
 
+	if (iommu_fixed_is_weak)
+		pr_info("IOMMU: Using weak ordering for fixed mapping\n");
+	else {
+		pr_info("IOMMU: Using strong ordering for fixed mapping\n");
+		base_pte |= IOPTE_SO_RW;
+	}
+
 	for (uaddr = 0; uaddr < fsize; uaddr += (1 << 24)) {
 		/* Don't touch the dynamic region */
 		ioaddr = uaddr + fbase;
@@ -1036,9 +1139,6 @@ static int __init cell_iommu_fixed_mappi
 		cell_iommu_setup_window(iommu, np, dbase, dsize, 0);
 	}
 
-	dma_iommu_fixed_ops = dma_direct_ops;
-	dma_iommu_fixed_ops.set_dma_mask = dma_set_mask_and_switch;
-
 	dma_iommu_ops.set_dma_mask = dma_set_mask_and_switch;
 	set_pci_dma_ops(&dma_iommu_ops);
 
@@ -1052,6 +1152,9 @@ static int __init setup_iommu_fixed(char
 	if (strcmp(str, "off") == 0)
 		iommu_fixed_disabled = 1;
 
+	else if (strcmp(str, "weak") == 0)
+		iommu_fixed_is_weak = 1;
+
 	return 1;
 }
 __setup("iommu_fixed=", setup_iommu_fixed);
Documentation/DMA-attributes.txt +9 -0
Index: linux-2.6/Documentation/DMA-attributes.txt
===================================================================
@@ -22,3 +22,12 @@ ready and available in memory.  The DMA 
 could race with data DMA.  Mapping the memory used for completion
 indications with DMA_ATTR_WRITE_BARRIER would prevent the race.
 
+DMA_ATTR_WEAK_ORDERING
+----------------------
+
+DMA_ATTR_WEAK_ORDERING specifies that reads and writes to the mapping
+may be weakly ordered, that is that reads and writes may pass each other.
+
+Since it is optional for platforms to implement DMA_ATTR_WEAK_ORDERING,
+those that do not will simply ignore the attribute and exhibit default
+behavior.
include/linux/dma-attrs.h +1 -0
Index: linux-2.6/include/linux/dma-attrs.h
===================================================================
@@ -12,6 +12,7 @@
  */
 enum dma_attr {
 	DMA_ATTR_WRITE_BARRIER,
+	DMA_ATTR_WEAK_ORDERING,
 	DMA_ATTR_MAX,
 };
 
#23 lore raw

Re: [Cbe-oss-dev] [PATCH] Add DMA_ATTR_WEAK_ORDERING dma attribute and use in Cell IOMMU code

Quoted 3 lines

Ok, this is the previous version of the patch from Mark, which does exactly that. Please use this one instead.

So, should we add a:

From: Mark Nelson <[email protected]>

?

Jeremy

#24 lore raw

Re: [Cbe-oss-dev] [PATCH] Add DMA_ATTR_WEAK_ORDERING dma attribute and use in Cell IOMMU code

Quoted 8 lines · Jeremy Kerr
Quoted 3 lines

Ok, this is the previous version of the patch from Mark, which does exactly that. Please use this one instead.

So, should we add a:

From: Mark Nelson <[email protected]>

Yes, of course. I keep losing this line when sending patches through quilt and kmail.

	Arnd <><