Linux userland API discussions
 help / color / mirror / Atom feed
* Re: [PATCH v0 01/11] stm class: Introduce an abstraction for System Trace Module devices
From: Mathieu Poirier @ 2015-03-20 15:19 UTC (permalink / raw)
  To: Alexander Shishkin
  Cc: Greg Kroah-Hartman,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Pratik Patel, peter.lachner-ral2JQCrhuEAvxtiuMwx3w,
	norbert.schulz-ral2JQCrhuEAvxtiuMwx3w,
	keven.boell-ral2JQCrhuEAvxtiuMwx3w,
	yann.fouassier-ral2JQCrhuEAvxtiuMwx3w,
	laurent.fert-ral2JQCrhuEAvxtiuMwx3w,
	linux-api-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <87k2ybd8jl.fsf-qxRn5AmX6ZD9BXuAQUXR0fooFf0ArEBIu+b9c/7xato@public.gmane.org>

On 20 March 2015 at 08:53, Alexander Shishkin
<alexander.shishkin-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> wrote:
> Mathieu Poirier <mathieu.poirier-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> writes:
>
>> As promised I worked on a prototype that connects the coresight-stm
>> driver with the generic STM interface you have suggested.  Things work
>> quite well and aside from the enhancement related to the ioctl() and
>> private member as discussed above, we should move ahead with this.
>>
>> I will send out a new version of the coresight-stm driver as soon as I
>> see your patches with those changes.
>
> Actually, instread of a private member I'd simply pass struct stm_data
> pointer to the callback (like we do with other callbacks) and the
> private data would be in the structure that embeds this struct stm_data,
> so that you can get to it using container_of():
>
> struct my_stm {
>        struct stm_data  data;
>        void             *my_stuff;
>        ...
> };
>
> ...
>
> long my_ioctl(struct stm_data *data, unsigned int cmd, unsigned long
>               arg)
> {
>         struct my_stm *mine = container_of(data, struct my_stm, data);
>
> ...
>
> Would this work for you?

That should be just fine yes.

> I'm otherwise ready to send the second version
> of my patchset.

Cool

>
> Regards,
> --
> Alex

^ permalink raw reply

* Re: [PATCH] Input: Add MT_TOOL_PALM
From: Dmitry Torokhov @ 2015-03-20 16:44 UTC (permalink / raw)
  To: Peter Hutterer
  Cc: Charlie Mooney, linux-input, Benjamin Tissoires, Hans de Goede,
	Andrew De Los Reyes, Henrik Rydberg, Jonathan Corbet,
	Masanari Iida, Jiri Kosina, linux-doc, linux-kernel, linux-api
In-Reply-To: <20150319224155.GA32380@jelly.redhat.com>

On Fri, Mar 20, 2015 at 08:41:55AM +1000, Peter Hutterer wrote:
> On Wed, Mar 18, 2015 at 03:26:35PM -0700, Charlie Mooney wrote:
> > Currently there are only two "tools" that can be specified by a
> > multi-touch driver: MT_TOOL_FINGER and MT_TOOL_PEN.  In working with
> > Elan (The touch vendor) and discussing their next-gen devices it
> > seems that it will be useful to have more tools so that their devices
> > can give the upper layers of the stack hints as to what is touching
> > the sensor.
> > 
> > In particular they have new experimental firmware that can better
> > differentiate between palms vs fingertips and would like to plumb a
> > patch so that we can use their hints in higher-level gesture soft-
> > ware.  The firmware on the device can reasonably do a better job of
> > palm detection because it has access to all of the raw sensor readings
> > as opposed to just the width/pressure/etc that are exposed by the
> > driver.  As such, the firmware can characterize what a palm looks like
> > in much finer-grained detail and this change would allow such a
> > device to share its findings with the kernel.
> > 
> > Signed-off-by: Charlie Mooney <charliemooney@chromium.org>
> 
> Acked-by: Peter Hutterer <peter.hutterer@who-t.net>

Applied, thank you.

> 
> Cheers,
>    Peter
> 
> > ---
> >  Documentation/input/multi-touch-protocol.txt | 9 ++++++---
> >  include/uapi/linux/input.h                   | 3 ++-
> >  2 files changed, 8 insertions(+), 4 deletions(-)
> > 
> > diff --git a/Documentation/input/multi-touch-protocol.txt b/Documentation/input/multi-touch-protocol.txt
> > index 7b4f59c..b85d000 100644
> > --- a/Documentation/input/multi-touch-protocol.txt
> > +++ b/Documentation/input/multi-touch-protocol.txt
> > @@ -312,9 +312,12 @@ ABS_MT_TOOL_TYPE
> >  
> >  The type of approaching tool. A lot of kernel drivers cannot distinguish
> >  between different tool types, such as a finger or a pen. In such cases, the
> > -event should be omitted. The protocol currently supports MT_TOOL_FINGER and
> > -MT_TOOL_PEN [2]. For type B devices, this event is handled by input core;
> > -drivers should instead use input_mt_report_slot_state().
> > +event should be omitted. The protocol currently supports MT_TOOL_FINGER,
> > +MT_TOOL_PEN, and MT_TOOL_PALM [2]. For type B devices, this event is handled
> > +by input core; drivers should instead use input_mt_report_slot_state().
> > +A contact's ABS_MT_TOOL_TYPE may change over time while still touching the
> > +device, because the firmware may not be able to determine which tool is being
> > +used when it first appears.
> >  
> >  ABS_MT_BLOB_ID
> >  
> > diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
> > index b0a8130..2f62ab2 100644
> > --- a/include/uapi/linux/input.h
> > +++ b/include/uapi/linux/input.h
> > @@ -973,7 +973,8 @@ struct input_keymap_entry {
> >   */
> >  #define MT_TOOL_FINGER		0
> >  #define MT_TOOL_PEN		1
> > -#define MT_TOOL_MAX		1
> > +#define MT_TOOL_PALM		2
> > +#define MT_TOOL_MAX		2
> >  
> >  /*
> >   * Values describing the status of a force-feedback effect
> > -- 
> > 2.1.2
> > 

-- 
Dmitry

^ permalink raw reply

* [PATCH v5] add support for Freescale's MMA8653FC 10 bit accelerometer
From: Martin Kepplinger @ 2015-03-20 16:58 UTC (permalink / raw)
  To: benjamin.tissoires, mark.rutland, robh+dt, Pawel.Moll,
	ijc+devicetree, galak, dmitry.torokhov, alexander.stein
  Cc: hadess, akpm, gregkh, linux-api, devicetree, linux-input,
	linux-kernel, Martin Kepplinger, Christoph Muellner
In-Reply-To: <1426860374-10415-1-git-send-email-martink@posteo.de>

From: Martin Kepplinger <martin.kepplinger@theobroma-systems.com>

The MMA8653FC is a low-power, three-axis, capacitive micromachined
accelerometer with 10 bits of resolution with flexible user-programmable
options.

Embedded interrupt functions enable overall power savings, by relieving the
host processor from continuously polling data, for example using the poll()
system call.

The device can be configured to generate wake-up interrupt signals from any
combination of the configurable embedded functions, enabling the MMA8653FC
to monitor events while remaining in a low-power mode during periods of
inactivity.

This driver provides devicetree properties to program the device's behaviour
and a simple, tested and documented sysfs interface. The data sheet and more
information is available on Freescale's website.

Signed-off-by: Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
Signed-off-by: Christoph Muellner <christoph.muellner@theobroma-systems.com>
---

This would be v5 aswell, but appies to the current -next tree.

 drivers/staging/mma8653fc/Kconfig     |  10 +
 drivers/staging/mma8653fc/Makefile    |   1 +
 drivers/staging/mma8653fc/TODO        | 146 ++++++
 drivers/staging/mma8653fc/mma8653fc.c | 864 ++++++++++++++++++++++++++++++++++
 4 files changed, 1021 insertions(+)
 create mode 100644 drivers/staging/mma8653fc/Kconfig
 create mode 100644 drivers/staging/mma8653fc/Makefile
 create mode 100644 drivers/staging/mma8653fc/TODO
 create mode 100644 drivers/staging/mma8653fc/mma8653fc.c

diff --git a/drivers/staging/mma8653fc/Kconfig b/drivers/staging/mma8653fc/Kconfig
new file mode 100644
index 0000000..988451b
--- /dev/null
+++ b/drivers/staging/mma8653fc/Kconfig
@@ -0,0 +1,10 @@
+config MMA8653FC
+        tristate "MMA8653FC - Freescale's 3-Axis, 10-bit Digital Accelerometer"
+        depends on I2C
+        default n
+        help
+          Say Y here if you want to support Freescale's MMA8653FC Accelerometer
+          through I2C interface.
+
+          To compile this driver as a module, choose M here: the
+          module will be called mma8653fc.
diff --git a/drivers/staging/mma8653fc/Makefile b/drivers/staging/mma8653fc/Makefile
new file mode 100644
index 0000000..9a245a3
--- /dev/null
+++ b/drivers/staging/mma8653fc/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MMA8653FC)         += mma8653fc.o
diff --git a/drivers/staging/mma8653fc/TODO b/drivers/staging/mma8653fc/TODO
new file mode 100644
index 0000000..0a31225
--- /dev/null
+++ b/drivers/staging/mma8653fc/TODO
@@ -0,0 +1,146 @@
+- move to IIO device API. The current DT/sysfs interface is documented below
+
+Documentation/ABI/testing/...
+-----------------------------
+What:		/sys/bus/i2c/drivers/mma8653fc/*/standby
+Date:		March 2015
+Contact:	Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
+Description:
+		Write 0 to this in order to turn on the device, and 1 to turn
+		it off. Read to see if it is turned on or off.
+
+
+What:		/sys/bus/i2c/drivers/mma8653fc/*/currentmode
+Date:		March 2015
+Contact:	Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
+Description:
+		Reading this provides the current state of the device, read
+		directly from a register. This can be "standby", "wake" or
+		"sleep".
+
+
+What:		/sys/bus/i2c/drivers/mma8653fc/*/position
+Date:		March 2015
+Contact:	Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
+Description:
+		Read only. Without interrupts enabled gets current position
+		values by reading. Poll "position" with interrupt conditions
+		set, to get notified; see Documentation/.../fsl,mma8653fc.txt
+
+		position file format:
+		"x y z [landscape/portrait status] [front/back status]"
+
+		x y z values:
+			in mg
+		landscape/portrait status char:
+			r	landscape right
+			d	portrait down
+			u	portrait up
+			l	landscape left
+		front/back status char:
+			f	front facing
+			b	back facing
+
+
+Documentation/devicetree/bindings/...
+-------------------------------------
+Required properties:
+- compatible
+	"fsl,mma8653fc"
+- reg
+	I2C address
+
+Optional properties:
+
+- interrupt-parent
+	a phandle for the interrupt controller (see
+	Documentation/devicetree/bindings/interrupt-controller/interrupts.txt)
+- interrupts
+	interrupt line to which the chip is connected (active low)
+- int1
+	set to use interrupt line 1, default is line 2
+	the interrupt sources can be routed to one of the two lines
+- ir-freefall-motion-x
+	activate freefall/motion interrupts on x axis
+- ir-freefall-motion-y
+	activate freefall/motion interrupts on y axis
+- ir-freefall-motion-z
+	activate freefall/motion interrupts on z axis
+- irq-threshold
+	0 < value < 8000: threshold for motion interrupts in mg
+- ir-landscape-portrait
+	activate landscape/portrait interrupts
+- ir-auto-wake
+	activate wake/sleep change interrupts
+- ir-data-ready:
+	activate data-ready interrupts
+	Interrupt events can be activated in any combination.
+- dynamic-range
+	2, 4, or 8: dynamic measurement range in g, default: 2
+	In ±2 g mode, sensitivity = 256 counts/g.
+	In ±4 g mode, sensitivity = 128 counts/g.
+	In ±8 g mode, sensitivity = 64 counts/g.
+- auto-wake-sleep
+	auto sleep mode (lower frequency)
+- motion-mode
+	use motion mode instead of freefall mode (trigger if >threshold).
+	per default an interrupt occurs if motion values fall below the
+	value set in "threshold" and therefore can detect free fall on the
+	vertical axis (depending on the position of the device).
+	Setting this values inverts the behaviour and an interrupt occurs
+	above the threshold value, so usually activate horizontal axis in
+	this case.
+
+- x-offset
+	0 < value < 500: calibration offset in mg
+	The offset correction values are used to realign the Zero-g position
+	of the X, Y, and Z-axis after the device is mounted on a board.
+	this value has an offset of 250 itself:
+	0 is -250mg, 250 is 0 mg, 500 is 250mg
+- y-offset
+	see x-offset
+- z-offset
+	see x-offset
+
+Example 1:
+for a device laying on flat ground to recognize acceleration over 100mg.
+x-axis is calibrated to +10mg. Adapt interrupt line to your device.
+
+mma8653fc@1d {
+		compatible = "fsl,mma8653fc";
+		interrupt-parent = <&gpio1>;
+		interrupts = <5 0>;
+		reg = <0x1d>;
+
+		dynamic-range = <2>;
+		motion-mode;
+		ir-freefall-motion-x;
+		ir-freefall-motion-y;
+		irq-threshold = <100>;
+		x-offset = <160>;
+};
+
+Example 2:
+for a device mounted on a wall with y being the vertical axis. This recognizes
+y-acceleration below 800mg, so free fall or changing the orientation of the
+device (y not being the vertical axis and having ~1000mg anymore).
+
+mma8653fc@1d {
+		compatible = "fsl,mma8653fc";
+		interrupt-parent = <&gpio1>;
+		interrupts = <5 0>;
+		reg = <0x1d>;
+
+		dynamic-range = <2>;
+		ir-freefall-motion-y;
+		irq-threshold = <800>;
+};
+
+Example 3:
+minimal example. lets users read current acceleration values. No polling
+is available.
+
+mma8653fc@1d {
+		compatible = "fsl,mma8653fc";
+		reg = <0x1d>;
+};
diff --git a/drivers/staging/mma8653fc/mma8653fc.c b/drivers/staging/mma8653fc/mma8653fc.c
new file mode 100644
index 0000000..4bd7f99
--- /dev/null
+++ b/drivers/staging/mma8653fc/mma8653fc.c
@@ -0,0 +1,864 @@
+/*
+ * mma8653fc.c - Support for Freescale MMA8653FC 3-axis 10-bit accelerometer
+ *
+ * Copyright (c) 2014 Theobroma Systems Design and Consulting GmbH
+ *
+ * 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 of the License, 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.
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#define DRV_NAME	"mma8653fc"
+#define MMA8653FC_DEVICE_ID	0x5a
+
+#define MMA8653FC_STATUS	0x00
+
+#define ZYXOW_MASK	0x80
+#define ZYXDR		0x08
+
+#define MMA8653FC_WHO_AM_I	0x0d
+
+#define MMA8653FC_SYSMOD	0x0b
+#define STATE_STANDBY	0x00
+#define STATE_WAKE	0x01
+#define STATE_SLEEP	0x02
+
+#define MMA8450_STATUS_ZXYDR	0x08
+
+/*
+ * 10 bit output data registers
+ * MSB: 7:0 bits 9:2 of data word
+ * LSB: 7:6 bits 1:0 of data word
+ */
+#define MMA8653FC_OUT_X_MSB	0x01
+#define MMA8653FC_OUT_X_LSB	0x02
+#define MMA8653FC_OUT_Y_MSB	0x03
+#define MMA8653FC_OUT_Y_LSB	0x04
+#define MMA8653FC_OUT_Z_MSB	0x05
+#define MMA8653FC_OUT_Z_LSB	0x06
+
+/*
+ * Portrait/Landscape Status
+ */
+#define MMA8653FC_PL_STATUS	0x10
+
+#define NEWLP		0x80
+#define LAPO_HIGH	0x04
+#define LAPO_LOW	0x02
+#define BAFRO		0x01
+
+/*
+ * Portrait/Landscape Configuration
+ */
+#define MMA8653FC_PL_CFG	0x11
+
+#define PL_EN		(1 << 6)
+
+/*
+ * Data calibration registers
+ */
+#define MMA8653FC_OFF_X		0x2f
+#define MMA8653FC_OFF_Y		0x30
+#define MMA8653FC_OFF_Z		0x31
+
+/* 0 to 500 for dts, but -250 to 250 in mg */
+#define DEFAULT_OFF	250
+
+/*
+ * bits 1:0 dynamic range
+ * 00 +/- 2g
+ * 01 +/- 4g
+ * 10 +/- 8g
+ *
+ * HPF_Out bit 4 - data high pass or low pass filtered
+ */
+#define MMA8653FC_XYZ_DATA_CFG	0x0e
+
+#define RANGE_MASK	0x03
+#define RANGE2G		0x00
+#define RANGE4G		0x01
+#define RANGE8G		0x02
+/* values for calculation */
+#define SHIFT_2G	8
+#define INCR_2G		128
+#define SHIFT_4G	7
+#define INCR_4G		64
+#define SHIFT_8G	6
+#define INCR_8G		32
+#define DYN_RANGE_2G	2
+#define DYN_RANGE_4G	4
+#define DYN_RANGE_8G	8
+
+/*
+ * System Control Reg 1
+ */
+#define MMA8653FC_CTRL_REG1	0x2a
+
+#define ACTIVE_BIT	(1 << 0)
+#define ODR_MASK	0x38
+#define ODR_DEFAULT	0x20 /* 50 Hz */
+#define ASLP_RATE_MASK	0xc0
+#define ASLP_RATE_DEFAULT 0x80 /* 6.25 Hz */
+
+/*
+ * Sys Control Reg 2
+ *
+ * auto-sleep enable, software reset
+ */
+#define MMA8653FC_CTRL_REG2	0x2b
+
+#define SLPE		(1 << 2)
+#define SELFTEST	(1 << 7)
+#define SOFT_RESET	(1 << 6)
+
+/*
+ * Interrupt Source
+ */
+#define MMA8653FC_INT_SOURCE	0x0c
+
+#define SRC_ASLP	(1 << 7)
+#define SRC_LNDPRT	(1 << 4)
+#define SRC_FF_MT	(1 << 2)
+#define SRC_DRDY	(1 << 0)
+
+/*
+ * Interrupt Control Register
+ *
+ * default: active low
+ * default push-pull, not open-drain
+ */
+#define MMA8653FC_CTRL_REG3	0x2c
+
+#define WAKE_LNDPRT	(1 << 5)
+#define WAKE_FF_MT	(1 << 3)
+#define IPOL		(1 << 1)
+#define PP_OD		(1 << 0)
+
+/*
+ * Interrupt Enable Register
+ */
+#define MMA8653FC_CTRL_REG4	0x2d
+
+#define INT_EN_ASLP	(1 << 7)
+#define INT_EN_LNDPRT	(1 << 4)
+#define INT_EN_FF_MT	(1 << 2)
+#define INT_EN_DRDY	(1 << 0)
+
+/*
+ * Interrupt Configuration Register
+ * bit value 0 ... INT2 (default)
+ * bit value 1 ... INT1
+ */
+#define MMA8653FC_CTRL_REG5	0x2e
+
+#define INT_CFG_ASLP	(1 << 7)
+#define INT_CFG_LNDPRT	(1 << 4)
+#define INT_CFG_FF_MT	(1 << 2)
+#define INT_CFG_DRDY	(1 << 0)
+
+/*
+ * Freefall / Motion Configuration Register
+ *
+ * Event Latch enable/disable, motion or freefall mode
+ * and event flag enable per axis
+ */
+#define MMA8653FC_FF_MT_CFG	0x15
+
+#define FF_MT_CFG_ELE	(1 << 7)
+#define FF_MT_CFG_OAE	(1 << 6)
+#define FF_MT_CFG_ZEFE	(1 << 5)
+#define FF_MT_CFG_YEFE	(1 << 4)
+#define FF_MT_CFG_XEFE	(1 << 3)
+
+/*
+ * Freefall / Motion Source Register
+ */
+#define MMA8653FC_FF_MT_SRC	0x16
+
+/*
+ * Freefall / Motion Threshold Register
+ *
+ * define motion threshold
+ * 0.063 g/LSB, 127 counts(0x7f) (7 bit from LSB)
+ * range: 0.063g - 8g
+ */
+#define MMA8653FC_FF_MT_THS	0x17
+
+struct axis_triple {
+	s16 x;
+	s16 y;
+	s16 z;
+};
+
+struct mma8653fc_pdata {
+	s8 x_axis_offset;
+	s8 y_axis_offset;
+	s8 z_axis_offset;
+	bool auto_wake_sleep;
+	u32 range;
+	bool int1;
+	bool motion_mode;
+	u8 freefall_motion_thr;
+	bool int_src_data_ready;
+	bool int_src_ff_mt_x;
+	bool int_src_ff_mt_y;
+	bool int_src_ff_mt_z;
+	bool int_src_lndprt;
+	bool int_src_aslp;
+};
+
+struct mma8653fc {
+	struct i2c_client *client;
+	struct mutex mutex;
+	struct mma8653fc_pdata pdata;
+	struct axis_triple saved;
+	char orientation;
+	char bafro;
+	bool standby;
+	int irq;
+	unsigned int_mask;
+	u8 (*read)(struct mma8653fc *, unsigned char);
+	void (*write)(struct mma8653fc *, unsigned char, unsigned char);
+};
+
+/* defaults */
+static const struct mma8653fc_pdata mma8653fc_default_init = {
+	.range = 2,
+	.x_axis_offset = DEFAULT_OFF,
+	.y_axis_offset = DEFAULT_OFF,
+	.z_axis_offset = DEFAULT_OFF,
+	.auto_wake_sleep = false,
+	.int1 = false,
+	.motion_mode = false,
+	.freefall_motion_thr = 5,
+	.int_src_data_ready = false,
+	.int_src_ff_mt_x = false,
+	.int_src_ff_mt_y = false,
+	.int_src_ff_mt_z = false,
+	.int_src_lndprt = false,
+	.int_src_aslp = false,
+};
+
+static void mma8653fc_get_triple(struct mma8653fc *mma)
+{
+	u8 buf[6];
+	u8 status;
+
+	buf[0] = 0;
+
+	status = mma->read(mma, MMA8653FC_STATUS);
+	if (status & ZYXOW_MASK)
+		dev_dbg(&mma->client->dev, "previous read not completed\n");
+
+	buf[0] = mma->read(mma, MMA8653FC_OUT_X_MSB);
+	buf[1] = mma->read(mma, MMA8653FC_OUT_X_LSB);
+	buf[2] = mma->read(mma, MMA8653FC_OUT_Y_MSB);
+	buf[3] = mma->read(mma, MMA8653FC_OUT_Y_LSB);
+	buf[4] = mma->read(mma, MMA8653FC_OUT_Z_MSB);
+	buf[5] = mma->read(mma, MMA8653FC_OUT_Z_LSB);
+
+	mutex_lock(&mma->mutex);
+	/* move from registers to s16 */
+	mma->saved.x = (buf[1] | (buf[0] << 8)) >> 6;
+	mma->saved.y = (buf[3] | (buf[2] << 8)) >> 6;
+	mma->saved.z = (buf[5] | (buf[4] << 8)) >> 6;
+	mma->saved.x = sign_extend32(mma->saved.x, 9);
+	mma->saved.y = sign_extend32(mma->saved.y, 9);
+	mma->saved.z = sign_extend32(mma->saved.z, 9);
+
+	/* calc g, see data sheet and application note */
+	switch (mma->pdata.range) {
+	case DYN_RANGE_2G:
+		mma->saved.x = le16_to_cpu((1000 * mma->saved.x +
+					    INCR_2G) >> SHIFT_2G);
+		mma->saved.y = le16_to_cpu((1000 * mma->saved.y +
+					   INCR_2G) >> SHIFT_2G);
+		mma->saved.z = le16_to_cpu((1000 * mma->saved.z +
+					   INCR_2G) >> SHIFT_2G);
+		break;
+	case DYN_RANGE_4G:
+		mma->saved.x = le16_to_cpu((1000 * mma->saved.x +
+					   INCR_4G) >> SHIFT_4G);
+		mma->saved.y = le16_to_cpu((1000 * mma->saved.y +
+					   INCR_4G) >> SHIFT_4G);
+		mma->saved.z = le16_to_cpu((1000 * mma->saved.z +
+					   INCR_4G) >> SHIFT_4G);
+		break;
+	case DYN_RANGE_8G:
+		mma->saved.x = le16_to_cpu((1000 * mma->saved.x +
+					   INCR_8G) >> SHIFT_8G);
+		mma->saved.y = le16_to_cpu((1000 * mma->saved.y +
+					   INCR_8G) >> SHIFT_8G);
+		mma->saved.z = le16_to_cpu((1000 * mma->saved.z +
+					   INCR_8G) >> SHIFT_8G);
+		break;
+	default:
+		dev_err(&mma->client->dev, "internal data corrupt\n");
+	}
+	mutex_unlock(&mma->mutex);
+}
+
+static void mma8653fc_get_orientation(struct mma8653fc *mma, u8 byte)
+{
+	if ((byte & LAPO_HIGH) && !(LAPO_LOW))
+		mma->orientation = 'r'; /* landscape right */
+	if (!(byte & LAPO_HIGH) && (byte & LAPO_LOW))
+		mma->orientation = 'd'; /* portrait down */
+	if (!(byte & LAPO_HIGH) && !(byte & LAPO_LOW))
+		mma->orientation = 'u'; /* portrait up */
+	if ((byte & LAPO_HIGH) && (byte & LAPO_LOW))
+		mma->orientation = 'l'; /* landscape left */
+
+	if (byte & BAFRO)
+		mma->bafro = 'b'; /* back facing */
+	else
+		mma->bafro = 'f'; /* front facing */
+}
+
+static irqreturn_t mma8653fc_irq(int irq, void *handle)
+{
+	struct mma8653fc *mma = handle;
+	u8 int_src;
+	u8 byte;
+
+	int_src = mma->read(mma, MMA8653FC_INT_SOURCE);
+	if (int_src & SRC_DRDY)
+		/* data ready handle */
+	if (int_src & SRC_FF_MT) {
+		/* freefall/motion change handle */
+		dev_dbg(&mma->client->dev,
+			"freefall or motion change\n");
+		byte = mma->read(mma, MMA8653FC_FF_MT_SRC);
+	}
+	if (int_src & SRC_LNDPRT) {
+		/* landscape/portrait change handle */
+		dev_dbg(&mma->client->dev,
+			"landscape / portrait change\n");
+		byte = mma->read(mma, MMA8653FC_PL_STATUS);
+		mma8653fc_get_orientation(mma, byte);
+	}
+	if (int_src & SRC_ASLP)
+		/* autosleep change handle */
+	mma8653fc_get_triple(mma);
+
+	sysfs_notify(&mma->client->dev.kobj, NULL, "position");
+
+	return IRQ_HANDLED;
+}
+
+static void __mma8653fc_enable(struct mma8653fc *mma)
+{
+	u8 byte;
+
+	byte = mma->read(mma, MMA8653FC_CTRL_REG1);
+	mma->write(mma, MMA8653FC_CTRL_REG1, byte | ACTIVE_BIT);
+}
+
+static void __mma8653fc_disable(struct mma8653fc *mma)
+{
+	u8 byte;
+
+	byte = mma->read(mma, MMA8653FC_CTRL_REG1);
+	mma->write(mma, MMA8653FC_CTRL_REG1, byte & ~ACTIVE_BIT);
+}
+
+static ssize_t mma8653fc_standby_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct mma8653fc *mma = i2c_get_clientdata(client);
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", mma->standby);
+}
+
+static ssize_t mma8653fc_standby_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct mma8653fc *mma = i2c_get_clientdata(client);
+	unsigned int val;
+	int error;
+
+	error = kstrtouint(buf, 10, &val);
+	if (error)
+		return error;
+
+	mutex_lock(&mma->mutex);
+
+	if (val) {
+		if (!mma->standby)
+			__mma8653fc_disable(mma);
+	} else {
+		if (mma->standby)
+			__mma8653fc_enable(mma);
+	}
+
+	mma->standby = !!val;
+
+	mutex_unlock(&mma->mutex);
+
+	return count;
+}
+
+static DEVICE_ATTR(standby, 0664, mma8653fc_standby_show,
+				  mma8653fc_standby_store);
+
+static ssize_t mma8653fc_currentmode_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct mma8653fc *mma = i2c_get_clientdata(client);
+	ssize_t count;
+	u8 byte;
+
+	byte = mma->read(mma, MMA8653FC_SYSMOD);
+	if (byte < 0)
+		return byte;
+
+	switch (byte) {
+	case STATE_STANDBY:
+		count = scnprintf(buf, PAGE_SIZE, "standby\n");
+		break;
+	case STATE_WAKE:
+		count = scnprintf(buf, PAGE_SIZE, "wake\n");
+		break;
+	case STATE_SLEEP:
+		count = scnprintf(buf, PAGE_SIZE, "sleep\n");
+		break;
+	default:
+		count = scnprintf(buf, PAGE_SIZE, "unknown\n");
+	}
+	return count;
+}
+static DEVICE_ATTR(currentmode, S_IRUGO, mma8653fc_currentmode_show, NULL);
+
+static ssize_t mma8653fc_position_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct mma8653fc *mma = i2c_get_clientdata(client);
+	ssize_t count;
+	u8 byte;
+
+	if (!mma->irq) {
+		byte = mma->read(mma, MMA8653FC_PL_STATUS);
+		if (byte & NEWLP)
+			mma8653fc_get_orientation(mma, byte);
+
+		byte = mma->read(mma, MMA8653FC_STATUS);
+		if (byte & ZYXDR)
+			mma8653fc_get_triple(mma);
+
+		msleep(20);
+		dev_dbg(&client->dev, "data polled\n");
+	}
+	mutex_lock(&mma->mutex);
+	count = scnprintf(buf, PAGE_SIZE, "%d %d %d %c %c\n",
+			mma->saved.x, mma->saved.y, mma->saved.z,
+			mma->orientation, mma->bafro);
+	mutex_unlock(&mma->mutex);
+
+	return count;
+}
+static DEVICE_ATTR(position, S_IRUGO, mma8653fc_position_show, NULL);
+
+static struct attribute *mma8653fc_attributes[] = {
+	&dev_attr_position.attr,
+	&dev_attr_standby.attr,
+	&dev_attr_currentmode.attr,
+	NULL,
+};
+
+static const struct attribute_group mma8653fc_attr_group = {
+	.attrs = mma8653fc_attributes,
+};
+
+static u8 mma8653fc_read(struct mma8653fc *mma, unsigned char reg)
+{
+	u8 val;
+
+	val = i2c_smbus_read_byte_data(mma->client, reg);
+	if (val < 0) {
+		dev_err(&mma->client->dev,
+			"failed to read %x register\n", reg);
+	}
+	return val;
+}
+
+static void mma8653fc_write(struct mma8653fc *mma, unsigned char reg,
+			   unsigned char val)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(mma->client, reg, val);
+	if (ret) {
+		dev_err(&mma->client->dev,
+			"failed to write %x register\n", reg);
+	}
+}
+
+static const struct of_device_id mma8653fc_dt_ids[] = {
+	{ .compatible = "fsl,mma8653fc", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, mma8653fc_dt_ids);
+
+static const struct mma8653fc_pdata *mma8653fc_probe_dt(struct device *dev)
+{
+	struct mma8653fc_pdata *pdata;
+	struct device_node *node = dev->of_node;
+	const struct of_device_id *match;
+	u32 testu32;
+	s32 tests32;
+
+	if (!node) {
+		dev_err(dev, "no associated DT data\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	match = of_match_device(mma8653fc_dt_ids, dev);
+	if (!match) {
+		dev_err(dev, "unknown device model\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return ERR_PTR(-ENOMEM);
+
+	*pdata = mma8653fc_default_init;
+
+	/* overwrite from dts */
+	testu32 = pdata->x_axis_offset;
+	tests32 = 0;
+	of_property_read_u32(node, "x-offset", &testu32);
+	tests32 = testu32 - DEFAULT_OFF;
+	if ((tests32) && (tests32 <= DEFAULT_OFF) &&
+	    (tests32 >= -DEFAULT_OFF)) {
+		dev_info(dev, "use %dmg offset on X axis\n", tests32);
+		/* calc register value, resolution: 1.96mg */
+		pdata->x_axis_offset = (s8) (tests32 / 2);
+	}
+	testu32 = pdata->y_axis_offset;
+	tests32 = 0;
+	of_property_read_u32(node, "y-offset", &testu32);
+	tests32 = testu32 - DEFAULT_OFF;
+	if ((tests32) && (tests32 <= DEFAULT_OFF) &&
+	    (tests32 >= -DEFAULT_OFF)) {
+		dev_info(dev, "use %dmg offset on Y axis\n", tests32);
+		pdata->y_axis_offset = (s8) (tests32 / 2);
+	}
+	testu32 = pdata->z_axis_offset;
+	tests32 = 0;
+	of_property_read_u32(node, "z-offset", &testu32);
+	tests32 = testu32 - DEFAULT_OFF;
+	if ((tests32) && (tests32 <= DEFAULT_OFF) &&
+	    (tests32 >= -DEFAULT_OFF)) {
+		dev_info(dev, "use %dmg offset on Z axis\n", tests32);
+		pdata->z_axis_offset = (s8) (tests32 / 2);
+	}
+
+	testu32 = 0;
+	of_property_read_u32(node, "dynamic-range", &testu32);
+	if ((testu32) && (testu32 != 2) && (testu32 != 4) && (testu32 != 8)) {
+		dev_warn(dev, "wrong value for full scale range in dtb\n");
+	} else {
+		if (testu32)
+			pdata->range = testu32;
+	}
+
+	if (of_property_read_bool(node, "auto-wake-sleep"))
+		pdata->auto_wake_sleep = true;
+
+	if (of_property_read_bool(node, "int1"))
+		pdata->int1 = true;
+
+	if (of_property_read_bool(node, "motion-mode"))
+		pdata->motion_mode = true;
+
+	if (of_property_read_bool(node, "ir-data-ready"))
+		pdata->int_src_data_ready = true;
+
+	if (of_property_read_bool(node, "ir-freefall-motion-x"))
+		pdata->int_src_ff_mt_x = true;
+
+	if (of_property_read_bool(node, "ir-freefall-motion-y"))
+		pdata->int_src_ff_mt_y = true;
+
+	if (of_property_read_bool(node, "ir-freefall-motion-z"))
+		pdata->int_src_ff_mt_z = true;
+
+	if (of_property_read_bool(node, "ir-auto-wake"))
+		pdata->int_src_aslp = true;
+
+	if (of_property_read_bool(node, "ir-landscape-portrait"))
+		pdata->int_src_lndprt = true;
+
+	testu32 = 0;
+	of_property_read_u32(node, "irq-threshold", &testu32);
+	/* always uses maximum range +/- 8000g, resolution 63mg */
+	if ((testu32 <= 8000) && (testu32 > 0)) {
+		dev_dbg(dev, "use freefall / motion threshold %dmg\n",
+			 testu32);
+		/* calculate register value from mg */
+		pdata->freefall_motion_thr = (testu32 / 63) + 1;
+	}
+
+	return pdata;
+}
+static int mma8653fc_i2c_probe(struct i2c_client *client,
+				       const struct i2c_device_id *id)
+{
+	struct mma8653fc *mma;
+	const struct mma8653fc_pdata *pdata;
+	int err;
+	u8 byte;
+
+	err = i2c_check_functionality(client->adapter,
+			I2C_FUNC_SMBUS_BYTE_DATA);
+	if (!err) {
+		dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
+		return -EIO;
+	}
+
+	mma = devm_kzalloc(&client->dev, sizeof(*mma), GFP_KERNEL);
+	if (!mma)
+		err = -ENOMEM;
+
+	pdata = dev_get_platdata(&client->dev);
+	if (!pdata) {
+		pdata = mma8653fc_probe_dt(&client->dev);
+		if (IS_ERR(pdata)) {
+			err = PTR_ERR(pdata);
+			dev_err(&client->dev, "pdata from DT failed\n");
+			return err;
+		}
+	}
+	mma->pdata = *pdata;
+	pdata = &mma->pdata;
+	mma->client = client;
+	mma->read = &mma8653fc_read;
+	mma->write = &mma8653fc_write;
+
+	mutex_init(&mma->mutex);
+
+	i2c_set_clientdata(client, mma);
+
+	err = sysfs_create_group(&client->dev.kobj, &mma8653fc_attr_group);
+	if (err)
+		return err;
+
+	byte = mma->read(mma, MMA8653FC_WHO_AM_I);
+	if (byte != MMA8653FC_DEVICE_ID) {
+		dev_err(&client->dev, "wrong device for driver\n");
+		return -ENODEV;
+	}
+	dev_info(&client->dev, "loading driver for device id %x\n", byte);
+
+	mma->irq = irq_of_parse_and_map(client->dev.of_node, 0);
+	if (!mma->irq) {
+		dev_err(&client->dev, "Unable to parse or map IRQ\n");
+		goto no_irq;
+	}
+
+	err = irq_set_irq_type(mma->irq, IRQ_TYPE_EDGE_FALLING);
+	if (err) {
+		dev_err(&client->dev, "set_irq_type failed\n");
+		return err;
+	}
+
+	err = devm_request_threaded_irq(&client->dev, mma->irq, NULL,
+					mma8653fc_irq, IRQF_ONESHOT,
+					dev_name(&mma->client->dev), mma);
+	if (err) {
+		dev_err(&client->dev, "irq %d busy?\n", mma->irq);
+		return err;
+	}
+
+	mma->write(mma, MMA8653FC_CTRL_REG2, SOFT_RESET);
+
+	__mma8653fc_disable(mma);
+	mma->standby = true;
+
+	/* enable desired interrupts */
+	mma->orientation = '\0';
+	mma->bafro = '\0';
+	byte = 0;
+	if (pdata->int_src_data_ready) {
+		byte |= INT_EN_DRDY;
+		dev_dbg(&client->dev, "DATA READY interrupt source enabled\n");
+	}
+	if (pdata->int_src_ff_mt_x || pdata->int_src_ff_mt_y ||
+	    pdata->int_src_ff_mt_z) {
+		byte |= INT_EN_FF_MT;
+		dev_dbg(&client->dev, "FF MT interrupt source enabled\n");
+	}
+	if (pdata->int_src_lndprt) {
+		mma->write(mma, MMA8653FC_PL_CFG, PL_EN);
+		byte |= INT_EN_LNDPRT;
+		dev_dbg(&client->dev, "LNDPRT interrupt source enabled\n");
+	}
+	if (pdata->int_src_aslp) {
+		byte |= INT_EN_ASLP;
+		dev_dbg(&client->dev, "ASLP interrupt source enabled\n");
+	}
+	mma->write(mma, MMA8653FC_CTRL_REG4, byte);
+
+	/* force everything to line 1 */
+	if (pdata->int1) {
+		mma->write(mma, MMA8653FC_CTRL_REG5,
+			   (INT_CFG_ASLP | INT_CFG_LNDPRT |
+			   INT_CFG_FF_MT | INT_CFG_DRDY));
+		dev_dbg(&client->dev, "using interrupt line 1\n");
+	}
+no_irq:
+	/* range mode */
+	byte = mma->read(mma, MMA8653FC_XYZ_DATA_CFG);
+	byte &= ~RANGE_MASK;
+	switch (pdata->range) {
+	case DYN_RANGE_2G:
+		byte |= RANGE2G;
+		dev_dbg(&client->dev, "use 2g range\n");
+		break;
+	case DYN_RANGE_4G:
+		byte |= RANGE4G;
+		dev_dbg(&client->dev, "use 4g range\n");
+		break;
+	case DYN_RANGE_8G:
+		byte |= RANGE8G;
+		dev_dbg(&client->dev, "use 8g range\n");
+		break;
+	default:
+		dev_err(&client->dev, "wrong range mode value\n");
+		return -EINVAL;
+	}
+	mma->write(mma, MMA8653FC_XYZ_DATA_CFG, byte);
+
+	/* data calibration offsets */
+	if (pdata->x_axis_offset)
+		mma->write(mma, MMA8653FC_OFF_X, pdata->x_axis_offset);
+	if (pdata->y_axis_offset)
+		mma->write(mma, MMA8653FC_OFF_Y, pdata->y_axis_offset);
+	if (pdata->z_axis_offset)
+		mma->write(mma, MMA8653FC_OFF_Z, pdata->z_axis_offset);
+
+	/* if autosleep, wake on both landscape and motion changes */
+	if (pdata->auto_wake_sleep) {
+		byte = 0;
+		byte |= WAKE_LNDPRT;
+		byte |= WAKE_FF_MT;
+		mma->write(mma, MMA8653FC_CTRL_REG3, byte);
+		mma->write(mma, MMA8653FC_CTRL_REG2, SLPE);
+		dev_dbg(&client->dev, "auto sleep enabled\n");
+	}
+
+	/* data rates */
+	byte = 0;
+	byte = mma->read(mma, MMA8653FC_CTRL_REG1);
+	byte &= ~ODR_MASK;
+	byte |= ODR_DEFAULT;
+	byte &= ~ASLP_RATE_MASK;
+	byte |= ASLP_RATE_DEFAULT;
+	mma->write(mma, MMA8653FC_CTRL_REG1, byte);
+
+	/* freefall / motion config */
+	byte = 0;
+	if (pdata->motion_mode) {
+		byte |= FF_MT_CFG_OAE;
+		dev_dbg(&client->dev, "detect motion instead of freefall\n");
+	}
+	byte |= FF_MT_CFG_ELE;
+	if (pdata->int_src_ff_mt_x)
+		byte |= FF_MT_CFG_XEFE;
+	if (pdata->int_src_ff_mt_y)
+		byte |= FF_MT_CFG_YEFE;
+	if (pdata->int_src_ff_mt_z)
+		byte |= FF_MT_CFG_ZEFE;
+	mma->write(mma, MMA8653FC_FF_MT_CFG, byte);
+
+	if (pdata->freefall_motion_thr) {
+		mma->write(mma, MMA8653FC_FF_MT_THS,
+			   pdata->freefall_motion_thr);
+		/* calculate back to mg */
+		dev_dbg(&client->dev, "threshold set to %dmg\n",
+			 (63 * pdata->freefall_motion_thr) - 1);
+	}
+
+	return 0;
+}
+
+static int mma8653fc_remove(struct i2c_client *client)
+{
+	dev_dbg(&client->dev, "unregistered accelerometer\n");
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mma8653fc_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct mma8653fc *mma = i2c_get_clientdata(client);
+
+	__mma8653fc_disable(mma);
+
+	return 0;
+}
+
+static int mma8653fc_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct mma8653fc *mma = i2c_get_clientdata(client);
+
+	__mma8653fc_enable(mma);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mma8653fc_pm_ops, mma8653fc_suspend, mma8653fc_resume);
+#define MMA8653FC_PM_OPS (&mma8653fc_pm_ops)
+#else
+#define MMA8653FC_PM_OPS NULL
+#endif
+
+static const struct i2c_device_id mma8653fc_id[] = {
+	{ DRV_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, mma8653fc_id);
+
+static struct i2c_driver mma8653fc_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = mma8653fc_dt_ids,
+		.pm = MMA8653FC_PM_OPS,
+	},
+	.probe    = mma8653fc_i2c_probe,
+	.remove   = mma8653fc_remove,
+	.id_table = mma8653fc_id,
+};
+
+module_i2c_driver(mma8653fc_driver);
+
+MODULE_AUTHOR("Martin Kepplinger <martin.kepplinger@theobroma-systems.com");
+MODULE_DESCRIPTION("Freescale's MMA8653FC Three-Axis Accelerometer I2C Driver");
+MODULE_LICENSE("GPL");
-- 
2.1.4

^ permalink raw reply related

* [PATCH v2] coresight: moving to new "hwtracing" directory
From: Mathieu Poirier @ 2015-03-20 17:01 UTC (permalink / raw)
  To: linux, catalin.marinas
  Cc: will.deacon, gregkh, alexander.shishkin, linux-kernel,
	peter.lachner, norbert.schulz, keven.boell, yann.fouassier,
	laurent.fert, linux-api, linux-arm-kernel, kaixu.xia,
	zhang.chunyan, mathieu.poirier

Keeping drivers related to HW tracing on ARM, i.e coresight,
under "drivers/coresight" doesn't make sense when other
architectures start rolling out technologies of the same
nature.

As such creating a new "drivers/hwtracing" directory where all
drivers of the same kind can reside, reducing namespace
pollution under "drivers/".

Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
Change for v2:
 - generated patch with -M option 
---
 MAINTAINERS                                              | 2 +-
 arch/arm/Kconfig.debug                                   | 2 +-
 arch/arm64/Kconfig.debug                                 | 2 +-
 drivers/Makefile                                         | 2 +-
 drivers/{ => hwtracing}/coresight/Kconfig                | 0
 drivers/{ => hwtracing}/coresight/Makefile               | 0
 drivers/{ => hwtracing}/coresight/coresight-etb10.c      | 0
 drivers/{ => hwtracing}/coresight/coresight-etm-cp14.c   | 0
 drivers/{ => hwtracing}/coresight/coresight-etm.h        | 0
 drivers/{ => hwtracing}/coresight/coresight-etm3x.c      | 0
 drivers/{ => hwtracing}/coresight/coresight-funnel.c     | 0
 drivers/{ => hwtracing}/coresight/coresight-priv.h       | 0
 drivers/{ => hwtracing}/coresight/coresight-replicator.c | 0
 drivers/{ => hwtracing}/coresight/coresight-tmc.c        | 0
 drivers/{ => hwtracing}/coresight/coresight-tpiu.c       | 0
 drivers/{ => hwtracing}/coresight/coresight.c            | 0
 drivers/{ => hwtracing}/coresight/of_coresight.c         | 0
 17 files changed, 4 insertions(+), 4 deletions(-)
 rename drivers/{ => hwtracing}/coresight/Kconfig (100%)
 rename drivers/{ => hwtracing}/coresight/Makefile (100%)
 rename drivers/{ => hwtracing}/coresight/coresight-etb10.c (100%)
 rename drivers/{ => hwtracing}/coresight/coresight-etm-cp14.c (100%)
 rename drivers/{ => hwtracing}/coresight/coresight-etm.h (100%)
 rename drivers/{ => hwtracing}/coresight/coresight-etm3x.c (100%)
 rename drivers/{ => hwtracing}/coresight/coresight-funnel.c (100%)
 rename drivers/{ => hwtracing}/coresight/coresight-priv.h (100%)
 rename drivers/{ => hwtracing}/coresight/coresight-replicator.c (100%)
 rename drivers/{ => hwtracing}/coresight/coresight-tmc.c (100%)
 rename drivers/{ => hwtracing}/coresight/coresight-tpiu.c (100%)
 rename drivers/{ => hwtracing}/coresight/coresight.c (100%)
 rename drivers/{ => hwtracing}/coresight/of_coresight.c (100%)

diff --git a/MAINTAINERS b/MAINTAINERS
index 0e1abe8cc684..bc5d53ba1197 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -953,7 +953,7 @@ ARM/CORESIGHT FRAMEWORK AND DRIVERS
 M:	Mathieu Poirier <mathieu.poirier@linaro.org>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:	Maintained
-F:	drivers/coresight/*
+F:	drivers/hwtracing/coresight/*
 F:	Documentation/trace/coresight.txt
 F:	Documentation/devicetree/bindings/arm/coresight.txt
 F:	Documentation/ABI/testing/sysfs-bus-coresight-devices-*
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 8d14ad4e1db0..8b0183a9a300 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -1610,6 +1610,6 @@ config DEBUG_SET_MODULE_RONX
 	  against certain classes of kernel exploits.
 	  If in doubt, say "N".
 
-source "drivers/coresight/Kconfig"
+source "drivers/hwtracing/coresight/Kconfig"
 
 endmenu
diff --git a/arch/arm64/Kconfig.debug b/arch/arm64/Kconfig.debug
index 5b2ffd8e6cdb..d6285ef9b5f9 100644
--- a/arch/arm64/Kconfig.debug
+++ b/arch/arm64/Kconfig.debug
@@ -89,6 +89,6 @@ config DEBUG_ALIGN_RODATA
 
 	  If in doubt, say N
 
-source "drivers/coresight/Kconfig"
+source "drivers/hwtracing/coresight/Kconfig"
 
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 527a6da8d539..46d2554be404 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -163,5 +163,5 @@ obj-$(CONFIG_POWERCAP)		+= powercap/
 obj-$(CONFIG_MCB)		+= mcb/
 obj-$(CONFIG_RAS)		+= ras/
 obj-$(CONFIG_THUNDERBOLT)	+= thunderbolt/
-obj-$(CONFIG_CORESIGHT)		+= coresight/
+obj-$(CONFIG_CORESIGHT)		+= hwtracing/coresight/
 obj-$(CONFIG_ANDROID)		+= android/
diff --git a/drivers/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
similarity index 100%
rename from drivers/coresight/Kconfig
rename to drivers/hwtracing/coresight/Kconfig
diff --git a/drivers/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
similarity index 100%
rename from drivers/coresight/Makefile
rename to drivers/hwtracing/coresight/Makefile
diff --git a/drivers/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c
similarity index 100%
rename from drivers/coresight/coresight-etb10.c
rename to drivers/hwtracing/coresight/coresight-etb10.c
diff --git a/drivers/coresight/coresight-etm-cp14.c b/drivers/hwtracing/coresight/coresight-etm-cp14.c
similarity index 100%
rename from drivers/coresight/coresight-etm-cp14.c
rename to drivers/hwtracing/coresight/coresight-etm-cp14.c
diff --git a/drivers/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
similarity index 100%
rename from drivers/coresight/coresight-etm.h
rename to drivers/hwtracing/coresight/coresight-etm.h
diff --git a/drivers/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
similarity index 100%
rename from drivers/coresight/coresight-etm3x.c
rename to drivers/hwtracing/coresight/coresight-etm3x.c
diff --git a/drivers/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c
similarity index 100%
rename from drivers/coresight/coresight-funnel.c
rename to drivers/hwtracing/coresight/coresight-funnel.c
diff --git a/drivers/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
similarity index 100%
rename from drivers/coresight/coresight-priv.h
rename to drivers/hwtracing/coresight/coresight-priv.h
diff --git a/drivers/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c
similarity index 100%
rename from drivers/coresight/coresight-replicator.c
rename to drivers/hwtracing/coresight/coresight-replicator.c
diff --git a/drivers/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
similarity index 100%
rename from drivers/coresight/coresight-tmc.c
rename to drivers/hwtracing/coresight/coresight-tmc.c
diff --git a/drivers/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c
similarity index 100%
rename from drivers/coresight/coresight-tpiu.c
rename to drivers/hwtracing/coresight/coresight-tpiu.c
diff --git a/drivers/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
similarity index 100%
rename from drivers/coresight/coresight.c
rename to drivers/hwtracing/coresight/coresight.c
diff --git a/drivers/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c
similarity index 100%
rename from drivers/coresight/of_coresight.c
rename to drivers/hwtracing/coresight/of_coresight.c
-- 
1.9.1

^ permalink raw reply related

* [PATCH v1 01/11] stm class: Introduce an abstraction for System Trace Module devices
From: Alexander Shishkin @ 2015-03-20 17:29 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: linux-kernel, mathieu.poirier, pebolle, peter.lachner,
	norbert.schulz, keven.boell, yann.fouassier, laurent.fert,
	Alexander Shishkin, linux-api, Pratik Patel
In-Reply-To: <1426872598-68807-1-git-send-email-alexander.shishkin@linux.intel.com>

A System Trace Module (STM) is a device exporting data in System Trace
Protocol (STP) format as defined by MIPI STP standards. Examples of such
devices are Intel Trace Hub and Coresight STM.

This abstraction provides a unified interface for software trace sources
to send their data over an STM device to a debug host. In order to do
that, such a trace source needs to be assigned a pair of master/channel
identifiers that all the data from this source will be tagged with. The
STP decoder on the debug host side will use these master/channel tags to
distinguish different trace streams from one another inside one STP
stream.

This abstraction provides a configfs-based policy management mechanism
for dynamic allocation of these master/channel pairs based on trace
source-supplied string identifier. It has the flexibility of being
defined at runtime and at the same time (provided that the policy
definition is aligned with the decoding end) consistency.

For userspace trace sources, this abstraction provides write()-based and
mmap()-based (if the underlying stm device allows this) output mechanism.

For kernel-side trace sources, we provide "stm_source" device class that
can be connected to an stm device at run time.

Cc: linux-api@vger.kernel.org
Cc: Pratik Patel <pratikp@codeaurora.org>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 Documentation/ABI/testing/configfs-stp-policy    |  44 ++
 Documentation/ABI/testing/sysfs-class-stm        |  14 +
 Documentation/ABI/testing/sysfs-class-stm_source |  11 +
 Documentation/trace/stm.txt                      |  77 ++
 drivers/Kconfig                                  |   2 +
 drivers/Makefile                                 |   1 +
 drivers/hwtracing/stm/Kconfig                    |   8 +
 drivers/hwtracing/stm/Makefile                   |   3 +
 drivers/hwtracing/stm/core.c                     | 897 +++++++++++++++++++++++
 drivers/hwtracing/stm/policy.c                   | 467 ++++++++++++
 drivers/hwtracing/stm/stm.h                      |  79 ++
 include/linux/stm.h                              | 102 +++
 include/uapi/linux/stm.h                         |  47 ++
 13 files changed, 1752 insertions(+)
 create mode 100644 Documentation/ABI/testing/configfs-stp-policy
 create mode 100644 Documentation/ABI/testing/sysfs-class-stm
 create mode 100644 Documentation/ABI/testing/sysfs-class-stm_source
 create mode 100644 Documentation/trace/stm.txt
 create mode 100644 drivers/hwtracing/stm/Kconfig
 create mode 100644 drivers/hwtracing/stm/Makefile
 create mode 100644 drivers/hwtracing/stm/core.c
 create mode 100644 drivers/hwtracing/stm/policy.c
 create mode 100644 drivers/hwtracing/stm/stm.h
 create mode 100644 include/linux/stm.h
 create mode 100644 include/uapi/linux/stm.h

diff --git a/Documentation/ABI/testing/configfs-stp-policy b/Documentation/ABI/testing/configfs-stp-policy
new file mode 100644
index 0000000000..1c7ab3dbcd
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-stp-policy
@@ -0,0 +1,44 @@
+What:		/config/stp-policy
+Date:		Jan 2015
+KernelVersion:	3.20
+Description:
+		This group contains policies mandating Master/Channel allocation
+		for software sources wishing to send trace data over an STM
+		device.
+
+What:		/config/stp-policy/<policy>
+Date:		Jan 2015
+KernelVersion:	3.20
+Description:
+		Root of a policy. This name is an arbitrary string.
+
+What:		/config/stp-policy/<policy>/device
+Date:		Jan 2015
+KernelVersion:	3.20
+Description:
+		STM device to which this policy applies. Write a valid stm class
+		device name here to assign this policy to that device.
+
+What:		/config/stp-policy/<policy>/<node>
+Date:		Jan 2015
+KernelVersion:	3.20
+Description:
+		Policy node is a string identifier that software clients will
+		use to request a master/channel to be allocated and assigned to
+		them.
+
+What:		/config/stp-policy/<policy>/<node>/masters
+Date:		Jan 2015
+KernelVersion:	3.20
+Description:
+		Range of masters from which to allocate for users of this node.
+		Write two numbers: the first master and the last master number.
+
+What:		/config/stp-policy/<policy>/<node>/channels
+Date:		Jan 2015
+KernelVersion:	3.20
+Description:
+		Range of channels from which to allocate for users of this node.
+		Write two numbers: the first channel and the last channel
+		number.
+
diff --git a/Documentation/ABI/testing/sysfs-class-stm b/Documentation/ABI/testing/sysfs-class-stm
new file mode 100644
index 0000000000..186f8e66e1
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-stm
@@ -0,0 +1,14 @@
+What:		/sys/class/stm/<stm>/masters
+Date:		Jan 2015
+KernelVersion:	3.20
+Contact:	Alexander Shishkin <alexander.shishkin@linux.intel.com>
+Description:
+		Shows first and last available to software master numbers on
+		this STM device.
+
+What:		/sys/class/stm/<stm>/channels
+Date:		Jan 2015
+KernelVersion:	3.20
+Contact:	Alexander Shishkin <alexander.shishkin@linux.intel.com>
+Description:
+		Shows the number of channels per master on this STM device.
diff --git a/Documentation/ABI/testing/sysfs-class-stm_source b/Documentation/ABI/testing/sysfs-class-stm_source
new file mode 100644
index 0000000000..735b0d657f
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-stm_source
@@ -0,0 +1,11 @@
+What:		/sys/class/stm_source/<stm_source>/stm_source_link
+Date:		Jan 2015
+KernelVersion:	3.20
+Contact:	Alexander Shishkin <alexander.shishkin@linux.intel.com>
+Description:
+		stm_source device linkage to stm device, where its tracing data
+		is directed. Reads return an existing connection or "<none>" if
+		this stm_source is not connected to any stm device yet.
+		Write an existing (registered) stm device's name here to
+		connect that device. If a device is already connected to this
+		stm_source, it will first be disconnected.
diff --git a/Documentation/trace/stm.txt b/Documentation/trace/stm.txt
new file mode 100644
index 0000000000..0ba5c9115c
--- /dev/null
+++ b/Documentation/trace/stm.txt
@@ -0,0 +1,77 @@
+System Trace Module
+===================
+
+System Trace Module (STM) is a device described in MIPI STP specs as
+STP trace stream generator. STP (System Trace Protocol) is a trace
+protocol multiplexing data from multiple trace sources, each one of
+which is assigned a unique pair of master and channel. While some of
+these masters and channels are statically allocated to certain
+hardware trace sources, others are available to software. Software
+trace sources are usually free to pick for themselves any
+master/channel combination from this pool.
+
+On the receiving end of this STP stream (the decoder side), trace
+sources can only be identified by master/channel combination, so in
+order for the decoder to be able to make sense of the trace that
+involves multiple trace sources, it needs to be able to map those
+master/channel pairs to the trace sources that it understands.
+
+For instance, it is helpful to know that syslog messages come on
+master 7 channel 15, while arbitrary user applications can use masters
+48 to 63 and channels 0 to 127.
+
+To solve this mapping problem, stm class provides a policy management
+mechanism via configfs, that allows defining rules that map string
+identifiers to ranges of masters and channels. If these rules (policy)
+are consistent with what decoder expects, it will be able to properly
+process the trace data.
+
+This policy is a tree structure containing rules (policy_node) that
+have a name (string identifier) and a range of masters and channels
+associated with it, located in "stp-policy" subsystem directory in
+configfs. From the examle above, a rule may look like this:
+
+$ ls /config/stp-policy/my-policy/user
+channels masters
+$ cat /config/stp-policy/my-policy/user/masters
+48 63
+$ cat /config/stp-policy/my-policy/user/channels
+0 127
+
+which means that the master allocation pool for this rule consists of
+masters 48 through 63 and channel allocation pool has channels 0
+through 127 in it. Now, any producer (trace source) identifying itself
+with "user" identification string will be allocated a master and
+channel from within these ranges.
+
+These rules can be nested, for example, one can define a rule "dummy"
+under "user" directory from the example above and this new rule will
+be used for trace sources with the id string of "user/dummy".
+
+Trace sources have to open the stm class device's node and write their
+trace data into its file descriptor. In order to identify themselves
+to the policy, they need to do a STP_POLICY_ID_SET ioctl on this file
+descriptor providing their id string. Otherwise, they will be
+automatically allocated a master/channel pair upon first write to this
+file descriptor according to the "default" rule of the policy, if such
+exists.
+
+Some STM devices may allow direct mapping of the channel mmio regions
+to userspace for zero-copy writing. One mappable page (in terms of
+mmu) will usually contain multiple channels' mmios, so the user will
+need to allocate that many channels to themselves (via the
+aforementioned ioctl() call) to be able to do this. That is, if your
+stm device's channel mmio region is 64 bytes and hardware page size is
+4096 bytes, after a successful STP_POLICY_ID_SET ioctl() call with
+width==64, you should be able to mmap() one page on this file
+descriptor and obtain direct access to an mmio region for 64 channels.
+
+For kernel-based trace sources, there is "stm_source" device
+class. Devices of this class can be connected and disconnected to/from
+stm devices at runtime via a sysfs attribute.
+
+Examples of STM devices are Intel Trace Hub [1] and Coresight STM
+[2].
+
+[1] https://software.intel.com/sites/default/files/managed/d3/3c/intel-th-developer-manual.pdf
+[2] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0444b/index.html
diff --git a/drivers/Kconfig b/drivers/Kconfig
index c0cc96bab9..9850ab81cc 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -182,4 +182,6 @@ source "drivers/thunderbolt/Kconfig"
 
 source "drivers/android/Kconfig"
 
+source "drivers/hwtracing/stm/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 527a6da8d5..87d7c74e39 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -165,3 +165,4 @@ obj-$(CONFIG_RAS)		+= ras/
 obj-$(CONFIG_THUNDERBOLT)	+= thunderbolt/
 obj-$(CONFIG_CORESIGHT)		+= coresight/
 obj-$(CONFIG_ANDROID)		+= android/
+obj-$(CONFIG_STM)		+= hwtracing/stm/
diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig
new file mode 100644
index 0000000000..90ed327461
--- /dev/null
+++ b/drivers/hwtracing/stm/Kconfig
@@ -0,0 +1,8 @@
+config STM
+	tristate "System Trace Module devices"
+	help
+	  A System Trace Module (STM) is a device exporting data in System
+	  Trace Protocol (STP) format as defined by MIPI STP standards.
+	  Examples of such devices are Intel Trace Hub and Coresight STM.
+
+	  Say Y here to enable System Trace Module device support.
diff --git a/drivers/hwtracing/stm/Makefile b/drivers/hwtracing/stm/Makefile
new file mode 100644
index 0000000000..adec701649
--- /dev/null
+++ b/drivers/hwtracing/stm/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_STM)	+= stm_core.o
+
+stm_core-y		:= core.o policy.o
diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c
new file mode 100644
index 0000000000..9e82634590
--- /dev/null
+++ b/drivers/hwtracing/stm/core.c
@@ -0,0 +1,897 @@
+/*
+ * System Trace Module (STM) infrastructure
+ * Copyright (c) 2014, 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.
+ *
+ * STM class implements generic infrastructure for  System Trace Module devices
+ * as defined in MIPI STPv2 specification.
+ */
+
+#include <linux/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/compat.h>
+#include <linux/kdev_t.h>
+#include <linux/srcu.h>
+#include <linux/slab.h>
+#include <linux/stm.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include "stm.h"
+
+#include <uapi/linux/stm.h>
+
+static unsigned int stm_core_up;
+
+/*
+ * The SRCU here makes sure that STM device doesn't disappear from under a
+ * stm_source_write() caller, which may want to have as little overhead as
+ * possible.
+ */
+static struct srcu_struct stm_source_srcu;
+
+static ssize_t masters_show(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	struct stm_device *stm = dev_get_drvdata(dev);
+	int ret;
+
+	ret = sprintf(buf, "%u %u\n", stm->data->sw_start, stm->data->sw_end);
+
+	return ret;
+}
+
+static DEVICE_ATTR_RO(masters);
+
+static ssize_t channels_show(struct device *dev,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	struct stm_device *stm = dev_get_drvdata(dev);
+	int ret;
+
+	ret = sprintf(buf, "%u\n", stm->data->sw_nchannels);
+
+	return ret;
+}
+
+static DEVICE_ATTR_RO(channels);
+
+static struct attribute *stm_attrs[] = {
+	&dev_attr_masters.attr,
+	&dev_attr_channels.attr,
+	NULL,
+};
+
+static const struct attribute_group stm_group = {
+	.attrs	= stm_attrs,
+};
+
+static const struct attribute_group *stm_groups[] = {
+	&stm_group,
+	NULL,
+};
+
+static struct class stm_class = {
+	.name		= "stm",
+	.dev_groups	= stm_groups,
+};
+
+static int stm_dev_match(struct device *dev, const void *data)
+{
+	const char *name = data;
+
+	return sysfs_streq(name, dev_name(dev));
+}
+
+/**
+ * stm_find_device() - find stm device by name
+ * @buf:	character buffer containing the name
+ * @len:	length of the name in @buf
+ *
+ * This is called from attributes' store methods, so it will
+ * also trim the trailing newline if necessary.
+ *
+ * Return:	device pointer or null if lookup failed.
+ */
+struct device *stm_find_device(const char *buf, size_t len)
+{
+	if (!stm_core_up)
+		return NULL;
+
+	return class_find_device(&stm_class, NULL, buf, stm_dev_match);
+}
+
+/*
+ * Internally we only care about software-writable masters here, that is the
+ * ones in the range [stm_data->sw_start..stm_data..sw_end], however we need
+ * original master numbers to be visible externally, since they are the ones
+ * that will appear in the STP stream. Thus, the internal bookkeeping uses
+ * $master - stm_data->sw_start to reference master descriptors and such.
+ */
+
+#define __stm_master(_s, _m)				\
+	((_s)->masters[(_m) - (_s)->data->sw_start])
+
+static inline struct stp_master *
+stm_master(struct stm_device *stm, unsigned int idx)
+{
+	if (idx < stm->data->sw_start || idx > stm->data->sw_end)
+		return NULL;
+
+	return __stm_master(stm, idx);
+}
+
+static int stp_master_alloc(struct stm_device *stm, unsigned int idx)
+{
+	struct stp_master *master;
+	size_t size;
+
+	size = ALIGN(stm->data->sw_nchannels, 8) / 8;
+	size += sizeof(struct stp_master);
+	master = kzalloc(size, GFP_ATOMIC);
+	if (!master)
+		return -ENOMEM;
+
+	master->nr_free = stm->data->sw_nchannels;
+	__stm_master(stm, idx) = master;
+
+	return 0;
+}
+
+static void stp_master_free(struct stm_device *stm, unsigned int idx)
+{
+	struct stp_master *master = stm_master(stm, idx);
+
+	if (!master)
+		return;
+
+	__stm_master(stm, idx) = NULL;
+	kfree(master);
+}
+
+static void stm_output_claim(struct stm_device *stm, struct stm_output *output)
+{
+	struct stp_master *master = stm_master(stm, output->master);
+
+	if (WARN_ON_ONCE(master->nr_free < output->nr_chans))
+		return;
+
+	bitmap_allocate_region(&master->chan_map[0], output->channel,
+			       ilog2(output->nr_chans));
+
+	master->nr_free -= output->nr_chans;
+}
+
+static void
+stm_output_disclaim(struct stm_device *stm, struct stm_output *output)
+{
+	struct stp_master *master = stm_master(stm, output->master);
+
+	bitmap_release_region(&master->chan_map[0], output->channel,
+			      ilog2(output->nr_chans));
+
+	master->nr_free += output->nr_chans;
+}
+
+/*
+ * This is like bitmap_find_free_region(), except it can ignore @start bits
+ * at the beginning.
+ */
+static int find_free_channels(unsigned long *bitmap, unsigned int start,
+			      unsigned int end, unsigned int width)
+{
+	unsigned int pos;
+	int i;
+
+	for (pos = start; pos < end + 1; pos = ALIGN(pos, width)) {
+		pos = find_next_zero_bit(bitmap, end + 1, pos);
+		if (pos + width > end + 1)
+			break;
+
+		if (pos & (width - 1))
+			continue;
+
+		for (i = 1; i < width && !test_bit(pos + i, bitmap); i++)
+			;
+		if (i == width)
+			return pos;
+	}
+
+	return -1;
+}
+
+static unsigned int
+stm_find_master_chan(struct stm_device *stm, unsigned int width,
+		     unsigned int *mstart, unsigned int mend,
+		     unsigned int *cstart, unsigned int cend)
+{
+	struct stp_master *master;
+	unsigned int midx;
+	int pos, err;
+
+	for (midx = *mstart; midx <= mend; midx++) {
+		if (!stm_master(stm, midx)) {
+			err = stp_master_alloc(stm, midx);
+			if (err)
+				return err;
+		}
+
+		master = stm_master(stm, midx);
+
+		if (!master->nr_free)
+			continue;
+
+		pos = find_free_channels(master->chan_map, *cstart, cend,
+					 width);
+		if (pos < 0)
+			continue;
+
+		*mstart = midx;
+		*cstart = pos;
+		return 0;
+	}
+
+	return -ENOSPC;
+}
+
+static int stm_output_assign(struct stm_device *stm, unsigned int width,
+			     struct stp_policy_node *policy_node,
+			     struct stm_output *output)
+{
+	unsigned int midx, cidx, mend, cend;
+	int ret = -EBUSY;
+
+	if (width > stm->data->sw_nchannels)
+		return -EINVAL;
+
+	if (policy_node) {
+		stp_policy_node_get_ranges(policy_node,
+					   &midx, &mend, &cidx, &cend);
+	} else {
+		midx = stm->data->sw_start;
+		cidx = 0;
+		mend = stm->data->sw_end;
+		cend = stm->data->sw_nchannels - 1;
+	}
+
+	spin_lock(&stm->mc_lock);
+	if (output->nr_chans)
+		goto unlock;
+
+	ret = stm_find_master_chan(stm, width, &midx, mend, &cidx, cend);
+	if (ret)
+		goto unlock;
+
+	output->master = midx;
+	output->channel = cidx;
+	output->nr_chans = width;
+	stm_output_claim(stm, output);
+	dev_dbg(stm->dev, "assigned %u:%u (+%u)\n", midx, cidx, width);
+
+	ret = 0;
+unlock:
+	spin_unlock(&stm->mc_lock);
+
+	return ret;
+}
+
+static void stm_output_free(struct stm_device *stm, struct stm_output *output)
+{
+	spin_lock(&stm->mc_lock);
+	if (output->nr_chans)
+		stm_output_disclaim(stm, output);
+	spin_unlock(&stm->mc_lock);
+}
+
+static int major_match(struct device *dev, const void *data)
+{
+	unsigned int major = *(unsigned int *)data;
+
+	return MAJOR(dev->devt) == major;
+}
+
+static int stm_char_open(struct inode *inode, struct file *file)
+{
+	struct stm_file *stmf;
+	struct device *dev;
+	unsigned int major = imajor(inode);
+	int err = -ENODEV;
+
+	dev = class_find_device(&stm_class, NULL, &major, major_match);
+	if (!dev)
+		return -ENODEV;
+
+	stmf = kzalloc(sizeof(*stmf), GFP_KERNEL);
+	if (!stmf)
+		return -ENOMEM;
+
+	stmf->stm = dev_get_drvdata(dev);
+
+	if (!try_module_get(stmf->stm->owner))
+		goto err_free;
+
+	file->private_data = stmf;
+
+	return nonseekable_open(inode, file);
+
+err_free:
+	kfree(stmf);
+
+	return err;
+}
+
+static int stm_char_release(struct inode *inode, struct file *file)
+{
+	struct stm_file *stmf = file->private_data;
+
+	stm_output_free(stmf->stm, &stmf->output);
+	module_put(stmf->stm->owner);
+	kfree(stmf);
+
+	return 0;
+}
+
+static int stm_file_assign(struct stm_file *stmf, char *id, unsigned int width)
+{
+	struct stm_device *stm = stmf->stm;
+	int ret;
+
+	mutex_lock(&stm->policy_mutex);
+	if (stm->policy)
+		stmf->policy_node = stp_policy_node_lookup(stm->policy, id);
+
+	ret = stm_output_assign(stm, width, stmf->policy_node, &stmf->output);
+	mutex_unlock(&stm->policy_mutex);
+
+	return ret;
+}
+
+static ssize_t stm_char_write(struct file *file, const char __user *buf,
+			      size_t count, loff_t *ppos)
+{
+	struct stm_file *stmf = file->private_data;
+	struct stm_device *stm = stmf->stm;
+	char *kbuf;
+	int err;
+
+	/*
+	 * if no m/c have been assigned to this writer up to this
+	 * point, use "default" policy entry
+	 */
+	if (!stmf->output.nr_chans) {
+		err = stm_file_assign(stmf, "default", 1);
+		/*
+		 * EBUSY means that somebody else just assigned this
+		 * output, which is just fine for write()
+		 */
+		if (err && err != -EBUSY)
+			return err;
+	}
+
+	kbuf = kmalloc(count + 1, GFP_KERNEL);
+	if (!kbuf)
+		return -ENOMEM;
+
+	err = copy_from_user(kbuf, buf, count);
+	if (err) {
+		kfree(kbuf);
+		return -EFAULT;
+	}
+
+	stm->data->write(stm->data, stmf->output.master,
+			 stmf->output.channel, kbuf, count);
+
+
+	kfree(kbuf);
+
+	return count;
+}
+
+static int stm_char_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct stm_file *stmf = file->private_data;
+	struct stm_device *stm = stmf->stm;
+	unsigned long size, phys;
+
+	if (!stm->data->mmio_addr)
+		return -EOPNOTSUPP;
+
+	if (vma->vm_pgoff)
+		return -EINVAL;
+
+	size = vma->vm_end - vma->vm_start;
+
+	if (stmf->output.nr_chans * stm->data->sw_mmiosz != size)
+		return -EINVAL;
+
+	phys = stm->data->mmio_addr(stm->data, stmf->output.master,
+				    stmf->output.channel,
+				    stmf->output.nr_chans);
+
+	if (!phys)
+		return -EINVAL;
+
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
+	vm_iomap_memory(vma, phys, size);
+
+	return 0;
+}
+
+static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg)
+{
+	struct stm_device *stm = stmf->stm;
+	struct stp_policy_id *id;
+	int ret = -EINVAL;
+	u32 size;
+
+	if (stmf->output.nr_chans)
+		return -EBUSY;
+
+	if (copy_from_user(&size, arg, sizeof(size)))
+		return -EFAULT;
+
+	if (size >= PATH_MAX + sizeof(*id))
+		return -EINVAL;
+
+	/* size + 1 to make sure the .id string at the bottom is terminated */
+	id = kzalloc(size + 1, GFP_KERNEL);
+	if (!id)
+		return -ENOMEM;
+
+	if (copy_from_user(id, arg, size)) {
+		ret = -EFAULT;
+		goto err_free;
+	}
+
+	if (id->__reserved_0 || id->__reserved_1)
+		goto err_free;
+
+	if (id->width < 1 ||
+	    id->width > PAGE_SIZE / stm->data->sw_mmiosz)
+		goto err_free;
+
+	ret = stm_file_assign(stmf, id->id, id->width);
+	if (ret)
+		goto err_free;
+
+	if (stm->data->link)
+		stm->data->link(stm->data, stmf->output.master,
+				stmf->output.channel);
+
+	ret = 0;
+
+err_free:
+	kfree(id);
+
+	return ret;
+}
+
+static int stm_char_policy_get_ioctl(struct stm_file *stmf, void __user *arg)
+{
+	struct stp_policy_id id = {
+		.size		= sizeof(id),
+		.master		= stmf->output.master,
+		.channel	= stmf->output.channel,
+		.width		= stmf->output.nr_chans,
+		.__reserved_0	= 0,
+		.__reserved_1	= 0,
+	};
+
+	return copy_to_user(arg, &id, id.size) ? -EFAULT : 0;
+}
+
+static long
+stm_char_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct stm_file *stmf = file->private_data;
+	struct stm_data *stm_data = stmf->stm->data;
+	int err = -ENOTTY;
+
+	switch (cmd) {
+	case STP_POLICY_ID_SET:
+		err = stm_char_policy_set_ioctl(stmf, (void __user *)arg);
+		if (err)
+			return err;
+
+		return stm_char_policy_get_ioctl(stmf, (void __user *)arg);
+
+	case STP_POLICY_ID_GET:
+		return stm_char_policy_get_ioctl(stmf, (void __user *)arg);
+
+	default:
+		if (stm_data->ioctl)
+			err = stm_data->ioctl(stm_data, cmd, arg);
+
+		break;
+	}
+
+	return err;
+}
+
+#ifdef CONFIG_COMPAT
+static long
+stm_char_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	return stm_char_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
+#else
+#define stm_char_compat_ioctl	NULL
+#endif
+
+static const struct file_operations stm_fops = {
+	.open		= stm_char_open,
+	.release	= stm_char_release,
+	.write		= stm_char_write,
+	.mmap		= stm_char_mmap,
+	.unlocked_ioctl	= stm_char_ioctl,
+	.compat_ioctl	= stm_char_compat_ioctl,
+	.llseek		= no_llseek,
+};
+
+int stm_register_device(struct device *parent, struct stm_data *stm_data,
+			struct module *owner)
+{
+	struct stm_device *stm;
+	struct device *dev;
+	unsigned int nmasters;
+	int err = -ENOMEM;
+
+	if (!stm_core_up)
+		return -EPROBE_DEFER;
+
+	if (!stm_data->write || !stm_data->sw_nchannels)
+		return -EINVAL;
+
+	nmasters = stm_data->sw_end - stm_data->sw_start;
+	stm = kzalloc(sizeof(*stm) + nmasters * sizeof(void *), GFP_KERNEL);
+	if (!stm)
+		return -ENOMEM;
+
+	stm->major = register_chrdev(0, stm_data->name, &stm_fops);
+	if (stm->major < 0)
+		goto err_free;
+
+	dev = device_create(&stm_class, parent, MKDEV(stm->major, 0), NULL,
+			    "%s", stm_data->name);
+	if (IS_ERR(dev)) {
+		err = PTR_ERR(dev);
+		goto err_device;
+	}
+
+	spin_lock_init(&stm->link_lock);
+	INIT_LIST_HEAD(&stm->link_list);
+
+	spin_lock_init(&stm->mc_lock);
+	mutex_init(&stm->policy_mutex);
+	stm->sw_nmasters = nmasters;
+	stm->owner = owner;
+	stm->data = stm_data;
+	stm->dev = dev;
+	stm_data->stm = stm;
+
+	dev_set_drvdata(dev, stm);
+
+	return 0;
+
+err_device:
+	device_unregister(dev);
+err_free:
+	kfree(stm);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(stm_register_device);
+
+static void stm_source_link_drop(struct stm_source_device *src);
+
+void stm_unregister_device(struct stm_data *stm_data)
+{
+	struct stm_device *stm = stm_data->stm;
+	struct stm_source_device *src, *iter;
+	int i;
+
+	spin_lock(&stm->link_lock);
+	list_for_each_entry_safe(src, iter, &stm->link_list, link_entry) {
+		stm_source_link_drop(src);
+	}
+	spin_unlock(&stm->link_lock);
+
+	synchronize_srcu(&stm_source_srcu);
+
+	unregister_chrdev(stm->major, stm_data->name);
+
+	if (stm->policy)
+		stp_policy_unbind(stm->policy);
+
+	for (i = 0; i < stm->sw_nmasters; i++)
+		stp_master_free(stm, i);
+
+	device_unregister(stm->dev);
+	kfree(stm);
+	stm_data->stm = NULL;
+}
+EXPORT_SYMBOL_GPL(stm_unregister_device);
+
+/**
+ * stm_source_link_add() - connect an stm_source device to an stm device
+ * @src:	stm_source device
+ * @stm:	stm device
+ *
+ * This function establishes a link from stm_source to an stm device so that
+ * the former can send out trace data to the latter.
+ *
+ * Return:	0 on success, -errno otherwise.
+ */
+static int stm_source_link_add(struct stm_source_device *src,
+			       struct stm_device *stm)
+{
+	int err;
+
+	spin_lock(&stm->link_lock);
+	spin_lock(&src->link_lock);
+
+	/* src->link is dereferenced under stm_source_srcu but not the list */
+	rcu_assign_pointer(src->link, stm);
+	list_add_tail(&src->link_entry, &stm->link_list);
+
+	spin_unlock(&src->link_lock);
+	spin_unlock(&stm->link_lock);
+
+	if (stm->policy) {
+		char *id = kstrdup(src->data->name, GFP_KERNEL);
+
+		if (id) {
+			src->policy_node =
+				stp_policy_node_lookup(stm->policy, id);
+
+			kfree(id);
+		}
+	}
+
+	err = stm_output_assign(stm, src->data->nr_chans,
+				src->policy_node, &src->output);
+	if (err)
+		return err;
+
+	/* this is to notify the STM device that a new link has been made */
+	if (stm->data->link)
+		stm->data->link(stm->data, src->output.master,
+				src->output.channel);
+
+	/* this is to let the source carry out all necessary preparations */
+	if (src->data->link)
+		src->data->link(src->data);
+
+	return 0;
+}
+
+/**
+ * stm_source_link_drop() - detach stm_source from its stm device
+ * @src:	stm_source device
+ *
+ * Unlinking means disconnecting from source's STM device; after this
+ * writes will be unsuccessful until it is linked to a new STM device.
+ *
+ * This will happen on "stm_source_link" sysfs attribute write to undo
+ * the existing link (if any), or on linked STM device's de-registration.
+ */
+static void stm_source_link_drop(struct stm_source_device *src)
+{
+	int idx = srcu_read_lock(&stm_source_srcu);
+
+	if (src->link && src->data->unlink)
+		src->data->unlink(src->data);
+
+	srcu_read_unlock(&stm_source_srcu, idx);
+
+	spin_lock(&src->link_lock);
+	if (src->link) {
+		stm_output_free(src->link, &src->output);
+		list_del_init(&src->link_entry);
+		rcu_assign_pointer(src->link, NULL);
+	}
+	spin_unlock(&src->link_lock);
+}
+
+static ssize_t stm_source_link_show(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	struct stm_source_device *src = dev_get_drvdata(dev);
+	int idx, ret;
+
+	idx = srcu_read_lock(&stm_source_srcu);
+	ret = sprintf(buf, "%s\n",
+		      src->link ? dev_name(src->link->dev) : "<none>");
+	srcu_read_unlock(&stm_source_srcu, idx);
+
+	return ret;
+}
+
+static ssize_t stm_source_link_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct stm_source_device *src = dev_get_drvdata(dev);
+	struct stm_device *link;
+	struct device *linkdev;
+	int err;
+
+	stm_source_link_drop(src);
+
+	linkdev = stm_find_device(buf, count);
+	if (!linkdev)
+		return -EINVAL;
+
+	link = dev_get_drvdata(linkdev);
+
+	err = stm_source_link_add(src, link);
+
+	return err ? : count;
+}
+
+static DEVICE_ATTR_RW(stm_source_link);
+
+static struct attribute *stm_source_attrs[] = {
+	&dev_attr_stm_source_link.attr,
+	NULL,
+};
+
+static const struct attribute_group stm_source_group = {
+	.attrs	= stm_source_attrs,
+};
+
+static const struct attribute_group *stm_source_groups[] = {
+	&stm_source_group,
+	NULL,
+};
+
+static struct class stm_source_class = {
+	.name		= "stm_source",
+	.dev_groups	= stm_source_groups,
+};
+
+/**
+ * stm_source_register_device() - register an stm_source device
+ * @parent:	parent device
+ * @data:	device description structure
+ *
+ * This will create a device of stm_source class that can write
+ * data to an stm device once linked.
+ *
+ * Return:	0 on success, -errno otherwise.
+ */
+int stm_source_register_device(struct device *parent,
+			       struct stm_source_data *data)
+{
+	struct stm_source_device *src;
+	struct device *dev;
+
+	if (!stm_core_up)
+		return -EPROBE_DEFER;
+
+	src = kzalloc(sizeof(*src), GFP_KERNEL);
+	if (!src)
+		return -ENOMEM;
+
+	dev = device_create(&stm_source_class, parent, MKDEV(0, 0), NULL, "%s",
+			    data->name);
+	if (IS_ERR(dev)) {
+		kfree(src);
+		return PTR_ERR(dev);
+	}
+
+	spin_lock_init(&src->link_lock);
+	INIT_LIST_HEAD(&src->link_entry);
+	src->dev = dev;
+	src->data = data;
+	data->src = src;
+	dev_set_drvdata(dev, src);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(stm_source_register_device);
+
+/**
+ * stm_source_unregister_device() - unregister an stm_source device
+ * @data:	device description that was used to register the device
+ *
+ * This will remove a previously created stm_source device from the system.
+ */
+void stm_source_unregister_device(struct stm_source_data *data)
+{
+	struct stm_source_device *src = data->src;
+
+	stm_source_link_drop(src);
+
+	device_destroy(&stm_source_class, src->dev->devt);
+
+	kfree(src);
+}
+EXPORT_SYMBOL_GPL(stm_source_unregister_device);
+
+int stm_source_write(struct stm_source_data *data, unsigned int chan,
+		     const char *buf, size_t count)
+{
+	struct stm_source_device *src = data->src;
+	struct stm_device *stm;
+	int idx;
+
+	if (!src->output.nr_chans)
+		return -ENODEV;
+
+	if (chan >= src->output.nr_chans)
+		return -EINVAL;
+
+	idx = srcu_read_lock(&stm_source_srcu);
+
+	stm = srcu_dereference(src->link, &stm_source_srcu);
+	if (stm)
+		count = stm->data->write(stm->data, src->output.master,
+					 src->output.channel + chan, buf,
+					 count);
+	else
+		count = -ENODEV;
+
+	srcu_read_unlock(&stm_source_srcu, idx);
+
+	return count;
+}
+EXPORT_SYMBOL_GPL(stm_source_write);
+
+static int __init stm_core_init(void)
+{
+	int err;
+
+	err = class_register(&stm_class);
+	if (err)
+		return err;
+
+	err = class_register(&stm_source_class);
+	if (err)
+		goto err_stm;
+
+	err = stp_configfs_init();
+	if (err)
+		goto err_src;
+
+	init_srcu_struct(&stm_source_srcu);
+
+	stm_core_up++;
+
+	return 0;
+
+err_src:
+	class_unregister(&stm_source_class);
+err_stm:
+	class_unregister(&stm_class);
+
+	return err;
+}
+
+postcore_initcall(stm_core_init);
+
+static void __exit stm_core_exit(void)
+{
+	class_unregister(&stm_source_class);
+	class_unregister(&stm_class);
+	stp_configfs_exit();
+}
+
+module_exit(stm_core_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("System Trace Module device class");
+MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c
new file mode 100644
index 0000000000..b5c59a0e0c
--- /dev/null
+++ b/drivers/hwtracing/stm/policy.c
@@ -0,0 +1,467 @@
+/*
+ * System Trace Module (STM) master/channel allocation policy management
+ * Copyright (c) 2014, 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.
+ *
+ * A master/channel allocation policy allows mapping string identifiers to
+ * master and channel ranges, where allocation can be done.
+ */
+
+#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/configfs.h>
+#include <linux/slab.h>
+#include <linux/stm.h>
+#include "stm.h"
+
+/*
+ * STP Master/Channel allocation policy configfs layout.
+ */
+
+struct stp_policy {
+	struct config_group	group;
+	struct stm_device	*stm;
+};
+
+struct stp_policy_node {
+	struct config_group	group;
+	struct stm_device	*stm;
+	struct stp_policy	*policy;
+	unsigned int		first_master;
+	unsigned int		last_master;
+	unsigned int		first_channel;
+	unsigned int		last_channel;
+};
+
+void stp_policy_node_get_ranges(struct stp_policy_node *policy_node,
+				unsigned int *mstart, unsigned int *mend,
+				unsigned int *cstart, unsigned int *cend)
+{
+	*mstart	= policy_node->first_master;
+	*mend	= policy_node->last_master;
+	*cstart	= policy_node->first_channel;
+	*cend	= policy_node->last_channel;
+}
+
+static inline char *stp_policy_node_name(struct stp_policy_node *policy_node)
+{
+	return policy_node->group.cg_item.ci_name ? : "<none>";
+}
+
+static inline struct stp_policy *to_stp_policy(struct config_item *item)
+{
+	return item ?
+		container_of(to_config_group(item), struct stp_policy, group) :
+		NULL;
+}
+
+static inline struct stp_policy_node *
+to_stp_policy_node(struct config_item *item)
+{
+	return item ?
+		container_of(to_config_group(item), struct stp_policy_node,
+			     group) :
+		NULL;
+}
+
+static ssize_t stp_policy_node_masters_show(struct stp_policy_node *policy_node,
+					    char *page)
+{
+	ssize_t count;
+
+	count = sprintf(page, "%u %u\n", policy_node->first_master,
+			policy_node->last_master);
+
+	return count;
+}
+
+static ssize_t
+stp_policy_node_masters_store(struct stp_policy_node *policy_node,
+			      const char *page, size_t count)
+{
+	struct stm_device *stm = policy_node->stm;
+	unsigned int first, last;
+	char *p = (char *) page;
+
+	if (sscanf(p, "%u %u", &first, &last) != 2)
+		return -EINVAL;
+
+	/* must be within [sw_start..sw_end], which is an inclusive range */
+	if (first > INT_MAX || last > INT_MAX || first > last ||
+	    first < stm->data->sw_start ||
+	    last > stm->data->sw_end)
+		return -ERANGE;
+
+	policy_node->first_master = first;
+	policy_node->last_master = last;
+
+	return count;
+}
+
+static ssize_t
+stp_policy_node_channels_show(struct stp_policy_node *policy_node, char *page)
+{
+	ssize_t count;
+
+	count = sprintf(page, "%u %u\n", policy_node->first_channel,
+			policy_node->last_channel);
+
+	return count;
+}
+
+static ssize_t
+stp_policy_node_channels_store(struct stp_policy_node *policy_node,
+			       const char *page, size_t count)
+{
+	unsigned int first, last;
+	char *p = (char *) page;
+
+	if (sscanf(p, "%u %u", &first, &last) != 2)
+		return -EINVAL;
+
+	if (first > INT_MAX || last > INT_MAX || first > last ||
+	    last >= policy_node->stm->data->sw_nchannels)
+		return -ERANGE;
+
+	policy_node->first_channel = first;
+	policy_node->last_channel = last;
+
+	return count;
+}
+
+static void stp_policy_node_release(struct config_item *item)
+{
+	kfree(to_stp_policy_node(item));
+}
+
+struct stp_policy_node_attribute {
+	struct configfs_attribute	attr;
+	ssize_t (*show)(struct stp_policy_node *, char *);
+	ssize_t (*store)(struct stp_policy_node *, const char *, size_t);
+};
+
+static ssize_t stp_policy_node_attr_show(struct config_item *item,
+					 struct configfs_attribute *attr,
+					 char *page)
+{
+	struct stp_policy_node *policy_node = to_stp_policy_node(item);
+	struct stp_policy_node_attribute *pn_attr =
+		container_of(attr, struct stp_policy_node_attribute, attr);
+	ssize_t count = 0;
+
+	if (pn_attr->show)
+		count = pn_attr->show(policy_node, page);
+
+	return count;
+}
+
+static ssize_t stp_policy_node_attr_store(struct config_item *item,
+					  struct configfs_attribute *attr,
+					  const char *page, size_t len)
+{
+	struct stp_policy_node *policy_node = to_stp_policy_node(item);
+	struct stp_policy_node_attribute *pn_attr =
+		container_of(attr, struct stp_policy_node_attribute, attr);
+	ssize_t count = -EINVAL;
+
+	if (pn_attr->store)
+		count = pn_attr->store(policy_node, page, len);
+
+	return count;
+}
+
+static struct configfs_item_operations stp_policy_node_item_ops = {
+	.release		= stp_policy_node_release,
+	.show_attribute		= stp_policy_node_attr_show,
+	.store_attribute	= stp_policy_node_attr_store,
+};
+
+static struct stp_policy_node_attribute stp_policy_node_attr_range = {
+	.attr	= {
+		.ca_owner = THIS_MODULE,
+		.ca_name = "masters",
+		.ca_mode = S_IRUGO | S_IWUSR,
+	},
+	.show	= stp_policy_node_masters_show,
+	.store	= stp_policy_node_masters_store,
+};
+
+static struct stp_policy_node_attribute stp_policy_node_attr_channels = {
+	.attr	= {
+		.ca_owner = THIS_MODULE,
+		.ca_name = "channels",
+		.ca_mode = S_IRUGO | S_IWUSR,
+	},
+	.show	= stp_policy_node_channels_show,
+	.store	= stp_policy_node_channels_store,
+};
+
+static struct configfs_attribute *stp_policy_node_attrs[] = {
+	&stp_policy_node_attr_range.attr,
+	&stp_policy_node_attr_channels.attr,
+	NULL,
+};
+
+static struct config_item_type stp_policy_type;
+static struct config_item_type stp_policy_node_type;
+
+static struct config_group *
+stp_policy_node_make(struct config_group *group, const char *name)
+{
+	struct stp_policy_node *policy_node, *parent_node;
+	struct stp_policy *policy;
+
+	if (group->cg_item.ci_type == &stp_policy_type) {
+		policy = container_of(group, struct stp_policy, group);
+	} else {
+		parent_node = container_of(group, struct stp_policy_node,
+					   group);
+		policy = parent_node->policy;
+	}
+
+	if (!policy->stm)
+		return ERR_PTR(-ENODEV);
+
+	policy_node = kzalloc(sizeof(struct stp_policy_node), GFP_KERNEL);
+	if (!policy_node)
+		return ERR_PTR(-ENOMEM);
+
+	config_group_init_type_name(&policy_node->group, name,
+				    &stp_policy_node_type);
+
+	policy_node->policy = policy;
+	policy_node->stm = policy->stm;
+
+	/* default values for the attributes */
+	policy_node->first_master = policy->stm->data->sw_start;
+	policy_node->last_master = policy->stm->data->sw_end;
+	policy_node->first_channel = 0;
+	policy_node->last_channel = policy->stm->data->sw_nchannels - 1;
+
+	return &policy_node->group;
+}
+
+static void
+stp_policy_node_drop(struct config_group *group, struct config_item *item)
+{
+	config_item_put(item);
+}
+
+static struct configfs_group_operations stp_policy_node_group_ops = {
+	.make_group	= stp_policy_node_make,
+	.drop_item	= stp_policy_node_drop,
+};
+
+static struct config_item_type stp_policy_node_type = {
+	.ct_item_ops	= &stp_policy_node_item_ops,
+	.ct_group_ops	= &stp_policy_node_group_ops,
+	.ct_attrs	= stp_policy_node_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+/*
+ * Root group: policies.
+ */
+static struct configfs_attribute stp_policy_attr_device = {
+	.ca_owner = THIS_MODULE,
+	.ca_name = "device",
+	.ca_mode = S_IRUGO | S_IWUSR,
+};
+
+static struct configfs_attribute *stp_policy_attrs[] = {
+	&stp_policy_attr_device,
+	NULL,
+};
+
+static ssize_t stp_policy_attr_show(struct config_item *item,
+				    struct configfs_attribute *attr,
+				    char *page)
+{
+	struct stp_policy *policy = to_stp_policy(item);
+
+	return sprintf(page, "%s\n",
+		       (policy && policy->stm) ?
+		       policy->stm->data->name :
+		       "<none>");
+}
+
+static ssize_t stp_policy_attr_store(struct config_item *item,
+				     struct configfs_attribute *attr,
+				     const char *page, size_t len)
+{
+	struct stp_policy *policy = to_stp_policy(item);
+	ssize_t count = -EINVAL;
+	struct device *dev;
+
+	dev = stm_find_device(page, len);
+	if (dev) {
+		count = len;
+		if (policy->stm)
+			put_device(policy->stm->dev);
+
+		policy->stm = dev_get_drvdata(dev);
+
+		mutex_lock(&policy->stm->policy_mutex);
+		policy->stm->policy = policy;
+		mutex_unlock(&policy->stm->policy_mutex);
+	}
+
+	return count;
+}
+
+void stp_policy_unbind(struct stp_policy *policy)
+{
+	put_device(policy->stm->dev);
+
+	mutex_lock(&policy->stm->policy_mutex);
+	policy->stm->policy = NULL;
+	mutex_unlock(&policy->stm->policy_mutex);
+
+	policy->stm = NULL;
+}
+
+static void stp_policy_release(struct config_item *item)
+{
+	struct stp_policy *policy = to_stp_policy(item);
+
+	stp_policy_unbind(policy);
+	kfree(policy);
+}
+
+static struct configfs_item_operations stp_policy_item_ops = {
+	.release		= stp_policy_release,
+	.show_attribute		= stp_policy_attr_show,
+	.store_attribute	= stp_policy_attr_store,
+};
+
+static struct configfs_group_operations stp_policy_group_ops = {
+	.make_group	= stp_policy_node_make,
+};
+
+static struct config_item_type stp_policy_type = {
+	.ct_item_ops	= &stp_policy_item_ops,
+	.ct_group_ops	= &stp_policy_group_ops,
+	.ct_attrs	= stp_policy_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_group *
+stp_policies_make(struct config_group *group, const char *name)
+{
+	struct stp_policy *policy;
+
+	policy = kzalloc(sizeof(*policy), GFP_KERNEL);
+	if (!policy)
+		return ERR_PTR(-ENOMEM);
+
+	config_group_init_type_name(&policy->group, name,
+				    &stp_policy_type);
+	policy->stm = NULL;
+
+	return &policy->group;
+}
+
+static struct configfs_group_operations stp_policies_group_ops = {
+	.make_group	= stp_policies_make,
+};
+
+static struct config_item_type stp_policies_type = {
+	.ct_group_ops	= &stp_policies_group_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct configfs_subsystem stp_policy_subsys = {
+	.su_group = {
+		.cg_item = {
+			.ci_namebuf	= "stp-policy",
+			.ci_type	= &stp_policies_type,
+		},
+	},
+};
+
+/*
+ * Lock the policy mutex from the outside
+ */
+static struct stp_policy_node *
+__stp_policy_node_lookup(struct stp_policy *policy, char *s)
+{
+	struct stp_policy_node *policy_node, *ret;
+	struct list_head *head = &policy->group.cg_children;
+	struct config_item *item;
+	char *start, *end = s;
+
+	if (list_empty(head))
+		return NULL;
+
+	/* return the first entry if everything else fails */
+	item = list_entry(head->next, struct config_item, ci_entry);
+	ret = to_stp_policy_node(item);
+
+next:
+	for (;;) {
+		start = strsep(&end, "/");
+		if (!start)
+			break;
+
+		if (!*start)
+			continue;
+
+		list_for_each_entry(item, head, ci_entry) {
+			policy_node = to_stp_policy_node(item);
+
+			if (!strcmp(start,
+				    policy_node->group.cg_item.ci_name)) {
+				ret = policy_node;
+
+				if (!end)
+					goto out;
+
+				head = &policy_node->group.cg_children;
+				goto next;
+			}
+		}
+		break;
+	}
+
+out:
+	return ret;
+}
+
+struct stp_policy_node *
+stp_policy_node_lookup(struct stp_policy *policy, char *s)
+{
+	struct stp_policy_node *policy_node;
+
+	mutex_lock(&stp_policy_subsys.su_mutex);
+	policy_node = __stp_policy_node_lookup(policy, s);
+	mutex_unlock(&stp_policy_subsys.su_mutex);
+
+	return policy_node;
+}
+
+int __init stp_configfs_init(void)
+{
+	int err;
+
+	config_group_init(&stp_policy_subsys.su_group);
+	mutex_init(&stp_policy_subsys.su_mutex);
+	err = configfs_register_subsystem(&stp_policy_subsys);
+
+	return err;
+}
+
+void __exit stp_configfs_exit(void)
+{
+	configfs_unregister_subsystem(&stp_policy_subsys);
+}
diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h
new file mode 100644
index 0000000000..4d088a1400
--- /dev/null
+++ b/drivers/hwtracing/stm/stm.h
@@ -0,0 +1,79 @@
+/*
+ * System Trace Module (STM) infrastructure
+ * Copyright (c) 2014, 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.
+ *
+ * STM class implements generic infrastructure for  System Trace Module devices
+ * as defined in MIPI STPv2 specification.
+ */
+
+#ifndef _CLASS_STM_H_
+#define _CLASS_STM_H_
+
+struct stp_policy;
+struct stp_policy_node;
+
+struct stp_policy_node *
+stp_policy_node_lookup(struct stp_policy *policy, char *s);
+void stp_policy_unbind(struct stp_policy *policy);
+
+void stp_policy_node_get_ranges(struct stp_policy_node *policy_node,
+				unsigned int *mstart, unsigned int *mend,
+				unsigned int *cstart, unsigned int *cend);
+int stp_configfs_init(void);
+void stp_configfs_exit(void);
+
+struct stp_master {
+	unsigned int	nr_free;
+	unsigned long	chan_map[0];
+};
+
+struct stm_device {
+	struct device		*dev;
+	struct module		*owner;
+	struct stp_policy	*policy;
+	struct mutex		policy_mutex;
+	int			major;
+	unsigned int		sw_nmasters;
+	struct stm_data		*data;
+	spinlock_t		link_lock;
+	struct list_head	link_list;
+	/* master allocation */
+	spinlock_t		mc_lock;
+	struct stp_master	*masters[0];
+};
+
+struct stm_output {
+	unsigned int		master;
+	unsigned int		channel;
+	unsigned int		nr_chans;
+};
+
+struct stm_file {
+	struct stm_device	*stm;
+	struct stp_policy_node	*policy_node;
+	struct stm_output	output;
+};
+
+struct device *stm_find_device(const char *name, size_t len);
+
+struct stm_source_device {
+	struct device		*dev;
+	struct stm_source_data	*data;
+	spinlock_t		link_lock;
+	struct stm_device	*link;
+	struct list_head	link_entry;
+	/* one output per stm_source device */
+	struct stp_policy_node	*policy_node;
+	struct stm_output	output;
+};
+
+#endif /* _CLASS_STM_H_ */
diff --git a/include/linux/stm.h b/include/linux/stm.h
new file mode 100644
index 0000000000..976c94d8f1
--- /dev/null
+++ b/include/linux/stm.h
@@ -0,0 +1,102 @@
+/*
+ * System Trace Module (STM) infrastructure apis
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef _STM_H_
+#define _STM_H_
+
+#include <linux/device.h>
+
+struct stp_policy;
+
+struct stm_device;
+
+/**
+ * struct stm_data - STM device description and callbacks
+ * @name:		device name
+ * @stm:		internal structure, only used by stm class code
+ * @sw_start:		first STP master available to software
+ * @sw_end:		last STP master available to software
+ * @sw_nchannels:	number of STP channels per master
+ * @sw_mmiosz:		size of one channel's IO space, for mmap, optional
+ * @write:		write callback
+ * @mmio_addr:		mmap callback, optional
+ * @link:		called when a new stm_source gets linked to us, optional
+ * @unlink:		likewise for unlinking, again optional
+ * @ioctl:		ioctl callback for device-specific commands
+ *
+ * Fill out this structure before calling stm_register_device() to create
+ * an STM device and stm_unregister_device() to destroy it. It will also be
+ * passed back to @write(), @mmio_addr(), @link(), @unlink() and @ioctl()
+ * callbacks.
+ *
+ * Normally, an STM device will have a range of masters available to software
+ * and the rest being statically assigned to various hardware trace sources.
+ * The former is defined by the the range [@sw_start..@sw_end] of the device
+ * description. That is, the lowest master that can be allocated to software
+ * writers is @sw_start and data from this writer will appear is @sw_start
+ * master in the STP stream.
+ */
+struct stm_data {
+	const char		*name;
+	struct stm_device	*stm;
+	unsigned int		sw_start;
+	unsigned int		sw_end;
+	unsigned int		sw_nchannels;
+	unsigned int		sw_mmiosz;
+	ssize_t			(*write)(struct stm_data *, unsigned int,
+					 unsigned int, const char *, size_t);
+	phys_addr_t		(*mmio_addr)(struct stm_data *, unsigned int,
+					     unsigned int, unsigned int);
+	void			(*link)(struct stm_data *, unsigned int,
+					unsigned int);
+	void			(*unlink)(struct stm_data *, unsigned int,
+					  unsigned int);
+	long			(*ioctl)(struct stm_data *, unsigned int,
+					 unsigned long);
+};
+
+int stm_register_device(struct device *parent, struct stm_data *stm_data,
+			struct module *owner);
+void stm_unregister_device(struct stm_data *stm_data);
+
+struct stm_source_device;
+
+/**
+ * struct stm_source_data - STM source device description and callbacks
+ * @name:	device name, will be used for policy lookup
+ * @src:	internal structure, only used by stm class code
+ * @nr_chans:	number of channels to allocate
+ * @link:	called when this source gets linked to an STM device
+ * @unlink:	called when this source is about to get unlinked from its STM
+ *
+ * Fill in this structure before calling stm_source_register_device() to
+ * register a source device. Also pass it to unregister and write calls.
+ */
+struct stm_source_data {
+	const char		*name;
+	struct stm_source_device *src;
+	unsigned int		percpu;
+	unsigned int		nr_chans;
+	int			(*link)(struct stm_source_data *data);
+	void			(*unlink)(struct stm_source_data *data);
+};
+
+int stm_source_register_device(struct device *parent,
+			       struct stm_source_data *data);
+void stm_source_unregister_device(struct stm_source_data *data);
+
+int stm_source_write(struct stm_source_data *data, unsigned int chan,
+		     const char *buf, size_t count);
+
+#endif /* _STM_H_ */
diff --git a/include/uapi/linux/stm.h b/include/uapi/linux/stm.h
new file mode 100644
index 0000000000..042b58b53b
--- /dev/null
+++ b/include/uapi/linux/stm.h
@@ -0,0 +1,47 @@
+/*
+ * System Trace Module (STM) userspace interfaces
+ * Copyright (c) 2014, 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.
+ *
+ * STM class implements generic infrastructure for  System Trace Module devices
+ * as defined in MIPI STPv2 specification.
+ */
+
+#ifndef _UAPI_LINUX_STM_H
+#define _UAPI_LINUX_STM_H
+
+/**
+ * struct stp_policy_id - identification for the STP policy
+ * @size:	size of the structure including real id[] length
+ * @master:	assigned master
+ * @channel:	first assigned channel
+ * @width:	number of requested channels
+ * @id:		identification string
+ *
+ * User must calculate the total size of the structure and put it into
+ * @size field, fill out the @id and desired @width. In return, kernel
+ * fills out @master, @channel and @width.
+ */
+struct stp_policy_id {
+	__u32		size;
+	__u16		master;
+	__u16		channel;
+	__u16		width;
+	/* padding */
+	__u16		__reserved_0;
+	__u32		__reserved_1;
+	char		id[0];
+};
+
+#define STP_POLICY_ID_SET	_IOWR('%', 0, struct stp_policy_id)
+#define STP_POLICY_ID_GET	_IOR('%', 1, struct stp_policy_id)
+
+#endif /* _UAPI_LINUX_STM_H */
-- 
2.1.4

^ permalink raw reply related

* Re: [PATCH v2 5/7] clone4: Add a CLONE_AUTOREAP flag to automatically reap the child process
From: Oleg Nesterov @ 2015-03-20 18:14 UTC (permalink / raw)
  To: Josh Triplett
  Cc: Al Viro, Andrew Morton, Andy Lutomirski, Ingo Molnar, Kees Cook,
	Paul E. McKenney, H. Peter Anvin, Rik van Riel, Thomas Gleixner,
	Michael Kerrisk, Thiago Macieira, linux-kernel, linux-api,
	linux-fsdevel, x86
In-Reply-To: <20150315233439.GA31890@thin>

Josh,

I am really sorry for delay.

On 03/15, Josh Triplett wrote:
>
> On Sun, Mar 15, 2015 at 08:55:06PM +0100, Oleg Nesterov wrote:
 >
> > It should be per-process simply because this "autoreap" affects the whole
> > process. And the sub-threads are already "autoreap". And these 2 autoreap's
> > semantics differ, we should not confuse them.
>
> Will the approach I suggested, of having clones with CLONE_THREAD
> inherit the autoreap value rather than setting it from CLONE_AUTOREAP,
> implement the semantics you're looking for?

Not sure I understand... CLONE_THREAD should not inherit the autoreap.
A sub-thread is always autoreapable.

> Also, are you suggesting that CLONE_AUTOREAP with CLONE_THREAD should
> produce -EINVAL, or just that it should be ignored?

Yes, I think CLONE_AUTOREAP | CLONE_THREAD should return -EINVAL. But
this all is minor...

The main problem is how/when we should check this "autoreap" without
making this code even more ugly.

I still think we need a preparation patch. I tried to make it today but
failed. Will try again on weekend...


Note that we can't solely rely on do_notify_parent() which (with your patch)
correctly checks !ptrace && autoreap.

Just for example. Please look at __ptrace_detach(). Note that if we add
CLONE_AUTOREAP this needs a fix in any case. The tracee can be "autoreap"
but zombie, because "autoreap" should be ignored until the tracer detaches.
But the "same_thread_group" should not call do_notify_parent() again. So
this needs another check.

And let me quote our discussion from the previous email:

	> > EXCEPT: do we really want SIGCHLD from the exiting child? I think we
	> > do not. I won't really argue though, but this should be discussed and
	> > documented. IIUC, with your patch it is still sent.
	>
	> I think we do, yes.  The caller of clone can already specify what signal
	> they want, including no signal at all.  If they specify a signal
	> (SIGCHLD or otherwise) along with CLONE_AUTOREAP, we can send that
	> signal.

	OK. Agreed.

Yes, I agree...

But the changes in __ptrace_detach() depend on whether we need to send a signal
or not. Either way the changle is simple, but looks ugly. It would be nice to
cleanup this somehow.

Also. I forgot that the kernel always resets ->exit_signal to SIGCHLD on exec
or reparenting. Reparenting is probably fine. But what about exec? Should it
keep ->exit_signal == 0 if "autoreap" ? I think it should not, to avoid the
strange special case.

> > > > And there are ptrace/mt issues,
> > > > it seems. Just for example, we should avoid EXIT_TRACE if autoreap in
> > > > wait_task_zombie() even if we are going to re-notify parent.
> > >
> > > I don't see how EXIT_TRACE can happen in wait_task_zombie if autoreap is
> > > set.  wait_task_zombie does a cmpxchg with exit_state and doesn't
> > > proceed unless exit_state was EXIT_ZOMBIE, and I don't see how we can
> > > ever reach the EXIT_ZOMBIE state if autoreap.
> >
> > Because you again forgot about ptrace ;)

And this too asks for preparation before CLONE_AUTOREAP...

So I'll try to think about this all again on weekend. I'll try very much
to not disappear again ;)

Oleg.

^ permalink raw reply

* [v11 0/5] ext4: add project quota support
From: Li Xi @ 2015-03-20 18:37 UTC (permalink / raw)
  To: linux-fsdevel, linux-ext4, linux-api, tytso, adilger, jack, viro,
	hch, dmonakhov

The following patches propose an implementation of project quota
support for ext4. A project is an aggregate of unrelated inodes
which might scatter in different directories. Inodes that belong
to the same project possess an identical identification i.e.
'project ID', just like every inode has its user/group
identification. The following patches add project quota as
supplement to the former uer/group quota types.

The semantics of ext4 project quota is consistent with XFS. Each
directory can have EXT4_INODE_PROJINHERIT flag set. When the
EXT4_INODE_PROJINHERIT flag of a parent directory is not set, a
newly created inode under that directory will have a default project
ID (i.e. 0). And its EXT4_INODE_PROJINHERIT flag is not set either.
When this flag is set on a directory, following rules will be kept:

1) The newly created inode under that directory will inherit both
the EXT4_INODE_PROJINHERIT flag and the project ID from its parent
directory.

2) Hard-linking a inode with different project ID into that directory
will fail with errno EXDEV.

3) Renaming a inode with different project ID into that directory
will fail with errno EXDEV. However, 'mv' command will detect this
failure and copy the renamed inode to a new inode in the directory.
Thus, this new inode will inherit both the project ID and
EXT4_INODE_PROJINHERIT flag.

4) If the project quota of that ID is being enforced, statfs() on
that directory will take the quotas as another upper limits along
with the capacity of the file system, i.e. the total block/inode
number will be the minimum of the quota limits and file system
capacity.

Changelog:
* v11 <- v10:
 - Remove project quota mount option;
 - Fix permission check when setting project ID
* v10 <- v9:
 - Remove non-journaled project quota interface;
 - Only allow admin to read project quota info;
 - Cleanup FS_IOC_FSSETXATTR/FS_IOC_FSGETXATTR interface.
* v9 <- v8:
 - Remove non-journaled project quota;
 - Rebase to newest dev branch of ext4 repository (3.19.0-rc3).
* v8 <- v7:
 - Rebase to newest dev branch of ext4 repository (3.18.0_rc3).
* v7 <- v6:
 - Map ext4 inode flags to xflags of struct fsxattr;
 - Add patch to cleanup ext4 inode flag definitions.
* v6 <- v5:
 - Add project ID check for cross rename;
 - Remove patch of EXT4_IOC_GETPROJECT/EXT4_IOC_SETPROJECT ioctl
* v5 <- v4:
 - Check project feature when set/get project ID;
 - Do not check project feature for project quota;
 - Add support of FS_IOC_FSSETXATTR/FS_IOC_FSGETXATTR.
* v4 <- v3:
 - Do not check project feature when set/get project ID;
 - Use EXT4_MAXQUOTAS instead of MAXQUOTAS in ext4 patches;
 - Remove unnecessary change of fs/quota/dquot.c;
 - Remove CONFIG_QUOTA_PROJECT.
* v3 <- v2:
 - Add EXT4_INODE_PROJINHERIT semantics.
* v2 <- v1:
 - Add ioctl interface for setting/getting project;
 - Add EXT4_FEATURE_RO_COMPAT_PROJECT;
 - Add get_projid() method in struct dquot_operations;
 - Add error check of ext4_inode_projid_set/get().

v10: http://www.spinics.net/lists/linux-ext4/msg47413.html
v9: http://www.spinics.net/lists/linux-ext4/msg47326.html
v8: http://www.spinics.net/lists/linux-ext4/msg46545.html
v7: http://www.spinics.net/lists/linux-fsdevel/msg80404.html
v6: http://www.spinics.net/lists/linux-fsdevel/msg80022.html
v5: http://www.spinics.net/lists/linux-api/msg04840.html
v4: http://lwn.net/Articles/612972/
v3: http://www.spinics.net/lists/linux-ext4/msg45184.html
v2: http://www.spinics.net/lists/linux-ext4/msg44695.html
v1: http://article.gmane.org/gmane.comp.file-systems.ext4/45153

Any comments or feedbacks are appreciated.

Regards,
                                         - Li Xi

Li Xi (5):
  vfs: adds general codes to enforces project quota limits
  ext4: adds project ID support
  ext4: adds project quota support
  ext4: adds FS_IOC_FSSETXATTR/FS_IOC_FSGETXATTR interface support
  ext4: cleanup inode flag definitions

 fs/ext4/ext4.h             |   86 +++++++----
 fs/ext4/ialloc.c           |    5 +
 fs/ext4/inode.c            |   29 ++++
 fs/ext4/ioctl.c            |  366 +++++++++++++++++++++++++++++++++-----------
 fs/ext4/namei.c            |   18 +++
 fs/ext4/super.c            |   57 +++++++-
 fs/quota/dquot.c           |   35 ++++-
 fs/quota/quota.c           |    5 +-
 fs/quota/quotaio_v2.h      |    6 +-
 fs/xfs/xfs_fs.h            |   47 ++----
 include/linux/quota.h      |    2 +
 include/uapi/linux/fs.h    |   33 ++++
 include/uapi/linux/quota.h |    6 +-
 13 files changed, 532 insertions(+), 163 deletions(-)


^ permalink raw reply

* [v5 1/5] vfs: adds general codes to enforces project quota limits
From: Li Xi @ 2015-03-20 18:37 UTC (permalink / raw)
  To: linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
	linux-ext4-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA, tytso-3s7WtUTddSA,
	adilger-m1MBpc4rdrD3fQ9qLvQP4Q, jack-AlSwsSmVLrQ,
	viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn, hch-wEGCiKHe2LqWVfeAwA7xHQ,
	dmonakhov-GEFAQzZX7r8dnm+yROfE0A
In-Reply-To: <1426876658-9557-1-git-send-email-lixi-LfVdkaOWEx8@public.gmane.org>

This patch adds support for a new quota type PRJQUOTA for project quota
enforcement. Also a new method get_projid() is added into dquot_operations
structure.

Signed-off-by: Li Xi <lixi-LfVdkaOWEx8@public.gmane.org>
Signed-off-by: Dmitry Monakhov <dmonakhov-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
Reviewed-by: Jan Kara <jack-AlSwsSmVLrQ@public.gmane.org>
---
 fs/quota/dquot.c           |   35 ++++++++++++++++++++++++++++++-----
 fs/quota/quota.c           |    5 ++++-
 fs/quota/quotaio_v2.h      |    6 ++++--
 include/linux/quota.h      |    2 ++
 include/uapi/linux/quota.h |    6 ++++--
 5 files changed, 44 insertions(+), 10 deletions(-)

diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index 8f0acef..a02bb68 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -1159,8 +1159,8 @@ static int need_print_warning(struct dquot_warn *warn)
 			return uid_eq(current_fsuid(), warn->w_dq_id.uid);
 		case GRPQUOTA:
 			return in_group_p(warn->w_dq_id.gid);
-		case PRJQUOTA:	/* Never taken... Just make gcc happy */
-			return 0;
+		case PRJQUOTA:
+			return 1;
 	}
 	return 0;
 }
@@ -1399,6 +1399,9 @@ static void __dquot_initialize(struct inode *inode, int type)
 	/* First get references to structures we might need. */
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		struct kqid qid;
+		kprojid_t projid;
+		int rc;
+
 		got[cnt] = NULL;
 		if (type != -1 && cnt != type)
 			continue;
@@ -1409,6 +1412,10 @@ static void __dquot_initialize(struct inode *inode, int type)
 		 */
 		if (i_dquot(inode)[cnt])
 			continue;
+
+		if (!sb_has_quota_active(sb, cnt))
+			continue;
+
 		init_needed = 1;
 
 		switch (cnt) {
@@ -1418,6 +1425,12 @@ static void __dquot_initialize(struct inode *inode, int type)
 		case GRPQUOTA:
 			qid = make_kqid_gid(inode->i_gid);
 			break;
+		case PRJQUOTA:
+			rc = inode->i_sb->dq_op->get_projid(inode, &projid);
+			if (rc)
+				continue;
+			qid = make_kqid_projid(projid);
+			break;
 		}
 		got[cnt] = dqget(sb, qid);
 	}
@@ -2161,7 +2174,8 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
 		error = -EROFS;
 		goto out_fmt;
 	}
-	if (!sb->s_op->quota_write || !sb->s_op->quota_read) {
+	if (!sb->s_op->quota_write || !sb->s_op->quota_read ||
+	    (type == PRJQUOTA && sb->dq_op->get_projid == NULL)) {
 		error = -EINVAL;
 		goto out_fmt;
 	}
@@ -2402,8 +2416,19 @@ static void do_get_dqblk(struct dquot *dquot, struct fs_disk_quota *di)
 
 	memset(di, 0, sizeof(*di));
 	di->d_version = FS_DQUOT_VERSION;
-	di->d_flags = dquot->dq_id.type == USRQUOTA ?
-			FS_USER_QUOTA : FS_GROUP_QUOTA;
+	switch (dquot->dq_id.type) {
+	case USRQUOTA:
+		di->d_flags = FS_USER_QUOTA;
+		break;
+	case GRPQUOTA:
+		di->d_flags = FS_GROUP_QUOTA;
+		break;
+	case PRJQUOTA:
+		di->d_flags = FS_PROJ_QUOTA;
+		break;
+	default:
+		BUG();
+	}
 	di->d_id = from_kqid_munged(current_user_ns(), dquot->dq_id);
 
 	spin_lock(&dq_data_lock);
diff --git a/fs/quota/quota.c b/fs/quota/quota.c
index 2aa4151..33b30b1 100644
--- a/fs/quota/quota.c
+++ b/fs/quota/quota.c
@@ -30,7 +30,10 @@ static int check_quotactl_permission(struct super_block *sb, int type, int cmd,
 	case Q_XGETQSTATV:
 	case Q_XQUOTASYNC:
 		break;
-	/* allow to query information for dquots we "own" */
+	/*
+	 * allow to query information for dquots we "own"
+	 * always allow querying project quota
+	 */
 	case Q_GETQUOTA:
 	case Q_XGETQUOTA:
 		if ((type == USRQUOTA && uid_eq(current_euid(), make_kuid(current_user_ns(), id))) ||
diff --git a/fs/quota/quotaio_v2.h b/fs/quota/quotaio_v2.h
index f1966b4..4e95430 100644
--- a/fs/quota/quotaio_v2.h
+++ b/fs/quota/quotaio_v2.h
@@ -13,12 +13,14 @@
  */
 #define V2_INITQMAGICS {\
 	0xd9c01f11,	/* USRQUOTA */\
-	0xd9c01927	/* GRPQUOTA */\
+	0xd9c01927,	/* GRPQUOTA */\
+	0xd9c03f14,	/* PRJQUOTA */\
 }
 
 #define V2_INITQVERSIONS {\
 	1,		/* USRQUOTA */\
-	1		/* GRPQUOTA */\
+	1,		/* GRPQUOTA */\
+	1,		/* PRJQUOTA */\
 }
 
 /* First generic header */
diff --git a/include/linux/quota.h b/include/linux/quota.h
index 50978b7..ba51f7e 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -50,6 +50,7 @@
 
 #undef USRQUOTA
 #undef GRPQUOTA
+#undef PRJQUOTA
 enum quota_type {
 	USRQUOTA = 0,		/* element used for user quotas */
 	GRPQUOTA = 1,		/* element used for group quotas */
@@ -317,6 +318,7 @@ struct dquot_operations {
 	/* get reserved quota for delayed alloc, value returned is managed by
 	 * quota code only */
 	qsize_t *(*get_reserved_space) (struct inode *);
+	int (*get_projid) (struct inode *, kprojid_t *);/* Get project ID */
 };
 
 struct path;
diff --git a/include/uapi/linux/quota.h b/include/uapi/linux/quota.h
index 3b6cfbe..b2d9486 100644
--- a/include/uapi/linux/quota.h
+++ b/include/uapi/linux/quota.h
@@ -36,11 +36,12 @@
 #include <linux/errno.h>
 #include <linux/types.h>
 
-#define __DQUOT_VERSION__	"dquot_6.5.2"
+#define __DQUOT_VERSION__	"dquot_6.6.0"
 
-#define MAXQUOTAS 2
+#define MAXQUOTAS 3
 #define USRQUOTA  0		/* element used for user quotas */
 #define GRPQUOTA  1		/* element used for group quotas */
+#define PRJQUOTA  2		/* element used for project quotas */
 
 /*
  * Definitions for the default names of the quotas files.
@@ -48,6 +49,7 @@
 #define INITQFNAMES { \
 	"user",    /* USRQUOTA */ \
 	"group",   /* GRPQUOTA */ \
+	"project", /* PRJQUOTA */ \
 	"undefined", \
 };
 
-- 
1.7.1

^ permalink raw reply related

* [v5 2/5] ext4: adds project ID support
From: Li Xi @ 2015-03-20 18:37 UTC (permalink / raw)
  To: linux-fsdevel, linux-ext4, linux-api, tytso, adilger, jack, viro,
	hch, dmonakhov
In-Reply-To: <1426876658-9557-1-git-send-email-lixi@ddn.com>

This patch adds a new internal field of ext4 inode to save project
identifier. Also a new flag EXT4_INODE_PROJINHERIT is added for
inheriting project ID from parent directory.

Signed-off-by: Li Xi <lixi@ddn.com>
Reviewed-by: Jan Kara <jack@suse.cz>
---
 fs/ext4/ext4.h          |   21 +++++++++++++++++----
 fs/ext4/ialloc.c        |    5 +++++
 fs/ext4/inode.c         |   29 +++++++++++++++++++++++++++++
 fs/ext4/namei.c         |   18 ++++++++++++++++++
 fs/ext4/super.c         |    1 +
 include/uapi/linux/fs.h |    1 +
 6 files changed, 71 insertions(+), 4 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 7fec2ef..7acb2da 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -378,16 +378,18 @@ struct flex_groups {
 #define EXT4_EA_INODE_FL	        0x00200000 /* Inode used for large EA */
 #define EXT4_EOFBLOCKS_FL		0x00400000 /* Blocks allocated beyond EOF */
 #define EXT4_INLINE_DATA_FL		0x10000000 /* Inode has inline data. */
+#define EXT4_PROJINHERIT_FL		0x20000000 /* Create with parents projid */
 #define EXT4_RESERVED_FL		0x80000000 /* reserved for ext4 lib */
 
-#define EXT4_FL_USER_VISIBLE		0x004BDFFF /* User visible flags */
-#define EXT4_FL_USER_MODIFIABLE		0x004380FF /* User modifiable flags */
+#define EXT4_FL_USER_VISIBLE		0x204BDFFF /* User visible flags */
+#define EXT4_FL_USER_MODIFIABLE		0x204380FF /* User modifiable flags */
 
 /* Flags that should be inherited by new inodes from their parent. */
 #define EXT4_FL_INHERITED (EXT4_SECRM_FL | EXT4_UNRM_FL | EXT4_COMPR_FL |\
 			   EXT4_SYNC_FL | EXT4_NODUMP_FL | EXT4_NOATIME_FL |\
 			   EXT4_NOCOMPR_FL | EXT4_JOURNAL_DATA_FL |\
-			   EXT4_NOTAIL_FL | EXT4_DIRSYNC_FL)
+			   EXT4_NOTAIL_FL | EXT4_DIRSYNC_FL |\
+			   EXT4_PROJINHERIT_FL)
 
 /* Flags that are appropriate for regular files (all but dir-specific ones). */
 #define EXT4_REG_FLMASK (~(EXT4_DIRSYNC_FL | EXT4_TOPDIR_FL))
@@ -435,6 +437,7 @@ enum {
 	EXT4_INODE_EA_INODE	= 21,	/* Inode used for large EA */
 	EXT4_INODE_EOFBLOCKS	= 22,	/* Blocks allocated beyond EOF */
 	EXT4_INODE_INLINE_DATA	= 28,	/* Data in inode. */
+	EXT4_INODE_PROJINHERIT	= 29,	/* Create with parents projid */
 	EXT4_INODE_RESERVED	= 31,	/* reserved for ext4 lib */
 };
 
@@ -684,6 +687,7 @@ struct ext4_inode {
 	__le32  i_crtime;       /* File Creation time */
 	__le32  i_crtime_extra; /* extra FileCreationtime (nsec << 2 | epoch) */
 	__le32  i_version_hi;	/* high 32 bits for 64-bit version */
+	__le32  i_projid;	/* Project ID */
 };
 
 struct move_extent {
@@ -939,6 +943,7 @@ struct ext4_inode_info {
 
 	/* Precomputed uuid+inum+igen checksum for seeding inode checksums */
 	__u32 i_csum_seed;
+	kprojid_t i_projid;
 };
 
 /*
@@ -1531,6 +1536,7 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
  */
 #define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM	0x0400
 #define EXT4_FEATURE_RO_COMPAT_READONLY		0x1000
+#define EXT4_FEATURE_RO_COMPAT_PROJECT		0x2000
 
 #define EXT4_FEATURE_INCOMPAT_COMPRESSION	0x0001
 #define EXT4_FEATURE_INCOMPAT_FILETYPE		0x0002
@@ -1581,7 +1587,8 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
 					 EXT4_FEATURE_RO_COMPAT_HUGE_FILE |\
 					 EXT4_FEATURE_RO_COMPAT_BIGALLOC |\
 					 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM|\
-					 EXT4_FEATURE_RO_COMPAT_QUOTA)
+					 EXT4_FEATURE_RO_COMPAT_QUOTA |\
+					 EXT4_FEATURE_RO_COMPAT_PROJECT)
 
 /*
  * Default values for user and/or group using reserved blocks
@@ -1589,6 +1596,11 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
 #define	EXT4_DEF_RESUID		0
 #define	EXT4_DEF_RESGID		0
 
+/*
+ * Default project ID
+ */
+#define	EXT4_DEF_PROJID		0
+
 #define EXT4_DEF_INODE_READAHEAD_BLKS	32
 
 /*
@@ -2141,6 +2153,7 @@ extern int ext4_zero_partial_blocks(handle_t *handle, struct inode *inode,
 			     loff_t lstart, loff_t lend);
 extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf);
 extern qsize_t *ext4_get_reserved_space(struct inode *inode);
+extern int ext4_get_projid(struct inode *inode, kprojid_t *projid);
 extern void ext4_da_update_reserve_space(struct inode *inode,
 					int used, int quota_claim);
 
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index ac644c3..10ca9dd 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -756,6 +756,11 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
 		inode->i_gid = dir->i_gid;
 	} else
 		inode_init_owner(inode, dir, mode);
+	if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_PROJECT) &&
+	    ext4_test_inode_flag(dir, EXT4_INODE_PROJINHERIT))
+		ei->i_projid = EXT4_I(dir)->i_projid;
+	else
+		ei->i_projid = make_kprojid(&init_user_ns, EXT4_DEF_PROJID);
 	dquot_initialize(inode);
 
 	if (!goal)
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 4df6d01..6e4833f 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3870,6 +3870,14 @@ static inline void ext4_iget_extra_inode(struct inode *inode,
 		EXT4_I(inode)->i_inline_off = 0;
 }
 
+int ext4_get_projid(struct inode *inode, kprojid_t *projid)
+{
+	if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, EXT4_FEATURE_RO_COMPAT_PROJECT))
+		return -EOPNOTSUPP;
+	*projid = EXT4_I(inode)->i_projid;
+	return 0;
+}
+
 struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
 {
 	struct ext4_iloc iloc;
@@ -3881,6 +3889,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
 	int block;
 	uid_t i_uid;
 	gid_t i_gid;
+	projid_t i_projid;
 
 	inode = iget_locked(sb, ino);
 	if (!inode)
@@ -3930,12 +3939,18 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
 	inode->i_mode = le16_to_cpu(raw_inode->i_mode);
 	i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
 	i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
+	if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_PROJECT))
+		i_projid = (projid_t)le32_to_cpu(raw_inode->i_projid);
+	else
+		i_projid = EXT4_DEF_PROJID;
+
 	if (!(test_opt(inode->i_sb, NO_UID32))) {
 		i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
 		i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
 	}
 	i_uid_write(inode, i_uid);
 	i_gid_write(inode, i_gid);
+	ei->i_projid = make_kprojid(&init_user_ns, i_projid);;
 	set_nlink(inode, le16_to_cpu(raw_inode->i_links_count));
 
 	ext4_clear_state_flags(ei);	/* Only relevant on 32-bit archs */
@@ -4165,6 +4180,7 @@ static int ext4_do_update_inode(handle_t *handle,
 	int need_datasync = 0, set_large_file = 0;
 	uid_t i_uid;
 	gid_t i_gid;
+	projid_t i_projid;
 
 	spin_lock(&ei->i_raw_lock);
 
@@ -4177,6 +4193,7 @@ static int ext4_do_update_inode(handle_t *handle,
 	raw_inode->i_mode = cpu_to_le16(inode->i_mode);
 	i_uid = i_uid_read(inode);
 	i_gid = i_gid_read(inode);
+	i_projid = from_kprojid(&init_user_ns, ei->i_projid);
 	if (!(test_opt(inode->i_sb, NO_UID32))) {
 		raw_inode->i_uid_low = cpu_to_le16(low_16_bits(i_uid));
 		raw_inode->i_gid_low = cpu_to_le16(low_16_bits(i_gid));
@@ -4256,6 +4273,18 @@ static int ext4_do_update_inode(handle_t *handle,
 		}
 	}
 
+	BUG_ON(!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
+			EXT4_FEATURE_RO_COMPAT_PROJECT) &&
+	       i_projid != EXT4_DEF_PROJID);
+	if (i_projid != EXT4_DEF_PROJID &&
+	    (EXT4_INODE_SIZE(inode->i_sb) <= EXT4_GOOD_OLD_INODE_SIZE ||
+	     (!EXT4_FITS_IN_INODE(raw_inode, ei, i_projid)))) {
+		spin_unlock(&ei->i_raw_lock);
+		err = -EFBIG;
+		goto out_brelse;
+	}
+	raw_inode->i_projid = cpu_to_le32(i_projid);
+
 	ext4_inode_csum_set(inode, raw_inode, ei);
 
 	spin_unlock(&ei->i_raw_lock);
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 2291923..63a9623 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2938,6 +2938,11 @@ static int ext4_link(struct dentry *old_dentry,
 	if (inode->i_nlink >= EXT4_LINK_MAX)
 		return -EMLINK;
 
+	if ((ext4_test_inode_flag(dir, EXT4_INODE_PROJINHERIT)) &&
+	    (!projid_eq(EXT4_I(dir)->i_projid,
+			EXT4_I(old_dentry->d_inode)->i_projid)))
+		return -EXDEV;
+
 	dquot_initialize(dir);
 
 retry:
@@ -3217,6 +3222,11 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
 	int credits;
 	u8 old_file_type;
 
+	if ((ext4_test_inode_flag(new_dir, EXT4_INODE_PROJINHERIT)) &&
+	    (!projid_eq(EXT4_I(new_dir)->i_projid,
+			EXT4_I(old_dentry->d_inode)->i_projid)))
+		return -EXDEV;
+
 	dquot_initialize(old.dir);
 	dquot_initialize(new.dir);
 
@@ -3395,6 +3405,14 @@ static int ext4_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
 	u8 new_file_type;
 	int retval;
 
+	if ((ext4_test_inode_flag(new_dir, EXT4_INODE_PROJINHERIT) &&
+	     !projid_eq(EXT4_I(new_dir)->i_projid,
+			EXT4_I(old_dentry->d_inode)->i_projid)) ||
+	    (ext4_test_inode_flag(old_dir, EXT4_INODE_PROJINHERIT) &&
+	     !projid_eq(EXT4_I(old_dir)->i_projid,
+			EXT4_I(new_dentry->d_inode)->i_projid)))
+		return -EXDEV;
+
 	dquot_initialize(old.dir);
 	dquot_initialize(new.dir);
 
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index bff3427..04c6cc3 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1073,6 +1073,7 @@ static const struct dquot_operations ext4_quota_operations = {
 	.write_info	= ext4_write_info,
 	.alloc_dquot	= dquot_alloc,
 	.destroy_dquot	= dquot_destroy,
+	.get_projid	= ext4_get_projid,
 };
 
 static const struct quotactl_ops ext4_qctl_operations = {
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index 3735fa0..fcbf647 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -195,6 +195,7 @@ struct inodes_stat_t {
 #define FS_EXTENT_FL			0x00080000 /* Extents */
 #define FS_DIRECTIO_FL			0x00100000 /* Use direct i/o */
 #define FS_NOCOW_FL			0x00800000 /* Do not cow file */
+#define FS_PROJINHERIT_FL		0x20000000 /* Create with parents projid */
 #define FS_RESERVED_FL			0x80000000 /* reserved for ext2 lib */
 
 #define FS_FL_USER_VISIBLE		0x0003DFFF /* User visible flags */
-- 
1.7.1


^ permalink raw reply related

* [v5 3/5] ext4: adds project quota support
From: Li Xi @ 2015-03-20 18:37 UTC (permalink / raw)
  To: linux-fsdevel, linux-ext4, linux-api, tytso, adilger, jack, viro,
	hch, dmonakhov
In-Reply-To: <1426876658-9557-1-git-send-email-lixi@ddn.com>

This patch adds mount options for enabling/disabling project quota
accounting and enforcement. A new specific inode is also used for
project quota accounting.

Signed-off-by: Li Xi <lixi@ddn.com>
Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
Reviewed-by: Jan Kara <jack@suse.cz>
---
 fs/ext4/ext4.h  |    6 +++-
 fs/ext4/super.c |   56 ++++++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 55 insertions(+), 7 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 7acb2da..8ddc723 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1169,7 +1169,8 @@ struct ext4_super_block {
 	__le32	s_overhead_clusters;	/* overhead blocks/clusters in fs */
 	__le32	s_backup_bgs[2];	/* groups with sparse_super2 SBs */
 	__u8	s_encrypt_algos[4];	/* Encryption algorithms in use  */
-	__le32	s_reserved[105];	/* Padding to the end of the block */
+	__le32  s_prj_quota_inum;       /* inode for tracking project quota */
+	__le32	s_reserved[104];	/* Padding to the end of the block */
 	__le32	s_checksum;		/* crc32c(superblock) */
 };
 
@@ -1184,7 +1185,7 @@ struct ext4_super_block {
 #define EXT4_MF_FS_ABORTED	0x0002	/* Fatal error detected */
 
 /* Number of quota types we support */
-#define EXT4_MAXQUOTAS 2
+#define EXT4_MAXQUOTAS 3
 
 /*
  * fourth extended-fs super-block data in memory
@@ -1376,6 +1377,7 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
 		ino == EXT4_BOOT_LOADER_INO ||
 		ino == EXT4_JOURNAL_INO ||
 		ino == EXT4_RESIZE_INO ||
+		ino == le32_to_cpu(EXT4_SB(sb)->s_es->s_prj_quota_inum) ||
 		(ino >= EXT4_FIRST_INO(sb) &&
 		 ino <= le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count));
 }
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 04c6cc3..476e46f 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1036,8 +1036,8 @@ static int bdev_try_to_free_page(struct super_block *sb, struct page *page,
 }
 
 #ifdef CONFIG_QUOTA
-#define QTYPE2NAME(t) ((t) == USRQUOTA ? "user" : "group")
-#define QTYPE2MOPT(on, t) ((t) == USRQUOTA?((on)##USRJQUOTA):((on)##GRPJQUOTA))
+static char *quotatypes[] = INITQFNAMES;
+#define QTYPE2NAME(t) (quotatypes[t])
 
 static int ext4_write_dquot(struct dquot *dquot);
 static int ext4_acquire_dquot(struct dquot *dquot);
@@ -3944,7 +3944,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 		sb->s_qcop = &ext4_qctl_sysfile_operations;
 	else
 		sb->s_qcop = &ext4_qctl_operations;
-	sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP;
+	sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP | QTYPE_MASK_PRJ;
 #endif
 	memcpy(sb->s_uuid, es->s_uuid, sizeof(es->s_uuid));
 
@@ -5040,6 +5040,46 @@ restore_opts:
 	return err;
 }
 
+static int ext4_statfs_project(struct super_block *sb,
+			       kprojid_t projid, struct kstatfs *buf)
+{
+	struct kqid qid;
+	struct dquot *dquot;
+	u64 limit;
+	u64 curblock;
+
+	qid = make_kqid_projid(projid);
+	dquot = dqget(sb, qid);
+	if (!dquot)
+		return -ESRCH;
+	spin_lock(&dq_data_lock);
+
+	limit = dquot->dq_dqb.dqb_bsoftlimit ?
+		dquot->dq_dqb.dqb_bsoftlimit :
+		dquot->dq_dqb.dqb_bhardlimit;
+	if (limit && buf->f_blocks * buf->f_bsize > limit) {
+		curblock = dquot->dq_dqb.dqb_curspace / buf->f_bsize;
+		buf->f_blocks = limit / buf->f_bsize;
+		buf->f_bfree = buf->f_bavail =
+			(buf->f_blocks > curblock) ?
+			 (buf->f_blocks - curblock) : 0;
+	}
+
+	limit = dquot->dq_dqb.dqb_isoftlimit ?
+		dquot->dq_dqb.dqb_isoftlimit :
+		dquot->dq_dqb.dqb_ihardlimit;
+	if (limit && buf->f_files > limit) {
+		buf->f_files = limit;
+		buf->f_ffree =
+			(buf->f_files > dquot->dq_dqb.dqb_curinodes) ?
+			 (buf->f_files - dquot->dq_dqb.dqb_curinodes) : 0;
+	}
+
+	spin_unlock(&dq_data_lock);
+	dqput(dquot);
+	return 0;
+}
+
 static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
 	struct super_block *sb = dentry->d_sb;
@@ -5048,6 +5088,7 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
 	ext4_fsblk_t overhead = 0, resv_blocks;
 	u64 fsid;
 	s64 bfree;
+	struct inode *inode = dentry->d_inode;
 	resv_blocks = EXT4_C2B(sbi, atomic64_read(&sbi->s_resv_clusters));
 
 	if (!test_opt(sb, MINIX_DF))
@@ -5072,6 +5113,9 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
 	buf->f_fsid.val[0] = fsid & 0xFFFFFFFFUL;
 	buf->f_fsid.val[1] = (fsid >> 32) & 0xFFFFFFFFUL;
 
+	if (ext4_test_inode_flag(inode, EXT4_INODE_PROJINHERIT) &&
+	    sb_has_quota_limits_enabled(sb, PRJQUOTA))
+		ext4_statfs_project(sb, EXT4_I(inode)->i_projid, buf);
 	return 0;
 }
 
@@ -5236,7 +5280,8 @@ static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
 	struct inode *qf_inode;
 	unsigned long qf_inums[EXT4_MAXQUOTAS] = {
 		le32_to_cpu(EXT4_SB(sb)->s_es->s_usr_quota_inum),
-		le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum)
+		le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum),
+		le32_to_cpu(EXT4_SB(sb)->s_es->s_prj_quota_inum)
 	};
 
 	BUG_ON(!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA));
@@ -5264,7 +5309,8 @@ static int ext4_enable_quotas(struct super_block *sb)
 	int type, err = 0;
 	unsigned long qf_inums[EXT4_MAXQUOTAS] = {
 		le32_to_cpu(EXT4_SB(sb)->s_es->s_usr_quota_inum),
-		le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum)
+		le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum),
+		le32_to_cpu(EXT4_SB(sb)->s_es->s_prj_quota_inum)
 	};
 
 	sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE;
-- 
1.7.1


^ permalink raw reply related

* [v11 0/5] ext4: add project quota support
From: Li Xi @ 2015-03-20 18:41 UTC (permalink / raw)
  To: linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
	linux-ext4-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA, tytso-3s7WtUTddSA,
	adilger-m1MBpc4rdrD3fQ9qLvQP4Q, jack-AlSwsSmVLrQ,
	viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn, hch-wEGCiKHe2LqWVfeAwA7xHQ,
	dmonakhov-GEFAQzZX7r8dnm+yROfE0A

The following patches propose an implementation of project quota
support for ext4. A project is an aggregate of unrelated inodes
which might scatter in different directories. Inodes that belong
to the same project possess an identical identification i.e.
'project ID', just like every inode has its user/group
identification. The following patches add project quota as
supplement to the former uer/group quota types.

The semantics of ext4 project quota is consistent with XFS. Each
directory can have EXT4_INODE_PROJINHERIT flag set. When the
EXT4_INODE_PROJINHERIT flag of a parent directory is not set, a
newly created inode under that directory will have a default project
ID (i.e. 0). And its EXT4_INODE_PROJINHERIT flag is not set either.
When this flag is set on a directory, following rules will be kept:

1) The newly created inode under that directory will inherit both
the EXT4_INODE_PROJINHERIT flag and the project ID from its parent
directory.

2) Hard-linking a inode with different project ID into that directory
will fail with errno EXDEV.

3) Renaming a inode with different project ID into that directory
will fail with errno EXDEV. However, 'mv' command will detect this
failure and copy the renamed inode to a new inode in the directory.
Thus, this new inode will inherit both the project ID and
EXT4_INODE_PROJINHERIT flag.

4) If the project quota of that ID is being enforced, statfs() on
that directory will take the quotas as another upper limits along
with the capacity of the file system, i.e. the total block/inode
number will be the minimum of the quota limits and file system
capacity.

Changelog:
* v11 <- v10:
 - Remove project quota mount option;
 - Fix permission check when setting project ID
* v10 <- v9:
 - Remove non-journaled project quota interface;
 - Only allow admin to read project quota info;
 - Cleanup FS_IOC_FSSETXATTR/FS_IOC_FSGETXATTR interface.
* v9 <- v8:
 - Remove non-journaled project quota;
 - Rebase to newest dev branch of ext4 repository (3.19.0-rc3).
* v8 <- v7:
 - Rebase to newest dev branch of ext4 repository (3.18.0_rc3).
* v7 <- v6:
 - Map ext4 inode flags to xflags of struct fsxattr;
 - Add patch to cleanup ext4 inode flag definitions.
* v6 <- v5:
 - Add project ID check for cross rename;
 - Remove patch of EXT4_IOC_GETPROJECT/EXT4_IOC_SETPROJECT ioctl
* v5 <- v4:
 - Check project feature when set/get project ID;
 - Do not check project feature for project quota;
 - Add support of FS_IOC_FSSETXATTR/FS_IOC_FSGETXATTR.
* v4 <- v3:
 - Do not check project feature when set/get project ID;
 - Use EXT4_MAXQUOTAS instead of MAXQUOTAS in ext4 patches;
 - Remove unnecessary change of fs/quota/dquot.c;
 - Remove CONFIG_QUOTA_PROJECT.
* v3 <- v2:
 - Add EXT4_INODE_PROJINHERIT semantics.
* v2 <- v1:
 - Add ioctl interface for setting/getting project;
 - Add EXT4_FEATURE_RO_COMPAT_PROJECT;
 - Add get_projid() method in struct dquot_operations;
 - Add error check of ext4_inode_projid_set/get().

v10: http://www.spinics.net/lists/linux-ext4/msg47413.html
v9: http://www.spinics.net/lists/linux-ext4/msg47326.html
v8: http://www.spinics.net/lists/linux-ext4/msg46545.html
v7: http://www.spinics.net/lists/linux-fsdevel/msg80404.html
v6: http://www.spinics.net/lists/linux-fsdevel/msg80022.html
v5: http://www.spinics.net/lists/linux-api/msg04840.html
v4: http://lwn.net/Articles/612972/
v3: http://www.spinics.net/lists/linux-ext4/msg45184.html
v2: http://www.spinics.net/lists/linux-ext4/msg44695.html
v1: http://article.gmane.org/gmane.comp.file-systems.ext4/45153

Any comments or feedbacks are appreciated.

Regards,
                                         - Li Xi

Li Xi (5):
  vfs: adds general codes to enforces project quota limits
  ext4: adds project ID support
  ext4: adds project quota support
  ext4: adds FS_IOC_FSSETXATTR/FS_IOC_FSGETXATTR interface support
  ext4: cleanup inode flag definitions

 fs/ext4/ext4.h             |   86 +++++++----
 fs/ext4/ialloc.c           |    5 +
 fs/ext4/inode.c            |   29 ++++
 fs/ext4/ioctl.c            |  366 +++++++++++++++++++++++++++++++++-----------
 fs/ext4/namei.c            |   18 +++
 fs/ext4/super.c            |   57 +++++++-
 fs/quota/dquot.c           |   35 ++++-
 fs/quota/quota.c           |    5 +-
 fs/quota/quotaio_v2.h      |    6 +-
 fs/xfs/xfs_fs.h            |   47 ++----
 include/linux/quota.h      |    2 +
 include/uapi/linux/fs.h    |   33 ++++
 include/uapi/linux/quota.h |    6 +-
 13 files changed, 532 insertions(+), 163 deletions(-)

^ permalink raw reply

* [v11 1/5] vfs: adds general codes to enforces project quota limits
From: Li Xi @ 2015-03-20 18:41 UTC (permalink / raw)
  To: linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
	linux-ext4-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA, tytso-3s7WtUTddSA,
	adilger-m1MBpc4rdrD3fQ9qLvQP4Q, jack-AlSwsSmVLrQ,
	viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn, hch-wEGCiKHe2LqWVfeAwA7xHQ,
	dmonakhov-GEFAQzZX7r8dnm+yROfE0A
In-Reply-To: <1426876890-9914-1-git-send-email-lixi-LfVdkaOWEx8@public.gmane.org>

This patch adds support for a new quota type PRJQUOTA for project quota
enforcement. Also a new method get_projid() is added into dquot_operations
structure.

Signed-off-by: Li Xi <lixi-LfVdkaOWEx8@public.gmane.org>
Signed-off-by: Dmitry Monakhov <dmonakhov-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
Reviewed-by: Jan Kara <jack-AlSwsSmVLrQ@public.gmane.org>
---
 fs/quota/dquot.c           |   35 ++++++++++++++++++++++++++++++-----
 fs/quota/quota.c           |    5 ++++-
 fs/quota/quotaio_v2.h      |    6 ++++--
 include/linux/quota.h      |    2 ++
 include/uapi/linux/quota.h |    6 ++++--
 5 files changed, 44 insertions(+), 10 deletions(-)

diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index 8f0acef..a02bb68 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -1159,8 +1159,8 @@ static int need_print_warning(struct dquot_warn *warn)
 			return uid_eq(current_fsuid(), warn->w_dq_id.uid);
 		case GRPQUOTA:
 			return in_group_p(warn->w_dq_id.gid);
-		case PRJQUOTA:	/* Never taken... Just make gcc happy */
-			return 0;
+		case PRJQUOTA:
+			return 1;
 	}
 	return 0;
 }
@@ -1399,6 +1399,9 @@ static void __dquot_initialize(struct inode *inode, int type)
 	/* First get references to structures we might need. */
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		struct kqid qid;
+		kprojid_t projid;
+		int rc;
+
 		got[cnt] = NULL;
 		if (type != -1 && cnt != type)
 			continue;
@@ -1409,6 +1412,10 @@ static void __dquot_initialize(struct inode *inode, int type)
 		 */
 		if (i_dquot(inode)[cnt])
 			continue;
+
+		if (!sb_has_quota_active(sb, cnt))
+			continue;
+
 		init_needed = 1;
 
 		switch (cnt) {
@@ -1418,6 +1425,12 @@ static void __dquot_initialize(struct inode *inode, int type)
 		case GRPQUOTA:
 			qid = make_kqid_gid(inode->i_gid);
 			break;
+		case PRJQUOTA:
+			rc = inode->i_sb->dq_op->get_projid(inode, &projid);
+			if (rc)
+				continue;
+			qid = make_kqid_projid(projid);
+			break;
 		}
 		got[cnt] = dqget(sb, qid);
 	}
@@ -2161,7 +2174,8 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
 		error = -EROFS;
 		goto out_fmt;
 	}
-	if (!sb->s_op->quota_write || !sb->s_op->quota_read) {
+	if (!sb->s_op->quota_write || !sb->s_op->quota_read ||
+	    (type == PRJQUOTA && sb->dq_op->get_projid == NULL)) {
 		error = -EINVAL;
 		goto out_fmt;
 	}
@@ -2402,8 +2416,19 @@ static void do_get_dqblk(struct dquot *dquot, struct fs_disk_quota *di)
 
 	memset(di, 0, sizeof(*di));
 	di->d_version = FS_DQUOT_VERSION;
-	di->d_flags = dquot->dq_id.type == USRQUOTA ?
-			FS_USER_QUOTA : FS_GROUP_QUOTA;
+	switch (dquot->dq_id.type) {
+	case USRQUOTA:
+		di->d_flags = FS_USER_QUOTA;
+		break;
+	case GRPQUOTA:
+		di->d_flags = FS_GROUP_QUOTA;
+		break;
+	case PRJQUOTA:
+		di->d_flags = FS_PROJ_QUOTA;
+		break;
+	default:
+		BUG();
+	}
 	di->d_id = from_kqid_munged(current_user_ns(), dquot->dq_id);
 
 	spin_lock(&dq_data_lock);
diff --git a/fs/quota/quota.c b/fs/quota/quota.c
index 2aa4151..33b30b1 100644
--- a/fs/quota/quota.c
+++ b/fs/quota/quota.c
@@ -30,7 +30,10 @@ static int check_quotactl_permission(struct super_block *sb, int type, int cmd,
 	case Q_XGETQSTATV:
 	case Q_XQUOTASYNC:
 		break;
-	/* allow to query information for dquots we "own" */
+	/*
+	 * allow to query information for dquots we "own"
+	 * always allow querying project quota
+	 */
 	case Q_GETQUOTA:
 	case Q_XGETQUOTA:
 		if ((type == USRQUOTA && uid_eq(current_euid(), make_kuid(current_user_ns(), id))) ||
diff --git a/fs/quota/quotaio_v2.h b/fs/quota/quotaio_v2.h
index f1966b4..4e95430 100644
--- a/fs/quota/quotaio_v2.h
+++ b/fs/quota/quotaio_v2.h
@@ -13,12 +13,14 @@
  */
 #define V2_INITQMAGICS {\
 	0xd9c01f11,	/* USRQUOTA */\
-	0xd9c01927	/* GRPQUOTA */\
+	0xd9c01927,	/* GRPQUOTA */\
+	0xd9c03f14,	/* PRJQUOTA */\
 }
 
 #define V2_INITQVERSIONS {\
 	1,		/* USRQUOTA */\
-	1		/* GRPQUOTA */\
+	1,		/* GRPQUOTA */\
+	1,		/* PRJQUOTA */\
 }
 
 /* First generic header */
diff --git a/include/linux/quota.h b/include/linux/quota.h
index 50978b7..ba51f7e 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -50,6 +50,7 @@
 
 #undef USRQUOTA
 #undef GRPQUOTA
+#undef PRJQUOTA
 enum quota_type {
 	USRQUOTA = 0,		/* element used for user quotas */
 	GRPQUOTA = 1,		/* element used for group quotas */
@@ -317,6 +318,7 @@ struct dquot_operations {
 	/* get reserved quota for delayed alloc, value returned is managed by
 	 * quota code only */
 	qsize_t *(*get_reserved_space) (struct inode *);
+	int (*get_projid) (struct inode *, kprojid_t *);/* Get project ID */
 };
 
 struct path;
diff --git a/include/uapi/linux/quota.h b/include/uapi/linux/quota.h
index 3b6cfbe..b2d9486 100644
--- a/include/uapi/linux/quota.h
+++ b/include/uapi/linux/quota.h
@@ -36,11 +36,12 @@
 #include <linux/errno.h>
 #include <linux/types.h>
 
-#define __DQUOT_VERSION__	"dquot_6.5.2"
+#define __DQUOT_VERSION__	"dquot_6.6.0"
 
-#define MAXQUOTAS 2
+#define MAXQUOTAS 3
 #define USRQUOTA  0		/* element used for user quotas */
 #define GRPQUOTA  1		/* element used for group quotas */
+#define PRJQUOTA  2		/* element used for project quotas */
 
 /*
  * Definitions for the default names of the quotas files.
@@ -48,6 +49,7 @@
 #define INITQFNAMES { \
 	"user",    /* USRQUOTA */ \
 	"group",   /* GRPQUOTA */ \
+	"project", /* PRJQUOTA */ \
 	"undefined", \
 };
 
-- 
1.7.1

^ permalink raw reply related

* [v11 2/5] ext4: adds project ID support
From: Li Xi @ 2015-03-20 18:41 UTC (permalink / raw)
  To: linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
	linux-ext4-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA, tytso-3s7WtUTddSA,
	adilger-m1MBpc4rdrD3fQ9qLvQP4Q, jack-AlSwsSmVLrQ,
	viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn, hch-wEGCiKHe2LqWVfeAwA7xHQ,
	dmonakhov-GEFAQzZX7r8dnm+yROfE0A
In-Reply-To: <1426876890-9914-1-git-send-email-lixi-LfVdkaOWEx8@public.gmane.org>

This patch adds a new internal field of ext4 inode to save project
identifier. Also a new flag EXT4_INODE_PROJINHERIT is added for
inheriting project ID from parent directory.

Signed-off-by: Li Xi <lixi-LfVdkaOWEx8@public.gmane.org>
Reviewed-by: Jan Kara <jack-AlSwsSmVLrQ@public.gmane.org>
---
 fs/ext4/ext4.h          |   21 +++++++++++++++++----
 fs/ext4/ialloc.c        |    5 +++++
 fs/ext4/inode.c         |   29 +++++++++++++++++++++++++++++
 fs/ext4/namei.c         |   18 ++++++++++++++++++
 fs/ext4/super.c         |    1 +
 include/uapi/linux/fs.h |    1 +
 6 files changed, 71 insertions(+), 4 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 7fec2ef..7acb2da 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -378,16 +378,18 @@ struct flex_groups {
 #define EXT4_EA_INODE_FL	        0x00200000 /* Inode used for large EA */
 #define EXT4_EOFBLOCKS_FL		0x00400000 /* Blocks allocated beyond EOF */
 #define EXT4_INLINE_DATA_FL		0x10000000 /* Inode has inline data. */
+#define EXT4_PROJINHERIT_FL		0x20000000 /* Create with parents projid */
 #define EXT4_RESERVED_FL		0x80000000 /* reserved for ext4 lib */
 
-#define EXT4_FL_USER_VISIBLE		0x004BDFFF /* User visible flags */
-#define EXT4_FL_USER_MODIFIABLE		0x004380FF /* User modifiable flags */
+#define EXT4_FL_USER_VISIBLE		0x204BDFFF /* User visible flags */
+#define EXT4_FL_USER_MODIFIABLE		0x204380FF /* User modifiable flags */
 
 /* Flags that should be inherited by new inodes from their parent. */
 #define EXT4_FL_INHERITED (EXT4_SECRM_FL | EXT4_UNRM_FL | EXT4_COMPR_FL |\
 			   EXT4_SYNC_FL | EXT4_NODUMP_FL | EXT4_NOATIME_FL |\
 			   EXT4_NOCOMPR_FL | EXT4_JOURNAL_DATA_FL |\
-			   EXT4_NOTAIL_FL | EXT4_DIRSYNC_FL)
+			   EXT4_NOTAIL_FL | EXT4_DIRSYNC_FL |\
+			   EXT4_PROJINHERIT_FL)
 
 /* Flags that are appropriate for regular files (all but dir-specific ones). */
 #define EXT4_REG_FLMASK (~(EXT4_DIRSYNC_FL | EXT4_TOPDIR_FL))
@@ -435,6 +437,7 @@ enum {
 	EXT4_INODE_EA_INODE	= 21,	/* Inode used for large EA */
 	EXT4_INODE_EOFBLOCKS	= 22,	/* Blocks allocated beyond EOF */
 	EXT4_INODE_INLINE_DATA	= 28,	/* Data in inode. */
+	EXT4_INODE_PROJINHERIT	= 29,	/* Create with parents projid */
 	EXT4_INODE_RESERVED	= 31,	/* reserved for ext4 lib */
 };
 
@@ -684,6 +687,7 @@ struct ext4_inode {
 	__le32  i_crtime;       /* File Creation time */
 	__le32  i_crtime_extra; /* extra FileCreationtime (nsec << 2 | epoch) */
 	__le32  i_version_hi;	/* high 32 bits for 64-bit version */
+	__le32  i_projid;	/* Project ID */
 };
 
 struct move_extent {
@@ -939,6 +943,7 @@ struct ext4_inode_info {
 
 	/* Precomputed uuid+inum+igen checksum for seeding inode checksums */
 	__u32 i_csum_seed;
+	kprojid_t i_projid;
 };
 
 /*
@@ -1531,6 +1536,7 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
  */
 #define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM	0x0400
 #define EXT4_FEATURE_RO_COMPAT_READONLY		0x1000
+#define EXT4_FEATURE_RO_COMPAT_PROJECT		0x2000
 
 #define EXT4_FEATURE_INCOMPAT_COMPRESSION	0x0001
 #define EXT4_FEATURE_INCOMPAT_FILETYPE		0x0002
@@ -1581,7 +1587,8 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
 					 EXT4_FEATURE_RO_COMPAT_HUGE_FILE |\
 					 EXT4_FEATURE_RO_COMPAT_BIGALLOC |\
 					 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM|\
-					 EXT4_FEATURE_RO_COMPAT_QUOTA)
+					 EXT4_FEATURE_RO_COMPAT_QUOTA |\
+					 EXT4_FEATURE_RO_COMPAT_PROJECT)
 
 /*
  * Default values for user and/or group using reserved blocks
@@ -1589,6 +1596,11 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
 #define	EXT4_DEF_RESUID		0
 #define	EXT4_DEF_RESGID		0
 
+/*
+ * Default project ID
+ */
+#define	EXT4_DEF_PROJID		0
+
 #define EXT4_DEF_INODE_READAHEAD_BLKS	32
 
 /*
@@ -2141,6 +2153,7 @@ extern int ext4_zero_partial_blocks(handle_t *handle, struct inode *inode,
 			     loff_t lstart, loff_t lend);
 extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf);
 extern qsize_t *ext4_get_reserved_space(struct inode *inode);
+extern int ext4_get_projid(struct inode *inode, kprojid_t *projid);
 extern void ext4_da_update_reserve_space(struct inode *inode,
 					int used, int quota_claim);
 
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index ac644c3..10ca9dd 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -756,6 +756,11 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
 		inode->i_gid = dir->i_gid;
 	} else
 		inode_init_owner(inode, dir, mode);
+	if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_PROJECT) &&
+	    ext4_test_inode_flag(dir, EXT4_INODE_PROJINHERIT))
+		ei->i_projid = EXT4_I(dir)->i_projid;
+	else
+		ei->i_projid = make_kprojid(&init_user_ns, EXT4_DEF_PROJID);
 	dquot_initialize(inode);
 
 	if (!goal)
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 4df6d01..6e4833f 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3870,6 +3870,14 @@ static inline void ext4_iget_extra_inode(struct inode *inode,
 		EXT4_I(inode)->i_inline_off = 0;
 }
 
+int ext4_get_projid(struct inode *inode, kprojid_t *projid)
+{
+	if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, EXT4_FEATURE_RO_COMPAT_PROJECT))
+		return -EOPNOTSUPP;
+	*projid = EXT4_I(inode)->i_projid;
+	return 0;
+}
+
 struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
 {
 	struct ext4_iloc iloc;
@@ -3881,6 +3889,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
 	int block;
 	uid_t i_uid;
 	gid_t i_gid;
+	projid_t i_projid;
 
 	inode = iget_locked(sb, ino);
 	if (!inode)
@@ -3930,12 +3939,18 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
 	inode->i_mode = le16_to_cpu(raw_inode->i_mode);
 	i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
 	i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
+	if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_PROJECT))
+		i_projid = (projid_t)le32_to_cpu(raw_inode->i_projid);
+	else
+		i_projid = EXT4_DEF_PROJID;
+
 	if (!(test_opt(inode->i_sb, NO_UID32))) {
 		i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
 		i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
 	}
 	i_uid_write(inode, i_uid);
 	i_gid_write(inode, i_gid);
+	ei->i_projid = make_kprojid(&init_user_ns, i_projid);;
 	set_nlink(inode, le16_to_cpu(raw_inode->i_links_count));
 
 	ext4_clear_state_flags(ei);	/* Only relevant on 32-bit archs */
@@ -4165,6 +4180,7 @@ static int ext4_do_update_inode(handle_t *handle,
 	int need_datasync = 0, set_large_file = 0;
 	uid_t i_uid;
 	gid_t i_gid;
+	projid_t i_projid;
 
 	spin_lock(&ei->i_raw_lock);
 
@@ -4177,6 +4193,7 @@ static int ext4_do_update_inode(handle_t *handle,
 	raw_inode->i_mode = cpu_to_le16(inode->i_mode);
 	i_uid = i_uid_read(inode);
 	i_gid = i_gid_read(inode);
+	i_projid = from_kprojid(&init_user_ns, ei->i_projid);
 	if (!(test_opt(inode->i_sb, NO_UID32))) {
 		raw_inode->i_uid_low = cpu_to_le16(low_16_bits(i_uid));
 		raw_inode->i_gid_low = cpu_to_le16(low_16_bits(i_gid));
@@ -4256,6 +4273,18 @@ static int ext4_do_update_inode(handle_t *handle,
 		}
 	}
 
+	BUG_ON(!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
+			EXT4_FEATURE_RO_COMPAT_PROJECT) &&
+	       i_projid != EXT4_DEF_PROJID);
+	if (i_projid != EXT4_DEF_PROJID &&
+	    (EXT4_INODE_SIZE(inode->i_sb) <= EXT4_GOOD_OLD_INODE_SIZE ||
+	     (!EXT4_FITS_IN_INODE(raw_inode, ei, i_projid)))) {
+		spin_unlock(&ei->i_raw_lock);
+		err = -EFBIG;
+		goto out_brelse;
+	}
+	raw_inode->i_projid = cpu_to_le32(i_projid);
+
 	ext4_inode_csum_set(inode, raw_inode, ei);
 
 	spin_unlock(&ei->i_raw_lock);
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 2291923..63a9623 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2938,6 +2938,11 @@ static int ext4_link(struct dentry *old_dentry,
 	if (inode->i_nlink >= EXT4_LINK_MAX)
 		return -EMLINK;
 
+	if ((ext4_test_inode_flag(dir, EXT4_INODE_PROJINHERIT)) &&
+	    (!projid_eq(EXT4_I(dir)->i_projid,
+			EXT4_I(old_dentry->d_inode)->i_projid)))
+		return -EXDEV;
+
 	dquot_initialize(dir);
 
 retry:
@@ -3217,6 +3222,11 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
 	int credits;
 	u8 old_file_type;
 
+	if ((ext4_test_inode_flag(new_dir, EXT4_INODE_PROJINHERIT)) &&
+	    (!projid_eq(EXT4_I(new_dir)->i_projid,
+			EXT4_I(old_dentry->d_inode)->i_projid)))
+		return -EXDEV;
+
 	dquot_initialize(old.dir);
 	dquot_initialize(new.dir);
 
@@ -3395,6 +3405,14 @@ static int ext4_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
 	u8 new_file_type;
 	int retval;
 
+	if ((ext4_test_inode_flag(new_dir, EXT4_INODE_PROJINHERIT) &&
+	     !projid_eq(EXT4_I(new_dir)->i_projid,
+			EXT4_I(old_dentry->d_inode)->i_projid)) ||
+	    (ext4_test_inode_flag(old_dir, EXT4_INODE_PROJINHERIT) &&
+	     !projid_eq(EXT4_I(old_dir)->i_projid,
+			EXT4_I(new_dentry->d_inode)->i_projid)))
+		return -EXDEV;
+
 	dquot_initialize(old.dir);
 	dquot_initialize(new.dir);
 
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index bff3427..04c6cc3 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1073,6 +1073,7 @@ static const struct dquot_operations ext4_quota_operations = {
 	.write_info	= ext4_write_info,
 	.alloc_dquot	= dquot_alloc,
 	.destroy_dquot	= dquot_destroy,
+	.get_projid	= ext4_get_projid,
 };
 
 static const struct quotactl_ops ext4_qctl_operations = {
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index 3735fa0..fcbf647 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -195,6 +195,7 @@ struct inodes_stat_t {
 #define FS_EXTENT_FL			0x00080000 /* Extents */
 #define FS_DIRECTIO_FL			0x00100000 /* Use direct i/o */
 #define FS_NOCOW_FL			0x00800000 /* Do not cow file */
+#define FS_PROJINHERIT_FL		0x20000000 /* Create with parents projid */
 #define FS_RESERVED_FL			0x80000000 /* reserved for ext2 lib */
 
 #define FS_FL_USER_VISIBLE		0x0003DFFF /* User visible flags */
-- 
1.7.1

^ permalink raw reply related

* [v11 3/5] ext4: adds project quota support
From: Li Xi @ 2015-03-20 18:41 UTC (permalink / raw)
  To: linux-fsdevel-u79uwXL29TY76Z2rM5mHXA,
	linux-ext4-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA, tytso-3s7WtUTddSA,
	adilger-m1MBpc4rdrD3fQ9qLvQP4Q, jack-AlSwsSmVLrQ,
	viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn, hch-wEGCiKHe2LqWVfeAwA7xHQ,
	dmonakhov-GEFAQzZX7r8dnm+yROfE0A
In-Reply-To: <1426876890-9914-1-git-send-email-lixi-LfVdkaOWEx8@public.gmane.org>

This patch adds mount options for enabling/disabling project quota
accounting and enforcement. A new specific inode is also used for
project quota accounting.

Signed-off-by: Li Xi <lixi-LfVdkaOWEx8@public.gmane.org>
Signed-off-by: Dmitry Monakhov <dmonakhov-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
Reviewed-by: Jan Kara <jack-AlSwsSmVLrQ@public.gmane.org>
---
 fs/ext4/ext4.h  |    6 +++-
 fs/ext4/super.c |   56 ++++++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 55 insertions(+), 7 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 7acb2da..8ddc723 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1169,7 +1169,8 @@ struct ext4_super_block {
 	__le32	s_overhead_clusters;	/* overhead blocks/clusters in fs */
 	__le32	s_backup_bgs[2];	/* groups with sparse_super2 SBs */
 	__u8	s_encrypt_algos[4];	/* Encryption algorithms in use  */
-	__le32	s_reserved[105];	/* Padding to the end of the block */
+	__le32  s_prj_quota_inum;       /* inode for tracking project quota */
+	__le32	s_reserved[104];	/* Padding to the end of the block */
 	__le32	s_checksum;		/* crc32c(superblock) */
 };
 
@@ -1184,7 +1185,7 @@ struct ext4_super_block {
 #define EXT4_MF_FS_ABORTED	0x0002	/* Fatal error detected */
 
 /* Number of quota types we support */
-#define EXT4_MAXQUOTAS 2
+#define EXT4_MAXQUOTAS 3
 
 /*
  * fourth extended-fs super-block data in memory
@@ -1376,6 +1377,7 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
 		ino == EXT4_BOOT_LOADER_INO ||
 		ino == EXT4_JOURNAL_INO ||
 		ino == EXT4_RESIZE_INO ||
+		ino == le32_to_cpu(EXT4_SB(sb)->s_es->s_prj_quota_inum) ||
 		(ino >= EXT4_FIRST_INO(sb) &&
 		 ino <= le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count));
 }
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 04c6cc3..476e46f 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1036,8 +1036,8 @@ static int bdev_try_to_free_page(struct super_block *sb, struct page *page,
 }
 
 #ifdef CONFIG_QUOTA
-#define QTYPE2NAME(t) ((t) == USRQUOTA ? "user" : "group")
-#define QTYPE2MOPT(on, t) ((t) == USRQUOTA?((on)##USRJQUOTA):((on)##GRPJQUOTA))
+static char *quotatypes[] = INITQFNAMES;
+#define QTYPE2NAME(t) (quotatypes[t])
 
 static int ext4_write_dquot(struct dquot *dquot);
 static int ext4_acquire_dquot(struct dquot *dquot);
@@ -3944,7 +3944,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 		sb->s_qcop = &ext4_qctl_sysfile_operations;
 	else
 		sb->s_qcop = &ext4_qctl_operations;
-	sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP;
+	sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP | QTYPE_MASK_PRJ;
 #endif
 	memcpy(sb->s_uuid, es->s_uuid, sizeof(es->s_uuid));
 
@@ -5040,6 +5040,46 @@ restore_opts:
 	return err;
 }
 
+static int ext4_statfs_project(struct super_block *sb,
+			       kprojid_t projid, struct kstatfs *buf)
+{
+	struct kqid qid;
+	struct dquot *dquot;
+	u64 limit;
+	u64 curblock;
+
+	qid = make_kqid_projid(projid);
+	dquot = dqget(sb, qid);
+	if (!dquot)
+		return -ESRCH;
+	spin_lock(&dq_data_lock);
+
+	limit = dquot->dq_dqb.dqb_bsoftlimit ?
+		dquot->dq_dqb.dqb_bsoftlimit :
+		dquot->dq_dqb.dqb_bhardlimit;
+	if (limit && buf->f_blocks * buf->f_bsize > limit) {
+		curblock = dquot->dq_dqb.dqb_curspace / buf->f_bsize;
+		buf->f_blocks = limit / buf->f_bsize;
+		buf->f_bfree = buf->f_bavail =
+			(buf->f_blocks > curblock) ?
+			 (buf->f_blocks - curblock) : 0;
+	}
+
+	limit = dquot->dq_dqb.dqb_isoftlimit ?
+		dquot->dq_dqb.dqb_isoftlimit :
+		dquot->dq_dqb.dqb_ihardlimit;
+	if (limit && buf->f_files > limit) {
+		buf->f_files = limit;
+		buf->f_ffree =
+			(buf->f_files > dquot->dq_dqb.dqb_curinodes) ?
+			 (buf->f_files - dquot->dq_dqb.dqb_curinodes) : 0;
+	}
+
+	spin_unlock(&dq_data_lock);
+	dqput(dquot);
+	return 0;
+}
+
 static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
 	struct super_block *sb = dentry->d_sb;
@@ -5048,6 +5088,7 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
 	ext4_fsblk_t overhead = 0, resv_blocks;
 	u64 fsid;
 	s64 bfree;
+	struct inode *inode = dentry->d_inode;
 	resv_blocks = EXT4_C2B(sbi, atomic64_read(&sbi->s_resv_clusters));
 
 	if (!test_opt(sb, MINIX_DF))
@@ -5072,6 +5113,9 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
 	buf->f_fsid.val[0] = fsid & 0xFFFFFFFFUL;
 	buf->f_fsid.val[1] = (fsid >> 32) & 0xFFFFFFFFUL;
 
+	if (ext4_test_inode_flag(inode, EXT4_INODE_PROJINHERIT) &&
+	    sb_has_quota_limits_enabled(sb, PRJQUOTA))
+		ext4_statfs_project(sb, EXT4_I(inode)->i_projid, buf);
 	return 0;
 }
 
@@ -5236,7 +5280,8 @@ static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
 	struct inode *qf_inode;
 	unsigned long qf_inums[EXT4_MAXQUOTAS] = {
 		le32_to_cpu(EXT4_SB(sb)->s_es->s_usr_quota_inum),
-		le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum)
+		le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum),
+		le32_to_cpu(EXT4_SB(sb)->s_es->s_prj_quota_inum)
 	};
 
 	BUG_ON(!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA));
@@ -5264,7 +5309,8 @@ static int ext4_enable_quotas(struct super_block *sb)
 	int type, err = 0;
 	unsigned long qf_inums[EXT4_MAXQUOTAS] = {
 		le32_to_cpu(EXT4_SB(sb)->s_es->s_usr_quota_inum),
-		le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum)
+		le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum),
+		le32_to_cpu(EXT4_SB(sb)->s_es->s_prj_quota_inum)
 	};
 
 	sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE;
-- 
1.7.1

^ permalink raw reply related

* [v11 4/5] ext4: adds FS_IOC_FSSETXATTR/FS_IOC_FSGETXATTR interface support
From: Li Xi @ 2015-03-20 18:41 UTC (permalink / raw)
  To: linux-fsdevel, linux-ext4, linux-api, tytso, adilger, jack, viro,
	hch, dmonakhov
In-Reply-To: <1426876890-9914-1-git-send-email-lixi@ddn.com>

This patch adds FS_IOC_FSSETXATTR/FS_IOC_FSGETXATTR ioctl interface
support for ext4. The interface is kept consistent with
XFS_IOC_FSGETXATTR/XFS_IOC_FSGETXATTR.

Signed-off-by: Li Xi <lixi@ddn.com>
---
 fs/ext4/ext4.h          |    9 ++
 fs/ext4/ioctl.c         |  366 ++++++++++++++++++++++++++++++++++++-----------
 fs/xfs/xfs_fs.h         |   47 +++----
 include/uapi/linux/fs.h |   32 ++++
 4 files changed, 337 insertions(+), 117 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 8ddc723..377fec0 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -384,6 +384,13 @@ struct flex_groups {
 #define EXT4_FL_USER_VISIBLE		0x204BDFFF /* User visible flags */
 #define EXT4_FL_USER_MODIFIABLE		0x204380FF /* User modifiable flags */
 
+#define EXT4_FL_XFLAG_VISIBLE		(EXT4_SYNC_FL | \
+					 EXT4_IMMUTABLE_FL | \
+					 EXT4_APPEND_FL | \
+					 EXT4_NODUMP_FL | \
+					 EXT4_NOATIME_FL | \
+					 EXT4_PROJINHERIT_FL)
+
 /* Flags that should be inherited by new inodes from their parent. */
 #define EXT4_FL_INHERITED (EXT4_SECRM_FL | EXT4_UNRM_FL | EXT4_COMPR_FL |\
 			   EXT4_SYNC_FL | EXT4_NODUMP_FL | EXT4_NOATIME_FL |\
@@ -606,6 +613,8 @@ enum {
 #define EXT4_IOC_RESIZE_FS		_IOW('f', 16, __u64)
 #define EXT4_IOC_SWAP_BOOT		_IO('f', 17)
 #define EXT4_IOC_PRECACHE_EXTENTS	_IO('f', 18)
+#define EXT4_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
+#define EXT4_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
 
 #if defined(__KERNEL__) && defined(CONFIG_COMPAT)
 /*
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index f58a0d1..ba8b87d 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -14,6 +14,8 @@
 #include <linux/compat.h>
 #include <linux/mount.h>
 #include <linux/file.h>
+#include <linux/quotaops.h>
+#include <linux/quota.h>
 #include <asm/uaccess.h>
 #include "ext4_jbd2.h"
 #include "ext4.h"
@@ -196,6 +198,222 @@ journal_err_out:
 	return err;
 }
 
+static int ext4_ioctl_setflags(struct inode *inode,
+			       unsigned int flags)
+{
+	struct ext4_inode_info *ei = EXT4_I(inode);
+	handle_t *handle = NULL;
+	int err = EPERM, migrate = 0;
+	struct ext4_iloc iloc;
+	unsigned int oldflags, mask, i;
+	unsigned int jflag;
+
+	/* Is it quota file? Do not allow user to mess with it */
+	if (IS_NOQUOTA(inode))
+		goto flags_out;
+
+	oldflags = ei->i_flags;
+
+	/* The JOURNAL_DATA flag is modifiable only by root */
+	jflag = flags & EXT4_JOURNAL_DATA_FL;
+
+	/*
+	 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
+	 * the relevant capability.
+	 *
+	 * This test looks nicer. Thanks to Pauline Middelink
+	 */
+	if ((flags ^ oldflags) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
+		if (!capable(CAP_LINUX_IMMUTABLE))
+			goto flags_out;
+	}
+
+	/*
+	 * The JOURNAL_DATA flag can only be changed by
+	 * the relevant capability.
+	 */
+	if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) {
+		if (!capable(CAP_SYS_RESOURCE))
+			goto flags_out;
+	}
+	if ((flags ^ oldflags) & EXT4_EXTENTS_FL)
+		migrate = 1;
+
+	if (flags & EXT4_EOFBLOCKS_FL) {
+		/* we don't support adding EOFBLOCKS flag */
+		if (!(oldflags & EXT4_EOFBLOCKS_FL)) {
+			err = -EOPNOTSUPP;
+			goto flags_out;
+		}
+	} else if (oldflags & EXT4_EOFBLOCKS_FL)
+		ext4_truncate(inode);
+
+	handle = ext4_journal_start(inode, EXT4_HT_INODE, 1);
+	if (IS_ERR(handle)) {
+		err = PTR_ERR(handle);
+		goto flags_out;
+	}
+	if (IS_SYNC(inode))
+		ext4_handle_sync(handle);
+	err = ext4_reserve_inode_write(handle, inode, &iloc);
+	if (err)
+		goto flags_err;
+
+	for (i = 0, mask = 1; i < 32; i++, mask <<= 1) {
+		if (!(mask & EXT4_FL_USER_MODIFIABLE))
+			continue;
+		if (mask & flags)
+			ext4_set_inode_flag(inode, i);
+		else
+			ext4_clear_inode_flag(inode, i);
+	}
+
+	ext4_set_inode_flags(inode);
+	inode->i_ctime = ext4_current_time(inode);
+
+	err = ext4_mark_iloc_dirty(handle, inode, &iloc);
+flags_err:
+	ext4_journal_stop(handle);
+	if (err)
+		goto flags_out;
+
+	if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL))
+		err = ext4_change_inode_journal_flag(inode, jflag);
+	if (err)
+		goto flags_out;
+	if (migrate) {
+		if (flags & EXT4_EXTENTS_FL)
+			err = ext4_ext_migrate(inode);
+		else
+			err = ext4_ind_migrate(inode);
+	}
+
+flags_out:
+	return err;
+}
+
+static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
+{
+	struct inode *inode = file_inode(filp);
+	struct super_block *sb = inode->i_sb;
+	struct ext4_inode_info *ei = EXT4_I(inode);
+	int err;
+	handle_t *handle;
+	kprojid_t kprojid;
+	struct ext4_iloc iloc;
+	struct ext4_inode *raw_inode;
+
+	struct dquot *transfer_to[EXT4_MAXQUOTAS] = { };
+
+	if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
+			EXT4_FEATURE_RO_COMPAT_PROJECT)) {
+		BUG_ON(__kprojid_val(EXT4_I(inode)->i_projid)
+		       != EXT4_DEF_PROJID);
+		if (projid != EXT4_DEF_PROJID)
+			return -EOPNOTSUPP;
+		else
+			return 0;
+	}
+
+	kprojid = make_kprojid(&init_user_ns, (projid_t)projid);
+
+	if (projid_eq(kprojid, EXT4_I(inode)->i_projid))
+		return 0;
+
+	err = mnt_want_write_file(filp);
+	if (err)
+		return err;
+
+	err = -EPERM;
+	mutex_lock(&inode->i_mutex);
+	/* Is it quota file? Do not allow user to mess with it */
+	if (IS_NOQUOTA(inode))
+		goto project_out;
+
+	dquot_initialize(inode);
+
+	handle = ext4_journal_start(inode, EXT4_HT_QUOTA,
+		EXT4_QUOTA_INIT_BLOCKS(sb) +
+		EXT4_QUOTA_DEL_BLOCKS(sb) + 3);
+	if (IS_ERR(handle)) {
+		err = PTR_ERR(handle);
+		goto project_out;
+	}
+
+	err = ext4_reserve_inode_write(handle, inode, &iloc);
+	if (err)
+		goto project_stop;
+
+	raw_inode = ext4_raw_inode(&iloc);
+	if ((EXT4_INODE_SIZE(sb) <=
+	     EXT4_GOOD_OLD_INODE_SIZE) ||
+	    (!EXT4_FITS_IN_INODE(raw_inode, ei, i_projid))) {
+	    	err = -EFBIG;
+	    	goto project_stop;
+	}
+
+	transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid));
+	if (!transfer_to[PRJQUOTA])
+		goto project_set;
+
+	err = __dquot_transfer(inode, transfer_to);
+	dqput(transfer_to[PRJQUOTA]);
+	if (err)
+		goto project_stop;
+
+project_set:
+	EXT4_I(inode)->i_projid = kprojid;
+	inode->i_ctime = ext4_current_time(inode);
+	err = ext4_mark_iloc_dirty(handle, inode, &iloc);
+project_stop:
+	ext4_journal_stop(handle);
+project_out:
+	mutex_unlock(&inode->i_mutex);
+	mnt_drop_write_file(filp);
+	return err;
+}
+
+/* Transfer internal flags to xflags */
+static inline __u32 ext4_iflags_to_xflags(unsigned long iflags)
+{
+	__u32 xflags = 0;
+
+	if (iflags & EXT4_SYNC_FL)
+		xflags |= FS_XFLAG_SYNC;
+	if (iflags & EXT4_IMMUTABLE_FL)
+		xflags |= FS_XFLAG_IMMUTABLE;
+	if (iflags & EXT4_APPEND_FL)
+		xflags |= FS_XFLAG_APPEND;
+	if (iflags & EXT4_NODUMP_FL)
+		xflags |= FS_XFLAG_NODUMP;
+	if (iflags & EXT4_NOATIME_FL)
+		xflags |= FS_XFLAG_NOATIME;
+	if (iflags & EXT4_PROJINHERIT_FL)
+		xflags |= FS_XFLAG_PROJINHERIT;
+	return xflags;
+}
+
+/* Transfer xflags flags to internal */
+static inline unsigned long ext4_xflags_to_iflags(__u32 xflags)
+{
+	unsigned long iflags = 0;
+
+	if (xflags & FS_XFLAG_SYNC)
+		iflags |= EXT4_SYNC_FL;
+	if (xflags & FS_XFLAG_IMMUTABLE)
+		iflags |= EXT4_IMMUTABLE_FL;
+	if (xflags & FS_XFLAG_APPEND)
+		iflags |= EXT4_APPEND_FL;
+	if (xflags & FS_XFLAG_NODUMP)
+		iflags |= EXT4_NODUMP_FL;
+	if (xflags & FS_XFLAG_NOATIME)
+		iflags |= EXT4_NOATIME_FL;
+	if (xflags & FS_XFLAG_PROJINHERIT)
+		iflags |= EXT4_PROJINHERIT_FL;
+
+	return iflags;
+}
+
 long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	struct inode *inode = file_inode(filp);
@@ -211,11 +429,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		flags = ei->i_flags & EXT4_FL_USER_VISIBLE;
 		return put_user(flags, (int __user *) arg);
 	case EXT4_IOC_SETFLAGS: {
-		handle_t *handle = NULL;
-		int err, migrate = 0;
-		struct ext4_iloc iloc;
-		unsigned int oldflags, mask, i;
-		unsigned int jflag;
+		int err;
 
 		if (!inode_owner_or_capable(inode))
 			return -EACCES;
@@ -229,89 +443,8 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 
 		flags = ext4_mask_flags(inode->i_mode, flags);
 
-		err = -EPERM;
 		mutex_lock(&inode->i_mutex);
-		/* Is it quota file? Do not allow user to mess with it */
-		if (IS_NOQUOTA(inode))
-			goto flags_out;
-
-		oldflags = ei->i_flags;
-
-		/* The JOURNAL_DATA flag is modifiable only by root */
-		jflag = flags & EXT4_JOURNAL_DATA_FL;
-
-		/*
-		 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
-		 * the relevant capability.
-		 *
-		 * This test looks nicer. Thanks to Pauline Middelink
-		 */
-		if ((flags ^ oldflags) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
-			if (!capable(CAP_LINUX_IMMUTABLE))
-				goto flags_out;
-		}
-
-		/*
-		 * The JOURNAL_DATA flag can only be changed by
-		 * the relevant capability.
-		 */
-		if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) {
-			if (!capable(CAP_SYS_RESOURCE))
-				goto flags_out;
-		}
-		if ((flags ^ oldflags) & EXT4_EXTENTS_FL)
-			migrate = 1;
-
-		if (flags & EXT4_EOFBLOCKS_FL) {
-			/* we don't support adding EOFBLOCKS flag */
-			if (!(oldflags & EXT4_EOFBLOCKS_FL)) {
-				err = -EOPNOTSUPP;
-				goto flags_out;
-			}
-		} else if (oldflags & EXT4_EOFBLOCKS_FL)
-			ext4_truncate(inode);
-
-		handle = ext4_journal_start(inode, EXT4_HT_INODE, 1);
-		if (IS_ERR(handle)) {
-			err = PTR_ERR(handle);
-			goto flags_out;
-		}
-		if (IS_SYNC(inode))
-			ext4_handle_sync(handle);
-		err = ext4_reserve_inode_write(handle, inode, &iloc);
-		if (err)
-			goto flags_err;
-
-		for (i = 0, mask = 1; i < 32; i++, mask <<= 1) {
-			if (!(mask & EXT4_FL_USER_MODIFIABLE))
-				continue;
-			if (mask & flags)
-				ext4_set_inode_flag(inode, i);
-			else
-				ext4_clear_inode_flag(inode, i);
-		}
-
-		ext4_set_inode_flags(inode);
-		inode->i_ctime = ext4_current_time(inode);
-
-		err = ext4_mark_iloc_dirty(handle, inode, &iloc);
-flags_err:
-		ext4_journal_stop(handle);
-		if (err)
-			goto flags_out;
-
-		if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL))
-			err = ext4_change_inode_journal_flag(inode, jflag);
-		if (err)
-			goto flags_out;
-		if (migrate) {
-			if (flags & EXT4_EXTENTS_FL)
-				err = ext4_ext_migrate(inode);
-			else
-				err = ext4_ind_migrate(inode);
-		}
-
-flags_out:
+		err = ext4_ioctl_setflags(inode, flags);
 		mutex_unlock(&inode->i_mutex);
 		mnt_drop_write_file(filp);
 		return err;
@@ -615,7 +748,66 @@ resizefs_out:
 	}
 	case EXT4_IOC_PRECACHE_EXTENTS:
 		return ext4_ext_precache(inode);
+	case EXT4_IOC_FSGETXATTR:
+	{
+		struct fsxattr fa;
 
+		memset(&fa, 0, sizeof(struct fsxattr));
+
+		ext4_get_inode_flags(ei);
+		fa.fsx_xflags = ext4_iflags_to_xflags(ei->i_flags & EXT4_FL_USER_VISIBLE);
+
+		if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
+				EXT4_FEATURE_RO_COMPAT_PROJECT)) {
+			fa.fsx_projid = (__u32)from_kprojid(&init_user_ns,
+				EXT4_I(inode)->i_projid);
+		}
+
+		if (copy_to_user((struct fsxattr __user *)arg,
+				 &fa, sizeof(fa)))
+			return -EFAULT;
+		return 0;
+	}
+	case EXT4_IOC_FSSETXATTR:
+	{
+		struct fsxattr fa;
+		int err;
+
+		if (copy_from_user(&fa, (struct fsxattr __user *)arg,
+				   sizeof(fa)))
+			return -EFAULT;
+
+		/* Make sure caller has proper permission */
+		if (__kprojid_val(EXT4_I(inode)->i_projid) == fa.fsx_projid) {
+			if (!inode_owner_or_capable(inode))
+				return -EACCES;
+		} else {
+			if (!capable(CAP_SYS_ADMIN))
+				return -EACCES;
+		}
+
+		err = mnt_want_write_file(filp);
+		if (err)
+			return err;
+
+		flags = ext4_xflags_to_iflags(fa.fsx_xflags);
+		flags = ext4_mask_flags(inode->i_mode, flags);
+
+		mutex_lock(&inode->i_mutex);
+		flags = (ei->i_flags & ~EXT4_FL_XFLAG_VISIBLE) |
+			 (flags & EXT4_FL_XFLAG_VISIBLE);
+		err = ext4_ioctl_setflags(inode, flags);
+		mutex_unlock(&inode->i_mutex);
+		mnt_drop_write_file(filp);
+		if (err)
+			return err;
+
+		err = ext4_ioctl_setproject(filp, fa.fsx_projid);
+		if (err)
+			return err;
+
+		return 0;
+	}
 	default:
 		return -ENOTTY;
 	}
diff --git a/fs/xfs/xfs_fs.h b/fs/xfs/xfs_fs.h
index 18dc721..64c7ae6 100644
--- a/fs/xfs/xfs_fs.h
+++ b/fs/xfs/xfs_fs.h
@@ -36,38 +36,25 @@ struct dioattr {
 #endif
 
 /*
- * Structure for XFS_IOC_FSGETXATTR[A] and XFS_IOC_FSSETXATTR.
- */
-#ifndef HAVE_FSXATTR
-struct fsxattr {
-	__u32		fsx_xflags;	/* xflags field value (get/set) */
-	__u32		fsx_extsize;	/* extsize field value (get/set)*/
-	__u32		fsx_nextents;	/* nextents field value (get)	*/
-	__u32		fsx_projid;	/* project identifier (get/set) */
-	unsigned char	fsx_pad[12];
-};
-#endif
-
-/*
  * Flags for the bs_xflags/fsx_xflags field
  * There should be a one-to-one correspondence between these flags and the
  * XFS_DIFLAG_s.
  */
-#define XFS_XFLAG_REALTIME	0x00000001	/* data in realtime volume */
-#define XFS_XFLAG_PREALLOC	0x00000002	/* preallocated file extents */
-#define XFS_XFLAG_IMMUTABLE	0x00000008	/* file cannot be modified */
-#define XFS_XFLAG_APPEND	0x00000010	/* all writes append */
-#define XFS_XFLAG_SYNC		0x00000020	/* all writes synchronous */
-#define XFS_XFLAG_NOATIME	0x00000040	/* do not update access time */
-#define XFS_XFLAG_NODUMP	0x00000080	/* do not include in backups */
-#define XFS_XFLAG_RTINHERIT	0x00000100	/* create with rt bit set */
-#define XFS_XFLAG_PROJINHERIT	0x00000200	/* create with parents projid */
-#define XFS_XFLAG_NOSYMLINKS	0x00000400	/* disallow symlink creation */
-#define XFS_XFLAG_EXTSIZE	0x00000800	/* extent size allocator hint */
-#define XFS_XFLAG_EXTSZINHERIT	0x00001000	/* inherit inode extent size */
-#define XFS_XFLAG_NODEFRAG	0x00002000  	/* do not defragment */
-#define XFS_XFLAG_FILESTREAM	0x00004000	/* use filestream allocator */
-#define XFS_XFLAG_HASATTR	0x80000000	/* no DIFLAG for this	*/
+#define XFS_XFLAG_REALTIME	FS_XFLAG_REALTIME	/* data in realtime volume */
+#define XFS_XFLAG_PREALLOC	FS_XFLAG_PREALLOC	/* preallocated file extents */
+#define XFS_XFLAG_IMMUTABLE	FS_XFLAG_IMMUTABLE	/* file cannot be modified */
+#define XFS_XFLAG_APPEND	FS_XFLAG_APPEND		/* all writes append */
+#define XFS_XFLAG_SYNC		FS_XFLAG_SYNC		/* all writes synchronous */
+#define XFS_XFLAG_NOATIME	FS_XFLAG_NOATIME	/* do not update access time */
+#define XFS_XFLAG_NODUMP	FS_XFLAG_NODUMP		/* do not include in backups */
+#define XFS_XFLAG_RTINHERIT	FS_XFLAG_RTINHERIT	/* create with rt bit set */
+#define XFS_XFLAG_PROJINHERIT	FS_XFLAG_PROJINHERIT	/* create with parents projid */
+#define XFS_XFLAG_NOSYMLINKS	FS_XFLAG_NOSYMLINKS	/* disallow symlink creation */
+#define XFS_XFLAG_EXTSIZE	FS_XFLAG_EXTSIZE	/* extent size allocator hint */
+#define XFS_XFLAG_EXTSZINHERIT	FS_XFLAG_EXTSZINHERIT	/* inherit inode extent size */
+#define XFS_XFLAG_NODEFRAG	FS_XFLAG_NODEFRAG  	/* do not defragment */
+#define XFS_XFLAG_FILESTREAM	FS_XFLAG_FILESTREAM	/* use filestream allocator */
+#define XFS_XFLAG_HASATTR	FS_XFLAG_HASATTR	/* no DIFLAG for this	*/
 
 /*
  * Structure for XFS_IOC_GETBMAP.
@@ -503,8 +490,8 @@ typedef struct xfs_swapext
 #define XFS_IOC_ALLOCSP		_IOW ('X', 10, struct xfs_flock64)
 #define XFS_IOC_FREESP		_IOW ('X', 11, struct xfs_flock64)
 #define XFS_IOC_DIOINFO		_IOR ('X', 30, struct dioattr)
-#define XFS_IOC_FSGETXATTR	_IOR ('X', 31, struct fsxattr)
-#define XFS_IOC_FSSETXATTR	_IOW ('X', 32, struct fsxattr)
+#define XFS_IOC_FSGETXATTR	FS_IOC_FSGETXATTR
+#define XFS_IOC_FSSETXATTR	FS_IOC_FSSETXATTR
 #define XFS_IOC_ALLOCSP64	_IOW ('X', 36, struct xfs_flock64)
 #define XFS_IOC_FREESP64	_IOW ('X', 37, struct xfs_flock64)
 #define XFS_IOC_GETBMAP		_IOWR('X', 38, struct getbmap)
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index fcbf647..69dda62 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -58,6 +58,36 @@ struct inodes_stat_t {
 	long dummy[5];		/* padding for sysctl ABI compatibility */
 };
 
+/*
+ * Structure for FS_IOC_FSGETXATTR and FS_IOC_FSSETXATTR.
+ */
+struct fsxattr {
+	__u32		fsx_xflags;	/* xflags field value (get/set) */
+	__u32		fsx_extsize;	/* extsize field value (get/set)*/
+	__u32		fsx_nextents;	/* nextents field value (get)	*/
+	__u32		fsx_projid;	/* project identifier (get/set) */
+	unsigned char	fsx_pad[12];
+};
+
+/*
+ * Flags for the fsx_xflags field
+ */
+#define FS_XFLAG_REALTIME	0x00000001	/* data in realtime volume */
+#define FS_XFLAG_PREALLOC	0x00000002	/* preallocated file extents */
+#define FS_XFLAG_IMMUTABLE	0x00000008	/* file cannot be modified */
+#define FS_XFLAG_APPEND		0x00000010	/* all writes append */
+#define FS_XFLAG_SYNC		0x00000020	/* all writes synchronous */
+#define FS_XFLAG_NOATIME	0x00000040	/* do not update access time */
+#define FS_XFLAG_NODUMP		0x00000080	/* do not include in backups */
+#define FS_XFLAG_RTINHERIT	0x00000100	/* create with rt bit set */
+#define FS_XFLAG_PROJINHERIT	0x00000200	/* create with parents projid */
+#define FS_XFLAG_NOSYMLINKS	0x00000400	/* disallow symlink creation */
+#define FS_XFLAG_EXTSIZE	0x00000800	/* extent size allocator hint */
+#define FS_XFLAG_EXTSZINHERIT	0x00001000	/* inherit inode extent size */
+#define FS_XFLAG_NODEFRAG	0x00002000  	/* do not defragment */
+#define FS_XFLAG_FILESTREAM	0x00004000	/* use filestream allocator */
+#define FS_XFLAG_HASATTR	0x80000000	/* no DIFLAG for this */
+
 
 #define NR_FILE  8192	/* this can well be larger on a larger system */
 
@@ -163,6 +193,8 @@ struct inodes_stat_t {
 #define	FS_IOC_GETVERSION		_IOR('v', 1, long)
 #define	FS_IOC_SETVERSION		_IOW('v', 2, long)
 #define FS_IOC_FIEMAP			_IOWR('f', 11, struct fiemap)
+#define FS_IOC_FSGETXATTR		_IOR('X', 31, struct fsxattr)
+#define FS_IOC_FSSETXATTR		_IOW('X', 32, struct fsxattr)
 #define FS_IOC32_GETFLAGS		_IOR('f', 1, int)
 #define FS_IOC32_SETFLAGS		_IOW('f', 2, int)
 #define FS_IOC32_GETVERSION		_IOR('v', 1, int)
-- 
1.7.1


^ permalink raw reply related

* [v11 5/5] ext4: cleanup inode flag definitions
From: Li Xi @ 2015-03-20 18:41 UTC (permalink / raw)
  To: linux-fsdevel, linux-ext4, linux-api, tytso, adilger, jack, viro,
	hch, dmonakhov
In-Reply-To: <1426876890-9914-1-git-send-email-lixi@ddn.com>

The inode flags defined in uapi/linux/fs.h were migrated from
ext4.h. This patch changes the inode flag definitions in ext4.h
to VFS definitions to make the gaps between them clearer.

Signed-off-by: Li Xi <lixi@ddn.com>
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
---
 fs/ext4/ext4.h |   50 +++++++++++++++++++++++++-------------------------
 1 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 377fec0..05d0e8d 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -352,34 +352,34 @@ struct flex_groups {
 /*
  * Inode flags
  */
-#define	EXT4_SECRM_FL			0x00000001 /* Secure deletion */
-#define	EXT4_UNRM_FL			0x00000002 /* Undelete */
-#define	EXT4_COMPR_FL			0x00000004 /* Compress file */
-#define EXT4_SYNC_FL			0x00000008 /* Synchronous updates */
-#define EXT4_IMMUTABLE_FL		0x00000010 /* Immutable file */
-#define EXT4_APPEND_FL			0x00000020 /* writes to file may only append */
-#define EXT4_NODUMP_FL			0x00000040 /* do not dump file */
-#define EXT4_NOATIME_FL			0x00000080 /* do not update atime */
+#define	EXT4_SECRM_FL			FS_SECRM_FL        /* Secure deletion */
+#define	EXT4_UNRM_FL			FS_UNRM_FL         /* Undelete */
+#define	EXT4_COMPR_FL			FS_COMPR_FL        /* Compress file */
+#define EXT4_SYNC_FL			FS_SYNC_FL         /* Synchronous updates */
+#define EXT4_IMMUTABLE_FL		FS_IMMUTABLE_FL    /* Immutable file */
+#define EXT4_APPEND_FL			FS_APPEND_FL       /* writes to file may only append */
+#define EXT4_NODUMP_FL			FS_NODUMP_FL       /* do not dump file */
+#define EXT4_NOATIME_FL			FS_NOATIME_FL      /* do not update atime */
 /* Reserved for compression usage... */
-#define EXT4_DIRTY_FL			0x00000100
-#define EXT4_COMPRBLK_FL		0x00000200 /* One or more compressed clusters */
-#define EXT4_NOCOMPR_FL			0x00000400 /* Don't compress */
+#define EXT4_DIRTY_FL			FS_DIRTY_FL
+#define EXT4_COMPRBLK_FL		FS_COMPRBLK_FL     /* One or more compressed clusters */
+#define EXT4_NOCOMPR_FL			FS_NOCOMP_FL       /* Don't compress */
 	/* nb: was previously EXT2_ECOMPR_FL */
-#define EXT4_ENCRYPT_FL			0x00000800 /* encrypted file */
+#define EXT4_ENCRYPT_FL			0x00000800         /* encrypted file */
 /* End compression flags --- maybe not all used */
-#define EXT4_INDEX_FL			0x00001000 /* hash-indexed directory */
-#define EXT4_IMAGIC_FL			0x00002000 /* AFS directory */
-#define EXT4_JOURNAL_DATA_FL		0x00004000 /* file data should be journaled */
-#define EXT4_NOTAIL_FL			0x00008000 /* file tail should not be merged */
-#define EXT4_DIRSYNC_FL			0x00010000 /* dirsync behaviour (directories only) */
-#define EXT4_TOPDIR_FL			0x00020000 /* Top of directory hierarchies*/
-#define EXT4_HUGE_FILE_FL               0x00040000 /* Set to each huge file */
-#define EXT4_EXTENTS_FL			0x00080000 /* Inode uses extents */
-#define EXT4_EA_INODE_FL	        0x00200000 /* Inode used for large EA */
-#define EXT4_EOFBLOCKS_FL		0x00400000 /* Blocks allocated beyond EOF */
-#define EXT4_INLINE_DATA_FL		0x10000000 /* Inode has inline data. */
-#define EXT4_PROJINHERIT_FL		0x20000000 /* Create with parents projid */
-#define EXT4_RESERVED_FL		0x80000000 /* reserved for ext4 lib */
+#define EXT4_INDEX_FL			FS_INDEX_FL        /* hash-indexed directory */
+#define EXT4_IMAGIC_FL			FS_IMAGIC_FL       /* AFS directory */
+#define EXT4_JOURNAL_DATA_FL		FS_JOURNAL_DATA_FL /* file data should be journaled */
+#define EXT4_NOTAIL_FL			FS_NOTAIL_FL       /* file tail should not be merged */
+#define EXT4_DIRSYNC_FL			FS_DIRSYNC_FL      /* dirsync behaviour (directories only) */
+#define EXT4_TOPDIR_FL			FS_TOPDIR_FL       /* Top of directory hierarchies*/
+#define EXT4_HUGE_FILE_FL               0x00040000         /* Set to each huge file */
+#define EXT4_EXTENTS_FL			FS_EXTENT_FL       /* Inode uses extents */
+#define EXT4_EA_INODE_FL	        0x00200000         /* Inode used for large EA */
+#define EXT4_EOFBLOCKS_FL		0x00400000         /* Blocks allocated beyond EOF */
+#define EXT4_INLINE_DATA_FL		0x10000000         /* Inode has inline data. */
+#define EXT4_PROJINHERIT_FL		FS_PROJINHERIT_FL  /* Create with parents projid */
+#define EXT4_RESERVED_FL		FS_RESERVED_FL     /* reserved for ext4 lib */
 
 #define EXT4_FL_USER_VISIBLE		0x204BDFFF /* User visible flags */
 #define EXT4_FL_USER_MODIFIABLE		0x204380FF /* User modifiable flags */
-- 
1.7.1


^ permalink raw reply related

* Re: [PATCH v2 5/7] clone4: Add a CLONE_AUTOREAP flag to automatically reap the child process
From: Thiago Macieira @ 2015-03-20 18:46 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Josh Triplett, Al Viro, Andrew Morton, Andy Lutomirski,
	Ingo Molnar, Kees Cook, Paul E. McKenney, H. Peter Anvin,
	Rik van Riel, Thomas Gleixner, Michael Kerrisk, linux-kernel,
	linux-api, linux-fsdevel, x86
In-Reply-To: <20150320181404.GA26343@redhat.com>

On Friday 20 March 2015 19:14:04 Oleg Nesterov wrote:
> Also. I forgot that the kernel always resets ->exit_signal to SIGCHLD on
> exec or reparenting. Reparenting is probably fine. But what about exec?
> Should it keep ->exit_signal == 0 if "autoreap" ? I think it should not, to
> avoid the strange special case.

Not delivering any signal was the objective of this patch series, so yes 
exit_signal == 0 should survive an exec and even re-exec.

-- 
Thiago Macieira - thiago.macieira (AT) intel.com
  Software Architect - Intel Open Source Technology Center

^ permalink raw reply

* Re: [PATCH v2 5/7] clone4: Add a CLONE_AUTOREAP flag to automatically reap the child process
From: Oleg Nesterov @ 2015-03-20 19:09 UTC (permalink / raw)
  To: Thiago Macieira
  Cc: Josh Triplett, Al Viro, Andrew Morton, Andy Lutomirski,
	Ingo Molnar, Kees Cook, Paul E. McKenney, H. Peter Anvin,
	Rik van Riel, Thomas Gleixner, Michael Kerrisk,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-fsdevel-u79uwXL29TY76Z2rM5mHXA, x86-DgEjT+Ai2ygdnm+yROfE0A
In-Reply-To: <1766646.jusoNDOCPE@tjmaciei-mobl4>

On 03/20, Thiago Macieira wrote:
>
> On Friday 20 March 2015 19:14:04 Oleg Nesterov wrote:
> > Also. I forgot that the kernel always resets ->exit_signal to SIGCHLD on
> > exec or reparenting. Reparenting is probably fine. But what about exec?
> > Should it keep ->exit_signal == 0 if "autoreap" ? I think it should not, to
> > avoid the strange special case.
>
> Not delivering any signal was the objective of this patch series, so yes
> exit_signal == 0 should survive an exec and even re-exec.

OK, but then perhaps we should never send SIGCHLD (on exit) if "autoreap",
to make the logic simple.

And copy_process() should probably do

	if ((clone_flags & CSIGNAL) && (clone_flags && CLONE_AUTOREAP))
		return -EINVAL;

so that we still can change this behaviour later.

Oleg.

^ permalink raw reply

* Re: [PATCH v8 tip 2/9] tracing: add kprobe flag
From: Steven Rostedt @ 2015-03-20 20:11 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: Ingo Molnar, Namhyung Kim, Arnaldo Carvalho de Melo, Jiri Olsa,
	Masami Hiramatsu, David S. Miller, Daniel Borkmann,
	Peter Zijlstra, linux-api, netdev, linux-kernel
In-Reply-To: <1426816787-5001-3-git-send-email-ast@plumgrid.com>

On Thu, 19 Mar 2015 18:59:40 -0700
Alexei Starovoitov <ast@plumgrid.com> wrote:

> add TRACE_EVENT_FL_KPROBE flag to differentiate kprobe type of tracepoints,
> since bpf programs can only be attached to kprobe type of
> PERF_TYPE_TRACEPOINT perf events.
> 
> Signed-off-by: Alexei Starovoitov <ast@plumgrid.com>
> ---
>  include/linux/ftrace_event.h |    3 +++
>  kernel/trace/trace_kprobe.c  |    2 +-
>  2 files changed, 4 insertions(+), 1 deletion(-)

Reviewed-by: Steven Rostedt <rostedt@goodmis.org>

-- Steve

^ permalink raw reply

* Re: [PATCH 05/45] drm.h: include stdlib.h in userspace
From: Emil Velikov @ 2015-03-20 20:25 UTC (permalink / raw)
  To: Mikko Rapeli, David Howells, Daniel Vetter, Dave Airlie
  Cc: Linux-Kernel@Vger. Kernel. Org, ML dri-devel,
	linux-api-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20150223103548.GT12550-dqH1CgrzRhOk/eJAJmRu5A@public.gmane.org>

On 23 February 2015 at 10:35, Mikko Rapeli <mikko.rapeli-X3B1VOXEql0@public.gmane.org> wrote:
> On Mon, Feb 23, 2015 at 10:26:58AM +0000, Emil Velikov wrote:
>> On 16/02/15 23:05, Mikko Rapeli wrote:
>> > Fixes <drm/drm.h> compilation error:
>> >
>> > drm/drm.h:132:2: error: unknown type name ‘size_t’
>> >
>> Hi Mikko,
>>
>> Can you let us know how you're getting these (series-wise) errors ? I've
>> been meaning to sync the uapi/drm and libdrm headers and would be nice
>> to have an extra step to test things.
>
> This should have everything needed to reproduce these compile errors,
> though some of the errors hide behind other errors and fixes:
>
> https://lkml.org/lkml/2015/2/16/525
>
Thanks for the link Mikko.

Afaict the general consensus seems to be that one should avoid using
stdint's uint8_t, but stick to __u8 and friends. Did you had the
chance to roll out another series that does so ?

That aside I'm not 100% sure that doing the UAPI split, as is, was the
perfect solution. Afaik drm used to live as an out of tree userspace
library(libdrm). Not sure at which point the major restructuring took
part, but one is certain - libdrm remains the only authoritative
sources of the headers. It's possible that some buggy programs pull
the UAPI headers while linking against the library, but I'd say that
won't end up well in the long term. Additionally since the UAPI split
the `make update-headers' target used to sync libdrm's headers have
been broken leading people to copy misc. hunks and/or files. Leading
to greater chance of things going sour.

All that said, I will need to gather some opinions for drm developers
and maintainers if the idea of part revering 718dcedd7e8(UAPI:
(Scripted) Disintegrate include/drm) will be the way forward.

Thanks
Emil

^ permalink raw reply

* Re: [PATCH v8 tip 3/9] tracing: attach BPF programs to kprobes
From: Steven Rostedt @ 2015-03-20 21:09 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: Ingo Molnar, Namhyung Kim, Arnaldo Carvalho de Melo, Jiri Olsa,
	Masami Hiramatsu, David S. Miller, Daniel Borkmann,
	Peter Zijlstra, linux-api, netdev, linux-kernel
In-Reply-To: <1426816787-5001-4-git-send-email-ast@plumgrid.com>

On Thu, 19 Mar 2015 18:59:41 -0700
Alexei Starovoitov <ast@plumgrid.com> wrote:

Some nits...



> --- /dev/null
> +++ b/kernel/trace/bpf_trace.c
> @@ -0,0 +1,123 @@
> +/* Copyright (c) 2011-2015 PLUMgrid, http://plumgrid.com
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of version 2 of the GNU General Public
> + * License as published by the Free Software Foundation.
> + */
> +#include <linux/kernel.h>
> +#include <linux/types.h>
> +#include <linux/slab.h>
> +#include <linux/bpf.h>
> +#include <linux/filter.h>
> +#include <linux/uaccess.h>
> +#include "trace.h"
> +
> +static DEFINE_PER_CPU(int, bpf_prog_active);
> +
> +/**
> + * trace_call_bpf - invoke BPF program
> + * @prog - BPF program
> + * @ctx - opaque context pointer
> + *
> + * kprobe handlers execute BPF programs via this helper.
> + * Can be used from static tracepoints in the future.

Should also state what the expected return values are. What does a
return of "1" mean?

> + */
> +unsigned int trace_call_bpf(struct bpf_prog *prog, void *ctx)
> +{
> +	unsigned int ret;
> +
> +	if (in_nmi()) /* not supported yet */
> +		return 1;
> +
> +	preempt_disable();
> +
> +	if (unlikely(__this_cpu_inc_return(bpf_prog_active) != 1)) {
> +		/* since some bpf program is already running on this cpu,

You are no longer in the net/ world. The rest of the Linux coding style
is:

	/*
	 * multi line comments
	 */

Only DaveM gets away with that format ;-)

> +		 * don't call into another bpf program (same or different)
> +		 * and don't send kprobe event into ring-buffer,
> +		 * so return zero here
> +		 */
> +		ret = 0;
> +		goto out;
> +	}
> +
> +	rcu_read_lock();
> +	ret = BPF_PROG_RUN(prog, ctx);
> +	rcu_read_unlock();
> +
> + out:
> +	__this_cpu_dec(bpf_prog_active);
> +	preempt_enable();
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(trace_call_bpf);

The rest looks fine. For that..

Reviewed-by: Steven Rostedt <rostedt@goodmis.org>

-- Steve

^ permalink raw reply

* Re: [PATCH v2 5/7] clone4: Add a CLONE_AUTOREAP flag to automatically reap the child process
From: josh @ 2015-03-20 21:10 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Thiago Macieira, Al Viro, Andrew Morton, Andy Lutomirski,
	Ingo Molnar, Kees Cook, Paul E. McKenney, H. Peter Anvin,
	Rik van Riel, Thomas Gleixner, Michael Kerrisk, linux-kernel,
	linux-api, linux-fsdevel, x86
In-Reply-To: <20150320190914.GA28357@redhat.com>

On Fri, Mar 20, 2015 at 08:09:14PM +0100, Oleg Nesterov wrote:
> On 03/20, Thiago Macieira wrote:
> >
> > On Friday 20 March 2015 19:14:04 Oleg Nesterov wrote:
> > > Also. I forgot that the kernel always resets ->exit_signal to SIGCHLD on
> > > exec or reparenting. Reparenting is probably fine. But what about exec?
> > > Should it keep ->exit_signal == 0 if "autoreap" ? I think it should not, to
> > > avoid the strange special case.
> >
> > Not delivering any signal was the objective of this patch series, so yes
> > exit_signal == 0 should survive an exec and even re-exec.
> 
> OK, but then perhaps we should never send SIGCHLD (on exit) if "autoreap",
> to make the logic simple.
> 
> And copy_process() should probably do
> 
> 	if ((clone_flags & CSIGNAL) && (clone_flags && CLONE_AUTOREAP))
> 		return -EINVAL;
> 
> so that we still can change this behaviour later.

I'm fine with that, as it would handle the particular use case we care
about.

However, the reset-signal-on-reparent thing might still make sense,
particularly for the ptrace-reparent case (less so for the
reparent-to-child-reaper case).

- Josh Triplett

^ permalink raw reply

* Re: [PATCH v8 tip 5/9] tracing: allow BPF programs to call bpf_trace_printk()
From: Steven Rostedt @ 2015-03-20 21:22 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: Ingo Molnar, Namhyung Kim, Arnaldo Carvalho de Melo, Jiri Olsa,
	Masami Hiramatsu, David S. Miller, Daniel Borkmann,
	Peter Zijlstra, linux-api, netdev, linux-kernel
In-Reply-To: <1426816787-5001-6-git-send-email-ast@plumgrid.com>

On Thu, 19 Mar 2015 18:59:43 -0700
Alexei Starovoitov <ast@plumgrid.com> wrote:

> Debugging of BPF programs needs some form of printk from the program,
> so let programs call limited trace_printk() with %d %u %x %p modifiers only.
> 
> Similar to kernel modules, during program load verifier checks whether program
> is calling bpf_trace_printk() and if so, kernel allocates trace_printk buffers
> and emits big 'this is debug only' banner.
> 
> Signed-off-by: Alexei Starovoitov <ast@plumgrid.com>
> ---
>  include/uapi/linux/bpf.h |    1 +
>  kernel/trace/bpf_trace.c |   75 ++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 76 insertions(+)
> 
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index 238c6883877b..cc47ef41076a 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -166,6 +166,7 @@ enum bpf_func_id {
>  	BPF_FUNC_map_delete_elem, /* int map_delete_elem(&map, &key) */
>  	BPF_FUNC_probe_read,      /* int bpf_probe_read(void *dst, int size, void *src) */
>  	BPF_FUNC_ktime_get_ns,    /* u64 bpf_ktime_get_ns(void) */
> +	BPF_FUNC_trace_printk,    /* int bpf_trace_printk(const char *fmt, int fmt_size, ...) */
>  	__BPF_FUNC_MAX_ID,
>  };
>  
> diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
> index 769c577a1fc8..d5b6284edd75 100644
> --- a/kernel/trace/bpf_trace.c
> +++ b/kernel/trace/bpf_trace.c
> @@ -10,6 +10,7 @@
>  #include <linux/bpf.h>
>  #include <linux/filter.h>
>  #include <linux/uaccess.h>
> +#include <linux/ctype.h>
>  #include "trace.h"
>  
>  static DEFINE_PER_CPU(int, bpf_prog_active);
> @@ -83,6 +84,72 @@ static const struct bpf_func_proto bpf_ktime_get_ns_proto = {
>  	.ret_type = RET_INTEGER,
>  };
>  
> +/* limited trace_printk()
> + * only %d %u %x %ld %lu %lx %lld %llu %llx %p conversion specifiers allowed
> + */

Ah! Again, don't contaminate the rest of the kernel with net comment
styles! :-)

I change my comment style when I submit net patches. So patches that go
into tracing must conform to the tracing style.

> +static u64 bpf_trace_printk(u64 r1, u64 fmt_size, u64 r3, u64 r4, u64 r5)
> +{
> +	char *fmt = (char *) (long) r1;
> +	int mod[3] = {};
> +	int fmt_cnt = 0;
> +	int i;
> +
> +	/* bpf_check()->check_func_arg()->check_stack_boundary()

It spreads like a disease!

> +	 * guarantees that fmt points to bpf program stack,
> +	 * fmt_size bytes of it were initialized and fmt_size > 0
> +	 */
> +	if (fmt[--fmt_size] != 0)
> +		return -EINVAL;
> +
> +	/* check format string for allowed specifiers */
> +	for (i = 0; i < fmt_size; i++) {
> +		if ((!isprint(fmt[i]) && !isspace(fmt[i])) || !isascii(fmt[i]))
> +			return -EINVAL;
> +
> +		if (fmt[i] != '%')
> +			continue;
> +
> +		if (fmt_cnt >= 3)
> +			return -EINVAL;
> +
> +		/* fmt[i] != 0 && fmt[last] == 0, so we can access fmt[i + 1] */
> +		i++;
> +		if (fmt[i] == 'l') {
> +			mod[fmt_cnt]++;
> +			i++;
> +		} else if (fmt[i] == 'p') {
> +			mod[fmt_cnt]++;
> +			i++;
> +			if (!isspace(fmt[i]) && fmt[i] != 0)

I wonder if we should allow punctuation here too? None alpha-numeric
characters?

> +				return -EINVAL;
> +			fmt_cnt++;
> +			continue;
> +		}
> +
> +		if (fmt[i] == 'l') {
> +			mod[fmt_cnt]++;
> +			i++;
> +		}
> +
> +		if (fmt[i] != 'd' && fmt[i] != 'u' && fmt[i] != 'x')
> +			return -EINVAL;
> +		fmt_cnt++;
> +	}
> +
> +	return __trace_printk(1/* fake ip will not be printed */, fmt,
> +			      mod[0] == 2 ? r3 : mod[0] == 1 ? (long) r3 : (u32) r3,
> +			      mod[1] == 2 ? r4 : mod[1] == 1 ? (long) r4 : (u32) r4,
> +			      mod[2] == 2 ? r5 : mod[2] == 1 ? (long) r5 : (u32) r5);

Nice trick.

Except for the net-contaminated-comments, the rest looks good.

Reviewed-by: Steven Rostedt <rostedt@goodmis.org>

-- Steve

> +}
> +
> +static const struct bpf_func_proto bpf_trace_printk_proto = {
> +	.func = bpf_trace_printk,
> +	.gpl_only = true,
> +	.ret_type = RET_INTEGER,
> +	.arg1_type = ARG_PTR_TO_STACK,
> +	.arg2_type = ARG_CONST_STACK_SIZE,
> +};
> +
>  static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func_id)
>  {
>  	switch (func_id) {
> @@ -96,6 +163,14 @@ static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func
>  		return &bpf_probe_read_proto;
>  	case BPF_FUNC_ktime_get_ns:
>  		return &bpf_ktime_get_ns_proto;
> +
> +	case BPF_FUNC_trace_printk:
> +		/* this program might be calling bpf_trace_printk,
> +		 * so allocate per-cpu printk buffers
> +		 */
> +		trace_printk_init_buffers();
> +
> +		return &bpf_trace_printk_proto;
>  	default:
>  		return NULL;
>  	}

^ permalink raw reply

* Re: [PATCH v8 tip 3/9] tracing: attach BPF programs to kprobes
From: Alexei Starovoitov @ 2015-03-20 21:38 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Ingo Molnar, Namhyung Kim, Arnaldo Carvalho de Melo, Jiri Olsa,
	Masami Hiramatsu, David S. Miller, Daniel Borkmann,
	Peter Zijlstra, linux-api-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20150320170907.388d8c33-f9ZlEuEWxVcJvu8Pb33WZ0EMvNT87kid@public.gmane.org>

On 3/20/15 2:09 PM, Steven Rostedt wrote:
>
>> +/**
>> + * trace_call_bpf - invoke BPF program
>> + * @prog - BPF program
>> + * @ctx - opaque context pointer
>> + *
>> + * kprobe handlers execute BPF programs via this helper.
>> + * Can be used from static tracepoints in the future.
>
> Should also state what the expected return values are. What does a
> return of "1" mean?

In earlier versions of this set I had detailed description of return
values from bpf program, somehow it got lost after all the revisions.
Will re-add.


> You are no longer in the net/ world. The rest of the Linux coding style
> is:
>
> 	/*
> 	 * multi line comments
> 	 */
>
> Only DaveM gets away with that format ;-)

:)
since bpf was born in the net/ world all comments are in that style.
When it moved into kernel/bpf/ we've decided to keep net/ style in there
as well, but for this file I don't mind using !net style ;)

> Reviewed-by: Steven Rostedt <rostedt-nx8X9YLhiw1AfugRpC6u6w@public.gmane.org>

Thanks a lot. Will respin.

^ permalink raw reply

* Re: [PATCH v8 tip 5/9] tracing: allow BPF programs to call bpf_trace_printk()
From: Alexei Starovoitov @ 2015-03-20 21:44 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Ingo Molnar, Namhyung Kim, Arnaldo Carvalho de Melo, Jiri Olsa,
	Masami Hiramatsu, David S. Miller, Daniel Borkmann,
	Peter Zijlstra, linux-api, netdev, linux-kernel
In-Reply-To: <20150320172219.45ff7157@gandalf.local.home>

On 3/20/15 2:22 PM, Steven Rostedt wrote:
>> +/* limited trace_printk()
>> + * only %d %u %x %ld %lu %lx %lld %llu %llx %p conversion specifiers allowed
>> + */
>
> Ah! Again, don't contaminate the rest of the kernel with net comment
> styles! :-)

ok :)

>> +		} else if (fmt[i] == 'p') {
>> +			mod[fmt_cnt]++;
>> +			i++;
>> +			if (!isspace(fmt[i]) && fmt[i] != 0)
>
> I wonder if we should allow punctuation here too? None alpha-numeric
> characters?

yes. just checked all of ispunct characters after %p.
All should be fine.

Thanks!

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox